13
\$\begingroup\$

Write the shortest program or function that mimics the addition in this XKCD strip:

enter image description here

Input

Two positive decimal integers containing only the digits 150.

Output

The positive decimal integer result of adding the two inputs together in the manner shown above. This output should also only have the digits 150.

Specification

Input and Output

Input and output are numbers only containing digits 150.

Roman Numerals

This challenge will use Standard Form numerals:

Thousands Hundreds Tens Units
1 M C X I
2 MM CC XX II
3 MMM CCC XXX III
4 MMMM CD XL IV
5 MMMMM D L V
6 MMMMMM DC LX VI
7 MMMMMMM DCC LXX VII
8 MMMMMMMM DCCC LXXX VIII
9 MMMMMMMMM CM XC IX

Note the subtractive notation on multiples of 40 and 90, and additive notation on multiples of 1000.

Worked Example

Consider a test case below, 1010111 + 101051 = 1050110 (23 + 26 = 49).

Convert the inputs to Roman numerals, then to Arabic numerals:

1010111 => XXIII => 23
101051  => XXVI  => 26

Perform the addition:

23 + 26 => 49

Convert the output back to Roman numerals, then for each Roman numeral substitute its Arabic numeral value:

49 => XLIX => 10 50 1 10 => 1050110

Test Cases

A + B = C (explanation)
=========
1 + 1 = 11 (1 + 1 = 2)
11 + 11 = 15 (2 + 2 = 4 == IV)
15 + 5 = 110 (4 + 5 = 9 == IX)
101010 + 1 = 1010101 (30 + 1 = 31)
1010 + 111 = 1010111 (20 + 3 = 23)
1010 + 15 = 101015 (20 + 4 = 24)
1010 + 5 = 10105 (20 + 5 = 25)
1010111 + 101051 = 1050110 (23 + 26 = 49)
101015 + 51 = 101010 (24 + 6 = 30)
1010015 + 15 = 101005111 (94 + 4 = 98)
100010010010101015 + 1000100010010010010505 = 100010001000500501010110 (1234 + 2345 = 3579)
100010010010101015 + 1000100010001000100010001000100050010010050105 = 100010001000100010001000100010001000100100010100110 (1234 + 8765 = 9999)

Other Notes

As seen in the comic, Randall is doing a simple substitution of Roman numerals with their decimal equivalents:

  • I => 1
  • V => 5
  • X => 10

Presumably, this will be extended for all Roman numerals (up to 1000):

  • L => 50
  • C => 100
  • D => 500
  • M => 1000

For more information, see the ExplainXKCD wiki page.

This is , so shortest code wins!

\$\endgroup\$
8
  • 1
    \$\begingroup\$ Based on the same XKCD: ro1000an nu1000era50 en100o501ng \$\endgroup\$
    – bigyihsuan
    Commented Jun 27, 2022 at 13:22
  • \$\begingroup\$ @Arnauld, Steffan yeah that testcase had two extra zeroes, fixed now \$\endgroup\$
    – bigyihsuan
    Commented Jun 27, 2022 at 15:43
  • \$\begingroup\$ @Arnauld thanks for letting me know, these test case issues should've been caught in sandbox but weren't somehow? \$\endgroup\$
    – bigyihsuan
    Commented Jun 27, 2022 at 16:20
  • \$\begingroup\$ The last test case is wrong too. 100010001000100010001000100010001010010050105 is MMMMMMMMXCCLXV, but it should be MMMMMMMMDCCLXV (1000100010001000100010001000100050010010050105). \$\endgroup\$
    – naffetS
    Commented Jun 27, 2022 at 16:30
  • \$\begingroup\$ May we take the string sum as input instead of 2 separate arguments? (e.g. "1010+15") \$\endgroup\$
    – Arnauld
    Commented Jun 28, 2022 at 15:27

4 Answers 4

3
\$\begingroup\$

Vyxal, 21 bytes

ƛ`.0*`Ẏ⌊øṘṅøṘ;∑øṘvøṘṅ

Try it Online!

How?

ƛ`.0*`Ẏ⌊øṘṅøṘ;∑øṘvøṘṅ
ƛ                      # Map, and for both strings:
 `.0*`Ẏ                #  Get all regex matches ".0*"
       ⌊               #  Convert each to integers
        øṘ             #  Convert each to their roman numeral
          ṅ            #  Join together to a single string
           øṘ          #  Convert from roman numeral to number
             ;         # Close map
              ∑        # Sum
               øṘ      # Convert to roman numeral
                 vøṘ   # Convert each character from roman numeral to number
                    ṅ  # Join together to a string
\$\endgroup\$
0
2
\$\begingroup\$

Factor + roman, 117 114 bytes

[ [ R/ .0*/ all-matching-subseqs [ dec> >roman ] f map-as concat ] bi@
roman+ 1 group [ roman> >dec ] map-concat ]

Needs modern Factor for >dec, but here's a version that works on ATO for 3 more bytes:

Attempt This Online!

-3 bytes thanks to @Steffan

\$\endgroup\$
1
  • \$\begingroup\$ You can use .0* instead of [15]0* \$\endgroup\$
    – naffetS
    Commented Jun 27, 2022 at 15:25
1
\$\begingroup\$

JavaScript (ES6), 204 bytes

Expects (a)(b), where both arguments are strings.

a=>b=>(h=(n,k)=>n?h(n/10|0,k+0)+[k%8?[,k,d=k+k,d+k,k+(x=k*5),x,x+k,x+d,x+d+k,d+0][n%10]:k.repeat(n)]:'')((g=s=>s.replace(p=/.0*/g,s=>s-p?(t+=s<p?n:s-n,p=s<p?n=+s:0/(n=0)):n+=p=+s,t=n=0)&&t+n)(a)+g(b),'1')

Try it online!

Parsing the input strings

g = s =>                    // s = input string
s.replace(                  // search in s:
  p = /.0*/g,               //   each non-zero digit followed by some 0's
  s =>                      //   given this substring s:
  s - p ?                   //     if s is not equal to the previous match:
    (                       //
      t +=                  //       add to t:
        s < p ? n           //         either n if s < p
              : s - n,      //         or s - n otherwise (e.g. 1 10 -> 9)
      p =                   //       set p to:
        s < p ? n = +s      //         s (also loaded in n) if s < p
              : 0 / (n = 0) //         or NaN otherwise (and set n to 0)
    )                       //
  :                         //     else:
    n +=                    //       add s to the accumulator n
      p = +s,               //       and copy s in p
  t = n = 0                 //   start with t = n = 0
)                           // end of replace()
&& t + n                    // return the sum of t and the accumulator

Formatting the output

h = (n, k) =>               // n = number to convert, k = current prefix
n ?                         // if n is not equal to 0:
  h(                        //   do a recursive call with:
    n / 10 | 0,             //     floor(n / 10)
    k + 0                   //     a '0' appended to k
  ) +                       //   end of recursive call
  [                         //   append:
    k % 8 ?                 //     if k is not equal to '1000':
      [ ,                   //       0 : nothing
        k,                  //       1 : k           --> e.g. '10'
        d = k + k,          //       2 : k+k         --> '1010'
        d + k,              //       3 : k+k+k       --> '101010'
        k + (x = k * 5),    //       4 : k+(5*k)     --> '1050'
        x,                  //       5 : 5*k         --> '50'
        x + k,              //       6 : (5*k)+k     --> '5010'
        x + d,              //       7 : (5*k)+k+k   --> '501010'
        x + d + k,          //       8 : (5*k)+k+k+k --> '50101010'
        d + 0               //       9 : k+k+0       --> '10100'
      ][n % 10]             //       select the correct entry
    :                       //     else:
      k.repeat(n)           //       just repeat '1000' n times
  ]                         //
:                           // else:
  ''                        //   stop the recursion
\$\endgroup\$
2
  • \$\begingroup\$ 157 bytes \$\endgroup\$
    – tsh
    Commented Jun 28, 2022 at 5:54
  • \$\begingroup\$ @tsh That's a really neat idea. I'll update my answer later when/if the OP confirms that it's a valid input format. \$\endgroup\$
    – Arnauld
    Commented Jun 28, 2022 at 15:28
1
\$\begingroup\$

05AB1E, 24 23 20 bytes

εDSĀÅ¡J.XJ.v}O.XS.vJ

-3 bytes thanks to @CommandMaster

Try it online or verify all test cases.

Original 24 23 bytes answer:

X3FD5*x}r)D.X:.vO.XS.vJ

ASCII only. :)

Try it online or verify all test cases.

Explanation:

ε            # Map over both integers in the (implicit) input-pair:
 D           #  Duplicate the current integer
  S          #  Convert the copy to a list of digits
   Ā         #  Check for each digit that it's NOT 0 (0 if 0; 1 otherwise)
    Å¡       #  Split the number on the truthy (==1) values
      J      #  Join each inner list together
       .X    #  Convert each from an integer to a Roman Numeral
         J   #  Join it together to a single string
          .v # Convert it from a Roman Numeral to an integer
}O           # After the map: sum the two integers together
  .X         # Convert this sum back to a Roman Numeral
    S        # Convert it to a list of characters
     .v      # Convert each character from a Roman Numeral back to an integer
       J     # Join them together
             # (after which the result is output implicitly)
X3FD5*x}r)   # Push list [1000,500,100,50,10,5,1]:
X            #  Push 1
 3F          #  Loop 3 times:
   D         #   Duplicate the current value
    5*       #   Multiply the copy by 5
      x      #   Double it (without popping)
       }r    #  After the loop: reverse all values on the stack
         )   #  Then wrap the entire stack into a list
D            # Duplicate this list
 .X          # Convert each to a Roman Numeral: ["M","C","D","L","T","V","I"]
   :         # Replace all integers to these characters in the (implicit) input-pair
.v           # Convert both strings from Roman Numerals to integers
  O.XS.vJ    # Same as above
\$\endgroup\$
1
  • 2
    \$\begingroup\$ You can split on non-zero digits instead of creating the list of all values, for a -3: εD€ĀÅ¡J.XJ.v}O.XS.vJ Try it online! (although you lose the ASCII-onlyness) \$\endgroup\$ Commented Jun 30, 2022 at 15:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.