1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use std::cmp::Ordering;

use num_traits::{One, ToPrimitive};

use crate::{cidr::Ipv6Cidr, num_bigint::BigUint, utils::Ipv6CidrCombiner};

/// To divide an IPv6 CIDR into subnetworks.
#[derive(Debug)]
pub struct Ipv6CidrSeparator;

impl Ipv6CidrSeparator {
    /// Evenly divide an IPv6 CIDR into a specific number of subnetworks.
    pub fn divide_by(cidr: &Ipv6Cidr, n: usize) -> Option<Vec<Ipv6CidrCombiner>> {
        let size = cidr.size();

        let n_big_int = BigUint::from(n);

        if n == 0 || n_big_int > size {
            return None;
        } else if n == 1 {
            let mut combiner = Ipv6CidrCombiner::with_capacity(1);

            combiner.push(*cidr);

            return Some(vec![combiner]);
        }

        let log2_n = (n as f64).log2();

        let mut output = Vec::with_capacity(n);

        if (log2_n - log2_n.floor()).abs() < 2.0 * std::f64::EPSILON {
            let mut iter = cidr.iter();

            let bits = cidr.get_bits() + log2_n as u8;

            let usize_max_big_int = BigUint::from(usize::MAX);

            let d = size / n_big_int;

            if d <= usize_max_big_int {
                for ip in iter.step_by(d.to_usize().unwrap()) {
                    let mut combiner = Ipv6CidrCombiner::with_capacity(1);

                    combiner.push(Ipv6Cidr::from_prefix_and_bits(ip, bits).unwrap());

                    output.push(combiner);
                }
            } else {
                let nth = d - BigUint::one();

                if let Some(ip) = iter.next() {
                    let mut combiner = Ipv6CidrCombiner::with_capacity(1);

                    combiner.push(Ipv6Cidr::from_prefix_and_bits(ip, bits).unwrap());

                    output.push(combiner);

                    while let Some(ip) = iter.nth_big_uint(nth.clone()) {
                        let mut combiner = Ipv6CidrCombiner::with_capacity(1);

                        combiner.push(Ipv6Cidr::from_prefix_and_bits(ip, bits).unwrap());

                        output.push(combiner);
                    }
                }
            }
        } else {
            let d = size / n_big_int;

            let iter = cidr.iter();

            let mut current_combiner = Ipv6CidrCombiner::new();

            let mut i = BigUint::one();

            for ip in iter {
                current_combiner.push(Ipv6Cidr::from_prefix_and_bits(ip, 128).unwrap());

                if i == d {
                    output.push(current_combiner);

                    current_combiner = Ipv6CidrCombiner::new();

                    i = BigUint::one();
                } else {
                    i += BigUint::one();
                }
            }

            let last_combiner = output.last_mut().unwrap();

            for cidr in current_combiner.into_ipv6_cidr_vec().into_iter() {
                last_combiner.push(cidr);
            }
        }

        Some(output)
    }

    /// Divide an IPv6 CIDR into subnetworks with a specific bits.
    pub fn sub_networks(cidr: &Ipv6Cidr, bits: u8) -> Option<Vec<Ipv6Cidr>> {
        let cidr_bits = cidr.get_bits();

        match cidr_bits.cmp(&bits) {
            Ordering::Greater => return None,
            Ordering::Equal => return Some(vec![*cidr]),
            Ordering::Less => (),
        }

        let n = 2usize.pow(u32::from(bits - cidr_bits));

        let n_big_int = BigUint::from(n);

        let mut output = Vec::with_capacity(n);

        let size = cidr.size();

        let d = size / n_big_int;

        let mut iter = cidr.iter();

        let usize_max_big_int = BigUint::from(usize::MAX);

        if d <= usize_max_big_int {
            for ip in iter.step_by(d.to_usize().unwrap()) {
                output.push(Ipv6Cidr::from_prefix_and_bits(ip, bits).unwrap());
            }
        } else {
            let nth = d - BigUint::one();

            if let Some(ip) = iter.next() {
                output.push(Ipv6Cidr::from_prefix_and_bits(ip, bits).unwrap());

                while let Some(ip) = iter.nth_big_uint(nth.clone()) {
                    output.push(Ipv6Cidr::from_prefix_and_bits(ip, bits).unwrap());
                }
            }
        }

        Some(output)
    }
}