diff --git a/src/Lib/Enums/ParsedNetAddressStringType.cs b/src/Lib/Enums/ParsedNetAddressStringType.cs
new file mode 100644
index 0000000..915e7f3
--- /dev/null
+++ b/src/Lib/Enums/ParsedNetAddressStringType.cs
@@ -0,0 +1,8 @@
+namespace SmallsOnline.Subnetting.Lib.Enums
+{
+ public enum ParsedNetAddressStringType
+ {
+ CidrNotation = 0,
+ SubnetMask = 1
+ }
+}
\ No newline at end of file
diff --git a/src/Lib/Models/BinaryNumber.cs b/src/Lib/Models/BinaryNumber.cs
new file mode 100644
index 0000000..8186037
--- /dev/null
+++ b/src/Lib/Models/BinaryNumber.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace SmallsOnline.Subnetting.Lib.Models
+{
+ ///
+ /// A representation of a binary number.
+ ///
+ public class BinaryNumber
+ {
+ ///
+ /// Create from a byte.
+ ///
+ /// A byte value.
+ public BinaryNumber(byte byteItem)
+ {
+ List bitValuesList = new();
+
+ BitArray bitArray = new(new byte[] { byteItem });
+ for (int i = bitArray.Length - 1; i >= 0; i--)
+ {
+ bitValuesList.Add(new(i, bitArray[i]));
+ }
+
+ bitValues = bitValuesList.ToArray();
+ }
+
+ ///
+ /// An array of the bits used for the binary number.
+ ///
+ public BitRepresentation[] BitValues
+ {
+ get => bitValues;
+ }
+
+ private readonly BitRepresentation[] bitValues;
+
+ ///
+ /// Get the amount of unused bits.
+ ///
+ /// The amount of unused bits.
+ public int GetUnusedBits()
+ {
+ int bitsUnused = 0;
+
+ foreach (BitRepresentation bitItem in bitValues)
+ {
+ bitsUnused += bitItem.BitActualValue;
+ }
+
+ return bitsUnused;
+ }
+
+ ///
+ /// Get the amount of used bits.
+ ///
+ /// The amount of used bits.
+ public int GetUsedBits()
+ {
+ int bitsUsed = 255;
+
+ foreach (BitRepresentation bitItem in bitValues)
+ {
+ bitsUsed -= bitItem.BitActualValue;
+ }
+
+ return bitsUsed;
+ }
+
+ ///
+ /// Return the amount of unused bits as a string.
+ ///
+ /// The amount of unused bits.
+ public override string ToString()
+ {
+ return $"{GetUnusedBits()}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Lib/Models/BitRepresentation.cs b/src/Lib/Models/BitRepresentation.cs
new file mode 100644
index 0000000..796746d
--- /dev/null
+++ b/src/Lib/Models/BitRepresentation.cs
@@ -0,0 +1,62 @@
+using System;
+
+namespace SmallsOnline.Subnetting.Lib.Models
+{
+ ///
+ /// A representation of a bit.
+ ///
+ public class BitRepresentation
+ {
+ ///
+ /// Create from the position of the bit and if it's on.
+ ///
+ /// The position of the bit.
+ /// The bit is on and used.
+ public BitRepresentation(int position, bool isOn)
+ {
+ bitPosition = position;
+ bitIsOn = isOn;
+ }
+
+ ///
+ /// The position of the bit.
+ ///
+ public int BitPosition
+ {
+ get => bitPosition;
+ }
+
+ ///
+ /// The value of the bit.
+ /// 2^bitPosition
+ ///
+ public int BitValue
+ {
+ get => (int)Math.Pow(2, bitPosition);
+ }
+
+ ///
+ /// If the bit is on or not.
+ ///
+ public bool BitIsOn
+ {
+ get => bitIsOn;
+ }
+
+ ///
+ /// The actual value of the bit if it's on or not.
+ ///
+ public int BitActualValue
+ {
+ get => bitIsOn ? BitValue : 0;
+ }
+
+ private readonly int bitPosition;
+ private readonly bool bitIsOn;
+
+ public override string ToString()
+ {
+ return $"{BitActualValue}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Lib/Models/IPv4Subnet.cs b/src/Lib/Models/IPv4Subnet.cs
new file mode 100644
index 0000000..493d68b
--- /dev/null
+++ b/src/Lib/Models/IPv4Subnet.cs
@@ -0,0 +1,204 @@
+using System;
+using System.Net;
+
+namespace SmallsOnline.Subnetting.Lib.Models
+{
+ using SmallsOnline.Subnetting.Lib.Enums;
+ ///
+ /// A representation of an IPv4 subnet.
+ ///
+ public class IPv4Subnet
+ {
+ ///
+ /// Create from an IP address and CIDR mask.
+ ///
+ /// An IP address in a subnet.
+ /// The CIDR mask for the subnet.
+ public IPv4Subnet(IPAddress ipAddress, double cidr)
+ {
+ _subnetMask = new(cidr);
+ _networkAddress = GetSubnetBoundary(ipAddress, _subnetMask);
+ _broadcastAddress = GetBroadcastAddress(_networkAddress, _subnetMask);
+ _usableHostRange = new(_networkAddress, _broadcastAddress);
+ }
+
+ ///
+ /// Create from an IP address and CIDR mask.
+ ///
+ /// An IP address in a subnet.
+ /// The subnet mask.
+ public IPv4Subnet(IPAddress ipAddress, IPv4SubnetMask subnetMask)
+ {
+ _subnetMask = subnetMask;
+ _networkAddress = GetSubnetBoundary(ipAddress, _subnetMask);
+ _broadcastAddress = GetBroadcastAddress(_networkAddress, _subnetMask);
+ _usableHostRange = new(_networkAddress, _broadcastAddress);
+ }
+
+ ///
+ /// Create from an IP address and CIDR mask.
+ ///
+ /// An IP address in a subnet.
+ /// The subnet mask.
+ public IPv4Subnet(IPAddress ipAddress, IPAddress subnetMask)
+ {
+ _subnetMask = new(subnetMask.GetAddressBytes());
+ _networkAddress = GetSubnetBoundary(ipAddress, _subnetMask);
+ _broadcastAddress = GetBroadcastAddress(_networkAddress, _subnetMask);
+ _usableHostRange = new(_networkAddress, _broadcastAddress);
+ }
+
+ ///
+ /// Create from a string of a network.
+ /// For example:
+ /// 10.21.6.0/18
+ ///
+ /// A network written in a string format.
+ public IPv4Subnet(string networkString)
+ {
+ ParsedNetAddressString parsedNetAddress = new(networkString);
+
+ _subnetMask = parsedNetAddress.ParsedType switch
+ {
+ ParsedNetAddressStringType.SubnetMask => new(parsedNetAddress.SubnetMask.GetAddressBytes()),
+ _ => new(parsedNetAddress.CidrNotation)
+ };
+
+ _networkAddress = GetSubnetBoundary(parsedNetAddress.IPAddress, _subnetMask);
+ _broadcastAddress = GetBroadcastAddress(_networkAddress, _subnetMask);
+ _usableHostRange = new(_networkAddress, _broadcastAddress);
+ }
+
+ ///
+ /// The network address of the subnet.
+ ///
+ public IPAddress NetworkAddress
+ {
+ get => _networkAddress;
+ }
+
+ ///
+ /// The subnet mask of the subnet.
+ ///
+ public IPv4SubnetMask SubnetMask
+ {
+ get => _subnetMask;
+ }
+
+ ///
+ /// The CIDR mask of the subnet.
+ ///
+ public double CidrMask
+ {
+ get => _subnetMask.CidrNotation;
+ }
+
+ ///
+ /// The broadcast address of the subnet.
+ ///
+ public IPAddress BroadcastAddress
+ {
+ get => _broadcastAddress;
+ }
+
+ ///
+ /// The total amount of addresses in the subnet.
+ ///
+ public double TotalAddresses
+ {
+ get => _subnetMask.TotalAddresses;
+ }
+
+ ///
+ /// The total amount of usable addresses in the subnet.
+ ///
+ public double UsableAddresses
+ {
+ get => _subnetMask.TotalAddresses - 2;
+ }
+
+ ///
+ /// The range of hosts available for use in the subnet.
+ ///
+ public UsableHostRange UsableHostRange
+ {
+ get => _usableHostRange;
+ }
+
+ private readonly IPAddress _networkAddress;
+ private readonly IPv4SubnetMask _subnetMask;
+ private readonly IPAddress _broadcastAddress;
+ private readonly UsableHostRange _usableHostRange;
+
+ ///
+ /// Display the subnet as a string.
+ ///
+ /// A string representation of the subnet with the network address and CIDR mask.
+ public override string ToString()
+ {
+ return $"{_networkAddress}/{CidrMask}";
+ }
+
+ ///
+ /// Gets the network address of the subnet from the supplied IP address and the subnet mask.
+ ///
+ /// The IP address in the subnet.
+ /// The subnet mask of the subnet.
+ /// The network address of the subnet.
+ private static IPAddress GetSubnetBoundary(IPAddress ipAddress, IPv4SubnetMask subnetMask)
+ {
+ // Get the byte arrays of the IP address and the subnet mask.
+ byte[] ipAddressBytes = ipAddress.GetAddressBytes();
+ byte[] subnetMaskBytes = subnetMask.ToBytes();
+
+ // Get the last used octet in the subnet mask.
+ Octet lastUsedOctet = subnetMask.GetLastUsedOctet();
+
+ // Create the byte array for generating the network address.
+ byte[] netAddressBytes = new byte[4];
+ for (int i = 0; i < netAddressBytes.Length; i++)
+ {
+ if (i < lastUsedOctet.OctetPosition - 1)
+ {
+ // If the current loop count is less than the last used octet's position
+ // then set the current index for the network address to the same value as
+ // the IP address.
+ netAddressBytes[i] = ipAddressBytes[i];
+ }
+ else
+ {
+ // If the current loop count is less than the last used octet's position
+ // then set the current index for the network address to the value of a
+ // bitwise AND operation of the IP address and the subnet mask.
+ netAddressBytes[i] = ipAddressBytes[i] &= subnetMaskBytes[i];
+ }
+ }
+
+ return new(netAddressBytes);
+ }
+
+ ///
+ /// Gets the broadcast address of the subnet.
+ ///
+ /// The network address of the subnet.
+ /// The subnet mask of the subnet.
+ /// The broadcast address for the subnet.
+ private static IPAddress GetBroadcastAddress(IPAddress networkAddress, IPv4SubnetMask subnetMask)
+ {
+ // Create an empty byte array for the broadcast address.
+ byte[] broadcastAddressBytes = new byte[4];
+
+ // Get the bytes for the wildcard subnet mask.
+ byte[] networkAddressBytes = networkAddress.GetAddressBytes();
+ byte[] wildcardBytes = subnetMask.WildcardMask.WildcardBytes;
+
+ // Iterate through each index of the network address bytes and add the amount from the wildcard bytes.
+ for (int i = 0; i < broadcastAddressBytes.Length; i++)
+ {
+ broadcastAddressBytes[i] = (byte)(networkAddressBytes[i] + wildcardBytes[i]);
+ }
+
+ return new(broadcastAddressBytes);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Lib/Models/IPv4SubnetMask.cs b/src/Lib/Models/IPv4SubnetMask.cs
new file mode 100644
index 0000000..0ea1c53
--- /dev/null
+++ b/src/Lib/Models/IPv4SubnetMask.cs
@@ -0,0 +1,245 @@
+using System;
+using System.Net;
+using System.Collections.Generic;
+
+namespace SmallsOnline.Subnetting.Lib.Models
+{
+ ///
+ /// A representation of an IPv4 subnet mask.
+ ///
+ public class IPv4SubnetMask
+ {
+ ///
+ /// Create from a CIDR notation.
+ ///
+ /// The CIDR notation of the subnet mask.
+ public IPv4SubnetMask(double cidr)
+ {
+ wildcardMask = GetWildcardMask_FromCidr(cidr);
+ octets = GetSubnetMaskOctets_FromWildcardMask(wildcardMask);
+ cidrNotation = cidr;
+ totalAddresses = (int)GetMaxAddresses_FromCidr(cidr);
+ }
+
+ ///
+ /// Create from a byte array.
+ ///
+ /// The byte array of the subnet mask.
+ public IPv4SubnetMask(byte[] bytes)
+ {
+ octets = GetOctets_FromBytes(bytes);
+ wildcardMask = new(bytes);
+ cidrNotation = GetCidrMask_FromSubnetMask(octets);
+ totalAddresses = (int)GetMaxAddresses_FromCidr(cidrNotation);
+ }
+
+ ///
+ /// The octets of the subnet mask.
+ ///
+ public Octet[] Octets
+ {
+ get => octets;
+ }
+
+ ///
+ /// The wildcard mask of the subnet mask.
+ ///
+ public IPv4WildcardMask WildcardMask
+ {
+ get => wildcardMask;
+ }
+
+ ///
+ /// The CIDR notation of the subnet mask.
+ ///
+ public double CidrNotation
+ {
+ get => cidrNotation;
+ }
+
+ ///
+ /// The total amount of addresses available from the subnet mask.
+ ///
+ public int TotalAddresses
+ {
+ get => totalAddresses;
+ }
+
+ private readonly Octet[] octets;
+ private readonly IPv4WildcardMask wildcardMask;
+ private readonly double cidrNotation;
+ private readonly int totalAddresses;
+
+ ///
+ /// Gets the last octet that was borrowed from.
+ ///
+ /// The last octet borrowed from.
+ public Octet GetLastUsedOctet()
+ {
+ List octetsList = new(octets);
+
+ Octet lastUsedOctet = octetsList.FindAll((octetItem) => octetItem.BitsUsed == true)[0];
+
+ return lastUsedOctet;
+ }
+
+ ///
+ /// Returns the subnet mask as a byte array.
+ ///
+ /// A byte array of the subnet mask.
+ public byte[] ToBytes()
+ {
+ byte[] byteArray = new byte[4];
+ for (int i = 0; i < octets.Length; i++)
+ {
+ byteArray[i] = (byte)(byte.MinValue + octets[i].ToByte());
+ }
+
+ return byteArray;
+ }
+
+ ///
+ /// Returns the subnet mask as an IPAddress.
+ ///
+ /// An IPAddress type.
+ public IPAddress ToIPAddress()
+ {
+ return new(ToBytes());
+ }
+
+ ///
+ /// Displays the subnet mask as a string.
+ ///
+ /// A string representation of the subnet mask.
+ public override string ToString()
+ {
+ return string.Join(".", ToBytes());
+ }
+
+ ///
+ /// Gets the value of the octets for the subnet mask from a byte array.
+ ///
+ /// A byte array of the subnet mask.
+ /// An array of the octets.
+ private static Octet[] GetOctets_FromBytes(byte[] bytes)
+ {
+ Octet[] _octets = new Octet[4];
+ for (int i = 0; i < _octets.Length; i++)
+ {
+ _octets[i] = new(i + 1, bytes[i]);
+ }
+
+ return _octets;
+ }
+
+ ///
+ /// Calculate the total amount of addresses available from a CIDR notation.
+ ///
+ /// The CIDR notation of the subnet mask.
+ /// The total amount of addresses.
+ private static double GetMaxAddresses_FromCidr(double cidr)
+ {
+ // Get the maximum amount of addresses that can be used.
+ // Calculated by: 2^(32-cidrNotation)
+ return Math.Pow(2, GetBitBlock_FromCidr(cidr));
+ }
+
+ private static double GetBitBlock_FromCidr(double cidr)
+ {
+ return 32 - cidr;
+ }
+
+ ///
+ /// Get the wildcard mask of the subnet mask from a CIDR notation.
+ ///
+ /// The CIDR notation of the subnet mask.
+ /// The wildcard mask of the subnet mask.
+ private static IPv4WildcardMask GetWildcardMask_FromCidr(double cidr)
+ {
+ byte[] wildcardByteArray = new byte[4];
+
+ double maxAddresses = GetMaxAddresses_FromCidr(cidr);
+
+ // Get the amount of bytes filled.
+ // This is calculated by getting the log of the max addresses from the base of 256 and rounding to the lowest number.
+ double bytesFilled = Math.Floor(Math.Log(maxAddresses, 256));
+
+ // Get the amount of bits used.
+ // This is calculated by: maxAddresses / 256^bytesFilled
+ double bitsUsed = maxAddresses / Math.Pow(256, bytesFilled);
+
+ // Determine the position to fill the amount of bits used.
+ int byteArrayPosition = bytesFilled switch
+ {
+ 0 => 3,
+ 1 => 2,
+ 2 => 1,
+ 3 => 0,
+ _ => 0
+ };
+
+ // Set the position in the wildcard byte array to the amount of bits used.
+ // The value of 'bitsUsed' is subtracted by 1, since a byte is denoted from 0 - 255.
+ wildcardByteArray[byteArrayPosition] = (byte)(bitsUsed - 1);
+
+ // If needed, fill the remaining bytes to the right of the previously modified byte position.
+ for (int i = byteArrayPosition + 1; i < wildcardByteArray.Length; i++)
+ {
+ wildcardByteArray[i] = byte.MaxValue;
+ }
+
+ return new(wildcardByteArray, true);
+ }
+
+ ///
+ /// Get the octets of a subnet mask from the wildcard mask.
+ ///
+ /// The wildcard mask of a subnet mask.
+ /// An array of octets of the subnet mask.
+ private static Octet[] GetSubnetMaskOctets_FromWildcardMask(IPv4WildcardMask _wildcardMask)
+ {
+ Octet[] _octets = new Octet[4];
+ for (int i = _wildcardMask.WildcardBytes.Length - 1; i >= 0; i--)
+ {
+ _octets[i] = new(i + 1, (byte)(byte.MaxValue - _wildcardMask.WildcardBytes[i]));
+ }
+
+ return _octets;
+ }
+
+ ///
+ /// Get the CIDR notation from the subnet mask's octets.
+ ///
+ /// The octets of the subnet mask.
+ /// The CIDR notation of the subnet mask.
+ private static double GetCidrMask_FromSubnetMask(Octet[] _octets)
+ {
+ List bitList = new();
+ foreach (Octet octetItem in _octets)
+ {
+ bitList.AddRange(octetItem.BinaryValue.BitValues);
+ }
+ int countOfUsedBits = bitList.FindAll((item) => item.BitIsOn == false).Count;
+
+ return 32 - countOfUsedBits;
+ }
+
+ ///
+ /// Calculate the total amount of addresses from the octets of a subnet mask.
+ ///
+ /// The octets of the subnet mask.
+ /// The total amount of addresses.
+ private static int GetTotalAddresses_FromSubnetMask(Octet _octet)
+ {
+ // Note:
+ // ------
+ // This is still bugging out.
+ // 255.255.255.0 (/24), 255.255.0 (/16), and 255.0.0.0 (/8) are still returning 0.
+ // Since I've gotten the CIDR notation being calculated already, I've switched to using
+ // the GetMaxAddresses_FromCidr() method instead. Will keep looking into fixing this.
+ // ------
+
+ return (int)(Math.Pow(256, _octet.OctetPosition - 1) * _octet.BinaryValue.GetUnusedBits());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Lib/Models/IPv4WildcardMask.cs b/src/Lib/Models/IPv4WildcardMask.cs
new file mode 100644
index 0000000..9469f0d
--- /dev/null
+++ b/src/Lib/Models/IPv4WildcardMask.cs
@@ -0,0 +1,50 @@
+using System;
+
+namespace SmallsOnline.Subnetting.Lib.Models
+{
+ ///
+ /// A representation of the wildcard mask of an IPv4 subnet mask.
+ ///
+ public class IPv4WildcardMask
+ {
+ ///
+ /// Generate the wildcard mask from a byte array.
+ ///
+ /// A byte array of a subnet mask or wildcard mask.
+ /// Whether the input byte array has already been calculated.
+ public IPv4WildcardMask(byte[] bytes, bool isAlreadyCalculated = false)
+ {
+ if (isAlreadyCalculated)
+ {
+ wildcardBytes = bytes;
+ }
+ else
+ {
+ wildcardBytes = new byte[4];
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ wildcardBytes[i] = (byte)(byte.MaxValue - bytes[i]);
+ }
+ }
+ }
+
+ ///
+ /// The bytes of the wildcard mask.
+ ///
+ public byte[] WildcardBytes
+ {
+ get => wildcardBytes;
+ }
+
+ private readonly byte[] wildcardBytes;
+
+ ///
+ /// Displays the wildcard mask as a string.
+ ///
+ /// A string representation of the wildcard mask.
+ public override string ToString()
+ {
+ return string.Join(".", wildcardBytes);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Lib/Models/Octet.cs b/src/Lib/Models/Octet.cs
new file mode 100644
index 0000000..ca997da
--- /dev/null
+++ b/src/Lib/Models/Octet.cs
@@ -0,0 +1,66 @@
+using System;
+
+namespace SmallsOnline.Subnetting.Lib.Models
+{
+ ///
+ /// A representation of an octet.
+ ///
+ public class Octet
+ {
+ ///
+ /// Create with the position of the octet and it's byte value.
+ ///
+ /// The position of the octet.
+ /// The byte value of the octet.
+ public Octet(int position, byte byteItem)
+ {
+ octetPosition = position;
+ binaryValue = new(byteItem);
+ }
+
+ ///
+ /// The position of the octet.
+ ///
+ public int OctetPosition
+ {
+ get => octetPosition;
+ }
+
+ ///
+ /// The binary value of the octet.
+ ///
+ public BinaryNumber BinaryValue
+ {
+ get => binaryValue;
+ }
+
+ ///
+ /// If bits have been used.
+ ///
+ public bool BitsUsed
+ {
+ get => binaryValue.GetUsedBits() > 0;
+ }
+
+ private readonly int octetPosition;
+ private readonly BinaryNumber binaryValue;
+
+ ///
+ /// Return the byte value of the octet.
+ ///
+ /// The byte value of the octet.
+ public byte ToByte()
+ {
+ return Convert.ToByte(binaryValue.GetUnusedBits());
+ }
+
+ ///
+ /// Returns the value of the octet as a string.
+ ///
+ /// The value of the octet as a string.
+ public override string ToString()
+ {
+ return $"{binaryValue.GetUnusedBits()}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Lib/Models/ParsedNetAddressString.cs b/src/Lib/Models/ParsedNetAddressString.cs
new file mode 100644
index 0000000..e82fd13
--- /dev/null
+++ b/src/Lib/Models/ParsedNetAddressString.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Net;
+using System.Text.RegularExpressions;
+
+namespace SmallsOnline.Subnetting.Lib.Models
+{
+ using SmallsOnline.Subnetting.Lib.Enums;
+ ///
+ /// A representation of a parsed network address.
+ ///
+ public class ParsedNetAddressString
+ {
+ ///
+ /// Create from a network string.
+ ///
+ /// The string of the network.
+ public ParsedNetAddressString(string netAddressString)
+ {
+ Initialize(netAddressString);
+ }
+
+ ///
+ /// The parsed IP address.
+ ///
+ public IPAddress IPAddress
+ {
+ get => _ipAddress;
+ }
+
+ ///
+ /// The parsed CIDR notation.
+ ///
+ public double CidrNotation
+ {
+ get => _cidrNotation;
+ }
+
+ ///
+ /// The parsed subnet mask.
+ ///
+ public IPAddress SubnetMask
+ {
+ get => _subnetMask;
+ }
+
+ public ParsedNetAddressStringType ParsedType
+ {
+ get => _parsedType;
+ }
+
+ private IPAddress _ipAddress;
+ private double _cidrNotation;
+ private IPAddress _subnetMask;
+ private ParsedNetAddressStringType _parsedType;
+
+ ///
+ /// Parses the string and sets the properties for the IP address and CIDR notation.
+ ///
+ /// The string of the network.
+ private void Initialize(string netAddressString)
+ {
+ Regex netAddressRegex = new(@"^(?'netAddress'(?:\d{1,3}(?:\.|)){4})(?:(?:\/)(?'cidrNotation'\d{1,2})|(?:\/|\s)(?'subnetMask'(?:\d{1,3}(?:\.|)){4}))$");
+ Match netAddressMatch = netAddressRegex.Match(netAddressString);
+
+ _ipAddress = IPAddress.Parse(netAddressMatch.Groups["netAddress"].Value);
+
+ if (netAddressMatch.Groups["cidrNotation"].Success)
+ {
+ _cidrNotation = Convert.ToDouble(netAddressMatch.Groups["cidrNotation"].Value);
+ _parsedType = ParsedNetAddressStringType.CidrNotation;
+ }
+ else if (netAddressMatch.Groups["subnetMask"].Success)
+ {
+ _subnetMask = IPAddress.Parse(netAddressMatch.Groups["subnetMask"].Value);
+ _parsedType = ParsedNetAddressStringType.SubnetMask;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Lib/Models/UsableHostRange.cs b/src/Lib/Models/UsableHostRange.cs
new file mode 100644
index 0000000..8686f66
--- /dev/null
+++ b/src/Lib/Models/UsableHostRange.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Net;
+
+namespace SmallsOnline.Subnetting.Lib.Models
+{
+ ///
+ /// A representation of the usable host range of a subnet.
+ ///
+ public class UsableHostRange
+ {
+ ///
+ /// Create from the network and broadcast address.
+ ///
+ /// The network address of the subnet.
+ /// The broadcast address of the subnet.
+ public UsableHostRange(IPAddress netAddress, IPAddress broadcastAddress)
+ {
+ Initialize(netAddress, broadcastAddress);
+ }
+
+ ///
+ /// The first usable host address in the subnet.
+ ///
+ public IPAddress FirstUsableHostAddress
+ {
+ get => _firstUsableHostAddress;
+ }
+
+ ///
+ /// The last usable host address in the subnet.
+ ///
+ public IPAddress LastUsableHostAddress
+ {
+ get => _lastUsableHostAddress;
+ }
+
+ private IPAddress _firstUsableHostAddress;
+ private IPAddress _lastUsableHostAddress;
+
+ private void Initialize(IPAddress netAddress, IPAddress broadcastAddress)
+ {
+ byte[] netAddressBytes = netAddress.GetAddressBytes();
+ byte[] broadcastAddressBytes = broadcastAddress.GetAddressBytes();
+
+ _firstUsableHostAddress = new(new byte[] { netAddressBytes[0], netAddressBytes[1], netAddressBytes[2], (byte)(netAddressBytes[3] + 1) });
+ _lastUsableHostAddress = new(new byte[] { broadcastAddressBytes[0], broadcastAddressBytes[1], broadcastAddressBytes[2], (byte)(broadcastAddressBytes[3] - 1) });
+ }
+
+ public override string ToString()
+ {
+ return $"{_firstUsableHostAddress} - {_lastUsableHostAddress}";
+ }
+ }
+}
\ No newline at end of file