public static void toCRAWDAD(LinkTrace links, OutputStream out, double timeMul)
      throws IOException {

    StatefulReader<LinkEvent, Link> linkReader = links.getReader();
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
    Map<Link, Long> activeContacts = new AdjacencyMap.Links<Long>();

    linkReader.seek(links.minTime());
    for (Link l : linkReader.referenceState()) activeContacts.put(l, links.minTime());
    while (linkReader.hasNext()) {
      for (LinkEvent lev : linkReader.next()) {
        Link l = lev.link();
        if (lev.isUp()) {
          activeContacts.put(l, linkReader.time());
        } else {
          double b = activeContacts.get(l) * timeMul;
          double e = linkReader.time() * timeMul;
          activeContacts.remove(l);
          bw.write(l.id1() + "\t" + l.id2() + "\t" + b + "\t" + e + "\n");
        }
      }
    }
    linkReader.close();
    bw.close();
  }
  public static void fromCRAWDAD(
      LinkTrace links,
      InputStream in,
      double timeMul,
      long ticsPerSecond,
      long offset,
      IdGenerator idGen)
      throws IOException {

    StatefulWriter<LinkEvent, Link> linkWriter = links.getWriter();
    BufferedReader br = new BufferedReader(new InputStreamReader(in));
    String line;

    while ((line = br.readLine()) != null) {
      String[] elems = line.split("[ \t]+");
      Integer id1 = idGen.getInternalId(elems[0]);
      Integer id2 = idGen.getInternalId(elems[1]);
      long begin = (long) (Long.parseLong(elems[2]) * timeMul) + offset;
      long end = (long) (Long.parseLong(elems[3]) * timeMul) + offset;
      linkWriter.queue(begin, new LinkEvent(id1, id2, LinkEvent.UP));
      linkWriter.queue(end, new LinkEvent(id1, id2, LinkEvent.DOWN));
    }
    linkWriter.flush();
    linkWriter.setProperty(Trace.timeUnitKey, Units.toTimeUnit(ticsPerSecond));
    idGen.writeTraceInfo(linkWriter);
    linkWriter.close();
    br.close();
  }