Regex (ECMAScript), 194 171 167 164 155 145 142 107 bytes
-35 bytes (142 → 107) thanks to H.PWiz; this is a complete reimplementation. The primary sources of bytes saved are using tail = floor((tail + Digit) / 10) - Digit
instead of tail = floor(tail / 10)
, and "factoring" the assertions – reusing the expression at the end to mean two different things.
^(?!(x{1,9})(((?=(x*)((\1\4){9}x*))\5)*(?=(x*)(\7{9}$))\8\1|(?!\1*$))((?=(x*)((\1\10){9}x*))\11)*(x{10})*$)
Try it online!
Takes its input in unary, as a sequence of x
characters whose length represents the number.
This was an interesting challenge to solve without molecular lookahead or variable-length lookbehind. The restrictions result in a completely different algorithm being used.
Note that the above version does not reject numbers that have a zero as at least one of their digits, as per the challenge's rules. It costs 2 bytes 10 bytes to handle zero correctly, for a total of 144 117 bytes:
^(?!(x{0,9})(((?=(x*)((\1\4){9}x*))\5)*(?=(x*)(\7{9}$))\8\1|(?!\1*$))((?=(x*)((\1\10){9}x*))\11)*((x{10})+|(?!\1))$)x
Try it online!
The commented version below describes the 144 117 byte version:
^ # tail = N = input number
(?! # Negative lookahead - Assert that there's no way for the following to match:
(x{0,9}) # \1 = conjectured digit; tail -= \1
# Assert one of the following two alternatives:
(
# Assert that \1 occurs as a digit of N, and chop it away (and all the digits
# right of it) from tail, returning only the digits left of its found instance.
(
(?=
(x*)((\1\4){9}x*) # \4 = floor((tail + \1) / 10) - \1;
# \5 = tool to make tail = \4
)
\5 # tail = \4
)*
(?=
(x*)(\7{9}$) # assert tail % 10 == 0; \7 = tail / 10;
# \8 = tool to make tail = \7
)
\8\1 # tail = \7 - \1 (all the digits of N left of where \1
# was found, minus \1)
|
# Assert that \1 is not a divisor of N
(?!\1*$)
)
# Assert that \1 occurs as a digit in tail+\1, which will either be a second occurrence
# found further left in N of an already-found occurrence, or any occurrence of a digit
# that is not a divisor of N.
(
(?=
(x*)((\1\10){9}x*) # \10 = floor((tail + \1) / 10) - \1;
# \11 = tool to make tail = \10
)
\11 # tail = \10
)*
# Assert one of the following two alternatives:
(
# Assert tail > 0 and tail % 10 == 0, which means we've found an occurrence of \1
# that isn't the leftmost digit
(x{10})+
|
# Assert tail == 0 and tail != \1, which means we've found that \1 is the leftmost
# digit of N and \1 != 0 (which needs to be asserted, otherwise we'd always find
# a phantom occurrence of the digit 0 left of the leftmost digit of N)
(?!\1)
)$
)
x # Prevent N=0 from being matched
The version above is very slow, so I'm keeping my 144 byte solution here:
^(?!(?=(x*)\1{9})(x{0,9})(?=(x{10})*$|(.*(?=\1$)((?=(x*)(\6{9}x*))\7)*?\B(?=(x*)(\8{9}\2$))\9))((?!\2*$)|\4((?=(x*)(\12{9}x*))\13)*\2(x{10})*$))
Try it online!
^ # tail = N = input number
(?! # Negative lookahead - Assert that there's no way for the following to match:
(?=(x*)\1{9}) # \1 = floor(N / 10)
(x{0,9}) # \2 = take a guess at a numeral that may occur as any
# digit of N, even the rightmost one; tail -= \2
# Assert that \2 occurs as a digit in N, and if it does, identify the rest of N to
# the left of that digit, so that we can set tail equal to it later (so we can later
# assert that \2 is not duplicated).
(?=
(x{10})*$ # Skip the rest if we picked the rightmost digit of N
|
( # \4 = tool to make tail = \8
.*(?=\1$) # tail = \1
# Loop through chopping away each rightmost decimal digit of tail, one by one
(
(?=
(x*) # \6 = floor(tail / 10)
(\6{9}x*) # \7 = tool to make tail = \6
)
\7 # tail = \6
)*? # Only iterate until we find the first match of the
# following:
\B # Leave at least one digit remaining (to avoid
# thinking we've found a zero-digit when there isn't
# actually one)
(?=(x*)(\8{9}\2$)) # \8 = floor(tail/10); \9 = tool to make tail = \8;
# assert that tail % 10 == \2, i.e. that our guessed
# digit matches the current rightmost digit of tail
\9 # tail = \8
)
)
# \2 is now one of the decimal digits of N.
# Assert that either of the following two alternatives could match, and in the
# context outside of the negative lookahead, that neither of them can match:
(
# Assert tail is not divisible by \2. It doesn't matter that tail == N-\2
# instead of N, because that doesn't alter tail's divisibility by \2.
(?!\2*$)
| # or...
# Assert that \2 doesn't occur as any decimal digit left of where it was found
\4 # tail = \8 if \8 is set, otherwise no change; this
# relies on ECMAScript NPCG behavior.
# Chop away any number (minimum 0) of rightmost decimal digits of tail
(
(?=(x*)(\12{9}x*)) # \12 = floor(tail / 10);
# \13 = tool to make tail = \12
\13 # tail = \12
)* # Iterate any number of times, minimum 0
\2 # tail -= \2
(x{10})*$ # assert tail is divisible by 10
)
)
Regex (ECMAScript + (?*)
), 81 bytes
^(?!(?*(((?=(x*)(\3{9}(x*)))\4)+))((?!\5+$)|\1((?=(x*)(\8{9}x*))\9)*\5(x{10})*$))
This challenge is much easier to solve with molecular lookahead; it's the version I implemented first.
Working on this alerted me to the presence of a bug in my regex engine (now fixed).
^ # tail = N = input number
(?! # Negative lookahead - Assert that there's no way for the following to match:
(?* # Molecular lookahead - cycle through all possible matches of the following:
( # \1 = tool to make tail = the final value of \3
# Loop through chopping away each rightmost decimal digit of tail, one by one
(
(?=(x*)(\3{9}(x*))) # \3 = floor(tail / 10); \4 = tool to make tail = \3;
# \5 = tail % 10
\4 # tail = \3
)+ # Iterate at least once, otherwise \5 could be unset
# The above loop relies on ECMAScript no-empty-optional behavior. This
# prevents it from matching when the final result of zero is again divided
# by 10, which would yield a value of \5 = 0, making it look like one of N's
# digits was zero and preventing all values of N from matching.
)
)
# \5 is now one of the decimal digits of N, and tail==N again
# Assert that either of the following two alternatives could match, and in the context
# outside of the negative lookahead, that neither of them can match:
(
(?!\5+$) # Assert tail is not divisible by \5
| # or...
# Assert that the \5 doesn't occur as any decimal digit left of where it was found
\1 # tail = \3
# Chop away any number (minimum 0) of rightmost decimal digits of tail
(
(?=(x*)(\8{9}x*)) # \8 = floor(tail / 10); \9 = tool to make tail = \8;
\9 # tail = \8
)* # Iterate any number of times, minimum 0
\5 # tail -= \5
(x{10})*$ # assert tail is divisible by 10
)
)
Regex (ECMAScript 2018), 85 bytes
^(?!((?=(x*)(\2{9}(x*)))\3)+((?<=(?=(?!\4+$))^.*)|((?=(x*)(\7{9}x*))\8)*\4(x{10})*$))
Try it online!
This is a port of the molecular lookahead version. The most direct port would be replacing (?*A)B
with A(?<=(?=B)^.*)
, but that would result in an 89 byte regex.
^ # tail = N = input number
(?! # Negative lookahead - Assert that there's no way for the following to match:
# Loop through chopping away each rightmost decimal digit of tail, one by one
(
(?=(x*)(\2{9}(x*))) # \2 = floor(tail / 10); \3 = tool to make tail = \2;
# \4 = tail % 10
\3 # tail = \2
)+ # Iterate at least once, otherwise \4 could be unset
# The above loop relies on ECMAScript no-empty-optional behavior. This prevents it
# from matching when the final result of zero is again divided by 10, which would
# yield a value of \4 = 0, making it look like one of N's digits was zero and
# preventing all values of N from matching.
# \4 is now one of the decimal digits of N, and tail==\2
# Assert that either of the following two alternatives could match, and in the context
# outside of the negative lookahead, that neither of them can match:
(
(?<= # Variable-length lookbehind - used to go back to start
(?=
# now tail==N again
(?!\4+$) # Assert tail is not divisible by \4
)
^.* # Go back to start, then execute the above lookahead
)
| # or...
# now tail==\2
# Assert that the \4 doesn't occur as any decimal digit left of where it was found
# Chop away any number (minimum 0) of rightmost decimal digits of tail
(
(?=(x*)(\7{9}x*)) # \7 = floor(tail / 10); \8 = tool to make tail = \7;
\8 # tail = \7
)* # Iterate any number of times, minimum 0
\4 # tail -= \4
(x{10})*$ # assert tail is divisible by 10
)
)