While working with some code base, I am trying to understand piece of code so as can work and customize it , I am able to understand almost 90% of the code flow. Here is the overall flow
Code is being used to generate 15 digit code (alphanumeric), first 3 digits are customer provided.
Initially code is generating 16 digit alphanumeric number and storing it in the cache.
Customer can generated any number of code by specifying quantity.
all customer generated codes are being generated from the 16 digit number (point 2). All code generated have numbers/ alphabets from that 16 digit alphanumeric number.
When some one try to use those codes, system is trying to validate if the provided code is valid or not.
I am struck at the logic used to determine if the provided code is valid or not, here is that piece of code, I am generating 6 code as a sample, in this case alphanumeric code being generated and stored in the cache is
initial-alphabet : M9W6K3TENDGSFAL4
Code generated based on initial-alphabet are
myList=[123-MK93-ES6D-36F3, 123-MK93-EFTW-D3LG, 123-MK93-EALK-TGLD, 123-MK93-ELKK-DN6S, 123-MK93-E4D9-3A6T, 123-MK93-EMTW-LNME]
protected int getVoucherNumber(String voucherCode){
int voucherNumberPos = voucherCode.length() - 12;
String voucherNumberHex = voucherCode.substring(voucherNumberPos, voucherNumberPos + 6);
int firstByte = getIntFromHexByte(voucherNumberHex.substring(0, 2), 0);
int secondByte = getIntFromHexByte(voucherNumberHex.substring(2, 4), 1);
int thirdByte = getIntFromHexByte(voucherNumberHex.substring(4, 6), 7);
return firstByte << 16 | secondByte << 8 | thirdByte;
}
private int getIntFromHexByte(String value, int offset){
return (getIntFromHexNibble(value.charAt(0), offset) << 4) + getIntFromHexNibble(value.charAt(1), offset + 4);
}
private int getIntFromHexNibble(char value, int offset){
int pos = getAlphabet().indexOf(value);
if (pos == -1) {// nothing found}
pos -= offset;
while (pos < 0) {
pos += 16;
}
return pos % 16;
}
Here is the code which is trying to validate code
int voucherNumber = getVoucherNumber(kyList.get(4));
In this case value of voucherNumber is 4 i.e the fourth element from the list, in case I pass any value which is not part of the list getVoucherNumber method is returning a higher value (greater than the list count).
One of the main thing which confusing me are these 2 lines
int voucherNumberPos = voucherCode.length() - 12;
String voucherNumberHex = voucherCode.substring(voucherNumberPos, voucherNumberPos + 6);
As per my understanding, they are first moving out the first 3 digit from the check which are customer supplied but again they have not used rest of the string but only specific part of the string.
Can any one help me to understand this
It appears you've inherited responsibility for some poorly written code. We've all been there so I'll try to answer in that spirit. I'm not positive this question is on-topic for this site, but it doesn't appear to be forbidden by the help center. In an attempt to stay on-topic I'll end with some general advice not limited to the highly-localized specifics of the question.
myList.get(4)
Arrays in Java are zero-based, so that's 123-MK93-E4D9-3A6T. You probably know that, but it isn't clear from your question that you do.
initial-alphabet : M9W6K3TENDGSFAL4
I assume this is what's returned by the call to getAlphabet in getIntFromHexNibble. So the alphanumeric characters in the code are meant to be hexadecimal but using a nonstandard set of 16 characters for the digits.
protected int getVoucherNumber(String voucherCode){
Ignoring the hyphens and the customer-supplied first three digits, the code is 'MK93E4D93A6T'. Twelve hex digits encode 48 bits, but an int in Java is only 32 bits long, so the code is already broken. Whatever it does, it isn't going to return the voucher number represented by the voucher code.
int voucherNumberPos = voucherCode.length() - 12;
String voucherNumberHex = voucherCode.substring(voucherNumberPos, voucherNumberPos + 6);
This is setting voucherNumberHex to a six-character long string, starting twelve from the end of voucherCode, in this case 93-E4D. It seems likely the author didn't expect the caller to include the hyphens when this code was first written. Even so the intent seems to be to ignore half the voucher code.
int firstByte = getIntFromHexByte(voucherNumberHex.substring(0, 2), 0);
int secondByte = getIntFromHexByte(voucherNumberHex.substring(2, 4), 1);
int thirdByte = getIntFromHexByte(voucherNumberHex.substring(4, 6), 7);
This looks straightforward at first, but the parameters 0, 1, and 7 are not offsets at all, despite the name of the argument. It's trying to turn each pair of hex digits into a byte, which would be sensible enough if not for the hyphen character. Now for the fun part:
private int getIntFromHexNibble(char value, int offset) {
int pos = getAlphabet().indexOf(value);
if (pos == -1) {// nothing found}
pos -= offset;
while (pos < 0) {
pos += 16;
}
return pos % 16;
}
The right curly brace after "found" is commented out, so the code you posted is actually incomplete. I'm going to assume there's another line or two that read
return pos;
}
So the basic idea is that M becomes 0, 9 becomes 1, and so on via the call to indexOf. But if this method sees a character not in the provided alphabet, like a hyphen, it uses the so-called offset to calculate a default value (in this case 14, if I've done the math in my head right), and returns that as the hex nibble value.
The end result is that you get back a number in the range 0 (inclusive) to 2^24 (exclusive). But of the 2^24 possible values such a number should have, only 2^20 different values will ever be returned. So from a voucher code that looks like twelve digits of base-32, which would have an astronomical number of values, you're limited to slightly over a million different voucher numbers within each customer prefix.
General advice:
Use peer reviews to prevent code like this from getting into
production.
Use unit tests to prove the code does what the function
name says it does.
Use exceptions to fail early if the input isn't
what you're expecting.
Related
I am new to Java and programming all together.. I am trying to make a program that ciphers a number for the user. The user inputs 5 digits separately so I add them together to get a total. I need to pull the first digit and second digit of the total and enter it into (firstDigit+key)%10 and (secondDigit+key)%10. Then need to combine the answer to each equation together.
My total needs to be two digits, so even if the user enters all 1's which would total to be 5, I need it to be displayed and seen as 05 so that both equations have a digit to use. It needs to be two digits. I cant seem to figure how to enter a place holder. I was thinking about trying to use:
if (total < 10)
but then what?
Secondly, the method I used below seems like a terrible way to pull a single digit from a number. I think I changed the int total into a string so I can use .substring to pull the digits, then converted back to an int. Surprisingly it works. Is there a better way to do this knowing that the number is random?
String totalString = Integer.toString(total);
String stringDigit1 = totalString.substring(0,1);
String stringDigit2 = totalString.substring(1,2);
int firstDigitInt1 = Integer.parseInt(stringDigit1);
int firstDigitInt2 = Integer.parseInt(stringDigit2);
int encodedDigit1 = (firstDigitInt1+key)%10;
int encodedDigit2 = (firstDigitInt2+key)%10;
System.out.println("Your encoded Number is: " + encodedDigit1 + encodedDigit2);
Your method for obtaining the individual digits is good, and if you want to maintain it I believe your intuition is correct, it should suffice to say:
if (total < 10) {
firstDigitInt1 = 0
}
This will work out with your following math.
Your method with substrings is far from terrible, but in case you wanted something more sleek you can instead say:
int Digit1 = total / 10;
int Digit2 = total % 10;
Here, you can take advantage of integer truncation (where an integer won't remember decimal places) to get the first digit, which also solves the first problem: 5 / 10 = 0 (in terms of ints). For the second digit, it suffices to say modulo 10, as it is the remainder once the total is divided by 10.
I am trying to figure out how to convert hex into a string and integer so I can manipulate an RGB light on my arduino micro-controller through it's serialport. I found a good example on the java website, but I'm having a difficult time understanding some of the methods and I am getting hung up. I could easily just copy-paste this code and have it work but I want to fully understand it. I will add comments to my understandings and hopefully someone can provide some feedback.
public class HexToDecimalExample3{
public static int getDecimal(String hex){ //this is the function which we will call later and they are declaring string hex here. Can we declare string hex inside the scope..?
String digits = "0123456789ABCDEF"; //declaring string "digits" with all possible inputs in linear order for later indexing
hex = hex.toUpperCase(); //converting string to uppercase, just "in case"
int val = 0; //declaring int val. I don't get this part.
for (int i = 0; i < hex.length(); i++) //hex.length is how long the string is I think, so we don't finish the loop until all letters in string is done. pls validate this
{
char c = hex.charAt(i); //char is completely new to me. Are we taking the characters from the string 'hex' and making an indexed array of a sort? It seems similar to indexOf but non-linear? help me understand this..
int d = digits.indexOf(c); //indexing linearly where 0=1 and A=11 and storing to an integer variable
val = 16*val + d; //How do we multiply 16(bits) by val=0 to get a converted value? I do not get this..
}
return val;
}
public static void main(String args[]){
System.out.println("Decimal of a is: "+getDecimal("a")); //printing the conversions out.
System.out.println("Decimal of f is: "+getDecimal("f"));
System.out.println("Decimal of 121 is: "+getDecimal("121"));
}}
To summerize the comments, it's primarily the char c = hex.charAt(i); AND the val = 16*val + d; parts I don't understand.
Ok, let's go line for line
public static int getDecimal(String hex)
hex is the parameter, it needs to be declared there, so you can pass a String when you call the function.
String digits = "0123456789ABCDEF";
Yes, this declares a string with all characters which can occur in a hexadecimal number.
hex = hex.toUpperCase();
It converts the letters in the hex-String to upper case, so that it is consistent, i.e. you always have F and never f, no matter which is being input.
int val = 0;
This is the variable where the corresponding decimal value will later be in. We will do our calculations with this variable.
for (int i = 0; i < hex.length(); i++)
hex.length() is the number of characters in the hex-String provided. We execute the code inside this for loop once per character.
char c = hex.charAt(i);
Yes, char represents a single character. We retrieve the character from the hex-String at index i, so in the first iteration it is the first character, in the second iteration the second character and so on.
int d = digits.indexOf(c);
We look which index the character has in the digit-String. In that way we determine the decimal representation of this specific digit. Like 0-9 stay 0-9 and F becomes a 15.
val = 16*val + d;
Let's think about what we have to do. We have the decimal value of the digit. But in hexadecimal we have this digit at a specific position with which it gets multiplied. Like the '1' in '100' is actually not a 1, but 100 * 1 because it is at this position.
10 in hexadecimal is 16 in decimal, because we have 1 * 16. Now the approach here is a little bit complicated. val is not uninitialized. val is 0 at the beginning and then contains the cumulated values from the previous iterations. Since the first character in the String is the highest position we don't know directly with what we have to multiply, because we don't know how many digits the number has (actually we do, but this approach doesn't use this). So we just add the digit value to it. In the consecutive iterations it will get multiplied by 16 to scale it up to the corresponding digit base value. Let me show you an example:
Take 25F as hex number. Now the first iteration takes the 2 and converts it to a 2 and adds it to val. The 16 * val resolves to 0 so is not effective in the first time.
The next iteration multiplies the 2 with 16 and takes the 5 (converted to 5) and adds it to val. So now we have (I split it mathematically so you understand it):
2 * 16 + 5
Next we get the F which is decimal 15. We multiply val by 16 and add the 15.
We get 2 * 256 + 5 * 16 + 16 (* 1), which is actually how you calculate the decimal value of this hex value mathematically.
Another possibility to compute val is:
val += Math.pow(16, hex.length() - i - 1) * d;
I want to generate an endless series of quasi random numbers to the following specification:-
Source of numbers is uniformly distributed and random, ranging 0 through 255 inclusive. It's an existing hardware device.
Required output range is 1 through 8 inclusive.
Two consecutive output numbers are never the same. For example 5 will never follow 5, but you can have 5,2,5.
Exactly one output number is required for every single source number. Rejection sampling therefore cannot be used. And while() loops, shuffles etc. can't be used.
It's this last stipulation that's vexing me. The source generator can only supply random bytes at a constant 1 /s and I want output at a constant 1 /s. Typically you'd simply reject a generated number if it was equal to the previous one, and generate another. In my case you only get one shot at each output. I think that it's some sort of random selection process, but this requirement has me going around in circles as I'm a bad programmer. An algorithm, flowchart or picture will do, but I'll be implementing in Java.
Apologies for the semi generic title, but I couldn't really think of anything more accurate yet concise.
If I understand the problem correctly, the first random number will be chosen randomly from among 8 different numbers (1 to 8), while every successive random number will be chosen from 7 different possibilities (1 to 8 excluding the previous one). Thus, your range of 256 values will need to be divided into 7 possibilities. It won't come out even, but that's the best you can do. So you need something like
public class RandomClass {
public RandomClass(HardwareSource source) {
this.source = source;
first = true;
}
pubic int nextRandom() {
int sourceValue = source.read();
int value;
if (first) {
value = sourceValue % 8 + 1;
prev = value;
} else {
value = sourceValue % 7 + 1;
if (value >= prev) {
value++;
}
prev = value;
first = false;
return value;
}
}
Suppose the first call generates 5. The second time you call it, value is first computed to be a number from 1 to 7; by incrementing it if the value is >= 5, the range of possible outputs becomes 1, 2, 3, 4, 6, 7, 8. The output will be almost evenly distributed between those two values. Since 256 is not divisible by 7, the distribution isn't quite even, and there will be a slight bias toward the lower numbers. You could fix it so that the bias will shift on each call and even out over the entire sequence; I believe one way is
value = (sourceValue + countGenerated) % 7 + 1;
where you keep track of how many numbers you've generated.
I think this is better than solutions that take the input modulo 8 and add 1 if the number equals the previous one. Those solutions will generate prev + 1 with twice the probability of generating other numbers, so it's more skewed than necessary.
int sum=0;
int prev=-1;
int next(int input){
sum=(sum+input)%8;
if(sum==prev)sum=(sum+1)%8;
prev=sum;
return sum+1;
}
(As I interpret even with the new bold emphasis, it is not required to always generate the same output value for the same input value - that would make the task impossible to solve)
I recall reading about a method for efficiently using random bits in an article on a math-oriented website, but I can't seem to get the right keywords in Google to find it anymore, and it's not in my browser history.
The gist of the problem that was being asked was to take a sequence of random numbers in the domain [domainStart, domainEnd) and efficiently use the bits of the random number sequence to project uniformly into the range [rangeStart, rangeEnd). Both the domain and the range are integers (more correctly, longs and not Z). What's an algorithm to do this?
Implementation-wise, I have a function with this signature:
long doRead(InputStream in, long rangeStart, long rangeEnd);
in is based on a CSPRNG (fed by a hardware RNG, conditioned through SecureRandom) that I am required to use; the return value must be between rangeStart and rangeEnd, but the obvious implementation of this is wasteful:
long doRead(InputStream in, long rangeStart, long rangeEnd) {
long retVal = 0;
long range = rangeEnd - rangeStart;
// Fill until we get to range
for (int i = 0; (1 << (8 * i)) < range; i++) {
int in = 0;
do {
in = in.read();
// but be sure we don't exceed range
} while(retVal + (in << (8 * i)) >= range);
retVal += in << (8 * i);
}
return retVal + rangeStart;
}
I believe this is effectively the same idea as (rand() * (max - min)) + min, only we're discarding bits that push us over max. Rather than use a modulo operator which may incorrectly bias the results to the lower values, we discard those bits and try again. Since hitting the CSPRNG may trigger re-seeding (which can block the InputStream), I'd like to avoid wasting random bits. Henry points out that this code biases against 0 and 257; Banthar demonstrates it in an example.
First edit: Henry reminded me that summation invokes the Central Limit Theorem. I've fixed the code above to get around that problem.
Second edit: Mechanical snail suggested that I look at the source for Random.nextInt(). After reading it for a while, I realized that this problem is similar to the base conversion problem. See answer below.
Your algorithm produces biased results. Let's assume rangeStart=0 and rangeEnd=257. If first byte is greater than 0, that will be the result. If it's 0, the result will be either 0 or 256 with 50/50 probability. So 0 and 256 are twice less likely to be chosen than any other number.
I did a simple test to confirm this:
p(0)=0.001945
p(1)=0.003827
p(2)=0.003818
...
p(254)=0.003941
p(255)=0.003817
p(256)=0.001955
I think you need to do the same as java.util.Random.nextInt and discard the whole number, instead just the last byte.
After reading the source to Random.nextInt(), I realized that this problem is similar to the base conversion problem.
Rather than converting a single symbol at a time, it would be more effective to convert blocks of input symbol at a time through an accumulator "buffer" which is large enough to represent at least one symbol in the domain and in the range. The new code looks like this:
public int[] fromStream(InputStream input, int length, int rangeLow, int rangeHigh) throws IOException {
int[] outputBuffer = new int[length];
// buffer is initially 0, so there is only 1 possible state it can be in
int numStates = 1;
long buffer = 0;
int alphaLength = rangeLow - rangeHigh;
// Fill outputBuffer from 0 to length
for (int i = 0; i < length; i++) {
// Until buffer has sufficient data filled in from input to emit one symbol in the output alphabet, fill buffer.
fill:
while(numStates < alphaLength) {
// Shift buffer by 8 (*256) to mix in new data (of 8 bits)
buffer = buffer << 8 | input.read();
// Multiply by 256, as that's the number of states that we have possibly introduced
numStates = numStates << 8;
}
// spits out least significant symbol in alphaLength
outputBuffer[i] = (int) (rangeLow + (buffer % alphaLength));
// We have consumed the least significant portion of the input.
buffer = buffer / alphaLength;
// Track the number of states we've introduced into buffer
numStates = numStates / alphaLength;
}
return outputBuffer;
}
There is a fundamental difference between converting numbers between bases and this problem, however; in order to convert between bases, I think one needs to have enough information about the number to perform the calculation - successive divisions by the target base result in remainders which are used to construct the digits in the target alphabet. In this problem, I don't really need to know all that information, as long as I'm not biasing the data, which means I can do what I did in the loop labeled "fill."
What would be the most performant (also safe) way to replace the last digit(Least significant digit) of a long(that was actually generated as timestamp by System.currentTimeInMillis()) by some other digit?
Or is there a better way to attach any fixed attachment to the end of it, by making use of bitwise operations?
In your comments you say that both binary digits or decimal digits would be fine. Since Andrew posted the decimal version, i post the binary version in which you want to replace the 2 ls-bits:
The following program goes through the 4 possibilities with which you can replace the 2 ls-bits and produces the output:
9999999999999999
9999999999999996
9999999999999997
9999999999999998
9999999999999999
code:
public class A {
public static void main(String[] args) {
long aLong = 9999999999999999L;
System.out.println(aLong);
long aLong2 = aLong & ~3 + 0;
System.out.println(aLong2);
aLong2 = aLong & ~3 + 1;
System.out.println(aLong2);
aLong2 = aLong & ~3 + 2;
System.out.println(aLong2);
aLong2 = aLong & ~3 + 3;
System.out.println(aLong2);
}
}
If this is not a hypothetical question, ie to find a fast algorithm for the heck of it, then please ignore this answer. The correct way (as has been mentioned) is to do (somelong/10)*10 + newvalue
A faster (hypothetical) way is probably to have some two dimension array of adjustment values.
int[][] adjustment = new int[16][10];
where the first array index represents what the current value is anded with 0x0F (the last 4 bits)
the second array index would be what you want the new number to be
the value is the adjustment to the variable
so the code would be
newLong = somelong + adjustment[somelong&0x0F][what_you_want_the_new_digit];
so no multiplication or division
as an example, let's say the input number is 22, and you want it to be 26
26 is 011010 so 26 & 0x0F is the bottom 4 bits 1010 which is 10
adjustment[10][6] = 4 (you have precalculated that it is 4)
so you'd have 22 + 4 = 26
obviously index 10 is the same as index 0, index 11, is the same as index 1, etc, etc.