Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

simple_graph_alg.h
Go to the documentation of this file.
1 
32 #pragma once
33 
34 #include <ogdf/basic/EdgeArray.h>
35 #include <ogdf/basic/NodeArray.h>
36 #include <ogdf/basic/SList.h>
37 #include <ogdf/basic/tuples.h>
38 
39 namespace ogdf {
40 
43 
49 OGDF_EXPORT void removeSelfLoops(Graph& graph, node v);
50 
52 
58 OGDF_EXPORT bool isLoopFree(const Graph& G);
59 
61 
68 template<class NODELIST>
69 void makeLoopFree(Graph& G, NODELIST& L) {
70  L.clear();
71 
72  safeForEach(G.edges, [&](edge e) {
73  if (e->isSelfLoop()) {
74  L.pushBack(e->source());
75  G.delEdge(e);
76  }
77  });
78 }
79 
82 OGDF_EXPORT bool hasNonSelfLoopEdges(const Graph& G);
83 
85 
90 OGDF_EXPORT void makeLoopFree(Graph& G);
91 
92 
96 
98 
104 OGDF_EXPORT void parallelFreeSort(const Graph& G, SListPure<edge>& edges);
105 
106 
108 
118 OGDF_EXPORT bool isParallelFree(const Graph& G);
119 
121 
134 template<bool ONLY_ONCE = false>
135 int numParallelEdges(const Graph& G) {
136  if (G.numberOfEdges() <= 1) {
137  return 0;
138  }
139 
140  SListPure<edge> edges;
141  parallelFreeSort(G, edges);
142 
143  int num = 0;
144  SListConstIterator<edge> it = edges.begin();
145  edge ePrev = *it, e;
146  for (it = ++it; it.valid(); ++it, ePrev = e) {
147  e = *it;
148  if (ePrev->isParallelDirected(e)) {
149  ++num;
150  if (ONLY_ONCE) {
151  return num;
152  }
153  }
154  }
155 
156  return num;
157 }
158 
160 
172 template<class EDGELIST>
173 void makeParallelFree(Graph& G, EDGELIST& parallelEdges) {
174  parallelEdges.clear();
175  if (G.numberOfEdges() <= 1) {
176  return;
177  }
178 
179  SListPure<edge> edges;
180  parallelFreeSort(G, edges);
181 
182  SListConstIterator<edge> it = edges.begin();
183  edge ePrev = *it++, e;
184  bool bAppend = true;
185  while (it.valid()) {
186  e = *it++;
187  if (e->isParallelDirected(ePrev)) {
188  G.delEdge(e);
189  if (bAppend) {
190  parallelEdges.pushBack(ePrev);
191  bAppend = false;
192  }
193  } else {
194  ePrev = e;
195  bAppend = true;
196  }
197  }
198 }
199 
201 
210 inline void makeParallelFree(Graph& G) {
211  List<edge> parallelEdges;
212  makeParallelFree(G, parallelEdges);
213 }
214 
216 
227 OGDF_EXPORT void parallelFreeSortUndirected(const Graph& G, SListPure<edge>& edges,
228  EdgeArray<int>& minIndex, EdgeArray<int>& maxIndex);
229 
230 
232 
241 OGDF_EXPORT bool isParallelFreeUndirected(const Graph& G);
242 
244 
256 template<bool ONLY_ONCE = false>
258  if (G.numberOfEdges() <= 1) {
259  return 0;
260  }
261 
262  SListPure<edge> edges;
263  EdgeArray<int> minIndex(G), maxIndex(G);
264  parallelFreeSortUndirected(G, edges, minIndex, maxIndex);
265 
266  int num = 0;
267  SListConstIterator<edge> it = edges.begin();
268  edge ePrev = *it, e;
269  for (it = ++it; it.valid(); ++it, ePrev = e) {
270  e = *it;
271  if (minIndex[ePrev] == minIndex[e] && maxIndex[ePrev] == maxIndex[e]) {
272  ++num;
273  if (ONLY_ONCE) {
274  return num;
275  }
276  }
277  }
278 
279  return num;
280 }
281 
283 
294 template<class EDGELIST>
295 void getParallelFreeUndirected(const Graph& G, EdgeArray<EDGELIST>& parallelEdges) {
296  if (G.numberOfEdges() <= 1) {
297  return;
298  }
299 
300  SListPure<edge> edges;
301  EdgeArray<int> minIndex(G), maxIndex(G);
302  parallelFreeSortUndirected(G, edges, minIndex, maxIndex);
303 
304  SListConstIterator<edge> it = edges.begin();
305  edge ePrev = *it++, e;
306  while (it.valid()) {
307  e = *it++;
308  if (minIndex[ePrev] == minIndex[e] && maxIndex[ePrev] == maxIndex[e]) {
309  parallelEdges[ePrev].pushBack(e);
310  } else {
311  ePrev = e;
312  }
313  }
314 }
315 
317 
334 template<class EDGELIST = SListPure<edge>>
335 void makeParallelFreeUndirected(Graph& G, EDGELIST* parallelEdges = nullptr,
336  EdgeArray<int>* cardPositive = nullptr, EdgeArray<int>* cardNegative = nullptr) {
337  if (parallelEdges != nullptr) {
338  parallelEdges->clear();
339  }
340  if (cardPositive != nullptr) {
341  cardPositive->fill(0);
342  }
343  if (cardNegative != nullptr) {
344  cardNegative->fill(0);
345  }
346 
347  if (G.numberOfEdges() <= 1) {
348  return;
349  }
350 
351  EdgeArray<SListPure<edge>> parEdges(G);
352  getParallelFreeUndirected(G, parEdges);
353 
354  for (edge e : G.edges) {
355  for (edge parEdge : parEdges(e)) {
356  if (cardPositive != nullptr && e->source() == parEdge->source()) {
357  (*cardPositive)[e]++;
358  }
359  if (cardNegative != nullptr && e->source() == parEdge->target()) {
360  (*cardNegative)[e]++;
361  }
362  G.delEdge(parEdge);
363  if (parallelEdges != nullptr) {
364  parallelEdges->pushBack(e);
365  }
366  }
367  }
368 }
369 
370 template<class EDGELIST>
371 OGDF_DEPRECATED("The pointer-based makeParallelFreeUndirected() should be used instead.")
372 void makeParallelFreeUndirected(Graph& G, EDGELIST& parallelEdges) {
373  makeParallelFreeUndirected(G, &parallelEdges);
374 }
375 
376 template<class EDGELIST>
377 OGDF_DEPRECATED("The pointer-based makeParallelFreeUndirected() should be used instead.")
378 void makeParallelFreeUndirected(Graph& G, EDGELIST& parallelEdges, EdgeArray<int>& cardPositive,
379  EdgeArray<int>& cardNegative) {
380  makeParallelFreeUndirected(G, &parallelEdges, &cardPositive, &cardNegative);
381 }
382 
386 
388 
394 inline bool isSimple(const Graph& G) { return isLoopFree(G) && isParallelFree(G); }
395 
397 
402 inline void makeSimple(Graph& G) {
403  makeLoopFree(G);
404  makeParallelFree(G);
405 }
406 
408 
415 inline bool isSimpleUndirected(const Graph& G) {
416  return isLoopFree(G) && isParallelFreeUndirected(G);
417 }
418 
420 
425 inline void makeSimpleUndirected(Graph& G) {
426  makeLoopFree(G);
428 }
429 
433 
435 
441 OGDF_EXPORT bool isConnected(const Graph& G);
442 
443 
445 
451 OGDF_EXPORT void makeConnected(Graph& G, List<edge>& added);
452 
454 
459 inline void makeConnected(Graph& G) {
460  List<edge> added;
461  makeConnected(G, added);
462 }
463 
466 
479 OGDF_EXPORT int connectedComponents(const Graph& G, NodeArray<int>& component,
480  List<node>* isolated = nullptr, ArrayBuffer<node>* reprs = nullptr);
481 
483 
489 inline int connectedComponents(const Graph& G) {
490  NodeArray<int> component(G);
491  return connectedComponents(G, component);
492 }
493 
494 
495 OGDF_DEPRECATED("connectedComponents() should be used instead.")
496 
497 
500 inline int connectedIsolatedComponents(const Graph& G, List<node>& isolated,
501  NodeArray<int>& component) {
502  return connectedComponents(G, component, &isolated);
503 }
504 
510 OGDF_EXPORT bool findCutVertices(const Graph& G, ArrayBuffer<node>& cutVertices,
511  ArrayBuffer<Tuple2<node, node>>& addEdges, bool onlyOne = false);
512 
515 
526 inline bool findCutVertices(const Graph& G, ArrayBuffer<node>& cutVertices, bool onlyOne = false) {
528  return findCutVertices(G, cutVertices, addEdges, onlyOne);
529 }
530 
532 
539 OGDF_EXPORT bool isBiconnected(const Graph& G, node& cutVertex);
540 
542 
547 inline bool isBiconnected(const Graph& G) {
548  node cutVertex;
549  return isBiconnected(G, cutVertex);
550 }
551 
553 
559 OGDF_EXPORT void makeBiconnected(Graph& G, List<edge>& added);
560 
562 
567 inline void makeBiconnected(Graph& G) {
568  List<edge> added;
569  makeBiconnected(G, added);
570 }
571 
578 OGDF_EXPORT int biconnectedComponents(const Graph& G, EdgeArray<int>& component,
579  int& nonEmptyComponents);
580 
582 
595 inline int biconnectedComponents(const Graph& G, EdgeArray<int>& component) {
596  int doNotNeedTheValue;
597  return biconnectedComponents(G, component, doNotNeedTheValue);
598 }
599 
605 OGDF_EXPORT bool isTwoEdgeConnected(const Graph& graph, edge& bridge);
606 
620 inline bool isTwoEdgeConnected(const Graph& graph) {
621  edge bridge;
622  return isTwoEdgeConnected(graph, bridge);
623 }
624 
626 
639 OGDF_EXPORT bool isTriconnected(const Graph& G, node& s1, node& s2);
640 
642 
648 inline bool isTriconnected(const Graph& G) {
649  node s1, s2;
650  return isTriconnected(G, s1, s2);
651 }
652 
654 
670 OGDF_EXPORT bool isTriconnectedPrimitive(const Graph& G, node& s1, node& s2);
671 
673 
682 inline bool isTriconnectedPrimitive(const Graph& G) {
683  node s1, s2;
684  return isTriconnectedPrimitive(G, s1, s2);
685 }
686 
688 
698 OGDF_EXPORT void triangulate(Graph& G);
699 
700 
704 
706 
713 OGDF_EXPORT bool isAcyclic(const Graph& G, List<edge>& backedges);
714 
716 
722 inline bool isAcyclic(const Graph& G) {
723  List<edge> backedges;
724  return isAcyclic(G, backedges);
725 }
726 
728 
735 OGDF_EXPORT bool isAcyclicUndirected(const Graph& G, List<edge>& backedges);
736 
738 
744 inline bool isAcyclicUndirected(const Graph& G) {
745  List<edge> backedges;
746  return isAcyclicUndirected(G, backedges);
747 }
748 
750 
757 OGDF_EXPORT void makeAcyclic(Graph& G);
758 
759 
761 
768 OGDF_EXPORT void makeAcyclicByReverse(Graph& G);
769 
770 
772 
779 OGDF_EXPORT bool hasSingleSource(const Graph& G, node& source);
780 
782 
788 inline bool hasSingleSource(const Graph& G) {
789  node source;
790  return hasSingleSource(G, source);
791 }
792 
794 
801 OGDF_EXPORT bool hasSingleSink(const Graph& G, node& sink);
802 
804 
810 inline bool hasSingleSink(const Graph& G) {
811  node sink;
812  return hasSingleSink(G, sink);
813 }
814 
816 
828 OGDF_EXPORT bool isStGraph(const Graph& G, node& s, node& t, edge& st);
829 
831 
839 inline bool isStGraph(const Graph& G) {
840  node s, t;
841  edge st;
842  return isStGraph(G, s, t, st);
843 }
844 
846 
854 OGDF_EXPORT void topologicalNumbering(const Graph& G, NodeArray<int>& num);
855 
856 
858 
867 OGDF_EXPORT int strongComponents(const Graph& G, NodeArray<int>& component);
868 
869 
871 
880 OGDF_EXPORT void makeBimodal(Graph& G, List<edge>& newEdges);
881 
883 
890 inline void makeBimodal(Graph& G) {
891  List<edge> dummy;
892  makeBimodal(G, dummy);
893 }
894 
895 
899 
900 OGDF_DEPRECATED("isAcyclicUndirected() should be used instead.")
901 
902 
905 inline bool isFreeForest(const Graph& G) { return isAcyclicUndirected(G); }
906 
908 
914 inline bool isTree(const Graph& G) {
915  return G.empty() || ((G.numberOfNodes() == G.numberOfEdges() + 1) && isConnected(G));
916 }
917 
919 
927 OGDF_EXPORT bool isArborescenceForest(const Graph& G, List<node>& roots);
928 
930 
936 inline bool isArborescenceForest(const Graph& G) {
937  List<node> roots;
938  return isArborescenceForest(G, roots);
939 }
940 
941 
942 OGDF_DEPRECATED("isArborescenceForest() should be used instead.")
943 
944 
947 inline bool isForest(const Graph& G, List<node>& roots) { return isArborescenceForest(G, roots); }
948 
949 
950 OGDF_DEPRECATED("isArborescenceForest() should be used instead.")
951 
952 
955 inline bool isForest(const Graph& G) { return isArborescenceForest(G); }
956 
958 
965 OGDF_EXPORT bool isArborescence(const Graph& G, node& root);
966 
968 
974 inline bool isArborescence(const Graph& G) {
975  node root;
976  return isArborescence(G, root);
977 }
978 
980 
982 
988 OGDF_EXPORT bool isRegular(const Graph& G);
989 
990 
992 
999 OGDF_EXPORT bool isRegular(const Graph& G, int d);
1000 
1001 
1003 
1011 OGDF_EXPORT bool isBipartite(const Graph& G, NodeArray<bool>& color);
1012 
1014 
1020 inline bool isBipartite(const Graph& G) {
1021  NodeArray<bool> color(G);
1022  return isBipartite(G, color);
1023 }
1024 
1057 OGDF_EXPORT void nodeDistribution(const Graph& G, Array<int>& degdist, std::function<int(node)> func);
1058 
1066 inline void degreeDistribution(const Graph& G, Array<int>& degdist) {
1067  nodeDistribution(G, degdist, [](node v) { return v->degree(); });
1068 }
1069 
1070 }
ogdf::ArrayBuffer
An array that keeps track of the number of inserted elements; also usable as an efficient stack.
Definition: Array.h:46
ogdf
The namespace for all OGDF objects.
Definition: AugmentationModule.h:36
ogdf::removeSelfLoops
void removeSelfLoops(Graph &graph, node v)
Removes all self-loops for a given node v in graph.
OGDF_DEPRECATED
#define OGDF_DEPRECATED(reason)
Mark a class / member / function as deprecated.
Definition: config.h:120
ogdf::hasNonSelfLoopEdges
bool hasNonSelfLoopEdges(const Graph &G)
Returns whether G has edges which are not self-loops.
ogdf::degreeDistribution
void degreeDistribution(const Graph &G, Array< int > &degdist)
Fills degdist with the degree distribution of graph G.
Definition: simple_graph_alg.h:1066
ogdf::SListIteratorBase
Encapsulates a pointer to an ogdf::SList element.
Definition: SList.h:41
ogdf::makeSimple
void makeSimple(Graph &G)
Removes all self-loops and all but one edge of each bundle of parallel edges.
Definition: simple_graph_alg.h:402
ogdf::connectedComponents
int connectedComponents(const Graph &G)
Computes the amount of connected components of G.
Definition: simple_graph_alg.h:489
ogdf::makeLoopFree
void makeLoopFree(Graph &G, NODELIST &L)
Removes all self-loops from G and returns all nodes with self-loops in L.
Definition: simple_graph_alg.h:69
ogdf::makeLoopFree
void makeLoopFree(Graph &G)
Removes all self-loops from G.
ogdf::getParallelFreeUndirected
void getParallelFreeUndirected(const Graph &G, EdgeArray< EDGELIST > &parallelEdges)
Computes the bundles of undirected parallel edges in G.
Definition: simple_graph_alg.h:295
ogdf::makeSimpleUndirected
void makeSimpleUndirected(Graph &G)
Removes all self-loops and all but one edge of each bundle of undirected parallel edges.
Definition: simple_graph_alg.h:425
ogdf::isConnected
bool isConnected(const Graph &G)
Returns true iff G is connected.
ogdf::parallelFreeSortUndirected
void parallelFreeSortUndirected(const Graph &G, SListPure< edge > &edges, EdgeArray< int > &minIndex, EdgeArray< int > &maxIndex)
Sorts the edges of G such that undirected parallel edges come after each other in the list.
ogdf::SListIteratorBase::valid
bool valid() const
Returns true iff the iterator points to an element.
Definition: SList.h:122
ogdf::isAcyclicUndirected
bool isAcyclicUndirected(const Graph &G)
Returns true iff the undirected graph G is acyclic.
Definition: simple_graph_alg.h:744
ogdf::safeForEach
void safeForEach(CONTAINER &container, std::function< void(typename CONTAINER::value_type)> func)
Calls (possibly destructive) func for each element of container.
Definition: list_templates.h:49
ogdf::isForest
bool isForest(const Graph &G)
Returns true iff G is a forest consisting only of arborescences.
Definition: simple_graph_alg.h:955
ogdf::parallelFreeSort
void parallelFreeSort(const Graph &G, SListPure< edge > &edges)
Sorts the edges of G such that parallel edges come after each other in the list.
ogdf::isTriconnected
bool isTriconnected(const Graph &G)
Returns true iff G is triconnected.
Definition: simple_graph_alg.h:648
ogdf::numParallelEdgesUndirected
int numParallelEdgesUndirected(const Graph &G)
Returns the number of undirected parallel edges in G.
Definition: simple_graph_alg.h:257
ogdf::isAcyclic
bool isAcyclic(const Graph &G)
Returns true iff the digraph G is acyclic.
Definition: simple_graph_alg.h:722
ogdf::SListPure::begin
iterator begin()
Returns an iterator to the first element of the list.
Definition: SList.h:332
ogdf::makeAcyclic
void makeAcyclic(Graph &G)
Makes the digraph G acyclic by removing edges.
ogdf::findCutVertices
bool findCutVertices(const Graph &G, ArrayBuffer< node > &cutVertices, bool onlyOne=false)
Finds cut vertices and potential edges that could be added to turn the cut vertices into non-cut vert...
Definition: simple_graph_alg.h:526
ogdf::edge
EdgeElement * edge
The type of edges.
Definition: Graph_d.h:67
ogdf::isTriconnectedPrimitive
bool isTriconnectedPrimitive(const Graph &G)
Returns true iff G is triconnected (using a quadratic time algorithm!).
Definition: simple_graph_alg.h:682
ogdf::numParallelEdges
int numParallelEdges(const Graph &G)
Returns the number of parallel edges in G.
Definition: simple_graph_alg.h:135
ogdf::NodeElement::degree
int degree() const
Returns the degree of the node (indegree + outdegree).
Definition: Graph_d.h:276
ogdf::isFreeForest
bool isFreeForest(const Graph &G)
Returns true iff the undirected graph G is acyclic.
Definition: simple_graph_alg.h:905
ogdf::isStGraph
bool isStGraph(const Graph &G)
Returns true if G is an st-digraph.
Definition: simple_graph_alg.h:839
SList.h
Declaration of singly linked lists and iterators.
ogdf::Array< int >
ogdf::isTwoEdgeConnected
bool isTwoEdgeConnected(const Graph &graph)
Returns true iff graph is 2-edge-connected.
Definition: simple_graph_alg.h:620
EdgeArray.h
Declaration and implementation of EdgeArray class.
ogdf::makeBiconnected
void makeBiconnected(Graph &G)
Makes G biconnected by adding edges.
Definition: simple_graph_alg.h:567
ogdf::isArborescenceForest
bool isArborescenceForest(const Graph &G)
Returns true iff G is a forest consisting only of arborescences.
Definition: simple_graph_alg.h:936
ogdf::strongComponents
int strongComponents(const Graph &G, NodeArray< int > &component)
Computes the strongly connected components of the digraph G.
ogdf::makeAcyclicByReverse
void makeAcyclicByReverse(Graph &G)
Makes the digraph G acyclic by reversing edges.
ogdf::SListPure
Singly linked lists.
Definition: SList.h:39
ogdf::makeParallelFree
void makeParallelFree(Graph &G)
Removes all but one edge of each bundle of parallel edges in G.
Definition: simple_graph_alg.h:210
ogdf::EdgeElement::source
node source() const
Returns the source node of the edge.
Definition: Graph_d.h:391
ogdf::node
NodeElement * node
The type of nodes.
Definition: Graph_d.h:63
ogdf::List< edge >
ogdf::internal::GraphRegisteredArray
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition: Graph_d.h:651
ogdf::makeBimodal
void makeBimodal(Graph &G)
Makes the digraph G bimodal.
Definition: simple_graph_alg.h:890
ogdf::Graph
Data type for general directed graphs (adjacency list representation).
Definition: Graph_d.h:862
ogdf::isSimpleUndirected
bool isSimpleUndirected(const Graph &G)
Returns true iff G contains neither self-loops nor undirected parallel edges.
Definition: simple_graph_alg.h:415
ogdf::isRegular
bool isRegular(const Graph &G, int d)
Checks if a graph is d-regular.
ogdf::isLoopFree
bool isLoopFree(const Graph &G)
Returns true iff G contains no self-loop.
ogdf::biconnectedComponents
int biconnectedComponents(const Graph &G, EdgeArray< int > &component)
Computes the biconnected components of G.
Definition: simple_graph_alg.h:595
NodeArray.h
Declaration and implementation of NodeArray class.
ogdf::triangulate
void triangulate(Graph &G)
Triangulates a planarly embedded graph G by adding edges.
ogdf::connectedIsolatedComponents
int connectedIsolatedComponents(const Graph &G, List< node > &isolated, NodeArray< int > &component)
Definition: simple_graph_alg.h:500
ogdf::hasSingleSource
bool hasSingleSource(const Graph &G)
Returns true iff the digraph G contains exactly one source node (or is empty).
Definition: simple_graph_alg.h:788
ogdf::isParallelFreeUndirected
bool isParallelFreeUndirected(const Graph &G)
Returns true iff G contains no undirected parallel edges.
ogdf::isBipartite
bool isBipartite(const Graph &G)
Checks whether a graph is bipartite.
Definition: simple_graph_alg.h:1020
ogdf::isParallelFree
bool isParallelFree(const Graph &G)
Returns true iff G contains no parallel edges.
ogdf::isBiconnected
bool isBiconnected(const Graph &G)
Returns true iff G is biconnected.
Definition: simple_graph_alg.h:547
OGDF_EXPORT
#define OGDF_EXPORT
Specifies that a function or class is exported by the OGDF DLL.
Definition: config.h:101
ogdf::hasSingleSink
bool hasSingleSink(const Graph &G)
Returns true iff the digraph G contains exactly one sink node (or is empty).
Definition: simple_graph_alg.h:810
ogdf::EdgeElement
Class for the representation of edges.
Definition: Graph_d.h:356
ogdf::topologicalNumbering
void topologicalNumbering(const Graph &G, NodeArray< int > &num)
Computes a topological numbering of an acyclic digraph G.
ogdf::isArborescence
bool isArborescence(const Graph &G)
Returns true iff G represents an arborescence.
Definition: simple_graph_alg.h:974
ogdf::makeParallelFreeUndirected
void makeParallelFreeUndirected(Graph &G, EDGELIST &parallelEdges, EdgeArray< int > &cardPositive, EdgeArray< int > &cardNegative)
Definition: simple_graph_alg.h:378
ogdf::NodeElement
Class for the representation of nodes.
Definition: Graph_d.h:233
tuples.h
Declaration and implementation of class Tuple2, Tuple3 and Tuple4.
ogdf::isSimple
bool isSimple(const Graph &G)
Returns true iff G contains neither self-loops nor parallel edges.
Definition: simple_graph_alg.h:394
ogdf::makeConnected
void makeConnected(Graph &G)
makes G connected by adding a minimum number of edges.
Definition: simple_graph_alg.h:459
ogdf::isTree
bool isTree(const Graph &G)
Returns true iff G is a tree, i.e. contains no undirected cycle and is connected.
Definition: simple_graph_alg.h:914
ogdf::internal::EdgeArrayBase2
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition: Graph_d.h:709
ogdf::nodeDistribution
void nodeDistribution(const Graph &G, Array< int > &degdist, std::function< int(node)> func)
Fills dist with the distribution given by a function func in graph G.