# HG changeset patch
# User Minh Van Nguyen
# Date 1275581569 25200
# Node ID 3166b00d898d9ff25ec2e3ba9062fa1d933ad4c6
# Parent 34e31a8c91d4dba1960a1012750538d18479a904
#8922: reviewer patch: induced subgraph search; unit tests for cdef functions and methods
diff git a/sage/graphs/base/c_graph.pxd b/sage/graphs/base/c_graph.pxd
 a/sage/graphs/base/c_graph.pxd
+++ b/sage/graphs/base/c_graph.pxd
@@ 28,6 +28,7 @@
cpdef add_arc(self, int u, int v)
cpdef bint has_arc(self, int u, int v)
cpdef del_all_arcs(self, int u, int v)
+ cdef int *adjacency_sequence(self, int n, int *vertices, int v)
cpdef list all_arcs(self, int u, int v)
cpdef list in_neighbors(self, int v)
cpdef list out_neighbors(self, int u)
diff git a/sage/graphs/base/c_graph.pyx b/sage/graphs/base/c_graph.pyx
 a/sage/graphs/base/c_graph.pyx
+++ b/sage/graphs/base/c_graph.pyx
@@ 840,6 +840,38 @@
"""
raise NotImplementedError()
+ cdef int *adjacency_sequence(self, int n, int *vertices, int v):
+ r"""
+ Returns the adjacency sequence corresponding to a list of vertices
+ and a vertex. See the function ``_test_adjacency_sequence()`` of
+ ``dense_graph.pyx`` and ``sparse_graph.pyx`` for unit tests.
+
+ INPUT:
+
+  ``n``  nonnegative integer; the maximum index in ``vertices`` up
+ to which we want to consider. If ``n = 0``, we only want to know if
+ ``v`` is adjacent to ``vertices[0]``. If ``n = 1``, we want to know
+ if ``v`` is adjacent to both ``vertices[0]`` and ``vertices[1]``.
+ Let ``k`` be the length of ``vertices``. If ``0 <= n < k``, then we
+ want to know if ``v`` is adjacent to each of
+ ``vertices[0], vertices[1], ..., vertices[n]``. Where ``n = k  1``,
+ then we consider all elements in the list ``vertices``.
+
+  ``vertices``  list of vertices.
+
+  ``v``  a vertex.
+
+ OUTPUT:
+
+ Returns a list of ``n`` integers, whose ith element
+ is set to 1 iff ``v`` is adjacent to ``vertices[i]``.
+ """
+ cdef int i = 0
+ cdef int *seq = sage_malloc(n * sizeof(int))
+ for 0 <= i < n:
+ seq[i] = 1 if self.has_arc_unsafe(v, vertices[i]) else 0
+ return seq
+
cpdef list all_arcs(self, int u, int v):
"""
Return the labels of all arcs from ``u`` to ``v``.
diff git a/sage/graphs/base/dense_graph.pxd b/sage/graphs/base/dense_graph.pxd
 a/sage/graphs/base/dense_graph.pxd
+++ b/sage/graphs/base/dense_graph.pxd
@@ 21,7 +21,6 @@
cdef int radix_mod_mask
cdef int num_longs
cdef unsigned long *edges
 cdef int * adjacency_sequence(self, int n, int * vertices, int vertex)
# Method declarations inherited from CGraph:
# cdef int add_arc_unsafe(self, int, int)
diff git a/sage/graphs/base/dense_graph.pyx b/sage/graphs/base/dense_graph.pyx
 a/sage/graphs/base/dense_graph.pyx
+++ b/sage/graphs/base/dense_graph.pyx
@@ 456,31 +456,6 @@
self.check_vertex(u)
self.check_vertex(v)
self.del_arc_unsafe(u,v)

 cdef int * adjacency_sequence(self, int n, int * vertices, int vertex):
 r"""
 Returns the adjacency sequence corresponding to a list of
 vertices, and a vertex

 INPUT:

  ``n``  number of elements in ``vertices``
  ``vertices``  list of vertices
  ``vertex``

 OUTPUT:

 Returns a list of ``n`` integers, whose i th element
 is set to 1 iif vertex is adjacent to vertices[i]
 """

 cdef int i = 0
 cdef int * line
 line = sage_malloc(n*sizeof(int))
 for 0 <= i < n:
 line[i] = 1 if self.has_arc_unsafe(vertices[i],vertex) else 0

 return line
###################################
# Neighbor functions
@@ 609,6 +584,9 @@
sage_free(neighbors)
return output
+##############################
+# Further tests. Unit tests for methods, functions, classes defined with cdef.
+##############################
def random_stress():
"""
@@ 647,6 +625,50 @@
if Gnew.in_degrees[i] != Gold.in_degree(i):
raise RuntimeError( "NO" )
+def _test_adjacency_sequence():
+ """
+ Randomly test the method ``DenseGraph.adjacency_sequence()``. No output
+ indicates that no errors were found.
+
+ TESTS::
+
+ sage: from sage.graphs.base.dense_graph import _test_adjacency_sequence
+ sage: _test_adjacency_sequence() # long time
+ """
+ from sage.graphs.digraph import DiGraph
+ from sage.graphs.graph_generators import GraphGenerators
+ from sage.misc.prandom import randint, random
+ low = 0
+ high = 500
+ randg = DiGraph(GraphGenerators().RandomGNP(randint(low, high), random()))
+ n = randg.order()
+ cdef DenseGraph g = DenseGraph(n,
+ verts=randg.vertices(),
+ arcs=randg.edges(labels=False))
+ assert g._num_verts() == randg.order(), (
+ "Graph order mismatch: %s vs. %s" % (g._num_verts(), randg.order()))
+ assert g._num_arcs() == randg.size(), (
+ "Graph size mismatch: %s vs. %s" % (g._num_arcs(), randg.size()))
+ M = randg.adjacency_matrix()
+ cdef int *V = sage_malloc(n * sizeof(int))
+ cdef int i = 0
+ for v in randg.vertex_iterator():
+ V[i] = v
+ i += 1
+ cdef int *seq
+ for 0 <= i < randint(50, 101):
+ u = randint(low, n  1)
+ seq = g.adjacency_sequence(n, V, u)
+ A = [seq[k] for k in range(n)]
+ try:
+ assert A == list(M[u])
+ except AssertionError:
+ sage_free(V)
+ sage_free(seq)
+ raise AssertionError("Graph adjacency mismatch")
+ sage_free(seq)
+ sage_free(V)
+
###########################################
# Dense Graph Backend
###########################################
@@ 1043,5 +1065,3 @@
NotImplementedError: Dense graphs do not support edge labels.
"""
raise NotImplementedError("Dense graphs do not support edge labels.")


diff git a/sage/graphs/base/sparse_graph.pyx b/sage/graphs/base/sparse_graph.pyx
 a/sage/graphs/base/sparse_graph.pyx
+++ b/sage/graphs/base/sparse_graph.pyx
@@ 1189,6 +1189,10 @@
raise RuntimeError("Label (%d) must be a nonnegative integer."%l)
return self.has_arc_label_unsafe(u,v,l) == 1
+##############################
+# Further tests. Unit tests for methods, functions, classes defined with cdef.
+##############################
+
def random_stress():
"""
Randomly search for mistakes in the code.
@@ 1302,6 +1306,52 @@
if Gnew.has_arc_unsafe(i,j) != Gold.has_edge(i,j):
raise RuntimeError( "NO" )
+def _test_adjacency_sequence():
+ """
+ Randomly test the method ``SparseGraph.adjacency_sequence()``. No output
+ indicates that no errors were found.
+
+ TESTS::
+
+ sage: from sage.graphs.base.sparse_graph import _test_adjacency_sequence
+ sage: _test_adjacency_sequence() # long time
+ """
+ from sage.graphs.digraph import DiGraph
+ from sage.graphs.graph_generators import GraphGenerators
+ from sage.misc.prandom import randint, random
+ low = 0
+ high = 1000
+ randg = DiGraph(GraphGenerators().RandomGNP(randint(low, high), random()))
+ n = randg.order()
+ # set all labels to 0
+ E = [(u, v, 0) for u, v in randg.edges(labels=False)]
+ cdef SparseGraph g = SparseGraph(n,
+ verts=randg.vertices(),
+ arcs=E)
+ assert g._num_verts() == randg.order(), (
+ "Graph order mismatch: %s vs. %s" % (g._num_verts(), randg.order()))
+ assert g._num_arcs() == randg.size(), (
+ "Graph size mismatch: %s vs. %s" % (g._num_arcs(), randg.size()))
+ M = randg.adjacency_matrix()
+ cdef int *V = sage_malloc(n * sizeof(int))
+ cdef int i = 0
+ for v in randg.vertex_iterator():
+ V[i] = v
+ i += 1
+ cdef int *seq
+ for 0 <= i < randint(50, 101):
+ u = randint(low, n  1)
+ seq = g.adjacency_sequence(n, V, u)
+ A = [seq[k] for k in range(n)]
+ try:
+ assert A == list(M[u])
+ except AssertionError:
+ sage_free(V)
+ sage_free(seq)
+ raise AssertionError("Graph adjacency mismatch")
+ sage_free(seq)
+ sage_free(V)
+
###########################################
# Sparse Graph Backend
###########################################
@@ 1798,5 +1848,3 @@
self._cg.del_arc_label(v_int, u_int, ll_int)
self._cg.add_arc_label(u_int, v_int, l_int)
self._cg.add_arc_label(v_int, u_int, l_int)


diff git a/sage/graphs/generic_graph.py b/sage/graphs/generic_graph.py
 a/sage/graphs/generic_graph.py
+++ b/sage/graphs/generic_graph.py
@@ 6912,108 +6912,88 @@
if not inplace:
return G
 def induced_subgraph_search(self, G):
 r"""
 Returns an induced copy of `G` in self.

 INPUT:

  ``G``  the graph whose copy we are looking for in ``self``
+ def subgraph_search(self, G, induced=False):
+ r"""
+ Returns a copy of ``G`` in ``self``.
+
+ INPUT:
+
+  ``G``  the graph whose copy we are looking for in ``self``.
+
+  ``induced``  boolean (default: ``False``). Whether or not to
+ search for an induced copy of ``G`` in ``self``.
+
+ OUTPUT:
+
+  If ``induced=False``, return a copy of ``G`` in this graph.
+ Otherwise, return an induced copy of ``G`` in ``self``. If ``G``
+ is the empty graph, return the empty graph since it is a subgraph
+ of every graph. Now suppose ``G`` is not the empty graph. If there
+ is no copy (induced or otherwise) of ``G`` in ``self``, we return
+ ``None``.
ALGORITHM:
 Bruteforce

 EXAMPLES:

 A Petersen Graph contains a `P_5` ::

+ Bruteforce search.
+
+ EXAMPLES:
+
+ The Petersen graph contains the path graph `P_5`::
+
sage: g = graphs.PetersenGraph()
 sage: h1 = g.induced_subgraph_search(graphs.PathGraph(5))
 sage: h1
+ sage: h1 = g.subgraph_search(graphs.PathGraph(5)); h1
Subgraph of (Petersen graph): Graph on 5 vertices
sage: h1.vertices()
+ [0, 1, 2, 3, 4]
+ sage: I1 = g.subgraph_search(graphs.PathGraph(5), induced=True); I1
+ Subgraph of (Petersen graph): Graph on 5 vertices
+ sage: I1.vertices()
[0, 1, 2, 3, 8]

 It also contains a Claw (`K_{1,3}`)::

 sage: h2 = g.induced_subgraph_search(graphs.ClawGraph())
 sage: h2
+
+ It also contains the claw `K_{1,3}`::
+
+ sage: h2 = g.subgraph_search(graphs.ClawGraph()); h2
Subgraph of (Petersen graph): Graph on 4 vertices
sage: h2.vertices()
[0, 1, 4, 5]

 Of course both copies are isomorphic to the graphs we were looking
 for ::

 sage: h1.is_isomorphic(graphs.PathGraph(5))
+ sage: I2 = g.subgraph_search(graphs.ClawGraph(), induced=True); I2
+ Subgraph of (Petersen graph): Graph on 4 vertices
+ sage: I2.vertices()
+ [0, 1, 4, 5]
+
+ Of course the induced copies are isomorphic to the graphs we were
+ looking for::
+
+ sage: I1.is_isomorphic(graphs.PathGraph(5))
True
 sage: h2.is_isomorphic(graphs.ClawGraph())
+ sage: I2.is_isomorphic(graphs.ClawGraph())
True
 However, as it contains no induced subgraphs isomorphic to
 `P_6`, an exception is raised in this case ::

 sage: g.induced_subgraph_search(graphs.PathGraph(6))
 Traceback (most recent call last):
 ...
 ValueError: No induced copy of the graph exists
+ However, the Petersen graph does not contain a subgraph isomorphic to
+ `K_3`::
+
+ sage: g.subgraph_search(graphs.CompleteGraph(3)) is None
+ True
+
+ Nor does it contain a nonempty induced subgraph isomorphic to `P_6`::
+
+ sage: g.subgraph_search(graphs.PathGraph(6), induced=True) is None
+ True
+
+ The empty graph is a subgraph of every graph::
+
+ sage: g.subgraph_search(graphs.EmptyGraph())
+ Graph on 0 vertices
+ sage: g.subgraph_search(graphs.EmptyGraph(), induced=True)
+ Graph on 0 vertices
"""
from sage.graphs.generic_graph_pyx import subgraph_search

 H = subgraph_search(self, G, induced = True)

+ from sage.graphs.graph_generators import GraphGenerators
+ if G.order() == 0:
+ return GraphGenerators().EmptyGraph()
+ H = subgraph_search(self, G, induced=induced)
if H == []:
 raise ValueError('No induced copy of the graph exists')

 return self.subgraph(H)

 def subgraph_search(self, G):
 r"""
 Returns an of `G` included in self.

 INPUT:

  ``G``  the graph whose copy we are looking for in ``self``

 ALGORITHM:

 Bruteforce

 EXAMPLES:

 A Petersen Graph contains a `P_5` ::

 sage: g = graphs.PetersenGraph()
 sage: h1 = g.subgraph_search(graphs.PathGraph(5))
 sage: h1
 Subgraph of (Petersen graph): Graph on 5 vertices
 sage: h1.vertices()
 [0, 1, 2, 3, 4]

 It also contains a Claw (`K_{1,3}`)::

 sage: h2 = g.subgraph_search(graphs.ClawGraph())
 sage: h2
 Subgraph of (Petersen graph): Graph on 4 vertices
 sage: h2.vertices()
 [0, 1, 4, 5]

 However, as it contains no subgraph isomorphic to
 `K_3`, an exception is raised in this case ::

 sage: g.subgraph_search(graphs.CompleteGraph(3))
 Traceback (most recent call last):
 ...
 ValueError: No copy of the graph exists
 """
 from sage.graphs.generic_graph_pyx import subgraph_search

 H = subgraph_search(self, G)

 if H == []:
 raise ValueError('No copy of the graph exists')

+ return None
return self.subgraph(H)
def random_subgraph(self, p, inplace=False):
diff git a/sage/graphs/generic_graph_pyx.pxd b/sage/graphs/generic_graph_pyx.pxd
 a/sage/graphs/generic_graph_pyx.pxd
+++ b/sage/graphs/generic_graph_pyx.pxd
@@ 6,4 +6,5 @@
cdef class GenericGraph_pyx(SageObject):
pass

+cdef inline bint vectors_equal(int n, int *a, int *b)
+cdef inline bint vectors_inferior(int n, int *a, int *b)
diff git a/sage/graphs/generic_graph_pyx.pyx b/sage/graphs/generic_graph_pyx.pyx
 a/sage/graphs/generic_graph_pyx.pyx
+++ b/sage/graphs/generic_graph_pyx.pyx
@@ 19,8 +19,11 @@
include '../ext/cdefs.pxi'
include '../ext/stdsage.pxi'
+# import from Python standard library
+from random import random
+
+# import from thirdparty library
from sage.graphs.base.dense_graph cimport DenseGraph
from random import random
cdef extern from *:
double sqrt(double)
@@ 455,195 +458,174 @@
# Exhaustive search in graphs
cpdef subgraph_search(G,H, induced = False):
+cpdef list subgraph_search(G, H, bint induced=False):
r"""
 Returns a set of vertices in G representing a copy of H
+ Returns a set of vertices in ``G`` representing a copy of ``H``.
ALGORITHM:
This algorithm is a bruteforce search.
 Let `V(H)=\{h_1,\dots,h_k\}`. It first tries
+ Let `V(H) = \{h_1,\dots,h_k\}`. It first tries
to find in `G` a possible representant of `h_1`, then a
representant of `h_2` compatible with `h_1`, then
a representant of `h_3` compatible with the first
 two, etc ...
+ two, etc.
 This way, most of the times we need to test far less than
 `\binom k!{V(G)}{k}` subsets, and hope this bruteforce
+ This way, most of the time we need to test far less than
+ `k! \binom{V(G)}{k}` subsets, and hope this bruteforce
technique can sometimes be useful.
INPUT:
  ``G``, ``H``  graphs
+  ``G``, ``H``  two graphs such that ``H`` is a subgraph of ``G``.
  ``induced`` (boolean)  whether to require that the subgraph
 is an induced subgraph
+  ``induced``  boolean (default: ``False``); whether to require that
+ the subgraph is an induced subgraph.
OUTPUT:
 A list of vertices inducing a copy of ``H``. If none is found,
+ A list of vertices inducing a copy of ``H`` in ``G``. If none is found,
an empty list is returned.
 EXAMPLE:
+ EXAMPLES:
 A Petersen Graph contains an induced `P_5` ::
+ A Petersen graph contains an induced path graph `P_5`::
sage: from sage.graphs.generic_graph_pyx import subgraph_search
sage: g = graphs.PetersenGraph()
 sage: subgraph_search(g, graphs.PathGraph(5), induced = True)
+ sage: subgraph_search(g, graphs.PathGraph(5), induced=True)
[0, 1, 2, 3, 8]
 It also contains a Claw (`K_{1,3}`)::
+ It also contains a the claw `K_{1,3}`::
sage: subgraph_search(g, graphs.ClawGraph())
[0, 1, 4, 5]
 Though it contains no induced `P_6` ::
+ Though it contains no induced `P_6`::
 sage: subgraph_search(g, graphs.PathGraph(6), induced = True)
+ sage: subgraph_search(g, graphs.PathGraph(6), induced=True)
+ []
+
+ TESTS:
+
+ Let `G` and `H` be graphs having orders `m` and `n`, respectively. If
+ `m < n`, then there are no copies of `H` in `G`::
+
+ sage: from sage.graphs.generic_graph_pyx import subgraph_search
+ sage: m = randint(100, 200)
+ sage: n = randint(m + 1, 300)
+ sage: G = graphs.RandomGNP(m, random())
+ sage: H = graphs.RandomGNP(n, random())
+ sage: G.order() < H.order()
+ True
+ sage: subgraph_search(G, H)
[]
"""

+ # TODO: This is a bruteforce search and can be very inefficient. Write
+ # a more efficient subgraph search implementation.
cdef int ng = G.order()
cdef int nh = H.order()
+ if ng < nh:
+ return []
cdef int i, j, k
 cdef int * tmp_array

 cdef (int) (*is_admissible) (int, int *, int *)

+ cdef int *tmp_array
+ cdef (bint) (*is_admissible) (int, int *, int *)
if induced:
is_admissible = vectors_equal
else:
is_admissible = vectors_inferior

 # Static copies of the two graphs for
 # more efficient operations

+ # static copies of the two graphs for more efficient operations
cdef DenseGraph g = DenseGraph(ng)
cdef DenseGraph h = DenseGraph(nh)

 # Copying the matrices

+ # copying the adjacency relations in both G and H
i = 0
 for l in G.adjacency_matrix():
+ for row in G.adjacency_matrix():
j = 0
 for k in l:
+ for k in row:
if k:
 g.add_arc(i,j)
 j=j+1
 i=i+1

+ g.add_arc(i, j)
+ j += 1
+ i += 1
i = 0
 for l in H.adjacency_matrix():
+ for row in H.adjacency_matrix():
j = 0
 for k in l:
+ for k in row:
if k:
 h.add_arc(i,j)
 j=j+1
 i=i+1

 # A vertex is said to be busy if it is already part
 # of the partial copy of H in G
 cdef int * busy
 busy = sage_malloc(ng*sizeof(int))
 memset(busy, 0, ng*sizeof(int))

 # 0 is the first vertex we use, so it is at first
 # busy
+ h.add_arc(i, j)
+ j += 1
+ i += 1
+ # A vertex is said to be busy if it is already part of the partial copy
+ # of H in G.
+ cdef int *busy = sage_malloc(ng * sizeof(int))
+ memset(busy, 0, ng * sizeof(int))
+ # 0 is the first vertex we use, so it is at first busy
busy[0] = 1

 # stack
+ # stack  list of the vertices which are part of the partial copy of H
+ # in G.
#
 # List of the vertices which are part of the
 # partial copy of H in G
 #
 # stack[i] is the integer corresponding
 # to the vertex of G representing
 # the i th vertex of H
+ # stack[i]  the integer corresponding to the vertex of G representing
+ # the ith vertex of H.
#
# stack[i] = 1 means that i is not represented
 # ... yet !

 cdef int * stack
 stack = sage_malloc(nh*sizeof(int))
+ # ... yet!
+ cdef int *stack = sage_malloc(nh * sizeof(int))
stack[0] = 0
stack[1] = 1

 # number of representants we have already
 # found

 # set to 1 as vertex 0 is already part of the partial
 # copy of H ...
+ # Number of representants we have already found. Set to 1 as vertex 0
+ # is already part of the partial copy of H in G.
cdef int active = 1

 # vertices is equal to range(nh), as an int * variable
 cdef int * vertices
 vertices = sage_malloc(nh*sizeof(int))
 for 0<= i < nh:
+ # vertices is equal to range(nh), as an int *variable
+ cdef int *vertices = sage_malloc(nh * sizeof(int))
+ for 0 <= i < nh:
vertices[i] = i

# line_h[i] represents the adjacency sequence of vertex i
 # in h relatively to vertices 0...i1
 cdef int ** line_h
 line_h = sage_malloc(nh * sizeof(int *))
 for 0<= i < nh:
 line_h[i] = h.adjacency_sequence( i, vertices, i)

+ # in h relative to vertices 0, 1, ..., i1
+ cdef int **line_h = sage_malloc(nh * sizeof(int *))
+ for 0 <= i < nh:
+ line_h[i] = h.adjacency_sequence(i, vertices, i)
# the sequence of vertices to be returned
 value = []
+ cdef list value = []
_sig_on
# as long as there is a nonvoid partial copy of H in G
while active:

 # If we are here and found nothing yet
 # we try the next possible vertex
 # as a representant of the active th
 # vertex of H
 i = stack[active] +1

 # Looking for a vertex which is not busy
 # and compatible with the partial copy we have of H
+ # If we are here and found nothing yet, we try the next possible
+ # vertex as a representant of the active ith vertex of H.
+ i = stack[active] + 1
+ # Looking for a vertex that is not busy and compatible with the
+ # partial copy we have of H.
while i < ng:
 if busy[i] == 1:
 i = i + 1
+ if busy[i]:
+ i += 1
else:
 tmp_array = g.adjacency_sequence(active, stack, i)

+ tmp_array = g.adjacency_sequence(active, stack, i)
if is_admissible(active, tmp_array, line_h[active]):
 free(tmp_array)
+ sage_free(tmp_array)
break
else:
 free(tmp_array)
 i = i + 1

 # if we found none, it means that we can not extend the current copy of H
 # so we update the status of stack[active]
 # and prepare to change the previous vertex
+ sage_free(tmp_array)
+ i += 1
+ # If we found none, it means that we cannot extend the current copy
+ # of H so we update the status of stack[active] and prepare to change
+ # the previous vertex.
if i >= ng:
if stack[active] != 1:
busy[stack[active]] = 0
stack[active] = 1
 active=active1


 # If we have found a good representant of H's i^{th} vertex in G
+ active = 1
+ # If we have found a good representant of H's ith vertex in G
else:

if stack[active] != 1:
 busy[stack[active]]=0
+ busy[stack[active]] = 0
stack[active] = i
 busy[stack[active]]=1

 active = active + 1

 # We have found our copy !!!
+ busy[stack[active]] = 1
+ active += 1
+ # We have found our copy!!!
if active == nh:
g_vertices = G.vertices()
value = [g_vertices[stack[i]] for i in xrange(nh)]
break

else:
# we begin the search of the next vertex at 0
stack[active] = 1
@@ 654,42 +636,187 @@
sage_free(busy)
sage_free(stack)
sage_free(vertices)
 for 0<= i < nh:
+ for 0 <= i < nh:
sage_free(line_h[i])
sage_free(line_h)
return value

cdef int vectors_equal(int n, int * a, int * b):
+cdef inline bint vectors_equal(int n, int *a, int *b):
r"""
 Tests whether two vectors given in argument are equal
+ Tests whether the two given vectors are equal. Two integer vectors
+ `a = (a_1, a_2, \dots, a_n)` and `b = (b_1, b_2, \dots, b_n)` are equal
+ iff `a_i = b_i` for all `i = 1, 2, \dots, n`. See the function
+ ``_test_vectors_equal_inferior()`` for unit tests.
INPUT:
  ``n``  length of the vectors
  ``a``,``b``  the two vectors
+  ``n``  positive integer; length of the vectors.
+
+  ``a``, ``b``  two vectors of integers.
+
+ OUTPUT:
+
+  ``True`` if ``a`` and ``b`` are the same vector; ``False`` otherwise.
"""

 cdef int i =0
 for 0<= i < n:
+ cdef int i = 0
+ for 0 <= i < n:
if a[i] != b[i]:
return False
return True
cdef int vectors_inferior(int n, int * a, int * b):
+cdef inline bint vectors_inferior(int n, int *a, int *b):
r"""
 Tests whether the second vector of integer is larger than the first
+ Tests whether the second vector of integers is inferior to the first. Let
+ `u = (u_1, u_2, \dots, u_k)` and `v = (v_1, v_2, \dots, v_k)` be two
+ integer vectors of equal length. Then `u` is said to be less than
+ (or inferior to) `v` if `u_i \leq v_i` for all `i = 1, 2, \dots, k`. See
+ the function ``_test_vectors_equal_inferior()`` for unit tests. Given two
+ equal integer vectors `u` and `v`, `u` is inferior to `v` and vice versa.
+ We could also define two vectors `a` and `b` to be equal if `a` is
+ inferior to `b` and `b` is inferior to `a`.
INPUT:
  ``n``  length of the vectors
  ``a``,``b``  the two vectors
+  ``n``  positive integer; length of the vectors.
+
+  ``a``, ``b``  two vectors of integers.
+
+ OUTPUT:
+
+  ``True`` if ``b`` is inferior to (or less than) ``a``; ``False``
+ otherwise.
"""

 cdef int i =0
 for 0<= i < n:
+ cdef int i = 0
+ for 0 <= i < n:
if a[i] < b[i]:
return False
return True
+##############################
+# Further tests. Unit tests for methods, functions, classes defined with cdef.
+##############################
+
+def _test_vectors_equal_inferior():
+ """
+ Unit testing the function ``vectors_equal()``. No output means that no
+ errors were found in the random tests.
+
+ TESTS::
+
+ sage: from sage.graphs.generic_graph_pyx import _test_vectors_equal_inferior
+ sage: _test_vectors_equal_inferior()
+ """
+ from sage.misc.prandom import randint
+ n = randint(500, 10**3)
+ cdef int *u = sage_malloc(n * sizeof(int))
+ cdef int *v = sage_malloc(n * sizeof(int))
+ cdef int i
+ # equal vectors: u = v
+ for 0 <= i < n:
+ u[i] = randint(10**6, 10**6)
+ v[i] = u[i]
+ try:
+ assert vectors_equal(n, u, v)
+ assert vectors_equal(n, v, u)
+ # Since u and v are equal vectors, then u is inferior to v and v is
+ # inferior to u. One could also define u and v as being equal if
+ # u is inferior to v and vice versa.
+ assert vectors_inferior(n, u, v)
+ assert vectors_inferior(n, v, u)
+ except AssertionError:
+ sage_free(u)
+ sage_free(v)
+ raise AssertionError("Vectors u and v should be equal.")
+ # Different vectors: u != v because we have u_j > v_j for some j. Thus,
+ # u_i = v_i for 0 <= i < j and u_j > v_j. For j < k < n  2, we could have:
+ # (1) u_k = v_k,
+ # (2) u_k < v_k, or
+ # (3) u_k > v_k.
+ # And finally, u_{n1} < v_{n1}.
+ cdef int j = randint(1, n//2)
+ cdef int k
+ for 0 <= i < j:
+ u[i] = randint(10**6, 10**6)
+ v[i] = u[i]
+ u[j] = randint(10**6, 10**6)
+ v[j] = u[j]  randint(1, 10**6)
+ for j < k < n:
+ u[k] = randint(10**6, 10**6)
+ v[k] = randint(10**6, 10**6)
+ u[n  1] = v[n  1]  randint(1, 10**6)
+ try:
+ assert not vectors_equal(n, u, v)
+ assert not vectors_equal(n, v, u)
+ # u is not inferior to v because at least u_j > v_j
+ assert u[j] > v[j]
+ assert not vectors_inferior(n, v, u)
+ # v is not inferior to u because at least v_{n1} > u_{n1}
+ assert v[n  1] > u[n  1]
+ assert not vectors_inferior(n, u, v)
+ except AssertionError:
+ sage_free(u)
+ sage_free(v)
+ raise AssertionError("".join([
+ "Vectors u and v should not be equal. ",
+ "u should not be inferior to v, and vice versa."]))
+ # Different vectors: u != v because we have u_j < v_j for some j. Thus,
+ # u_i = v_i for 0 <= i < j and u_j < v_j. For j < k < n  2, we could have:
+ # (1) u_k = v_k,
+ # (2) u_k < v_k, or
+ # (3) u_k > v_k.
+ # And finally, u_{n1} > v_{n1}.
+ j = randint(1, n//2)
+ for 0 <= i < j:
+ u[i] = randint(10**6, 10**6)
+ v[i] = u[i]
+ u[j] = randint(10**6, 10**6)
+ v[j] = u[j] + randint(1, 10**6)
+ for j < k < n:
+ u[k] = randint(10**6, 10**6)
+ v[k] = randint(10**6, 10**6)
+ u[n  1] = v[n  1] + randint(1, 10**6)
+ try:
+ assert not vectors_equal(n, u, v)
+ assert not vectors_equal(n, v, u)
+ # u is not inferior to v because at least u_{n1} > v_{n1}
+ assert u[n  1] > v[n  1]
+ assert not vectors_inferior(n, v, u)
+ # v is not inferior to u because at least u_j < v_j
+ assert u[j] < v[j]
+ assert not vectors_inferior(n, u, v)
+ except AssertionError:
+ sage_free(u)
+ sage_free(v)
+ raise AssertionError("".join([
+ "Vectors u and v should not be equal. ",
+ "u should not be inferior to v, and vice versa."]))
+ # different vectors u != v
+ # What's the probability of two random vectors being equal?
+ for 0 <= i < n:
+ u[i] = randint(10**6, 10**6)
+ v[i] = randint(10**6, 10**6)
+ try:
+ assert not vectors_equal(n, u, v)
+ assert not vectors_equal(n, v, u)
+ except AssertionError:
+ sage_free(u)
+ sage_free(v)
+ raise AssertionError("Vectors u and v should not be equal.")
+ # u is inferior to v, but v is not inferior to u
+ for 0 <= i < n:
+ v[i] = randint(10**6, 10**6)
+ u[i] = randint(10**6, 10**6)
+ while u[i] > v[i]:
+ u[i] = randint(10**6, 10**6)
+ try:
+ assert not vectors_equal(n, u, v)
+ assert not vectors_equal(n, v, u)
+ assert vectors_inferior(n, v, u)
+ assert not vectors_inferior(n, u, v)
+ except AssertionError:
+ raise AssertionError(
+ "u should be inferior to v, but v is not inferior to u.")
+ finally:
+ sage_free(u)
+ sage_free(v)