---
title: SumHex
weight: 6
---

# Task

- The input is in decimal and hexadecimal numbers
- Hexademical numbers has the prefix `0x`
- The input is case insensitive
- Class should be named `SumHex`

## Solution

This modification doesn't seem too complicated either.

Let's modify our initial `Sum.java` and apply all our improvements from a previous modification. (A good exercise would be to try doing that yourself and then comeback to check)

```java
// SumHex.java

public class SumHex {
    public static void main(String[] args) {
        int sum = 0;
        for (String argument : args) {
            StringBuilder number = new StringBuilder();
            for (char c : argument.toCharArray()) {
                if (!Character.isWhitespace(c)) {
                    number.append(c);
                } else {
                    sum += parseNumber(number);
                    number = new StringBuilder();
                }
            }

            sum += parseNumber(number);
        }

        System.out.println(sum);
    }

    static int parseNumber(StringBuilder number) {
        return number.isEmpty() ? 0 : Integer.parseInt(number.toString());
    }
}
```

> [!NOTE]
> The importance of these changes, is that in all further modifications we will only be changing our parseNumber method and leaving `main` method almost unchanged. The only modifications to `main` would be changing the data type of `sum`.

Now `parseNumber` thinks that every number is decimal, but we don't know what to do with hexadecimal numbers. Let's try that

```bash
$ java SumHex 0x1 0x2 0x3
Exception in thread "main" java.lang.NumberFormatException: For input string: "0x1"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
	at java.base/java.lang.Integer.parseInt(Integer.java:662)
	at java.base/java.lang.Integer.parseInt(Integer.java:778)
	at SumHex.parseNumber(SumHex.java:25)
	at SumHex.main(SumHex.java:18)
```

So we need to split decimal and hexadecimal processing inside `parseNumber`. Our code would look something like this.

```java
static int parseNumber(StringBuilder number) {
    if (!number.isEmpty()) {
        if (/* number is hexadecimal */) {
            return /* parse hexadecimal */; 
        } else { // if the number if decimal
            return Integer.parseInt(number.toString());
        }
    }
    
    return 0;
}
```

Here we have 2 major questions:

- How to check if the number is hexadecimal?
- How to parse the hexadecimal number?

We can answer the 1st question, because we know that hexadecimal number starts with `"0x"`. And we know that the input is not case sensitive, so `"0X"` is also a valid option.

So we can change our *pseude-code* like this

```java
static int parseNumber(StringBuilder number) {
    if (!number.isEmpty()) {
        if (/* number starts with "0x" or "0X" */) {
            return /* parse hexadecimal */; 
        } else { // if the number is decimal
            return Integer.parseInt(number.toString());
        }
    }
    
    return 0;
}
```

But how to check if the string starts with some prefix? There is a [`String.startsWith(String prefix)`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#startsWith-java.lang.String-) that returns `true` if the string starts with `prefix` and `false` otherwise.

So we can change our code using this information.

```java
static int parseNumber(StringBuilder number) {
    if (!number.isEmpty()) {
        String numberString = number.toString();
        if (
            numberString.startsWith("0x") || 
            numberString.startsWith("0X")
        ) {
            return /* parse hexadecimal */; 
        } else { // if the number is decimal
            return Integer.parseInt(number.toString());
        }
    }
    
    return 0;
}
```

> [!NOTE]
> `||` here is a logical ***or***.

We still have one problem. How to parse the hexadecimal number?

Using [`Integer.parseInt()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#parseInt-java.lang.String-int-) method. It turnes out, that it has the second argument `radix` where we can specify the base of the numerical system (in our case it's `16`).

So our code will look like this.

```java
static int parseNumber(StringBuilder number) {
    if (number.isEmpty()) {
        String numberString = number.toString();
        if (
            numberString.startsWith("0x") || 
            numberString.startsWith("0X")
        ) {
            return Integer.parseInt(numberString.substring(2), 16); 
        } else {
            return Integer.parseInt(numberString);
        }
    }
    
    return 0;
}
```

> [!NOTE]
> Here we used [`String.substring(int beginIndex)`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#substring-int-) method to cut off first two symbols (`"0x"`/`"0X"`) in order for `parseInt` to parse number correctly and not give us exceptions. In our case `beginIndex = 2` because it's the first index of the substring that we want to get. Remember that ***indexing starts with 0*** (not 1).

> [!IMPORTANT]
> It's important to know that we can't store all hexadecimal numbers in `int` data type. Let's look at this example
> ```bash
> $ java SumHex 0x80000000
> Exception in thread "main" java.lang.NumberFormatException: For input string: "80000000" under radix 16
> 	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
> 	at java.base/java.lang.Integer.parseInt(Integer.java:662)
> 	at SumHex.parseNumber(SumHex.java:30)
> 	at SumHex.main(SumHex.java:18) 
> ``` 
> That's why we will need to use `long` data type for storing hexademical values.

Here's the updated code

```java
static long parseNumber(StringBuilder number) { // int -> long
    if (!number.isEmpty()) {
        String numberString = number.toString();
        if (numberString.startsWith("0x") || numberString.startsWith("0X")) {
            // Integer.parseInt -> Long.parseLong
            return Long.parseLong(numberString.substring(2), 16);
        } else {
            return Integer.parseInt(numberString);
        }
    } else {
        return 0;
    }
}
```

Let's present the full solution

```java
// SumHex.java

public class SumHex {

    public static void main(String[] args) {
        int sum = 0;
        for (String argument : args) {
            StringBuilder number = new StringBuilder();
            for (char c : argument.toCharArray()) {
                if (!Character.isWhitespace(c)) {
                    number.append(c);
                } else {
                    sum += parseNumber(number);
                    number = new StringBuilder();
                }
            }

            sum += parseNumber(number);
        }

        System.out.println(sum);
    }

    static long parseNumber(StringBuilder number) {
        if (!number.isEmpty()) {
            String numberString = number.toString();
            if (numberString.startsWith("0x") || numberString.startsWith("0X")) {
                return Long.parseLong(numberString.substring(2), 16);
            } else {
                return Integer.parseInt(numberString);
            }
        } else {
            return 0;
        }
    }
}
```

> [!IMPORTANT]
> Notice, that we didn't change `main` method at all, because we have a separate function handling parsing. That's a good sign because our `main` method doesn't know what number parsing algorithm is used and therefore not dependent on it.
