The Weekly Challenge 363

The Truth About Words: Can They Lie About Themselves?

Original Challenge Link

Task 1: String Lie Detector

"The Truth About Words: Can They Lie About Themselves?"

This week's first task involves parsing self-referential strings that make claims about their own composition. We need to verify whether the number of vowels and consonants mentioned in the string actually matches the content.

The Strategy: Parse the string to extract the subject and the claims. Count the actual vowels and consonants in the subject, then compare with the claimed values. Handle both word numbers (like "two") and digit numbers.
Perl Implementation
sub string_lie_detector ($text) {
    my ( $subject, $vowel_token, $consonant_token ) =
      $text =~ /^\s*(.*?)\s*[—-]\s*(\w+)\s+vowels?\s+and\s+(\w+)\s+consonants?\s*$/i;
    return 0 if !defined $subject;

    my $claimed_vowels     = _parse_count($vowel_token);
    my $claimed_consonants = _parse_count($consonant_token);
    return 0 if !defined($claimed_vowels) || !defined($claimed_consonants);

    my @letters = grep { /[A-Za-z]/ } split //, $subject;
    my $vowels     = scalar grep { /[AEIOU]/i } @letters;
    my $consonants = scalar(@letters) - $vowels;

    return ( $vowels == $claimed_vowels && $consonants == $claimed_consonants ) ? 1 : 0;
}
Python Implementation
def string_lie_detector(text: str) -> bool:
    """
    Parse self-referential string and check if vowel/consonant claims are true.
    """
    import re
    
    match = re.match(
        r'^\s*(.*?)\s*[—-]\s*(\w+)\s+vowels?\s+and\s+(\w+)\s+consonants?\s*$',
        text, re.IGNORECASE
    )
    if not match:
        return False
    
    subject, vowel_token, consonant_token = match.groups()
    
    claimed_vowels = _parse_count(vowel_token)
    claimed_consonants = _parse_count(consonant_token)
    
    if claimed_vowels is None or claimed_consonants is None:
        return False
    
    letters = [c for c in subject if c.isalpha()]
    vowels = sum(1 for c in letters if c.upper() in 'AEIOU')
    consonants = len(letters) - vowels
    
    return vowels == claimed_vowels and consonants == claimed_consonants

Task 2: Subnet Sheriff

"The Network Detective: Finding IPs in the Right Neighborhood"

The second task requires validating IPv4 addresses and CIDR subnet masks, then determining whether a given IP address falls within the specified network range.

The Strategy: Parse both the IP address and the CIDR notation. Convert IP addresses to 32-bit integers and compute the network mask based on the prefix length. Compare the masked values to determine if the IP belongs to the network.
Perl Implementation
sub subnet_sheriff ( $ip_addr, $network ) {
    my $ip = _parse_ipv4($ip_addr);
    return 0 if !defined $ip;

    my $parsed = _parse_cidr($network);
    return 0 if !defined $parsed;

    my ( $network_ip, $prefix ) = @$parsed;
    my $mask = $prefix == 0 ? 0 : ( ( 0xFFFFFFFF << ( 32 - $prefix ) ) & 0xFFFFFFFF );

    return ( ( $ip & $mask ) == ( $network_ip & $mask ) ) ? 1 : 0;
}

sub _parse_ipv4 ($text) {
    my @parts = split /\./, $text, -1;
    return undef if @parts != 4;

    my $ip = 0;
    for my $part (@parts) {
        return undef if $part !~ /^\d+$/;
        my $value = 0 + $part;
        return undef if $value < 0 || $value > 255;
        $ip = ( $ip << 8 ) | $value;
    }

    return $ip;
}
Python Implementation
def subnet_sheriff(ip_addr: str, network: str) -> bool:
    """
    Check if an IP address belongs to a CIDR network.
    """
    ip = _parse_ipv4(ip_addr)
    if ip is None:
        return False
    
    parsed = _parse_cidr(network)
    if parsed is None:
        return False
    
    network_ip, prefix = parsed
    mask = 0 if prefix == 0 else (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF
    
    return (ip & mask) == (network_ip & mask)


def _parse_ipv4(text: str) -> int | None:
    """Parse IPv4 address to 32-bit integer."""
    parts = text.split('.')
    if len(parts) != 4:
        return None
    
    ip = 0
    for part in parts:
        if not part.isdigit():
            return None
        value = int(part)
        if value < 0 or value > 255:
            return None
        ip = (ip << 8) | value
    
    return ip