summing check boxes and checking if a bit is set in php - php

I need to use two functions, one sums the values of checkboxes and I put that value into a cookie for one of many checkboxes used for a search page on a website.
The second function checks if the bit is set and I have no idea what this function is supposed to be doing. I know what it should do but I don't see it actually doing anything in the function.
All of the values are powers of 2 and it is supposed to see if the summed number contains that power of two. Below is the function:
function isBitSet($power, $decimal)
{
if((pow(2,$power)) & ($decimal))
{
return 1;
}
else
return 0;
}

& is the bitwise AND operator (not to be confused with &&, which is a comparison operator)! & compares the thing on its left and the thing on its right, bit by bit, and only outputs a 1 for each bit where BOTH inputs bits had a 1. I'll show an example:
In the following example, the top number is pow(2, $power), where $power = 3. It's written in binary form to make it clearer: doing 2 to the power of 3 has shifted a binary 1 right 3 places.
00001000 <- this is pow(2, 3)
& 01101010 <- this is your $decimal (in binary form)
========
= 00001000
You see? All the output bits are 0, except where BOTH input bits were 1.
What we have done is we have used pow(2, 3) as a MASK: it has cleared EVERY bit to 0, except the one we are interested in (bit 3). That bit will be 1 if $decimal contains this particular power of 2, otherwise it will be 0:
$decimal contains that power of 2 -> result will be 00001000
$decimal does not contain that ^2 -> result will be 00000000
Since any non-zero integer evaluates to true in PHP, the if will occur if that bit is set (i.e. $decimal contains that power of 2), and the else will occur if that bit is not set (because the & will result in an output of 00000000).
Please comment if you need any of that clarified. Hope it helps.

Related

Where did this number come from?

I was reviewing some PHP code and stumbled across this:
if(($this->int & 0b111111111) === 0){ //blah
I was very puzzled at where 0b111111111 came from. What is this and how was it generated? I var_dumped it and it turned out to be 511, but why was this used instead of the number 511? Where did it come from?
It is an integer using binary notation, and equals to 511 in decimal notation.
The coder is using it as mask for a binary AND (the &).
The test equals 0 only if $this->int == 0, as the binary AND operator compares each bit of the binary value, and returns 1 at the corresponding position where both the left and right numbers have a 1 at that position.
I have no special idea of the use here though: depending on the context, it could be for consistency. Or it's just "clever code" for writing
if($this->int === 0){ // Dumber blah

PHP two different result localhost and live host from a function

I have no idea why i get two different results from the same function, When i use it in local host i get a result and when i upload file to my host i get different result.
I test it, i give it the same word and i get two different results.
it's a function from CAPTCHA library: Captcha library
function rpHash($value) {
$hash = 5381;
$value = strtoupper($value);
for($i = 0; $i < strlen($value); $i++) {
$hash = (($hash << 5) + $hash) + ord(substr($value, $i));
}
return $hash;
}
The problem is that your localhost is 32bit and your host is 64bit. To understand why, read the accepted answer on this post. To summarize:
The << operator does a Left shift
Integers are stored, in memory, as a series of bits. For example, the number 6 stored as a 32-bit int would be represented in base 2 as:
00000000 00000000 00000000 00000110
Shifting this bit pattern to the left one position (6 << 1) would result in the number 12 and represented in base 2 as:
00000000 00000000 00000000 00001100
As you can see, the digits have shifted to the left by one position, and the last digit on the right is filled with a zero.
When you shift left, you are increasing the number by an order of ^2 for each shift.
On a 32 bit system, the maximum integer you can have is 2,147,483,647 (which is 01111111 11111111 11111111 11111111 (there are 31 bits available). That is how much room you have to shift left before you run out of room to store the number.
Your starting hash of 5381 is 13 bits long
Your function shifts FIVE bits for each letter in the $value.
You get to 31 bits after 3 letters (3*5) + 13 = 28
So you can only have 3 letters max in your CAPTCHA before it breaks.
Your OPTIONS:
Restrict your CAPTCHA to 3 characters or less and use the
existing function.
Shift using $hash << 3. This will get you 5
characters
Shift using $hash << 2. This will get you 7
characters
Use a different method
For example:
function rpHash($value) {
$hash = 5381;
$value = strtoupper($value);
for($i = 0; $i < strlen($value); $i++) {
$hash = (($hash << 2) + $hash) + ord(substr($value, $i));
}
return $hash;
}
It may have to do with whether or not the operating system is 32-bit or 64-bit. If you shift $hash too far left, you could end up with an overflow error, and wrap around to negative numbers.
The solution depends on your needs. If all you need is a positive integer, and it doesn't matter if the values are consistent across operating systems, just wrap the returned value with abs() to make sure the result is positive.
If you need the result to always be consistent, you'll probably need to truncate the result to be stored in 32 bits at most. Using something like:
$32bitresult = rphash($value) & 0xFFFFFFFF;
should work.
NOTE: You may want to add a check to see if the result of rphash is negative before AND-ing the two.
Take a look at this similar problem/answer:
Force PHP integer overflow
I haven't been able to test the code out, but maybe it'll help you on your way at least.

Two-way hashing of fixed range numbers

I need to create a function which takes a single integer as argument in the range 0-N and returns a seemingly random number in the same range.
Each input number should always have exactly one output and it should always be the same.
Such a function would produce something like this:
f(1) = 4
f(2) = 1
f(3) = 5
f(4) = 2
f(5) = 3
I believe this could be accomplished by some kind of a hashing algorithm? I don't need anything complex, just not something too simple like f(1) = 2, f(2) = 3 etc.
The biggest issue is that I need this to be reversible. E.g. the above table should be true left-to-right as well as right-to-left, using a different function for the right-to-left conversion is fine.
I know the easiest way is to create an array, shuffle it and just store the relations in a db or something, but as I need N to be quite large I'd like to avoid this if possible.
Edit: For my particular case N is a specific number, it's exactly 16777216 (64^4).
If the range is always a power of two -- like [0,16777216) -- then you can use exclusive-or just as #MarkBaker suggested. It just doesn't work so easily if your range is not a power of two.
You can use addition and subtraction modulo N, although these alone are too obvious, so you have to combine it with something else.
You can also do multiplication modulo-N, but reversing that is complicated. To make it simpler, we can isolate the bottom eight bits and multiply those and add them in a way that doesn't interfere with those bits so we can use them again to reverse the operation.
I don't know PHP so I'm going to give an example in C, instead. Maybe it's the same.
int enc(int x) {
x = x + 4799 * 256 * (x % 256);
x = x + 8896843;
x = x ^ 4777277;
return (x + 1073741824) % 16777216;
}
And to decode, play the operations back in reverse order:
int dec(int x) {
x = x + 1073741824;
x = x ^ 4777277;
x = x - 8896843;
x = x - 4799 * 256 * (x % 256);
return x % 16777216;
}
That 1073741824 must be a multiple of N, and 256 must be a factor of N, and if N is not a power of two then you can't (necessarily) use exclusive-or (^ is exclusive-or in C and I assume in PHP too). The other numbers you can fiddle with, and add and remove stages, at your leisure.
The addition of 1073741824 in both functions is to ensure that x stays positive; this is so that the modulo operation doesn't ever give a negative result, even after we've subtracted values from x which might have made it go negative in the interim.
I offered to describe how I "randomly" scramble up 9-digit SSNs when producing research data sets. This does not replace or hash an SSN. It re-orders the digits. It is difficult to put the digits back in the correct order if you don't know the order in which they were scrambled. I have a gut feeling that this is not what the questioner really wants. So, I am happy to delete this answer if it is deemed off-topic.
I know that I have 9 digits. So, I start with an array that has 9 index values in order:
$a = array(0,1,2,3,4,5,6,7,8);
Now, I need to turn a key that I can remember into a way to shuffle the array. The shuffling has to be the same order for the same key every time. I use a couple tricks. I use crc32 to turn a word into a number. I use srand/rand to get a predictable order of random values. Note: mt_rand no longer produces the same sequence of random digits with the same seed, so I have to use rand.
srand(crc32("My secret key"));
usort($a, function($a, $b) { return rand(-1,1); });
The array $a still has the digits 0 through 8, but they are shuffled. If I use the same keyword I will get the same shuffled order every time. That lets me repeat this every month and get the same result. Then, with a shuffled array, I can pick the digits off the SSN. First, I ensure it has 9 characters (some SSNs are sent as integers and a leading 0 is omitted). Then, I build a masked SSN by picking the digits using $a.
$ssn = str_pad($ssn, 9, '0', STR_PAD_LEFT);
$masked_ssn = '';
foreach($a as $i) $masked_ssn.= $ssn{$i};
$masked_ssn will now have all the digits in $ssn, but in a different order. Technically, there are keywords that make $a become the original ordered array after shuffling, but that is very very rare.
Hopefully this makes sense. If so, you can do it all much faster. If you turn the original string into an array of characters, you can shuffle the array of characters. You just need to reseed rand every time.
$ssn = "111223333"; // Assume I'm using a proper 9-digit SSN
$a = str_split($ssn);
srand(crc32("My secret key"));
usort($a, function($a, $b) { return rand(-1,1); });
$masked_ssn = implode('', $a);
This is not really faster in a runtime way because rand is a rather expensive function and you run rand a hell of lot more here. If you are masking thousands of values as I do, you will want to use an index array that is shuffled just once, not a shuffling for every value.
Now, how do I undo it? Assume I'm using the first method with the index array. It will be something like $a = {5, 3, 6, 1, 0, 2, 7, 8, 4}. Those are the indexes for the original SSN in the masked order. So, I can easily build the original SSN.
$ssn = '000000000'; // I like to define all 9 characters before I start
foreach($a as $i=>$j) $ssn[$j] = $masked_ssn{$i};
As you can see, $i counts from 0 to 8 across the masked SSN. $j counts 5, 3, 6... and puts each value from the masked SSN in the correct place in the original SSN.
Looks like you've got good answer, but still there is an alternative. Linear Congruential Generator (LCG) could provide 1-to-1 mapping and it is known to be a reversible using Euclid's algorithm. For 24bit
Xi = [(A * Xi-1) + C] Mod M
where M = 2^24 = 16,777,216
A = 16,598,013
C = 12,820,163
For LCG reversability take a look at Reversible pseudo-random sequence generator

Reading top nibble and bottom nibble in a byte

What's the correct way to handle two distinct values being stored in one byte of data. I have a byte that contains two nibbles each containing their own data. I want to read the top nibble and the bottom nibble into their own variables.
11110000 = High 4 bits throttle, to be read into $throttle, and should be a value from 0 to 15.
00001111 = Low 4 bits brake, to be read into $brake, and should be a value from 0 to 15.
Don't forget, drivers can apply the throttle and the brake at the same time, so you might get a value like 11000111. I've myself come up with a solution for the high 4 bits, and it's as simple as pushing the lower 4 bits out of the way with the >> (bit shift right) operator 4 times. $Throttle = $ThrBrk >> 4, but as I can't do that in one move for the lower four bits it looks kinda bad in my source code.
Use ANDoperators for both and shift the top nibble four bits to the right.
$brake = $value & 0x0F;
$throttle = ($value & 0xF0) >> 4;
Check out the & operator, which is a bitwise AND. To get the first (least significant bit), do this:
$lsb = $bits & 1;
So, to get the whole "nibble":
$break = $bits & 15;

Understanding PHP & (ampersand, bitwise and) operator

I often use ($var & 1) in my code, which returns true if $var is an odd number and false if it's an even number.
But what does "&" actually do?
& is binary and. If you have a binary value, and you and with another binary value, then the result will be the bitwise and of the two. An example:
01101010
& 01011001
= 01001000
The rightmost bit is either a 1 (and in that case the number is an odd number) or it is a 0, in which case the number is even. If you & a number with 1, you only look at the least significant bit, and the if checks if the number is a 1 or a 0. As others have mentioned, look at the bitwise operators for info on how they work.
Two operations which are fundamental to binary systems are OR and AND.
OR means 'if either A is on or B is on'. A real world example would be two switches in parallel. If either is allowing current through, then current passes through.
AND means 'if both A and B is on'. The real world example is two switches in series. Current will only pass through if both are allowing current through.
In a computer, these aren't physical switches but semiconductors, and their functionality are called logic gates. They do the same sorts of things as the switches - react to current or no current.
When applied to integers, every bit in one number is combined with every bit in the other number. So to understand the bitwise operators OR and AND, you need to convert the numbers to binary, then do the OR or AND operation on every pair of matching bits.
That is why:
00011011 (odd number)
AND
00000001 (& 1)
==
00000001 (results in 1)
Whereas
00011010 (even number)
AND
00000001 (& 1)
==
00000000 (results in 0)
The (& 1) operation therefore compares the right-most bit to 1 using AND logic. All the other bits are effectively ignored because anything AND nothing is nothing. An even number in binary is also an even number in decimal notation (10 is a multiple of 2).
Other fundamental operations to binary systems include NOT and XOR. NOT means 'if A is off' and is the only form of logic gate that takes only one signal or 'parameter' instead of two. XOR means 'if either A or B is on, but not both'. And then there are NAND, NOR, and NXOR, which are basically just NOT combined with AND, OR, and XOR, ie NAND means 'if A and B are not both on'.
In programming, the operator
& means AND,
| means OR,
~ means NOT, and
^ means XOR.
The others can be made up by combining these, for example:
~ (a & b) is equivalent to a NAND operation
PHP specific note
Bitwise operators do not work on floating-point values, and in PHP float values will be implicitly converted to integers first. Numbers outside the range that can be expressed as integers will be truncated to zero - that is, all numbers over PHP_INT_MAX will look "even" in the expression ($num & 1)). If you want to support numbers outside of PHP_INT_MIN/PHP_INT_MAX, you'll need fmod($num, 2). If, however, you're on 64-bit PHP your integers will have greater precision than floats anyway.
This is also interesting to know about bitwise and PHP:
/**
* Regular
*/
echo (true && true); // 1
echo (true && false); // nothing
echo (true || false); // 1
echo (false || false); // nothing
echo (true xor false); // 1
echo (false xor false); // nothing
/**
* Bitwise
*/
echo (true & true); // 1
echo (true & false); // 0
echo (true | false); // 1
echo (false | false); // 0
echo (true ^ false); // 1
echo (false ^ false); // 0
I know your question is about understanding the bitwise operator and the accepted answer explains it well. But for the example you give, I cannot help but recommending you use the modulo operator instead:
($var % 2) /* instead of */ ($var & 1)
Because it makes the intent clear that you are checking that the number is odd (not divisible by two), and it is more generic, so you can use ($var % 3) the same way and deduce how it works for any N.
In addition to the other answers, it's worth noting that
if(func1() && func2())
Will only call func2() if func1() returns true ("lazy evaluation"), whereas
if(func1() & func2())
Will call both functions regardless, but the truth tables for both will be the same (assuming they return booleans).
thomasrutter points out (in the comments below) that you probably shouldn't do the latter in practice. (A & B) won't necessarily have the same truthiness as (A && B), particularly when A and B are integers. e.g., if A=1 and B=2 (both truthy), A & B will be falsey, whereas A && B is truthy. Also, another developer may think this is a typo and 'correct' it to two ampersands.

Categories