RosettaCodeData/Task/Roman-numerals-Decode/PL-SQL/roman-numerals-decode.sql

84 lines
2.8 KiB
SQL

/*****************************************************************
* $Author: Atanas Kebedjiev $
*****************************************************************
* PL/SQL code can be run as anonymous block.
* To test, execute the whole script or create the functions and then e.g. 'select rdecode('2012') from dual;
* Please note that task definition does not describe fully some current rules, such as
* * subtraction - IX XC CM are the valid subtraction combinations
* * A subtraction character cannot be repeated: 8 is expressed as VIII and not as IIX
* * V L and D cannot be used for subtraction
* * Any numeral cannot be repeated more than 3 times: 1910 should be MCMX and not MDCCCCX
* Code below does not validate the Roman numeral itself and will return a result even for a non-compliant number
* E.g. both MCMXCIX and IMM will return 1999 but the first one is the correct notation
*/
DECLARE
FUNCTION rvalue(c IN CHAR) RETURN NUMBER IS
i INTEGER;
BEGIN
i := 0;
CASE (c)
when 'M' THEN i := 1000;
when 'D' THEN i := 500;
when 'C' THEN i := 100;
when 'L' THEN i := 50;
when 'X' THEN i := 10;
when 'V' THEN i := 5;
when 'I' THEN i := 1;
END CASE;
RETURN i;
END;
FUNCTION decode(rn IN VARCHAR2) RETURN NUMBER IS
i INTEGER;
l INTEGER;
cr CHAR; -- current Roman numeral as substring from r
cv INTEGER; -- value of current Roman numeral
gr CHAR; -- next Roman numeral
gv NUMBER; -- value of the next numeral;
dv NUMBER; -- decimal value to return
BEGIN
l := length(rn);
i := 1;
dv := 0;
while (i <= l)
LOOP
cr := substr(rn,i,1);
cv := rvalue(cr);
/* Look for a larger numeral in next position, like IV or CM
The number to subtract should be at least 1/10th of the bigger number
CM and XC are valid, but IC and XM are not */
IF (i < l) THEN
gr := substr(rn,i+1,1);
gv := rvalue(gr);
IF (cv < gv ) THEN
dv := dv - cv;
ELSE
dv := dv + cv;
END IF;
ELSE
dv := dv + cv;
END IF; -- need to add the last value unconditionally
i := i + 1;
END LOOP;
RETURN dv;
END;
BEGIN
DBMS_OUTPUT.PUT_LINE ('MMXII = ' || rdecode('MMXII')); -- 2012
DBMS_OUTPUT.PUT_LINE ('MCMLI = ' || rdecode('MCMLI')); -- 1951
DBMS_OUTPUT.PUT_LINE ('MCMLXXXVII = ' || rdecode('MCMLXXXVII')); -- 1987
DBMS_OUTPUT.PUT_LINE ('MDCLXVI = ' || rdecode('MDCLXVI')); -- 1666
DBMS_OUTPUT.PUT_LINE ('MCMXCIX = ' || rdecode('MCMXCIX')); -- 1999
END;