You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: net.go
    path of file: ./repos/gomplate/internal/funcs
    
    the code of the file until where you have to start completion: package funcs

import (
	"context"
	"fmt"
	"math/big"
	stdnet "net"
	"net/netip"

	"github.com/hairyhenderson/gomplate/v4/conv"
	"github.com/hairyhenderson/gomplate/v4/internal/cidr"
	"github.com/hairyhenderson/gomplate/v4/internal/deprecated"
	"github.com/hairyhenderson/gomplate/v4/net"
	"go4.org/netipx"
	"inet.af/netaddr"
)

// CreateNetFuncs -
func CreateNetFuncs(ctx context.Context) map[string]interface{} {
	ns := &NetFuncs{ctx}
	return map[string]interface{}{
		"net": func() interface{} { return ns },
	}
}

// NetFuncs -
type NetFuncs struct {
	ctx context.Context
}

// LookupIP -
func (f NetFuncs) LookupIP(name interface{}) (string, error) {
	return net.LookupIP(conv.ToString(name))
}

// LookupIPs -
func (f NetFuncs) LookupIPs(name interface{}) ([]string, error) {
	return net.LookupIPs(conv.ToString(name))
}

// LookupCNAME -
func (f NetFuncs) LookupCNAME(name interface{}) (string, error) {
	return net.LookupCNAME(conv.ToString(name))
}

// LookupSRV -
func (f NetFuncs) LookupSRV(name interface{}) (*stdnet.SRV, error) {
	return net.LookupSRV(conv.ToString(name))
}

// LookupSRVs -
func (f NetFuncs) LookupSRVs(name interface{}) ([]*stdnet.SRV, error) {
	return net.LookupSRVs(conv.ToString(name))
}

// LookupTXT -
func (f NetFuncs) LookupTXT(name interface{}) ([]string, error) {
	return net.LookupTXT(conv.ToString(name))
}

// ParseIP -
//
// Deprecated: use [ParseAddr] instead
func (f *NetFuncs) ParseIP(ip interface{}) (netaddr.IP, error) {
	deprecated.WarnDeprecated(f.ctx, "net.ParseIP is deprecated - use net.ParseAddr instead")
	return netaddr.ParseIP(conv.ToString(ip))
}

// ParseIPPrefi
    
func (f *NetFuncs) CIDRSubnetSizes(args ...interface{}) ([]netip.Prefix, error) {
	if err := checkExperimental(f.ctx); err != nil {
		return nil, err
	}

	if len(args) < 2 {
		return nil, fmt.Errorf("wrong number of args: want 2 or more, got %d", len(args))
	}

	network, err := f.parseNetipPrefix(args[len(args)-1])
	if err != nil {
		return nil, err
	}
	newbits := conv.ToInts(args[:len(args)-1]...)

	startPrefixLen := network.Bits()
	firstLength := newbits[0]

	firstLength += startPrefixLen
	retValues := make([]netip.Prefix, len(newbits))

	current, _ := cidr.PreviousSubnet(network, firstLength)

	for i, length := range newbits {
		if length < 1 {
			return nil, fmt.Errorf("must extend prefix by at least one bit")
		}
		// For portability with 32-bit systems where the subnet number
		// will be a 32-bit int, we only allow extension of 32 bits in
		// one call even if we're running on a 64-bit machine.
		// (Of course, this is significant only for IPv6.)
		if length > 32 {
			return nil, fmt.Errorf("may not extend prefix by more than 32 bits")
		}

		length += startPrefixLen
		if length > network.Addr().BitLen() {
			protocol := "IP"
			switch {
			case network.Addr().Is4():
				protocol = "IPv4"
			case network.Addr().Is6():
				protocol = "IPv6"
			}
			return nil, fmt.Errorf("would extend prefix to %d bits, which is too long for an %s address", length, protocol)
		}

		next, rollover := cidr.NextSubnet(current, length)
		if rollover || !network.Contains(next.Addr()) {
			// If we run out of suffix bits in the base CIDR prefix then
			// NextSubnet will start incrementing the prefix bits, which
			// we don't allow because it would then allocate addresses
			// outside of the caller's given prefix.
			return nil, fmt.Errorf("not enough remaining address space for a subnet with a prefix of %d bits after %s", length, current.String())
		}
		current = next
		retValues[i] = current
	}

	return retValues, nil
}