spade.storage.CompressedTextFile.java Source code

Java tutorial

Introduction

Here is the source code for spade.storage.CompressedTextFile.java

Source

/*
 --------------------------------------------------------------------------------
 SPADE - Support for Provenance Auditing in Distributed Environments.
 Copyright (C) 2015 SRI International
    
 This program is free software: you can redistribute it and/or
 modify it under the terms of the GNU General Public License as
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.
    
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 General Public License for more details.
    
 You should have received a copy of the GNU General Public License
 along with this program. If not, see <http://www.gnu.org/licenses/>.
 --------------------------------------------------------------------------------
 */
package spade.storage;

import org.apache.commons.codec.digest.DigestUtils;

import com.sleepycat.je.utilint.Pair;

import spade.core.AbstractEdge;
import spade.core.AbstractStorage;
import spade.core.AbstractVertex;
import spade.core.Graph;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.Deflater;

/**
 * A storage implementation that simply outputs plain text to a file.
 * @author Armando Caro
 */
public class CompressedTextFile extends AbstractStorage {

    private static FileWriter scaffoldWriter;
    private static FileWriter annotationsWriter;
    private String filePath;
    public static Integer W;
    public static Integer L;
    static Deflater compresser;
    static Integer nextVertexID;
    static Vector<String> alreadyRenamed;
    static Map<String, Integer> hashToID;
    static Integer edgesInMemory;
    static final Integer maxEdgesInMemory = 10;
    Map<Integer, Pair<SortedSet<Integer>, SortedSet<Integer>>> scaffoldInMemory;
    private static final Logger logger = Logger.getLogger(CompressedStorage.class.getName());
    long clock;
    PrintWriter benchmarks;
    File annotationsFile;
    File scaffoldFile;
    Scanner annotationsScanner;
    Scanner scaffoldScanner;

    @Override
    public boolean initialize(String arguments) {
        clock = System.currentTimeMillis();
        scaffoldInMemory = new HashMap<Integer, Pair<SortedSet<Integer>, SortedSet<Integer>>>();
        edgesInMemory = 0;
        hashToID = new HashMap<String, Integer>();
        alreadyRenamed = new Vector<String>();
        compresser = new Deflater(Deflater.BEST_COMPRESSION);
        W = 10;
        L = 5;
        nextVertexID = 0;
        try {
            if (arguments == null) {
                return false;
            }
            annotationsFile = new File(filePath + "/annotations.txt");
            scaffoldFile = new File(filePath + "/scaffold.txt");
            annotationsScanner = new Scanner(annotationsFile);
            scaffoldScanner = new Scanner(scaffoldFile);
            benchmarks = new PrintWriter("/Users/melanie/Documents/benchmarks/compression_time_TextFile.txt",
                    "UTF-8");
            filePath = arguments;
            scaffoldWriter = new FileWriter(filePath + "/scaffold.txt", false);
            scaffoldWriter.write("[BEGIN]\n");
            annotationsWriter = new FileWriter(filePath + "/annotations.txt", false);
            annotationsWriter.write("[BEGIN]\n");

            return true;
        } catch (Exception ex) {
            logger.log(Level.SEVERE, "Compressed Storage Initialized not successful!", ex);
            return false;
        }
    }

    @Override
    public boolean putVertex(AbstractVertex incomingVertex) {
        try {
            String vertexHash = DigestUtils.sha256Hex(incomingVertex.toString());
            Integer vertexId = nextVertexID;
            nextVertexID++;
            hashToID.put(vertexHash, vertexId);
            StringBuilder annotationString = new StringBuilder();
            //annotationString.append("VERTEX (" + vertexId + "): {");
            for (Map.Entry<String, String> currentEntry : incomingVertex.getAnnotations().entrySet()) {
                String key = currentEntry.getKey();
                String value = currentEntry.getValue();
                if (key == null || value == null) {
                    continue;
                }
                annotationString.append(key);
                annotationString.append(":");
                annotationString.append(value);
                annotationString.append(",");
            }
            //annotationString.append("}\n");
            String vertexString = annotationString.toString();
            //outputFile.write(vertexString);
            byte[] input = vertexString.getBytes("UTF-8");
            byte[] output = new byte[input.length + 100];
            compresser.setInput(input);
            compresser.finish();
            int compressedDataLength = compresser.deflate(output);
            put(annotationsWriter, vertexId, output);
            compresser.reset();
            return true;
        } catch (Exception exception) {
            Logger.getLogger(TextFile.class.getName()).log(Level.SEVERE, null, exception);
            return false;
        }
    }

    private void put(FileWriter writer, String key, byte[] output) {
        try {
            writer.write(key + " {" + new String(output) + "}" + '\n');
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void put(FileWriter writer, Integer vertexId, byte[] output) {
        put(writer, vertexId.toString(), output);
    }

    // generated by raza to fix things
    private static void put(FileWriter writer, Integer key, String output) {

        try {
            writer.write(key + " {" + new String(output) + "}" + '\n');
        } catch (IOException e) {

            e.printStackTrace();
        }
    }

    @Override
    public Object executeQuery(String query) {
        return null;
    }

    @Override
    public boolean putEdge(AbstractEdge incomingEdge) {
        try {
            String srcHash = DigestUtils.sha256Hex(incomingEdge.getChildVertex().toString());
            String dstHash = DigestUtils.sha256Hex(incomingEdge.getParentVertex().toString());
            Integer srcID = hashToID.get(srcHash);
            Integer dstID = hashToID.get(dstHash);
            StringBuilder annotationString = new StringBuilder();
            //annotationString.append("EDGE (" + srcID + " -> " + dstID + "): {");
            for (Map.Entry<String, String> currentEntry : incomingEdge.getAnnotations().entrySet()) {
                String key = currentEntry.getKey();
                String value = currentEntry.getValue();
                if (key == null || value == null) {
                    continue;
                }
                annotationString.append(key);
                annotationString.append(":");
                annotationString.append(value);
                annotationString.append(",");
            }
            //annotationString.append("}\n");
            //String edgeString = annotationString.toString();
            //annotationsWriter.write(edgeString);
            //annotationString.append("}\n");
            String edgeString = annotationString.toString();
            //outputFile.write(edgeString);
            byte[] input = edgeString.getBytes("UTF-8");
            byte[] output = new byte[input.length + 100];
            compresser.setInput(input);
            compresser.finish();
            int compressedDataLength = compresser.deflate(output);
            String key = srcID + "->" + dstID;
            put(annotationsWriter, key, output);
            compresser.reset();
            // scaffold storage
            //update scaffoldInMemory
            Pair<SortedSet<Integer>, SortedSet<Integer>> srcLists = scaffoldInMemory.get(srcID);
            if (srcLists == null) {
                srcLists = new Pair<SortedSet<Integer>, SortedSet<Integer>>(new TreeSet<Integer>(),
                        new TreeSet<Integer>());
            }
            srcLists.second().add(dstID);
            scaffoldInMemory.put(srcID, srcLists);
            Pair<SortedSet<Integer>, SortedSet<Integer>> dstLists = scaffoldInMemory.get(dstID);
            if (dstLists == null) {
                dstLists = new Pair<SortedSet<Integer>, SortedSet<Integer>>(new TreeSet<Integer>(),
                        new TreeSet<Integer>());
            }
            dstLists.first().add(dstID);
            scaffoldInMemory.put(dstID, dstLists);
            edgesInMemory++;
            if (edgesInMemory == maxEdgesInMemory) {
                updateAncestorsSuccessors(scaffoldInMemory);
                scaffoldInMemory.clear();
                edgesInMemory = 0;
                long aux = System.currentTimeMillis();
                benchmarks.println(aux - clock);
                clock = aux;
            }

            return true;
        } catch (Exception exception) {
            Logger.getLogger(TextFile.class.getName()).log(Level.SEVERE, null, exception);
            return false;
        }
    }

    @Override
    public boolean shutdown() {
        try {
            scaffoldWriter.write("[END]\n");
            scaffoldWriter.close();
            annotationsWriter.write("[END]\n");
            annotationsWriter.close();
            compresser.end();
            benchmarks.close();
            return true;
        } catch (Exception ex) {
            logger.log(Level.SEVERE, "Compressed TextFile Shutdown not successful!", ex);
            return false;
        }
    }

    /**
     * This function queries the underlying storage and retrieves the edge
     * matching the given criteria.
     *
     * @param childVertexHash  hash of the source vertex.
     * @param parentVertexHash hash of the destination vertex.
     * @return returns edge object matching the given vertices OR NULL.
     */
    @Override
    public AbstractEdge getEdge(String childVertexHash, String parentVertexHash) {
        return null;
    }

    /**
     * This function queries the underlying storage and retrieves the vertex
     * matching the given criteria.
     *
     * @param vertexHash hash of the vertex to find.
     * @return returns vertex object matching the given hash OR NULL.
     */
    @Override
    public AbstractVertex getVertex(String vertexHash) {
        return null;
    }

    /**
     * This function finds the children of a given vertex.
     * A child is defined as a vertex which is the source of a
     * direct edge between itself and the given vertex.
     *
     * @param parentHash hash of the given vertex
     * @return returns graph object containing children of the given vertex OR NULL.
     */
    @Override
    public Graph getChildren(String parentHash) {
        return null;
    }

    /**
     * This function finds the parents of a given vertex.
     * A parent is defined as a vertex which is the destination of a
     * direct edge between itself and the given vertex.
     *
     * @param childVertexHash hash of the given vertex
     * @return returns graph object containing parents of the given vertex OR NULL.
     */
    @Override
    public Graph getParents(String childVertexHash) {
        return null;
    }

    private static void updateAncestorsSuccessors(Map<Integer, Pair<SortedSet<Integer>, SortedSet<Integer>>> asl) {
        //for each line to update 
        Set<Map.Entry<Integer, Pair<SortedSet<Integer>, SortedSet<Integer>>>> entries = asl.entrySet();
        SortedSet<Integer> ancestors;
        SortedSet<Integer> successors;
        boolean updateAncestors;
        boolean updateSuccessors;
        HashMap<Integer, Pair<SortedSet<Integer>, SortedSet<Integer>>> toUpdate = new HashMap<Integer, Pair<SortedSet<Integer>, SortedSet<Integer>>>();
        //HashMap<Integer, Pair<SortedSet<Integer>, SortedSet<Integer>>> unchangedToUpdate = new HashMap<Integer, Pair<SortedSet<Integer>, SortedSet<Integer>>>();
        for (HashMap.Entry<Integer, Pair<SortedSet<Integer>, SortedSet<Integer>>> nodeToUpdate : entries) {
            Integer id = nodeToUpdate.getKey();
            Pair<Pair<Integer, SortedSet<Integer>>, Pair<Integer, SortedSet<Integer>>> lists = uncompressAncestorsSuccessorsWithLayer(
                    id, true, true);
            ancestors = lists.first().second();
            ancestors.addAll(nodeToUpdate.getValue().first());
            successors = lists.second().second();
            successors.addAll(nodeToUpdate.getValue().second());
            toUpdate.put(id, new Pair(ancestors, successors));

            //update other nodes but do not update the reference
            for (Integer nodeID = id + 1; nodeID < id + W + 1; nodeID++) {
                String line = get(scaffoldWriter, nodeID);
                if (line != null && line.contains("/")) {
                    // get reference and see if it is id.
                    String ancestorList = line.substring(line.indexOf(' ') + 1, line.indexOf("/") - 1);
                    boolean isReferenceAncestor;
                    if (ancestorList.contains("_")) {
                        isReferenceAncestor = false;
                    } else {
                        Integer referenceAncestor = Integer
                                .parseInt(ancestorList.substring(0, ancestorList.indexOf(' ')));
                        isReferenceAncestor = id.equals(nodeID + referenceAncestor);
                    }
                    String successorList = line.substring(line.indexOf("/") + 2);
                    successorList = successorList.substring(successorList.indexOf(' ') + 1);
                    boolean isReferenceSuccessor;
                    if (successorList.contains("_")) {
                        isReferenceSuccessor = false;
                    } else {
                        Integer referenceSuccessor = Integer
                                .parseInt(successorList.substring(0, successorList.indexOf(' ')));
                        isReferenceSuccessor = id.equals(nodeID + referenceSuccessor);
                    }
                    if (isReferenceAncestor || isReferenceSuccessor) {
                        //update the encoding of the line
                        Pair<Pair<Integer, SortedSet<Integer>>, Pair<Integer, SortedSet<Integer>>> aux = uncompressAncestorsSuccessorsWithLayer(
                                nodeID, true, true);
                        if (!toUpdate.containsKey(nodeID))
                            toUpdate.put(nodeID, new Pair(aux.first().second(), aux.second().second()));
                    }
                }
            }
        }
        Set<HashMap.Entry<Integer, Pair<SortedSet<Integer>, SortedSet<Integer>>>> entriesToUpdate = toUpdate
                .entrySet();
        for (HashMap.Entry<Integer, Pair<SortedSet<Integer>, SortedSet<Integer>>> nodeToUpdate : entriesToUpdate) {
            encodeAncestorsSuccessors(nodeToUpdate);
        }
    }

    private static String get(FileWriter scaffoldWriter2, Integer nodeID) {
        return null;
    }

    public static Pair<Pair<Integer, SortedSet<Integer>>, Pair<Integer, SortedSet<Integer>>> uncompressAncestorsSuccessorsWithLayer(
            Integer id, boolean uncompressAncestors, boolean uncompressSuccessors) {
        //System.out.println("step a");
        SortedSet<Integer> ancestors = new TreeSet<Integer>();
        SortedSet<Integer> successors = new TreeSet<Integer>();
        Integer ancestorLayer = 1;
        Integer successorLayer = 1;
        String aux = get(scaffoldWriter, id);
        //System.out.println("step b");
        if (aux != null && aux.contains("/")) {
            // split the line in two parts : ancestor list and successor list.
            String ancestorList = aux.substring(0, aux.indexOf('/'));
            String successorList = aux.substring(aux.indexOf('/') + 2);
            ancestorLayer = Integer.parseInt(ancestorList.substring(0, ancestorList.indexOf(' ')));
            successorLayer = Integer.parseInt(successorList.substring(0, successorList.indexOf(' ')));
            ancestorList = ancestorList.substring(ancestorList.indexOf(' ') + 1);
            successorList = successorList.substring(successorList.indexOf(' ') + 1);
            //System.out.println("step c");
            if (uncompressAncestors) { //uncompressAncestors
                if (ancestorList.contains("_")) { // means there is no reference
                    //System.out.println("step d");
                    String ancestorList2 = ancestorList.substring(ancestorList.indexOf("_") + 1);
                    ancestors.addAll(uncompressRemainingNodes(id, ancestorList2));
                } else { // there is a reference that we have to uncompress
                    // uncompress the remaining Nodes
                    //System.out.println("step e");
                    String remaining = ancestorList.substring(ancestorList.indexOf(" ") + 1);
                    remaining = remaining.substring(remaining.indexOf(" ") + 1);
                    ancestors.addAll(uncompressRemainingNodes(id, remaining));
                    //uncompress the reference and its reference after that
                    try {
                        ancestors.addAll(uncompressReference(id, ancestorList, true));
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                }
            }
            //System.out.println("step f");
            if (uncompressSuccessors) { // uncompressSuccessors
                if (successorList.contains("_")) { // means there is no reference
                    //System.out.println("step g " );
                    String successorList2 = successorList.substring(successorList.indexOf("_") + 1);
                    successors.addAll(uncompressRemainingNodes(id, successorList2));
                } else { // there is a reference that we have to uncompress
                    // uncompress the remaining Nodes
                    //System.out.println("step h");
                    /*         String remaining = successorList.substring(successorList.indexOf(" ")+1);
                             remaining = remaining.substring(remaining.indexOf(" ")+1);
                             System.out.println("remaining" +remaining);
                             //System.out.println("step i ");
                             successors.addAll(uncompressRemainingNodes(id, remaining));*/
                    //uncompress the reference and its reference after that
                    try {
                        //System.out.println("step j ");
                        successors.addAll(uncompressReference(id, successorList, false));
                        //System.out.println("step k");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        //System.out.println("step k");
        Pair<Integer, SortedSet<Integer>> ancestorsAndLayer = new Pair<Integer, SortedSet<Integer>>(ancestorLayer,
                ancestors);
        Pair<Integer, SortedSet<Integer>> successorsAndLayer = new Pair<Integer, SortedSet<Integer>>(successorLayer,
                successors);
        Pair<Pair<Integer, SortedSet<Integer>>, Pair<Integer, SortedSet<Integer>>> ancestorsAndSuccessors = new Pair<Pair<Integer, SortedSet<Integer>>, Pair<Integer, SortedSet<Integer>>>(
                ancestorsAndLayer, successorsAndLayer);
        return ancestorsAndSuccessors;
    }

    public static SortedSet<Integer> uncompressRemainingNodes(Integer nodeID, String remainingNodes) {
        //System.out.println("remainingNodes :" + remainingNodes);
        SortedSet<Integer> successors = new TreeSet<Integer>();
        Integer currentID = nodeID;
        String uncompressing;
        int length;
        StringTokenizer st = new StringTokenizer(remainingNodes);
        while (st.hasMoreTokens()) {
            uncompressing = st.nextToken();
            if (uncompressing.contains(":")) { //uncompress consecutive numbers
                if (uncompressing.charAt(0) != ':') {
                    currentID += Integer.parseInt(uncompressing.substring(0, uncompressing.indexOf(':')));
                    successors.add(currentID);
                }
                length = Integer.parseInt(uncompressing.substring(uncompressing.indexOf(':') + 1));
                for (int i = 0; i < length; i++) {
                    currentID++;
                    successors.add(currentID);
                }
            } else {
                currentID = currentID + Integer.parseInt(uncompressing);
                successors.add(currentID);
            }
        }
        //System.out.println("uncompressRemainingNodes : " + successors.toString());
        return successors;
    }

    private static SortedSet<Integer> uncompressReference(Integer id, String ancestorOrSuccessorList,
            boolean ancestorOrSuccessor) throws UnsupportedEncodingException {
        //System.out.println("step m");
        SortedSet<Integer> list = new TreeSet<Integer>();
        StringTokenizer st = new StringTokenizer(ancestorOrSuccessorList);
        //System.out.println("ancestorOrSuccessorList :" + ancestorOrSuccessorList + " /id :" + id);
        //st.nextToken();
        String token = st.nextToken();
        //System.out.println("step n");
        Integer referenceID = id + Integer.parseInt(token);
        //System.out.println("referenceID1: " + referenceID);
        LinkedList<String> previousLayers = new LinkedList<String>();
        previousLayers.addFirst(id + " " + ancestorOrSuccessorList);
        String toUncompress;
        //System.out.println("step o");
        boolean hasReference = true;
        //System.out.println(toUncompress);
        //System.out.println(ancestorOrSuccessorList);
        while (hasReference) {
            //System.out.println("step p");
            String currentLine = get(scaffoldWriter, referenceID);
            if (currentLine.length() > 0) {

                if (ancestorOrSuccessor) { // we want to uncompress ancestors
                    toUncompress = currentLine.substring(currentLine.indexOf(' ') + 1,
                            currentLine.indexOf("/") - 1);
                } else { // we want to uncompress successors
                    toUncompress = currentLine.substring(currentLine.indexOf("/") + 2);
                    toUncompress = toUncompress.substring(toUncompress.indexOf(' ') + 1);
                }
                //System.out.println("step q");
                //System.out.println("toUncompress:" + toUncompress);
                // System.out.println(toUncompress);
                toUncompress = referenceID + " " + toUncompress;
                previousLayers.addFirst(toUncompress);
                //System.out.println("step r");
                if (toUncompress.contains(" _ ")) { // this is the last layer
                    hasReference = false;
                } else { // we need to go one layer further to uncompress the successors
                    String aux = toUncompress.substring(toUncompress.indexOf(" ") + 1);
                    //System.out.println("toUncompress:" + toUncompress);
                    referenceID = referenceID + Integer.parseInt(aux.substring(0, aux.indexOf(" ")));
                    // System.out.println("referenceID : " + referenceID);
                    //System.out.println("step s");
                }
            } else {
                System.out.println("Data missing.");
                hasReference = false;
            } //System.out.println("step t");
        }

        // System.out.println("previousLayers: " + previousLayers.toString());
        String bitListLayer;
        String remainingNodesLayer;
        Integer layerID;
        for (String layer : previousLayers) { //find the successors of the first layer and then those of the second layer and so on...
            layerID = Integer.parseInt(layer.substring(0, layer.indexOf(" ")));
            //System.out.println("step u");
            if (layer.contains("_ ")) { //this is the case for the first layer only
                remainingNodesLayer = layer.substring(layer.indexOf("_ ") + 2);
                //System.out.println("step v");
            } else {
                // uncompress the bitlist
                remainingNodesLayer = layer.substring(layer.indexOf(" ") + 1);
                //System.out.println("remaining Nodes Layer 1: " + remainingNodesLayer);
                //// System.out.println("step 1 :" + remainingNodesLayer + "/");
                remainingNodesLayer = remainingNodesLayer.substring(remainingNodesLayer.indexOf(" ") + 1);
                //remainingNodesLayer = remainingNodesLayer.substring(remainingNodesLayer.indexOf(" ") + 1);
                //// System.out.println("step 2 :" + remainingNodesLayer + "/");
                //System.out.println("step w");
                //System.out.println("remaining Nodes Layer " + remainingNodesLayer);
                if (remainingNodesLayer.contains(" ")) {
                    bitListLayer = remainingNodesLayer.substring(0, remainingNodesLayer.indexOf(" "));
                    remainingNodesLayer = remainingNodesLayer.substring(remainingNodesLayer.indexOf(" ") + 1);
                } else {

                    bitListLayer = remainingNodesLayer.substring(0);
                    remainingNodesLayer = "";
                }
                //System.out.println("bitListLayer :" + bitListLayer + "/");
                int count = 0;
                SortedSet<Integer> list2 = new TreeSet<Integer>();
                list2.addAll(list);
                //   System.out.println("step x");
                //System.out.println(bitListLayer);
                for (Integer successor : list2) {
                    //System.out.println(successor + " " + count);
                    if (bitListLayer.charAt(count) == '0') {
                        list.remove(successor);
                        //System.out.println("step y");
                    }
                    count++;
                }
            }
            // uncompress remaining nodes
            list.addAll(uncompressRemainingNodes(layerID, remainingNodesLayer));
            //System.out.println("step z");
        }
        //System.out.println("uncompressReference : " + list.toString() + "id : " + id);
        return list;
    }

    public static boolean encodeAncestorsSuccessors(
            HashMap.Entry<Integer, Pair<SortedSet<Integer>, SortedSet<Integer>>> nodeToCompress) {
        //find reference node
        Integer id = nodeToCompress.getKey();
        //System.out.println(id);
        SortedSet<Integer> ancestors = nodeToCompress.getValue().first();
        SortedSet<Integer> successors = nodeToCompress.getValue().second();
        //NodeLayerAncestorSuccessor currentNode = new NodeLayerAncestorSuccessor(id, ancestors, successors);
        Pair<Integer, Integer> maxNodesInCommonAncestor = new Pair<Integer, Integer>(0, 0);
        Pair<Integer, Integer> maxNodesInCommonSuccessor = new Pair<Integer, Integer>(0, 0); //first integer is the max of nodes in common, the second one is the number of 0 in the corresponding bit list
        //NodeLayerAncestorSuccessor referenceAncestor = currentNode;
        String bitlistAncestor = "";
        int layerAncestor = 1;
        Integer referenceAncestor = -1;
        SortedSet<Integer> referenceAncestorList = new TreeSet<Integer>();
        //NodeLayerAncestorSuccessor referenceSuccessor = currentNode;
        String bitlistSuccessor = "";
        int layerSuccessor = 1;
        Integer referenceSuccessor = -1;
        SortedSet<Integer> referenceSuccessorList = new TreeSet<Integer>();
        //Iterator<NodeLayerAncestorSuccessor> iteratorPossibleReference = lastNodesSeen.iterator(); 
        //while (iteratorPossibleReference.hasNext()){
        //System.out.println("step 1");
        for (Integer possibleReferenceID = 1; possibleReferenceID < id; possibleReferenceID++) {
            //for each node in the W last nodes seen, compute the proximity, i.e. the number of successors of the current node that also are successors of the possibleReference node.
            Pair<Pair<Integer, SortedSet<Integer>>, Pair<Integer, SortedSet<Integer>>> asl = uncompressAncestorsSuccessorsWithLayer(
                    possibleReferenceID, true, true);
            if (asl.first().first() < L) {
                //System.out.println("step 1.1");
                Pair<Pair<Integer, Integer>, String> nodesInCommonAncestor = commonNodes(asl.first().second(),
                        ancestors);
                int numberOfOneAncestor = nodesInCommonAncestor.first().first();
                int numberOfZeroAncestor = nodesInCommonAncestor.first().second();
                int maxNumberOfOneAncestor = maxNodesInCommonAncestor.first();
                int maxNumberOfZeroAncestor = maxNodesInCommonAncestor.second();
                //System.out.println("step 2");
                if (numberOfOneAncestor > maxNumberOfOneAncestor || (numberOfOneAncestor == maxNumberOfOneAncestor
                        && numberOfZeroAncestor < maxNumberOfZeroAncestor)) {
                    maxNodesInCommonAncestor = nodesInCommonAncestor.first();
                    bitlistAncestor = nodesInCommonAncestor.second();
                    referenceAncestor = possibleReferenceID;
                    layerAncestor = asl.first().first() + 1;
                    referenceAncestorList = asl.first().second();
                    //System.out.println("step 3");
                }
            }
            //System.out.println("step 4");
            if (asl.second().first() < L) {
                //System.out.println("step 4.1");
                Pair<Pair<Integer, Integer>, String> nodesInCommonSuccessor = commonNodes(asl.second().second(),
                        successors);
                int numberOfOneSuccessor = nodesInCommonSuccessor.first().first();
                int numberOfZeroSuccessor = nodesInCommonSuccessor.first().second();
                int maxNumberOfOneSuccessor = maxNodesInCommonSuccessor.first();
                int maxNumberOfZeroSuccessor = maxNodesInCommonSuccessor.second();
                if (numberOfOneSuccessor > maxNumberOfOneSuccessor
                        || (numberOfOneSuccessor == maxNumberOfOneSuccessor
                                && numberOfZeroSuccessor < maxNumberOfZeroSuccessor)) {
                    maxNodesInCommonSuccessor = nodesInCommonSuccessor.first();
                    bitlistSuccessor = nodesInCommonSuccessor.second();
                    referenceSuccessor = possibleReferenceID;
                    layerSuccessor = asl.second().first() + 1;
                    referenceSuccessorList = asl.second().second();
                }
            }
            //System.out.println("step 5");
        }
        //System.out.println("step 6");

        //encode ancestor list
        SortedSet<Integer> remainingNodesAncestor = new TreeSet<Integer>();
        remainingNodesAncestor.addAll(ancestors);
        //encode reference
        //String encoding = id.toString() + " ";
        String encoding = layerAncestor + " ";
        if (maxNodesInCommonAncestor.first() > 0) {
            encoding = encoding + (referenceAncestor - id) + " " + bitlistAncestor + "";
            //keep only remaining nodes
            remainingNodesAncestor.removeAll(referenceAncestorList);
        } else {
            encoding = encoding + "_";
        }

        //encode consecutive nodes and delta encoding
        Integer previousNode = id;
        int countConsecutives = 0;
        for (Integer nodeID : remainingNodesAncestor) {
            Integer delta = nodeID - previousNode;
            if (delta == 1) {
                countConsecutives++;
            } else {

                if (countConsecutives > 0) {
                    encoding = encoding + ":" + countConsecutives;
                    countConsecutives = 1;
                }
                encoding = encoding + " " + delta;
                countConsecutives = 0;
            }
            previousNode = nodeID;

        }
        // encode successor list
        SortedSet<Integer> remainingNodesSuccessor = new TreeSet<Integer>();
        remainingNodesSuccessor.addAll(successors);
        //encode reference
        encoding = encoding + " / " + layerSuccessor + " ";
        if (maxNodesInCommonSuccessor.first() > 0) {
            encoding = encoding + (referenceSuccessor - id) + " " + bitlistSuccessor + "";
            //keep only remaining nodes
            remainingNodesSuccessor.removeAll(referenceSuccessorList);
        } else {
            encoding = encoding + "_ ";
        }

        //encode consecutive nodes and delta encoding
        previousNode = id;
        countConsecutives = 0;
        for (Integer nodeID : remainingNodesSuccessor) {
            Integer delta = nodeID - previousNode;
            if (delta == 1) {
                countConsecutives++;
            } else {

                if (countConsecutives > 0) {

                    encoding = encoding + ":" + countConsecutives;
                    countConsecutives = 1;
                }
                encoding = encoding + " " + delta;
                countConsecutives = 0;
            }
            previousNode = nodeID;

        }
        put(scaffoldWriter, id, encoding);
        //System.out.println(id + " " + encoding);
        return true;
    }

    public static Pair<Pair<Integer, Integer>, String> commonNodes(SortedSet<Integer> reference,
            SortedSet<Integer> node) {
        int nodesInCommon = 0;
        int numberOfZero = 0;
        String bitlist = "";
        for (Integer successor : reference) {
            if (node.contains(successor)) {
                nodesInCommon++;
                bitlist = bitlist + "1";
            } else {
                numberOfZero++;
                bitlist = bitlist + "0";
            }
        }
        Pair<Integer, Integer> count = new Pair<Integer, Integer>(nodesInCommon, numberOfZero);
        return new Pair<Pair<Integer, Integer>, String>(count, bitlist);
    }

}