class MACCSFingerprinter extends AbstractFingerprinter implements IFingerprinter {

    @Override
    public IBitFingerprint getBitFingerprint(IAtomContainer container) throws CDKException {

        MaccsKey[] keys = keys(container.getBuilder());
        BitSet fp = new BitSet(keys.length);

        // init SMARTS invariants (connectivity, degree, etc)
        SmartsPattern.prepare(container);

        final int numAtoms = container.getAtomCount();


        final GraphUtil.EdgeToBondMap bmap    = GraphUtil.EdgeToBondMap.withSpaceFor(container);
        final int[][]                 adjlist = GraphUtil.toAdjList(container, bmap);

        for (int i = 0; i < keys.length; i++) {
            final MaccsKey key     = keys[i];
            final Pattern  pattern = key.pattern;

            switch (key.smarts) {
                case "[!*]":
                    break;
                case "[!0]":
                    for (IAtom atom : container.atoms()) {
                        if (atom.getMassNumber() != null) {
                            fp.set(i);
                            break;
                        }
                    }
                    break;

                // ring bits
                case "[R]1@*@*@1": // 3M RING bit22
                case "[R]1@*@*@*@1": // 4M RING bit11
                case "[R]1@*@*@*@*@1": // 5M RING bit96
                case "[R]1@*@*@*@*@*@1": // 6M RING bit163, x2=bit145
                case "[R]1@*@*@*@*@*@*@1": // 7M RING, bit19
                case "[R]1@*@*@*@*@*@*@*@1": // 8M RING, bit101
                    // handled separately
                    break;

                case "(*).(*)":
                    // bit 166 (*).(*) we can match this in SMARTS but it's faster to just
                    // count the number of components or in this case try to traverse the
                    // component, iff there are some atoms not visited we have more than
                    // one component
                    boolean[] visit = new boolean[numAtoms];
                    if (numAtoms > 1 && visitPart(visit, adjlist, 0, -1) < numAtoms)
                        fp.set(165);
                    break;

                default:
                    if (key.count == 0) {
                        if (pattern.matches(container))
                            fp.set(i);
                    } else {
                        // check if there are at least 'count' unique hits, key.count = 0
                        // means find at least one match hence we add 1 to out limit
                        if (pattern.matchAll(container).uniqueAtoms().atLeast(key.count + 1))
                            fp.set(i);
                    }
                    break;
            }
        }

        // Ring Bits

        // threshold=126, see AllRingsFinder.Threshold.PubChem_97
        if (numAtoms > 2) {
            AllCycles allcycles = new AllCycles(adjlist,
                                                Math.min(8, numAtoms),
                                                126);
            int numArom = 0;
            for (int[] path : allcycles.paths()) {
                // length is +1 as we repeat the closure vertex
                switch (path.length) {
                    case 4: // 3M bit22
                        fp.set(21);
                        break;
                    case 5: // 4M bit11
                        fp.set(10);
                        break;
                    case 6: // 5M bit96
                        fp.set(95);
                        break;
                    case 7: // 6M bit163->bit145, bit124 numArom > 1

                        if (numArom < 2) {
                            if (isAromPath(path, bmap)) {
                                numArom++;
                                if (numArom == 2)
                                    fp.set(124);
                            }
                        }

                        if (fp.get(162)) {
                            fp.set(144); // >0
                        } else {
                            fp.set(162); // >1
                        }
                        break;
                    case 8: // 7M bit19
                        fp.set(18);
                        break;
                    case 9: // 8M bit101
                        fp.set(100);
                        break;
                }
            }
        }

        return new BitSetFingerprint(fp);
    }

    public  MACCSFingerprinter();
    public  MACCSFingerprinter(IChemObjectBuilder builder);

}