Repository: mlocati/ip-lib (php)
Issue: bug report] \IPLib\Factory::rangesFromBoundaries() getEndAddress problem
this is problem. thank you.

```php
$ranges = \IPLib\Factory::rangesFromBoundaries('192.168.0.1', '192.168.0.78');
echo sprintf("%-15s %-15s %-15s %-15s\n", 'ip', 'start', 'end', 'realend');
foreach($ranges as $range) {
	$range2 = \IPLib\Range\Subnet::fromString($range . '');
	echo sprintf("%-15s %-15s %-15s %-15s\n", $range, $range->getStartAddress(), $range->getEndAddress(), $range2->getEndAddress());
}
```

```
ip              start           end             realend        
192.168.0.1/32  192.168.0.1     192.168.0.1     192.168.0.1    
192.168.0.2/31  192.168.0.2     192.168.0.2     192.168.0.3    
192.168.0.4/30  192.168.0.4     192.168.0.4     192.168.0.7    
192.168.0.8/29  192.168.0.8     192.168.0.8     192.168.0.15   
192.168.0.16/28 192.168.0.16    192.168.0.16    192.168.0.31   
192.168.0.32/27 192.168.0.32    192.168.0.32    192.168.0.63   
192.168.0.64/29 192.168.0.64    192.168.0.64    192.168.0.71   
192.168.0.72/30 192.168.0.72    192.168.0.72    192.168.0.75   
192.168.0.76/31 192.168.0.76    192.168.0.76    192.168.0.77   
192.168.0.78/32 192.168.0.78    192.168.0.78    192.168.0.78   
```
Pull request description: Fix end address of ranges built by rangesFromBoundaries()
Fix #64.

With this fix, the result of

```php
$ranges = \IPLib\Factory::rangesFromBoundaries('192.168.0.1', '192.168.0.78');
echo sprintf("%-15s %-15s %-15s %-15s\n", 'ip', 'start', 'end', 'realend');
foreach($ranges as $range) {
    $range2 = \IPLib\Range\Subnet::fromString($range . '');
    echo sprintf("%-15s %-15s %-15s %-15s\n", $range, $range->getStartAddress(), $range->getEndAddress(), $range2->getEndAddress());
}
```

is

```
ip              start           end             realend        
192.168.0.1/32  192.168.0.1     192.168.0.1     192.168.0.1    
192.168.0.2/31  192.168.0.2     192.168.0.3     192.168.0.3    
192.168.0.4/30  192.168.0.4     192.168.0.7     192.168.0.7    
192.168.0.8/29  192.168.0.8     192.168.0.15    192.168.0.15   
192.168.0.16/28 192.168.0.16    192.168.0.31    192.168.0.31   
192.168.0.32/27 192.168.0.32    192.168.0.63    192.168.0.63   
192.168.0.64/29 192.168.0.64    192.168.0.71    192.168.0.71   
192.168.0.72/30 192.168.0.72    192.168.0.75    192.168.0.75   
192.168.0.76/31 192.168.0.76    192.168.0.77    192.168.0.77   
192.168.0.78/32 192.168.0.78    192.168.0.78    192.168.0.78   
```


Patch:
diff --git a/src/Service/RangesFromBoundaryCalculator.php b/src/Service/RangesFromBoundaryCalculator.php
index 9605763..56a61a2 100644
--- a/src/Service/RangesFromBoundaryCalculator.php
+++ b/src/Service/RangesFromBoundaryCalculator.php
@@ -154,8 +154,13 @@ class RangesFromBoundaryCalculator
      */
     private function subnetFromBits($bits, $networkPrefix)
     {
-        $address = $this->addressFromBits($bits);
+        $startAddress = $this->addressFromBits($bits);
+        $numOnes = $this->numBits - $networkPrefix;
+        if ($numOnes === 0) {
+            return new Subnet($startAddress, $startAddress, $networkPrefix);
+        }
+        $endAddress = $this->addressFromBits(substr($bits, 0, -$numOnes) . str_repeat('1', $numOnes));
 
-        return new Subnet($address, $address, $networkPrefix);
+        return new Subnet($startAddress, $endAddress, $networkPrefix);
     }
 }


Test patch:
diff --git a/test/tests/Ranges/RangesFromBoundariesTest.php b/test/tests/Ranges/RangesFromBoundariesTest.php
index 713b0e8..dab9088 100644
--- a/test/tests/Ranges/RangesFromBoundariesTest.php
+++ b/test/tests/Ranges/RangesFromBoundariesTest.php
@@ -53,6 +53,7 @@ class RangesFromBoundariesTest extends TestCase
             array('1.2.0.0', '1.2.0.3', array('1.2.0.0/30')),
             array('1.2.0.0', '1.2.1.0', array('1.2.0.0/24', '1.2.1.0/32')),
             array('128.0.0.0', '127.0.0.0', array('127.0.0.0/8', '128.0.0.0/32')),
+            array('192.168.0.1', '192.168.0.78', array('192.168.0.1/32', '192.168.0.2/31', '192.168.0.4/30', '192.168.0.8/29', '192.168.0.16/28', '192.168.0.32/27', '192.168.0.64/29', '192.168.0.72/30', '192.168.0.76/31', '192.168.0.78/32')),
             array('::1', null, array('::1/128')),
             array('::', '::1', array('::/127')),
             array('::1', '::1', array('::1/128')),
@@ -79,6 +80,12 @@ class RangesFromBoundariesTest extends TestCase
         $ranges = Factory::rangesFromBoundaries($from, $to);
         $this->assertNotNull($ranges, "Boundaries '{$from}' -> '{$to}' should be resolved to an address");
         $this->assertSameIPRanges($expected, $ranges);
+        foreach ($ranges as $range) {
+            $range2 = Factory::rangeFromString((string) $range);
+            $this->assertSame((string) $range, (string) $range2, 'Same range');
+            $this->assertSame((string) $range->getStartAddress(), (string) $range2->getStartAddress(), 'Same start address');
+            $this->assertSame((string) $range->getEndAddress(), (string) $range2->getEndAddress(), 'Same end address');
+        }
         list($from, $to) = array($to, $from);
         $ranges = Factory::rangesFromBoundaries($from, $to);
         $this->assertNotNull($ranges, "Boundaries '{$from}' -> '{$to}' should be resolved to an address");