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();
  }
  @Override
  public void convert() throws IOException {
    final AdjacencyMap.Arcs<ArcEvent> to_bring_down = new AdjacencyMap.Arcs<ArcEvent>();

    final StatefulWriter<ArcEvent, Arc> upper_writer = _upper.getWriter();
    final StatefulReader<ArcEvent, Arc> lower_reader = _lower.getReader();

    lower_reader.seek(_lower.minTime());
    upper_writer.setInitState(_lower.minTime(), lower_reader.referenceState());
    long last_time = _lower.minTime();
    long time = last_time;
    final long eta = _lower.eta();

    while (lower_reader.hasNext()) {
      time = lower_reader.nextTime();
      if (time > last_time + eta) {
        for (final ArcEvent aev : to_bring_down.values()) upper_writer.queue(last_time, aev);
        to_bring_down.clear();
      }
      boolean first_down = true;
      for (final ArcEvent aev : lower_reader.next()) { // assumes that all
        // UP event come
        // before all DOWN
        // events
        final Arc a = aev.arc();
        if (aev.isUp()) {
          if (to_bring_down.containsKey(a)) to_bring_down.remove(a);
          else upper_writer.queue(time, aev);
        } else { // down event. Just queue for next time step
          if (first_down) { // first flush previous time's down events
            for (final ArcEvent dev : to_bring_down.values()) upper_writer.queue(last_time, dev);
            to_bring_down.clear();
            first_down = false;
          }
          to_bring_down.put(a, aev);
        }
      }
      upper_writer.flush(last_time);
      last_time = time;
    }

    // flush final events
    for (final ArcEvent dev : to_bring_down.values()) upper_writer.queue(last_time, dev);
    upper_writer.flush();

    upper_writer.setPropertiesFromTrace(_lower);
    _lower.copyOverTraceInfo(upper_writer);
    upper_writer.close();
    lower_reader.close();
  }