Upside Down Numbers in Java


The challenge

Consider the numbers 6969 and 9116. When you rotate them 180 degrees (upside down), these numbers remain the same. To clarify, if we write them down on a paper and turn the paper upside down, the numbers will be the same. Try it and see! Some numbers such as 2 or 5 don’t yield numbers when rotated.

Given a range, return the count of upside down numbers within that range. For example, solve(0,10) = 3, because there are only 3 upside down numbers >= 0 and < 10. They are 0, 1, 8.

More examples in the test cases.

The solution in Java code

Option 1 (using IntStream):

import java.util.stream.IntStream;
import org.apache.commons.lang3.StringUtils;

public class UpsideDown {

    public int solve(int x, int y) {
        return (int) IntStream.range(x, y)
            .filter(i -> !StringUtils.containsAny(String.valueOf(i), "23457"))
            .filter(i -> new StringBuilder(i + "").reverse().toString()
                .replaceAll("6", "2")
                .replaceAll("9", "6")
                .replaceAll("2", "9")
                .equals(String.valueOf(i)))
            .count();
    }
}

Option 2 (using UnaryOperator):

import java.util.function.UnaryOperator;
import static java.util.stream.IntStream.range;

public class UpsideDown {
    public int solve(int x, int y) {
    UnaryOperator<String> upside = s -> new StringBuilder(
        s.replaceAll("[23457]", "0").replace('6', '_').replace('9', '6').replace('_', '9')).reverse()
            .toString();
    return (int) range(x, y).filter(i -> i == Integer.parseInt(upside.apply(i + ""))).count();
    }
}

Option 3 (using streams):

import java.util.stream.*; 
public class UpsideDown {

    public int solve(int x, int y) {
        return (int) IntStream.range(x, y).boxed().filter(z -> {
          String s = String.valueOf(z);
          if(s.matches(".*[23457].*")) return false;
          int l = s.length();
          if(l % 2 == 1) {
            if(s.substring(l/2, l/2+1).matches(".*[69].*")) return false;
          }
          int[] d = s.chars().map(Character::getNumericValue).toArray();
          for(int i = 0; i < l/2; i++) {
            if(d[i] != d[l-1-i]) {
              if(d[i] == 6 && d[l-1-i] == 9) continue;
              if(d[i] == 9 && d[l-1-i] == 6) continue;
              return false;
            } else {
              if(d[i] == 6 || d[i] == 9) return false;
            }
          }
          return true;
        }).count();
    }
}

Test cases to validate our solution

import org.junit.Test;
import static org.junit.Assert.assertEquals;
import org.junit.runners.JUnit4;

public class UpsideDownTest {
    UpsideDown sol = new UpsideDown();
    
    @Test
    public void basicTests() {
        assertEquals(3, sol.solve(0,10));
        assertEquals(4, sol.solve(10,100));
        assertEquals(12, sol.solve(100,1000));
        assertEquals(20, sol.solve(1000,10000));
        assertEquals(6, sol.solve(10000,15000));
        assertEquals(9, sol.solve(15000,20000));
        assertEquals(15, sol.solve(60000,70000));
        assertEquals(55, sol.solve(60000,130000));
    }
}