class HashCrawlMapper extends CrawlMapper {

    @SuppressWarnings("unused")
    private static final long serialVersionUID = 2L;
    
    protected Frontier frontier;
    public Frontier getFrontier() {
        return this.frontier;
    }
    @Autowired
    public void setFrontier(Frontier frontier) {
        this.frontier = frontier;
    }

    /**
     * Number of crawlers among which to split up the URIs. Their names are
     * assumed to be 0..N-1.
     */
    protected long crawlerCount = 1L; 
    public long getCrawlerCount() {
        return this.crawlerCount; 
    }
    public void setCrawlerCount(long count) {
        this.crawlerCount = count;
    }

    {
        setUsePublicSuffixesRegex(true);
    }
    public boolean getUsePublicSuffixesRegex() {
        return (Boolean) kp.get("usePublicSuffixesRegex");
    }
    /**
     * Whether to use the PublicSuffixes-supplied reduce regex.
     *
     */
    public void setUsePublicSuffixesRegex(boolean usePublicSuffixes) {
        kp.put("usePublicSuffixesRegex",usePublicSuffixes);
    }
    
    {
        setReducePrefixRegex("");
    }
    public String getReducePrefixRegex() {
        return (String) kp.get("reducePrefixRegex");
    }
    /**
     * A regex pattern to apply to the classKey, using the first match as the
     * mapping key. If empty (the default), use the full classKey.
     *
     */
    public void setReducePrefixRegex(String regex) {
        kp.put("reducePrefixRegex",regex);
    }
    
    /**
     * Constructor.
     */
    public HashCrawlMapper() {
        super();
    }

    /**
     * Look up the crawler node name to which the given CrawlURI 
     * should be mapped. 
     * 
     * @param cauri CrawlURI to consider
     * @return String node name which should handle URI
     */
    protected String map(CrawlURI cauri) {
        // get classKey, via frontier to generate if necessary
        String key = frontier.getClassKey(cauri);
        String reduceRegex = getReduceRegex(cauri);
        return mapString(key, reduceRegex, getCrawlerCount()); 
    }

    protected String getReduceRegex(CrawlURI cauri) {
        if(getUsePublicSuffixesRegex()) {
            return PublicSuffixes.getTopmostAssignedSurtPrefixRegex();
        } else {
            return getReducePrefixRegex();
        }
    }

    public static String mapString(String key, String reducePattern,
            long bucketCount) {<FILL_FUNCTION_BODY>}
}


        if (reducePattern != null && reducePattern.length()>0) {
            Matcher matcher = TextUtils.getMatcher(reducePattern,key);
            if(matcher.find()) {
                key = matcher.group();
            }
            TextUtils.recycleMatcher(matcher);
        }
        long fp = FPGenerator.std64.fp(key);
        long bucket = fp % bucketCount;
        return Long.toString(bucket >= 0 ? bucket : -bucket);
    
<methods>public void <init>() ,public boolean getCheckOutlinks() ,public boolean getCheckUri() ,public org.archive.spring.ConfigPath getDiversionDir() ,public java.lang.String getLocalName() ,public org.archive.modules.deciderules.DecideRule getOutlinkRule() ,public int getRotationDigits() ,public boolean isRunning() ,public void setCheckOutlinks(boolean) ,public void setCheckUri(boolean) ,public void setDiversionDir(org.archive.spring.ConfigPath) ,public void setLocalName(java.lang.String) ,public void setOutlinkRule(org.archive.modules.deciderules.DecideRule) ,public void setRotationDigits(int) ,public void start() ,public void stop() <variables>protected org.archive.util.fingerprint.ArrayLongFPCache cache,protected boolean checkOutlinks,protected boolean checkUri,protected org.archive.spring.ConfigPath diversionDir,protected HashMap<java.lang.String,java.io.PrintWriter> diversionLogs,protected java.lang.String localName,protected java.lang.String logGeneration,protected org.archive.modules.deciderules.DecideRule outlinkRule,protected int rotationDigits