I am trying to count the number of combinations of 1, 5, 10 and 25 that sum to n. Given that I don't want any repetitions (like 1+5 = 6 and 5+1 = 6). I am using a hashSet. I implemented a class named ResultSet that saves the number of 1, 5, 10, and 25 in a solution and I overrode the equals method. However, for some reason, my solution hashSet keeps returning duplicate values. Why?
import java.util.HashSet;
public class Solution {
public static void main(String[] args) {
int N = 6;
int combinationsSolution = new Combine(N).getSolution();
System.out.println("N= " + N + " Number of solutions= " + combinationsSolution);
}
}
class Combine {
private int solution;
private int n;
private HashSet<ResultSet> cacheUnordered = new HashSet<ResultSet>();
public Combine(int N) {
this.n = N;
this.solution = solve(n);
}
public int getSolution() {
return solution;
}
public int solve(int N) {
solve(N, 0, 0, 0, 0);
for (ResultSet r:cacheUnordered){
System.out.println(r.toString());
}
return cacheUnordered.size();
}
public void solve(int N, int substracted1, int substracted5, int substracted10, int substracted25) {
if (N == 0) {
cacheUnordered.add(new ResultSet(substracted1, substracted5, substracted10, substracted25));
} else if (N > 0) {
solve(N - 1, substracted1 + 1, substracted5, substracted10, substracted25);
solve(N - 5, substracted1, substracted5 + 1, substracted10, substracted25);
solve(N - 10, substracted1, substracted5, substracted10 + 1, substracted25);
solve(N - 25, substracted1, substracted5, substracted10, substracted25 + 1);
}
}
}
class ResultSet {
private int numberOf1;
private int numberOf5;
private int numberOf10;
private int numberOf25;
public ResultSet(int num1, int num5, int num10, int num25) {
numberOf1 = num1;
numberOf5 = num5;
numberOf10 = num10;
numberOf25 = num25;
}
#Override
public String toString(){
String result;
result = numberOf1 + " " + numberOf5 + " " + numberOf10 + " " + numberOf25;
return result;
}
#Override
public boolean equals(Object r2) {
if (r2 == null) {
return false;
}
if (!(r2 instanceof ResultSet)) {
return false;
}
ResultSet rr = (ResultSet) r2;
if (rr.numberOf1 == this.numberOf1 && rr.numberOf5 == this.numberOf5
&& rr.numberOf10 == this.numberOf10 && rr.numberOf25 == this.numberOf25) {
System.out.println("Comparing " + this.toString() + " to " + rr.toString());
return true;
} else {
return false;
}
}
public int getNum1() {
return numberOf1;
}
public int getNum5() {
return numberOf5;
}
public int getNum10() {
return numberOf10;
}
public int getNum25() {
return numberOf25;
}
}
For your ResultSet class, you defined an equals() method but not a hashCode() method. You need both methods for HashSet to work correctly. Please see this explanation. (It talks about HashMap, but it also applies to HashSet.)
As JavaDoc Clearly Specified
Note that it is generally necessary to override the hashCode method
whenever this method is overridden, so as to maintain the general
contract for the hashCode method, which states that equal objects must
have equal hash codes.
and you have not followed it , that is why you get duplicates ,
Please Read How HashCode and Equals Work it will help you out to understand the above statement better
Related
I would like to send multiple values from my getMultiples method to my main method using a return statement and no print or println statements.
public class StaticMethods {
public static void main (String[] args) {
int a = 6;
int b = 9;
int result = getMultiple(a,b);
System.out.println(result + "\n")
System.out.println("The first " + a + " multiples of " + b + " are: ");
int p = getMultiples(a,b);
}
public static int getMultiple(int a,int b) {
return (int) (a * b);
}
public static int getMultiples(int a, int b) {
int p = 0;
for (int i = 1; i <= a; i++) {
p = getMultiple(a,i);
}
return (p);
}
}
I have tried putting the return statement in the for loop but it does not work.
In Java as soon as return is encountered in the code, method is removed from execution stack and flow is returned back to calling method. So you can not return multiple values from a method. Rather you should create a list/array and return that as below(array example):
public class StaticMethods {
public static void main (String[] args) {
int a = 6;
int b = 9;
int result = getMultiple(a,b);
System.out.println(result + "\n");
System.out.println("The first " + a + " multiples of " + b + " are: ");
int p[] = getMultiples(a,b);
}
public static int getMultiple(int a,int b) {
return (int) (a * b);
}
public static int[] getMultiples(int a, int b) {
int[] p = new int[a];
for (int i = 1; i <= a; i++) {
p[i-1] = getMultiple(a,i);
}
return p;
}
}
I'm having some issues with how I should approach this problem.
I have a Boat class which contains a toString() and getters and setters.
PowerBoat class which extends functionality and overrides toString() method from it's superclass.
SailBoat class which extends functionality and overrides toString() method from it's superclass.
In my test class I am adding different PowerBoats, SailBoats in a ArrayList of type of Boat.
I need to find the most expensive boat and print a toString() information about that boat.
public class Boat {
String color;
int length;
public Boat(){
color = "white";
length = 20;
}
public Boat(String color, int length){
setColor(color);
setLength(length);
}
public String getColor() {
return color;
}
public boolean setColor(String color) {
switch (color){
case "white" : this.color = color;
case "red" : this.color = color;
case "blue" : this.color = color;
case "yellow" : this.color = color;
return true;
}
return false;
}
public int getLength() {
return length;
}
public boolean setLength(int length) {
if(length >= 20 && length <= 50) {
this.length = length;
return true;
}
return false;
}
#Override
public String toString() {
return "Boat{" +
"color='" + color + '\'' +
", length=" + length +
'}';
}
}
public class PowerBoat extends Boat {
int engineSize;
public PowerBoat(){
super();
setEngineSize(5);
}
public PowerBoat(String color, int length, int engineSize){
super(color, length);
setEngineSize(engineSize);
}
public boolean setEngineSize(int engineSize){
if(engineSize >= 5 && engineSize <= 350){
this.engineSize = engineSize;
return true;
}
return false;
}
public int getEngineSize() {
return engineSize;
}
public int calcPrice(){
return 5000 + length + 300 + engineSize * 20;
}
#Override
public String toString() {
return super.toString() +
"engineSize= " + engineSize +
'}' + " Price " + calcPrice();
}
}
public class SailBoat extends Boat {
int numSails = 0;
public SailBoat(){
numSails = 1;
}
public SailBoat(String color, int length, int numSails){
super(color, length);
setNumSails(numSails);
}
public int getNumSails() {
return numSails;
}
public boolean setNumSails(int numSails) {
if(numSails >= 1 && numSails <= 4){
this.numSails = numSails;
return true;
}
return false;
}
public int calcPrice(){
return length * 1000 + numSails * 2000;
}
#Override
public String toString() {
return super.toString() +
"numSails= " + numSails +
'}' + " price " + calcPrice();
}
}
public class Inventory {
public static void main(String[] args){
ArrayList<Boat> list = new ArrayList();
Boat powerBoat = new PowerBoat("blue", 46, 60);
Boat powerBoat1 = new PowerBoat("yellow", 42, 55);
Boat sailBoat = new SailBoat("white", 32, 1);
Boat sailBoat1 = new SailBoat("red", 24, 2);
list.add(powerBoat);
list.add(powerBoat1);
list.add(sailBoat);
list.add(sailBoat1);
int sumSailBoat = 0;
int sumPowerBoat = 0;
int largest = Integer.MIN_VALUE;
for(Boat b : list){
if (b instanceof SailBoat){
sumSailBoat+= ((SailBoat) b).calcPrice();
if (((SailBoat) b).calcPrice() > largest){
if(b instanceof SailBoat) {
largest = ((SailBoat) b).calcPrice();
}
}
}
if (b instanceof PowerBoat){
sumPowerBoat+= ((PowerBoat) b).calcPrice();
if(((PowerBoat) b).calcPrice() > largest){
if(b instanceof PowerBoat){
largest = ((PowerBoat) b).calcPrice();
}
}
}
}
int totalSum = sumSailBoat + sumPowerBoat;
System.out.println("Total price of sail boats is " + sumSailBoat);
System.out.println("Total price of sail boats is " + sumPowerBoat);
System.out.println("Total price of sail and power boats is " + totalSum);
System.out.println("Most expensive boat is " + largest);
}
}
I managed to find the largest price but how can I print the toString information about that Boat?
If you know the type you can use:
System.out.println("My PowerBoat " + ((PowerBoat) b));
System.out.println("My SailBoat " + ((SailBoat) b));
If you already found the object you need to print store it in a variable and just call toString on it.
Boat b = //whichever one you found here from the arraylist
System.out.println(b.toString())
Since classes in java use dynamic binding, even if your reference is of the superclass, the contents of the method should still be the same.
[teach-me] You seem to have some misconceptions on OO design and java method calls. Please talk to your teachers or read some OO/Java books. Start with the concept of abstract and final in Java - that should give you some idea how methods are called and how inheritance can be used.
That being said, you need to do 2 things.
(1) To answer your question, in addition to int largest, also keep Boat mostExpensiveBoat, update it at the same time you update largest. Then at the end print mostExpensiveBoat.toString().
(2) You need to make calcPrice() an abstract method of Boat. Then when you call b.calcPrice(), the method that matches the type of object b refers to will "magically" be invoked. No need check instanceof or to cast.
It will be better, to make calcPrice() part of Boat interface:
abstract class Boat {
String color;
int length;
public Boat(){
color = "white";
length = 20;
}
public Boat(String color, int length){
setColor(color);
setLength(length);
}
public String getColor() {
return color;
}
public boolean setColor(String color) {
switch (color){
case "white" : this.color = color;
case "red" : this.color = color;
case "blue" : this.color = color;
case "yellow" : this.color = color;
return true;
}
return false;
}
public int getLength() {
return length;
}
public boolean setLength(int length) {
if(length >= 20 && length <= 50) {
this.length = length;
return true;
}
return false;
}
public abstract int calcPrice();
#Override
public String toString() {
return "Boat{" +
"color='" + color + '\'' +
", length=" + length +
'}';
}
}
Then you can make search in this way:
int sumSailBoat = 0;
int sumPowerBoat = 0;
int largest = Integer.MIN_VALUE;
Boat boatWithLargestPrice = null;
for (Boat b : list) {
int boatPrice = b.calcPrice();
if (boatPrice > largest) {
largest = boatPrice;
boatWithLargestPrice = b;
}
if (b instanceof SailBoat) {
sumSailBoat += boatPrice;
} else {
sumPowerBoat += boatPrice;
}
}
int totalSum = sumSailBoat + sumPowerBoat;
System.out.println("Total price of sail boats is " + sumSailBoat);
System.out.println("Total price of power boats is " + sumPowerBoat);
System.out.println("Total price of sail and power boats is " + totalSum);
System.out.println("Most expensive boat is " + boatWithLargestPrice + " with price " + largest);
By making your Boat class abstract you can require all Boats to implement calcPrice. Then you can just treat all Boats the same.
public abstract class Boat {
final String color;
final int length;
public Boat(String color, int length) {
this.color = color;
this.length = length;
}
public abstract int calcPrice();
#Override
public String toString() {
return "Boat{" +
"color='" + color + '\'' +
", length=" + length +
'}';
}
}
public class PowerBoat extends Boat {
final int engineSize;
public PowerBoat(String color, int length, int engineSize) {
super(color, length);
this.engineSize = engineSize;
}
#Override
public int calcPrice() {
return 5000 + length + 300 + engineSize * 20;
}
#Override
public String toString() {
return super.toString() +
"engineSize= " + engineSize +
'}' + " Price " + calcPrice();
}
}
public class SailBoat extends Boat {
final int numSails;
public SailBoat(String color, int length, int numSails) {
super(color, length);
this.numSails = numSails;
}
#Override
public int calcPrice() {
return length * 1000 + numSails * 2000;
}
#Override
public String toString() {
return super.toString() +
"numSails= " + numSails +
'}' + " price " + calcPrice();
}
}
public void test() {
ArrayList<Boat> list = new ArrayList();
list.add(new PowerBoat("blue", 46, 60));
list.add(new PowerBoat("yellow", 42, 55));
list.add(new SailBoat("white", 32, 1));
list.add(new SailBoat("red", 24, 2));
Boat mostExpensiveBoat = null;
int totalSum = 0;
for (Boat boat : list) {
totalSum += boat.calcPrice();
if(mostExpensiveBoat == null || boat.calcPrice() > mostExpensiveBoat.calcPrice() ) {
mostExpensiveBoat = boat;
}
}
System.out.println("Total price of sail and power boats is " + totalSum);
System.out.println("Most expensive boat is " + mostExpensiveBoat);
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Does an open-ended interval implementation exist for Java?
I'm new to Java and i would like to know what is the best data structure and how can i search through that data structure for my case:
I have int intervals eg: 10-100, 200-500, 1000-5000 and for each interval i have a value 1, 2, 3, 4.
I would like to know how can i save all those intervals and their values in a data structure and how can i search through that data structure to return the value for the specific interval.
Eg. if i search 15, that is in interval 10-100, i would like to return 1.
Thank you
Use TreeMap, which is NavigableMap (Java 6 or higher).
Suppose you have entries key->value (10->1, 100->1, 200->2, 500->2, 1000->3, 5000->3)
floorEntry(15) will return 10->1
ceilingEntry(15) will return 100->1
With this you can determine the interval number of 15, which is 1.
You can also determine if a number is between intervals.
Edit: added example
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
map.put(10, 1);
map.put(100, 1);
map.put(200, 2);
map.put(500, 2);
map.put(1000, 3);
map.put(5000, 3);
int lookingFor = 15;
int groupBelow = map.floorEntry(lookingFor).getValue();
int groupAbove = map.ceilingEntry(lookingFor).getValue();
if (groupBelow == groupAbove) {
System.out.println("Number " + lookingFor + " is in group " + groupBelow);
} else {
System.out.println("Number " + lookingFor +
" is between groups " + groupBelow + " and " + groupAbove);
}
I would use this approach:
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class IntervalsTest {
#Test
public void shouldReturn1() {
Intervals intervals = new Intervals();
intervals.add(1, 10, 100);
intervals.add(2, 200, 500);
int result = intervals.findInterval(15);
assertThat(result, is(1));
}
#Test
public void shouldReturn2() {
Intervals intervals = new Intervals();
intervals.add(1, 10, 100);
intervals.add(2, 200, 500);
int result = intervals.findInterval(201);
assertThat(result, is(2));
}
}
class Range {
private final int value;
private final int lowerBound;
private final int upperBound;
Range(int value, int lowerBound, int upperBound) {
this.value = value;
this.lowerBound = lowerBound;
this.upperBound = upperBound;
}
boolean includes(int givenValue) {
return givenValue >= lowerBound && givenValue <= upperBound;
}
public int getValue() {
return value;
}
}
class Intervals {
public List<Range> ranges = new ArrayList<Range>();
void add(int value, int lowerBound, int upperBound) {
add(new Range(value, lowerBound, upperBound));
}
void add(Range range) {
this.ranges.add(range);
}
int findInterval(int givenValue) {
for (Range range : ranges) {
if(range.includes(givenValue)){
return range.getValue();
}
}
return 0; // nothing found // or exception
}
}
If your intervals are mutually exclusive, a sorted map (java.util.TreeMap) using a comparator on the last member of the interval, and using firstKey on a tailMap on the searched item should work fine.
If the intervals may overlap, you need a segment tree (http://en.wikipedia.org/wiki/Segment_tree), of which there's no implementation in the standard library.
Use hashmap (fast, more memory) or List (slower, little memory). I provide you both solutions below:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class Interval {
private int begin;
private int end;
// 1, 2, 3, 4
private int value;
public Interval(int begin, int end, int value) {
this.begin = begin;
this.end = end;
this.value = value;
}
public int getBegin() {
return begin;
}
public void setBegin(int begin) {
this.begin = begin;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public boolean contains(int number) {
return (number > begin - 1) && (number < end + 1);
}
}
public class IntervalSearch {
// more memory consuming struct, fastest
Map<Integer, Interval> intervalMap = new HashMap<Integer, Interval>();
// less memory consuming, little slower
List<Interval> intervalList = new ArrayList<Interval>();
private boolean fastMethod = true;
public IntervalSearch(boolean useFastMethod) {
this.fastMethod = useFastMethod;
}
public Integer search(int number) {
return fastMethod ? searchFast(number) : searchSlow(number);
}
private Integer searchFast(int number) {
return intervalMap.get(number).getValue();
}
private Integer searchSlow(int number) {
for (Interval ivl : intervalList) {
if (ivl.contains(number)) {
return ivl.getValue();
}
}
return null;
}
public void addInterval(Integer begin, Integer end, Integer value) {
Interval newIvl = new Interval(begin, end, value);
if (fastMethod) {
addIntervalToMap(newIvl);
} else {
addIntervalToList(newIvl);
}
}
private void addIntervalToList(Interval newIvl) {
intervalList.add(newIvl);
}
private void addIntervalToMap(Interval newIvl) {
for (int i = newIvl.getBegin(); i < newIvl.getEnd() + 1; i++) {
intervalMap.put(i, newIvl);
}
}
public boolean isFastMethod() {
return fastMethod;
}
}
Your question is not entirely clear, particularly what you mean by the values 1, 2, 3, 4. But if you want a data structure that holds the limits of an interval, and checks if a number is within them, then make one! Like this:
public class Interval {
int low;
int high;
public Interval(int low, int high) {
this.low = low;
this.high = high;
}
public boolean intervalContains(int value) {
return ((value >= low) && (value <= high));
}
}
And use it:
Interval theInterval = new Interval(10,100);
System.out.print(theInterval.contains(15)); // prints "true"
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 11 years ago.
Given a string, compute recursively (no loops) the number of times lowercase "hi" appears in the string
countHi("xxhixx") -> 1
countHi("xhixhixx") -> 2
countHi("hi") -> 1
public class Tester {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int count = countHi("xxhixx");
System.out.println("countHi: " + count);
}
public static int countHi(String s) {
if (s.length() == 0) {
return 0;
}
int spot = s.indexOf("hi");
if(spot > 0)
{
String nextString = s.substring(spot + 2);
return 1 + countHi(nextString);
}
return 1;
}
}
Should work with the following code:
public static int countHi(String s) {
return countHi(s, 0);
}
public static int countHi(String s, int pos) {
if (s.length() - pos < 2) {
return 0;
}
int result = 0;
if(s.charAt(pos) == 'h' && s.charAt(pos + 1) == 'i') {
result++;
}
return result + countHi(s, pos + 2);
}
F(x[1...n]) =
if the string starts with "hi" then 1 + F(x[3...n])
else F(x[2...n])
Your recursive function will need a parameter that tells it where in the string to start looking. If the character at that position is 'h', then check whether the one after it is 'i'; if it is, you've found a match.
For the recursive call that checks the remainder of the string, pass the index of the next character if it wasn't 'i', or two characters ahead if it was.
(I'm giving just a description instead of actual code because this looks like a homework question.)
package com.indivunet.utils;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class Tester
{
/**
* #param args
*/
public static void main(String[] args)
{
printCountFor("xxhixx", 1);
printCountFor("", 0);
printCountFor("xxhixxhihi", 3);
printCountFor(null, 0);
}
#Test
public void countHi()
{
assertAndPrint("xxhixx", 1);
assertAndPrint("", 0);
assertAndPrint("xxhixxhihi", 3);
assertAndPrint(null, 0);
}
private void assertAndPrint(String string, int expectedCount)
{
int count = printCountFor(string, expectedCount);
assertEquals(expectedCount, count);
}
private static int printCountFor(String string, int expected)
{
int count = countHi(string);
System.out.println("string: \"" + string + "\" expected: " + expected + " count: " + count);
return count;
}
public static int countHi(String s)
{
if (s == null)
{
return 0;
}
int count = 0;
boolean hiSpotted = true;
while (hiSpotted)
{
int startOfNextHi = s.indexOf("hi");
hiSpotted = startOfNextHi >= 0;
if (!hiSpotted)
{
return count;
}
s = s.substring(startOfNextHi + 2);
count++;
}
return count;
}
}
public static int recCountHi(String str) {
if(str.length() < 2) {
return 0;
}
if(str.substring(0, 2).equals("hi")) {
return 1 + recCountHi(str.substring(1));
}
return recCountHi(str.substring(1));
}
I wrote an utility class to encode numbers in a custom numeral system with base N. As any self-respecting Java programmer I then wrote a unit test to check that the code works as expected (for any number I could throw at it).
It turned out, that for small numbers, it worked. However, for sufficiently large numbers, the tests failed.
The code:
public class EncodeUtil {
private String symbols;
private boolean isCaseSensitive;
private boolean useDefaultSymbols;
private int[] symbolLookup = new int[255];
public EncodeUtil() {
this(true);
}
public EncodeUtil(boolean isCaseSensitive) {
this.useDefaultSymbols = true;
setCaseSensitive(isCaseSensitive);
}
public EncodeUtil(boolean isCaseSensitive, String symbols) {
this.useDefaultSymbols = false;
setCaseSensitive(isCaseSensitive);
setSymbols(symbols);
}
public void setSymbols(String symbols) {
this.symbols = symbols;
fillLookupArray();
}
public void setCaseSensitive(boolean isCaseSensitive) {
this.isCaseSensitive = isCaseSensitive;
if (useDefaultSymbols) {
setSymbols(makeAlphaNumericString(isCaseSensitive));
}
}
private void fillLookupArray() {
//reset lookup array
for (int i = 0; i < symbolLookup.length; i++) {
symbolLookup[i] = -1;
}
for (int i = 0; i < symbols.length(); i++) {
char c = symbols.charAt(i);
if (symbolLookup[(int) c] == -1) {
symbolLookup[(int) c] = i;
} else {
throw new IllegalArgumentException("duplicate symbol:" + c);
}
}
}
private static String makeAlphaNumericString(boolean caseSensitive) {
StringBuilder sb = new StringBuilder(255);
int caseDiff = 'a' - 'A';
for (int i = 'A'; i <= 'Z'; i++) {
sb.append((char) i);
if (caseSensitive) sb.append((char) (i + caseDiff));
}
for (int i = '0'; i <= '9'; i++) {
sb.append((char) i);
}
return sb.toString();
}
public String encodeNumber(long decNum) {
return encodeNumber(decNum, 0);
}
public String encodeNumber(long decNum, int minLen) {
StringBuilder result = new StringBuilder(20);
long num = decNum;
long mod = 0;
int base = symbols.length();
do {
mod = num % base;
result.append(symbols.charAt((int) mod));
num = Math.round(Math.floor((num-mod) / base));
} while (num > 0);
if (result.length() < minLen) {
for (int i = result.length(); i < minLen; i++) {
result.append(symbols.charAt(0));
}
}
return result.toString();
}
public long decodeNumber(String encNum) {
if (encNum == null) return 0;
if (!isCaseSensitive) encNum = encNum.toUpperCase();
long result = 0;
int base = symbols.length();
long multiplier = 1;
for (int i = 0; i < encNum.length(); i++) {
char c = encNum.charAt(i);
int pos = symbolLookup[(int) c];
if (pos == -1) {
String debugValue = encNum.substring(0, i) + "[" + c + "]";
if (encNum.length()-1 > i) {
debugValue += encNum.substring(i + 1);
}
throw new IllegalArgumentException(
"invalid symbol '" + c + "' at position "
+ (i+1) + ": " + debugValue);
} else {
result += pos * multiplier;
multiplier = multiplier * base;
}
}
return result;
}
#Override
public String toString() {
return symbols;
}
}
The test:
public class EncodeUtilTest {
#Test
public void testRoundTrip() throws Exception {
//for some reason, numbers larger than this range will not be decoded correctly
//maybe some bug in JVM with arithmetic with long values?
//tried also BigDecimal, didn't make any difference
//anyway, it is highly improbable that we ever need such large numbers
long value = 288230376151711743L;
test(value, new EncodeUtil());
test(value, new EncodeUtil(false));
test(value, new EncodeUtil(true, "1234567890qwertyuiopasdfghjklzxcvbnm"));
}
#Test
public void testRoundTripMax() throws Exception {
//this will fail, see above
test(Long.MAX_VALUE, new EncodeUtil());
}
#Test
public void testRoundTripGettingCloserToMax() throws Exception {
//here we test different values, getting closer to Long.MAX_VALUE
//this will fail, see above
EncodeUtil util = new EncodeUtil();
for (long i = 1000; i > 0; i--) {
System.out.println(i);
test(Long.MAX_VALUE / i, util);
}
}
private void test(long number, EncodeUtil util) throws Exception {
String encoded = util.encodeNumber(number);
long result = util.decodeNumber(encoded);
long diff = number - result;
//System.out.println(number + " = " + encoded + " diff " + diff);
assertEquals("original=" + number + ", result=" + result + ", encoded=" + encoded, 0, diff);
}
}
Any ideas why things start failing when the values get large? I also tried BigInteger, but it did not seem to make a difference.
You're using floating point maths in your encodeNumber method, which makes your code rely on the precision of the double type.
Replacing
num = Math.round(Math.floor((num-mod) / base));
with
num = (num - mod) / base;
Makes the tests pass. Actually
num = num / base;
Should work just as well (thought experiment: what is 19 / 10 when / is integer division?).
You have a conversion to double in your code, which could be generating strange results for large values.
num = Math.round(Math.floor((num-mod) / base));
that would be my first port of call.