Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@MikeMKH
Created May 23, 2014 18:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MikeMKH/5ee3f4eaf18474d244d5 to your computer and use it in GitHub Desktop.
Save MikeMKH/5ee3f4eaf18474d244d5 to your computer and use it in GitHub Desktop.
Roman Numerals kata in C# using FsCheck with XUnit to test.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RomanNumerals
{
public class RomanNumeraler
{
public string Translate(int value)
{
var result =
new List<Translation>
{
new Translation(1000, "M"),
new Translation( 900, "CM"),
new Translation( 500, "D"),
new Translation( 400, "CD"),
new Translation( 100, "C"),
new Translation( 90, "XC"),
new Translation( 50, "L"),
new Translation( 40, "XL"),
new Translation( 10, "X"),
new Translation( 9, "IX"),
new Translation( 5, "V"),
new Translation( 4, "IV")
}.Aggregate(
new Tuple<string, int>(string.Empty, value),
(k, t) =>
{
while (t.Test(k.Item2))
{
k = new Tuple<string, int>(
k.Item1 + t.To,
k.Item2 - t.Value);
}
return k;
});
return result.Item1 + new string('I', result.Item2%4);
}
protected class Translation
{
public string To { get; private set; }
public int Value { get; private set; }
public Translation(int value, string to)
{
To = to;
Value = value;
}
public bool Test(int amount)
{
return amount >= Value;
}
}
}
}
namespace RomanNumeralsTests
open Xunit
open FsCheck
open FsCheck.Xunit
open RomanNumerals
module ``Roman Numeral Unit tests`` =
[<Fact>]
let ``For this input it will produce this output`` () =
let testpairs = [
(0, "")
(1, "I")
(2, "II")
(3, "III")
(4, "IV")
(5, "V")
(6, "VI")
(7, "VII")
(8, "VIII")
(9, "IX")
(10, "X")
(20, "XX")
(30, "XXX")
(40, "XL")
(50, "L")
(90, "XC")
(100, "C")
(400, "CD")
(500, "D")
(900, "CM")
(1000, "M")
]
for (number, expected) in testpairs do
let romanizer = RomanNumeraler ()
let actual = romanizer.Translate number
Assert.Equal<string>(expected, actual)
module ``Roman Numeral Property tests`` =
[<Fact>]
let ``NCrunch work``() = ()
let normalize x = (abs x % 3999) + 1
let ``has max number of repeating letter is 3`` letter number =
let romanizer = RomanNumeraler ()
not ((romanizer.Translate number).Contains(String.replicate 4 letter))
let ``has max number of repeating I is 3`` =
``has max number of repeating letter is 3`` "I"
let ``has max number of repeating X is 3`` =
``has max number of repeating letter is 3`` "X"
let ``divisible by 5 will have a V`` number =
let romanizer = RomanNumeraler ()
(romanizer.Translate number).Contains "V"
let ``max number of letter is 1`` letter number =
let romanNumerals = ((RomanNumeraler()).Translate number)
let numberOf = match romanNumerals with
| "" -> 0
| _ -> (romanNumerals.Length - romanNumerals.Replace(letter, "").Length) / romanNumerals.Length
match numberOf with
| 0 | 1 -> true
| _ -> false
let ``max number of V is 1`` =
``max number of letter is 1`` "V"
let ``max number of L is 1`` =
``max number of letter is 1`` "L"
let ``max number of D is 1`` =
``max number of letter is 1`` "D"
let ``max number of CM is 1`` =
``max number of letter is 1`` "CM"
let ``max number of CD is 1`` =
``max number of letter is 1`` "CD"
let ``max number of XC is 1`` =
``max number of letter is 1`` "XC"
let ``max number of XL is 1`` =
``max number of letter is 1`` "XL"
let ``max number of IX is 1`` =
``max number of letter is 1`` "IX"
let ``max number of IV is 1`` =
``max number of letter is 1`` "IV"
[<Property>]
let ``for all valid inputs, there is a max of three repeating I`` (x:int) =
normalize x |> ``has max number of repeating I is 3``
[<Property>]
let ``for all valid inputs divisible by 5, there is a V`` (x:int) =
x % 2 <> 0
==> ``divisible by 5 will have a V`` (abs x * 5)
[<Property>]
let ``for all valid inputs, there is a max of three repeating X`` (x:int) =
normalize x |> ``has max number of repeating X is 3``
[<Property>]
let ``for all valid inputs there is a maximum of one V`` (x:int) =
normalize x |> ``max number of V is 1``
[<Property>]
let ``for all valid inputs there is a maximum of one L`` (x:int) =
normalize x |> ``max number of L is 1``
[<Property>]
let ``for all valid inputs there is a maximum of one D`` (x:int) =
normalize x |> ``max number of D is 1``
[<Property>]
let ``for all valid inputs there is a maximum of one CM`` (x:int) =
normalize x |> ``max number of CM is 1``
[<Property>]
let ``for all valid inputs there is a maximum of one CD`` (x:int) =
normalize x |> ``max number of CD is 1``
[<Property>]
let ``for all valid inputs there is a maximum of one XC`` (x:int) =
normalize x |> ``max number of XC is 1``
[<Property>]
let ``for all valid inputs there is a maximum of one XL`` (x:int) =
normalize x |> ``max number of XL is 1``
[<Property>]
let ``for all valid inputs there is a maximum of one IX`` (x:int) =
normalize x |> ``max number of IX is 1``
[<Property>]
let ``for all valid inputs there is a maximum of one IV`` (x:int) =
normalize x |> ``max number of IV is 1``
@MikeMKH
Copy link
Author

MikeMKH commented May 25, 2014

See my blog post which goes with this gist.

http://comp-phil.blogspot.com/2014/05/roman-numerals-and-fscheck.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment