Match Roman Numerals Announcing the arrival of Valued Associate #679: Cesar Manara Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern) The PPCG Site design is on its way - help us make it awesome! Sandbox for Proposed ChallengesCovering a Skyline with brush strokesSwap the EndiannessDetermine the “Luck” of a stringTips for golfing in 05AB1ECollapsing numbersQuine outputs itself in binaryMulti-level marketing “legs” investment ruleRoman numeral converter functionOptimal short-hand roman numeral generatorCount from 1 to 100… in Roman NumeralsCounting rods; count your rodsNumbers as Circular GraphicsAmbiguous Roman Numeral Magic SquaresConvert ciphered Roman numerals to Arabic decimalsInteger goes back and forth through timeLook-and-say sequence: roman numerals editionAm I a Self Number?When in Rome, Count as Romans do?

How do you clear the ApexPages.getMessages() collection in a test?

How should I respond to a player wanting to catch a sword between their hands?

How to politely respond to generic emails requesting a PhD/job in my lab? Without wasting too much time

I'm having difficulty getting my players to do stuff in a sandbox campaign

How to stop my camera from exagerrating differences in skin colour?

Cauchy Sequence Characterized only By Directly Neighbouring Sequence Members

What do you call a plan that's an alternative plan in case your initial plan fails?

What is the electric potential inside a point charge?

Geometric mean and geometric standard deviation

Can I throw a longsword at someone?

If I can make up priors, why can't I make up posteriors?

Why does tar appear to skip file contents when output file is /dev/null?

Statistical model of ligand substitution

Estimate capacitor parameters

If A makes B more likely then B makes A more likely"

Active filter with series inductor and resistor - do these exist?

Should you tell Jews they are breaking a commandment?

Working around an AWS network ACL rule limit

What are the performance impacts of 'functional' Rust?

Jazz greats knew nothing of modes. Why are they used to improvise on standards?

What to do with post with dry rot?

Replacing HDD with SSD; what about non-APFS/APFS?

How to rotate it perfectly?

Slither Like a Snake



Match Roman Numerals



Announcing the arrival of Valued Associate #679: Cesar Manara
Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern)
The PPCG Site design is on its way - help us make it awesome!
Sandbox for Proposed ChallengesCovering a Skyline with brush strokesSwap the EndiannessDetermine the “Luck” of a stringTips for golfing in 05AB1ECollapsing numbersQuine outputs itself in binaryMulti-level marketing “legs” investment ruleRoman numeral converter functionOptimal short-hand roman numeral generatorCount from 1 to 100… in Roman NumeralsCounting rods; count your rodsNumbers as Circular GraphicsAmbiguous Roman Numeral Magic SquaresConvert ciphered Roman numerals to Arabic decimalsInteger goes back and forth through timeLook-and-say sequence: roman numerals editionAm I a Self Number?When in Rome, Count as Romans do?










17












$begingroup$


Challenge



Given some input string, return a truthy value if it represents a correct roman numeral between 1 (=I) and 3999 (=MMMCMXCIX), and a falsey value otherwise.



Details



  • The input is a non-empty string that only comprises the characters IVXLCDM.

  • The roman numerals (that we use here in this challenge) are defined as follows:

We use only following symbols:



Symbol I V X L C D M
Value 1 5 10 50 100 500 1000


To define which strings are actually valid roman numerals, it is probably easiest to provide the rule of conversation: To write a decimal number a3 a2 a1 a0 (where each ai represents one digit. So for example to represent 792 we have a3=0, a2=7, a1=9, a0=2.) as a roman numeral, we decompose it into the power of tens. The different powers of ten can be written as follows:



 1-9: I, II, III, IV, V, VI, VII, VIII, IX
10-90: X, XX, XXX, XL, L, LX, LXX, LXXX, XC
100-900: C, CC, CCC, CD, D, DC, DCC, DCCC, CM
1000-3000: M, MM, MMM


Beginning at the left side with the most significant digit of the, we can convert the number that each digit represents separately and concatenate them. So for the example from above this would look like so:



Digit a3 a2 a1 a0
Decimal 0 7 9 2
Roman DCC XC II


Therefore the roman numeral for 792 is DCCXCII. Here is a full list of all roman numerals that are relevant for this challenge: OEIS a006968.txt



Examples



Truthy



MCCXXXIV (1234)
CMLXXXVIII (988)
DXIV (514)
CI (101)


Falsey



MMIXVIII
IVX
IXV
MMMM
XXXVX
IVI
VIV











share|improve this question











$endgroup$











  • $begingroup$
    Subset of this conversion challenge.
    $endgroup$
    – Shaggy
    Apr 11 at 13:03










  • $begingroup$
    I still don't think this qualifies as a "subset" as the set of invalid inputs is larger. This challenge here only refers to the "well"-defined numbers that are used in OEIS A006968
    $endgroup$
    – flawr
    Apr 11 at 13:59







  • 2




    $begingroup$
    Why is MMMM invalid? Is there a letter for 5000 that should be used instead for M<letter>?
    $endgroup$
    – Skyler
    2 days ago










  • $begingroup$
    Check out the specs, there is no such letter. The only symbols used are I,V,X,L,C,D,M.
    $endgroup$
    – flawr
    2 days ago
















17












$begingroup$


Challenge



Given some input string, return a truthy value if it represents a correct roman numeral between 1 (=I) and 3999 (=MMMCMXCIX), and a falsey value otherwise.



Details



  • The input is a non-empty string that only comprises the characters IVXLCDM.

  • The roman numerals (that we use here in this challenge) are defined as follows:

We use only following symbols:



Symbol I V X L C D M
Value 1 5 10 50 100 500 1000


To define which strings are actually valid roman numerals, it is probably easiest to provide the rule of conversation: To write a decimal number a3 a2 a1 a0 (where each ai represents one digit. So for example to represent 792 we have a3=0, a2=7, a1=9, a0=2.) as a roman numeral, we decompose it into the power of tens. The different powers of ten can be written as follows:



 1-9: I, II, III, IV, V, VI, VII, VIII, IX
10-90: X, XX, XXX, XL, L, LX, LXX, LXXX, XC
100-900: C, CC, CCC, CD, D, DC, DCC, DCCC, CM
1000-3000: M, MM, MMM


Beginning at the left side with the most significant digit of the, we can convert the number that each digit represents separately and concatenate them. So for the example from above this would look like so:



Digit a3 a2 a1 a0
Decimal 0 7 9 2
Roman DCC XC II


Therefore the roman numeral for 792 is DCCXCII. Here is a full list of all roman numerals that are relevant for this challenge: OEIS a006968.txt



Examples



Truthy



MCCXXXIV (1234)
CMLXXXVIII (988)
DXIV (514)
CI (101)


Falsey



MMIXVIII
IVX
IXV
MMMM
XXXVX
IVI
VIV











share|improve this question











$endgroup$











  • $begingroup$
    Subset of this conversion challenge.
    $endgroup$
    – Shaggy
    Apr 11 at 13:03










  • $begingroup$
    I still don't think this qualifies as a "subset" as the set of invalid inputs is larger. This challenge here only refers to the "well"-defined numbers that are used in OEIS A006968
    $endgroup$
    – flawr
    Apr 11 at 13:59







  • 2




    $begingroup$
    Why is MMMM invalid? Is there a letter for 5000 that should be used instead for M<letter>?
    $endgroup$
    – Skyler
    2 days ago










  • $begingroup$
    Check out the specs, there is no such letter. The only symbols used are I,V,X,L,C,D,M.
    $endgroup$
    – flawr
    2 days ago














17












17








17


2



$begingroup$


Challenge



Given some input string, return a truthy value if it represents a correct roman numeral between 1 (=I) and 3999 (=MMMCMXCIX), and a falsey value otherwise.



Details



  • The input is a non-empty string that only comprises the characters IVXLCDM.

  • The roman numerals (that we use here in this challenge) are defined as follows:

We use only following symbols:



Symbol I V X L C D M
Value 1 5 10 50 100 500 1000


To define which strings are actually valid roman numerals, it is probably easiest to provide the rule of conversation: To write a decimal number a3 a2 a1 a0 (where each ai represents one digit. So for example to represent 792 we have a3=0, a2=7, a1=9, a0=2.) as a roman numeral, we decompose it into the power of tens. The different powers of ten can be written as follows:



 1-9: I, II, III, IV, V, VI, VII, VIII, IX
10-90: X, XX, XXX, XL, L, LX, LXX, LXXX, XC
100-900: C, CC, CCC, CD, D, DC, DCC, DCCC, CM
1000-3000: M, MM, MMM


Beginning at the left side with the most significant digit of the, we can convert the number that each digit represents separately and concatenate them. So for the example from above this would look like so:



Digit a3 a2 a1 a0
Decimal 0 7 9 2
Roman DCC XC II


Therefore the roman numeral for 792 is DCCXCII. Here is a full list of all roman numerals that are relevant for this challenge: OEIS a006968.txt



Examples



Truthy



MCCXXXIV (1234)
CMLXXXVIII (988)
DXIV (514)
CI (101)


Falsey



MMIXVIII
IVX
IXV
MMMM
XXXVX
IVI
VIV











share|improve this question











$endgroup$




Challenge



Given some input string, return a truthy value if it represents a correct roman numeral between 1 (=I) and 3999 (=MMMCMXCIX), and a falsey value otherwise.



Details



  • The input is a non-empty string that only comprises the characters IVXLCDM.

  • The roman numerals (that we use here in this challenge) are defined as follows:

We use only following symbols:



Symbol I V X L C D M
Value 1 5 10 50 100 500 1000


To define which strings are actually valid roman numerals, it is probably easiest to provide the rule of conversation: To write a decimal number a3 a2 a1 a0 (where each ai represents one digit. So for example to represent 792 we have a3=0, a2=7, a1=9, a0=2.) as a roman numeral, we decompose it into the power of tens. The different powers of ten can be written as follows:



 1-9: I, II, III, IV, V, VI, VII, VIII, IX
10-90: X, XX, XXX, XL, L, LX, LXX, LXXX, XC
100-900: C, CC, CCC, CD, D, DC, DCC, DCCC, CM
1000-3000: M, MM, MMM


Beginning at the left side with the most significant digit of the, we can convert the number that each digit represents separately and concatenate them. So for the example from above this would look like so:



Digit a3 a2 a1 a0
Decimal 0 7 9 2
Roman DCC XC II


Therefore the roman numeral for 792 is DCCXCII. Here is a full list of all roman numerals that are relevant for this challenge: OEIS a006968.txt



Examples



Truthy



MCCXXXIV (1234)
CMLXXXVIII (988)
DXIV (514)
CI (101)


Falsey



MMIXVIII
IVX
IXV
MMMM
XXXVX
IVI
VIV








code-golf string number decision-problem roman-numerals






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 2 days ago







flawr

















asked Apr 11 at 12:43









flawrflawr

27.1k667190




27.1k667190











  • $begingroup$
    Subset of this conversion challenge.
    $endgroup$
    – Shaggy
    Apr 11 at 13:03










  • $begingroup$
    I still don't think this qualifies as a "subset" as the set of invalid inputs is larger. This challenge here only refers to the "well"-defined numbers that are used in OEIS A006968
    $endgroup$
    – flawr
    Apr 11 at 13:59







  • 2




    $begingroup$
    Why is MMMM invalid? Is there a letter for 5000 that should be used instead for M<letter>?
    $endgroup$
    – Skyler
    2 days ago










  • $begingroup$
    Check out the specs, there is no such letter. The only symbols used are I,V,X,L,C,D,M.
    $endgroup$
    – flawr
    2 days ago

















  • $begingroup$
    Subset of this conversion challenge.
    $endgroup$
    – Shaggy
    Apr 11 at 13:03










  • $begingroup$
    I still don't think this qualifies as a "subset" as the set of invalid inputs is larger. This challenge here only refers to the "well"-defined numbers that are used in OEIS A006968
    $endgroup$
    – flawr
    Apr 11 at 13:59







  • 2




    $begingroup$
    Why is MMMM invalid? Is there a letter for 5000 that should be used instead for M<letter>?
    $endgroup$
    – Skyler
    2 days ago










  • $begingroup$
    Check out the specs, there is no such letter. The only symbols used are I,V,X,L,C,D,M.
    $endgroup$
    – flawr
    2 days ago
















$begingroup$
Subset of this conversion challenge.
$endgroup$
– Shaggy
Apr 11 at 13:03




$begingroup$
Subset of this conversion challenge.
$endgroup$
– Shaggy
Apr 11 at 13:03












$begingroup$
I still don't think this qualifies as a "subset" as the set of invalid inputs is larger. This challenge here only refers to the "well"-defined numbers that are used in OEIS A006968
$endgroup$
– flawr
Apr 11 at 13:59





$begingroup$
I still don't think this qualifies as a "subset" as the set of invalid inputs is larger. This challenge here only refers to the "well"-defined numbers that are used in OEIS A006968
$endgroup$
– flawr
Apr 11 at 13:59





2




2




$begingroup$
Why is MMMM invalid? Is there a letter for 5000 that should be used instead for M<letter>?
$endgroup$
– Skyler
2 days ago




$begingroup$
Why is MMMM invalid? Is there a letter for 5000 that should be used instead for M<letter>?
$endgroup$
– Skyler
2 days ago












$begingroup$
Check out the specs, there is no such letter. The only symbols used are I,V,X,L,C,D,M.
$endgroup$
– flawr
2 days ago





$begingroup$
Check out the specs, there is no such letter. The only symbols used are I,V,X,L,C,D,M.
$endgroup$
– flawr
2 days ago











15 Answers
15






active

oldest

votes


















14












$begingroup$


Verbose, 1362 bytes



GET A ROMAN NUMERAL TYPED IN BY THE CURRENT PERSON USING THIS PROGRAM AND PUT IT ONTO THE TOP OF THE PROGRAM STACK
PUT THE NUMBER MMMM ONTO THE TOP OF THE PROGRAM STACK
MOVE THE FIRST ELEMENT OF THE PROGRAM STACK TO THE SECOND ELEMENT'S PLACE AND THE SECOND ELEMENT OF THE STACK TO THE FIRST ELEMENT'S PLACE
DIVIDE THE FIRST ELEMENT OF THE PROGRAM STACK BY THE SECOND ELEMENT OF THE PROGRAM STACK AND PUT THE RESULT ONTO THE TOP OF THE PROGRAM STACK
PUT THE NUMBER V ONTO THE TOP OF THE PROGRAM STACK
GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
PUT THE NUMBER I ONTO THE TOP OF THE PROGRAM STACK
GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE
PUT THE NUMBER III ONTO THE TOP OF THE PROGRAM STACK
GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
PUT THE NUMBER NULLA ONTO THE TOP OF THE PROGRAM STACK
GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE


Outputs I for valid roman numerals in the range I-MMMCMXCIX and NULLA (0) or informs user input is not a valid roman numeral otherwise.






share|improve this answer









$endgroup$








  • 12




    $begingroup$
    I can't decide if this is the right tool for the job or not.
    $endgroup$
    – Vaelus
    Apr 11 at 17:57






  • 5




    $begingroup$
    Is this the right tool for any job?
    $endgroup$
    – omzrs
    Apr 11 at 19:39


















8












$begingroup$


C# (Visual C# Interactive Compiler), 79 109 bytes



This seems like a Regex challenge, I'm sure a shorter solution can be found...



s=>System.Text.RegularExpressions.Regex.IsMatch(s,"^M0,3(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)$")


Try it online!






share|improve this answer











$endgroup$












  • $begingroup$
    Couldn't you shorten 0,3 to ,3?
    $endgroup$
    – flawr
    Apr 11 at 13:44










  • $begingroup$
    @flawr doesn't seem to capture anything then
    $endgroup$
    – Innat3
    Apr 11 at 13:55






  • 1




    $begingroup$
    Ah sorry, only things like 5, work, but not ,5.
    $endgroup$
    – flawr
    Apr 11 at 14:01






  • 2




    $begingroup$
    You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
    $endgroup$
    – Kevin Cruijssen
    Apr 11 at 20:07







  • 3




    $begingroup$
    Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
    $endgroup$
    – Embodiment of Ignorance
    Apr 11 at 20:30



















8












$begingroup$


Wolfram Language (Mathematica), 35 bytes



Check[FromRomanNumeral@#<3999,1<0]&


Try it online!



5 bytes saved, thanks to @attinat



the limitation [1,3999] unfortunateley costs 7 bytes...

here is the code for any roman number




Wolfram Language (Mathematica), 28 bytes



Check[FromRomanNumeral@#,F]&


Try it online!



the above code works for any number, not just [1,3999]






share|improve this answer











$endgroup$








  • 2




    $begingroup$
    @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
    $endgroup$
    – mathmandan
    Apr 11 at 17:31











  • $begingroup$
    35 bytes. Boole is also shorter (by one byte) than using If in that way.
    $endgroup$
    – attinat
    Apr 11 at 19:28


















8












$begingroup$


CP-1610 assembly (Intellivision),  52 ... 48  47 DECLEs1 = 59 bytes



Let's try this on a system that predates Perl by a good 7 years. :-)



Takes a pointer to a null-terminated string in R4. Sets the Zero flag if the input is a valid Roman numeral, or clears it otherwise.



 ROMW 10 ; use 10-bit ROM width
ORG $4800 ; map this program at $4800

;; ------------------------------------------------------------- ;;
;; test code ;;
;; ------------------------------------------------------------- ;;
4800 EIS ; enable interrupts

4801 SDBD ; R5 = pointer into test case index
4802 MVII #ndx, R5
4805 MVII #$214, R3 ; R3 = backtab pointer
4807 MVII #11, R0 ; R0 = number of test cases

4809 loop SDBD ; R4 = pointer to next test case
480A MVI@ R5, R4
480B PSHR R0 ; save R0, R3, R5 onto the stack
480C PSHR R3
480D PSHR R5
480E CALL isRoman ; invoke our routine
4811 PULR R5 ; restore R5 and R3
4812 PULR R3

4813 MVII #$1A7, R0 ; use a white 'T' by default
4815 BEQ disp

4817 MVII #$137, R0 ; or a white 'F' is the Z flag was cleared

4819 disp MVO@ R0, R3 ; draw it
481A INCR R3 ; increment the backtab pointer

481B PULR R0 ; restore R0
481C DECR R0 ; and advance to the next test case, if any
481D BNEQ loop

481F DECR R7 ; loop forever

;; ------------------------------------------------------------- ;;
;; test cases ;;
;; ------------------------------------------------------------- ;;
4820 ndx BIDECLE test0, test1, test2, test3
4828 BIDECLE test4, test5, test6, test7, test8, test9, test10

; truthy
4836 test0 STRING "MCCXXXIV", 0
483F test1 STRING "CMLXXXVIII", 0
484A test2 STRING "DXIV", 0
484F test3 STRING "CI", 0

; falsy
4852 test4 STRING "MMIXVIII", 0
485B test5 STRING "IVX", 0
485F test6 STRING "IXV", 0
4863 test7 STRING "MMMM", 0
4868 test8 STRING "XXXVX", 0
486E test9 STRING "IVI", 0
4872 test10 STRING "VIV", 0

;; ------------------------------------------------------------- ;;
;; routine ;;
;; ------------------------------------------------------------- ;;
isRoman PROC

4876 PSHR R5 ; push the return address

4877 MOVR R7, R2 ; R2 = dummy 1st suffix
4878 MOVR R2, R5 ; R5 = pointer into table
4879 ADDI #@tbl-$+1,R5

487B @loop MVI@ R5, R1 ; R1 = main digit (M, C, X, I)
487C MVI@ R5, R3 ; R3 = prefix or 2nd suffix (-, D, L, V)

487D MVI@ R4, R0 ; R0 = next digit

487E CMPR R0, R3 ; if this is the prefix ...
487F BNEQ @main

4881 COMR R2 ; ... disable the suffixes
4882 COMR R3 ; by setting them to invalid values
4883 MVI@ R4, R0 ; and read R0 again

4884 @main CMPR R0, R1 ; if R0 is not equal to the main digit,
4885 BNEQ @back ; assume that this part is over

4887 MVI@ R4, R0 ; R0 = next digit
4888 CMPR R0, R1 ; if this is a 2nd occurrence
4889 BNEQ @suffix ; of the main digit ...

488B CMP@ R4, R1 ; ... it may be followed by a 3rd occurrence
488C BNEQ @back

488E MOVR R2, R0 ; if so, force the test below to succeed

488F @suffix CMPR R0, R2 ; otherwise, it may be either the 1st suffix
4890 BEQ @next
4892 CMPR R0, R3 ; or the 2nd suffix (these tests always fail
4893 BEQ @next ; if the suffixes were disabled above)

4895 @back DECR R4 ; the last digit either belongs to the next
; iteration or is invalid

4896 @next MOVR R1, R2 ; use the current main digit
; as the next 1st suffix

4897 SUBI #'I', R1 ; was it the last iteration? ...
4899 BNEQ @loop

489B CMP@ R4, R1 ; ... yes: make sure that we've also reached
; the end of the input

489C PULR R7 ; return

489D @tbl DECLE 'M', '-' ; table format: main digit, 2nd suffix
489F DECLE 'C', 'D'
48A1 DECLE 'X', 'L'
48A3 DECLE 'I', 'V'

ENDP


How?



The regular expression can be rewritten as 4 groups with the same structure, provided that # is any invalid character that is guaranteed not be present in the input string.



 +-------+---> main digit
| |
(M[##]|#?M0,3)(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)
|| |
|+--+-----> prefix or second suffix
|
+---------> first suffix


The first suffix of the group $N$ is the main digit of the group $N-1$. Therefore, we can store the patterns with the pair $(textmain_digit, textsecond_suffix)$ alone.



Our routine attempts to parse the input string character by character according to these patterns and eventually checks whether the end of the string is reached.



Output



output



screenshot of jzIntv




1. A CP-1610 opcode is encoded with a 10-bit value, known as a 'DECLE'. This routine is 47 DECLEs long, starting at $4876 and ending at $48A4 (included).






share|improve this answer











$endgroup$












  • $begingroup$
    wouldn't this be one of the few places where fractional bytes are valid
    $endgroup$
    – ASCII-only
    2 days ago










  • $begingroup$
    @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
    $endgroup$
    – Arnauld
    2 days ago










  • $begingroup$
    @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
    $endgroup$
    – Arnauld
    2 days ago










  • $begingroup$
    ah, so it's only 10 bits when it's in RAM?
    $endgroup$
    – ASCII-only
    2 days ago











  • $begingroup$
    The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
    $endgroup$
    – Arnauld
    2 days ago


















7












$begingroup$

Java 8, 70 bytes





s->s.matches("M0,3(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)")


Port of @Innat3's C# answer, so make sure to upvote him!



Try it online.



Explanation:



s-> // Method with String parameter and boolean return-type
s.matches("...") // Check if the string matches the regex fully
// (which implicitly adds a leading "^" and trailing "$")

M0,3 // No, 1, 2, or 3 adjacent "M"
( | ) // Followed by either:
C[MD] // A "C" with an "M" or "D" after it
| // or:
D? // An optional "D"
C0,3 // Followed by no, 1, 2, or 3 adjacent "C"
( | ) // Followed by either:
X[CL] // An "X" with a "C" or "L" after it
| // or:
L? // An optional "L"
X0,3 // Followed by no, 1, 2, or 3 adjacent "X"
( | ) // Followed by either:
I[XV] // An "I" with an "X" or "V" after it
| // or:
V? // An optional "V"
I0,3 // Followed by no, 1, 2, or 3 adjacent "I"





share|improve this answer









$endgroup$




















    5












    $begingroup$


    R, 74 71 56 bytes



    Thanks to @RobinRyder, @Giuseppe, & @MickyT for their suggestions how to use grep effectively with R's built in as.roman.





    sub("^M(.+)","\1",scan(,""))%in%paste(as.roman(1:2999))


    Try it online!






    share|improve this answer











    $endgroup$












    • $begingroup$
      as.roman won't work anyway, since it only works up to 3899 for some reason.
      $endgroup$
      – Giuseppe
      Apr 11 at 15:56











    • $begingroup$
      I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
      $endgroup$
      – CT Hall
      Apr 11 at 16:01











    • $begingroup$
      @Giuseppe, addressed, using the same regex as the other answers.
      $endgroup$
      – CT Hall
      Apr 11 at 16:26










    • $begingroup$
      I wonder if there's a way to use .romans here...probably not.
      $endgroup$
      – Giuseppe
      Apr 11 at 16:30






    • 2




      $begingroup$
      66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
      $endgroup$
      – Robin Ryder
      Apr 11 at 17:49


















    4












    $begingroup$


    Wolfram Language (Mathematica), 32 bytes



    RomanNumeral@Range@3999~Count~#&


    Try it online!






    share|improve this answer









    $endgroup$




















      2












      $begingroup$


      Jelly,  48 47 46  44 bytes



      -1 thanks to Nick Kennedy



      5Żo7;“ÆæC‘ð“IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ


      A monadic Link accepting a non-empty list of characters consisting only of IVXLCDM which yields either 1 (when it's a valid Roman numeral between $1$ and $3999$) or 0 (if not).



      Try it online! Or see the test-suite.



      How?



      5Żo7;“ÆæC‘ð“IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - Main Link: list of characters S

      5Żo7;“ÆæC‘ - chain 1: f(S) -> X
      5Ż - zero range of five = [0,1,2,3,4,5]
      o7 - OR seven [7,1,2,3,4,5]
      “ÆæC‘ - list of code-page indices [13,22,67]
      ; - concatenate [7,1,2,3,4,5,13,22,67]

      ð - start a new dyadic chain...

      “IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - chain 2: f(X,S) -> isValid
      “IVXLCDM” - list of characters, IVXLCDM
      3Ƥ - for infixes of length three:
      - (i.e. IVX VXL XLC LCD CDM)
      ṃ@ - base decompression with swapped arguments
      - (i.e. use characters as base-3 digits of X's values)
      - (e.g. IVX -> VI I V IX II IV III VII VIII)
      m2 - modulo two slice (results for IVX XLC and CDM only)
      ¤ - nilad followed by link(s) as a nilad:
      ”M - character 'M'
      Ɱ3 - map across [1,2,3] with:
      ẋ - repeat -> M MM MMM
      ṭ - tack
      Ż€ - prepend a zero to each
      Ṛ - reverse
      - -- now we have the table:
      - 0 M MM MMM
      - 0 DC C D CM CC CD CCC DCC DCCC
      - 0 LX X L XC XX XL XXX LXX LXXX
      - 0 VI I V IX II IV III VII VIII
      Œp - Cartesian product [[0,0,0,0],...,["M","CM",0,"IV"],...]
      F€ - flatten €ach [[0,0,0,0],...,['M','C','M',0,'I','V'],...]
      ḟ€0 - filter out the zeros from €ach ["",...,"MCMIV",...]
      ċ - count occurrences of S





      share|improve this answer











      $endgroup$












      • $begingroup$
        There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
        $endgroup$
        – Nick Kennedy
        2 days ago











      • $begingroup$
        Thanks, I've saved one more from it.
        $endgroup$
        – Jonathan Allan
        2 days ago


















      1












      $begingroup$

      Perl 5 (-p), 57 bytes



      $_=/^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$/&!/(.)13/


      TIO



      • uses almost the same regular expression except 0,3 quantifier was changed by *


      • &!/(.)13/ to ensure the same character can't occur 4 times in a row.

      • can't be golfed with -/(.)13/ because would give-1 for IIIIVI for example





      share|improve this answer











      $endgroup$




















        1












        $begingroup$


        Python 2, 81 bytes





        import re
        re.compile('M,3(D?C,3|C[DM])(L?X,3|X[LC])(V?I,3|I[VX])$').match


        Try it online!



        Let's look at the last part of the regex, which matching the Roman numerals up to 9 (including the empty string)



        V?I,3|I[VX]


        This has two alternatives separated by |:




        • V?I,3: An optional V followed by up to 3 I's. This matches the empty string I,II,III, V, VI,VII,VIII.


        • I[VX]: An I followed by a V or X. This matches IV and IX.

        The same things with X,L,C matching the tens, with C,D,M matches the hundreds, and finally ^M,3 allows up to 3 M's (thousands) at the start.



        I tried generating the template for each trio of characters rather than writing it 3 times, but this was a lot longer.






        share|improve this answer











        $endgroup$












        • $begingroup$
          No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
          $endgroup$
          – ShadowRanger
          Apr 12 at 1:28










        • $begingroup$
          @ShadowRanger Thanks, I removed the ^.
          $endgroup$
          – xnor
          Apr 12 at 1:29










        • $begingroup$
          Although I think you messed up the count in the edit; should be 83, not 81.
          $endgroup$
          – ShadowRanger
          Apr 12 at 1:40










        • $begingroup$
          @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
          $endgroup$
          – xnor
          Apr 12 at 1:42






        • 1




          $begingroup$
          Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
          $endgroup$
          – ShadowRanger
          Apr 12 at 1:44


















        1












        $begingroup$


        Retina, 56 51 bytes



        (.)13
        0
        ^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$


        Port of @NahuelFouilleul's Perl 5 answer, so make sure to upvote him!



        Try it online or verify all test cases.



        Explanation:





        (.)13 # If four adjacent characters can be found which are the same
        0 # Replace it with a 0

        ^...$ # Then check if the string matches the following fully:
        M* # No or any amount of adjacent "M"
        ( | ) # Followed by either:
        C[MD] # A "C" with an "M" or "D" after it
        | # or:
        D? # An optional "D"
        C* # Followed by no or any amount of adjacent "C"
        ( | ) # Followed by either:
        X[CL] # An "X" with a "C" or "L" after it
        | # or:
        L? # An optional "L"
        X* # Followed by no or any amount of adjacent "X"
        ( | ) # Followed by either:
        I[XV] # An "I" with an "X" or "V" after it
        | # or:
        V? # An optional "V"
        I* # Followed by no or any amount of adjacent "I"





        share|improve this answer











        $endgroup$




















          1












          $begingroup$


          05AB1E, 61 9 8 bytes



          ŽF¯L.XIå


          Whopping $colorgreentextrm-52 bytes$ thanks to @Adnan, because apparently 05AB1E's Roman Number builtin wasn't documented, haha.. xD



          Try it online or verify all test cases.



          Explanation:





          ŽF¯ # Push comressed integer 3999
          L # Create a list in the range [1,3999]
          .X # Convert each integer in this list to a roman number string
          Iå # Check if the input is in this list
          # (and output the result implicitly)




          See this 05AB1E tip of mine (section How to compress large integers?) to understand why ŽF¯ is 3999.




          Original 61 bytes answer:



          •1∞Γ'иÛnuÞ₂…•Ž8вв€SÐ)v.•6#&‘нδ•u3ôNèyè}'M3L×)Rεõš}`3Fâ}€˜JIå


          Try it online or verify all test cases.



          Explanation:





          •1∞Γ'иÛnuÞ₂…• '# Push compressed integer 397940501547566186191992778
          Ž8в # Push compressed integer 2112
          в # Convert the integer to Base-2112 as list:
          # [1,11,111,12,2,21,211,2111,10]
          €S # Convert each number to a list of digits
          Ð # Triplicate this list
          ) # And wrap it into a list of lists (of lists)
          v # Loop `y` over each these three lists:
          .•6#&‘нδ• # Push compressed string "xivcxlmcd"
          u # Uppercased
          3ô # And split into parts of size 3: ["XIV","CXL","MCD"]
          Nè # Use the loop index to get the current part
          yè # And index the list of lists of digits into this string
          }'M '# After the loop: push "M"
          3L # Push list [1,2,3]
          × # Repeat the "M" that many times: ["M","MM","MMM"]
          ) # Wrap all lists on the stack into a list:
          # [[["I"],["I","I"],["I","I","I"],["I","V"],["V"],["V","I"],["V","I","I"],["V","I","I","I"],["I","X"]],[["X"],["X","X"],["X","X","X"],["X","L"],["L"],["L","X"],["L","X","X"],["L","X","X","X"],["X","C"]],[["C"],["C","C"],["C","C","C"],["C","D"],["D"],["D","C"],["D","C","C"],["D","C","C","C"],["C","M"]],["M","MM","MMM"]]
          R # Reverse this list
          εõš} # Prepend an empty string "" before each inner list
          ` # Push the four lists onto the stack
          3F # Loop 3 times:
          â # Take the cartesian product of the two top lists
          }€˜ # After the loop: flatten each inner list
          J # Join each inner list together to a single string
          Iå # And check if the input is in this list
          # (after which the result is output implicitly)


          See this 05AB1E tip of mine (sections How to compress strings not part of the dictionary?, How to compress large integers?, and How to compress integer lists?) to understand why:




          • •1∞Γ'иÛnuÞ₂…• is 397940501547566186191992778


          • Ž8в is 2112


          • •1∞Γ'иÛnuÞ₂…•Ž8вв is [1,11,111,12,2,21,211,2111,10]


          • .•6#&‘нδ• is "xivcxlmcd"





          share|improve this answer











          $endgroup$








          • 1




            $begingroup$
            I'm not sure why .X is not documented, but I think this should work: 3999L.XQO
            $endgroup$
            – Adnan
            yesterday










          • $begingroup$
            @Adnan Haha, -52 bytes right there. Completely forgot you indeed told us about adding a Roman Number builtin. Will ask @Mr.Xcoder in chat to add it to the docs. Are any other commands missing? ;) PS: Saved another byte by compressing 3999. :)
            $endgroup$
            – Kevin Cruijssen
            yesterday


















          0












          $begingroup$

          perl -MRegexp::Common -pe, 34 bytes



          $_=/^$REnumroman$/&!/(.)13/


          The &!/(.)13/ part is necessary, because Regexp::Common allows four (but not five) of the same characters in a row. That way, it matches roman numbers used on clock faces, where IIII is often used for 4.






          share|improve this answer









          $endgroup$




















            0












            $begingroup$


            Python 3, 116 113 109 107 105 106 bytes





            import re
            lambda n:re.match(r'(M,3(C(M|CC?|D)?|DC,3))(X(C|XX?|L)?|(LX,3))?(I(X|II?|V)?|VI,3)?$',n)


            Try it online!



            -1 byte thanks to ShadowRanger






            share|improve this answer











            $endgroup$








            • 2




              $begingroup$
              As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
              $endgroup$
              – ShadowRanger
              Apr 12 at 1:35










            • $begingroup$
              @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
              $endgroup$
              – Noodle9
              2 days ago










            • $begingroup$
              Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
              $endgroup$
              – ShadowRanger
              2 days ago










            • $begingroup$
              @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
              $endgroup$
              – Noodle9
              2 days ago



















            0












            $begingroup$


            Ruby, (-n) 56 bytes





            p~/^M,3(D?C,3|CM|CD)(L?X,3|XC|XL)(V?I,3|IV|IX)$/


            Try it online!



            Outputs 0 (truthy) or nil (falsy).






            share|improve this answer









            $endgroup$













              Your Answer






              StackExchange.ifUsing("editor", function ()
              StackExchange.using("externalEditor", function ()
              StackExchange.using("snippets", function ()
              StackExchange.snippets.init();
              );
              );
              , "code-snippets");

              StackExchange.ready(function()
              var channelOptions =
              tags: "".split(" "),
              id: "200"
              ;
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function()
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled)
              StackExchange.using("snippets", function()
              createEditor();
              );

              else
              createEditor();

              );

              function createEditor()
              StackExchange.prepareEditor(
              heartbeatType: 'answer',
              autoActivateHeartbeat: false,
              convertImagesToLinks: false,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: null,
              bindNavPrevention: true,
              postfix: "",
              imageUploader:
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              ,
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              );



              );













              draft saved

              draft discarded


















              StackExchange.ready(
              function ()
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodegolf.stackexchange.com%2fquestions%2f183014%2fmatch-roman-numerals%23new-answer', 'question_page');

              );

              Post as a guest















              Required, but never shown

























              15 Answers
              15






              active

              oldest

              votes








              15 Answers
              15






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              14












              $begingroup$


              Verbose, 1362 bytes



              GET A ROMAN NUMERAL TYPED IN BY THE CURRENT PERSON USING THIS PROGRAM AND PUT IT ONTO THE TOP OF THE PROGRAM STACK
              PUT THE NUMBER MMMM ONTO THE TOP OF THE PROGRAM STACK
              MOVE THE FIRST ELEMENT OF THE PROGRAM STACK TO THE SECOND ELEMENT'S PLACE AND THE SECOND ELEMENT OF THE STACK TO THE FIRST ELEMENT'S PLACE
              DIVIDE THE FIRST ELEMENT OF THE PROGRAM STACK BY THE SECOND ELEMENT OF THE PROGRAM STACK AND PUT THE RESULT ONTO THE TOP OF THE PROGRAM STACK
              PUT THE NUMBER V ONTO THE TOP OF THE PROGRAM STACK
              GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
              PUT THE NUMBER I ONTO THE TOP OF THE PROGRAM STACK
              GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE
              PUT THE NUMBER III ONTO THE TOP OF THE PROGRAM STACK
              GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
              PUT THE NUMBER NULLA ONTO THE TOP OF THE PROGRAM STACK
              GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE


              Outputs I for valid roman numerals in the range I-MMMCMXCIX and NULLA (0) or informs user input is not a valid roman numeral otherwise.






              share|improve this answer









              $endgroup$








              • 12




                $begingroup$
                I can't decide if this is the right tool for the job or not.
                $endgroup$
                – Vaelus
                Apr 11 at 17:57






              • 5




                $begingroup$
                Is this the right tool for any job?
                $endgroup$
                – omzrs
                Apr 11 at 19:39















              14












              $begingroup$


              Verbose, 1362 bytes



              GET A ROMAN NUMERAL TYPED IN BY THE CURRENT PERSON USING THIS PROGRAM AND PUT IT ONTO THE TOP OF THE PROGRAM STACK
              PUT THE NUMBER MMMM ONTO THE TOP OF THE PROGRAM STACK
              MOVE THE FIRST ELEMENT OF THE PROGRAM STACK TO THE SECOND ELEMENT'S PLACE AND THE SECOND ELEMENT OF THE STACK TO THE FIRST ELEMENT'S PLACE
              DIVIDE THE FIRST ELEMENT OF THE PROGRAM STACK BY THE SECOND ELEMENT OF THE PROGRAM STACK AND PUT THE RESULT ONTO THE TOP OF THE PROGRAM STACK
              PUT THE NUMBER V ONTO THE TOP OF THE PROGRAM STACK
              GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
              PUT THE NUMBER I ONTO THE TOP OF THE PROGRAM STACK
              GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE
              PUT THE NUMBER III ONTO THE TOP OF THE PROGRAM STACK
              GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
              PUT THE NUMBER NULLA ONTO THE TOP OF THE PROGRAM STACK
              GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE


              Outputs I for valid roman numerals in the range I-MMMCMXCIX and NULLA (0) or informs user input is not a valid roman numeral otherwise.






              share|improve this answer









              $endgroup$








              • 12




                $begingroup$
                I can't decide if this is the right tool for the job or not.
                $endgroup$
                – Vaelus
                Apr 11 at 17:57






              • 5




                $begingroup$
                Is this the right tool for any job?
                $endgroup$
                – omzrs
                Apr 11 at 19:39













              14












              14








              14





              $begingroup$


              Verbose, 1362 bytes



              GET A ROMAN NUMERAL TYPED IN BY THE CURRENT PERSON USING THIS PROGRAM AND PUT IT ONTO THE TOP OF THE PROGRAM STACK
              PUT THE NUMBER MMMM ONTO THE TOP OF THE PROGRAM STACK
              MOVE THE FIRST ELEMENT OF THE PROGRAM STACK TO THE SECOND ELEMENT'S PLACE AND THE SECOND ELEMENT OF THE STACK TO THE FIRST ELEMENT'S PLACE
              DIVIDE THE FIRST ELEMENT OF THE PROGRAM STACK BY THE SECOND ELEMENT OF THE PROGRAM STACK AND PUT THE RESULT ONTO THE TOP OF THE PROGRAM STACK
              PUT THE NUMBER V ONTO THE TOP OF THE PROGRAM STACK
              GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
              PUT THE NUMBER I ONTO THE TOP OF THE PROGRAM STACK
              GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE
              PUT THE NUMBER III ONTO THE TOP OF THE PROGRAM STACK
              GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
              PUT THE NUMBER NULLA ONTO THE TOP OF THE PROGRAM STACK
              GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE


              Outputs I for valid roman numerals in the range I-MMMCMXCIX and NULLA (0) or informs user input is not a valid roman numeral otherwise.






              share|improve this answer









              $endgroup$




              Verbose, 1362 bytes



              GET A ROMAN NUMERAL TYPED IN BY THE CURRENT PERSON USING THIS PROGRAM AND PUT IT ONTO THE TOP OF THE PROGRAM STACK
              PUT THE NUMBER MMMM ONTO THE TOP OF THE PROGRAM STACK
              MOVE THE FIRST ELEMENT OF THE PROGRAM STACK TO THE SECOND ELEMENT'S PLACE AND THE SECOND ELEMENT OF THE STACK TO THE FIRST ELEMENT'S PLACE
              DIVIDE THE FIRST ELEMENT OF THE PROGRAM STACK BY THE SECOND ELEMENT OF THE PROGRAM STACK AND PUT THE RESULT ONTO THE TOP OF THE PROGRAM STACK
              PUT THE NUMBER V ONTO THE TOP OF THE PROGRAM STACK
              GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
              PUT THE NUMBER I ONTO THE TOP OF THE PROGRAM STACK
              GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE
              PUT THE NUMBER III ONTO THE TOP OF THE PROGRAM STACK
              GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
              PUT THE NUMBER NULLA ONTO THE TOP OF THE PROGRAM STACK
              GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE


              Outputs I for valid roman numerals in the range I-MMMCMXCIX and NULLA (0) or informs user input is not a valid roman numeral otherwise.







              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered Apr 11 at 15:24









              Expired DataExpired Data

              948217




              948217







              • 12




                $begingroup$
                I can't decide if this is the right tool for the job or not.
                $endgroup$
                – Vaelus
                Apr 11 at 17:57






              • 5




                $begingroup$
                Is this the right tool for any job?
                $endgroup$
                – omzrs
                Apr 11 at 19:39












              • 12




                $begingroup$
                I can't decide if this is the right tool for the job or not.
                $endgroup$
                – Vaelus
                Apr 11 at 17:57






              • 5




                $begingroup$
                Is this the right tool for any job?
                $endgroup$
                – omzrs
                Apr 11 at 19:39







              12




              12




              $begingroup$
              I can't decide if this is the right tool for the job or not.
              $endgroup$
              – Vaelus
              Apr 11 at 17:57




              $begingroup$
              I can't decide if this is the right tool for the job or not.
              $endgroup$
              – Vaelus
              Apr 11 at 17:57




              5




              5




              $begingroup$
              Is this the right tool for any job?
              $endgroup$
              – omzrs
              Apr 11 at 19:39




              $begingroup$
              Is this the right tool for any job?
              $endgroup$
              – omzrs
              Apr 11 at 19:39











              8












              $begingroup$


              C# (Visual C# Interactive Compiler), 79 109 bytes



              This seems like a Regex challenge, I'm sure a shorter solution can be found...



              s=>System.Text.RegularExpressions.Regex.IsMatch(s,"^M0,3(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)$")


              Try it online!






              share|improve this answer











              $endgroup$












              • $begingroup$
                Couldn't you shorten 0,3 to ,3?
                $endgroup$
                – flawr
                Apr 11 at 13:44










              • $begingroup$
                @flawr doesn't seem to capture anything then
                $endgroup$
                – Innat3
                Apr 11 at 13:55






              • 1




                $begingroup$
                Ah sorry, only things like 5, work, but not ,5.
                $endgroup$
                – flawr
                Apr 11 at 14:01






              • 2




                $begingroup$
                You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
                $endgroup$
                – Kevin Cruijssen
                Apr 11 at 20:07







              • 3




                $begingroup$
                Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
                $endgroup$
                – Embodiment of Ignorance
                Apr 11 at 20:30
















              8












              $begingroup$


              C# (Visual C# Interactive Compiler), 79 109 bytes



              This seems like a Regex challenge, I'm sure a shorter solution can be found...



              s=>System.Text.RegularExpressions.Regex.IsMatch(s,"^M0,3(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)$")


              Try it online!






              share|improve this answer











              $endgroup$












              • $begingroup$
                Couldn't you shorten 0,3 to ,3?
                $endgroup$
                – flawr
                Apr 11 at 13:44










              • $begingroup$
                @flawr doesn't seem to capture anything then
                $endgroup$
                – Innat3
                Apr 11 at 13:55






              • 1




                $begingroup$
                Ah sorry, only things like 5, work, but not ,5.
                $endgroup$
                – flawr
                Apr 11 at 14:01






              • 2




                $begingroup$
                You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
                $endgroup$
                – Kevin Cruijssen
                Apr 11 at 20:07







              • 3




                $begingroup$
                Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
                $endgroup$
                – Embodiment of Ignorance
                Apr 11 at 20:30














              8












              8








              8





              $begingroup$


              C# (Visual C# Interactive Compiler), 79 109 bytes



              This seems like a Regex challenge, I'm sure a shorter solution can be found...



              s=>System.Text.RegularExpressions.Regex.IsMatch(s,"^M0,3(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)$")


              Try it online!






              share|improve this answer











              $endgroup$




              C# (Visual C# Interactive Compiler), 79 109 bytes



              This seems like a Regex challenge, I'm sure a shorter solution can be found...



              s=>System.Text.RegularExpressions.Regex.IsMatch(s,"^M0,3(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)$")


              Try it online!







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited Apr 11 at 17:46

























              answered Apr 11 at 13:02









              Innat3Innat3

              2314




              2314











              • $begingroup$
                Couldn't you shorten 0,3 to ,3?
                $endgroup$
                – flawr
                Apr 11 at 13:44










              • $begingroup$
                @flawr doesn't seem to capture anything then
                $endgroup$
                – Innat3
                Apr 11 at 13:55






              • 1




                $begingroup$
                Ah sorry, only things like 5, work, but not ,5.
                $endgroup$
                – flawr
                Apr 11 at 14:01






              • 2




                $begingroup$
                You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
                $endgroup$
                – Kevin Cruijssen
                Apr 11 at 20:07







              • 3




                $begingroup$
                Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
                $endgroup$
                – Embodiment of Ignorance
                Apr 11 at 20:30

















              • $begingroup$
                Couldn't you shorten 0,3 to ,3?
                $endgroup$
                – flawr
                Apr 11 at 13:44










              • $begingroup$
                @flawr doesn't seem to capture anything then
                $endgroup$
                – Innat3
                Apr 11 at 13:55






              • 1




                $begingroup$
                Ah sorry, only things like 5, work, but not ,5.
                $endgroup$
                – flawr
                Apr 11 at 14:01






              • 2




                $begingroup$
                You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
                $endgroup$
                – Kevin Cruijssen
                Apr 11 at 20:07







              • 3




                $begingroup$
                Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
                $endgroup$
                – Embodiment of Ignorance
                Apr 11 at 20:30
















              $begingroup$
              Couldn't you shorten 0,3 to ,3?
              $endgroup$
              – flawr
              Apr 11 at 13:44




              $begingroup$
              Couldn't you shorten 0,3 to ,3?
              $endgroup$
              – flawr
              Apr 11 at 13:44












              $begingroup$
              @flawr doesn't seem to capture anything then
              $endgroup$
              – Innat3
              Apr 11 at 13:55




              $begingroup$
              @flawr doesn't seem to capture anything then
              $endgroup$
              – Innat3
              Apr 11 at 13:55




              1




              1




              $begingroup$
              Ah sorry, only things like 5, work, but not ,5.
              $endgroup$
              – flawr
              Apr 11 at 14:01




              $begingroup$
              Ah sorry, only things like 5, work, but not ,5.
              $endgroup$
              – flawr
              Apr 11 at 14:01




              2




              2




              $begingroup$
              You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
              $endgroup$
              – Kevin Cruijssen
              Apr 11 at 20:07





              $begingroup$
              You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
              $endgroup$
              – Kevin Cruijssen
              Apr 11 at 20:07





              3




              3




              $begingroup$
              Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
              $endgroup$
              – Embodiment of Ignorance
              Apr 11 at 20:30





              $begingroup$
              Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
              $endgroup$
              – Embodiment of Ignorance
              Apr 11 at 20:30












              8












              $begingroup$


              Wolfram Language (Mathematica), 35 bytes



              Check[FromRomanNumeral@#<3999,1<0]&


              Try it online!



              5 bytes saved, thanks to @attinat



              the limitation [1,3999] unfortunateley costs 7 bytes...

              here is the code for any roman number




              Wolfram Language (Mathematica), 28 bytes



              Check[FromRomanNumeral@#,F]&


              Try it online!



              the above code works for any number, not just [1,3999]






              share|improve this answer











              $endgroup$








              • 2




                $begingroup$
                @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
                $endgroup$
                – mathmandan
                Apr 11 at 17:31











              • $begingroup$
                35 bytes. Boole is also shorter (by one byte) than using If in that way.
                $endgroup$
                – attinat
                Apr 11 at 19:28















              8












              $begingroup$


              Wolfram Language (Mathematica), 35 bytes



              Check[FromRomanNumeral@#<3999,1<0]&


              Try it online!



              5 bytes saved, thanks to @attinat



              the limitation [1,3999] unfortunateley costs 7 bytes...

              here is the code for any roman number




              Wolfram Language (Mathematica), 28 bytes



              Check[FromRomanNumeral@#,F]&


              Try it online!



              the above code works for any number, not just [1,3999]






              share|improve this answer











              $endgroup$








              • 2




                $begingroup$
                @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
                $endgroup$
                – mathmandan
                Apr 11 at 17:31











              • $begingroup$
                35 bytes. Boole is also shorter (by one byte) than using If in that way.
                $endgroup$
                – attinat
                Apr 11 at 19:28













              8












              8








              8





              $begingroup$


              Wolfram Language (Mathematica), 35 bytes



              Check[FromRomanNumeral@#<3999,1<0]&


              Try it online!



              5 bytes saved, thanks to @attinat



              the limitation [1,3999] unfortunateley costs 7 bytes...

              here is the code for any roman number




              Wolfram Language (Mathematica), 28 bytes



              Check[FromRomanNumeral@#,F]&


              Try it online!



              the above code works for any number, not just [1,3999]






              share|improve this answer











              $endgroup$




              Wolfram Language (Mathematica), 35 bytes



              Check[FromRomanNumeral@#<3999,1<0]&


              Try it online!



              5 bytes saved, thanks to @attinat



              the limitation [1,3999] unfortunateley costs 7 bytes...

              here is the code for any roman number




              Wolfram Language (Mathematica), 28 bytes



              Check[FromRomanNumeral@#,F]&


              Try it online!



              the above code works for any number, not just [1,3999]







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited 2 days ago

























              answered Apr 11 at 13:03









              J42161217J42161217

              14k21353




              14k21353







              • 2




                $begingroup$
                @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
                $endgroup$
                – mathmandan
                Apr 11 at 17:31











              • $begingroup$
                35 bytes. Boole is also shorter (by one byte) than using If in that way.
                $endgroup$
                – attinat
                Apr 11 at 19:28












              • 2




                $begingroup$
                @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
                $endgroup$
                – mathmandan
                Apr 11 at 17:31











              • $begingroup$
                35 bytes. Boole is also shorter (by one byte) than using If in that way.
                $endgroup$
                – attinat
                Apr 11 at 19:28







              2




              2




              $begingroup$
              @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
              $endgroup$
              – mathmandan
              Apr 11 at 17:31





              $begingroup$
              @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
              $endgroup$
              – mathmandan
              Apr 11 at 17:31













              $begingroup$
              35 bytes. Boole is also shorter (by one byte) than using If in that way.
              $endgroup$
              – attinat
              Apr 11 at 19:28




              $begingroup$
              35 bytes. Boole is also shorter (by one byte) than using If in that way.
              $endgroup$
              – attinat
              Apr 11 at 19:28











              8












              $begingroup$


              CP-1610 assembly (Intellivision),  52 ... 48  47 DECLEs1 = 59 bytes



              Let's try this on a system that predates Perl by a good 7 years. :-)



              Takes a pointer to a null-terminated string in R4. Sets the Zero flag if the input is a valid Roman numeral, or clears it otherwise.



               ROMW 10 ; use 10-bit ROM width
              ORG $4800 ; map this program at $4800

              ;; ------------------------------------------------------------- ;;
              ;; test code ;;
              ;; ------------------------------------------------------------- ;;
              4800 EIS ; enable interrupts

              4801 SDBD ; R5 = pointer into test case index
              4802 MVII #ndx, R5
              4805 MVII #$214, R3 ; R3 = backtab pointer
              4807 MVII #11, R0 ; R0 = number of test cases

              4809 loop SDBD ; R4 = pointer to next test case
              480A MVI@ R5, R4
              480B PSHR R0 ; save R0, R3, R5 onto the stack
              480C PSHR R3
              480D PSHR R5
              480E CALL isRoman ; invoke our routine
              4811 PULR R5 ; restore R5 and R3
              4812 PULR R3

              4813 MVII #$1A7, R0 ; use a white 'T' by default
              4815 BEQ disp

              4817 MVII #$137, R0 ; or a white 'F' is the Z flag was cleared

              4819 disp MVO@ R0, R3 ; draw it
              481A INCR R3 ; increment the backtab pointer

              481B PULR R0 ; restore R0
              481C DECR R0 ; and advance to the next test case, if any
              481D BNEQ loop

              481F DECR R7 ; loop forever

              ;; ------------------------------------------------------------- ;;
              ;; test cases ;;
              ;; ------------------------------------------------------------- ;;
              4820 ndx BIDECLE test0, test1, test2, test3
              4828 BIDECLE test4, test5, test6, test7, test8, test9, test10

              ; truthy
              4836 test0 STRING "MCCXXXIV", 0
              483F test1 STRING "CMLXXXVIII", 0
              484A test2 STRING "DXIV", 0
              484F test3 STRING "CI", 0

              ; falsy
              4852 test4 STRING "MMIXVIII", 0
              485B test5 STRING "IVX", 0
              485F test6 STRING "IXV", 0
              4863 test7 STRING "MMMM", 0
              4868 test8 STRING "XXXVX", 0
              486E test9 STRING "IVI", 0
              4872 test10 STRING "VIV", 0

              ;; ------------------------------------------------------------- ;;
              ;; routine ;;
              ;; ------------------------------------------------------------- ;;
              isRoman PROC

              4876 PSHR R5 ; push the return address

              4877 MOVR R7, R2 ; R2 = dummy 1st suffix
              4878 MOVR R2, R5 ; R5 = pointer into table
              4879 ADDI #@tbl-$+1,R5

              487B @loop MVI@ R5, R1 ; R1 = main digit (M, C, X, I)
              487C MVI@ R5, R3 ; R3 = prefix or 2nd suffix (-, D, L, V)

              487D MVI@ R4, R0 ; R0 = next digit

              487E CMPR R0, R3 ; if this is the prefix ...
              487F BNEQ @main

              4881 COMR R2 ; ... disable the suffixes
              4882 COMR R3 ; by setting them to invalid values
              4883 MVI@ R4, R0 ; and read R0 again

              4884 @main CMPR R0, R1 ; if R0 is not equal to the main digit,
              4885 BNEQ @back ; assume that this part is over

              4887 MVI@ R4, R0 ; R0 = next digit
              4888 CMPR R0, R1 ; if this is a 2nd occurrence
              4889 BNEQ @suffix ; of the main digit ...

              488B CMP@ R4, R1 ; ... it may be followed by a 3rd occurrence
              488C BNEQ @back

              488E MOVR R2, R0 ; if so, force the test below to succeed

              488F @suffix CMPR R0, R2 ; otherwise, it may be either the 1st suffix
              4890 BEQ @next
              4892 CMPR R0, R3 ; or the 2nd suffix (these tests always fail
              4893 BEQ @next ; if the suffixes were disabled above)

              4895 @back DECR R4 ; the last digit either belongs to the next
              ; iteration or is invalid

              4896 @next MOVR R1, R2 ; use the current main digit
              ; as the next 1st suffix

              4897 SUBI #'I', R1 ; was it the last iteration? ...
              4899 BNEQ @loop

              489B CMP@ R4, R1 ; ... yes: make sure that we've also reached
              ; the end of the input

              489C PULR R7 ; return

              489D @tbl DECLE 'M', '-' ; table format: main digit, 2nd suffix
              489F DECLE 'C', 'D'
              48A1 DECLE 'X', 'L'
              48A3 DECLE 'I', 'V'

              ENDP


              How?



              The regular expression can be rewritten as 4 groups with the same structure, provided that # is any invalid character that is guaranteed not be present in the input string.



               +-------+---> main digit
              | |
              (M[##]|#?M0,3)(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)
              || |
              |+--+-----> prefix or second suffix
              |
              +---------> first suffix


              The first suffix of the group $N$ is the main digit of the group $N-1$. Therefore, we can store the patterns with the pair $(textmain_digit, textsecond_suffix)$ alone.



              Our routine attempts to parse the input string character by character according to these patterns and eventually checks whether the end of the string is reached.



              Output



              output



              screenshot of jzIntv




              1. A CP-1610 opcode is encoded with a 10-bit value, known as a 'DECLE'. This routine is 47 DECLEs long, starting at $4876 and ending at $48A4 (included).






              share|improve this answer











              $endgroup$












              • $begingroup$
                wouldn't this be one of the few places where fractional bytes are valid
                $endgroup$
                – ASCII-only
                2 days ago










              • $begingroup$
                @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
                $endgroup$
                – Arnauld
                2 days ago










              • $begingroup$
                @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
                $endgroup$
                – Arnauld
                2 days ago










              • $begingroup$
                ah, so it's only 10 bits when it's in RAM?
                $endgroup$
                – ASCII-only
                2 days ago











              • $begingroup$
                The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
                $endgroup$
                – Arnauld
                2 days ago















              8












              $begingroup$


              CP-1610 assembly (Intellivision),  52 ... 48  47 DECLEs1 = 59 bytes



              Let's try this on a system that predates Perl by a good 7 years. :-)



              Takes a pointer to a null-terminated string in R4. Sets the Zero flag if the input is a valid Roman numeral, or clears it otherwise.



               ROMW 10 ; use 10-bit ROM width
              ORG $4800 ; map this program at $4800

              ;; ------------------------------------------------------------- ;;
              ;; test code ;;
              ;; ------------------------------------------------------------- ;;
              4800 EIS ; enable interrupts

              4801 SDBD ; R5 = pointer into test case index
              4802 MVII #ndx, R5
              4805 MVII #$214, R3 ; R3 = backtab pointer
              4807 MVII #11, R0 ; R0 = number of test cases

              4809 loop SDBD ; R4 = pointer to next test case
              480A MVI@ R5, R4
              480B PSHR R0 ; save R0, R3, R5 onto the stack
              480C PSHR R3
              480D PSHR R5
              480E CALL isRoman ; invoke our routine
              4811 PULR R5 ; restore R5 and R3
              4812 PULR R3

              4813 MVII #$1A7, R0 ; use a white 'T' by default
              4815 BEQ disp

              4817 MVII #$137, R0 ; or a white 'F' is the Z flag was cleared

              4819 disp MVO@ R0, R3 ; draw it
              481A INCR R3 ; increment the backtab pointer

              481B PULR R0 ; restore R0
              481C DECR R0 ; and advance to the next test case, if any
              481D BNEQ loop

              481F DECR R7 ; loop forever

              ;; ------------------------------------------------------------- ;;
              ;; test cases ;;
              ;; ------------------------------------------------------------- ;;
              4820 ndx BIDECLE test0, test1, test2, test3
              4828 BIDECLE test4, test5, test6, test7, test8, test9, test10

              ; truthy
              4836 test0 STRING "MCCXXXIV", 0
              483F test1 STRING "CMLXXXVIII", 0
              484A test2 STRING "DXIV", 0
              484F test3 STRING "CI", 0

              ; falsy
              4852 test4 STRING "MMIXVIII", 0
              485B test5 STRING "IVX", 0
              485F test6 STRING "IXV", 0
              4863 test7 STRING "MMMM", 0
              4868 test8 STRING "XXXVX", 0
              486E test9 STRING "IVI", 0
              4872 test10 STRING "VIV", 0

              ;; ------------------------------------------------------------- ;;
              ;; routine ;;
              ;; ------------------------------------------------------------- ;;
              isRoman PROC

              4876 PSHR R5 ; push the return address

              4877 MOVR R7, R2 ; R2 = dummy 1st suffix
              4878 MOVR R2, R5 ; R5 = pointer into table
              4879 ADDI #@tbl-$+1,R5

              487B @loop MVI@ R5, R1 ; R1 = main digit (M, C, X, I)
              487C MVI@ R5, R3 ; R3 = prefix or 2nd suffix (-, D, L, V)

              487D MVI@ R4, R0 ; R0 = next digit

              487E CMPR R0, R3 ; if this is the prefix ...
              487F BNEQ @main

              4881 COMR R2 ; ... disable the suffixes
              4882 COMR R3 ; by setting them to invalid values
              4883 MVI@ R4, R0 ; and read R0 again

              4884 @main CMPR R0, R1 ; if R0 is not equal to the main digit,
              4885 BNEQ @back ; assume that this part is over

              4887 MVI@ R4, R0 ; R0 = next digit
              4888 CMPR R0, R1 ; if this is a 2nd occurrence
              4889 BNEQ @suffix ; of the main digit ...

              488B CMP@ R4, R1 ; ... it may be followed by a 3rd occurrence
              488C BNEQ @back

              488E MOVR R2, R0 ; if so, force the test below to succeed

              488F @suffix CMPR R0, R2 ; otherwise, it may be either the 1st suffix
              4890 BEQ @next
              4892 CMPR R0, R3 ; or the 2nd suffix (these tests always fail
              4893 BEQ @next ; if the suffixes were disabled above)

              4895 @back DECR R4 ; the last digit either belongs to the next
              ; iteration or is invalid

              4896 @next MOVR R1, R2 ; use the current main digit
              ; as the next 1st suffix

              4897 SUBI #'I', R1 ; was it the last iteration? ...
              4899 BNEQ @loop

              489B CMP@ R4, R1 ; ... yes: make sure that we've also reached
              ; the end of the input

              489C PULR R7 ; return

              489D @tbl DECLE 'M', '-' ; table format: main digit, 2nd suffix
              489F DECLE 'C', 'D'
              48A1 DECLE 'X', 'L'
              48A3 DECLE 'I', 'V'

              ENDP


              How?



              The regular expression can be rewritten as 4 groups with the same structure, provided that # is any invalid character that is guaranteed not be present in the input string.



               +-------+---> main digit
              | |
              (M[##]|#?M0,3)(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)
              || |
              |+--+-----> prefix or second suffix
              |
              +---------> first suffix


              The first suffix of the group $N$ is the main digit of the group $N-1$. Therefore, we can store the patterns with the pair $(textmain_digit, textsecond_suffix)$ alone.



              Our routine attempts to parse the input string character by character according to these patterns and eventually checks whether the end of the string is reached.



              Output



              output



              screenshot of jzIntv




              1. A CP-1610 opcode is encoded with a 10-bit value, known as a 'DECLE'. This routine is 47 DECLEs long, starting at $4876 and ending at $48A4 (included).






              share|improve this answer











              $endgroup$












              • $begingroup$
                wouldn't this be one of the few places where fractional bytes are valid
                $endgroup$
                – ASCII-only
                2 days ago










              • $begingroup$
                @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
                $endgroup$
                – Arnauld
                2 days ago










              • $begingroup$
                @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
                $endgroup$
                – Arnauld
                2 days ago










              • $begingroup$
                ah, so it's only 10 bits when it's in RAM?
                $endgroup$
                – ASCII-only
                2 days ago











              • $begingroup$
                The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
                $endgroup$
                – Arnauld
                2 days ago













              8












              8








              8





              $begingroup$


              CP-1610 assembly (Intellivision),  52 ... 48  47 DECLEs1 = 59 bytes



              Let's try this on a system that predates Perl by a good 7 years. :-)



              Takes a pointer to a null-terminated string in R4. Sets the Zero flag if the input is a valid Roman numeral, or clears it otherwise.



               ROMW 10 ; use 10-bit ROM width
              ORG $4800 ; map this program at $4800

              ;; ------------------------------------------------------------- ;;
              ;; test code ;;
              ;; ------------------------------------------------------------- ;;
              4800 EIS ; enable interrupts

              4801 SDBD ; R5 = pointer into test case index
              4802 MVII #ndx, R5
              4805 MVII #$214, R3 ; R3 = backtab pointer
              4807 MVII #11, R0 ; R0 = number of test cases

              4809 loop SDBD ; R4 = pointer to next test case
              480A MVI@ R5, R4
              480B PSHR R0 ; save R0, R3, R5 onto the stack
              480C PSHR R3
              480D PSHR R5
              480E CALL isRoman ; invoke our routine
              4811 PULR R5 ; restore R5 and R3
              4812 PULR R3

              4813 MVII #$1A7, R0 ; use a white 'T' by default
              4815 BEQ disp

              4817 MVII #$137, R0 ; or a white 'F' is the Z flag was cleared

              4819 disp MVO@ R0, R3 ; draw it
              481A INCR R3 ; increment the backtab pointer

              481B PULR R0 ; restore R0
              481C DECR R0 ; and advance to the next test case, if any
              481D BNEQ loop

              481F DECR R7 ; loop forever

              ;; ------------------------------------------------------------- ;;
              ;; test cases ;;
              ;; ------------------------------------------------------------- ;;
              4820 ndx BIDECLE test0, test1, test2, test3
              4828 BIDECLE test4, test5, test6, test7, test8, test9, test10

              ; truthy
              4836 test0 STRING "MCCXXXIV", 0
              483F test1 STRING "CMLXXXVIII", 0
              484A test2 STRING "DXIV", 0
              484F test3 STRING "CI", 0

              ; falsy
              4852 test4 STRING "MMIXVIII", 0
              485B test5 STRING "IVX", 0
              485F test6 STRING "IXV", 0
              4863 test7 STRING "MMMM", 0
              4868 test8 STRING "XXXVX", 0
              486E test9 STRING "IVI", 0
              4872 test10 STRING "VIV", 0

              ;; ------------------------------------------------------------- ;;
              ;; routine ;;
              ;; ------------------------------------------------------------- ;;
              isRoman PROC

              4876 PSHR R5 ; push the return address

              4877 MOVR R7, R2 ; R2 = dummy 1st suffix
              4878 MOVR R2, R5 ; R5 = pointer into table
              4879 ADDI #@tbl-$+1,R5

              487B @loop MVI@ R5, R1 ; R1 = main digit (M, C, X, I)
              487C MVI@ R5, R3 ; R3 = prefix or 2nd suffix (-, D, L, V)

              487D MVI@ R4, R0 ; R0 = next digit

              487E CMPR R0, R3 ; if this is the prefix ...
              487F BNEQ @main

              4881 COMR R2 ; ... disable the suffixes
              4882 COMR R3 ; by setting them to invalid values
              4883 MVI@ R4, R0 ; and read R0 again

              4884 @main CMPR R0, R1 ; if R0 is not equal to the main digit,
              4885 BNEQ @back ; assume that this part is over

              4887 MVI@ R4, R0 ; R0 = next digit
              4888 CMPR R0, R1 ; if this is a 2nd occurrence
              4889 BNEQ @suffix ; of the main digit ...

              488B CMP@ R4, R1 ; ... it may be followed by a 3rd occurrence
              488C BNEQ @back

              488E MOVR R2, R0 ; if so, force the test below to succeed

              488F @suffix CMPR R0, R2 ; otherwise, it may be either the 1st suffix
              4890 BEQ @next
              4892 CMPR R0, R3 ; or the 2nd suffix (these tests always fail
              4893 BEQ @next ; if the suffixes were disabled above)

              4895 @back DECR R4 ; the last digit either belongs to the next
              ; iteration or is invalid

              4896 @next MOVR R1, R2 ; use the current main digit
              ; as the next 1st suffix

              4897 SUBI #'I', R1 ; was it the last iteration? ...
              4899 BNEQ @loop

              489B CMP@ R4, R1 ; ... yes: make sure that we've also reached
              ; the end of the input

              489C PULR R7 ; return

              489D @tbl DECLE 'M', '-' ; table format: main digit, 2nd suffix
              489F DECLE 'C', 'D'
              48A1 DECLE 'X', 'L'
              48A3 DECLE 'I', 'V'

              ENDP


              How?



              The regular expression can be rewritten as 4 groups with the same structure, provided that # is any invalid character that is guaranteed not be present in the input string.



               +-------+---> main digit
              | |
              (M[##]|#?M0,3)(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)
              || |
              |+--+-----> prefix or second suffix
              |
              +---------> first suffix


              The first suffix of the group $N$ is the main digit of the group $N-1$. Therefore, we can store the patterns with the pair $(textmain_digit, textsecond_suffix)$ alone.



              Our routine attempts to parse the input string character by character according to these patterns and eventually checks whether the end of the string is reached.



              Output



              output



              screenshot of jzIntv




              1. A CP-1610 opcode is encoded with a 10-bit value, known as a 'DECLE'. This routine is 47 DECLEs long, starting at $4876 and ending at $48A4 (included).






              share|improve this answer











              $endgroup$




              CP-1610 assembly (Intellivision),  52 ... 48  47 DECLEs1 = 59 bytes



              Let's try this on a system that predates Perl by a good 7 years. :-)



              Takes a pointer to a null-terminated string in R4. Sets the Zero flag if the input is a valid Roman numeral, or clears it otherwise.



               ROMW 10 ; use 10-bit ROM width
              ORG $4800 ; map this program at $4800

              ;; ------------------------------------------------------------- ;;
              ;; test code ;;
              ;; ------------------------------------------------------------- ;;
              4800 EIS ; enable interrupts

              4801 SDBD ; R5 = pointer into test case index
              4802 MVII #ndx, R5
              4805 MVII #$214, R3 ; R3 = backtab pointer
              4807 MVII #11, R0 ; R0 = number of test cases

              4809 loop SDBD ; R4 = pointer to next test case
              480A MVI@ R5, R4
              480B PSHR R0 ; save R0, R3, R5 onto the stack
              480C PSHR R3
              480D PSHR R5
              480E CALL isRoman ; invoke our routine
              4811 PULR R5 ; restore R5 and R3
              4812 PULR R3

              4813 MVII #$1A7, R0 ; use a white 'T' by default
              4815 BEQ disp

              4817 MVII #$137, R0 ; or a white 'F' is the Z flag was cleared

              4819 disp MVO@ R0, R3 ; draw it
              481A INCR R3 ; increment the backtab pointer

              481B PULR R0 ; restore R0
              481C DECR R0 ; and advance to the next test case, if any
              481D BNEQ loop

              481F DECR R7 ; loop forever

              ;; ------------------------------------------------------------- ;;
              ;; test cases ;;
              ;; ------------------------------------------------------------- ;;
              4820 ndx BIDECLE test0, test1, test2, test3
              4828 BIDECLE test4, test5, test6, test7, test8, test9, test10

              ; truthy
              4836 test0 STRING "MCCXXXIV", 0
              483F test1 STRING "CMLXXXVIII", 0
              484A test2 STRING "DXIV", 0
              484F test3 STRING "CI", 0

              ; falsy
              4852 test4 STRING "MMIXVIII", 0
              485B test5 STRING "IVX", 0
              485F test6 STRING "IXV", 0
              4863 test7 STRING "MMMM", 0
              4868 test8 STRING "XXXVX", 0
              486E test9 STRING "IVI", 0
              4872 test10 STRING "VIV", 0

              ;; ------------------------------------------------------------- ;;
              ;; routine ;;
              ;; ------------------------------------------------------------- ;;
              isRoman PROC

              4876 PSHR R5 ; push the return address

              4877 MOVR R7, R2 ; R2 = dummy 1st suffix
              4878 MOVR R2, R5 ; R5 = pointer into table
              4879 ADDI #@tbl-$+1,R5

              487B @loop MVI@ R5, R1 ; R1 = main digit (M, C, X, I)
              487C MVI@ R5, R3 ; R3 = prefix or 2nd suffix (-, D, L, V)

              487D MVI@ R4, R0 ; R0 = next digit

              487E CMPR R0, R3 ; if this is the prefix ...
              487F BNEQ @main

              4881 COMR R2 ; ... disable the suffixes
              4882 COMR R3 ; by setting them to invalid values
              4883 MVI@ R4, R0 ; and read R0 again

              4884 @main CMPR R0, R1 ; if R0 is not equal to the main digit,
              4885 BNEQ @back ; assume that this part is over

              4887 MVI@ R4, R0 ; R0 = next digit
              4888 CMPR R0, R1 ; if this is a 2nd occurrence
              4889 BNEQ @suffix ; of the main digit ...

              488B CMP@ R4, R1 ; ... it may be followed by a 3rd occurrence
              488C BNEQ @back

              488E MOVR R2, R0 ; if so, force the test below to succeed

              488F @suffix CMPR R0, R2 ; otherwise, it may be either the 1st suffix
              4890 BEQ @next
              4892 CMPR R0, R3 ; or the 2nd suffix (these tests always fail
              4893 BEQ @next ; if the suffixes were disabled above)

              4895 @back DECR R4 ; the last digit either belongs to the next
              ; iteration or is invalid

              4896 @next MOVR R1, R2 ; use the current main digit
              ; as the next 1st suffix

              4897 SUBI #'I', R1 ; was it the last iteration? ...
              4899 BNEQ @loop

              489B CMP@ R4, R1 ; ... yes: make sure that we've also reached
              ; the end of the input

              489C PULR R7 ; return

              489D @tbl DECLE 'M', '-' ; table format: main digit, 2nd suffix
              489F DECLE 'C', 'D'
              48A1 DECLE 'X', 'L'
              48A3 DECLE 'I', 'V'

              ENDP


              How?



              The regular expression can be rewritten as 4 groups with the same structure, provided that # is any invalid character that is guaranteed not be present in the input string.



               +-------+---> main digit
              | |
              (M[##]|#?M0,3)(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)
              || |
              |+--+-----> prefix or second suffix
              |
              +---------> first suffix


              The first suffix of the group $N$ is the main digit of the group $N-1$. Therefore, we can store the patterns with the pair $(textmain_digit, textsecond_suffix)$ alone.



              Our routine attempts to parse the input string character by character according to these patterns and eventually checks whether the end of the string is reached.



              Output



              output



              screenshot of jzIntv




              1. A CP-1610 opcode is encoded with a 10-bit value, known as a 'DECLE'. This routine is 47 DECLEs long, starting at $4876 and ending at $48A4 (included).







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited 2 days ago

























              answered Apr 11 at 17:40









              ArnauldArnauld

              80.9k797334




              80.9k797334











              • $begingroup$
                wouldn't this be one of the few places where fractional bytes are valid
                $endgroup$
                – ASCII-only
                2 days ago










              • $begingroup$
                @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
                $endgroup$
                – Arnauld
                2 days ago










              • $begingroup$
                @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
                $endgroup$
                – Arnauld
                2 days ago










              • $begingroup$
                ah, so it's only 10 bits when it's in RAM?
                $endgroup$
                – ASCII-only
                2 days ago











              • $begingroup$
                The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
                $endgroup$
                – Arnauld
                2 days ago
















              • $begingroup$
                wouldn't this be one of the few places where fractional bytes are valid
                $endgroup$
                – ASCII-only
                2 days ago










              • $begingroup$
                @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
                $endgroup$
                – Arnauld
                2 days ago










              • $begingroup$
                @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
                $endgroup$
                – Arnauld
                2 days ago










              • $begingroup$
                ah, so it's only 10 bits when it's in RAM?
                $endgroup$
                – ASCII-only
                2 days ago











              • $begingroup$
                The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
                $endgroup$
                – Arnauld
                2 days ago















              $begingroup$
              wouldn't this be one of the few places where fractional bytes are valid
              $endgroup$
              – ASCII-only
              2 days ago




              $begingroup$
              wouldn't this be one of the few places where fractional bytes are valid
              $endgroup$
              – ASCII-only
              2 days ago












              $begingroup$
              @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
              $endgroup$
              – Arnauld
              2 days ago




              $begingroup$
              @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
              $endgroup$
              – Arnauld
              2 days ago












              $begingroup$
              @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
              $endgroup$
              – Arnauld
              2 days ago




              $begingroup$
              @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
              $endgroup$
              – Arnauld
              2 days ago












              $begingroup$
              ah, so it's only 10 bits when it's in RAM?
              $endgroup$
              – ASCII-only
              2 days ago





              $begingroup$
              ah, so it's only 10 bits when it's in RAM?
              $endgroup$
              – ASCII-only
              2 days ago













              $begingroup$
              The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
              $endgroup$
              – Arnauld
              2 days ago




              $begingroup$
              The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
              $endgroup$
              – Arnauld
              2 days ago











              7












              $begingroup$

              Java 8, 70 bytes





              s->s.matches("M0,3(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)")


              Port of @Innat3's C# answer, so make sure to upvote him!



              Try it online.



              Explanation:



              s-> // Method with String parameter and boolean return-type
              s.matches("...") // Check if the string matches the regex fully
              // (which implicitly adds a leading "^" and trailing "$")

              M0,3 // No, 1, 2, or 3 adjacent "M"
              ( | ) // Followed by either:
              C[MD] // A "C" with an "M" or "D" after it
              | // or:
              D? // An optional "D"
              C0,3 // Followed by no, 1, 2, or 3 adjacent "C"
              ( | ) // Followed by either:
              X[CL] // An "X" with a "C" or "L" after it
              | // or:
              L? // An optional "L"
              X0,3 // Followed by no, 1, 2, or 3 adjacent "X"
              ( | ) // Followed by either:
              I[XV] // An "I" with an "X" or "V" after it
              | // or:
              V? // An optional "V"
              I0,3 // Followed by no, 1, 2, or 3 adjacent "I"





              share|improve this answer









              $endgroup$

















                7












                $begingroup$

                Java 8, 70 bytes





                s->s.matches("M0,3(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)")


                Port of @Innat3's C# answer, so make sure to upvote him!



                Try it online.



                Explanation:



                s-> // Method with String parameter and boolean return-type
                s.matches("...") // Check if the string matches the regex fully
                // (which implicitly adds a leading "^" and trailing "$")

                M0,3 // No, 1, 2, or 3 adjacent "M"
                ( | ) // Followed by either:
                C[MD] // A "C" with an "M" or "D" after it
                | // or:
                D? // An optional "D"
                C0,3 // Followed by no, 1, 2, or 3 adjacent "C"
                ( | ) // Followed by either:
                X[CL] // An "X" with a "C" or "L" after it
                | // or:
                L? // An optional "L"
                X0,3 // Followed by no, 1, 2, or 3 adjacent "X"
                ( | ) // Followed by either:
                I[XV] // An "I" with an "X" or "V" after it
                | // or:
                V? // An optional "V"
                I0,3 // Followed by no, 1, 2, or 3 adjacent "I"





                share|improve this answer









                $endgroup$















                  7












                  7








                  7





                  $begingroup$

                  Java 8, 70 bytes





                  s->s.matches("M0,3(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)")


                  Port of @Innat3's C# answer, so make sure to upvote him!



                  Try it online.



                  Explanation:



                  s-> // Method with String parameter and boolean return-type
                  s.matches("...") // Check if the string matches the regex fully
                  // (which implicitly adds a leading "^" and trailing "$")

                  M0,3 // No, 1, 2, or 3 adjacent "M"
                  ( | ) // Followed by either:
                  C[MD] // A "C" with an "M" or "D" after it
                  | // or:
                  D? // An optional "D"
                  C0,3 // Followed by no, 1, 2, or 3 adjacent "C"
                  ( | ) // Followed by either:
                  X[CL] // An "X" with a "C" or "L" after it
                  | // or:
                  L? // An optional "L"
                  X0,3 // Followed by no, 1, 2, or 3 adjacent "X"
                  ( | ) // Followed by either:
                  I[XV] // An "I" with an "X" or "V" after it
                  | // or:
                  V? // An optional "V"
                  I0,3 // Followed by no, 1, 2, or 3 adjacent "I"





                  share|improve this answer









                  $endgroup$



                  Java 8, 70 bytes





                  s->s.matches("M0,3(C[MD]|D?C0,3)(X[CL]|L?X0,3)(I[XV]|V?I0,3)")


                  Port of @Innat3's C# answer, so make sure to upvote him!



                  Try it online.



                  Explanation:



                  s-> // Method with String parameter and boolean return-type
                  s.matches("...") // Check if the string matches the regex fully
                  // (which implicitly adds a leading "^" and trailing "$")

                  M0,3 // No, 1, 2, or 3 adjacent "M"
                  ( | ) // Followed by either:
                  C[MD] // A "C" with an "M" or "D" after it
                  | // or:
                  D? // An optional "D"
                  C0,3 // Followed by no, 1, 2, or 3 adjacent "C"
                  ( | ) // Followed by either:
                  X[CL] // An "X" with a "C" or "L" after it
                  | // or:
                  L? // An optional "L"
                  X0,3 // Followed by no, 1, 2, or 3 adjacent "X"
                  ( | ) // Followed by either:
                  I[XV] // An "I" with an "X" or "V" after it
                  | // or:
                  V? // An optional "V"
                  I0,3 // Followed by no, 1, 2, or 3 adjacent "I"






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Apr 11 at 14:07









                  Kevin CruijssenKevin Cruijssen

                  42.7k571217




                  42.7k571217





















                      5












                      $begingroup$


                      R, 74 71 56 bytes



                      Thanks to @RobinRyder, @Giuseppe, & @MickyT for their suggestions how to use grep effectively with R's built in as.roman.





                      sub("^M(.+)","\1",scan(,""))%in%paste(as.roman(1:2999))


                      Try it online!






                      share|improve this answer











                      $endgroup$












                      • $begingroup$
                        as.roman won't work anyway, since it only works up to 3899 for some reason.
                        $endgroup$
                        – Giuseppe
                        Apr 11 at 15:56











                      • $begingroup$
                        I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
                        $endgroup$
                        – CT Hall
                        Apr 11 at 16:01











                      • $begingroup$
                        @Giuseppe, addressed, using the same regex as the other answers.
                        $endgroup$
                        – CT Hall
                        Apr 11 at 16:26










                      • $begingroup$
                        I wonder if there's a way to use .romans here...probably not.
                        $endgroup$
                        – Giuseppe
                        Apr 11 at 16:30






                      • 2




                        $begingroup$
                        66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
                        $endgroup$
                        – Robin Ryder
                        Apr 11 at 17:49















                      5












                      $begingroup$


                      R, 74 71 56 bytes



                      Thanks to @RobinRyder, @Giuseppe, & @MickyT for their suggestions how to use grep effectively with R's built in as.roman.





                      sub("^M(.+)","\1",scan(,""))%in%paste(as.roman(1:2999))


                      Try it online!






                      share|improve this answer











                      $endgroup$












                      • $begingroup$
                        as.roman won't work anyway, since it only works up to 3899 for some reason.
                        $endgroup$
                        – Giuseppe
                        Apr 11 at 15:56











                      • $begingroup$
                        I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
                        $endgroup$
                        – CT Hall
                        Apr 11 at 16:01











                      • $begingroup$
                        @Giuseppe, addressed, using the same regex as the other answers.
                        $endgroup$
                        – CT Hall
                        Apr 11 at 16:26










                      • $begingroup$
                        I wonder if there's a way to use .romans here...probably not.
                        $endgroup$
                        – Giuseppe
                        Apr 11 at 16:30






                      • 2




                        $begingroup$
                        66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
                        $endgroup$
                        – Robin Ryder
                        Apr 11 at 17:49













                      5












                      5








                      5





                      $begingroup$


                      R, 74 71 56 bytes



                      Thanks to @RobinRyder, @Giuseppe, & @MickyT for their suggestions how to use grep effectively with R's built in as.roman.





                      sub("^M(.+)","\1",scan(,""))%in%paste(as.roman(1:2999))


                      Try it online!






                      share|improve this answer











                      $endgroup$




                      R, 74 71 56 bytes



                      Thanks to @RobinRyder, @Giuseppe, & @MickyT for their suggestions how to use grep effectively with R's built in as.roman.





                      sub("^M(.+)","\1",scan(,""))%in%paste(as.roman(1:2999))


                      Try it online!







                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited Apr 11 at 20:16

























                      answered Apr 11 at 15:54









                      CT HallCT Hall

                      51110




                      51110











                      • $begingroup$
                        as.roman won't work anyway, since it only works up to 3899 for some reason.
                        $endgroup$
                        – Giuseppe
                        Apr 11 at 15:56











                      • $begingroup$
                        I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
                        $endgroup$
                        – CT Hall
                        Apr 11 at 16:01











                      • $begingroup$
                        @Giuseppe, addressed, using the same regex as the other answers.
                        $endgroup$
                        – CT Hall
                        Apr 11 at 16:26










                      • $begingroup$
                        I wonder if there's a way to use .romans here...probably not.
                        $endgroup$
                        – Giuseppe
                        Apr 11 at 16:30






                      • 2




                        $begingroup$
                        66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
                        $endgroup$
                        – Robin Ryder
                        Apr 11 at 17:49
















                      • $begingroup$
                        as.roman won't work anyway, since it only works up to 3899 for some reason.
                        $endgroup$
                        – Giuseppe
                        Apr 11 at 15:56











                      • $begingroup$
                        I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
                        $endgroup$
                        – CT Hall
                        Apr 11 at 16:01











                      • $begingroup$
                        @Giuseppe, addressed, using the same regex as the other answers.
                        $endgroup$
                        – CT Hall
                        Apr 11 at 16:26










                      • $begingroup$
                        I wonder if there's a way to use .romans here...probably not.
                        $endgroup$
                        – Giuseppe
                        Apr 11 at 16:30






                      • 2




                        $begingroup$
                        66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
                        $endgroup$
                        – Robin Ryder
                        Apr 11 at 17:49















                      $begingroup$
                      as.roman won't work anyway, since it only works up to 3899 for some reason.
                      $endgroup$
                      – Giuseppe
                      Apr 11 at 15:56





                      $begingroup$
                      as.roman won't work anyway, since it only works up to 3899 for some reason.
                      $endgroup$
                      – Giuseppe
                      Apr 11 at 15:56













                      $begingroup$
                      I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
                      $endgroup$
                      – CT Hall
                      Apr 11 at 16:01





                      $begingroup$
                      I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
                      $endgroup$
                      – CT Hall
                      Apr 11 at 16:01













                      $begingroup$
                      @Giuseppe, addressed, using the same regex as the other answers.
                      $endgroup$
                      – CT Hall
                      Apr 11 at 16:26




                      $begingroup$
                      @Giuseppe, addressed, using the same regex as the other answers.
                      $endgroup$
                      – CT Hall
                      Apr 11 at 16:26












                      $begingroup$
                      I wonder if there's a way to use .romans here...probably not.
                      $endgroup$
                      – Giuseppe
                      Apr 11 at 16:30




                      $begingroup$
                      I wonder if there's a way to use .romans here...probably not.
                      $endgroup$
                      – Giuseppe
                      Apr 11 at 16:30




                      2




                      2




                      $begingroup$
                      66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
                      $endgroup$
                      – Robin Ryder
                      Apr 11 at 17:49




                      $begingroup$
                      66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
                      $endgroup$
                      – Robin Ryder
                      Apr 11 at 17:49











                      4












                      $begingroup$


                      Wolfram Language (Mathematica), 32 bytes



                      RomanNumeral@Range@3999~Count~#&


                      Try it online!






                      share|improve this answer









                      $endgroup$

















                        4












                        $begingroup$


                        Wolfram Language (Mathematica), 32 bytes



                        RomanNumeral@Range@3999~Count~#&


                        Try it online!






                        share|improve this answer









                        $endgroup$















                          4












                          4








                          4





                          $begingroup$


                          Wolfram Language (Mathematica), 32 bytes



                          RomanNumeral@Range@3999~Count~#&


                          Try it online!






                          share|improve this answer









                          $endgroup$




                          Wolfram Language (Mathematica), 32 bytes



                          RomanNumeral@Range@3999~Count~#&


                          Try it online!







                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Apr 11 at 16:15









                          Expired DataExpired Data

                          948217




                          948217





















                              2












                              $begingroup$


                              Jelly,  48 47 46  44 bytes



                              -1 thanks to Nick Kennedy



                              5Żo7;“ÆæC‘ð“IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ


                              A monadic Link accepting a non-empty list of characters consisting only of IVXLCDM which yields either 1 (when it's a valid Roman numeral between $1$ and $3999$) or 0 (if not).



                              Try it online! Or see the test-suite.



                              How?



                              5Żo7;“ÆæC‘ð“IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - Main Link: list of characters S

                              5Żo7;“ÆæC‘ - chain 1: f(S) -> X
                              5Ż - zero range of five = [0,1,2,3,4,5]
                              o7 - OR seven [7,1,2,3,4,5]
                              “ÆæC‘ - list of code-page indices [13,22,67]
                              ; - concatenate [7,1,2,3,4,5,13,22,67]

                              ð - start a new dyadic chain...

                              “IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - chain 2: f(X,S) -> isValid
                              “IVXLCDM” - list of characters, IVXLCDM
                              3Ƥ - for infixes of length three:
                              - (i.e. IVX VXL XLC LCD CDM)
                              ṃ@ - base decompression with swapped arguments
                              - (i.e. use characters as base-3 digits of X's values)
                              - (e.g. IVX -> VI I V IX II IV III VII VIII)
                              m2 - modulo two slice (results for IVX XLC and CDM only)
                              ¤ - nilad followed by link(s) as a nilad:
                              ”M - character 'M'
                              Ɱ3 - map across [1,2,3] with:
                              ẋ - repeat -> M MM MMM
                              ṭ - tack
                              Ż€ - prepend a zero to each
                              Ṛ - reverse
                              - -- now we have the table:
                              - 0 M MM MMM
                              - 0 DC C D CM CC CD CCC DCC DCCC
                              - 0 LX X L XC XX XL XXX LXX LXXX
                              - 0 VI I V IX II IV III VII VIII
                              Œp - Cartesian product [[0,0,0,0],...,["M","CM",0,"IV"],...]
                              F€ - flatten €ach [[0,0,0,0],...,['M','C','M',0,'I','V'],...]
                              ḟ€0 - filter out the zeros from €ach ["",...,"MCMIV",...]
                              ċ - count occurrences of S





                              share|improve this answer











                              $endgroup$












                              • $begingroup$
                                There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
                                $endgroup$
                                – Nick Kennedy
                                2 days ago











                              • $begingroup$
                                Thanks, I've saved one more from it.
                                $endgroup$
                                – Jonathan Allan
                                2 days ago















                              2












                              $begingroup$


                              Jelly,  48 47 46  44 bytes



                              -1 thanks to Nick Kennedy



                              5Żo7;“ÆæC‘ð“IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ


                              A monadic Link accepting a non-empty list of characters consisting only of IVXLCDM which yields either 1 (when it's a valid Roman numeral between $1$ and $3999$) or 0 (if not).



                              Try it online! Or see the test-suite.



                              How?



                              5Żo7;“ÆæC‘ð“IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - Main Link: list of characters S

                              5Żo7;“ÆæC‘ - chain 1: f(S) -> X
                              5Ż - zero range of five = [0,1,2,3,4,5]
                              o7 - OR seven [7,1,2,3,4,5]
                              “ÆæC‘ - list of code-page indices [13,22,67]
                              ; - concatenate [7,1,2,3,4,5,13,22,67]

                              ð - start a new dyadic chain...

                              “IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - chain 2: f(X,S) -> isValid
                              “IVXLCDM” - list of characters, IVXLCDM
                              3Ƥ - for infixes of length three:
                              - (i.e. IVX VXL XLC LCD CDM)
                              ṃ@ - base decompression with swapped arguments
                              - (i.e. use characters as base-3 digits of X's values)
                              - (e.g. IVX -> VI I V IX II IV III VII VIII)
                              m2 - modulo two slice (results for IVX XLC and CDM only)
                              ¤ - nilad followed by link(s) as a nilad:
                              ”M - character 'M'
                              Ɱ3 - map across [1,2,3] with:
                              ẋ - repeat -> M MM MMM
                              ṭ - tack
                              Ż€ - prepend a zero to each
                              Ṛ - reverse
                              - -- now we have the table:
                              - 0 M MM MMM
                              - 0 DC C D CM CC CD CCC DCC DCCC
                              - 0 LX X L XC XX XL XXX LXX LXXX
                              - 0 VI I V IX II IV III VII VIII
                              Œp - Cartesian product [[0,0,0,0],...,["M","CM",0,"IV"],...]
                              F€ - flatten €ach [[0,0,0,0],...,['M','C','M',0,'I','V'],...]
                              ḟ€0 - filter out the zeros from €ach ["",...,"MCMIV",...]
                              ċ - count occurrences of S





                              share|improve this answer











                              $endgroup$












                              • $begingroup$
                                There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
                                $endgroup$
                                – Nick Kennedy
                                2 days ago











                              • $begingroup$
                                Thanks, I've saved one more from it.
                                $endgroup$
                                – Jonathan Allan
                                2 days ago













                              2












                              2








                              2





                              $begingroup$


                              Jelly,  48 47 46  44 bytes



                              -1 thanks to Nick Kennedy



                              5Żo7;“ÆæC‘ð“IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ


                              A monadic Link accepting a non-empty list of characters consisting only of IVXLCDM which yields either 1 (when it's a valid Roman numeral between $1$ and $3999$) or 0 (if not).



                              Try it online! Or see the test-suite.



                              How?



                              5Żo7;“ÆæC‘ð“IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - Main Link: list of characters S

                              5Żo7;“ÆæC‘ - chain 1: f(S) -> X
                              5Ż - zero range of five = [0,1,2,3,4,5]
                              o7 - OR seven [7,1,2,3,4,5]
                              “ÆæC‘ - list of code-page indices [13,22,67]
                              ; - concatenate [7,1,2,3,4,5,13,22,67]

                              ð - start a new dyadic chain...

                              “IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - chain 2: f(X,S) -> isValid
                              “IVXLCDM” - list of characters, IVXLCDM
                              3Ƥ - for infixes of length three:
                              - (i.e. IVX VXL XLC LCD CDM)
                              ṃ@ - base decompression with swapped arguments
                              - (i.e. use characters as base-3 digits of X's values)
                              - (e.g. IVX -> VI I V IX II IV III VII VIII)
                              m2 - modulo two slice (results for IVX XLC and CDM only)
                              ¤ - nilad followed by link(s) as a nilad:
                              ”M - character 'M'
                              Ɱ3 - map across [1,2,3] with:
                              ẋ - repeat -> M MM MMM
                              ṭ - tack
                              Ż€ - prepend a zero to each
                              Ṛ - reverse
                              - -- now we have the table:
                              - 0 M MM MMM
                              - 0 DC C D CM CC CD CCC DCC DCCC
                              - 0 LX X L XC XX XL XXX LXX LXXX
                              - 0 VI I V IX II IV III VII VIII
                              Œp - Cartesian product [[0,0,0,0],...,["M","CM",0,"IV"],...]
                              F€ - flatten €ach [[0,0,0,0],...,['M','C','M',0,'I','V'],...]
                              ḟ€0 - filter out the zeros from €ach ["",...,"MCMIV",...]
                              ċ - count occurrences of S





                              share|improve this answer











                              $endgroup$




                              Jelly,  48 47 46  44 bytes



                              -1 thanks to Nick Kennedy



                              5Żo7;“ÆæC‘ð“IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ


                              A monadic Link accepting a non-empty list of characters consisting only of IVXLCDM which yields either 1 (when it's a valid Roman numeral between $1$ and $3999$) or 0 (if not).



                              Try it online! Or see the test-suite.



                              How?



                              5Żo7;“ÆæC‘ð“IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - Main Link: list of characters S

                              5Żo7;“ÆæC‘ - chain 1: f(S) -> X
                              5Ż - zero range of five = [0,1,2,3,4,5]
                              o7 - OR seven [7,1,2,3,4,5]
                              “ÆæC‘ - list of code-page indices [13,22,67]
                              ; - concatenate [7,1,2,3,4,5,13,22,67]

                              ð - start a new dyadic chain...

                              “IVXLCDM”ṃ@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - chain 2: f(X,S) -> isValid
                              “IVXLCDM” - list of characters, IVXLCDM
                              3Ƥ - for infixes of length three:
                              - (i.e. IVX VXL XLC LCD CDM)
                              ṃ@ - base decompression with swapped arguments
                              - (i.e. use characters as base-3 digits of X's values)
                              - (e.g. IVX -> VI I V IX II IV III VII VIII)
                              m2 - modulo two slice (results for IVX XLC and CDM only)
                              ¤ - nilad followed by link(s) as a nilad:
                              ”M - character 'M'
                              Ɱ3 - map across [1,2,3] with:
                              ẋ - repeat -> M MM MMM
                              ṭ - tack
                              Ż€ - prepend a zero to each
                              Ṛ - reverse
                              - -- now we have the table:
                              - 0 M MM MMM
                              - 0 DC C D CM CC CD CCC DCC DCCC
                              - 0 LX X L XC XX XL XXX LXX LXXX
                              - 0 VI I V IX II IV III VII VIII
                              Œp - Cartesian product [[0,0,0,0],...,["M","CM",0,"IV"],...]
                              F€ - flatten €ach [[0,0,0,0],...,['M','C','M',0,'I','V'],...]
                              ḟ€0 - filter out the zeros from €ach ["",...,"MCMIV",...]
                              ċ - count occurrences of S






                              share|improve this answer














                              share|improve this answer



                              share|improve this answer








                              edited yesterday

























                              answered Apr 11 at 19:53









                              Jonathan AllanJonathan Allan

                              54.3k537174




                              54.3k537174











                              • $begingroup$
                                There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
                                $endgroup$
                                – Nick Kennedy
                                2 days ago











                              • $begingroup$
                                Thanks, I've saved one more from it.
                                $endgroup$
                                – Jonathan Allan
                                2 days ago
















                              • $begingroup$
                                There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
                                $endgroup$
                                – Nick Kennedy
                                2 days ago











                              • $begingroup$
                                Thanks, I've saved one more from it.
                                $endgroup$
                                – Jonathan Allan
                                2 days ago















                              $begingroup$
                              There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
                              $endgroup$
                              – Nick Kennedy
                              2 days ago





                              $begingroup$
                              There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
                              $endgroup$
                              – Nick Kennedy
                              2 days ago













                              $begingroup$
                              Thanks, I've saved one more from it.
                              $endgroup$
                              – Jonathan Allan
                              2 days ago




                              $begingroup$
                              Thanks, I've saved one more from it.
                              $endgroup$
                              – Jonathan Allan
                              2 days ago











                              1












                              $begingroup$

                              Perl 5 (-p), 57 bytes



                              $_=/^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$/&!/(.)13/


                              TIO



                              • uses almost the same regular expression except 0,3 quantifier was changed by *


                              • &!/(.)13/ to ensure the same character can't occur 4 times in a row.

                              • can't be golfed with -/(.)13/ because would give-1 for IIIIVI for example





                              share|improve this answer











                              $endgroup$

















                                1












                                $begingroup$

                                Perl 5 (-p), 57 bytes



                                $_=/^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$/&!/(.)13/


                                TIO



                                • uses almost the same regular expression except 0,3 quantifier was changed by *


                                • &!/(.)13/ to ensure the same character can't occur 4 times in a row.

                                • can't be golfed with -/(.)13/ because would give-1 for IIIIVI for example





                                share|improve this answer











                                $endgroup$















                                  1












                                  1








                                  1





                                  $begingroup$

                                  Perl 5 (-p), 57 bytes



                                  $_=/^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$/&!/(.)13/


                                  TIO



                                  • uses almost the same regular expression except 0,3 quantifier was changed by *


                                  • &!/(.)13/ to ensure the same character can't occur 4 times in a row.

                                  • can't be golfed with -/(.)13/ because would give-1 for IIIIVI for example





                                  share|improve this answer











                                  $endgroup$



                                  Perl 5 (-p), 57 bytes



                                  $_=/^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$/&!/(.)13/


                                  TIO



                                  • uses almost the same regular expression except 0,3 quantifier was changed by *


                                  • &!/(.)13/ to ensure the same character can't occur 4 times in a row.

                                  • can't be golfed with -/(.)13/ because would give-1 for IIIIVI for example






                                  share|improve this answer














                                  share|improve this answer



                                  share|improve this answer








                                  edited Apr 11 at 15:16

























                                  answered Apr 11 at 14:32









                                  Nahuel FouilleulNahuel Fouilleul

                                  3,035211




                                  3,035211





















                                      1












                                      $begingroup$


                                      Python 2, 81 bytes





                                      import re
                                      re.compile('M,3(D?C,3|C[DM])(L?X,3|X[LC])(V?I,3|I[VX])$').match


                                      Try it online!



                                      Let's look at the last part of the regex, which matching the Roman numerals up to 9 (including the empty string)



                                      V?I,3|I[VX]


                                      This has two alternatives separated by |:




                                      • V?I,3: An optional V followed by up to 3 I's. This matches the empty string I,II,III, V, VI,VII,VIII.


                                      • I[VX]: An I followed by a V or X. This matches IV and IX.

                                      The same things with X,L,C matching the tens, with C,D,M matches the hundreds, and finally ^M,3 allows up to 3 M's (thousands) at the start.



                                      I tried generating the template for each trio of characters rather than writing it 3 times, but this was a lot longer.






                                      share|improve this answer











                                      $endgroup$












                                      • $begingroup$
                                        No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
                                        $endgroup$
                                        – ShadowRanger
                                        Apr 12 at 1:28










                                      • $begingroup$
                                        @ShadowRanger Thanks, I removed the ^.
                                        $endgroup$
                                        – xnor
                                        Apr 12 at 1:29










                                      • $begingroup$
                                        Although I think you messed up the count in the edit; should be 83, not 81.
                                        $endgroup$
                                        – ShadowRanger
                                        Apr 12 at 1:40










                                      • $begingroup$
                                        @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
                                        $endgroup$
                                        – xnor
                                        Apr 12 at 1:42






                                      • 1




                                        $begingroup$
                                        Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
                                        $endgroup$
                                        – ShadowRanger
                                        Apr 12 at 1:44















                                      1












                                      $begingroup$


                                      Python 2, 81 bytes





                                      import re
                                      re.compile('M,3(D?C,3|C[DM])(L?X,3|X[LC])(V?I,3|I[VX])$').match


                                      Try it online!



                                      Let's look at the last part of the regex, which matching the Roman numerals up to 9 (including the empty string)



                                      V?I,3|I[VX]


                                      This has two alternatives separated by |:




                                      • V?I,3: An optional V followed by up to 3 I's. This matches the empty string I,II,III, V, VI,VII,VIII.


                                      • I[VX]: An I followed by a V or X. This matches IV and IX.

                                      The same things with X,L,C matching the tens, with C,D,M matches the hundreds, and finally ^M,3 allows up to 3 M's (thousands) at the start.



                                      I tried generating the template for each trio of characters rather than writing it 3 times, but this was a lot longer.






                                      share|improve this answer











                                      $endgroup$












                                      • $begingroup$
                                        No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
                                        $endgroup$
                                        – ShadowRanger
                                        Apr 12 at 1:28










                                      • $begingroup$
                                        @ShadowRanger Thanks, I removed the ^.
                                        $endgroup$
                                        – xnor
                                        Apr 12 at 1:29










                                      • $begingroup$
                                        Although I think you messed up the count in the edit; should be 83, not 81.
                                        $endgroup$
                                        – ShadowRanger
                                        Apr 12 at 1:40










                                      • $begingroup$
                                        @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
                                        $endgroup$
                                        – xnor
                                        Apr 12 at 1:42






                                      • 1




                                        $begingroup$
                                        Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
                                        $endgroup$
                                        – ShadowRanger
                                        Apr 12 at 1:44













                                      1












                                      1








                                      1





                                      $begingroup$


                                      Python 2, 81 bytes





                                      import re
                                      re.compile('M,3(D?C,3|C[DM])(L?X,3|X[LC])(V?I,3|I[VX])$').match


                                      Try it online!



                                      Let's look at the last part of the regex, which matching the Roman numerals up to 9 (including the empty string)



                                      V?I,3|I[VX]


                                      This has two alternatives separated by |:




                                      • V?I,3: An optional V followed by up to 3 I's. This matches the empty string I,II,III, V, VI,VII,VIII.


                                      • I[VX]: An I followed by a V or X. This matches IV and IX.

                                      The same things with X,L,C matching the tens, with C,D,M matches the hundreds, and finally ^M,3 allows up to 3 M's (thousands) at the start.



                                      I tried generating the template for each trio of characters rather than writing it 3 times, but this was a lot longer.






                                      share|improve this answer











                                      $endgroup$




                                      Python 2, 81 bytes





                                      import re
                                      re.compile('M,3(D?C,3|C[DM])(L?X,3|X[LC])(V?I,3|I[VX])$').match


                                      Try it online!



                                      Let's look at the last part of the regex, which matching the Roman numerals up to 9 (including the empty string)



                                      V?I,3|I[VX]


                                      This has two alternatives separated by |:




                                      • V?I,3: An optional V followed by up to 3 I's. This matches the empty string I,II,III, V, VI,VII,VIII.


                                      • I[VX]: An I followed by a V or X. This matches IV and IX.

                                      The same things with X,L,C matching the tens, with C,D,M matches the hundreds, and finally ^M,3 allows up to 3 M's (thousands) at the start.



                                      I tried generating the template for each trio of characters rather than writing it 3 times, but this was a lot longer.







                                      share|improve this answer














                                      share|improve this answer



                                      share|improve this answer








                                      edited Apr 12 at 1:29

























                                      answered Apr 12 at 0:35









                                      xnorxnor

                                      94.1k18192451




                                      94.1k18192451











                                      • $begingroup$
                                        No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
                                        $endgroup$
                                        – ShadowRanger
                                        Apr 12 at 1:28










                                      • $begingroup$
                                        @ShadowRanger Thanks, I removed the ^.
                                        $endgroup$
                                        – xnor
                                        Apr 12 at 1:29










                                      • $begingroup$
                                        Although I think you messed up the count in the edit; should be 83, not 81.
                                        $endgroup$
                                        – ShadowRanger
                                        Apr 12 at 1:40










                                      • $begingroup$
                                        @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
                                        $endgroup$
                                        – xnor
                                        Apr 12 at 1:42






                                      • 1




                                        $begingroup$
                                        Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
                                        $endgroup$
                                        – ShadowRanger
                                        Apr 12 at 1:44
















                                      • $begingroup$
                                        No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
                                        $endgroup$
                                        – ShadowRanger
                                        Apr 12 at 1:28










                                      • $begingroup$
                                        @ShadowRanger Thanks, I removed the ^.
                                        $endgroup$
                                        – xnor
                                        Apr 12 at 1:29










                                      • $begingroup$
                                        Although I think you messed up the count in the edit; should be 83, not 81.
                                        $endgroup$
                                        – ShadowRanger
                                        Apr 12 at 1:40










                                      • $begingroup$
                                        @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
                                        $endgroup$
                                        – xnor
                                        Apr 12 at 1:42






                                      • 1




                                        $begingroup$
                                        Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
                                        $endgroup$
                                        – ShadowRanger
                                        Apr 12 at 1:44















                                      $begingroup$
                                      No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
                                      $endgroup$
                                      – ShadowRanger
                                      Apr 12 at 1:28




                                      $begingroup$
                                      No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
                                      $endgroup$
                                      – ShadowRanger
                                      Apr 12 at 1:28












                                      $begingroup$
                                      @ShadowRanger Thanks, I removed the ^.
                                      $endgroup$
                                      – xnor
                                      Apr 12 at 1:29




                                      $begingroup$
                                      @ShadowRanger Thanks, I removed the ^.
                                      $endgroup$
                                      – xnor
                                      Apr 12 at 1:29












                                      $begingroup$
                                      Although I think you messed up the count in the edit; should be 83, not 81.
                                      $endgroup$
                                      – ShadowRanger
                                      Apr 12 at 1:40




                                      $begingroup$
                                      Although I think you messed up the count in the edit; should be 83, not 81.
                                      $endgroup$
                                      – ShadowRanger
                                      Apr 12 at 1:40












                                      $begingroup$
                                      @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
                                      $endgroup$
                                      – xnor
                                      Apr 12 at 1:42




                                      $begingroup$
                                      @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
                                      $endgroup$
                                      – xnor
                                      Apr 12 at 1:42




                                      1




                                      1




                                      $begingroup$
                                      Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
                                      $endgroup$
                                      – ShadowRanger
                                      Apr 12 at 1:44




                                      $begingroup$
                                      Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
                                      $endgroup$
                                      – ShadowRanger
                                      Apr 12 at 1:44











                                      1












                                      $begingroup$


                                      Retina, 56 51 bytes



                                      (.)13
                                      0
                                      ^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$


                                      Port of @NahuelFouilleul's Perl 5 answer, so make sure to upvote him!



                                      Try it online or verify all test cases.



                                      Explanation:





                                      (.)13 # If four adjacent characters can be found which are the same
                                      0 # Replace it with a 0

                                      ^...$ # Then check if the string matches the following fully:
                                      M* # No or any amount of adjacent "M"
                                      ( | ) # Followed by either:
                                      C[MD] # A "C" with an "M" or "D" after it
                                      | # or:
                                      D? # An optional "D"
                                      C* # Followed by no or any amount of adjacent "C"
                                      ( | ) # Followed by either:
                                      X[CL] # An "X" with a "C" or "L" after it
                                      | # or:
                                      L? # An optional "L"
                                      X* # Followed by no or any amount of adjacent "X"
                                      ( | ) # Followed by either:
                                      I[XV] # An "I" with an "X" or "V" after it
                                      | # or:
                                      V? # An optional "V"
                                      I* # Followed by no or any amount of adjacent "I"





                                      share|improve this answer











                                      $endgroup$

















                                        1












                                        $begingroup$


                                        Retina, 56 51 bytes



                                        (.)13
                                        0
                                        ^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$


                                        Port of @NahuelFouilleul's Perl 5 answer, so make sure to upvote him!



                                        Try it online or verify all test cases.



                                        Explanation:





                                        (.)13 # If four adjacent characters can be found which are the same
                                        0 # Replace it with a 0

                                        ^...$ # Then check if the string matches the following fully:
                                        M* # No or any amount of adjacent "M"
                                        ( | ) # Followed by either:
                                        C[MD] # A "C" with an "M" or "D" after it
                                        | # or:
                                        D? # An optional "D"
                                        C* # Followed by no or any amount of adjacent "C"
                                        ( | ) # Followed by either:
                                        X[CL] # An "X" with a "C" or "L" after it
                                        | # or:
                                        L? # An optional "L"
                                        X* # Followed by no or any amount of adjacent "X"
                                        ( | ) # Followed by either:
                                        I[XV] # An "I" with an "X" or "V" after it
                                        | # or:
                                        V? # An optional "V"
                                        I* # Followed by no or any amount of adjacent "I"





                                        share|improve this answer











                                        $endgroup$















                                          1












                                          1








                                          1





                                          $begingroup$


                                          Retina, 56 51 bytes



                                          (.)13
                                          0
                                          ^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$


                                          Port of @NahuelFouilleul's Perl 5 answer, so make sure to upvote him!



                                          Try it online or verify all test cases.



                                          Explanation:





                                          (.)13 # If four adjacent characters can be found which are the same
                                          0 # Replace it with a 0

                                          ^...$ # Then check if the string matches the following fully:
                                          M* # No or any amount of adjacent "M"
                                          ( | ) # Followed by either:
                                          C[MD] # A "C" with an "M" or "D" after it
                                          | # or:
                                          D? # An optional "D"
                                          C* # Followed by no or any amount of adjacent "C"
                                          ( | ) # Followed by either:
                                          X[CL] # An "X" with a "C" or "L" after it
                                          | # or:
                                          L? # An optional "L"
                                          X* # Followed by no or any amount of adjacent "X"
                                          ( | ) # Followed by either:
                                          I[XV] # An "I" with an "X" or "V" after it
                                          | # or:
                                          V? # An optional "V"
                                          I* # Followed by no or any amount of adjacent "I"





                                          share|improve this answer











                                          $endgroup$




                                          Retina, 56 51 bytes



                                          (.)13
                                          0
                                          ^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$


                                          Port of @NahuelFouilleul's Perl 5 answer, so make sure to upvote him!



                                          Try it online or verify all test cases.



                                          Explanation:





                                          (.)13 # If four adjacent characters can be found which are the same
                                          0 # Replace it with a 0

                                          ^...$ # Then check if the string matches the following fully:
                                          M* # No or any amount of adjacent "M"
                                          ( | ) # Followed by either:
                                          C[MD] # A "C" with an "M" or "D" after it
                                          | # or:
                                          D? # An optional "D"
                                          C* # Followed by no or any amount of adjacent "C"
                                          ( | ) # Followed by either:
                                          X[CL] # An "X" with a "C" or "L" after it
                                          | # or:
                                          L? # An optional "L"
                                          X* # Followed by no or any amount of adjacent "X"
                                          ( | ) # Followed by either:
                                          I[XV] # An "I" with an "X" or "V" after it
                                          | # or:
                                          V? # An optional "V"
                                          I* # Followed by no or any amount of adjacent "I"






                                          share|improve this answer














                                          share|improve this answer



                                          share|improve this answer








                                          edited 2 days ago

























                                          answered 2 days ago









                                          Kevin CruijssenKevin Cruijssen

                                          42.7k571217




                                          42.7k571217





















                                              1












                                              $begingroup$


                                              05AB1E, 61 9 8 bytes



                                              ŽF¯L.XIå


                                              Whopping $colorgreentextrm-52 bytes$ thanks to @Adnan, because apparently 05AB1E's Roman Number builtin wasn't documented, haha.. xD



                                              Try it online or verify all test cases.



                                              Explanation:





                                              ŽF¯ # Push comressed integer 3999
                                              L # Create a list in the range [1,3999]
                                              .X # Convert each integer in this list to a roman number string
                                              Iå # Check if the input is in this list
                                              # (and output the result implicitly)




                                              See this 05AB1E tip of mine (section How to compress large integers?) to understand why ŽF¯ is 3999.




                                              Original 61 bytes answer:



                                              •1∞Γ'иÛnuÞ₂…•Ž8вв€SÐ)v.•6#&‘нδ•u3ôNèyè}'M3L×)Rεõš}`3Fâ}€˜JIå


                                              Try it online or verify all test cases.



                                              Explanation:





                                              •1∞Γ'иÛnuÞ₂…• '# Push compressed integer 397940501547566186191992778
                                              Ž8в # Push compressed integer 2112
                                              в # Convert the integer to Base-2112 as list:
                                              # [1,11,111,12,2,21,211,2111,10]
                                              €S # Convert each number to a list of digits
                                              Ð # Triplicate this list
                                              ) # And wrap it into a list of lists (of lists)
                                              v # Loop `y` over each these three lists:
                                              .•6#&‘нδ• # Push compressed string "xivcxlmcd"
                                              u # Uppercased
                                              3ô # And split into parts of size 3: ["XIV","CXL","MCD"]
                                              Nè # Use the loop index to get the current part
                                              yè # And index the list of lists of digits into this string
                                              }'M '# After the loop: push "M"
                                              3L # Push list [1,2,3]
                                              × # Repeat the "M" that many times: ["M","MM","MMM"]
                                              ) # Wrap all lists on the stack into a list:
                                              # [[["I"],["I","I"],["I","I","I"],["I","V"],["V"],["V","I"],["V","I","I"],["V","I","I","I"],["I","X"]],[["X"],["X","X"],["X","X","X"],["X","L"],["L"],["L","X"],["L","X","X"],["L","X","X","X"],["X","C"]],[["C"],["C","C"],["C","C","C"],["C","D"],["D"],["D","C"],["D","C","C"],["D","C","C","C"],["C","M"]],["M","MM","MMM"]]
                                              R # Reverse this list
                                              εõš} # Prepend an empty string "" before each inner list
                                              ` # Push the four lists onto the stack
                                              3F # Loop 3 times:
                                              â # Take the cartesian product of the two top lists
                                              }€˜ # After the loop: flatten each inner list
                                              J # Join each inner list together to a single string
                                              Iå # And check if the input is in this list
                                              # (after which the result is output implicitly)


                                              See this 05AB1E tip of mine (sections How to compress strings not part of the dictionary?, How to compress large integers?, and How to compress integer lists?) to understand why:




                                              • •1∞Γ'иÛnuÞ₂…• is 397940501547566186191992778


                                              • Ž8в is 2112


                                              • •1∞Γ'иÛnuÞ₂…•Ž8вв is [1,11,111,12,2,21,211,2111,10]


                                              • .•6#&‘нδ• is "xivcxlmcd"





                                              share|improve this answer











                                              $endgroup$








                                              • 1




                                                $begingroup$
                                                I'm not sure why .X is not documented, but I think this should work: 3999L.XQO
                                                $endgroup$
                                                – Adnan
                                                yesterday










                                              • $begingroup$
                                                @Adnan Haha, -52 bytes right there. Completely forgot you indeed told us about adding a Roman Number builtin. Will ask @Mr.Xcoder in chat to add it to the docs. Are any other commands missing? ;) PS: Saved another byte by compressing 3999. :)
                                                $endgroup$
                                                – Kevin Cruijssen
                                                yesterday















                                              1












                                              $begingroup$


                                              05AB1E, 61 9 8 bytes



                                              ŽF¯L.XIå


                                              Whopping $colorgreentextrm-52 bytes$ thanks to @Adnan, because apparently 05AB1E's Roman Number builtin wasn't documented, haha.. xD



                                              Try it online or verify all test cases.



                                              Explanation:





                                              ŽF¯ # Push comressed integer 3999
                                              L # Create a list in the range [1,3999]
                                              .X # Convert each integer in this list to a roman number string
                                              Iå # Check if the input is in this list
                                              # (and output the result implicitly)




                                              See this 05AB1E tip of mine (section How to compress large integers?) to understand why ŽF¯ is 3999.




                                              Original 61 bytes answer:



                                              •1∞Γ'иÛnuÞ₂…•Ž8вв€SÐ)v.•6#&‘нδ•u3ôNèyè}'M3L×)Rεõš}`3Fâ}€˜JIå


                                              Try it online or verify all test cases.



                                              Explanation:





                                              •1∞Γ'иÛnuÞ₂…• '# Push compressed integer 397940501547566186191992778
                                              Ž8в # Push compressed integer 2112
                                              в # Convert the integer to Base-2112 as list:
                                              # [1,11,111,12,2,21,211,2111,10]
                                              €S # Convert each number to a list of digits
                                              Ð # Triplicate this list
                                              ) # And wrap it into a list of lists (of lists)
                                              v # Loop `y` over each these three lists:
                                              .•6#&‘нδ• # Push compressed string "xivcxlmcd"
                                              u # Uppercased
                                              3ô # And split into parts of size 3: ["XIV","CXL","MCD"]
                                              Nè # Use the loop index to get the current part
                                              yè # And index the list of lists of digits into this string
                                              }'M '# After the loop: push "M"
                                              3L # Push list [1,2,3]
                                              × # Repeat the "M" that many times: ["M","MM","MMM"]
                                              ) # Wrap all lists on the stack into a list:
                                              # [[["I"],["I","I"],["I","I","I"],["I","V"],["V"],["V","I"],["V","I","I"],["V","I","I","I"],["I","X"]],[["X"],["X","X"],["X","X","X"],["X","L"],["L"],["L","X"],["L","X","X"],["L","X","X","X"],["X","C"]],[["C"],["C","C"],["C","C","C"],["C","D"],["D"],["D","C"],["D","C","C"],["D","C","C","C"],["C","M"]],["M","MM","MMM"]]
                                              R # Reverse this list
                                              εõš} # Prepend an empty string "" before each inner list
                                              ` # Push the four lists onto the stack
                                              3F # Loop 3 times:
                                              â # Take the cartesian product of the two top lists
                                              }€˜ # After the loop: flatten each inner list
                                              J # Join each inner list together to a single string
                                              Iå # And check if the input is in this list
                                              # (after which the result is output implicitly)


                                              See this 05AB1E tip of mine (sections How to compress strings not part of the dictionary?, How to compress large integers?, and How to compress integer lists?) to understand why:




                                              • •1∞Γ'иÛnuÞ₂…• is 397940501547566186191992778


                                              • Ž8в is 2112


                                              • •1∞Γ'иÛnuÞ₂…•Ž8вв is [1,11,111,12,2,21,211,2111,10]


                                              • .•6#&‘нδ• is "xivcxlmcd"





                                              share|improve this answer











                                              $endgroup$








                                              • 1




                                                $begingroup$
                                                I'm not sure why .X is not documented, but I think this should work: 3999L.XQO
                                                $endgroup$
                                                – Adnan
                                                yesterday










                                              • $begingroup$
                                                @Adnan Haha, -52 bytes right there. Completely forgot you indeed told us about adding a Roman Number builtin. Will ask @Mr.Xcoder in chat to add it to the docs. Are any other commands missing? ;) PS: Saved another byte by compressing 3999. :)
                                                $endgroup$
                                                – Kevin Cruijssen
                                                yesterday













                                              1












                                              1








                                              1





                                              $begingroup$


                                              05AB1E, 61 9 8 bytes



                                              ŽF¯L.XIå


                                              Whopping $colorgreentextrm-52 bytes$ thanks to @Adnan, because apparently 05AB1E's Roman Number builtin wasn't documented, haha.. xD



                                              Try it online or verify all test cases.



                                              Explanation:





                                              ŽF¯ # Push comressed integer 3999
                                              L # Create a list in the range [1,3999]
                                              .X # Convert each integer in this list to a roman number string
                                              Iå # Check if the input is in this list
                                              # (and output the result implicitly)




                                              See this 05AB1E tip of mine (section How to compress large integers?) to understand why ŽF¯ is 3999.




                                              Original 61 bytes answer:



                                              •1∞Γ'иÛnuÞ₂…•Ž8вв€SÐ)v.•6#&‘нδ•u3ôNèyè}'M3L×)Rεõš}`3Fâ}€˜JIå


                                              Try it online or verify all test cases.



                                              Explanation:





                                              •1∞Γ'иÛnuÞ₂…• '# Push compressed integer 397940501547566186191992778
                                              Ž8в # Push compressed integer 2112
                                              в # Convert the integer to Base-2112 as list:
                                              # [1,11,111,12,2,21,211,2111,10]
                                              €S # Convert each number to a list of digits
                                              Ð # Triplicate this list
                                              ) # And wrap it into a list of lists (of lists)
                                              v # Loop `y` over each these three lists:
                                              .•6#&‘нδ• # Push compressed string "xivcxlmcd"
                                              u # Uppercased
                                              3ô # And split into parts of size 3: ["XIV","CXL","MCD"]
                                              Nè # Use the loop index to get the current part
                                              yè # And index the list of lists of digits into this string
                                              }'M '# After the loop: push "M"
                                              3L # Push list [1,2,3]
                                              × # Repeat the "M" that many times: ["M","MM","MMM"]
                                              ) # Wrap all lists on the stack into a list:
                                              # [[["I"],["I","I"],["I","I","I"],["I","V"],["V"],["V","I"],["V","I","I"],["V","I","I","I"],["I","X"]],[["X"],["X","X"],["X","X","X"],["X","L"],["L"],["L","X"],["L","X","X"],["L","X","X","X"],["X","C"]],[["C"],["C","C"],["C","C","C"],["C","D"],["D"],["D","C"],["D","C","C"],["D","C","C","C"],["C","M"]],["M","MM","MMM"]]
                                              R # Reverse this list
                                              εõš} # Prepend an empty string "" before each inner list
                                              ` # Push the four lists onto the stack
                                              3F # Loop 3 times:
                                              â # Take the cartesian product of the two top lists
                                              }€˜ # After the loop: flatten each inner list
                                              J # Join each inner list together to a single string
                                              Iå # And check if the input is in this list
                                              # (after which the result is output implicitly)


                                              See this 05AB1E tip of mine (sections How to compress strings not part of the dictionary?, How to compress large integers?, and How to compress integer lists?) to understand why:




                                              • •1∞Γ'иÛnuÞ₂…• is 397940501547566186191992778


                                              • Ž8в is 2112


                                              • •1∞Γ'иÛnuÞ₂…•Ž8вв is [1,11,111,12,2,21,211,2111,10]


                                              • .•6#&‘нδ• is "xivcxlmcd"





                                              share|improve this answer











                                              $endgroup$




                                              05AB1E, 61 9 8 bytes



                                              ŽF¯L.XIå


                                              Whopping $colorgreentextrm-52 bytes$ thanks to @Adnan, because apparently 05AB1E's Roman Number builtin wasn't documented, haha.. xD



                                              Try it online or verify all test cases.



                                              Explanation:





                                              ŽF¯ # Push comressed integer 3999
                                              L # Create a list in the range [1,3999]
                                              .X # Convert each integer in this list to a roman number string
                                              Iå # Check if the input is in this list
                                              # (and output the result implicitly)




                                              See this 05AB1E tip of mine (section How to compress large integers?) to understand why ŽF¯ is 3999.




                                              Original 61 bytes answer:



                                              •1∞Γ'иÛnuÞ₂…•Ž8вв€SÐ)v.•6#&‘нδ•u3ôNèyè}'M3L×)Rεõš}`3Fâ}€˜JIå


                                              Try it online or verify all test cases.



                                              Explanation:





                                              •1∞Γ'иÛnuÞ₂…• '# Push compressed integer 397940501547566186191992778
                                              Ž8в # Push compressed integer 2112
                                              в # Convert the integer to Base-2112 as list:
                                              # [1,11,111,12,2,21,211,2111,10]
                                              €S # Convert each number to a list of digits
                                              Ð # Triplicate this list
                                              ) # And wrap it into a list of lists (of lists)
                                              v # Loop `y` over each these three lists:
                                              .•6#&‘нδ• # Push compressed string "xivcxlmcd"
                                              u # Uppercased
                                              3ô # And split into parts of size 3: ["XIV","CXL","MCD"]
                                              Nè # Use the loop index to get the current part
                                              yè # And index the list of lists of digits into this string
                                              }'M '# After the loop: push "M"
                                              3L # Push list [1,2,3]
                                              × # Repeat the "M" that many times: ["M","MM","MMM"]
                                              ) # Wrap all lists on the stack into a list:
                                              # [[["I"],["I","I"],["I","I","I"],["I","V"],["V"],["V","I"],["V","I","I"],["V","I","I","I"],["I","X"]],[["X"],["X","X"],["X","X","X"],["X","L"],["L"],["L","X"],["L","X","X"],["L","X","X","X"],["X","C"]],[["C"],["C","C"],["C","C","C"],["C","D"],["D"],["D","C"],["D","C","C"],["D","C","C","C"],["C","M"]],["M","MM","MMM"]]
                                              R # Reverse this list
                                              εõš} # Prepend an empty string "" before each inner list
                                              ` # Push the four lists onto the stack
                                              3F # Loop 3 times:
                                              â # Take the cartesian product of the two top lists
                                              }€˜ # After the loop: flatten each inner list
                                              J # Join each inner list together to a single string
                                              Iå # And check if the input is in this list
                                              # (after which the result is output implicitly)


                                              See this 05AB1E tip of mine (sections How to compress strings not part of the dictionary?, How to compress large integers?, and How to compress integer lists?) to understand why:




                                              • •1∞Γ'иÛnuÞ₂…• is 397940501547566186191992778


                                              • Ž8в is 2112


                                              • •1∞Γ'иÛnuÞ₂…•Ž8вв is [1,11,111,12,2,21,211,2111,10]


                                              • .•6#&‘нδ• is "xivcxlmcd"






                                              share|improve this answer














                                              share|improve this answer



                                              share|improve this answer








                                              edited yesterday

























                                              answered Apr 11 at 21:06









                                              Kevin CruijssenKevin Cruijssen

                                              42.7k571217




                                              42.7k571217







                                              • 1




                                                $begingroup$
                                                I'm not sure why .X is not documented, but I think this should work: 3999L.XQO
                                                $endgroup$
                                                – Adnan
                                                yesterday










                                              • $begingroup$
                                                @Adnan Haha, -52 bytes right there. Completely forgot you indeed told us about adding a Roman Number builtin. Will ask @Mr.Xcoder in chat to add it to the docs. Are any other commands missing? ;) PS: Saved another byte by compressing 3999. :)
                                                $endgroup$
                                                – Kevin Cruijssen
                                                yesterday












                                              • 1




                                                $begingroup$
                                                I'm not sure why .X is not documented, but I think this should work: 3999L.XQO
                                                $endgroup$
                                                – Adnan
                                                yesterday










                                              • $begingroup$
                                                @Adnan Haha, -52 bytes right there. Completely forgot you indeed told us about adding a Roman Number builtin. Will ask @Mr.Xcoder in chat to add it to the docs. Are any other commands missing? ;) PS: Saved another byte by compressing 3999. :)
                                                $endgroup$
                                                – Kevin Cruijssen
                                                yesterday







                                              1




                                              1




                                              $begingroup$
                                              I'm not sure why .X is not documented, but I think this should work: 3999L.XQO
                                              $endgroup$
                                              – Adnan
                                              yesterday




                                              $begingroup$
                                              I'm not sure why .X is not documented, but I think this should work: 3999L.XQO
                                              $endgroup$
                                              – Adnan
                                              yesterday












                                              $begingroup$
                                              @Adnan Haha, -52 bytes right there. Completely forgot you indeed told us about adding a Roman Number builtin. Will ask @Mr.Xcoder in chat to add it to the docs. Are any other commands missing? ;) PS: Saved another byte by compressing 3999. :)
                                              $endgroup$
                                              – Kevin Cruijssen
                                              yesterday




                                              $begingroup$
                                              @Adnan Haha, -52 bytes right there. Completely forgot you indeed told us about adding a Roman Number builtin. Will ask @Mr.Xcoder in chat to add it to the docs. Are any other commands missing? ;) PS: Saved another byte by compressing 3999. :)
                                              $endgroup$
                                              – Kevin Cruijssen
                                              yesterday











                                              0












                                              $begingroup$

                                              perl -MRegexp::Common -pe, 34 bytes



                                              $_=/^$REnumroman$/&!/(.)13/


                                              The &!/(.)13/ part is necessary, because Regexp::Common allows four (but not five) of the same characters in a row. That way, it matches roman numbers used on clock faces, where IIII is often used for 4.






                                              share|improve this answer









                                              $endgroup$

















                                                0












                                                $begingroup$

                                                perl -MRegexp::Common -pe, 34 bytes



                                                $_=/^$REnumroman$/&!/(.)13/


                                                The &!/(.)13/ part is necessary, because Regexp::Common allows four (but not five) of the same characters in a row. That way, it matches roman numbers used on clock faces, where IIII is often used for 4.






                                                share|improve this answer









                                                $endgroup$















                                                  0












                                                  0








                                                  0





                                                  $begingroup$

                                                  perl -MRegexp::Common -pe, 34 bytes



                                                  $_=/^$REnumroman$/&!/(.)13/


                                                  The &!/(.)13/ part is necessary, because Regexp::Common allows four (but not five) of the same characters in a row. That way, it matches roman numbers used on clock faces, where IIII is often used for 4.






                                                  share|improve this answer









                                                  $endgroup$



                                                  perl -MRegexp::Common -pe, 34 bytes



                                                  $_=/^$REnumroman$/&!/(.)13/


                                                  The &!/(.)13/ part is necessary, because Regexp::Common allows four (but not five) of the same characters in a row. That way, it matches roman numbers used on clock faces, where IIII is often used for 4.







                                                  share|improve this answer












                                                  share|improve this answer



                                                  share|improve this answer










                                                  answered 2 days ago









                                                  AbigailAbigail

                                                  46617




                                                  46617





















                                                      0












                                                      $begingroup$


                                                      Python 3, 116 113 109 107 105 106 bytes





                                                      import re
                                                      lambda n:re.match(r'(M,3(C(M|CC?|D)?|DC,3))(X(C|XX?|L)?|(LX,3))?(I(X|II?|V)?|VI,3)?$',n)


                                                      Try it online!



                                                      -1 byte thanks to ShadowRanger






                                                      share|improve this answer











                                                      $endgroup$








                                                      • 2




                                                        $begingroup$
                                                        As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                                                        $endgroup$
                                                        – ShadowRanger
                                                        Apr 12 at 1:35










                                                      • $begingroup$
                                                        @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                                                        $endgroup$
                                                        – Noodle9
                                                        2 days ago










                                                      • $begingroup$
                                                        Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                                                        $endgroup$
                                                        – ShadowRanger
                                                        2 days ago










                                                      • $begingroup$
                                                        @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                                                        $endgroup$
                                                        – Noodle9
                                                        2 days ago
















                                                      0












                                                      $begingroup$


                                                      Python 3, 116 113 109 107 105 106 bytes





                                                      import re
                                                      lambda n:re.match(r'(M,3(C(M|CC?|D)?|DC,3))(X(C|XX?|L)?|(LX,3))?(I(X|II?|V)?|VI,3)?$',n)


                                                      Try it online!



                                                      -1 byte thanks to ShadowRanger






                                                      share|improve this answer











                                                      $endgroup$








                                                      • 2




                                                        $begingroup$
                                                        As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                                                        $endgroup$
                                                        – ShadowRanger
                                                        Apr 12 at 1:35










                                                      • $begingroup$
                                                        @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                                                        $endgroup$
                                                        – Noodle9
                                                        2 days ago










                                                      • $begingroup$
                                                        Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                                                        $endgroup$
                                                        – ShadowRanger
                                                        2 days ago










                                                      • $begingroup$
                                                        @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                                                        $endgroup$
                                                        – Noodle9
                                                        2 days ago














                                                      0












                                                      0








                                                      0





                                                      $begingroup$


                                                      Python 3, 116 113 109 107 105 106 bytes





                                                      import re
                                                      lambda n:re.match(r'(M,3(C(M|CC?|D)?|DC,3))(X(C|XX?|L)?|(LX,3))?(I(X|II?|V)?|VI,3)?$',n)


                                                      Try it online!



                                                      -1 byte thanks to ShadowRanger






                                                      share|improve this answer











                                                      $endgroup$




                                                      Python 3, 116 113 109 107 105 106 bytes





                                                      import re
                                                      lambda n:re.match(r'(M,3(C(M|CC?|D)?|DC,3))(X(C|XX?|L)?|(LX,3))?(I(X|II?|V)?|VI,3)?$',n)


                                                      Try it online!



                                                      -1 byte thanks to ShadowRanger







                                                      share|improve this answer














                                                      share|improve this answer



                                                      share|improve this answer








                                                      edited 2 days ago

























                                                      answered Apr 11 at 16:30









                                                      Noodle9Noodle9

                                                      30137




                                                      30137







                                                      • 2




                                                        $begingroup$
                                                        As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                                                        $endgroup$
                                                        – ShadowRanger
                                                        Apr 12 at 1:35










                                                      • $begingroup$
                                                        @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                                                        $endgroup$
                                                        – Noodle9
                                                        2 days ago










                                                      • $begingroup$
                                                        Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                                                        $endgroup$
                                                        – ShadowRanger
                                                        2 days ago










                                                      • $begingroup$
                                                        @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                                                        $endgroup$
                                                        – Noodle9
                                                        2 days ago













                                                      • 2




                                                        $begingroup$
                                                        As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                                                        $endgroup$
                                                        – ShadowRanger
                                                        Apr 12 at 1:35










                                                      • $begingroup$
                                                        @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                                                        $endgroup$
                                                        – Noodle9
                                                        2 days ago










                                                      • $begingroup$
                                                        Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                                                        $endgroup$
                                                        – ShadowRanger
                                                        2 days ago










                                                      • $begingroup$
                                                        @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                                                        $endgroup$
                                                        – Noodle9
                                                        2 days ago








                                                      2




                                                      2




                                                      $begingroup$
                                                      As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                                                      $endgroup$
                                                      – ShadowRanger
                                                      Apr 12 at 1:35




                                                      $begingroup$
                                                      As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                                                      $endgroup$
                                                      – ShadowRanger
                                                      Apr 12 at 1:35












                                                      $begingroup$
                                                      @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                                                      $endgroup$
                                                      – Noodle9
                                                      2 days ago




                                                      $begingroup$
                                                      @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                                                      $endgroup$
                                                      – Noodle9
                                                      2 days ago












                                                      $begingroup$
                                                      Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                                                      $endgroup$
                                                      – ShadowRanger
                                                      2 days ago




                                                      $begingroup$
                                                      Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                                                      $endgroup$
                                                      – ShadowRanger
                                                      2 days ago












                                                      $begingroup$
                                                      @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                                                      $endgroup$
                                                      – Noodle9
                                                      2 days ago





                                                      $begingroup$
                                                      @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                                                      $endgroup$
                                                      – Noodle9
                                                      2 days ago












                                                      0












                                                      $begingroup$


                                                      Ruby, (-n) 56 bytes





                                                      p~/^M,3(D?C,3|CM|CD)(L?X,3|XC|XL)(V?I,3|IV|IX)$/


                                                      Try it online!



                                                      Outputs 0 (truthy) or nil (falsy).






                                                      share|improve this answer









                                                      $endgroup$

















                                                        0












                                                        $begingroup$


                                                        Ruby, (-n) 56 bytes





                                                        p~/^M,3(D?C,3|CM|CD)(L?X,3|XC|XL)(V?I,3|IV|IX)$/


                                                        Try it online!



                                                        Outputs 0 (truthy) or nil (falsy).






                                                        share|improve this answer









                                                        $endgroup$















                                                          0












                                                          0








                                                          0





                                                          $begingroup$


                                                          Ruby, (-n) 56 bytes





                                                          p~/^M,3(D?C,3|CM|CD)(L?X,3|XC|XL)(V?I,3|IV|IX)$/


                                                          Try it online!



                                                          Outputs 0 (truthy) or nil (falsy).






                                                          share|improve this answer









                                                          $endgroup$




                                                          Ruby, (-n) 56 bytes





                                                          p~/^M,3(D?C,3|CM|CD)(L?X,3|XC|XL)(V?I,3|IV|IX)$/


                                                          Try it online!



                                                          Outputs 0 (truthy) or nil (falsy).







                                                          share|improve this answer












                                                          share|improve this answer



                                                          share|improve this answer










                                                          answered 2 days ago









                                                          iamnotmaynardiamnotmaynard

                                                          95349




                                                          95349



























                                                              draft saved

                                                              draft discarded
















































                                                              If this is an answer to a challenge…



                                                              • …Be sure to follow the challenge specification. However, please refrain from exploiting obvious loopholes. Answers abusing any of the standard loopholes are considered invalid. If you think a specification is unclear or underspecified, comment on the question instead.


                                                              • …Try to optimize your score. For instance, answers to code-golf challenges should attempt to be as short as possible. You can always include a readable version of the code in addition to the competitive one.
                                                                Explanations of your answer make it more interesting to read and are very much encouraged.


                                                              • …Include a short header which indicates the language(s) of your code and its score, as defined by the challenge.


                                                              More generally…



                                                              • …Please make sure to answer the question and provide sufficient detail.


                                                              • …Avoid asking for help, clarification or responding to other answers (use comments instead).




                                                              draft saved


                                                              draft discarded














                                                              StackExchange.ready(
                                                              function ()
                                                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodegolf.stackexchange.com%2fquestions%2f183014%2fmatch-roman-numerals%23new-answer', 'question_page');

                                                              );

                                                              Post as a guest















                                                              Required, but never shown





















































                                                              Required, but never shown














                                                              Required, but never shown












                                                              Required, but never shown







                                                              Required, but never shown

































                                                              Required, but never shown














                                                              Required, but never shown












                                                              Required, but never shown







                                                              Required, but never shown







                                                              Popular posts from this blog

                                                              Tamil (spriik) Luke uk diar | Nawigatjuun

                                                              Align equal signs while including text over equalitiesAMS align: left aligned text/math plus multicolumn alignmentMultiple alignmentsAligning equations in multiple placesNumbering and aligning an equation with multiple columnsHow to align one equation with another multline equationUsing \ in environments inside the begintabularxNumber equations and preserving alignment of equal signsHow can I align equations to the left and to the right?Double equation alignment problem within align enviromentAligned within align: Why are they right-aligned?

                                                              Where does the image of a data connector as a sharp metal spike originate from?Where does the concept of infected people turning into zombies only after death originate from?Where does the motif of a reanimated human head originate?Where did the notion that Dragons could speak originate?Where does the archetypal image of the 'Grey' alien come from?Where did the suffix '-Man' originate?Where does the notion of being injured or killed by an illusion originate?Where did the term “sophont” originate?Where does the trope of magic spells being driven by advanced technology originate from?Where did the term “the living impaired” originate?