Created
May 23, 2014 18:03
-
-
Save MikeMKH/5ee3f4eaf18474d244d5 to your computer and use it in GitHub Desktop.
Roman Numerals kata in C# using FsCheck with XUnit to test.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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`` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
See my blog post which goes with this gist.
http://comp-phil.blogspot.com/2014/05/roman-numerals-and-fscheck.html