class FusiformSimilarityAlgorithm extends AbstractAlgorithm {

    public static final String ALGO_NAME = "fusiform_similarity";

    public static final String KEY_MIN_NEIGHBORS = "min_neighbors";
    public static final String KEY_MIN_SIMILARS = "min_similars";
    public static final String KEY_TOP_SIMILARS = "top_similars";
    public static final String KEY_GROUP_PROPERTY = "group_property";
    public static final String KEY_MIN_GROUPS = "min_groups";

    public static final int DEFAULT_MIN_NEIGHBORS = 10;
    public static final int DEFAULT_MIN_SIMILARS = 6;
    public static final int DEFAULT_TOP_SIMILARS = 0;
    public static final int DEFAULT_MIN_GROUPS = 0;

    @Override
    public String category() {
        return CATEGORY_SIMI;
    }

    @Override
    public String name() {
        return ALGO_NAME;
    }

    @Override
    public void checkParameters(Map<String, Object> parameters) {
        minNeighbors(parameters);
        alpha(parameters);
        minSimilars(parameters);
        topSimilars(parameters);
        groupProperty(parameters);
        minGroups(parameters);
        degree(parameters);
        limit(parameters);
        sourceLabel(parameters);
        sourceCLabel(parameters);
        direction(parameters);
        edgeLabel(parameters);
        workers(parameters);
    }

    @Override
    public Object call(UserJob<Object> job, Map<String, Object> parameters) {
        int workers = workers(parameters);
        try (Traverser traverser = new Traverser(job, workers)) {
            return traverser.fusiformSimilars(sourceLabel(parameters),
                                              sourceCLabel(parameters),
                                              direction(parameters),
                                              edgeLabel(parameters),
                                              minNeighbors(parameters),
                                              alpha(parameters),
                                              minSimilars(parameters),
                                              topSimilars(parameters),
                                              groupProperty(parameters),
                                              minGroups(parameters),
                                              degree(parameters),
                                              limit(parameters));
        }
    }

    protected static int minNeighbors(Map<String, Object> parameters) {
        if (!parameters.containsKey(KEY_MIN_NEIGHBORS)) {
            return DEFAULT_MIN_NEIGHBORS;
        }
        int minNeighbors = ParameterUtil.parameterInt(parameters,
                                                      KEY_MIN_NEIGHBORS);
        HugeTraverser.checkPositive(minNeighbors, KEY_MIN_NEIGHBORS);
        return minNeighbors;
    }

    protected static int minSimilars(Map<String, Object> parameters) {
        if (!parameters.containsKey(KEY_MIN_SIMILARS)) {
            return DEFAULT_MIN_SIMILARS;
        }
        int minSimilars = ParameterUtil.parameterInt(parameters,
                                                     KEY_MIN_SIMILARS);
        HugeTraverser.checkPositive(minSimilars, KEY_MIN_SIMILARS);
        return minSimilars;
    }

    protected static int topSimilars(Map<String, Object> parameters) {<FILL_FUNCTION_BODY>}

    protected static String groupProperty(Map<String, Object> parameters) {
        if (!parameters.containsKey(KEY_GROUP_PROPERTY)) {
            return null;
        }
        return ParameterUtil.parameterString(parameters, KEY_GROUP_PROPERTY);
    }

    protected static int minGroups(Map<String, Object> parameters) {
        if (!parameters.containsKey(KEY_MIN_GROUPS)) {
            return DEFAULT_MIN_GROUPS;
        }
        int minGroups = ParameterUtil.parameterInt(parameters, KEY_MIN_GROUPS);
        HugeTraverser.checkPositive(minGroups, KEY_MIN_GROUPS);
        return minGroups;
    }

    protected static long limit(Map<String, Object> parameters) {
        if (!parameters.containsKey(KEY_LIMIT)) {
            return DEFAULT_LIMIT;
        }
        long limit = ParameterUtil.parameterLong(parameters, KEY_LIMIT);
        HugeTraverser.checkLimit(limit);
        return limit;
    }

    private static class Traverser extends AlgoTraverser {

        public Traverser(UserJob<Object> job, int workers) {
            super(job, ALGO_NAME, workers);
        }

        public Object fusiformSimilars(String sourceLabel, String sourceCLabel,
                                       Directions direction, String label,
                                       int minNeighbors, double alpha,
                                       int minSimilars, long topSimilars,
                                       String groupProperty, int minGroups,
                                       long degree, long limit) {
            HugeGraph graph = this.graph();

            FusiformSimilarityTraverser traverser =
                    new FusiformSimilarityTraverser(graph);

            AtomicLong count = new AtomicLong(0L);
            JsonMap similarsJson = new JsonMap();
            similarsJson.startObject();

            this.traverse(sourceLabel, sourceCLabel, v -> {
                SimilarsMap similars = traverser.fusiformSimilarity(
                        IteratorUtils.of(v), direction,
                        label, minNeighbors, alpha,
                        minSimilars, (int) topSimilars,
                        groupProperty, minGroups, degree,
                        MAX_CAPACITY, NO_LIMIT, true);
                if (similars.isEmpty()) {
                    return;
                }
                String result = JsonUtil.toJson(similars.toMap());
                result = result.substring(1, result.length() - 1);
                synchronized (similarsJson) {
                    if (count.incrementAndGet() > limit && limit != NO_LIMIT) {
                        throw new StopExecution("exceed limit %s", limit);
                    }
                    similarsJson.appendRaw(result);
                }
            });

            similarsJson.endObject();

            return similarsJson.asJson();
        }
    }
}

        if (!parameters.containsKey(KEY_TOP_SIMILARS)) {
            return DEFAULT_TOP_SIMILARS;
        }
        int minSimilars = ParameterUtil.parameterInt(parameters,
                                                     KEY_TOP_SIMILARS);
        HugeTraverser.checkNonNegative(minSimilars, KEY_TOP_SIMILARS);
        return minSimilars;
    
<methods>public non-sealed void <init>() ,public void checkParameters(Map<java.lang.String,java.lang.Object>) <variables>public static final int BATCH,public static final java.lang.String CATEGORY_AGGR,public static final java.lang.String CATEGORY_CENT,public static final java.lang.String CATEGORY_COMM,public static final java.lang.String CATEGORY_PATH,public static final java.lang.String CATEGORY_RANK,public static final java.lang.String CATEGORY_SIMI,public static final java.lang.String C_LABEL,public static final double DEFAULT_ALPHA,public static final long DEFAULT_CAPACITY,public static final long DEFAULT_DEGREE,public static final long DEFAULT_EACH_LIMIT,public static final long DEFAULT_LIMIT,public static final double DEFAULT_PRECISION,public static final long DEFAULT_SAMPLE,public static final long DEFAULT_STABLE_TIMES,public static final long DEFAULT_TIMES,public static final java.lang.String EXPORT_PATH,public static final java.lang.String KEY_ALPHA,public static final java.lang.String KEY_CAPACITY,public static final java.lang.String KEY_CLEAR,public static final java.lang.String KEY_DEGREE,public static final java.lang.String KEY_DEPTH,public static final java.lang.String KEY_DIRECTION,public static final java.lang.String KEY_EACH_LIMIT,public static final java.lang.String KEY_EXPORT_COMM,public static final java.lang.String KEY_LABEL,public static final java.lang.String KEY_LIMIT,public static final java.lang.String KEY_PRECISION,public static final java.lang.String KEY_SAMPLE,public static final java.lang.String KEY_SHOW_COMM,public static final java.lang.String KEY_SHOW_MOD,public static final java.lang.String KEY_SKIP_ISOLATED,public static final java.lang.String KEY_SOURCE_CLABEL,public static final java.lang.String KEY_SOURCE_LABEL,public static final java.lang.String KEY_SOURCE_SAMPLE,public static final java.lang.String KEY_STABLE_TIMES,public static final java.lang.String KEY_TIMES,public static final java.lang.String KEY_TOP,public static final java.lang.String KEY_WORKERS,public static final long MAX_CAPACITY,public static final long MAX_QUERY_LIMIT,public static final long MAX_RESULT_SIZE,public static final java.lang.String R_RANK,public static final java.lang.String USER_DIR