RomanNumeral.java

1
package com.roxoft.test;
2
3
import java.util.ArrayList;
4
import java.util.Arrays;
5
import java.util.List;
6
7
public final class RomanNumeral {
8
    /** Roman notation for ZERO. */
9
    public static final String ZERO = "NULLA";
10
11
    /** Characters which cannot be repeated in a Roman numeral. */
12
    private static final String UNREPEATABLES = "VLD";
13
    /** Characters which can be used as a subtractive in a Roman numeral. */
14
    private static final String SUBTRACTIVE = "IXC";
15
16
    /** The Roman numeral String in question. */
17
    private final String numeral;
18
    /** A numerical representation of the Roman numeral in question. */
19
    private final int value;
20
21
    @SuppressWarnings("magicnumber")
22
    private int characterValue(final String romanCharacter) {
23 1 1. characterValue : replaced int return with 0 for com/roxoft/test/RomanNumeral::characterValue → KILLED
        return switch (romanCharacter) {
24
            case "I" -> 1;
25
            case "V" -> 5;
26
            case "X" -> 10;
27
            case "L" -> 50;
28
            case "C" -> 100;
29
            case "D" -> 500;
30
            case "M" -> 1000;
31
            default ->
32
                    throw new InvalidRomanNumeralException("'"
33
                            + romanCharacter
34
                            + "' is an unknown Roman numeral character"
35
                    );
36
        };
37
    }
38
39
    private RomanNumeral(final String numeralString) {
40 1 1. <init> : negated conditional → KILLED
        if (numeralString.isEmpty()) {
41
            value = 0;
42
            this.numeral = ZERO;
43
            return;
44
        } else {
45
            this.numeral = numeralString;
46
        }
47
48
        //get all the characters that are the same
49
        final List<Integer> asIntegers = Arrays.stream(numeralString.split(""))
50
                .map(this::characterValue)
51
                .toList();
52
        final List<Integer> calculatedValues = new ArrayList<>(List.of(asIntegers.get(0)));
53
54
        for (int characterIndex = 1,
55
             previousValue = calculatedValues.get(0),
56
             calculatedValueIndex = 0,
57
             characterRepetitionCount = 1;
58 2 1. <init> : changed conditional boundary → KILLED
2. <init> : negated conditional → KILLED
             characterIndex < asIntegers.size(); characterIndex++) {
59
            int subject = asIntegers.get(characterIndex);
60
61 2 1. <init> : negated conditional → KILLED
2. <init> : changed conditional boundary → KILLED
            if (subject > previousValue) {
62
                characterRepetitionCount = 1;
63 2 1. <init> : negated conditional → KILLED
2. <init> : Replaced integer subtraction with addition → KILLED
                if (SUBTRACTIVE.contains(numeralString.substring(characterIndex - 1, characterIndex))) {
64 1 1. <init> : Replaced integer subtraction with addition → KILLED
                    calculatedValues.set(calculatedValueIndex, subject - calculatedValues.get(calculatedValueIndex));
65
                } else {
66
                    throw new InvalidRomanNumeralException("Use of '" + numeralString.charAt(characterIndex) + "' as subtractive character. Only I, X and C can be used as subtractive numerals.");
67
                }
68 1 1. <init> : negated conditional → KILLED
            } else if (subject == previousValue) {
69 2 1. <init> : Replaced integer addition with subtraction → KILLED
2. <init> : negated conditional → KILLED
                if (UNREPEATABLES.contains(numeralString.substring(characterIndex, characterIndex + 1))) {
70
                    throw new InvalidRomanNumeralException("Repeated instance of '" + numeralString.charAt(characterIndex) + "'. V, L or D cannot be repeated.");
71
                }
72 3 1. <init> : changed conditional boundary → KILLED
2. <init> : Changed increment from 1 to -1 → KILLED
3. <init> : negated conditional → KILLED
                if (++characterRepetitionCount > 3) {
73
                    throw new InvalidRomanNumeralException("Roman characters (in this case '" + numeralString.charAt(characterIndex) + "') cannot repeat more than 3 times");
74
                }
75 1 1. <init> : Replaced integer addition with subtraction → KILLED
                calculatedValues.set(calculatedValueIndex, calculatedValues.get(calculatedValueIndex) + subject);
76
            } else {
77
                characterRepetitionCount = 1;
78
                calculatedValues.add(subject);
79 1 1. <init> : Changed increment from 1 to -1 → KILLED
                calculatedValueIndex++;
80
            }
81
            previousValue = subject;
82
        }
83
84 1 1. lambda$new$0 : replaced int return with 0 for com/roxoft/test/RomanNumeral::lambda$new$0 → KILLED
        value = calculatedValues.stream().mapToInt(i -> i).sum();
85
86
    }
87
88
    /**
89
     * @param numeralString a Roman numeral String.
90
     * @return a {@link RomanNumeral} of the provided String.
91
     */
92
    public static RomanNumeral of(final String numeralString) {
93 1 1. of : replaced return value with null for com/roxoft/test/RomanNumeral::of → KILLED
        return new RomanNumeral(numeralString);
94
    }
95
96
    /** @return the numerical value of this Roman numeral. */
97
    public int numericalValue() {
98 1 1. numericalValue : replaced int return with 0 for com/roxoft/test/RomanNumeral::numericalValue → KILLED
        return value;
99
    }
100
101
    /** @return the String representation of this Roman numeral. */
102
    public String romanNotation() {
103 1 1. romanNotation : replaced return value with "" for com/roxoft/test/RomanNumeral::romanNotation → KILLED
        return numeral;
104
    }
105
}

Mutations

23

1.1
Location : characterValue
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testInvalid(java.lang.String)]/[test-template-invocation:#10]
replaced int return with 0 for com/roxoft/test/RomanNumeral::characterValue → KILLED

40

1.1
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[method:testEmpty()]
negated conditional → KILLED

58

1.1
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testRandomNumers(java.lang.String, int)]/[test-template-invocation:#10]
changed conditional boundary → KILLED

2.2
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testInvalid(java.lang.String)]/[test-template-invocation:#10]
negated conditional → KILLED

61

1.1
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testInvalid(java.lang.String)]/[test-template-invocation:#10]
negated conditional → KILLED

2.2
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testInvalid(java.lang.String)]/[test-template-invocation:#3]
changed conditional boundary → KILLED

63

1.1
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testInvalid(java.lang.String)]/[test-template-invocation:#10]
negated conditional → KILLED

2.2
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testInvalid(java.lang.String)]/[test-template-invocation:#10]
Replaced integer subtraction with addition → KILLED

64

1.1
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testRandomNumers(java.lang.String, int)]/[test-template-invocation:#4]
Replaced integer subtraction with addition → KILLED

68

1.1
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testInvalid(java.lang.String)]/[test-template-invocation:#4]
negated conditional → KILLED

69

1.1
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testInvalid(java.lang.String)]/[test-template-invocation:#4]
Replaced integer addition with subtraction → KILLED

2.2
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testInvalid(java.lang.String)]/[test-template-invocation:#6]
negated conditional → KILLED

72

1.1
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testRandomNumers(java.lang.String, int)]/[test-template-invocation:#3]
changed conditional boundary → KILLED

2.2
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testInvalid(java.lang.String)]/[test-template-invocation:#3]
Changed increment from 1 to -1 → KILLED

3.3
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testRepeatedNumerals(java.lang.String, int)]/[test-template-invocation:#1]
negated conditional → KILLED

75

1.1
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testRepeatedNumerals(java.lang.String, int)]/[test-template-invocation:#1]
Replaced integer addition with subtraction → KILLED

79

1.1
Location : <init>
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testRandomNumers(java.lang.String, int)]/[test-template-invocation:#12]
Changed increment from 1 to -1 → KILLED

84

1.1
Location : lambda$new$0
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testRandomNumers(java.lang.String, int)]/[test-template-invocation:#10]
replaced int return with 0 for com/roxoft/test/RomanNumeral::lambda$new$0 → KILLED

93

1.1
Location : of
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[method:testEmpty()]
replaced return value with null for com/roxoft/test/RomanNumeral::of → KILLED

98

1.1
Location : numericalValue
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[test-template:testRandomNumers(java.lang.String, int)]/[test-template-invocation:#10]
replaced int return with 0 for com/roxoft/test/RomanNumeral::numericalValue → KILLED

103

1.1
Location : romanNotation
Killed by : com.roxoft.test.RomanNumeralTest.[engine:junit-jupiter]/[class:com.roxoft.test.RomanNumeralTest]/[method:testEmpty()]
replaced return value with "" for com/roxoft/test/RomanNumeral::romanNotation → KILLED

Active mutators

Tests examined


Report generated by PIT 1.14.4