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));
    }
}