protected function _decodePart($input)
    {
        $n = static::INITIAL_N;
        $i = 0;
        $bias = static::INITIAL_BIAS;
        $output = '';

        $pos = strrpos($input, static::DELIMITER);
        if ($pos !== false) {
            $output = substr($input, 0, $pos++);
        } else {
            $pos = 0;
        }

        $outputLength = strlen($output);
        $inputLength = strlen($input);

        /* Punycode lookup is case-insensitive. */
        $input = Horde_String::lower($input);

        while ($pos < $inputLength) {
            $oldi = $i;
            $w = 1;

            for ($k = static::BASE; ; $k += static::BASE) {
                $digit = static::$_decodeTable[$input[$pos++]];
                $i = $i + ($digit * $w);
                $t = $this->_calculateThreshold($k, $bias);

                if ($digit < $t) {
                    break;
                }

                $w = $w * (static::BASE - $t);
            }

            $bias = $this->_adapt($i - $oldi, ++$outputLength, ($oldi === 0));
            $n = $n + (int) ($i / $outputLength);
            $i = $i % ($outputLength);

            $output = Horde_String::substr($output, 0, $i, 'UTF-8') .
                $this->_codePointToChar($n) .
                Horde_String::substr($output, $i, $outputLength - 1, 'UTF-8');

            ++$i;
        }

        return $output;
    }