Bron–Kerbosch algorithm

Develop a program that identifies and lists all maximal cliques within an undirected graph. A maximal clique is a subset of vertices where every two distinct vertices are adjacent (i.e., there's an edge connecting them), and the clique cannot be extended by including any adjacent vertex not already in the clique.

Task
Bron–Kerbosch algorithm
You are encouraged to solve this task according to the task description, using any language you may know.
Objective
Background

In graph theory, a clique is a subset of vertices of an undirected graph such that every two distinct vertices in the clique are connected by an edge. A maximal clique is a clique that cannot be extended by adding one more adjacent vertex, meaning it's not a subset of a larger clique.

The Bron-Kerbosch algorithm is a recursive backtracking algorithm used to find all maximal cliques in an undirected graph efficiently. The algorithm is particularly effective due to its use of pivot selection, which reduces the number of recursive calls and improves performance.

Program Functionality
  1. Input Representation:
  • The graph is represented as a list of edges, where each edge is a tuple of two vertices (e.g., `('a', 'b')` indicates an undirected edge between vertices `'a'` and `'b'`).
  1. Graph Construction:
  • Convert the list of edges into an adjacency list using a `HashMap<String, HashSet<String>>`. Each key in the hashmap represents a vertex, and its corresponding hash set contains all adjacent vertices (neighbors).
  1. Bron-Kerbosch Algorithm Implementation:
  • Sets Used:
    • R (Current Clique): A set representing the current clique being constructed.
    • P (Potential Candidates): A set of vertices that can be added to R to form a larger clique.
    • X (Excluded Vertices): A set of vertices that have already been processed and should not be reconsidered for the current clique.
  • Algorithm Steps:
    • If both P and X are empty, and R contains more than two vertices, R is a maximal clique and is added to the list of cliques.
    • Otherwise, select a pivot vertex from the union of P and X that has the maximum number of neighbors. This pivot helps minimize the number of recursive calls.
    • Iterate over all vertices in P that are **not** neighbors of the pivot. For each such vertex:
      • Add the vertex to R.
      • Update P and X to include only those vertices that are neighbors of the added vertex.
      • Recursively call the Bron-Kerbosch function with the updated sets.
      • After recursion, move the vertex from P to X to mark it as processed.
  1. Output:
  • After executing the algorithm, the program prints all maximal cliques with more than two vertices. Each clique is displayed as a comma-separated list of its constituent vertices.
Example

Given the following edges:

('a', 'b'), ('b', 'a'), ('a', 'c'), ('c', 'a'), ('b', 'c'), ('c', 'b'),
('d', 'e'), ('e', 'd'), ('d', 'f'), ('f', 'd'), ('e', 'f'), ('f', 'e')

Expected Output:

a, b, c
d, e, f

These outputs represent the two maximal cliques in the graph: one comprising vertices `a`, `b`, and `c`, and the other comprising vertices `d`, `e`, and `f`.

Pseudocode

Below is the pseudocode for the Bron-Kerbosch algorithm with pivoting, closely following the Rust implementation provided earlier.

FUNCTION BronKerbosch(R, P, X, G, Cliques)
    // R: Current clique (Set)
    // P: Potential candidates to expand the clique (Set)
    // X: Vertices already processed (Set)
    // G: Graph represented as an adjacency list (Dictionary)
    // Cliques: List to store all maximal cliques (List of Lists)

    IF P is empty AND X is empty THEN
        IF size of R > 2 THEN
            SORT R lexicographically
            ADD R to Cliques
        END IF
        RETURN
    END IF

    // Select a pivot vertex from P ∪ X with the maximum degree
    Pivot := vertex in (P ∪ X) with the highest number of neighbors in G

    // Candidates are vertices in P that are not neighbors of the pivot
    Candidates := P - Neighbors(Pivot)

    FOR EACH vertex v IN Candidates DO
        // New clique including vertex v
        NewR := R ∪ {v}

        // New potential candidates are neighbors of v in P
        Neighbors_v := G[v]
        NewP := P ∩ Neighbors_v

        // New excluded vertices are neighbors of v in X
        NewX := X ∩ Neighbors_v

        // Recursive call with updated sets
        BronKerbosch(NewR, NewP, NewX, G, Cliques)

        // Move vertex v from P to X
        P := P - {v}
        X := X ∪ {v}
    END FOR
END FUNCTION

FUNCTION Main()
    // Define input edges as a list of tuples
    Input := [
        ('a', 'b'), ('b', 'a'), ('a', 'c'), ('c', 'a'),
        ('b', 'c'), ('c', 'b'), ('d', 'e'), ('e', 'd'),
        ('d', 'f'), ('f', 'd'), ('e', 'f'), ('f', 'e')
    ]

    // Build the graph as an adjacency list
    Graph := empty Dictionary
    FOR EACH (src, dest) IN Input DO
        IF src NOT IN Graph THEN
            Graph[src] := empty Set
        END IF
        ADD dest TO Graph[src]
    END FOR

    // Initialize R, P, X
    R := empty Set
    P := set of all vertices in Graph
    X := empty Set

    // Initialize list to store cliques
    Cliques := empty List

    // Execute the Bron-Kerbosch algorithm
    BronKerbosch(R, P, X, Graph, Cliques)

    // Sort the cliques for consistent output
    SORT Cliques lexicographically

    // Print each clique
    FOR EACH Clique IN Cliques DO
        PRINT elements of Clique separated by commas
    END FOR
END FUNCTION


Works with: C# version .NET 9
Translation of: Java
using System;
using System.Collections.Generic;
using System.Linq;

public class BronKerboschAlgorithm
{
    private static List<List<string>> cliques = new List<List<string>>();

    public static void Main(string[] args)
    {
        var edges = new List<Edge>
        {
            new Edge("a", "b"), new Edge("b", "a"),
            new Edge("a", "c"), new Edge("c", "a"),
            new Edge("b", "c"), new Edge("c", "b"),
            new Edge("d", "e"), new Edge("e", "d"),
            new Edge("d", "f"), new Edge("f", "d"),
            new Edge("e", "f"), new Edge("f", "e")
        };

        // Build the graph as an adjacency list
        var graph = new Dictionary<string, HashSet<string>>();
        foreach (var edge in edges)
        {
            if (!graph.ContainsKey(edge.Start))
                graph[edge.Start] = new HashSet<string>();
            graph[edge.Start].Add(edge.End);
        }

        // Initialize current clique, candidates and processed vertices
        var currentClique = new SortedSet<string>();
        var candidates = new HashSet<string>(graph.Keys);
        var processedVertices = new HashSet<string>();

        // Execute the Bron-Kerbosch algorithm to collect the cliques
        BronKerbosch(currentClique, candidates, processedVertices, graph);

        // Sort the cliques for consistent display
        cliques = cliques.OrderBy(c => c, new ListComparer()).ToList();

        // Display the cliques
        Console.WriteLine("[{0}]", string.Join(", ", cliques.Select(c => "[" + string.Join(", ", c) + "]")));
    }

    private static void BronKerbosch(
        SortedSet<string> currentClique,
        HashSet<string> candidates,
        HashSet<string> processedVertices,
        Dictionary<string, HashSet<string>> graph)
    {
        if (candidates.Count == 0 && processedVertices.Count == 0)
        {
            if (currentClique.Count > 2)
            {
                cliques.Add(new List<string>(currentClique));
            }
            return;
        }

        // Select a pivot vertex from 'candidates' union 'processedVertices' with the maximum degree
        var union = new HashSet<string>(candidates);
        union.UnionWith(processedVertices);

        string pivot = union.OrderByDescending(v => graph.ContainsKey(v) ? graph[v].Count : 0).First();

        // 'possibles' are vertices in 'candidates' that are not neighbours of the 'pivot'
        var possibles = new HashSet<string>(candidates);
        if (graph.ContainsKey(pivot))
            possibles.ExceptWith(graph[pivot]);

        foreach (var vertex in possibles)
        {
            // Create a new clique including 'vertex'
            var newClique = new SortedSet<string>(currentClique);
            newClique.Add(vertex);

            // 'newCandidates' are the members of 'candidates' that are neighbours of 'vertex'
            var neighbours = graph.ContainsKey(vertex) ? graph[vertex] : new HashSet<string>();
            var newCandidates = new HashSet<string>(candidates);
            newCandidates.IntersectWith(neighbours);

            // 'newProcessedVertices' are members of 'processedVertices' that are neighbours of 'vertex'
            var newProcessedVertices = new HashSet<string>(processedVertices);
            newProcessedVertices.IntersectWith(neighbours);

            // Recursive call with the updated sets
            BronKerbosch(newClique, newCandidates, newProcessedVertices, graph);

            // Move 'vertex' from 'candidates' to 'processedVertices'
            candidates.Remove(vertex);
            processedVertices.Add(vertex);
        }
    }

    private class ListComparer : IComparer<List<string>>
    {
        public int Compare(List<string> x, List<string> y)
        {
            for (int i = 0; i < Math.Min(x.Count, y.Count); i++)
            {
                int comparison = x[i].CompareTo(y[i]);
                if (comparison != 0)
                    return comparison;
            }
            return x.Count.CompareTo(y.Count);
        }
    }

    private class Edge
    {
        public string Start { get; }
        public string End { get; }

        public Edge(string start, string end)
        {
            Start = start;
            End = end;
        }
    }
}
Output:
[[a, b, c], [d, e, f]]
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>

std::vector<std::vector<std::string>> cliques{};

struct Edge {
	std::string start;
	std::string end;
};

template <typename T>
void print_vector(const std::vector<T>& vec) {
	std::cout << "[";
	for ( uint32_t i = 0; i < vec.size() - 1; ++i ) {
		std::cout << vec[i] << ", ";
	}
	std::cout << vec.back() << "]";
}

template <typename T>
void print_2D_vector(const std::vector<std::vector<T>>& vecs) {
	std::cout << "[";
	for ( uint32_t i = 0; i < vecs.size() - 1; ++i ) {
		print_vector(vecs[i]); std::cout << ", ";
	}
	print_vector(vecs.back()); std::cout << "]" << std::endl;
}

void bron_kerbosch(const std::set<std::string>& current_clique,
				   std::set<std::string> candidates,
			       std::set<std::string> processed_vertices,
				   std::unordered_map<std::string, std::set<std::string>> graph) {

	if ( candidates.empty() && processed_vertices.empty() ) {
		if ( current_clique.size() > 2 ) {
			std::vector<std::string> clique(current_clique.begin(), current_clique.end());
			cliques.emplace_back(clique);
		}
		return;
	}

	// Select a pivot vertex from 'candidates' union 'processedVertices' with the maximum degree
	std::set<std::string> union_set(candidates.begin(), candidates.end());
	union_set.insert(processed_vertices.begin(), processed_vertices.end());
	const std::string pivot = *std::max_element(union_set.begin(), union_set.end(),
	    [&graph](const std::string& s1, const std::string& s2) { return graph[s1].size() < graph[s2].size(); });

	// 'possibles' are vertices in 'candidates' that are not neighbours of the 'pivot'
	std::set<std::string> possibles{};
	std::set_difference(candidates.begin(), candidates.end(),
		graph[pivot].begin(), graph[pivot].end(), std::inserter(possibles, possibles.begin()));

	for ( const std::string& vertex : possibles) {

		// Create a new clique including 'vertex'
		std::set<std::string> new_cliques(current_clique.begin(), current_clique.end());
		new_cliques.insert(vertex);

		// 'newCandidates' are the members of 'candidates' that are neighbours of 'vertex'
		std::set<std::string> new_candidates{};
		std::set_intersection(candidates.begin(), candidates.end(), graph[vertex].begin(), graph[vertex].end(),
			std::inserter(new_candidates, new_candidates.begin()));

		// 'newProcessedVertices' are members of 'processedVertices' that are neighbours of 'vertex'
		std::set<std::string> new_processed_vertices{};
		std::set_intersection(processed_vertices.begin(), processed_vertices.end(),
			graph[vertex].begin(), graph[vertex].end(),
			std::inserter(new_processed_vertices, new_processed_vertices.begin()));

		// Recursive call with the updated sets
		bron_kerbosch(new_cliques, new_candidates, new_processed_vertices, graph);

		// Move 'vertex' from 'candidates' to 'processedVertices'
		candidates.erase(vertex);
		processed_vertices.insert(vertex);
	}
}

int main() {
	const std::vector<Edge> edges = { Edge("a", "b"), Edge("b", "a"), Edge("a", "c"), Edge("c", "a"),
									  Edge("b", "c"), Edge("c", "b"), Edge("d", "e"), Edge("e", "d"),
									  Edge("d", "f"), Edge("f", "d"), Edge("e", "f"), Edge("f", "e") };

	// Build the graph as an adjacency list
	std::unordered_map<std::string, std::set<std::string>> graph{};
	std::for_each(edges.begin(), edges.end(),
		[&graph](const Edge& edge) { graph[edge.start].insert(edge.end); } );

	// Initialize current clique, candidates and processed vertices
	std::set<std::string> current_clique{};
	std::set<std::string> candidates{};
	std::transform(graph.begin(), graph.end(), std::inserter(candidates, candidates.end()),
	    [](const auto& pair) { return pair.first; } );
	std::set<std::string> processed_vertices{};

	// Execute the Bron-Kerbosch algorithm to collect the cliques
	bron_kerbosch(current_clique, candidates, processed_vertices, graph);

	// Sort the cliques for consistent display
	std::sort(cliques.begin(), cliques.end(),
		[](const std::vector<std::string>& a, const std::vector<std::string>& b) {
			for ( uint32_t i = 0; i < std::min(a.size(), b.size()); ++i ) {
				if ( a[i] != b[i] ) {
					return a[i] < b[i];
				}
			}
			return a.size() < b.size(); });

	// Display the cliques
	print_2D_vector(cliques);
}
Output:
[[a, b, c], [d, e, f]]
Works with: Dart version 3.6.1
Translation of: C++
List<List<String>> cliques = [];

class Edge {
  String start;
  String end;
  Edge(this.start, this.end);
}

void printVector<T>(List<T> vec) {
  StringBuffer sb = StringBuffer();
  sb.write('[');
  for (int i = 0; i < vec.length - 1; i++) {
    sb.write('${vec[i]}, ');
  }
  sb.write('${vec.last}]');
  print(sb.toString());
}

void print2DVector<T>(List<List<T>> vecs) {
  StringBuffer sb = StringBuffer();
  sb.write('[');
  for (int i = 0; i < vecs.length - 1; i++) {
    printVector(vecs[i]);
    sb.write(', ');
  }
  printVector(vecs.last);
  sb.write(']');
}

void bronKerbosch(
    Set<String> currentClique, Set<String> candidates, Set<String> processedVertices, Map<String, Set<String>> graph) {
  if (candidates.isEmpty && processedVertices.isEmpty) {
    if (currentClique.length > 2) {
      cliques.add(List<String>.from(currentClique));
    }
    return;
  }

  // Select a pivot vertex from 'candidates' union 'processedVertices' with the maximum degree
  Set<String> unionSet = Set<String>.from(candidates)..addAll(processedVertices);
  String pivot = unionSet.reduce((s1, s2) => graph[s1]!.length > graph[s2]!.length ? s1 : s2);


  // 'possibles' are vertices in 'candidates' that are not neighbors of the 'pivot'
  Set<String> possibles = candidates.difference(graph[pivot]!);

  for (String vertex in possibles) {
    // Create a new clique including 'vertex'
    Set<String> newCliques = Set<String>.from(currentClique)..add(vertex);

    // 'newCandidates' are the members of 'candidates' that are neighbors of 'vertex'
    Set<String> newCandidates = candidates.intersection(graph[vertex]!);

    // 'newProcessedVertices' are members of 'processedVertices' that are neighbors of 'vertex'
    Set<String> newProcessedVertices = processedVertices.intersection(graph[vertex]!);

    // Recursive call with the updated sets
    bronKerbosch(newCliques, newCandidates, newProcessedVertices, graph);

    // Move 'vertex' from 'candidates' to 'processedVertices'
    candidates.remove(vertex);
    processedVertices.add(vertex);
  }
}

void main() {
  List<Edge> edges = [
    Edge("a", "b"),
    Edge("b", "a"),
    Edge("a", "c"),
    Edge("c", "a"),
    Edge("b", "c"),
    Edge("c", "b"),
    Edge("d", "e"),
    Edge("e", "d"),
    Edge("d", "f"),
    Edge("f", "d"),
    Edge("e", "f"),
    Edge("f", "e")
  ];

  // Build the graph as an adjacency list
  Map<String, Set<String>> graph = {};
  edges.forEach((edge) {
    graph.putIfAbsent(edge.start, () => <String>{}).add(edge.end);
  });

  // Initialize current clique, candidates, and processed vertices
  Set<String> currentClique = {};
  Set<String> candidates = graph.keys.toSet();
  Set<String> processedVertices = {};

  // Execute the Bron-Kerbosch algorithm to collect the cliques
  bronKerbosch(currentClique, candidates, processedVertices, graph);

  // Sort the cliques for consistent display
  cliques.sort((a, b) {
    for (int i = 0; i < a.length && i < b.length; i++) {
      if (a[i] != b[i]) {
        return a[i].compareTo(b[i]);
      }
    }
    return a.length.compareTo(b.length);
  });

  // Display the cliques
  print2DVector(cliques);
}
Output:
[a, c, b]
[f, e, d]
Works with: Fortran version 7
Translation of: C++
program bron_kerbosch_cliques
    implicit none
    
    ! Maximum number of vertices and cliques
    integer, parameter :: MAX_VERTICES = 100
    integer, parameter :: MAX_CLIQUES = 1000
    integer, parameter :: MAX_EDGES = 1000
    integer, parameter :: MAX_NAME_LEN = 10
    
    ! Graph representation using adjacency matrix and vertex names
    logical :: adjacency_matrix(MAX_VERTICES, MAX_VERTICES)
    character(len=MAX_NAME_LEN) :: vertex_names(MAX_VERTICES)
    integer :: num_vertices
    
    ! Cliques storage
    integer :: cliques(MAX_CLIQUES, MAX_VERTICES)
    integer :: clique_sizes(MAX_CLIQUES)
    integer :: num_cliques
    
    ! Edge structure
    type :: edge_type
        character(len=MAX_NAME_LEN) :: start
        character(len=MAX_NAME_LEN) :: end
    end type edge_type
    
    ! Local variables
    type(edge_type) :: edges(MAX_EDGES)
    integer :: num_edges
    logical :: current_clique(MAX_VERTICES)
    logical :: candidates(MAX_VERTICES)
    logical :: processed(MAX_VERTICES)
    integer :: i, j
    
    ! Initialize
    num_vertices = 0
    num_cliques = 0
    num_edges = 12
    
    ! Initialize adjacency matrix
    adjacency_matrix = .false.
    
    ! Define edges
    edges(1) = edge_type("a", "b")
    edges(2) = edge_type("b", "a")
    edges(3) = edge_type("a", "c")
    edges(4) = edge_type("c", "a")
    edges(5) = edge_type("b", "c")
    edges(6) = edge_type("c", "b")
    edges(7) = edge_type("d", "e")
    edges(8) = edge_type("e", "d")
    edges(9) = edge_type("d", "f")
    edges(10) = edge_type("f", "d")
    edges(11) = edge_type("e", "f")
    edges(12) = edge_type("f", "e")
    
    ! Build graph
    call build_graph(edges, num_edges)
    
    ! Initialize sets for Bron-Kerbosch
    current_clique = .false.
    candidates = .false.
    processed = .false.
    
    ! Set all vertices as candidates
    do i = 1, num_vertices
        candidates(i) = .true.
    end do
    
    ! Run Bron-Kerbosch algorithm
    call bron_kerbosch_recursive(current_clique, candidates, processed)
    
    ! Sort and display cliques
    call sort_cliques()
    call print_cliques()
    
contains

    ! Function to find vertex index by name
    function find_vertex_index(name) result(index)
        implicit none
        character(len=*), intent(in) :: name
        integer :: index
        integer :: i
        
        do i = 1, num_vertices
            if (trim(vertex_names(i)) == trim(name)) then
                index = i
                return
            end if
        end do
        
        ! Add new vertex if not found
        num_vertices = num_vertices + 1
        vertex_names(num_vertices) = trim(name)
        index = num_vertices
    end function find_vertex_index

    ! Subroutine to build graph from edges
    subroutine build_graph(edges_array, n_edges)
        implicit none
        type(edge_type), intent(in) :: edges_array(:)
        integer, intent(in) :: n_edges
        integer :: i, start_idx, end_idx
        
        do i = 1, n_edges
            start_idx = find_vertex_index(edges_array(i)%start)
            end_idx = find_vertex_index(edges_array(i)%end)
            adjacency_matrix(start_idx, end_idx) = .true.
        end do
    end subroutine build_graph

    ! Count number of true values in logical array
    function count_true(logical_array) result(count)
        implicit none
        logical, intent(in) :: logical_array(:)
        integer :: count
        integer :: i
        
        count = 0
        do i = 1, num_vertices
            if (logical_array(i)) count = count + 1
        end do
    end function count_true

    ! Find pivot vertex with maximum degree
    function find_pivot(candidates_set, processed_set) result(pivot)
        implicit none
        logical, intent(in) :: candidates_set(:), processed_set(:)
        integer :: pivot
        integer :: i, max_degree, current_degree
        logical :: union_set(MAX_VERTICES)
        
        ! Create union of candidates and processed
        do i = 1, num_vertices
            union_set(i) = candidates_set(i) .or. processed_set(i)
        end do
        
        max_degree = -1
        pivot = 1
        
        do i = 1, num_vertices
            if (union_set(i)) then
                current_degree = count_neighbors(i)
                if (current_degree > max_degree) then
                    max_degree = current_degree
                    pivot = i
                end if
            end if
        end do
    end function find_pivot

    ! Count neighbors of a vertex
    function count_neighbors(vertex) result(count)
        implicit none
        integer, intent(in) :: vertex
        integer :: count
        integer :: i
        
        count = 0
        do i = 1, num_vertices
            if (adjacency_matrix(vertex, i)) count = count + 1
        end do
    end function count_neighbors

    ! Set difference: result = set_a - set_b
    subroutine set_difference(set_a, set_b, result_set)
        implicit none
        logical, intent(in) :: set_a(:), set_b(:)
        logical, intent(out) :: result_set(:)
        integer :: i
        
        do i = 1, num_vertices
            result_set(i) = set_a(i) .and. .not. set_b(i)
        end do
    end subroutine set_difference

    ! Set intersection: result = set_a ∩ set_b
    subroutine set_intersection(set_a, set_b, result_set)
        implicit none
        logical, intent(in) :: set_a(:), set_b(:)
        logical, intent(out) :: result_set(:)
        integer :: i
        
        do i = 1, num_vertices
            result_set(i) = set_a(i) .and. set_b(i)
        end do
    end subroutine set_intersection

    ! Get neighbors of a vertex
    subroutine get_neighbors(vertex, neighbors_set)
        implicit none
        integer, intent(in) :: vertex
        logical, intent(out) :: neighbors_set(:)
        integer :: i
        
        do i = 1, num_vertices
            neighbors_set(i) = adjacency_matrix(vertex, i)
        end do
    end subroutine get_neighbors

    ! Recursive Bron-Kerbosch algorithm
    recursive subroutine bron_kerbosch_recursive(current_clique_set, candidates_set, processed_set)
        implicit none
        logical, intent(in) :: current_clique_set(:)
        logical, intent(inout) :: candidates_set(:), processed_set(:)
        
        logical :: new_clique(MAX_VERTICES)
        logical :: new_candidates(MAX_VERTICES)
        logical :: new_processed(MAX_VERTICES)
        logical :: possibles(MAX_VERTICES)
        logical :: neighbors(MAX_VERTICES)
        logical :: pivot_neighbors(MAX_VERTICES)
        integer :: pivot, vertex
        integer :: clique_size, i
        
        ! If both candidates and processed are empty, we found a maximal clique
        if (count_true(candidates_set) == 0 .and. count_true(processed_set) == 0) then
            clique_size = count_true(current_clique_set)
            if (clique_size > 2) then
                call store_clique(current_clique_set)
            end if
            return
        end if
        
        ! Find pivot vertex
        pivot = find_pivot(candidates_set, processed_set)
        call get_neighbors(pivot, pivot_neighbors)
        
        ! Get vertices in candidates that are not neighbors of pivot
        call set_difference(candidates_set, pivot_neighbors, possibles)
        
        ! For each vertex in possibles
        do vertex = 1, num_vertices
            if (.not. possibles(vertex)) cycle
            
            ! Create new clique including vertex
            new_clique = current_clique_set
            new_clique(vertex) = .true.
            
            ! Get neighbors of vertex
            call get_neighbors(vertex, neighbors)
            
            ! New candidates = candidates ∩ neighbors(vertex)
            call set_intersection(candidates_set, neighbors, new_candidates)
            
            ! New processed = processed ∩ neighbors(vertex)
            call set_intersection(processed_set, neighbors, new_processed)
            
            ! Recursive call
            call bron_kerbosch_recursive(new_clique, new_candidates, new_processed)
            
            ! Move vertex from candidates to processed
            candidates_set(vertex) = .false.
            processed_set(vertex) = .true.
        end do
    end subroutine bron_kerbosch_recursive

    ! Store a clique
    subroutine store_clique(clique_set)
        implicit none
        logical, intent(in) :: clique_set(:)
        integer :: i, size
        
        if (num_cliques >= MAX_CLIQUES) return
        
        num_cliques = num_cliques + 1
        size = 0
        
        do i = 1, num_vertices
            if (clique_set(i)) then
                size = size + 1
                cliques(num_cliques, size) = i
            end if
        end do
        
        clique_sizes(num_cliques) = size
    end subroutine store_clique

    ! Sort cliques for consistent output
    subroutine sort_cliques()
        implicit none
        integer :: i, j, k
        integer :: temp_clique(MAX_VERTICES)
        integer :: temp_size
        logical :: swap_needed
        
        ! Simple bubble sort
        do i = 1, num_cliques - 1
            do j = i + 1, num_cliques
                swap_needed = .false.
                
                ! Compare cliques lexicographically
                do k = 1, min(clique_sizes(i), clique_sizes(j))
                    if (vertex_names(cliques(i, k)) < vertex_names(cliques(j, k))) then
                        exit
                    else if (vertex_names(cliques(i, k)) > vertex_names(cliques(j, k))) then
                        swap_needed = .true.
                        exit
                    end if
                end do
                
                if (.not. swap_needed .and. clique_sizes(i) > clique_sizes(j)) then
                    swap_needed = .true.
                end if
                
                if (swap_needed) then
                    ! Swap cliques
                    temp_clique(1:clique_sizes(i)) = cliques(i, 1:clique_sizes(i))
                    temp_size = clique_sizes(i)
                    
                    cliques(i, 1:clique_sizes(j)) = cliques(j, 1:clique_sizes(j))
                    clique_sizes(i) = clique_sizes(j)
                    
                    cliques(j, 1:temp_size) = temp_clique(1:temp_size)
                    clique_sizes(j) = temp_size
                end if
            end do
        end do
    end subroutine sort_cliques

    ! Print all cliques
    subroutine print_cliques()
        implicit none
        integer :: i, j
        
        write(*, '(A)', advance='no') '['
        do i = 1, num_cliques
            write(*, '(A)', advance='no') '['
            do j = 1, clique_sizes(i)
                write(*, '(A)', advance='no') trim(vertex_names(cliques(i, j)))
                if (j < clique_sizes(i)) write(*, '(A)', advance='no') ', '
            end do
            write(*, '(A)', advance='no') ']'
            if (i < num_cliques) write(*, '(A)', advance='no') ', '
        end do
        write(*, '(A)') ']'
    end subroutine print_cliques

end program bron_kerbosch_cliques
Output:
[[a, b, c], [d, e, f]]

Type StringSet
    values(99) As String
    cnt As Integer
    
    Declare Sub add_(value As String)
    Declare Sub remove(value As String)
    Declare Function isEmpty() As Boolean
    Declare Function contains(value As String) As Boolean
    Declare Function copy() As StringSet
    Declare Function intersect(other As StringSet) As StringSet
    Declare Function union_(other As StringSet) As StringSet
    Declare Function except_(other As StringSet) As StringSet
End Type

Type Graph
    adjacency(99, 99) As Byte
    vertices(99) As String
    vertexCount As Integer
    
    Declare Sub aditionEdge(v1 As String, v2 As String)
    Declare Function getNeighbors(vertex As String) As StringSet
End Type

Type CliqueList
    cliques(99) As StringSet
    cnt As Integer
    
    Declare Sub add_(clique As StringSet)
End Type

' StringSet methods implementation
Sub StringSet.add_(value As String)
    If Not this.contains(value) Then
        this.values(this.cnt) = value
        this.cnt += 1
    End If
End Sub

Sub StringSet.remove(value As String)
    Dim As Integer i, j
    For i = 0 To this.cnt - 1
        If this.values(i) = value Then
            For j = i To this.cnt - 2
                this.values(j) = this.values(j + 1)
            Next
            this.cnt -= 1
            Exit For
        End If
    Next
End Sub

Function StringSet.isEmpty() As Boolean
    Return this.cnt = 0
End Function

Function StringSet.contains(value As String) As Boolean
    For i As Integer = 0 To this.cnt - 1
        If this.values(i) = value Then Return True
    Next
    Return False
End Function

Function StringSet.copy() As StringSet
    Dim As StringSet result
    result.cnt = this.cnt
    For i As Integer = 0 To this.cnt - 1
        result.values(i) = this.values(i)
    Next
    Return result
End Function

Function StringSet.intersect(other As StringSet) As StringSet
    Dim As StringSet result
    For i As Integer = 0 To this.cnt - 1
        If other.contains(this.values(i)) Then
            result.add_(this.values(i))
        End If
    Next
    Return result
End Function

Function StringSet.union_(other As StringSet) As StringSet
    Dim As StringSet result = this.copy()
    For i As Integer = 0 To other.cnt - 1
        result.add_(other.values(i))
    Next
    Return result
End Function

Function StringSet.except_(other As StringSet) As StringSet
    Dim As StringSet result
    For i As Integer = 0 To this.cnt - 1
        If Not other.contains(this.values(i)) Then
            result.add_(this.values(i))
        End If
    Next
    Return result
End Function

' Graph methods implementation
Sub Graph.aditionEdge(v1 As String, v2 As String)
    Dim As Integer v1idx = -1, v2idx = -1
    
    For i As Integer = 0 To this.vertexCount - 1
        If this.vertices(i) = v1 Then v1idx = i
        If this.vertices(i) = v2 Then v2idx = i
    Next
    
    If v1idx = -1 Then
        v1idx = this.vertexCount
        this.vertices(this.vertexCount) = v1
        this.vertexCount += 1
    End If
    
    If v2idx = -1 Then
        v2idx = this.vertexCount
        this.vertices(this.vertexCount) = v2
        this.vertexCount += 1
    End If
    
    this.adjacency(v1idx, v2idx) = 1
End Sub

Function Graph.getNeighbors(vertex As String) As StringSet
    Dim As StringSet result
    Dim As Integer vidx = -1, i
    
    For i = 0 To this.vertexCount - 1
        If this.vertices(i) = vertex Then
            vidx = i
            Exit For
        End If
    Next
    
    If vidx <> -1 Then
        For i = 0 To this.vertexCount - 1
            If this.adjacency(vidx, i) = 1 Then
                result.add_(this.vertices(i))
            End If
        Next
    End If
    
    Return result
End Function

' CliqueList methods
Sub CliqueList.add_(clique As StringSet)
    this.cliques(this.cnt) = clique
    this.cnt += 1
End Sub

' Bron-Kerbosch algorithm
Sub BronKerbosch(r As StringSet, p As StringSet, x As StringSet, g As Graph, Byref cliques As CliqueList)
    Dim As Integer i, j
    
    If p.isEmpty() Andalso x.isEmpty() Then
        If r.cnt > 2 Then
            ' Sort the clique before aditioning (simple bubble sort)
            For i = 0 To r.cnt - 2
                For j = 0 To r.cnt - 2 - i
                    If r.values(j) > r.values(j + 1) Then
                        Swap r.values(j), r.values(j + 1)
                    End If
                Next
            Next
            cliques.add_(r)
        End If
        Return
    End If
    
    ' Find pivot
    Dim As String pivot, v
    Dim As Integer maxC = -1
    Dim As StringSet unionSet = p.union_(x)
    
    For i = 0 To unionSet.cnt - 1
        v = unionSet.values(i)
        Dim As StringSet neighbors = g.getNeighbors(v)
        If neighbors.cnt > maxC Then
            maxC = neighbors.cnt
            pivot = v
        End If
    Next
    
    Dim As StringSet pivotNeighbors = g.getNeighbors(pivot)
    Dim As StringSet candidates = p.except_(pivotNeighbors)
    
    For i = 0 To candidates.cnt - 1
        v = candidates.values(i)
        Dim As StringSet newR = r.copy()
        newR.add_(v)
        
        Dim As StringSet neighbors = g.getNeighbors(v)
        Dim As StringSet newP = p.intersect(neighbors)
        Dim As StringSet newX = x.intersect(neighbors)
        
        BronKerbosch(newR, newP, newX, g, cliques)
        
        p.remove(v)
        x.add_(v)
    Next
End Sub

' Main program
Sub main()
    Dim As Integer i, j
    Dim As Graph g
    
    ' add_ edges
    g.aditionEdge("a", "b")
    g.aditionEdge("b", "a")
    g.aditionEdge("a", "c")
    g.aditionEdge("c", "a")
    g.aditionEdge("b", "c")
    g.aditionEdge("c", "b")
    g.aditionEdge("d", "e")
    g.aditionEdge("e", "d")
    g.aditionEdge("d", "f")
    g.aditionEdge("f", "d")
    g.aditionEdge("e", "f")
    g.aditionEdge("f", "e")
    
    Dim As StringSet r, p, x
    Dim As CliqueList cliques
    
    ' Initialize p with all vertices
    For i = 0 To g.vertexCount - 1
        p.add_(g.vertices(i))
    Next
    
    ' Execute algorithm
    BronKerbosch(r, p, x, g, cliques)
    
    ' Print results
    For i = 0 To cliques.cnt - 1
        Dim As StringSet clique = cliques.cliques(i)
        For j = 0 To clique.cnt - 1
            Print clique.values(j);
            If j < clique.cnt - 1 Then Print ", ";
        Next
        Print
    Next
End Sub

main()

Sleep
Output:
a, b, c
d, e, f
Translation of: Python
package main

import (
	"fmt"
	"sort"
	"strings"
)

// Using map[string]bool to simulate a set of strings
type StringSet map[string]bool

// Edge struct represents a graph edge
type Edge struct {
	Start string
	End   string
}

// Global variable to store cliques (slices of sorted strings)
var cliques [][]string

// --- Set Operations Helper Functions ---

// setUnion returns the union of two sets (a U b)
func setUnion(a, b StringSet) StringSet {
	union := make(StringSet)
	for k := range a {
		union[k] = true
	}
	for k := range b {
		union[k] = true
	}
	return union
}

// setIntersection returns the intersection of two sets (a ∩ b)
func setIntersection(a, b StringSet) StringSet {
	intersection := make(StringSet)
	// Iterate over the smaller set for efficiency
	if len(a) < len(b) {
		for k := range a {
			if b[k] {
				intersection[k] = true
			}
		}
	} else {
		for k := range b {
			if a[k] {
				intersection[k] = true
			}
		}
	}
	return intersection
}

// setDifference returns the difference of two sets (a \ b)
func setDifference(a, b StringSet) StringSet {
	difference := make(StringSet)
	for k := range a {
		if !b[k] { // If element from a is NOT in b
			difference[k] = true
		}
	}
	return difference
}

// setCopy creates a shallow copy of the set
func setCopy(s StringSet) StringSet {
	newSet := make(StringSet)
	for k := range s {
		newSet[k] = true
	}
	return newSet
}

// setToSortedSlice converts a set to a sorted slice of strings
func setToSortedSlice(s StringSet) []string {
	slice := make([]string, 0, len(s))
	for k := range s {
		slice = append(slice, k)
	}
	sort.Strings(slice)
	return slice
}

// --- Printing Helper Functions ---

// printSlice prints a sorted string slice like [a, b, c]
func printSlice(slice []string) {
	fmt.Printf("[%s]", strings.Join(slice, ", "))
}

// print2DSlice prints a slice of slices like [[a, b, c], [d, e, f]]
func print2DSlice(slices [][]string) {
	fmt.Print("[")
	for i, slice := range slices {
		printSlice(slice)
		if i < len(slices)-1 {
			fmt.Print(", ")
		}
	}
	fmt.Println("]")
}

// --- Bron-Kerbosch Algorithm ---

// bronKerbosch implements the algorithm with pivoting.
// R: current clique being built
// P: candidate vertices
// X: processed vertices
// graph: adjacency list representation (map[vertex] -> set of neighbors)
func bronKerbosch(currentClique, candidates, processedVertices StringSet, graph map[string]StringSet) {

	if len(candidates) == 0 && len(processedVertices) == 0 {
		// Base case: Found a maximal clique
		if len(currentClique) > 2 {
			// Convert set to sorted slice before storing
			cliqueSlice := setToSortedSlice(currentClique)
			cliques = append(cliques, cliqueSlice)
		}
		return
	}

	// --- Pivot Selection ---
	// Choose a pivot u from P union X with the maximum number of neighbors in P.
	// Python version simplifies to max degree in the *whole graph* from P union X. We'll mimic that.
	unionSet := setUnion(candidates, processedVertices)
	pivot := ""
	maxDegree := -1

	// Find pivot (vertex in P U X with highest degree in the whole graph)
    if len(unionSet) > 0 {
        // Find *any* element first to initialize pivot
        for v := range unionSet {
            pivot = v
            if neighbors, ok := graph[pivot]; ok {
                maxDegree = len(neighbors)
            } else {
                maxDegree = 0 // Handle nodes with no outgoing edges if they exist
            }
            break // Found the first element, stop
        }

        // Now find the actual max degree pivot
        for v := range unionSet {
            degree := 0
            if neighbors, ok := graph[v]; ok {
                degree = len(neighbors)
            }
            if degree > maxDegree {
                maxDegree = degree
                pivot = v
            }
        }
    } else {
         // If unionSet is empty, we should have hit the base case already,
         // but handle defensively.
         return
    }


	// --- Iteration ---
	// Iterate through vertices v in P \ N(u) (candidates not neighbors of pivot)
	pivotNeighbors := graph[pivot] // Might be nil if pivot has no neighbors listed
	if pivotNeighbors == nil {
		pivotNeighbors = make(StringSet) // Treat as empty set if nil
	}

	// Create a copy of candidates to iterate over, as candidates set is modified inside the loop
	candidatesWithoutPivotNeighbors := setDifference(candidates, pivotNeighbors)

	// Need to iterate over a stable copy because 'candidates' is modified
	verticesToProcess := setToSortedSlice(candidatesWithoutPivotNeighbors) // Get keys to iterate safely

	for _, vertex := range verticesToProcess {
        // Ensure vertex is still in the *current* candidates set before processing
        // (It might have been removed if processing another vertex moved it to X implicitly,
        // although the standard BK algorithm structure should prevent this here).
        // This check is mostly for robustness if the sets were modified unexpectedly.
        // In this specific BK implementation, removing happens *after* recursion, so it's safe.
        // if !candidates[vertex] {
        //     continue
        // }

		vertexNeighbors := graph[vertex]
		if vertexNeighbors == nil {
			vertexNeighbors = make(StringSet)
		}

		// Create new clique R U {v}
		newClique := setCopy(currentClique)
		newClique[vertex] = true

		// New candidates P ∩ N(v)
		newCandidates := setIntersection(candidates, vertexNeighbors)

		// New processed vertices X ∩ N(v)
		newProcessed := setIntersection(processedVertices, vertexNeighbors)

		// Recursive call
		bronKerbosch(newClique, newCandidates, newProcessed, graph)

		// Move vertex from P to X: P = P \ {v}, X = X U {v}
		delete(candidates, vertex)
		processedVertices[vertex] = true
	}
}

func main() {
	// Define edges
	edges := []Edge{
		{"a", "b"}, {"b", "a"}, {"a", "c"}, {"c", "a"},
		{"b", "c"}, {"c", "b"}, {"d", "e"}, {"e", "d"},
		{"d", "f"}, {"f", "d"}, {"e", "f"}, {"f", "e"},
	}

	// Build the graph as an adjacency list (map[string]StringSet)
	graph := make(map[string]StringSet)
	allVertices := make(StringSet) // Keep track of all unique vertices

	for _, edge := range edges {
		// Ensure the map for the start node exists
		if _, ok := graph[edge.Start]; !ok {
			graph[edge.Start] = make(StringSet)
		}
		graph[edge.Start][edge.End] = true // Add neighbor

		// Add vertices to our set of all vertices
		allVertices[edge.Start] = true
		allVertices[edge.End] = true
	}

	// Initialize sets for the algorithm
	currentClique := make(StringSet)
	// Candidates initially contain all vertices in the graph
	candidates := setCopy(allVertices) // Start with all unique vertices found
	processedVertices := make(StringSet)

	// Execute the Bron-Kerbosch algorithm
	bronKerbosch(currentClique, candidates, processedVertices, graph)

	// Sort the final list of cliques for consistent display
	// Sort first by length, then lexicographically by the elements
	sort.Slice(cliques, func(i, j int) bool {
		if len(cliques[i]) != len(cliques[j]) {
			return len(cliques[i]) < len(cliques[j])
		}
		// If lengths are equal, compare element by element
		for k := 0; k < len(cliques[i]); k++ {
			if cliques[i][k] != cliques[j][k] {
				return cliques[i][k] < cliques[j][k]
			}
		}
		return false // Should not happen for distinct cliques
	})

	// Display the cliques
	print2DSlice(cliques)
}
Output:
[[a, b, c], [d, e, f]]
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public final class BronKerboschAlgorithm {

	public static void main(String[] args) {
		List<Edge> edges = List.of( new Edge("a", "b"), new Edge("b", "a"), new Edge("a", "c"), new Edge("c", "a"),
									new Edge("b", "c"), new Edge("c", "b"), new Edge("d", "e"), new Edge("e", "d"),
									new Edge("d", "f"), new Edge("f", "d"), new Edge("e", "f"), new Edge("f", "e") );
		
		// Build the graph as an adjacency list
	    Map<String, Set<String>> graph = new HashMap<String, Set<String>>();
	    edges.forEach( edge -> graph.computeIfAbsent(edge.start, v -> new HashSet<String>()).add(edge.end) );
	    
	    // Initialize current clique, candidates and processed vertices
	    Set<String> currentClique = new TreeSet<String>();
	    Set<String> candidates = new HashSet<String>(graph.keySet());
	    Set<String> processedVertices = new HashSet<String>();

	    // Execute the Bron-Kerbosch algorithm to collect the cliques
	    bronKerbosch(currentClique, candidates, processedVertices, graph);
	    
	    // Sort the cliques for consistent display
	    Collections.sort(cliques, listComparator);
	    
	    // Display the cliques
	    System.out.println(cliques);
	}
	
	private static void bronKerbosch(Set<String> currentClique, Set<String> candidates,
			                         Set<String> processedVertices, Map<String, Set<String>> graph) {
		
	    if ( candidates.isEmpty() && processedVertices.isEmpty() ) {
	        if ( currentClique.size() > 2 ) {
	            List<String> clique = new ArrayList<String>(currentClique);
	            cliques.add(clique);
	        }
	        return;
	    }
	    
	    // Select a pivot vertex from 'candidates' union 'processedVertices' with the maximum degree
	    Set<String> union = new HashSet<String>(candidates);
	    union.addAll(processedVertices);	 
	    String pivot = 
	    	union.stream().max( (s1, s2) -> Integer.compare(graph.get(s1).size(), graph.get(s2).size()) ).get();

	    // 'possibles' are vertices in 'candidates' that are not neighbours of the 'pivot'
	    Set<String> possibles = new HashSet<String>(candidates);
	    possibles.removeAll(graph.get(pivot));
	    
	    for ( String vertex : possibles) {
	        // Create a new clique including 'vertex'
	        Set<String> newCliques = new TreeSet<String>(currentClique);
	        newCliques.add(vertex);

	        // 'newCandidates' are the members of 'candidates' that are neighbours of 'vertex'
	        Set<String> neighbours = graph.get(vertex);
	        Set<String> newCandidates = new HashSet<String>(candidates);
	        newCandidates.retainAll(neighbours);

	        // 'newProcessedVertices' are members of 'processedVertices' that are neighbours of 'vertex'
	        Set<String> newProcessedVertices = new HashSet<String>(processedVertices);
	        newProcessedVertices.retainAll(neighbours);

	        // Recursive call with the updated sets
	        bronKerbosch(newCliques, newCandidates, newProcessedVertices, graph);

	        // Move 'vertex' from 'candidates' to 'processedVertices'
	        candidates.remove(vertex);
	        processedVertices.add(vertex);
	    }	    
	}
	
	private static Comparator<List<String>> listComparator = (list1, list2) -> {
        for ( int i = 0; i < Math.min(list1.size(), list2.size()); i++ ) {
            final int comparison = list1.get(i).compareTo(list2.get(i));
            if ( comparison != 0 ) {
                return comparison;
            }
        }
        return Integer.compare(list1.size(), list2.size());
	};
	
	private static List<List<String>> cliques = new ArrayList<List<String>>();
	
	private static record Edge(String start, String end) {}

}
Output:
[[a, b, c], [d, e, f]]
Translation of: C++
// Global variable to store the found cliques
let cliques = [];

// Equivalent to the Edge struct
// (We'll just use plain objects inline)

// Helper function to print the final result (similar to print_2D_vector)
function printCliques(cliquesArray) {
    // Sort cliques before printing for consistent output
    // Sort primarily by first element, then second, etc., then by length
    cliquesArray.sort((a, b) => {
        const len = Math.min(a.length, b.length);
        for (let i = 0; i < len; i++) {
            if (a[i] < b[i]) return -1;
            if (a[i] > b[i]) return 1;
        }
        // If one is a prefix of the other, shorter comes first
        return a.length - b.length;
    });

    // Pretty print using JSON.stringify
    console.log(JSON.stringify(cliquesArray, null, 2));
}

// --- Set Operations Helpers ---

// Set Intersection: Returns a new Set containing elements present in both setA and setB
function setIntersection(setA, setB) {
    const intersection = new Set();
    for (const elem of setB) { // Iterate smaller set for potential efficiency
        if (setA.has(elem)) {
            intersection.add(elem);
        }
    }
    return intersection;
}

// Set Difference: Returns a new Set containing elements present in setA but NOT in setB (A \ B)
function setDifference(setA, setB) {
    const difference = new Set(setA); // Start with a copy of setA
    for (const elem of setB) {
        difference.delete(elem); // Remove elements found in setB
    }
    return difference;
}


/**
 * Bron-Kerbosch algorithm with pivoting to find maximal cliques.
 * @param {Set<string>} currentClique - The clique being built (R in common notation).
 * @param {Set<string>} candidates - Potential vertices to add (P).
 * @param {Set<string>} processedVertices - Vertices already processed/excluded (X).
 * @param {Map<string, Set<string>>} graph - Adjacency list representation of the graph.
 */
function bronKerbosch(currentClique, candidates, processedVertices, graph) {

    if (candidates.size === 0 && processedVertices.size === 0) {
        // Base case: Found a maximal clique
        if (currentClique.size > 2) {
            // Convert Set to Array, sort it for consistent ordering, and add to results
            const cliqueArray = Array.from(currentClique).sort();
            cliques.push(cliqueArray);
        }
        return;
    }

    // --- Pivoting ---
    // Combine candidates and processed vertices for pivot selection
    const unionSet = new Set([...candidates, ...processedVertices]);
    let pivot = '';
    let maxDegree = -1;

    // Select pivot: vertex in unionSet with the most neighbors *in candidates*
    // (Optimization: C++ version used total degree, JS version uses neighbors in candidates for better pruning)
    // Let's stick closer to the provided C++ heuristic (max degree in the *entire graph* among unionSet members) for direct translation.
     for (const u of unionSet) {
        const neighbors = graph.get(u) || new Set(); // Neighbors in the whole graph
        if (neighbors.size > maxDegree) {
             maxDegree = neighbors.size;
             pivot = u;
         }
     }

    // If unionSet was empty (should be caught by base case, but safety check)
    if (!pivot && unionSet.size > 0) {
        pivot = unionSet.values().next().value; // Fallback: pick any element if degree calc failed
    } else if (!pivot) {
        return; // Union set is truly empty
    }


    // 'possibles': Candidates that are NOT neighbors of the pivot (candidates \ N(pivot))
    const pivotNeighbors = graph.get(pivot) || new Set();
    const possibles = setDifference(candidates, pivotNeighbors); // P \ N(u)

    // --- Recursion ---
    // Iterate over a *copy* of possibles because we modify 'candidates' and 'processedVertices' below
    const possiblesCopy = Array.from(possibles);

    for (const vertex of possiblesCopy) {
        // Ensure vertex hasn't been moved from candidates in another branch's backtracking
        // (This check is usually implicitly handled by iterating the right set,
        // but explicit check can prevent errors if logic is complex)
        if (!candidates.has(vertex)) {
            continue;
        }

        const neighborsOfVertex = graph.get(vertex) || new Set();

        // Create NEW clique for recursive call: currentClique U {vertex}
        const newClique = new Set(currentClique);
        newClique.add(vertex);

        // Create NEW candidates for recursive call: candidates ∩ N(vertex)
        const newCandidates = setIntersection(candidates, neighborsOfVertex);

        // Create NEW processed vertices for recursive call: processedVertices ∩ N(vertex)
        const newProcessedVertices = setIntersection(processedVertices, neighborsOfVertex);

        // Recursive call
        bronKerbosch(newClique, newCandidates, newProcessedVertices, graph);

        // Backtracking: Move 'vertex' from candidates to processedVertices
        // Modify the sets belonging to *this* function call's scope
        candidates.delete(vertex);
        processedVertices.add(vertex);
    }
}

// --- Main Execution ---
function main() {
    const edges = [
        { start: "a", end: "b" }, { start: "b", end: "a" },
        { start: "a", end: "c" }, { start: "c", end: "a" },
        { start: "b", end: "c" }, { start: "c", end: "b" },
        { start: "d", end: "e" }, { start: "e", end: "d" },
        { start: "d", end: "f" }, { start: "f", end: "d" },
        { start: "e", end: "f" }, { start: "f", end: "e" }
    ];

    // Build the graph as an adjacency list (Map<string, Set<string>>)
    const graph = new Map();
    const allVertices = new Set(); // Keep track of all unique vertices

    for (const edge of edges) {
        // Ensure nodes exist in the map
        if (!graph.has(edge.start)) {
            graph.set(edge.start, new Set());
        }
        // Add edge
        graph.get(edge.start).add(edge.end);

        // Keep track of all vertices encountered
        allVertices.add(edge.start);
        allVertices.add(edge.end); // Ensure end node is also tracked if it never starts an edge
    }

    // Ensure all nodes mentioned (even if only as end nodes) exist as keys in the graph map
    for(const vertex of allVertices) {
         if (!graph.has(vertex)) {
            graph.set(vertex, new Set());
        }
    }


    // Initialize sets for the algorithm
    const initialCurrentClique = new Set();
    // Candidates are initially all vertices in the graph
    const initialCandidates = new Set(allVertices);
    const initialProcessedVertices = new Set();

    // Reset global cliques array before running
    cliques = [];

    // Execute the Bron-Kerbosch algorithm
    bronKerbosch(
        initialCurrentClique,
        initialCandidates,
        initialProcessedVertices,
        graph
    );

    // Display the cliques (sorted)
    printCliques(cliques);
}

// Run the main function
main();
Output:
[
  [
    "a",
    "b",
    "c"
  ],
  [
    "d",
    "e",
    "f"
  ]
]
"""
For rosettacode.org/wiki/Bron–Kerbosch_algorithm

The implementation below is taken from the Graphs julia package at
https://github.com/JuliaGraphs/Graphs.jl/blob/ec6ab1b0e267e2b1722837aa113e8da9a405785b/src/community/cliques.jl

Because the Graphs.jl package uses integers for vertex labels, the letters in the Rust example are converted
to and from integers for program testing.
"""

mutable struct SimpleGraph{T <: Integer}
    ne::T
    vertices::Vector{T}
    edges::Set{Tuple{T, T}}
    adjacencies::Vector{Set{T}}
end

function SimpleGraph(ne::T, edge_list) where T <: Integer
    vertices = collect(1:ne)
    edges = Set{Tuple{T, T}}()
    sets = [Set{T}() for _ in vertices]
    for e in edge_list
        push!(edges, e, reverse(e))
        push!(sets[e[1]], e[2])
        push!(sets[e[2]], e[1])
    end
    return SimpleGraph{T}(ne, vertices, edges, sets)
end
vertices(g::SimpleGraph) = g.vertices
edges(g::SimpleGraph) = g.edges
neighbors(g, v) = g.adjacencies[v]

function Bron_Kerbosch_maximal_cliques(g::SimpleGraph{T}) where T
    max_connections = -1
    n_numbers = [Set{T}() for n in vertices(g)]
    pivot_numbers = Set{T}() # handle empty graph
    pivot_done_numbers = Set{T}()  # initialize

    for n in vertices(g)
        nbrs = Set{T}()
        union!(nbrs, neighbors(g, n))
        delete!(nbrs, n) # ignore edges between n and itself
        conn = length(nbrs)
        if conn > max_connections
            pivot_numbers = nbrs
            n_numbers[n] = pivot_numbers
            max_connections = conn
        else
            n_numbers[n] = nbrs
        end
    end

    # Initial setup
    cand = Set{T}(vertices(g))
    smallcand = setdiff(cand, pivot_numbers)
    done = Set{T}()
    stack = Vector{Tuple{Set{T}, Set{T}, Set{T}}}()
    clique_so_far = Vector{T}()
    cliques = Vector{Vector{T}}()

    # Start main loop
    while !isempty(smallcand) || !isempty(stack)
        if !isempty(smallcand) # Any vertices left to check?
            n = pop!(smallcand)
        else
            # back out clique_so_far
            cand, done, smallcand = pop!(stack)
            pop!(clique_so_far)
            continue
        end
        # Add next node to clique
        push!(clique_so_far, n)
        delete!(cand, n)
        push!(done, n)
        nn = n_numbers[n]
        new_cand = intersect(cand, nn)
        new_done = intersect(done, nn)
        # check if we have more to search
        if isempty(new_cand)
            if isempty(new_done)
                # Found a clique!
                push!(cliques, collect(clique_so_far))
            end
            pop!(clique_so_far)
            continue
        end
        # Shortcut--only one node left!
        if isempty(new_done) && length(new_cand) == 1
            push!(cliques, vcat(clique_so_far, collect(new_cand)))
            pop!(clique_so_far)
            continue
        end
        # find pivot node (max connected in cand)
        # look in done vertices first
        numb_cand = length(new_cand)
        max_connections_done = -1
        for n in new_done
            cn = intersect(new_cand, n_numbers[n])
            conn = length(cn)
            if conn > max_connections_done
                pivot_done_numbers = cn
                max_connections_done = conn
                if max_connections_done == numb_cand
                    break
                end
            end
        end
        # Shortcut--this part of tree already searched
        if max_connections_done == numb_cand
            pop!(clique_so_far)
            continue
        end
        # still finding pivot node
        # look in cand vertices second
        max_connections = -1
        for n in new_cand
            cn = intersect(new_cand, n_numbers[n])
            conn = length(cn)
            if conn > max_connections
                pivot_numbers = cn
                max_connections = conn
                if max_connections == numb_cand - 1
                    break
                end
            end
        end
        # pivot node is max connected in cand from done or cand
        if max_connections_done > max_connections
            pivot_numbers = pivot_done_numbers
        end
        # save search status for later backout
        push!(stack, (cand, done, smallcand))
        cand = new_cand
        done = new_done
        smallcand = setdiff(cand, pivot_numbers)
    end
    return cliques
end

char(i) = Char(Int32('a') + i - 1)
int(t) = Tuple(Int32(x) .- Int32('a') .+ 1 for x in t)

TEST_EDGES = [('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'c'), ('c', 'a'), ('c', 'b'),
    ('d', 'e'), ('d', 'f'), ('e', 'd'), ('e', 'f'), ('f', 'd'), ('f', 'e')]
g_test = SimpleGraph(6, map(int, TEST_EDGES))

println("\nBron-Kerbosch maximal cliques: ")
println([sort!(map(char, clique)) for clique in Bron_Kerbosch_maximal_cliques(g_test)])
Output:
Bron-Kerbosch maximal cliques:
[['d', 'e', 'f'], ['a', 'b', 'c']]
Works with: Kotlin version 1.3.31
Translation of: Java
import java.util.*

object MainKt {
    private val cliques: MutableList<List<String>> = mutableListOf()

    @JvmStatic
    fun main(args: Array<String>) {
        val edges = listOf(
            Edge("a", "b"), Edge("b", "a"), Edge("a", "c"), Edge("c", "a"),
            Edge("b", "c"), Edge("c", "b"), Edge("d", "e"), Edge("e", "d"),
            Edge("d", "f"), Edge("f", "d"), Edge("e", "f"), Edge("f", "e")
        )

        // Build the graph as an adjacency list
        val graph: MutableMap<String, MutableSet<String>> = mutableMapOf()
        edges.forEach { edge ->
            graph.computeIfAbsent(edge.start) { mutableSetOf() }.add(edge.end)
        }

        // Initialize current clique, candidates, and processed vertices
        val currentClique: MutableSet<String> = mutableSetOf()
        val candidates: MutableSet<String> = mutableSetOf<String>().apply { addAll(graph.keys) }
        val processedVertices: MutableSet<String> = mutableSetOf()

        // Execute the Bron-Kerbosch algorithm to collect the cliques
        bronKerbosch(currentClique, candidates, processedVertices, graph)

        // Sort the cliques for consistent display
        cliques.sortWith(listComparator)

        // Display the cliques
        println(cliques)
    }

    private fun bronKerbosch(
        currentClique: MutableSet<String>,
        candidates: MutableSet<String>,
        processedVertices: MutableSet<String>,
        graph: Map<String, Set<String>>
    ) {
        if (candidates.isEmpty() && processedVertices.isEmpty()) {
            if (currentClique.size > 2) {
                val clique = ArrayList(currentClique)
                cliques.add(clique)
            }
            return
        }

        // Select a pivot vertex from 'candidates' union 'processedVertices' with the maximum degree
        val union = candidates.toMutableSet().apply { addAll(processedVertices) }
        val pivot = union.maxBy { s -> graph[s]?.size ?: 0 }

        if (pivot == null) {
            return
        }

        // 'possibles' are vertices in 'candidates' that are not neighbors of the 'pivot'
        val possibles = candidates.toMutableSet().apply { removeAll(graph[pivot] ?: emptySet()) }

        for (vertex in possibles) {
            // Create a new clique including 'vertex'
            val newCliques = currentClique.toMutableSet().apply { add(vertex) }

            // 'newCandidates' are the members of 'candidates' that are neighbors of 'vertex'
            val neighbors = graph[vertex] ?: emptySet()
            val newCandidates = candidates.toMutableSet().apply { retainAll(neighbors) }

            // 'newProcessedVertices' are members of 'processedVertices' that are neighbors of 'vertex'
            val newProcessedVertices = processedVertices.toMutableSet().apply { retainAll(neighbors) }

            // Recursive call with the updated sets
            bronKerbosch(newCliques, newCandidates, newProcessedVertices, graph)

            // Move 'vertex' from 'candidates' to 'processedVertices'
            candidates.remove(vertex)
            processedVertices.add(vertex)
        }
    }

    private val listComparator = Comparator<Any> { list1, list2 ->
        val typedList1 = list1 as List<String>
        val typedList2 = list2 as List<String>
        for (i in 0 until minOf(typedList1.size, typedList2.size)) {
            val comparison = typedList1[i].compareTo(typedList2[i])
            if (comparison != 0) {
                return@Comparator comparison
            }
        }
        typedList1.size.compareTo(typedList2.size)
    }

    private data class Edge(val start: String, val end: String)
}
Output:
[[a, b, c], [d, e, f]]

Works with: Lua version 5.4
Translation of: Perl
local edges = {
    { start = "a", end_ = "b" },
    { start = "b", end_ = "a" },
    { start = "a", end_ = "c" },
    { start = "c", end_ = "a" },
    { start = "b", end_ = "c" },
    { start = "c", end_ = "b" },
    { start = "d", end_ = "e" },
    { start = "e", end_ = "d" },
    { start = "d", end_ = "f" },
    { start = "f", end_ = "d" },
    { start = "e", end_ = "f" },
    { start = "f", end_ = "e" },
}

-- Simple serialization function for table display
function serialize(obj)
    local lua = ""
    local t = type(obj)
    if t == "number" then
        lua = lua .. obj
    elseif t == "boolean" then
        lua = lua .. tostring(obj)
    elseif t == "string" then
        lua = lua .. string.format("%q", obj)
    elseif t == "table" then
        lua = lua .. "{\n"
        for k, v in pairs(obj) do
            lua = lua .. "[" .. serialize(k) .. "]=" .. serialize(v) .. ",\n"
        end
        lua = lua .. "}"
    elseif t == "nil" then
        return nil
    else
        error("can not serialize a " .. t .. " type.")
    end
    return lua
end

function bron_kerbosch(current_clique, candidates, processed_vertices, graph, cliques)
    if next(candidates) == nil and next(processed_vertices) == nil then
        if #current_clique > 2 then
            local new_clique = {}
            for _, v in ipairs(current_clique) do
                table.insert(new_clique, v)
            end
            table.insert(cliques, new_clique)
        end
        return
    end

    -- Select a pivot vertex from 'candidates' union 'processedVertices' with the maximum degree
    local union = {}
    for k, v in pairs(candidates) do union[k] = v end
    for k, v in pairs(processed_vertices) do union[k] = v end
    local pivot = max_degree_vertex(union, graph)

    -- 'possibles' are vertices in 'candidates' that are not neighbors of the 'pivot'
    local possibles = {}
    for k, v in pairs(candidates) do possibles[k] = v end
    
    if graph[pivot] then
        for _, neighbor in ipairs(graph[pivot]) do
            possibles[neighbor] = nil
        end
    end

    for vertex, _ in pairs(possibles) do
        -- Create a new clique including 'vertex'
        local new_clique = {}
        for _, v in ipairs(current_clique) do
            table.insert(new_clique, v)
        end
        table.insert(new_clique, vertex)

        -- 'newCandidates' are the members of 'candidates' that are neighbors of 'vertex'
        local neighbors = {}
        if graph[vertex] then
            for _, neighbor in ipairs(graph[vertex]) do
                neighbors[neighbor] = true
            end
        end
        
        local new_candidates = {}
        for candidate, _ in pairs(candidates) do
            if neighbors[candidate] then
                new_candidates[candidate] = true
            end
        end

        -- 'newProcessedVertices' are members of 'processedVertices' that are neighbors of 'vertex'
        local new_processed_vertices = {}
        for processed, _ in pairs(processed_vertices) do
            if neighbors[processed] then
                new_processed_vertices[processed] = true
            end
        end

        -- Recursive call with the updated sets
        bron_kerbosch(new_clique, new_candidates, new_processed_vertices, graph, cliques)

        -- Move 'vertex' from 'candidates' to 'processedVertices'
        candidates[vertex] = nil
        processed_vertices[vertex] = true
    end
end

function max_degree_vertex(vertices, graph)
    local max_vertex, max_degree = nil, -1

    for vertex, _ in pairs(vertices) do
        local degree = 0
        if graph[vertex] then
            degree = #graph[vertex]
        end
        if degree > max_degree then
            max_degree = degree
            max_vertex = vertex
        end
    end

    return max_vertex
end

function list_comparator(list1, list2)
    local min_length = math.min(#list1, #list2)

    for i = 1, min_length do
        if list1[i] < list2[i] then
            return -1
        elseif list1[i] > list2[i] then
            return 1
        end
    end

    if #list1 < #list2 then
        return -1
    elseif #list1 > #list2 then
        return 1
    else
        return 0
    end
end

-- Build the graph as an adjacency list
local graph = {}
for _, edge in ipairs(edges) do
    if not graph[edge.start] then
        graph[edge.start] = {}
    end
    table.insert(graph[edge.start], edge.end_)
end

-- Initialize current clique, candidates, and processed vertices
local current_clique = {}
local candidates = {}
for vertex, _ in pairs(graph) do
    candidates[vertex] = true
end
local processed_vertices = {}

-- Execute the Bron-Kerbosch algorithm to collect the cliques
local cliques = {}
bron_kerbosch(current_clique, candidates, processed_vertices, graph, cliques)

-- Sort the cliques for consistent display
table.sort(cliques, function(a, b) return list_comparator(a, b) < 0 end)

-- Display the cliques
print(serialize(cliques))
Output:
{
[1]={
[1]="a",
[2]="c",
[3]="b",
},
[2]={
[1]="f",
[2]="e",
[3]="d",
},
}
Translation of: R
(* Global variable to store cliques *)
cliques = {};

(* Function to print a sorted list *)
printVector[vec_] := Print["[", StringRiffle[Sort[ToString /@ vec], ", "], "]"]

(* Function to print a list of lists *)
print2DVector[vecs_] := (
  Print["[" <> StringRiffle[
    (Function[v, "[" <> StringRiffle[Sort[ToString /@ v], ", "] <> "]"] /@ vecs),
    ", "
  ] <> "]"]
)

(* Bron-Kerbosch recursive function *)
bronKerbosch[currentClique_, candidates_, processed_, adj_] := Module[
  {unionSet, pivot, possibles, neighbors, newCandidates, newProcessed},
  
  (* Base case *)
  If[Length[candidates] == 0 && Length[processed] == 0,
    If[Length[currentClique] > 2,
      AppendTo[cliques, Sort[currentClique]]
    ];
    Return[]
  ];

  (* Union of candidates and processed *)
  unionSet = Union[candidates, processed];

  (* Choose pivot with maximum degree *)
  pivot = First[MaximalBy[unionSet, Length[adj[#]] &]];

  (* Possibles: candidates not adjacent to pivot *)
  possibles = Complement[candidates, adj[pivot]];

  (* Iterate over each possible vertex *)
  Do[
    neighbors = adj[vertex];
    newCandidates = Intersection[candidates, neighbors];
    newProcessed = Intersection[processed, neighbors];
    bronKerbosch[Append[currentClique, vertex], newCandidates, newProcessed, adj],
    {vertex, possibles}
  ]
]

(* Main function *)
main[] := Module[
  {edges, nodes, adj, currentClique, candidates, processed},

  (* Clear global cliques *)
  cliques = {};

  (* Define edges *)
  edges = {
    {"a", "b"}, {"b", "a"}, {"a", "c"}, {"c", "a"},
    {"b", "c"}, {"c", "b"}, {"d", "e"}, {"e", "d"},
    {"d", "f"}, {"f", "d"}, {"e", "f"}, {"f", "e"}
  };

  (* Build adjacency list as an association *)
  nodes = Union[Flatten[edges]];
  adj = Association[# -> {} & /@ nodes];
  Do[
    {u, v} = edge;
    If[! MemberQ[adj[u], v], AppendTo[adj[u], v]],
    {edge, edges}
  ];

  (* Initialize Bron-Kerbosch parameters *)
  currentClique = {};
  candidates = nodes;
  processed = {};

  (* Run Bron-Kerbosch *)
  bronKerbosch[currentClique, candidates, processed, adj];

  (* Remove duplicates and sort cliques by size and content *)
  cliques = DeleteDuplicates[cliques];
  cliques = SortBy[cliques, {Length, Identity}];

  (* Print result *)
  print2DVector[cliques]
]

(* Run main *)
main[]
Output:
[[a, b, c], [d, e, f]]

Translation of: Wren
import std/[algorithm, sequtils, sets, strutils, tables]

type StringSet = HashSet[string]

const Empty = initHashSet[string]()

proc bronKerbosch(r: var StringSet;               # Current clique.
                  p: var StringSet;               # Potential candidates to expand the clique.
                  x: var StringSet;               # Vertices already processed.
                  g: Table[string, StringSet];    # Graph represented as an adjacency list.
                  cliques: var seq[seq[string]]   # List to store all maximal cliques.
                 ) =

  if card(p) == 0 and card(x) == 0:
    if card(r) > 2:
      let clique = sorted(toSeq(r.items))
      cliques.add clique
    return

  # Select a pivot vertex from P ∪ X with the maximum degree.
  var pivot = ""
  for v in p + x:
    let n = g.getOrDefault(v, Empty).len
    if n > pivot.len: pivot = v

  # Candidates are vertices in "p" that are not neighbors of the pivot.
  let candidates = p - g[pivot]

  for v in candidates:
    # New clique including vertex "v".
    var newR = r
    newR.incl v
    # New potential candidates are neighbors of "v" in "p".
    let neighbors = g[v]
    var newP = p * neighbors
    # New excluded vertices are neighbors of "v" in "x".
    var newX = x * neighbors
    # Recursive call with updated sets.
    bronKerbosch(newR, newP, newX, g, cliques)
    # Move vertex "v" from "p" to "x".
    p.excl v
    x.incl v


when isMainModule:

  proc cmp(a, b: seq[string]): int =
    ## Comparison procedure to use for sorting.
    result = cmp(a.len, b.len)
    if result == 0:
      for i in 0..a.len:
        result = cmp(a[i], b[i])
        if result != 0: return

  # Define the input edges as a list of tuples.
  let input = [("a", "b"), ("b", "a"), ("a", "c"), ("c", "a"),
               ("b", "c"), ("c", "b"), ("d", "e"), ("e", "d"),
               ("d", "f"), ("f", "d"), ("e", "f"), ("f", "e")]

  # Build the graph as an adjacency list.
  var graph: Table[string, StringSet]
  for (t1, t2) in input:
    graph.mgetOrPut(t1, initHashSet[string]()).incl t2

  # Initialize "r", "p", "x".
  var r, x: StringSet
  var p = toSeq(graph.keys).toHashSet()

  # Initialize list to store cliques.
  var cliques: seq[seq[string]]

  # Execute the Bron-Kerbosch algorithm.
  bronKerbosch(r, p, x, graph, cliques)

  # Sort the cliques for consistent output.
  cliques.sort(cmp)

  # Print each clique.
  for clique in cliques:
    echo clique.join(", ")
Output:
a, b, c
d, e, f
Works with: Perl version 5.22.1
Translation of: Java
use strict;
use warnings;
use Data::Dumper;

my @edges = (
    { start => "a", end => "b" },
    { start => "b", end => "a" },
    { start => "a", end => "c" },
    { start => "c", end => "a" },
    { start => "b", end => "c" },
    { start => "c", end => "b" },
    { start => "d", end => "e" },
    { start => "e", end => "d" },
    { start => "d", end => "f" },
    { start => "f", end => "d" },
    { start => "e", end => "f" },
    { start => "f", end => "e" },
);

# Build the graph as an adjacency list
my %graph;
foreach my $edge (@edges) {
    push @{ $graph{ $edge->{start} } }, $edge->{end};
}

# Initialize current clique, candidates, and processed vertices
my @current_clique;
my %candidates = map { $_ => 1 } keys %graph;
my %processed_vertices;

# Execute the Bron-Kerbosch algorithm to collect the cliques
my @cliques;
bron_kerbosch(\@current_clique, \%candidates, \%processed_vertices, \%graph, \@cliques);

# Sort the cliques for consistent display
@cliques = sort { list_comparator($a, $b) } @cliques;

# Display the cliques
print Dumper(\@cliques);

sub bron_kerbosch {
    my ($current_clique, $candidates, $processed_vertices, $graph, $cliques) = @_;

    if (!%$candidates && !%$processed_vertices) {
        if (@$current_clique > 2) {
            push @$cliques, [@$current_clique];
        }
        return;
    }

    # Select a pivot vertex from 'candidates' union 'processedVertices' with the maximum degree
    my %union = (%$candidates, %$processed_vertices);
    my $pivot = max_degree_vertex(\%union, $graph);

    # 'possibles' are vertices in 'candidates' that are not neighbors of the 'pivot'
    my %possibles = %$candidates;
    delete $possibles{$_} for @{ $graph->{$pivot} };

    foreach my $vertex (keys %possibles) {
        # Create a new clique including 'vertex'
        my @new_cliques = @$current_clique;
        push @new_cliques, $vertex;

        # 'newCandidates' are the members of 'candidates' that are neighbors of 'vertex'
        my %neighbors = map { $_ => 1 } @{ $graph->{$vertex} };
        my %new_candidates = map { $_ => 1 } grep { $neighbors{$_} } keys %$candidates;

        # 'newProcessedVertices' are members of 'processedVertices' that are neighbors of 'vertex'
        my %new_processed_vertices = map { $_ => 1 } grep { $neighbors{$_} } keys %$processed_vertices;

        # Recursive call with the updated sets
        bron_kerbosch(\@new_cliques, \%new_candidates, \%new_processed_vertices, $graph, $cliques);

        # Move 'vertex' from 'candidates' to 'processedVertices'
        delete $candidates->{$vertex};
        $processed_vertices->{$vertex} = 1;
    }
}

sub max_degree_vertex {
    my ($vertices, $graph) = @_;
    my ($max_vertex, $max_degree) = (undef, -1);

    foreach my $vertex (keys %$vertices) {
        my $degree = scalar @{ $graph->{$vertex} };
        if ($degree > $max_degree) {
            $max_degree = $degree;
            $max_vertex = $vertex;
        }
    }

    return $max_vertex;
}

sub list_comparator {
    my ($list1, $list2) = @_;
    my $min_length = $#$list1 < $#$list2 ? $#$list1 : $#$list2;

    for my $i (0..$min_length) {
        my $comparison = $list1->[$i] cmp $list2->[$i];
        if ($comparison != 0) {
            return $comparison;
        }
    }

    return $#$list1 <=> $#$list2;
}
Output:
$VAR1 = [
          [
            'a',
            'c',
            'b'
          ],
          [
            'f',
            'e',
            'd'
          ]
        ];
Translation of: Wren

Note this uses an updated sets2.e, the experimental version of sets.e - download from the github repo and remove the requires statement to run (if/since 1.0.6 not yet shipped). Code to clean up no longer needed sets omitted for clarity and out of sheer laziness.

with javascript_semantics
requires("1.0.6")
include sets2.e

// Cliques: List to store all maximal cliques (List of Lists of strings)
sequence cliques = {}

procedure BronKerbosch(integer r, p, x, g)
    // r: Current clique (Set of strings)
    // p: Potential candidates to expand the clique (Set of strings)
    // x: Vertices already processed (Set of strings)
    // g: Graph represented as an adjacency list (Dict of strings to List of Strings)

    if is_empty(p) and is_empty(x) then
        if set_size(r)>2 then
            cliques = append(cliques,get_members(r))
        end if
        return
    end if
    
    // Select a pivot vertex from union(p,x) with the maximum degree.
    object pivot
    integer maxC = -1
    for v in union(p,x) do
        integer l = length(getd(v,g))
        if l>maxC then
            {maxC,pivot} = {l,v}
        end if
    end for

    // Candidates are vertices in P that are not neighbours of the pivot.
    sequence candidates = difference(p, getd(pivot,g), symmetric:=false)

    for v in candidates do
        // New clique including vertex v.
        integer newR = new_set(r)
        add_member(newR,v)

        // New potential candidates are neighbours of v in p.
        // New excluded vertices are neighbours of v in x.
        sequence neighbours = getd(v,g)
        integer newP = intersection(p,neighbours,-1),
                newX = intersection(x,neighbours,-1)

        // Recursive call with updated sets.
        BronKerbosch(newR, newP, newX, g)

        // Move vertex v from p to x.
        remove_member(p,v)
        add_member(x,v)
    end for
end procedure

// Define the input edges as a list of tuples.
constant input = {
    {"a", "b"}, {"b", "a"}, {"a", "c"}, {"c", "a"},
    {"b", "c"}, {"c", "b"}, {"d", "e"}, {"e", "d"},
    {"d", "f"}, {"f", "d"}, {"e", "f"}, {"f", "e"}
}

// Build the graph as an adjacency list.
integer graph = new_dict()
for t in input do
    setd(t[1],unique(deep_copy(getdd(t[1],{},graph))&{t[2]}),graph)
end for

// Initialize r, p, x.
integer r = new_set(),
        p = new_set(getd_all_keys(graph)),
        x = new_set()

// Execute the Bron-Kerbosch algorithm.
BronKerbosch(r, p, x, graph)

// Sort cliques for consistent output.
cliques = sort(deep_copy(cliques))
?cliques
Output:
{{"a","b","c"},{"d","e","f"}}
Works with: PowerShell version 6.2.3
Translation of: Python
# Global variable to store cliques
$global:cliques = @()

# Edge class equivalent
class Edge {
    [string]$Start
    [string]$End
    
    Edge([string]$start, [string]$end) {
        $this.Start = $start
        $this.End = $end
    }
}

function Print-Vector {
    param([array]$vec)
    
    $sortedVec = $vec | Sort-Object
    Write-Host -NoNewline "[$($sortedVec -join ', ')]"
}

function Print-2DVector {
    param([array]$vecs)
    
    Write-Host -NoNewline "["
    for ($i = 0; $i -lt ($vecs.Count - 1); $i++) {
        Print-Vector $vecs[$i]
        Write-Host -NoNewline ", "
    }
    if ($vecs.Count -gt 0) {
        Print-Vector $vecs[-1]
    }
    Write-Host "]"
}

function Invoke-BronKerbosch {
    param(
        [System.Collections.Generic.HashSet[string]]$CurrentClique,
        [System.Collections.Generic.HashSet[string]]$Candidates,
        [System.Collections.Generic.HashSet[string]]$ProcessedVertices,
        [hashtable]$Graph
    )
    
    if ($Candidates.Count -eq 0 -and $ProcessedVertices.Count -eq 0) {
        if ($CurrentClique.Count -gt 2) {
            $cliqueArray = @()
            foreach ($item in $CurrentClique) {
                $cliqueArray += $item
            }
            $global:cliques += ,$cliqueArray
        }
        return
    }
    
    # Select a pivot vertex from 'Candidates' union 'ProcessedVertices' with the maximum degree
    $unionSet = [System.Collections.Generic.HashSet[string]]::new()
    $unionSet.UnionWith($Candidates)
    $unionSet.UnionWith($ProcessedVertices)
    
    $pivot = $null
    $maxDegree = -1
    foreach ($vertex in $unionSet) {
        $degree = $Graph[$vertex].Count
        if ($degree -gt $maxDegree) {
            $maxDegree = $degree
            $pivot = $vertex
        }
    }
    
    # 'Possibles' are vertices in 'Candidates' that are not neighbors of the 'pivot'
    $possibles = [System.Collections.Generic.HashSet[string]]::new()
    $possibles.UnionWith($Candidates)
    $possibles.ExceptWith($Graph[$pivot])
    
    # Create a copy of candidates to iterate over (to avoid modification during iteration)
    $candidatesCopy = [System.Collections.Generic.HashSet[string]]::new()
    $candidatesCopy.UnionWith($Candidates)
    
    foreach ($vertex in $possibles) {
        # Create a new clique including 'vertex'
        $newClique = [System.Collections.Generic.HashSet[string]]::new()
        $newClique.UnionWith($CurrentClique)
        $newClique.Add($vertex) | Out-Null
        
        # 'NewCandidates' are the members of 'Candidates' that are neighbors of 'vertex'
        $newCandidates = [System.Collections.Generic.HashSet[string]]::new()
        $newCandidates.UnionWith($Candidates)
        $newCandidates.IntersectWith($Graph[$vertex])
        
        # 'NewProcessedVertices' are members of 'ProcessedVertices' that are neighbors of 'vertex'
        $newProcessedVertices = [System.Collections.Generic.HashSet[string]]::new()
        $newProcessedVertices.UnionWith($ProcessedVertices)
        $newProcessedVertices.IntersectWith($Graph[$vertex])
        
        # Recursive call with the updated sets
        Invoke-BronKerbosch $newClique $newCandidates $newProcessedVertices $Graph
        
        # Move 'vertex' from 'Candidates' to 'ProcessedVertices'
        $Candidates.Remove($vertex) | Out-Null
        $ProcessedVertices.Add($vertex) | Out-Null
    }
}

function Main {
    $global:cliques = @()
    
    # Define edges
    $edges = @(
        [Edge]::new("a", "b"), [Edge]::new("b", "a"), [Edge]::new("a", "c"), [Edge]::new("c", "a"),
        [Edge]::new("b", "c"), [Edge]::new("c", "b"), [Edge]::new("d", "e"), [Edge]::new("e", "d"),
        [Edge]::new("d", "f"), [Edge]::new("f", "d"), [Edge]::new("e", "f"), [Edge]::new("f", "e")
    )
    
    # Build the graph as an adjacency list using hashtable
    $graph = @{}
    foreach ($edge in $edges) {
        if (-not $graph.ContainsKey($edge.Start)) {
            $graph[$edge.Start] = [System.Collections.Generic.HashSet[string]]::new()
        }
        $graph[$edge.Start].Add($edge.End) | Out-Null
    }
    
    # Initialize current clique, candidates, and processed vertices
    $currentClique = [System.Collections.Generic.HashSet[string]]::new()
    $candidates = [System.Collections.Generic.HashSet[string]]::new()
    foreach ($key in $graph.Keys) {
        $candidates.Add($key) | Out-Null
    }
    $processedVertices = [System.Collections.Generic.HashSet[string]]::new()
    
    # Execute the Bron-Kerbosch algorithm to collect the cliques
    Invoke-BronKerbosch $currentClique $candidates $processedVertices $graph
    
    # Sort the cliques for consistent display
    $global:cliques = $global:cliques | Sort-Object { $_.Count }, { $_ -join ',' }
    
    # Display the cliques
    Print-2DVector $global:cliques
}

# Run the main function
Main
Output:
[[a, b, c], [d, e, f]]
from collections import defaultdict

# Global variable to store cliques
cliques = []

class Edge:
    def __init__(self, start, end):
        self.start = start
        self.end = end

def print_vector(vec):
    print("[" + ", ".join(sorted(map(str, vec))) + "]", end="")

def print_2D_vector(vecs):
    print("[", end="")
    for i in range(len(vecs) - 1):
        print_vector(vecs[i])
        print(", ", end="")
    print_vector(vecs[-1])
    print("]")

def bron_kerbosch(current_clique, candidates, processed_vertices, graph):
    global cliques

    if not candidates and not processed_vertices:
        if len(current_clique) > 2:
            cliques.append(list(current_clique))
        return

    # Select a pivot vertex from 'candidates' union 'processed_vertices' with the maximum degree
    union_set = candidates.union(processed_vertices)
    pivot = max(union_set, key=lambda v: len(graph[v]))

    # 'possibles' are vertices in 'candidates' that are not neighbors of the 'pivot'
    possibles = candidates.difference(graph[pivot])

    for vertex in possibles:
        # Create a new clique including 'vertex'
        new_clique = current_clique.union({vertex})

        # 'new_candidates' are the members of 'candidates' that are neighbors of 'vertex'
        new_candidates = candidates.intersection(graph[vertex])

        # 'new_processed_vertices' are members of 'processed_vertices' that are neighbors of 'vertex'
        new_processed_vertices = processed_vertices.intersection(graph[vertex])

        # Recursive call with the updated sets
        bron_kerbosch(new_clique, new_candidates, new_processed_vertices, graph)

        # Move 'vertex' from 'candidates' to 'processed_vertices'
        candidates.remove(vertex)
        processed_vertices.add(vertex)

def main():
    global cliques

    # Define edges
    edges = [
        Edge("a", "b"), Edge("b", "a"), Edge("a", "c"), Edge("c", "a"),
        Edge("b", "c"), Edge("c", "b"), Edge("d", "e"), Edge("e", "d"),
        Edge("d", "f"), Edge("f", "d"), Edge("e", "f"), Edge("f", "e")
    ]

    # Build the graph as an adjacency list
    graph = defaultdict(set)
    for edge in edges:
        graph[edge.start].add(edge.end)

    # Initialize current clique, candidates, and processed vertices
    current_clique = set()
    candidates = set(graph.keys())
    processed_vertices = set()

    # Execute the Bron-Kerbosch algorithm to collect the cliques
    bron_kerbosch(current_clique, candidates, processed_vertices, graph)

    # Sort the cliques for consistent display
    cliques.sort(key=lambda x: (len(x), x))

    # Display the cliques
    print_2D_vector(cliques)

if __name__ == "__main__":
    main()
[[a, b, c], [d, e, f]]

R

Works with: R
Translation of: Python
# Global variable to store cliques
cliques <- list()

# Function to print a vector (sorted)
print_vector <- function(vec) {
  cat("[", paste(sort(as.character(vec)), collapse = ", "), "]", sep = "")
}

# Function to print a list of vectors
print_2D_vector <- function(vecs) {
  cat("[")
  n <- length(vecs)
  for (i in seq_len(n)) {
    print_vector(vecs[[i]])
    if (i < n) cat(", ")
  }
  cat("]\n")
}

# Bron-Kerbosch algorithm implementation
bron_kerbosch <- function(current_clique, candidates, processed_vertices, graph) {
  # Access the global 'cliques' variable
  cliques <<- cliques

  # Base case: if both candidates and processed are empty, we have a maximal clique
  if (length(candidates) == 0 && length(processed_vertices) == 0) {
    if (length(current_clique) > 2) {
      cliques <<- append(cliques, list(sort(current_clique)))
    }
    return()
  }

  # Union of candidates and processed vertices
  union_set <- unique(c(candidates, processed_vertices))

  # Choose pivot with maximum degree
  pivot <- union_set[which.max(sapply(union_set, function(v) length(graph[[v]])))]

  # Possibles: candidates not adjacent to pivot
  possibles <- setdiff(candidates, graph[[pivot]])

  # Iterate over each possible vertex
  for (vertex in possibles) {
    # Neighbors of vertex
    neighbors <- graph[[vertex]]

    # Intersect candidates and processed with neighbors
    new_candidates <- intersect(candidates, neighbors)
    new_processed_vertices <- intersect(processed_vertices, neighbors)

    # Recursive call with updated sets (don't modify original sets)
    bron_kerbosch(c(current_clique, vertex), new_candidates, new_processed_vertices, graph)
  }
}

# Main function
main <- function() {
  # Clear global cliques
  cliques <<- list()

  # Define edges (as character pairs)
  edges <- list(
    c("a", "b"), c("b", "a"), c("a", "c"), c("c", "a"),
    c("b", "c"), c("c", "b"), c("d", "e"), c("e", "d"),
    c("d", "f"), c("f", "d"), c("e", "f"), c("f", "e")
  )

  # Build graph as adjacency list
  graph <- list()
  nodes <- unique(unlist(edges))
  for (node in nodes) {
    graph[[node]] <- c()
  }

  for (edge in edges) {
    start <- edge[1]
    end <- edge[2]
    if (!(end %in% graph[[start]])) {
      graph[[start]] <- c(graph[[start]], end)
    }
  }

  # Initialize Bron-Kerbosch parameters
  current_clique <- c()
  candidates <- names(graph)
  processed_vertices <- c()

  # Run Bron-Kerbosch
  bron_kerbosch(current_clique, candidates, processed_vertices, graph)

  # Remove duplicates and sort cliques by size and content
  unique_cliques <- unique(lapply(cliques, sort))
  cliques <<- unique_cliques[order(sapply(unique_cliques, length), sapply(unique_cliques, function(x) paste(sort(x), collapse = ",")))]

  # Print result
  print_2D_vector(cliques)
}

# Run main
main()
Output:
[[a, b, c], [d, e, f]]
# Returns all maximal Cliques as sorted List of Lists.
sub BronKerbosch ( @undirected_edges ) {

    #| Adjacency list of the whole Graph, as immutable hash (Map) of immutable Sets.
    my Map $G = @undirected_edges
                .map({ |( .[0,1], .[1,0] ) })
                .classify( {.[0]}, :as{.[1]} )
                .duckmap( *.Set )
                .Map;

    #| Number of neighbors in G
    my Map $degree = $G.map({ .key => .value.elems }).Map;

    return gather BK();

    sub BK (
        Set     :$R         = Set.new,              #= Current clique
        SetHash :$P is copy = SetHash.new($G.keys), #= Potential candidates to expand the clique
        SetHash :$X is copy = SetHash.new,          #= Vertices already processed
    ) {
        if !$P and !$X {
            take $R.keys.sort.cache if $R.elems > 2;
            return;
        }

        my $Pivot = ($P$X).max({ $degree{$_} }).key;

        my @Candidates = ( $P (-) $G{$Pivot} ).keys;

        for $G{@Candidates}:kv -> $v, $Neighbors_of_v {

            &?ROUTINE.(     # Recursive call with updated sets
                R => ($R$v),
                P => ($P$Neighbors_of_v),
                X => ($X$Neighbors_of_v),
            );

            # Move vertex v from P to X
            $P{$v} = False;
            $X{$v} = True;
        }
    }
}

say .join(",") for sort BronKerbosch <a-b a-c b-c d-e d-f e-f>».split("-");
Output:
a,b,c
d,e,f
require 'set'

def bron_kerbosch_v2(r, p, x, g, cliques)
  if p.empty? && x.empty?
    if r.size > 2
      cliques << r.to_a.sort
    end
    return
  end

  pivot = (p | x).max_by { |v| g[v]&.size.to_i }

  if pivot
    neighbors = g[pivot] || Set.new
    candidates = p - neighbors

    candidates.each do |v|
      new_r = r.dup.add(v)
      neighbors_v = g[v] || Set.new
      new_p = p & neighbors_v
      new_x = x & neighbors_v

      bron_kerbosch_v2(new_r, new_p, new_x, g, cliques)

      p.delete(v)
      x.add(v)
    end
  end
end

def main
  input = [
    ['a', 'b'], ['b', 'a'], ['a', 'c'], ['c', 'a'],
    ['b', 'c'], ['c', 'b'], ['d', 'e'], ['e', 'd'],
    ['d', 'f'], ['f', 'd'], ['e', 'f'], ['f', 'e']
  ]

  graph = Hash.new { |hash, key| hash[key] = Set.new }
  input.each do |src, dest|
    graph[src].add(dest)
  end

  r = Set.new
  p = graph.keys.to_set
  x = Set.new
  cliques = []

  bron_kerbosch_v2(r, p, x, graph, cliques)

  sorted_cliques = cliques.sort

  sorted_cliques.each do |clique|
    puts clique.join(', ')
  end
end

main
use std::collections::{HashMap, HashSet};

fn bron_kerbosch_v2(
    r: &HashSet<String>,
    p: &mut HashSet<String>,
    x: &mut HashSet<String>,
    g: &HashMap<String, HashSet<String>>,
    cliques: &mut Vec<Vec<String>>,
) {
    if p.is_empty() && x.is_empty() {
        if r.len() > 2 {
            let mut clique: Vec<String> = r.iter().cloned().collect();
            clique.sort();
            cliques.push(clique);
        }
        return;
    }

    // Choose a pivot with the maximum degree in P ∪ X
    let pivot = p
        .union(x)
        .max_by_key(|v| g.get(*v).map_or(0, |neighbors| neighbors.len()))
        .cloned();

    if let Some(pivot_vertex) = pivot {
        let neighbors = g.get(&pivot_vertex).cloned().unwrap_or_default();
        let candidates: Vec<String> = p.difference(&neighbors).cloned().collect();

        for v in candidates {
            // New R is R ∪ {v}
            let mut new_r = r.clone();
            new_r.insert(v.clone());

            // New P is P ∩ N(v)
            let neighbors_v = g.get(&v).cloned().unwrap_or_default();
            let mut new_p = p.intersection(&neighbors_v).cloned().collect::<HashSet<String>>();

            // New X is X ∩ N(v)
            let mut new_x = x.intersection(&neighbors_v).cloned().collect::<HashSet<String>>();

            // Recursive call
            bron_kerbosch_v2(&new_r, &mut new_p, &mut new_x, g, cliques);

            // Move v from P to X
            p.remove(&v);
            x.insert(v);
        }
    }
}

fn main() {
    // Define the input edges
    let input = vec![
        ("a", "b"),
        ("b", "a"),
        ("a", "c"),
        ("c", "a"),
        ("b", "c"),
        ("c", "b"),
        ("d", "e"),
        ("e", "d"),
        ("d", "f"),
        ("f", "d"),
        ("e", "f"),
        ("f", "e"),
    ];

    // Build the graph as an adjacency list
    let mut graph: HashMap<String, HashSet<String>> = HashMap::new();
    for (src, dest) in input.iter() {
        graph
            .entry(src.to_string())
            .or_insert_with(HashSet::new)
            .insert(dest.to_string());
    }

    // Initialize R, P, X
    let r: HashSet<String> = HashSet::new();
    let mut p: HashSet<String> = graph.keys().cloned().collect();
    let mut x: HashSet<String> = HashSet::new();

    // Collect cliques
    let mut cliques: Vec<Vec<String>> = Vec::new();
    bron_kerbosch_v2(&r, &mut p, &mut x, &graph, &mut cliques);

    // Sort the cliques for consistent output
    let mut sorted_cliques = cliques.clone();
    sorted_cliques.sort();

    // Print each clique
    for clique in sorted_cliques {
        println!("{}", clique.join(", "));
    }
}
Works with: Scala version 2.13.8
Translation of: Java
import scala.collection.mutable
import scala.collection.immutable.TreeSet

object BronKerboschAlgorithm {
  
  case class Edge(start: String, end: String)
  
  private var cliques: mutable.ListBuffer[List[String]] = mutable.ListBuffer.empty[List[String]]
  
  def main(args: Array[String]): Unit = {
    val edges = List(
      Edge("a", "b"), Edge("b", "a"), Edge("a", "c"), Edge("c", "a"),
      Edge("b", "c"), Edge("c", "b"), Edge("d", "e"), Edge("e", "d"),
      Edge("d", "f"), Edge("f", "d"), Edge("e", "f"), Edge("f", "e")
    )
    
    // Build the graph as an adjacency list
    val graph: mutable.Map[String, mutable.Set[String]] = mutable.Map.empty
    edges.foreach { edge =>
      graph.getOrElseUpdate(edge.start, mutable.Set.empty[String]).add(edge.end)
    }
    
    // Initialize current clique, candidates and processed vertices
    val currentClique: TreeSet[String] = TreeSet.empty[String]
    val candidates: mutable.Set[String] = mutable.Set(graph.keySet.toSeq: _*)
    val processedVertices: mutable.Set[String] = mutable.Set.empty[String]
    
    // Execute the Bron-Kerbosch algorithm to collect the cliques
    val immutableGraph = graph.map { case (k, v) => k -> v.toSet }.toMap
    bronKerbosch(currentClique, candidates, processedVertices, immutableGraph)
    
    // Sort the cliques for consistent display
    cliques = cliques.sortWith(listComparator)
    
    // Display the cliques
    println(cliques.toList)
  }
  
  private def bronKerbosch(
    currentClique: TreeSet[String],
    candidates: mutable.Set[String],
    processedVertices: mutable.Set[String],
    graph: Map[String, Set[String]]
  ): Unit = {
    
    if (candidates.isEmpty && processedVertices.isEmpty) {
      if (currentClique.size > 2) {
        val clique = currentClique.toList
        cliques += clique
      }
      return
    }
    
    // Select a pivot vertex from 'candidates' union 'processedVertices' with the maximum degree
    val union = candidates ++ processedVertices
    val pivot = union.maxBy(vertex => graph(vertex).size)
    
    // 'possibles' are vertices in 'candidates' that are not neighbours of the 'pivot'
    val possibles = candidates -- graph(pivot)
    
    for (vertex <- possibles.toList) { // Convert to List to avoid concurrent modification
      // Create a new clique including 'vertex'
      val newCliques = currentClique + vertex
      
      // 'newCandidates' are the members of 'candidates' that are neighbours of 'vertex'
      val neighbours = graph(vertex)
      val newCandidates = candidates.intersect(neighbours)
      
      // 'newProcessedVertices' are members of 'processedVertices' that are neighbours of 'vertex'
      val newProcessedVertices = processedVertices.intersect(neighbours)
      
      // Recursive call with the updated sets
      bronKerbosch(newCliques, newCandidates, newProcessedVertices, graph)
      
      // Move 'vertex' from 'candidates' to 'processedVertices'
      candidates.remove(vertex)
      processedVertices.add(vertex)
    }
  }
  
  private def listComparator(list1: List[String], list2: List[String]): Boolean = {
    val minSize = math.min(list1.size, list2.size)
    for (i <- 0 until minSize) {
      val comparison = list1(i).compare(list2(i))
      if (comparison != 0) {
        return comparison < 0
      }
    }
    list1.size < list2.size
  }
}
Output:
List(List(a, b, c), List(d, e, f))

Works with: Swift
Translation of: Java
var cliques: [[String]] = []

struct Edge {
    let start: String
    let end: String
}

func bronKerbosch(currentClique: Set<String>, candidates: Set<String>, processedVertices: Set<String>, graph: [String: Set<String>]) {
    if candidates.isEmpty && processedVertices.isEmpty {
        if currentClique.count > 2 {
            let clique = Array(currentClique).sorted()
            cliques.append(clique)
        }
        return
    }

    let union = candidates.union(processedVertices)
    let pivot = union.max(by: { graph[$0]!.count < graph[$1]!.count })!

    let possibles = candidates.subtracting(graph[pivot]!)

    for vertex in possibles {
        var newClique = currentClique
        newClique.insert(vertex)

        let neighbors = graph[vertex]!
        let newCandidates = candidates.intersection(neighbors)
        let newProcessedVertices = processedVertices.intersection(neighbors)

        bronKerbosch(currentClique: newClique, candidates: newCandidates, processedVertices: newProcessedVertices, graph: graph)

        var newCandidatesSet = candidates
        newCandidatesSet.remove(vertex)

        var newProcessedVerticesSet = processedVertices
        newProcessedVerticesSet.insert(vertex)

        bronKerbosch(currentClique: currentClique, candidates: newCandidatesSet, processedVertices: newProcessedVerticesSet, graph: graph)
    }
}

func main() {
    let edges = [
        Edge(start: "a", end: "b"), Edge(start: "b", end: "a"),
        Edge(start: "a", end: "c"), Edge(start: "c", end: "a"),
        Edge(start: "b", end: "c"), Edge(start: "c", end: "b"),
        Edge(start: "d", end: "e"), Edge(start: "e", end: "d"),
        Edge(start: "d", end: "f"), Edge(start: "f", end: "d"),
        Edge(start: "e", end: "f"), Edge(start: "f", end: "e")
    ]

    var graph: [String: Set<String>] = [:]
    for edge in edges {
        if graph[edge.start] == nil {
            graph[edge.start] = Set<String>()
        }
        graph[edge.start]?.insert(edge.end)
    }

    let currentClique = Set<String>()
    let candidates = Set<String>(graph.keys)
    let processedVertices = Set<String>()

    bronKerbosch(currentClique: currentClique, candidates: candidates, processedVertices: processedVertices, graph: graph)

    cliques.sort { (list1, list2) -> Bool in
        for i in 0..<min(list1.count, list2.count) {
            if list1[i] != list2[i] {
                return list1[i] < list2[i]
            }
        }
        return list1.count < list2.count
    }

    // Remove duplicates
    var uniqueCliques = [[String]]()
    var seen = Set<Set<String>>()
    
    for clique in cliques {
        let cliqueSet = Set(clique)
        if !seen.contains(cliqueSet) {
            seen.insert(cliqueSet)
            uniqueCliques.append(clique)
        }
    }
    
    print(uniqueCliques)
}

main()
Output:
[["a", "b", "c"], ["d", "e", "f"]]

Works with: Tcl version 8.6
Translation of: C++
# Global variable to store cliques
set cliques {}

# Procedure to print a list (equivalent to print_vector)
proc print_list {lst} {
    puts -nonewline "\["
    set len [llength $lst]
    for {set i 0} {$i < $len} {incr i} {
        if {$i > 0} {
            puts -nonewline ", "
        }
        puts -nonewline [lindex $lst $i]
    }
    puts -nonewline "\]"
}

# Procedure to print 2D list (equivalent to print_2D_vector)
proc print_2D_list {lists} {
    puts -nonewline "\["
    set len [llength $lists]
    for {set i 0} {$i < $len} {incr i} {
        if {$i > 0} {
            puts -nonewline ", "
        }
        print_list [lindex $lists $i]
    }
    puts "\]"
}

# Helper procedure to find set difference
proc set_difference {set1 set2} {
    set result {}
    foreach item $set1 {
        if {$item ni $set2} {
            lappend result $item
        }
    }
    return $result
}

# Helper procedure to find set intersection
proc set_intersection {set1 set2} {
    set result {}
    foreach item $set1 {
        if {$item in $set2} {
            lappend result $item
        }
    }
    return $result
}

# Helper procedure to find union of two sets
proc set_union {set1 set2} {
    set result {}
    foreach item $set1 {
        lappend result $item
    }
    foreach item $set2 {
        if {$item ni $result} {
            lappend result $item
        }
    }
    return $result
}

# Bron-Kerbosch algorithm implementation
proc bron_kerbosch {current_clique candidates processed_vertices graph} {
    global cliques
    
    # Check if both candidates and processed_vertices are empty
    if {[llength $candidates] == 0 && [llength $processed_vertices] == 0} {
        if {[llength $current_clique] > 2} {
            lappend cliques $current_clique
            set cliques $cliques
        }
        return
    }
    
    # Select pivot vertex with maximum degree
    set union_set [set_union $candidates $processed_vertices]
    set pivot ""
    set max_degree -1
    
    foreach vertex $union_set {
        set degree [llength [dict get $graph $vertex]]
        if {$degree > $max_degree} {
            set max_degree $degree
            set pivot $vertex
        }
    }
    
    # Find possibles: vertices in candidates that are not neighbors of pivot
    set pivot_neighbors [dict get $graph $pivot]
    set possibles [set_difference $candidates $pivot_neighbors]
    
    foreach vertex $possibles {
        # Create new clique including vertex
        set new_clique $current_clique
        lappend new_clique $vertex
        
        # Find new candidates: members of candidates that are neighbors of vertex
        set vertex_neighbors [dict get $graph $vertex]
        set new_candidates [set_intersection $candidates $vertex_neighbors]
        
        # Find new processed vertices: members of processed_vertices that are neighbors of vertex
        set new_processed_vertices [set_intersection $processed_vertices $vertex_neighbors]
        
        # Recursive call
        bron_kerbosch $new_clique $new_candidates $new_processed_vertices $graph
        
        # Move vertex from candidates to processed_vertices
        set candidates [set_difference $candidates [list $vertex]]
        lappend processed_vertices $vertex
    }
}

# Main procedure
proc main {} {
    global cliques
    
    # Define edges
    set edges {
        {a b} {b a} {a c} {c a}
        {b c} {c b} {d e} {e d}
        {d f} {f d} {e f} {f e}
    }
    
    # Build graph as adjacency list (using dictionary)
    set graph [dict create]
    foreach edge $edges {
        set start [lindex $edge 0]
        set end [lindex $edge 1]
        if {[dict exists $graph $start]} {
            set neighbors [dict get $graph $start]
            if {$end ni $neighbors} {
                lappend neighbors $end
                dict set graph $start $neighbors
            }
        } else {
            dict set graph $start [list $end]
        }
    }
    
    # Initialize current clique, candidates and processed vertices
    set current_clique {}
    set candidates {}
    dict for {vertex neighbors} $graph {
        lappend candidates $vertex
    }
    set processed_vertices {}
    
    # Execute Bron-Kerbosch algorithm
    bron_kerbosch $current_clique $candidates $processed_vertices $graph
    
    # Sort cliques for consistent display
    # First sort each clique internally
    set sorted_cliques {}
    foreach clique $cliques {
        set sorted_clique [lsort $clique]
        lappend sorted_cliques $sorted_clique
    }
    
    # Then sort cliques by lexicographic order
    proc compare_cliques {a b} {
        set len_a [llength $a]
        set len_b [llength $b]
        set min_len [expr {$len_a < $len_b ? $len_a : $len_b}]
        
        for {set i 0} {$i < $min_len} {incr i} {
            set elem_a [lindex $a $i]
            set elem_b [lindex $b $i]
            if {$elem_a ne $elem_b} {
                return [string compare $elem_a $elem_b]
            }
        }
        return [expr {$len_a - $len_b}]
    }
    
    set cliques [lsort -command compare_cliques $sorted_cliques]
    
    # Display the cliques
    print_2D_list $cliques
}

# Run the main procedure
main
Output:
[[a, b, c], [d, e, f]]

Library: Wren-set
Library: Wren-sort
import "./set" for Set
import "./sort" for Sort

// r: Current clique (Set of strings)
// p: Potential candidates to expand the clique (Set of strings)
// x: Vertices already processed (Set of strings)
// g: Graph represented as an adjacency list (Map of strings to Sets)
// Cliques: List to store all maximal cliques (List of Lists of strings)
var BronKerbosch = Fn.new { |r, p, x, g, cliques|
    if (p.isEmpty && x.isEmpty) {
        if (r.count > 2) {
            var clique = r.toList
            Sort.quick(clique)
            cliques.add(clique)
        }
        return
    }

    // Select a pivot vertex from P ∪ X with the maximum degree.
    var pivot = p.union(x).max { |v| g[v] ? g[v].count : 0 }

    // Candidates are vertices in P that are not neighbors of the pivot.
    var candidates = p.except(g[pivot])

    for (v in candidates) {
        // New clique including vertex v.
        var newR = r.copy()
        newR.add(v)

        // New potential candidates are neighbors of v in p.
        var neighbors = g[v]
        var newP = p.intersect(neighbors)

        // New excluded vertices are neighbors of v in x.
        var newX = x.intersect(neighbors)

        // Recursive call with updated sets.
        BronKerbosch.call(newR, newP, newX, g, cliques)

        // Move vertex v from p to x.
        p.remove(v)
        x.add(v)
    }
}

// Define the input edges as a list of tuples.
var input = [
    ["a", "b"], ["b", "a"], ["a", "c"], ["c", "a"],
    ["b", "c"], ["c", "b"], ["d", "e"], ["e", "d"],
    ["d", "f"], ["f", "d"], ["e", "f"], ["f", "e"]
]

// Build the graph as an adjacency list.
var graph = {}
for (t in input) {
    if (!graph.containsKey(t[0])) graph[t[0]] = Set.new()
    graph[t[0]].add(t[1])
}

// Initialize r, p, x.
var r = Set.new()
var p = Set.new(graph.keys)
var x = Set.new()

// Initialize list to store cliques.
var cliques = []

// Execute the Bron-Kerbosch algorithm.
BronKerbosch.call(r, p, x, graph, cliques)

// Sort the cliqes for consistent output.
Sort.quick(cliques)

// Print each clique.
for (clique in cliques) {
    System.print(clique.join(", "))
}
Output:
a, b, c
d, e, f
Works with: Zig version 0.14.0
Translation of: C++
const std = @import("std");

const Edge = struct {
    start: []const u8,
    end: []const u8,
};

fn printVector(vec: []const []const u8) !void {
    const stdout = std.io.getStdOut().writer();
    try stdout.print("[", .{});
    for (vec, 0..) |element, i| {
        if (i < vec.len - 1) {
            try stdout.print("{s}, ", .{element});
        } else {
            try stdout.print("{s}", .{element});
        }
    }
    try stdout.print("]", .{});
}

fn print2DVector(vecs: []const []const []const u8) !void {
    const stdout = std.io.getStdOut().writer();
    try stdout.print("[", .{});
    for (vecs, 0..) |vec, i| {
        try printVector(vec);
        if (i < vecs.len - 1) {
            try stdout.print(", ", .{});
        }
    }
    try stdout.print("]\n", .{});
}

fn bronKerbosch(
    allocator: std.mem.Allocator,
    currentClique: *std.StringArrayHashMap([]const u8),
    candidates: *std.StringArrayHashMap([]const u8),
    processedVertices: *std.StringArrayHashMap([]const u8),
    graph: *std.StringArrayHashMap(std.StringHashMap(void)),
    cliques: *std.ArrayList([]const []const u8),
) !void {
    if (candidates.count() == 0 and processedVertices.count() == 0) {
        if (currentClique.count() > 2) {
            var cliqueList = std.ArrayList([]const u8).init(allocator);
            var it = currentClique.iterator();
            while (it.next()) |entry| {
                try cliqueList.append(entry.key_ptr.*);
            }
            const cliqueSlice = try cliqueList.toOwnedSlice();
            try cliques.append(cliqueSlice);
        }
        return;
    }

    // Find pivot with maximum degree
    var pivot: []const u8 = "";
    var maxDegree: usize = 0;
    var it = graph.iterator();
    while (it.next()) |entry| {
        const vertex = entry.key_ptr.*;
        const degree = entry.value_ptr.count();
        if (degree > maxDegree or (maxDegree == 0 and pivot.len == 0)) {
            maxDegree = degree;
            pivot = vertex;
        }
    }

    // Candidates not connected to pivot
    var possibles = std.StringArrayHashMap([]const u8).init(allocator);
    defer possibles.deinit();
    var cand_it = candidates.iterator();
    while (cand_it.next()) |entry| {
        const vertex = entry.key_ptr.*;
        const is_connected = if (graph.get(vertex)) |neighbors|
            neighbors.contains(pivot)
        else
            false;
        if (!is_connected) {
            try possibles.put(vertex, vertex);
        }
    }

    // Iterate over possible vertices
    var poss_it = possibles.iterator();
    while (poss_it.next()) |entry| {
        const vertex = entry.key_ptr.*;

        // New clique
        var newClique = std.StringArrayHashMap([]const u8).init(allocator);
        defer newClique.deinit();
        var clique_it = currentClique.iterator();
        while (clique_it.next()) |clique_entry| {
            try newClique.put(clique_entry.key_ptr.*, clique_entry.key_ptr.*);
        }
        try newClique.put(vertex, vertex);

        // New candidates (neighbors of vertex)
        var newCandidates = std.StringArrayHashMap([]const u8).init(allocator);
        defer newCandidates.deinit();
        if (graph.get(vertex)) |vertexNeighbors| {
            var cand_iter = candidates.iterator();
            while (cand_iter.next()) |cand| {
                if (vertexNeighbors.contains(cand.key_ptr.*)) {
                    try newCandidates.put(cand.key_ptr.*, cand.key_ptr.*);
                }
            }
        }

        // New processed vertices (neighbors of vertex)
        var newProcessed = std.StringArrayHashMap([]const u8).init(allocator);
        defer newProcessed.deinit();
        if (graph.get(vertex)) |vertexNeighbors| {
            var proc_it = processedVertices.iterator();
            while (proc_it.next()) |proc| {
                if (vertexNeighbors.contains(proc.key_ptr.*)) {
                    try newProcessed.put(proc.key_ptr.*, proc.key_ptr.*);
                }
            }
        }

        // Recursive call
        try bronKerbosch(allocator, &newClique, &newCandidates, &newProcessed, graph, cliques);

        // Move vertex from candidates to processed
        _ = candidates.*.swapRemove(vertex);         // swapRemove - O(1)  
        // _ = candidates.*.orderedRemove(vertex);         // orderedRemove - O(N)
        try processedVertices.put(vertex, vertex);
    }
}

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    const edges: []const Edge = &.{
        .{ .start = "a", .end = "b" },
        .{ .start = "b", .end = "a" },
        .{ .start = "a", .end = "c" },
        .{ .start = "c", .end = "a" },
        .{ .start = "b", .end = "c" },
        .{ .start = "c", .end = "b" },
        .{ .start = "d", .end = "e" },
        .{ .start = "e", .end = "d" },
        .{ .start = "d", .end = "f" },
        .{ .start = "f", .end = "d" },
        .{ .start = "e", .end = "f" },
        .{ .start = "f", .end = "e" },
    };

    // Initialize graph
    var graph = std.StringArrayHashMap(std.StringHashMap(void)).init(allocator);
    defer graph.deinit();
    for (edges) |edge| {
        if (!graph.contains(edge.start)) {
            try graph.put(edge.start, std.StringHashMap(void).init(allocator));
        }
        if (!graph.contains(edge.end)) {
            try graph.put(edge.end, std.StringHashMap(void).init(allocator));
        }
        if (graph.getPtr(edge.start)) |neighbors| {
            try neighbors.put(edge.end, {});
        }
        if (graph.getPtr(edge.end)) |neighbors| {
            try neighbors.put(edge.start, {});
        }
    }

    var currentClique = std.StringArrayHashMap([]const u8).init(allocator);
    defer currentClique.deinit();

    var candidates = std.StringArrayHashMap([]const u8).init(allocator);
    defer candidates.deinit();
    var graph_it = graph.iterator();
    while (graph_it.next()) |entry| {
        try candidates.put(entry.key_ptr.*, entry.key_ptr.*);
    }

    var processedVertices = std.StringArrayHashMap([]const u8).init(allocator);
    defer processedVertices.deinit();

    var cliques = std.ArrayList([]const []const u8).init(allocator);
    defer cliques.deinit();

    try bronKerbosch(allocator, &currentClique, &candidates, &processedVertices, &graph, &cliques);

    // Sort cliques
    std.sort.heap([]const []const u8, cliques.items, {}, struct {
        fn lessThan(_: void, a: []const []const u8, b: []const []const u8) bool {
            const minLen = @min(a.len, b.len);
            for (0..minLen) |i| {
                if (!std.mem.eql(u8, a[i], b[i])) {
                    return std.mem.lessThan(u8, a[i], b[i]);
                }
            }
            return a.len < b.len;
        }
    }.lessThan);

    try print2DVector(cliques.items);
}
Output:
[[d, f, e]]