Dividing subnets with ipv6 - php

for example, i recieved from POST ip and mask:
$ip = 2001:db8::1428:55ab;
$mask = 48;
and i want divide this subnet, i choose for example mask and how many subnets i want, for example $mask = 50, subnets = 2
and my result is (tree in a table), like this:
ip mask status
2001:db8::1428:55ab 48 divided
- 2001:db8::?????? 50 free
- 2001:db8::?????? 50 free
how can i do this? need help!

The $mask is actually the prefix-length. It is the number of bits that is 'fixed'. So a /48 prefix length means that the first 48 bits are fixed and the last (128 - 48 = ) 80 bits are available to be used.
In IPv6 a LAN always gets a /64. This means that with a /48 you get (64 - 48 = 16 bits. 216 = ) 65536 subnets.
IPv6 addresses and prefixes are written in hexadecimal. Each hexadecimal character is 4 bits. In IPv6 each group of numbers between : is 16 bits. Leading zeroes in each group can be omitted. So 2001:0db8:0000:0000:0000:0000:0000:0001 is the same as 2001:db8:0:0:0:0:0:1. The combination :: means that in that spot are only zeroes. So that address can also be written as2001:db8::1.
The information you give is a little confusing. You specify 2001:db8::1428:55ab/48. The problem is that you specify the last 32 bits of the IPv6 address (1428:55ab), while the mast specifies that those bits are not fixed. I will therefore use 2001:db8:1a2b::/48 as example.
Each increase in prefix length splits the previous prefix in two.
2001:db8:1a2b::/48 can be split into:
2001:db8:1a2b::/49
2001:db8:1a2b:8000::/49
2001:db8:1a2b::/49 can be split into:
2001:db8:1a2b::/50
2001:db8:1a2b:4000::/50
2001:db8:1a2b:8000::/49 can be split into:
2001:db8:1a2b:8000::/50
2001:db8:1a2b:c000::/50
Etc.
You could write it as a tree (just showing a few branches, I don't want to fill the page with the full 65536 subnets):
2001:0db8:1a2b:0000::/48
2001:0db8:1a2b:0000::/49
2001:0db8:1a2b:8000::/49
2001:0db8:1a2b:8000::/50
2001:0db8:1a2b:8000::/51
2001:0db8:1a2b:a000::/51
2001:0db8:1a2b:a000::/52
2001:0db8:1a2b:a000::/53
2001:0db8:1a2b:a800::/53
2001:0db8:1a2b:b000::/52
2001:0db8:1a2b:b000::/53
2001:0db8:1a2b:b800::/53
2001:0db8:1a2b:c000:/50
etc.
As you can see it's not that easy to read. It's easier if you subnet on multiples of 4 because then it matches the hexadecimal characters:
2001:0db8:1a2b:0000::/48
2001:0db8:1a2b:0000::/52
2001:0db8:1a2b:1000::/52
2001:0db8:1a2b:2000::/52
2001:0db8:1a2b:2000::/56
2001:0db8:1a2b:2000::/60
2001:0db8:1a2b:2000::/64
2001:0db8:1a2b:2001::/64
...
2001:0db8:1a2b:200f::/64
2001:0db8:1a2b:2010::/60
2001:0db8:1a2b:2010::/64
2001:0db8:1a2b:2011::/64
...
2001:0db8:1a2b:201f::/64
...
2001:0db8:1a2b:20f0::/60
2001:0db8:1a2b:3000::/52
etc.

You can use cidrl6 to split a /48 into four /50 subnets:
$ cidrl6 -s50 2001:db8::1428:55ab/48
2001:db8::/50
2001:db8:0:4000::/50
2001:db8:0:8000::/50
2001:db8:0:c000::/50
You could then read the output of this command into PHP as an array with something like exec().
<?php
$ip = "2001:db8::1428:55ab";
$mask = 48;
$subnet = 50;
$subnets = [];
exec("cidrl6 -s$subnet $ip/$mask", $subnets);
print_r($subnets);
Output:
Array
(
[0] => 2001:db8::/50
[1] => 2001:db8:0:4000::/50
[2] => 2001:db8:0:8000::/50
[3] => 2001:db8:0:c000::/50
)

Related

Looking for a PHP smart format number

I am looking for a solution for a smart number formatting in PHP.
For example we have 2 numbers below and we need 4 digits after decimal:
1.12345678
0.00002345678
Using normal number formatting, here are the results:
1.1234 // Looking good
0.0000 // No good
Can we make it keep going until there are 4 non-zero digits? If it can return 0.00002345, perfect!!!
Many thanks!!!
Might be overkill and the pattern could be optimized, but for fun; get optional 0s AND 4 NOT 0s after the .:
preg_match('/\d+\.([0]+)?[^0]{4}/', $num, $match);
echo $match[0];
To round it we can get 5 digits after the 0s and then format it to the length of that -1 (which will round):
preg_match('/\d+\.([0]+?[^0]{5})/', $num, $match);
echo number_format($match[0], strlen($match[1])-1);
For $num = '1234.000023456777'; the result will be 1,234.00002346 and the $matches will contain:
Array
(
[0] => 1234.000023456
[1] => 000023456
)
So this is the code I made to slove this:
$num = 0.00002345678;
$num_as_string = number_format($num,PHP_FLOAT_DIG,'.','');
$zeros = strspn($num_as_string, "0", strpos($num_as_string, ".")+1);
echo number_format($num, (4+$zeros), '.', '');
It converts the float number to a string, checks how many zeros exist after the decimal point and then does a number format with the extra zeros accounted for.
Note that it may break if your float is too big, you can change PHP_FLOAT_DIG to a number larger that may fix that.

PHP slice off numbers after 4 non zero numbers in decimal

What I am trying to do is :
0.000000023455676554434 -> 0.0000002345
0.00000000000000000000002656565-> 0.00000000000000000000002656
0.012345 -> 0.01234
Code till now :
bcdiv(rtrim(sprintf('%.20f', $decimal), '0'),1,13);
The current code removes scientific notation, and trims any zeros towards the end if any, and cuts the decimals after 13 decimal points. but if the decimal is something like 0.023123123235435346 it would still show 13 decimal points while I am looking to get only 0.02312.
Any help wold be highly appreciated.
You can use base 10 log to get the number of zeros between the point and the first digit. This would do what you want:
$decimal = "0.00000000000000000000002656565";
echo bcdiv($decimal, 1, -1*floor(log($decimal, 10))-1+4);
Output: 0.00000000000000000000002656
I think preg_replace is also very easy to do. The value should be available as strings and not as float.
$trimValue = preg_replace('~(\d*\.0*[1-9]{4}).*~','$1',$value);
If you want 4 numbers after first non-zero number use this:
$trimValue = preg_replace('~(\d*\.0*[1-9]\d{3}).*~','$1',$value);
A complete test (First variant):
$data = [
'0.000000023455676554434', // -> 0.0000002345
'0.00000000000000000000002656565', //-> 0.00000000000000000000002656
'0.012345', // -> 0.01234
'0.023123123235435346', //0.02312
'0.00565',
];
$trimValues = [];
foreach($data as $value){
$trimValues[] = preg_replace('~(\d*\.0*[1-9]{4}).*~','$1',$value);
}
echo '<pre>';
var_export($trimValues);
Output:
array (
0 => '0.00000002345',
1 => '0.00000000000000000000002656',
2 => '0.01234',
3 => '0.02312',
4 => '0.00565',
)

php binary to decimal

I am connecting to an api that returns binary string data that I need to convert to decimal format with php. The data comes across in four-part chunks like "66 50 235 133" and should translate to some value around 50.00 (not sure exactly what the decimal value should be, but should be close to 50).
So, I need to convert the binary string "66 50 235 133" to a decimal value around 50.
I have read the pack and unpack function pages on php.net, but am unsure if this is the correct function to use and which formats to use (signed/unsigned,endian,etc). Thanks for any help you can offer or any help point me in the right direction!
Might be:
$input = '66 50 235 133';
$value = unpack('g', implode('', array_map('chr', explode(' ', $input))));
// outputs -2.2117755600046E-35
Or:
$input = '66 50 235 133';
$value = unpack('G', implode('', array_map('chr', explode(' ', $input))));
// outputs 44.729999542236
Depending on the Endianness.
I suspect it's the later, if you think this number is close to 50.00.

How to generate decimal in range for 24 bit?

I have 24 bit of binary, which is
111010001100001010011000
equals to 15254168
I only "guess" its 24 bit, because binary length is 24.
I would like to generate all 24 bit decimals programmaticly. (C, PHP or Python)
2**24 returns 16.777.216
So there are 16.777.216 other decimals (combinations). How can i generate them?
I can't understand the "range" of 24 bit.
May someone help me on this?
Thanks.
<!-- language: python -->
Is this what you want?
>>> n = 3
>>> result = [bin(k)[2:].rjust(n, '0') for k in xrange(2**n)]
>>> print result
['000', '001', '010', '011', '100', '101', '110', '111']
>>> n = 24
24bits just mean there are 24 bits(zeros or ones) that together create a binary number.
If you want all combinations, or all numbers that can be expressed with 24 bits, it is just range from 0 to 16777215. Why? here is the table in format (binary = decimal):
000000000000000000000000 = 0
000000000000000000000001 = 1
000000000000000000000010 = 2
000000000000000000000011 = 3
....
....
111111111111111111111110 = 16777214
111111111111111111111111 = 16777215
you dont really need to generate anything. You can check the binary to decimal here: http://www.binaryhexconverter.com/binary-to-decimal-converter
Another thing: Sometimes in binary, the leading zeros are omitted. So decimal three is not 000000000000000000000011 but rather just 11. If the length is 24 and first digit is 1, its still just range 8388608 - 16777215

Finding out what numbers have been added to come up with a sum

Sorry for the title. I wasn't sure how to ask this question.
I have a form on a website that asks a question. The answers are in check box form. Each answer is saved into my database with a 'score', the values look like this:
Allergy 1
Cardiology 2
Chest Disease 4
Dermatology 8
Emergency Room 16
Ambulance Trips 32
Gastroenterology 64
General Medicine 128
Gynecology 256
Hematology 512
Neurology 1024
Obstetrics 2048
Opthamology 4096
Orthopedics 8192
Physical Therapy 16384
Plastic Surgery 32768
Podiatry 65536
Proctology 131072
Psychiatry 262144
Surgery Performed 524288
Thoracic Surgery 1048576
Urology 2097152
Outside X-Rays 4194304
Diagnostic Tests (outside) 8388608
As you can see, the score is the previous value times two. When a user fills out the form, the answer is saved in the database as one value - all the answers added together.
For example, a user selected the values: Allergy, General Medicine, Hematology, Obstetrics. In the database, the answer for this question is saved as 2689.
Is there a way to figure out what answers have been selected by only having the answer to the question?
For example, I would query my database and pull the 2689 value, and I need to determine what answers were checked.
edit: I was hoping to reverse engineer the answer in PHP.
Yes, this is a common pattern called bit masking. Use your language's binary AND operator on the value corresponding to a given answer and the value submitted from the form to see if the given answer was one of the selected choices. For example, if the answer submitted and saved is 2689 as in your example, you can check whether "chest disease" was one of the selected choices by seeing if 2689 & 4 is nonzero. (& should be substituted with whatever the binary AND operator is in your language of choice.)
Note that this only works as long as all the values corresponding to individual choices are powers of 2. In general, the question posed in your title, about finding out what numbers from a given set have been added to come up with a given sum, is an instance of something called the knapsack problem and is only known to be solvable by checking every possible combination, which is very inefficient. (NP-complete, specifically)
You can find the values by ANDing with powers of 2.
20 = 1
21 = 2
22 = 4
23 = 8
...
223 = 8388608
You can find out the value of 2n using binary shifting like this: 1 << n
php like code:
$item[] = {"Allergy", "Cardiology", ..., "Diagnostic Tests (outside)"};
$answer = 2689;
for ( $power = 0; $power < count($item); $power++ ) {
if ( 1 << $power & $answer ) {
echo $item[$power] . "\n";
}
}
Edit: made it more php friendly
Yes, there is. Note that each k'th "score" is of the form 2^(k - 1), which corresponds to a bitstring with only the k'th bit set. If you know which bits are set, you can reconstruct the sum.
Taking 2689 as an example, we first need to write it out in binary:
2689 = 101010000001b
Counting from the right, we see that the first, eighth, tenth and twelfth bits are set, so (as you can verify)
2689 = 2^0 + 2^7 + 2^9 + 2^11
= 1 + 128 + 512 + 2048
The actual implementation of this can be done efficiently using bitwise operations. By taking the AND of the value and each of the "scores" in turn, then checking whether that gives a non-zero value, we can check which scores went into the sum.
this will do exactly what you wanted :
<?php
Print bindecValues("2689");
function bindecValues($decimal, $reverse=false, $inverse=false) {
$bin = decbin($decimal);
if ($inverse) {
$bin = str_replace("0", "x", $bin);
$bin = str_replace("1", "0", $bin);
$bin = str_replace("x", "1", $bin);
}
$total = strlen($bin);
$stock = array();
for ($i = 0; $i < $total; $i++) {
if ($bin{$i} != 0) {
$bin_2 = str_pad($bin{$i}, $total - $i, 0);
array_push($stock, bindec($bin_2));
}
}
$reverse ? rsort($stock):sort($stock);
return implode(", ", $stock);
}
?>
Happy coding
Remember that integers are stored in binary - so each of these flags (Allergy = 1) etc. will correspond to a single bit being true or false in the binary representation of the sum.
For example, 2689 in binary is 0000 1010 1000 0001 which, if you think of it as an array of bits, where the least significant bit (right most in that array) is the least significant flag (allergy) then we can easily see that the first (allergy), eighth (gen. medicine), tenth (hematology) and twelfth (obs) slots of the array are marked with a 1 for true.
The largest value in your array of flags is 24th bit in a 32 bit integer. You could define up to 8 more flags in this system before having to use a larger integer.
Since all your numbers seem to be powers of two, you just need to store the input value in a long enough integer to hold it, then bit mask.
if( value & 1 ) then 1 was part of the selection
if( value & 2 ) then 2 was part of the selection
if( value & 3 ) then 3 was part of the selection
and so on

Categories