⍝   R o m a n   N u m e r a l s     See →notes.roman←

    ← .()                           ⍝ digit binding:            7 → V.I.I
      ⍴() ⍺()                       ⍝ Roman ←→ Arabic: C.X.L.I.X ←→ 1.4.9
    ← ×() ÷() %()                   ⍝ mul div rem
    ← +() -()                       ⍝ add sub

⍝ Pattern sets:

      s p q r ::                                        ⍝ match anything.
          n m :: I V X L C D M                          ⍝ match single digit.
            k :: absurdus nimius                        ⍝ too small, too big.
            i :: nulla                                  ⍝ nothing.
            j :: I V X L C D M absurdus nulla nimius    ⍝ any atomic value.

⍝ Auxiliary functions for expanded digit strings:

       \()              ⍝ expand (remove subtractive prefixes).
       /()              ⍝ compress (apply subtractive prefixes).
    ←  ∘()              ⍝ expanded digit separator (disables normalisation).

⍝ Add
       p + q = /(\p ++ \q)          ⍝ Add expanded digit strings.

       ← ++()                       ⍝ merge strings, preserving order.

       p ++ k   = k                 ⍝ special values.
       k ++ p   = k
       i ++ q   = q
       p ++ i   = p

       p ++ I   = p∘I               ⍝ First, collect Is, then
       I ++ q   = q∘I
     p   ++ q∘I = (p ++ q)∘I
     p∘I ++ q   = (p ++ q)∘I

       p ++ V   = p∘V               ⍝ collect Vs, then
       V ++ q   = q∘V
     p   ++ q∘V = (p ++ q)∘V
     p∘V ++ q   = (p ++ q)∘V

       p ++ X   = p∘X               ⍝ collect Xs, then
       X ++ q   = q∘X
     p   ++ q∘X = (p ++ q)∘X
     p∘X ++ q   = (p ++ q)∘X

       p ++ L   = p∘L               ⍝ collect Ls, then
       L ++ q   = q∘L
     p   ++ q∘L = (p ++ q)∘L
     p∘L ++ q   = (p ++ q)∘L

       p ++ C   = p∘C               ⍝ collect Cs, then
       C ++ q   = q∘C
     p   ++ q∘C = (p ++ q)∘C
     p∘C ++ q   = (p ++ q)∘C

       p ++ D   = p∘D               ⍝ collect Ds, then
       D ++ q   = q∘D
     p   ++ q∘D = (p ++ q)∘D
     p∘D ++ q   = (p ++ q)∘D

       p ++ M   = p∘M               ⍝ collect Ms.
       M ++ q   = q∘M
     p   ++ q∘M = (p ++ q)∘M
     p∘M ++ q   = (p ++ q)∘M

⍝ Sub
          p - q = /(\p -- \q)           ⍝ Subtract expanded digit strings.

         ← --()                         ⍝ remove common digits.

         V -- I = I∘I∘I∘I                       ⍝ sub I
         X -- I = V∘I∘I∘I∘I
         L -- I = X∘X∘X∘X∘V∘I∘I∘I∘I
         C -- I = L∘X∘X∘X∘X∘V∘I∘I∘I∘I
         D -- I = C∘C∘C∘C∘L∘X∘X∘X∘X∘V∘I∘I∘I∘I
         M -- I = D∘C∘C∘C∘C∘L∘X∘X∘X∘X∘V∘I∘I∘I∘I

         X -- V = V                             ⍝ sub V
         L -- V = X∘X∘X∘X∘V
         C -- V = L∘X∘X∘X∘X∘V
         D -- V = C∘C∘C∘C∘L∘X∘X∘X∘X∘V
         M -- V = D∘C∘C∘C∘C∘L∘X∘X∘X∘X∘V

         L -- X = X∘X∘X∘X                       ⍝ sub X
         C -- X = L∘X∘X∘X∘X
         D -- X = C∘C∘C∘C∘L∘X∘X∘X∘X
         M -- X = D∘C∘C∘C∘C∘L∘X∘X∘X∘X

         C -- L = L                             ⍝ sub L
         D -- L = C∘C∘C∘C∘L
         M -- L = D∘C∘C∘C∘C∘L

         D -- C = C∘C∘C∘C                       ⍝ sub C
         M -- C = D∘C∘C∘C∘C

         M -- D = D∘C∘C∘C∘C                     ⍝ sub D

       p∘m -- m = p                             ⍝ subtract multi-digit nos.
       p -- q∘m = p -- q -- m
       p∘m -- r = (p -- r) ++ m
       (p--p)∘m = m

         m -- m = nulla                         ⍝ nothing left.
         m -- n = absurdus                      ⍝ less than nothing left.

        p -- i = p                              ⍝ special values.
        i -- q = absurdus
        p -- k = absurdus
        k -- q = k

⍝ Mul
        p × q = /(\p ×× \q)         ⍝ Multiply expanded digit strings.

        ← ××()                      ⍝ auxiliary function.

        p ×× I = p                  ⍝ I × ···
        I ×× q = q                  ⍝ ··· × I

        V ×× V = X∘X∘V              ⍝ V × ···
        V ×× X = L
        V ×× L = C∘C∘L
        V ×× C = D
        V ×× D = M∘M∘D

        X ×× V = L                  ⍝ X × ···
        X ×× X = C
        X ×× L = D
        X ×× C = M

        L ×× V = C∘C∘L              ⍝ L × ···
        L ×× X = D
        L ×× L = M∘M∘D

        C ×× V = D                  ⍝ C × ···
        C ×× X = M

        D ×× V = M∘M∘D              ⍝ D × ···

        i ×× q = i                  ⍝ special values
        p ×× i = i
        k ×× q = k
        p ×× k = k

    p∘m ×× q∘n = (p××q)++(p××n)++(m××q)++(m××n)
    p   ×× q∘n = (p××q)++(p××n)
    p∘m ×× q   = (p××q)++        (m××q)

        p ×× q = nimius             ⍝ not already covered: overflow.

⍝ Intermediate simplification of expanded strings, speeds up multiply:

      I∘I∘I∘I∘I =   V
    p∘I∘I∘I∘I∘I = p∘V

            V∘V =   X
          p∘V∘V = p∘X

      X∘X∘X∘X∘X =   L
    p∘X∘X∘X∘X∘X = p∘L

            L∘L =   C
          p∘L∘L = p∘C

      C∘C∘C∘C∘C =   D
    p∘C∘C∘C∘C∘C = p∘D

            D∘D =   M
          p∘D∘D = p∘M

⍝ Div                                               ⍝ Integer divide.

    p ÷ q = /(div(qr(\p; \q)))                      ⍝ div expanded strings.

    div() ⋄ div(q; r) = q                           ⍝ extract quotient

⍝ Rem                                               ⍝ Integer remainder.

    p % q = /(rem(qr(\p; \q)))                      ⍝ rem expanded strings.

    rem() ⋄ rem(q; r) = r                           ⍝ extract remainder

⍝ Auxiliary functions for division:

    ← ;()                                           ⍝ tuple item separation.

    qr() ⋄ qr(p; q) = dv(p; (sh(p; q; ⊥)); nulla)   ⍝ quotient and remainder.

⍝ Long division:

    dv() dv'() dv"()                                ⍝ division.

    dv  p;  ⊥;     r = nulla; p                     ⍝ dividend too small: done.
    dv  p; (q; s); r = dv' r; s; (sb p; q; nulla)
    dv' r; s; (p; q) = dv" q; s; (p++(10 r))
    dv" r; ⊥;     q  = q; r                         ⍝ finished.
    dv" p; q; r      = dv p; q; r

    sb() sb'()                                      ⍝ repeated subtraction.

    sb  p; q; r      = sb' p; q; r; (p?q)           ⍝ dividend ? subtrahend:
    sb' p; q; r; lt  = r; p                         ⍝ p<q:
    sb' p; q; r; s   = sb p--q; q; r∘I              ⍝ p≥q:

    sh() sh'()                                      ⍝ divisor "shift".

    sh  p; q; r      = sh' p; q; r; (p?q)
    sh' p; q; r; lt  = r
    sh' p; q; r; s   = sh p; (10 q); (q; r)

⍝ Tenfold (note that "10" is a function).

    10()                        ⍝ tenfold.

    10 I = X
    10 V = L
    10 X = C
    10 L = D
    10 C = M
    10 D = nimius               ⍝ too big
    10 M = nimius               ⍝ too big
    10 j = j                    ⍝ ten nothings are still nothing, etc.

    10(p∘n) = (10 p)∘(10 n)

⍝  Compare

      ← ?() ??()                ⍝ compare expanded strings.

        p ?  q = (⌽p)??(⌽q)     ⍝ ⌽: compare more significant values first.

        m ?? m = eq

        I ?? n = lt
        m ?? I = gt

        V ?? n = lt
        m ?? V = gt

        X ?? n = lt
        m ?? X = gt

        L ?? n = lt
        m ?? L = gt

        C ?? n = lt
        m ?? C = gt

        D ?? n = lt
        m ?? D = gt

        M ?? n = lt
        m ?? M = gt

   p ?? nimius = lt             ⍝ compare with "too big"
   nimius ?? q = gt

 p ?? absurdus = gt             ⍝ compare with "too small"
 absurdus ?? q = lt

    p ?? nulla = gt             ⍝ compare with "nothing"
    nulla ?? q = lt

    m∘p ?? n   = (m??n)∘gt
    m   ?? n∘q = (m??n)∘lt
    m∘p ?? n∘q = (m??n)∘(p??q)

          eq∘q =  q
          lt∘q = lt
          gt∘q = gt

⍝ Reverse expanded string binding:  ((a∘b)∘c)∘d → a∘(b∘(c∘d))

    ⌽()
    ⌽j     = j
    ⌽(p∘q) = ⌽⌽(p;q)

    ⌽⌽()
    ⌽⌽(j;r)   = j∘r
    ⌽⌽(p∘q;r) = ⌽⌽(p;q∘r)

⍝ Expand (remove subtractive prefixes). See →notes.roman←

            \j = j

          \I.V = I∘I∘I∘I
          \I.X = V∘I∘I∘I∘I
          \X.L = X∘X∘X∘X
          \X.C = L∘X∘X∘X∘X
          \C.D = C∘C∘C∘C
          \C.M = D∘C∘C∘C∘C

        \p.I.V = \p∘I∘I∘I∘I
        \p.I.X = \p∘V∘I∘I∘I∘I
        \p.X.L = \p∘X∘X∘X∘X
        \p.X.C = \p∘L∘X∘X∘X∘X
        \p.C.D = \p∘C∘C∘C∘C
        \p.C.M = \p∘D∘C∘C∘C∘C

          \p.n = \p∘n

⍝ Compress: replace '∘' with '.' to enable renormalisation.

            /j = j
        /(p∘n) = (/p).n

⍝ Normalise (per norma)

          I.I.I.I =   I.V
        p.I.I.I.I = p.I.V

          I.V.I   =   V
        p.I.V.I   = p.V

        V.I.I.I.I =   I.X
      p.V.I.I.I.I = p.I.X

            I.X.I =   X
          p.I.X.I = p.X

            V.I.V =   I.X
          p.V.I.V = p.I.X

          X.X.X.X =   X.L
        p.X.X.X.X = p.X.L

           X.L.X  =   L
         p.X.L.X  = p.L

      X.X.X.X.I.X =   X.L.I.X
    p.X.X.X.X.I.X = p.X.L.I.X

            L.X.L =   X.C
          p.L.X.L = p.X.C

            X.C.X =   C
          p.X.C.X = p.C

          C.C.C.C =   C.D
        p.C.C.C.C = p.C.D

            C.D.C =   D
          p.C.D.C = p.D

        D.C.C.C.C =   C.M
      p.D.C.C.C.C = p.C.M

            D.C.D =   C.M
          p.D.C.D = p.C.M

            C.M.C =   M
          p.C.M.C = p.M


              V.V =   X
            p.V.V = p.X

              L.L =   C
            p.L.L = p.C

              D.D =   M
            p.D.D = p.M

          M.M.M.M = nimius          ⍝ overflow (too big)

              p.i = p               ⍝ special values.
              i.q = q
              p.k = k
              k.q = k

⍝ Roman from Arabic     1 ·· 3,999

          ⍴⍴()          ⍝ Auxiliary function

    ⍴ p = ⍴⍴(p∘I)       ⍝ start with I units

    ⍴⍴(0∘I) = nulla                     ⍝⍝⍝⍝
    ⍴⍴(1∘I) = I                         ⍝
    ⍴⍴(2∘I) = I.I                       ⍝
    ⍴⍴(3∘I) = I.I.I                     ⍝
    ⍴⍴(4∘I) = I.V                       ⍝
    ⍴⍴(5∘I) = V                         ⍝
    ⍴⍴(6∘I) = V.I                       ⍝
    ⍴⍴(7∘I) = V.I.I                     ⍝
    ⍴⍴(8∘I) = V.I.I.I                   ⍝
    ⍴⍴(9∘I) = I.X                       ⍝
                                        ⍝  1 ·· 9
    ⍴⍴(p.0∘I) =  ⍴⍴(p∘X)                ⍝
    ⍴⍴(p.1∘I) = (⍴⍴(p∘X)).I             ⍝
    ⍴⍴(p.2∘I) = (⍴⍴(p∘X)).I.I           ⍝
    ⍴⍴(p.3∘I) = (⍴⍴(p∘X)).I.I.I         ⍝
    ⍴⍴(p.4∘I) = (⍴⍴(p∘X)).I.V           ⍝
    ⍴⍴(p.5∘I) = (⍴⍴(p∘X)).V             ⍝
    ⍴⍴(p.6∘I) = (⍴⍴(p∘X)).V.I           ⍝
    ⍴⍴(p.7∘I) = (⍴⍴(p∘X)).V.I.I         ⍝
    ⍴⍴(p.8∘I) = (⍴⍴(p∘X)).V.I.I.I       ⍝
    ⍴⍴(p.9∘I) = (⍴⍴(p∘X)).I.X        ⍝⍝⍝⍝

    ⍴⍴(1∘X) = X                      ⍝⍝⍝⍝
    ⍴⍴(2∘X) = X.X                       ⍝
    ⍴⍴(3∘X) = X.X.X                     ⍝
    ⍴⍴(4∘X) = X.L                       ⍝
    ⍴⍴(5∘X) = L                         ⍝
    ⍴⍴(6∘X) = L.X                       ⍝
    ⍴⍴(7∘X) = L.X.X                     ⍝
    ⍴⍴(8∘X) = L.X.X.X                   ⍝
    ⍴⍴(9∘X) = X.C                       ⍝
                                        ⍝  1.0 ·· 9.0
    ⍴⍴(p.0∘X) =  ⍴⍴(p∘C)                ⍝
    ⍴⍴(p.1∘X) = (⍴⍴(p∘C)).X             ⍝
    ⍴⍴(p.2∘X) = (⍴⍴(p∘C)).X.X           ⍝
    ⍴⍴(p.3∘X) = (⍴⍴(p∘C)).X.X.X         ⍝
    ⍴⍴(p.4∘X) = (⍴⍴(p∘C)).X.L           ⍝
    ⍴⍴(p.5∘X) = (⍴⍴(p∘C)).L             ⍝
    ⍴⍴(p.6∘X) = (⍴⍴(p∘C)).L.X           ⍝
    ⍴⍴(p.7∘X) = (⍴⍴(p∘C)).L.X.X         ⍝
    ⍴⍴(p.8∘X) = (⍴⍴(p∘C)).L.X.X.X       ⍝
    ⍴⍴(p.9∘X) = (⍴⍴(p∘C)).X.C        ⍝⍝⍝⍝

    ⍴⍴(1∘C) = C                      ⍝⍝⍝⍝
    ⍴⍴(2∘C) = C.C                       ⍝
    ⍴⍴(3∘C) = C.C.C                     ⍝
    ⍴⍴(4∘C) = C.D                       ⍝
    ⍴⍴(5∘C) = D                         ⍝
    ⍴⍴(6∘C) = D.C                       ⍝
    ⍴⍴(7∘C) = D.C.C                     ⍝
    ⍴⍴(8∘C) = D.C.C.C                   ⍝
    ⍴⍴(9∘C) = C.M                       ⍝
                                        ⍝  1.0.0 ·· 9.0.0
    ⍴⍴(p.0∘C) =  ⍴⍴(p∘M)                ⍝
    ⍴⍴(p.1∘C) = (⍴⍴(p∘M)).C             ⍝
    ⍴⍴(p.2∘C) = (⍴⍴(p∘M)).C.C           ⍝
    ⍴⍴(p.3∘C) = (⍴⍴(p∘M)).C.C.C         ⍝
    ⍴⍴(p.4∘C) = (⍴⍴(p∘M)).C.D           ⍝
    ⍴⍴(p.5∘C) = (⍴⍴(p∘M)).D             ⍝
    ⍴⍴(p.6∘C) = (⍴⍴(p∘M)).D.C           ⍝
    ⍴⍴(p.7∘C) = (⍴⍴(p∘M)).D.C.C         ⍝
    ⍴⍴(p.8∘C) = (⍴⍴(p∘M)).D.C.C.C       ⍝
    ⍴⍴(p.9∘C) = (⍴⍴(p∘M)).C.M        ⍝⍝⍝⍝

    ⍴⍴(1∘M) = M                      ⍝⍝⍝⍝
    ⍴⍴(2∘M) = M.M                       ⍝  1.0.0.0 ·· 3.0.0.0
    ⍴⍴(3∘M) = M.M.M                  ⍝⍝⍝⍝

      ⍴⍴(p) = nimius                 ⍝⍝⍝⍝  otherwise: too big

⍝ Arabic from Roman

          ⍺⍺()                      ⍝ Auxiliary function.
      ← <+>()                       ⍝ add multi-digit Arabic numbers.

    a b :: 0 1 2 3 4 5 6 7 8 9      ⍝ Arabic digits

    ⍺ r = ⍺⍺(\r)

    ⍺⍺ i =       0
    ⍺⍺ I =       1
    ⍺⍺ V =       5
    ⍺⍺ X =     1.0
    ⍺⍺ L =     5.0
    ⍺⍺ C =   1.0.0
    ⍺⍺ D =   5.0.0
    ⍺⍺ M = 1.0.0.0

    ⍺⍺ nulla    = 0
    ⍺⍺ nimius   = too_big
    ⍺⍺ absurdus = negative

    ⍺⍺(p∘m) = ⍺⍺p <+> ⍺⍺m


    a <+> 0 = a
    0 <+> b = b

    1 <+> 1 = 2
    2 <+> 1 = 3
    3 <+> 1 = 4
    4 <+> 1 = 5
    5 <+> 1 = 6
    6 <+> 1 = 7
    7 <+> 1 = 8
    8 <+> 1 = 9

    p.a <+>   b =       p.(a<+>b)
    p.a <+> q.b = (p<+>q).(a<+>b)

⍝ Equivalence functions

    ← =() ≡()           ⍝ reduction equivalence/trace

⍝ Test Cases
⍝
⍝                 I.I.I.I -> I.V                ⍝ normalisation
⍝               I.I.I.I.I -> V
⍝             I.I.I.I.I.I -> V.I
⍝
⍝             I+I+I+I+I+I -> V.I                ⍝ addition
⍝                 I.V + I -> V
⍝           V.I.I + V.I.I -> X.I.V
⍝               I.X + I.X -> X.V.I.I.I
⍝       X.L.I.X + X.L.I.X -> X.C.V.I.I.I
⍝                 M+M+M+M -> nimius
⍝
⍝                 X.X - I -> X.I.X              ⍝ subtraction.
⍝                 V.I - V -> I
⍝     X-I.I.I.I.I.I.I.I.I -> I
⍝               X.X - I.I -> X.V.I.I.I
⍝             X.I.V - V.I -> V.I.I.I
⍝                 C.I - V -> X.C.V.I
⍝                 M.M - I -> M.C.M.X.C.I.X
⍝                   X-V-V -> nulla
⍝                 X-V-V-V -> absurdus
⍝
⍝           X.I.V × X.I.V -> C.X.C.V.I          ⍝ multiplication
⍝                   X×X×X -> M
⍝                 X×X×X×X -> nimius
⍝             I.I × nulla -> nulla
⍝
⍝                   V ÷ I -> V                  ⍝ division
⍝                   C ÷ X -> X
⍝               X.I ÷ I.I -> V
⍝           C.X.L.V ÷ V.I -> X.X.I.V
⍝                   V ÷ X -> nulla
⍝               X.I.I ÷ V -> I.I
⍝
⍝           C.X.L.V % V.I -> I                  ⍝ remainder
⍝                   X % V -> nulla
⍝                   V % X -> V
⍝               X.I.I % V -> I.I
⍝
⍝               ⍴ 1.9.9.9 -> M.C.M.X.C.I.X      ⍝ Roman from Arabic
⍝               ⍴ 3.9.9.9 -> M.M.M.C.M.X.C.I.X
⍝               ⍴ 4.0.0.0 -> nimius             ⍝ (number too big)
⍝
⍝         ⍺ M.C.M.X.C.I.X -> 1.9.9.9            ⍝ Arabic from Roman
⍝     ⍺ M.M.M.C.M.X.C.I.X -> 3.9.9.9
⍝
⍝                  ⍺⍴ 4.9 -> 4.9                ⍝ round trip.
⍝          ⍴⍺ C.M.X.C.I.X -> C.M.X.C.I.X        ⍝   ..   ..
⍝          ⍴⍺ C.D.X.L.I.V -> C.D.X.L.I.V        ⍝   ..   ..
⍝
⍝         M + I.X × C.X.I -> M.C.M.X.C.I.X      ⍝ misc
⍝   
⍝   Back to: Contents