public synchronized void updateCategoryTrend(long catId) {
    String trendStr = "trend_unknown";
    float stdDev = 0.0f;
    float lastTrend = 0.0f;
    float newTrend = 0.0f;

    gatherLatestDatapointsLocking(catId, mHistory);
    TimeSeries ts = getSeriesByIdLocking(catId);
    if (ts == null) return;

    if (ts.getDbRow().getSynthetic() == true) return;

    lastTrend = ts.getTrendStats().mTrendPrev;
    newTrend = ts.getTrendStats().mTrend;
    stdDev = ts.getValueStats().mStdDev;

    TrendState state =
        Number.getTrendState(lastTrend, newTrend, ts.getDbRow().getGoal(), mSensitivity, stdDev);
    trendStr = Number.mapTrendStateToString(state);

    mDbh.updateCategoryTrend(catId, trendStr, newTrend);

    if (ts.getDependees() != null && ts.getDependees().size() > 0) {
      for (int i = 0; i < ts.getDependees().size(); i++) {
        TimeSeries dependee = ts.getDependees().get(i);

        for (int j = 0; j < dependee.getDependents().size(); j++) {
          TimeSeries tmp = dependee.getDependents().get(j);
          if (tmp != null) gatherLatestDatapointsLocking(tmp.getDbRow().getId(), mHistory);
        }

        Formula formula = mFormulaCache.getFormula(dependee.getDbRow().getId());
        ArrayList<Datapoint> calculated = formula.apply(dependee.getDependents());
        dependee.setDatapoints(null, calculated, null, true);

        lastTrend = dependee.getTrendStats().mTrendPrev;
        newTrend = dependee.getTrendStats().mTrend;
        stdDev = dependee.getValueStats().mStdDev;

        state =
            Number.getTrendState(
                lastTrend, newTrend, dependee.getDbRow().getGoal(), mSensitivity, stdDev);
        trendStr = Number.mapTrendStateToString(state);

        mDbh.updateCategoryTrend(dependee.getDbRow().getId(), trendStr, newTrend);
      }
    }
  }
  private void generateSynthetic(TimeSeries synth) {
    Formula formula = mFormulaCache.getFormula(synth.getDbRow().getId());

    long ms;
    long firstVisibleMs = Long.MAX_VALUE;
    long lastVisibleMs = Long.MIN_VALUE;

    for (int j = 0; j < synth.getDependents().size(); j++) {
      TimeSeries ts = synth.getDependents().get(j);
      List<Datapoint> range = ts.getVisible();

      if (range != null) {
        ms = range.get(0).mMillis;
        if (ms < firstVisibleMs) firstVisibleMs = ms;
        ms = range.get(range.size() - 1).mMillis;
        if (ms > lastVisibleMs) lastVisibleMs = ms;
      }
    }

    ArrayList<Datapoint> calculated = formula.apply(synth.getDependents());
    ArrayList<Datapoint> pre = new ArrayList<Datapoint>();
    ArrayList<Datapoint> visible = new ArrayList<Datapoint>();
    ArrayList<Datapoint> post = new ArrayList<Datapoint>();

    for (int j = 0; j < calculated.size(); j++) {
      Datapoint d = calculated.get(j);
      d.mCatId = synth.getDbRow().getId();
      d.mSynthetic = true;
      if (d.mMillis < firstVisibleMs) pre.add(d);
      else if (d.mMillis <= lastVisibleMs) visible.add(d);
      else post.add(d);
    }

    pre = aggregateDatapoints(pre, synth.getDbRow().getType());
    visible = aggregateDatapoints(visible, synth.getDbRow().getType());
    post = aggregateDatapoints(post, synth.getDbRow().getType());

    synth.setDatapoints(pre, visible, post, true);
  }