function normalize_ipv6addr($v6addr)
{
    if (strlen($v6addr) == 0) {
        return null;
    }
    $v6str = expand_ipv6addr($v6addr);
    // suppress prefix zero
    $v6a = explode(":", $v6str);
    foreach ($v6a as &$tmp) {
        $tmp = preg_replace("/^[0]+/", "", $tmp);
        if (strlen($tmp) == 0) {
            $tmp = "0";
        }
    }
    $v6str = implode(":", $v6a);
    // replace first zero as "::"
    $replace_flag = 1;
    $found_zero = 0;
    $v6a = explode(":", $v6str);
    foreach ($v6a as &$tmp) {
        if (strcmp($tmp, "0") == 0) {
            if ($replace_flag) {
                $tmp = "z";
                $found_zero++;
            }
        } else {
            if ($found_zero) {
                $replace_flag = 0;
            }
        }
    }
    unset($tmp);
    $v6str = implode(":", $v6a);
    if ($found_zero > 1) {
        $v6str = preg_replace("/(:?z:?)+/", "::", $v6str);
    } else {
        $v6str = preg_replace("/(z)+/", "0", $v6str);
    }
    return $v6str;
}
function get_ipv6network($v6addr, $mask)
{
    if (strlen($v6addr) == 0) {
        return null;
    }
    $v6str = expand_ipv6addr($v6addr);
    // compute by 16bits
    $v6a = explode(":", $v6str);
    foreach ($v6a as &$tmp) {
        if ($mask >= 16) {
            $mask -= 16;
        } else {
            if ($mask != 0) {
                $bmask = 0xffff;
                $bmask <<= 16 - $mask;
                $tmp = sprintf("%x", intval($tmp, 16) & $bmask);
                $mask = 0;
            } else {
                $tmp = 0;
            }
        }
    }
    unset($tmp);
    $v6str = implode(":", $v6a);
    $v6str = normalize_ipv6addr($v6str);
    return $v6str;
}