Perl / Java / PCRE / .NET, 35 bytes
((\2x|^x)+)\1(\3xx|x(?=\2$))+$|^x?$
This beats primo's solution by 4 bytes while supporting the exact same set of regex engines.
Try it online! - Perl
Try it online! - Java
Try it online! - PCRE1
Try it online! / Attempt This Online! - PCRE2
Try it online! - .NET
It works by first finding two consecutive triangular numbers whose sum is \$n\$ (using the equivalent of (\1x|^x)+
to find the smaller one), so that \$\sqrt n\$ will be the triangular root of the larger triangular number. Then it tries to match that root as itself being a perfect square, using the equivalent of (\1xx|^x)+$
.
If it did not need to match \$0^4\$, the regex would be 34 bytes, and if it didn't need to match \$1^4\$ either, it would be 30 bytes.
# tail = N = input number
( # \1 = a triangular number T_0; tail -= \1;
# \2 = triangular root of \1
(
\2x # On iterations after the first, \2 = \2 + 1
|
^x # On the first iteration, \2 = 1
)+ # Iterate the above any nonzero number of times
)
# T_1, the next consecutive triangular number after \1, is \1 + \2 + 1.
# N is a perfect square iff tail == T_1.
\1 # tail -= \1
(
\3xx # On iterations after the first, \3 = \3 + 2
|
x # On iterations after the first, \3 = 1
(?=\2$) # Assert that this is the first iteration, while
# simultaneously asserting that N is a perfect square,
# because if tail == \2 here, it will have equalled \2+1
# before this loop began.
)+ # Iterate the above any nonzero number of times
$ # Assert tail == 0
|
^x?$ # Allow us to match N=0 or N=1, which can't be matched above
Perl / Java / PCRE2 v10.34 or later / .NET, 33 bytes
((\2x|^x?)+)\1(\3xx|x(?=\2$)|^)+$
This drops 2 bytes at the cost of sacrificing support for PCRE1 and older versions of PCRE2, because they automatically force capture groups with nested backreference(s) to be atomic – which would prevent the ^x?
from ever matching \$0\$ instead of \$1\$, blocking \$1^4\$ from being matched.
If it did not need to match \$0^4\$, the regex would be 31 bytes.
Try it online! - Perl
Try it online! - Java
Attempt This Online! - PCRE2 v10.40+
Try it online! - .NET
Perl / Java / PCRE, 39 bytes
^((((\3|^x)xx)*)x?+)(?=(\1*)\2+$)\1*$\5
Try it online! - Perl
Try it online! - Java
Try it online! - PCRE1
Try it online! / Attempt This Online! - PCRE2
This is the same as the 40 byte regex below, but drops .NET support by using a possessive quantifier, x?+
instead of |^$
, to match \$0^4\$. This works because x?+
will always consume 1 x
if it can, and will only consume 0 x
s if \$\it\text{tail}=0\$, which can happen in two ways:
- If the input was \$0\$, in which case
\1
and\2
will have both captured \$0\$, and the second half of the regex will match. - If the input was nonzero, in which case
\2
will have captured the entire nonzero input value, making it impossible for the second half of the regex to match, as\2+
won't be able to match with \$\it\text{tail}=0\$.
Perl / Java / PCRE / .NET, 42 40 bytes
^((((\3|^x)xx)*)x)(?=(\1*)\2+$)\1*$\5|^$
Try it online! - Perl
Try it online! - Java
Try it online! - PCRE1
Try it online! / Attempt This Online! - PCRE2
Try it online! - .NET
This uses the equivalent of ^(\1xx|^x)*$
to match a perfect square using a nested backreference (made more complicated by needing to capture that square minus \$1\$), and then uses a square-testing algorithm which works thanks to the Chinese remainder theorem (which only requires ECMAScript or better, and is the reason for needing to capture the square minus \$1\$), to assert that the captured perfect square is the square root of the inputted number. The latter algorithm is explained in this post, and this exact square-testing regex is used in the second answer in this post.
^ # tail = N = input number
( # \1 = the largest perfect square for which the
# subsequent expression matches; tail -= \1
( # \2 = \1 - 1
( # \3 = nested backreference
# On the first iteration, \3 = 1 + 2 = 3;
# on subsequent iterations, \3 = \3 + 2
(\3|^x)
xx
)* # iterate \3 any number of times (may be zero); \2
# becomes the total of all these iterations
)
x # \1 = \2 + 1
)
# Assert that N == \1^2
(?=
(\1*)\2+$ # iff \1*\1 == N, the first match here must capture \5=0
)
\1*$\5 # assert \1 divides N-\1, and \5==0
|
^$ # Allow us to match N=0, which can't be matched above