user
Task

Implement a function or program that, given a range of IPv4 addresses in CIDR notation (dotted-decimal/network-bits), will return/output the same range in canonical form.

That is, the IP address portion of the output CIDR block must not contain any set (1) bits in the host part of the address.

Example

Given   **87.70.141.1/22**,   your code should output   **87.70.140.0/22**

Explanation

An Internet Protocol version 4 address is a 32-bit value, conventionally represented as a number in base 256 using dotted-decimal notation, where each base-256 digit is given in decimal and the digits are separated by periods. Logically, this 32-bit value represents two components: the leftmost (most-significant) bits determine the network portion of the address, while the rightmost (least-significant) bits determine the host portion. Classless Internet Domain Routing block notation indicates where the boundary between these two components is for a given address by adding a slash followed by the number of bits in the network portion.

In general, CIDR blocks stand in for the entire set of IP addresses sharing the same network component, so it's common to see access control lists that specify individual IP addresses using /32 to indicate that only the one address is included. Software accepting this notation as input often expects it to be entered in canonical form, in which the host bits are all zeroes. But network admins sometimes skip this step and just enter the address of a specific host on the subnet with the network size, resulting in a non-canonical entry.

The example address, 87.70.141.1/22, represents binary 0101011101000110100011 / 0100000001, with the / indicating the network/host division. To canonicalize, clear all the bits to the right of the / and convert back to dotted decimal: 0101011101000110100011 / 0000000000 → 87.70.140.0.

More examples for testing

```
        36.18.154.103/12    →  36.16.0.0/12
        62.62.197.11/29     →  62.62.197.8/29
        67.137.119.181/4    →  64.0.0.0/4
        161.214.74.21/24    →  161.214.74.0/24
        184.232.176.184/18  →  184.232.128.0/18

```
assistant
### Using library

**Library:** raku-IP-Addr

```
use IP::Addr;
for «87.70.141.1/22 36.18.154.103/12 62.62.197.11/29 67.137.119.181/4 161.214.74.21/24 184.232.176.184/18» -> $cidr {
   say "$cidr -> $(IP::Addr.new($cidr).network)";
}

```

### String manipulation

**Translation of**: Perl

```
#!/usr/bin/env raku
unit sub MAIN(*@cidrs);

if !@cidrs {
  # test data
  @cidrs = «87.70.141.1/22 36.18.154.103/12 62.62.197.11/29 67.137.119.181/4 161.214.74.21/24 184.232.176.184/18»;
}

for @cidrs -> $cidr {
  say "$cidr -> $(canonicalize $cidr)";
}

# canonicalize a CIDR block: make sure none of the host bits are set
sub canonicalize($cidr) {
  # dotted-decimal / bits in network part
  my ($dotted, $size) = $cidr.split: '/';

  # get network part of the IP as binary string
  my $binary = $dotted.split('.')».fmt('%08b').join.substr(0, $size);

  # Add host part with all zeroes
  $binary ~= 0 x (32 - $size);

  # Convert back to dotted-decimal
  my $canon = $binary.comb(8)».parse-base(2).join: '.';

  # And output
  say "$canon/$size";
}

```

Output:

```
87.70.141.1/22 -> 87.70.140.0/22
36.18.154.103/12 -> 36.16.0.0/12
62.62.197.11/29 -> 62.62.197.8/29
67.137.119.181/4 -> 64.0.0.0/4
161.214.74.21/24 -> 161.214.74.0/24
184.232.176.184/18 -> 184.232.128.0/18
```

### Bit mask and shift

```
# canonicalize a IP4 CIDR block
sub CIDR-IP4-canonicalize ($address) {
  constant @mask = 24, 16, 8, 0;
 
  # dotted-decimal / subnet size
  my ($dotted, $size) = |$address.split('/'), 32;
 
  # get IP as binary address
  my $binary = sum $dotted.comb(/\d+/) Z+< @mask;
 
  # mask off subnet
  $binary +&= (2 ** $size - 1) +< (32 - $size);
 
  # Return dotted-decimal notation
  (@mask.map($binary +> * +& 0xFF).join('.'), $size)
}
 
my @tests = <
  87.70.141.1/22
  36.18.154.103/12
  62.62.197.11/29
  67.137.119.181/4
  161.214.74.21/24
  184.232.176.184/18
  100.68.0.18/18
  10.4.30.77/30
  10.207.219.251/32
  10.207.219.251
  110.200.21/4
  10.11.12.13/8
  10.../8
>;
 
printf "CIDR: %18s  Routing prefix: %s/%s\n", $_, |.&CIDR-IP4-canonicalize
  for @*ARGS || @tests;

```

Output:

```
CIDR:     87.70.141.1/22  Routing prefix: 87.70.140.0/22
CIDR:   36.18.154.103/12  Routing prefix: 36.16.0.0/12
CIDR:    62.62.197.11/29  Routing prefix: 62.62.197.8/29
CIDR:   67.137.119.181/4  Routing prefix: 64.0.0.0/4
CIDR:   161.214.74.21/24  Routing prefix: 161.214.74.0/24
CIDR: 184.232.176.184/18  Routing prefix: 184.232.128.0/18
CIDR:     100.68.0.18/18  Routing prefix: 100.68.0.0/18
CIDR:      10.4.30.77/30  Routing prefix: 10.4.30.76/30
CIDR:  10.207.219.251/32  Routing prefix: 10.207.219.251/32
CIDR:     10.207.219.251  Routing prefix: 10.207.219.251/32
CIDR:       110.200.21/4  Routing prefix: 96.0.0.0/4
CIDR:      10.11.12.13/8  Routing prefix: 10.0.0.0/8
CIDR:            10.../8  Routing prefix: 10.0.0.0/8
```