class ClusterEvaluationUtil implements AllWindowFunction<Row, Row, TimeWindow> {

    public static Tuple1<Double> calSilhouetteCoefficient(Row row, ClusterMetricsSummary clusterMetricsSummary) {
        if (!EvaluationUtil.checkRowFieldNotNull(row)) {
            return Tuple1.of(0.);
        }
        String clusterId = row.getField(0).toString();
        Vector vec = VectorUtil.getVector(row.getField(1));
        double currentClusterDissimilarity = 0.0;
        double neighboringClusterDissimilarity = Double.MAX_VALUE;
        if (clusterMetricsSummary.distance instanceof EuclideanDistance) {
            double normSquare = vec.normL2Square();
            for (int i = 0; i < clusterMetricsSummary.k; i++) {
                double dissimilarity = clusterMetricsSummary.clusterCnt.get(i) * normSquare
                    - 2 * clusterMetricsSummary.clusterCnt.get(i) * MatVecOp.dot(vec, clusterMetricsSummary.meanVector.get(i)) + clusterMetricsSummary.vectorNormL2Sum.get(i);
                if (clusterId.equals(clusterMetricsSummary.clusterId.get(i))) {
                    if (clusterMetricsSummary.clusterCnt.get(i) > 1) {
                        currentClusterDissimilarity = dissimilarity / (clusterMetricsSummary.clusterCnt.get(i) - 1);
                    }
                } else {
                    neighboringClusterDissimilarity = Math.min(neighboringClusterDissimilarity,
                        dissimilarity / clusterMetricsSummary.clusterCnt.get(i));
                }
            }
        } else {
            for (int i = 0; i < clusterMetricsSummary.k; i++) {
                double dissimilarity = 1.0 - MatVecOp.dot(vec, clusterMetricsSummary.meanVector.get(i));
                if (clusterId.equals(clusterMetricsSummary.clusterId.get(i))) {
                    if (clusterMetricsSummary.clusterCnt.get(i) > 1) {
                        currentClusterDissimilarity = dissimilarity * clusterMetricsSummary.clusterCnt.get(i) / (clusterMetricsSummary.clusterCnt.get(i) - 1);
                    }
                } else {
                    neighboringClusterDissimilarity = Math.min(neighboringClusterDissimilarity,
                        dissimilarity);
                }
            }
        }
        return Tuple1.of(currentClusterDissimilarity < neighboringClusterDissimilarity ?
            1 - (currentClusterDissimilarity / neighboringClusterDissimilarity) :
            (neighboringClusterDissimilarity / currentClusterDissimilarity) - 1);
    }

    public  ClusterEvaluationUtil(ContinuousDistance distance);

}