All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aya.util.terck.MutableGraph Maven / Gradle / Ivy

There is a newer version: 0.36.0
Show newest version
// Copyright (c) 2020-2023 Tesla (Yinsen) Zhang.
// Use of this source code is governed by the MIT license that can be found in the LICENSE.md file.
package org.aya.util.terck;

import kala.collection.SeqView;
import kala.collection.immutable.ImmutableSeq;
import kala.collection.mutable.*;
import org.jetbrains.annotations.NotNull;

public record MutableGraph(@NotNull MutableMap> E) {
  public static @NotNull  MutableGraph create() {
    return new MutableGraph<>(MutableLinkedHashMap.of());
  }

  public @NotNull MutableList sucMut(@NotNull T elem) {
    return E.getOrPut(elem, MutableList::of);
  }

  public @NotNull SeqView suc(@NotNull T elem) {
    var suc = E.getOrNull(elem);
    return suc == null ? SeqView.empty() : suc.view();
  }

  public boolean hasPath(@NotNull T from, @NotNull T to) {
    return hasPath(MutableSet.create(), from, to);
  }

  private boolean hasPath(@NotNull MutableSet book, @NotNull T from, @NotNull T to) {
    if (from == to) return true;
    if (book.contains(from)) return false;
    book.add(from);
    for (var test : suc(from)) if (hasPath(book, test, to)) return true;
    return false;
  }

  public @NotNull ImmutableSeq> findCycles() {
    return topologicalOrder().filter(scc -> scc.sizeGreaterThan(1));
  }

  /**
   * Returns a topological order of the graph
   * whose edge (v, w) means v depends on w.
   */
  public ImmutableSeq> topologicalOrder() {
    return new Tarjan().tarjan();
  }

  public @NotNull MutableGraph transpose() {
    var tr = MutableGraph.create();
    E.forEach((v, ws) -> {
      tr.sucMut(v);
      ws.forEach(w -> tr.sucMut(w).append(v));
    });
    return tr;
  }

  private static class Info {
    static final int INDEX_NONE = Integer.MAX_VALUE;
    int index = INDEX_NONE;
    int lowlink = INDEX_NONE;
    boolean free = false;

    boolean noIndex() {
      return index == INDEX_NONE;
    }
  }

  /**
   * Find strongly connected components in a graph,
   * and return the topological order (need reversing) of the components.
   */
  private class Tarjan {
    final MutableMap info = MutableLinkedHashMap.of();
    final MutableSinglyLinkedList stack = MutableSinglyLinkedList.create();
    final MutableList> SCCs = MutableList.create();
    int index = 0;

    private @NotNull Info info(@NotNull T t) {
      return info.getOrPut(t, Info::new);
    }

    private void push(@NotNull T v) {
      stack.push(v);
      info(v).free = true;
    }

    private @NotNull T pop() {
      var v = stack.pop();
      info(v).free = false;
      return v;
    }

    public void makeSCC(@NotNull T v) {
      var infoV = info(v);
      infoV.index = infoV.lowlink = index++;
      push(v);

      suc(v).forEach(w -> {
        var infoW = info(w);
        if (infoW.noIndex()) {
          makeSCC(w);
          infoV.lowlink = Math.min(infoV.lowlink, infoW.lowlink);
        } else if (infoW.free) {
          // successor `w` is in stack and free, so it is in the current SCC.
          // If `w` is not free, then (v, w) is an edge pointing to an SCC already found,
          // we must ignore `w` or we will get more than one `w` in topological order.
          infoV.lowlink = Math.min(infoV.lowlink, infoW.index);
        }
      });

      // If v is a root node, pop the stack and generate an SCC
      if (infoV.lowlink == infoV.index) {
        var scc = MutableList.create();
        T t = null;
        while (v != t) {
          t = pop();
          scc.append(t);
        }
        SCCs.append(scc.toImmutableSeq());
      }
    }

    public ImmutableSeq> tarjan() {
      // view should be lazy or this code will blow up
      E.keysView().filter(v -> info(v).noIndex()).forEach(this::makeSCC);
      return SCCs.toImmutableSeq();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy