žQÇεÑÅs¸˜2∍DT‹N59‹iPi©'WªëP®P-®„WMJëài`9-Dgi…9MWJ©NUë®NX-'M««]JR•4ñ4₄¬[Ý•5äŽ6þbT„WM‡5äøJ•XDŒ•27вǝεDg46N44‹è‹i©NUëW≠i®NX-ì'A«]"19M29MW"16ǝIÇƵPαè';ý1õ.;
Not too happy with the byte-count, but it could be worse..
Try it online or verify all ASCII characters.
Explanation:
I first tried to do the mapping of each printable ASCII character (integers in the range \$[32,126]\$) manually. With this, I've used the following strategy:
1) I've first determined all integers that can be formed by multiplying two single-digit integers:
(32) → 48W
# (35) → 57W
$ (36) → 66W
( (40) → 58W
* (42) → 67W
- (45) → 59W
0 (48) → 68W
1 (49) → 77W
6 (54) → 69W
8 (56) → 78W
? (63) → 79W
@ (64) → 88W
H (72) → 89W
Q (81) → 99W
We can add or subtract a single digit from each of these numbers to form all integers in the range \$[32,90]\$:
! (33) → [48W1M] / 257WA
" (34) → 48W2M / [157WA]
% (37) → [66W1M] / 358WA
& (38) → 66W2M / 258WA
' (39) → 66W3M / [158WA]
) (41) → 58W1M / 167WA
+ (43) → [67W1M] / 259WA
, (44) → 67W2M / [159WA]
. (46) → [59W1M] / 268WA
/ (47) → 59W2M / [168WA]
2 (50) → [77W1M] / 469WA
3 (51) → 77W2M / 369WA
4 (52) → 77W3M / 269WA
5 (53) → 77W4M / [169WA]
7 (55) → 69W1M / 178WA
9 (57) → [78W1M] / 679WA
: (58) → 78W2M / 579WA
; (59) → 78W3M / 479WA
< (60) → 78W4M / 379WA
= (61) → 78W5M / 279WA
> (62) → 78W6M / [179WA]
A (65) → [88W1M] / 789WA
B (66) → 88W2M / 689WA
C (67) → 88W3M / 589WA
D (68) → 88W4M / 489WA
E (69) → 88W5M / 389WA
F (70) → 88W6M / 289WA
G (71) → 88W7M / [189WA]
I (73) → [89W1M] / 799WA
J (74) → 89W2M / 799WA
K (75) → 89W3M / 699WA
L (76) → 89W4M / 599WA
M (77) → 89W5M / 499WA
N (78) → 89W6M / 399WA
O (79) → 89W7M / 299WA
P (80) → 89W8M / [199WA]
R (82) → 99W1M
S (83) → 99W2M
T (84) → 99W3M
U (85) → 99W4M
V (86) → 99W5M
W (87) → 99W6M
X (88) → 99W7M
Y (89) → 99W8M
Z (90) → 99W9M
3) When there are two options, we want to prioritize the ones containing a digit 1
(since those is already initially on the stack, and we could potentially remove later on). When one of the two numbers contains a 1
I've put it in square-blocks in the list above. If both or neither contain a 1
it's irrelevant which one we choose.
4) Now that everything below 91 is covered, we are left with the integers in the range \$[91,126]\$.
I first determined all integers which could be formed by one single-digit integer multiplied by a two-digit integer in the range \$[10,18]\$, since 18 being 9+9 is the highest we could create:
[ (91) → 794MW
` (96) → 697MW
b (98) → 795MW
c (99) → 992MW
f (102) → 698MW
h (104) → 894MW
i (105) → 796MW
l (108) → 699MW
p (112) → 797MW
u (117) → 994MW
w (119) → 798MW
~ (126) → 799MW
5) And there are also some additional integers which can be formed by using three single digits outside of the ones above, which are edge cases in my program:
d (100) → 455WW
x (120) → 869MW
y (121) → 358WW
} (125) → 555WW
6) After that, we can use a similar strategy as we did in steps 2 and 3:
\ (92) → [794MW1M] / 4268WWA
] (93) → 794MW2M / 3268WWA
^ (94) → 794MW3M / 2268WWA
_ (95) → 794MW4M / [1268WWA]
a (97) → 697MW1M / 1795MWA
e (101) → 455WW1M / 1698MWA
g (103) → 698MW1M / 1894MWA
j (106) → [796MW1M] / 2699MWA
k (107) → 796MW2M / [1699MWA]
m (109) → [699MW1M] / 3797MWA
n (110) → 699MW2M / 2797MWA *
o (111) → 699MW3M / [1797MWA]
q (113) → [797MW1M] / 4994MWA
r (114) → 797MW2M / 3994MWA
s (115) → 797MW3M / 2994MWA
t (116) → 797MW4M / [1994MWA]
v (118) → 994MW1M / 1798MWA
z (122) → [358WW1M] / 3555WWA
{ (123) → 358WW2M / 2555WWA
| (124) → 358WW3M / [1555WWA]
The one with *
behind it is the only edge case after all these steps, since 19M29MW
can be used that contains a 1, so is potentially shorter than both 699MW2M
and 2797MWA
.
As for the actual code to implement these steps and encode the input:
žQ # Push all printable ASCII characters
Ç # Convert it to an integer list in the range [32,127]
ε # Map each integer to:
Ñ # Get the divisors of the current integer
Ås # Get the middle (if it's a square) or middle two integers
¸˜2∍ # If it's a square, duplicate that integer
# (so we only have pairs of integers now)
D # Duplicate the pair
T‹ # Check for both whether they're a single digit (<10):
y91‹i # If the integer we're mapping over is below 91:
Pi # If both values in the pair are a single digit:
© # Store the pair in variable `®` (without popping)
'Wª '# And append a "W" to the list
ë # Else:
P # Pop and take the product of the duplicated pair
®P # Take the product of pair `®` as well
- # Subtract them from one another
® # Push pair `®`
„WM # Push string "WM"
J # Join the pair together, and then all three together
ë # Else (the integer we're mapping over is above 90):
ài # If either of the two integers is a single digit:
` # Push both separated to the stack
9- # Subtract 9 from the larger 2-digit integer
Dgi # If it's now a single digit:
…9MW # Push string "9MW"
J # And join all three together
© # Store this in variable `®` (without popping)
NU # And store the map-index in variable `X`
ë # Else:
® # Push the string from variable `®`
N # Push the current map-index
X- # Subtract the saved map-index of `X`
'M '# Push an "M"
«« # Join the three together
] # Close all if-statements and map
J # Join each inner list together to a single string
We now have the following list:
["48W","148WM","248WM","57W","66W","166WM","266WM","366WM","58W","158WM","67W","167WM","267WM","59W","159WM","259WM","68W","77W","177WM","277WM","377WM","477WM","69W","169WM","78W","178WM","278WM","378WM","478WM","578WM","678WM","79W","88W","188WM","288WM","388WM","488WM","588WM","688WM","788WM","89W","189WM","289WM","389WM","489WM","589WM","689WM","789WM","889WM","99W","199WM","299WM","399WM","499WM","599WM","699WM","799WM","899WM","999WM","749MW","749MW1M","749MW2M","749MW3M","749MW4M","839MW","839MW1M","759MW","929MW","1010","929MW2M","689MW","689MW1M","849MW","769MW","769MW1M","769MW2M","939MW","939MW1M","1011","939MW3M","859MW","859MW1M","859MW2M","859MW3M","859MW4M","949MW","949MW1M","789MW","1012","1111","789MW3M","789MW4M","789MW5M","789MW6M","959MW"]
As you can see, it contains some errors like "1010","1011","1012","1111"
for the edge cases [100,110,120,121] respectively as discussed earlier, which could be "455WW","869MW","358WW","555WW"
instead; "789MW6M"
for 125, which could be "555WW"
instead; and "789MW3M"
for 122, which could be "358WW1M"
instead. So we fix those edge cases manually (except for "1011"
since we already fix that edge case at the very end anyway):
R # Reverse the list
•4ñ4₄¬[Ý• # Push compressed integer 1358555358869455
5ä # Split it into 5 equal-size parts: [1358,555,358,869,455]
Ž6þ # Push compressed integer 1783
b # Convert it to binary: 11011110111
T„WM‡ # Transliterate "10" to "WM": "WWMWWWWMWWW"
5ä # Also split it into 5 equal-size parts:
# ["WWM","WW","WW","MW","WW"]
ø # Pair the two lists together
J # And join each inner pair together:
# ["1358WWM","555WW","358WW","869MW","455WW"]
•XDŒ• # Push compressed integer 2149280
27в # Convert it to base-27 as list: [4,1,5,6,26]
ǝ # Insert the strings at those indices in the reversed list
Then we'll change every case with ...2M
or higher to x...A
:
ε # Map each string to:
Dg # Get the length (without popping by duplicating first)
N44‹ # Check if the map-index is below 44
# (1 if truthy; 0 if falsey)
46 è # Use it to index into 46 (6 if truthy; 4 if falsey)
‹i # If the length is smaller than that:
© # Store the current string in variable `®`
NU # And the map-index in variable `X`
ë # Else:
W # Get the smallest digit (without popping)
≠i # If it's NOT 1:
® # Push the string of variable `®`
NX- # Push the map-index and subtract `X`
ì # Prepend this digit in front of the string
'A« '# And append an "A"
] # Close the if-statements and map
We now have the following (still reversed!) list:
["959MW","555WW","1555WWA","2555WWA","1358WWM","358WW","869MW","789MW","949MW1M","949MW","1949MWA","2949MWA","3949MWA","859MW1M","859MW","1859MWA","1011","939MW1M","939MW","1939MWA","769MW1M","769MW","849MW","689MW1M","689MW","1689MWA","455WW","929MW","759MW","839MW1M","839MW","1839MWA","2839MWA","3839MWA","749MW1M","749MW","999WM","899WM","799WM","699WM","599WM","499WM","399WM","299WM","199WM","99W","199WA","299WA","399WA","499WA","599WA","699WA","799WA","189WM","89W","189WA","289WA","389WA","489WA","589WA","689WA","188WM","88W","79W","179WA","279WA","379WA","479WA","579WA","178WM","78W","169WM","69W","169WA","269WA","369WA","177WM","77W","68W","168WA","159WM","59W","159WA","167WM","67W","158WM","58W","158WA","258WA","166WM","66W","57W","157WA","148WM","48W"]
Fix the final test case as discussed earlier:
"19M29MW" # Push string "19M29MW"
16ǝ # And insert it at index 16
And then convert the input using this finished (still reversed) list:
I # Push the input-string
Ç # Convert each character to its codepoint integer
ƵP # Push compressed integer 126
α # Take its absolute difference with each codepoint integer
è # Index those into the list we created
';ý '# Join them together with a ";" delimiter
1õ.; # And remove the very first "1"
# (after which the result is output implicitly)
See this 05AB1E tip of mine (sections How to compress large integers? and How to compress integer lists?) to understand why •4ñ4₄¬[Ý•
is 1358555358869455
; Ž6þ
is 1783
; •XDŒ•
is 2149280
; •XDŒ•27в
is [4,1,5,6,26]
; and ƵP
is 126
.
79
can still be done with just multiply and addition:98W7M
. \$\endgroup\$94W1M3W;
so no. Other than that, it does outputo
\$\endgroup\$xyA
returnx-y
,y-x
or|x-y|
? DoesxyP
returnx//y
ory//x
? \$\endgroup\$