1 /*
2  * graphtheory.cc
3  *
4  * (c) 2018 Luka Marohnić
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "giacPCH.h"
21 #include "giac.h"
22 #include "graphe.h"
23 #include "graphtheory.h"
24 
25 using namespace std;
26 
27 #ifndef NO_NAMESPACE_GIAC
28 namespace giac {
29 #endif // ndef NO_NAMESPACE_GIAC
30 
31 /* error messages */
32 static const char *gt_error_messages[] = {
33     "Unknown error",                                                    //  0
34     "Argument is not a graph",                                          //  1
35     "Weighted graph is required",                                       //  2
36     "Unweighted graph is required",                                     //  3
37     "Directed graph is required",                                       //  4
38     "Undirected graph is required",                                     //  5
39     "does not specify an edge",                                         //  6
40     "Mixing edges and arcs not allowed",                                //  7
41     "Weight/adjacency matrix must be symmetric for undirected graphs",  //  8
42     "Failed to read graph from file",                                   //  9
43     "Edge not found",                                                   // 10
44     "Vertex not found",                                                 // 11
45     "Graph is not a tree",                                              // 12
46     "Exactly one root node must be specified per connected component",  // 13
47     "Invalid root node specification",                                  // 14
48     "Graph is not planar",                                              // 15
49     "Connected graph required",                                         // 16
50     "Invalid drawing method specification",                             // 17
51     "does not specify a cycle in the given graph",                      // 18
52     "No cycle found",                                                   // 19
53     "Graph name is not recognized",                                     // 20
54     "Argument is not a subgraph",                                       // 21
55     "Graph is null",                                                    // 22
56     "Expected \"tag\"=value pair",                                      // 23
57     "Argument is not a valid graphic sequence",                         // 24
58     "Graph is not acyclic",                                             // 25
59     "Biconnected graph required",                                       // 26
60     "Graph is not bipartite",                                           // 27
61     "Wrong number of arguments",                                        // 28
62     "Positive integer required",                                        // 29
63     "Invalid vertex specification",                                     // 30
64 };
65 
generr(const char * msg)66 gen generr(const char* msg) {
67     string m(msg);
68     m.append("\n");
69     return gensizeerr(m.c_str());
70 }
71 
generrtype(const char * msg)72 gen generrtype(const char* msg) {
73     string m(msg);
74     m.append("\n");
75     return gentypeerr(m.c_str());
76 }
77 
generrdim(const char * msg)78 gen generrdim(const char* msg) {
79     string m(msg);
80     m.append("\n");
81     return gendimerr(m.c_str());
82 }
83 
gt_err(int code)84 gen gt_err(int code) {
85     return generr((string(gt_error_messages[code])).c_str());
86 }
87 
gt_err(const gen & g,int code)88 gen gt_err(const gen &g,int code) {
89     stringstream ss;
90     ss << g << ": " << gt_error_messages[code];
91     return generr(ss.str().c_str());
92 }
93 
identifier_assign(const identificateur & var,const gen & value,GIAC_CONTEXT)94 void identifier_assign(const identificateur &var,const gen &value,GIAC_CONTEXT) {
95     _eval(symbolic(at_sto,makesequence(value,var)),contextptr);
96 }
97 
has_suffix(const string & str,const string & suffix)98 bool has_suffix(const string &str,const string &suffix)
99 {
100     return str.size()>=suffix.size() &&
101             str.compare(str.size()-suffix.size(),suffix.size(),suffix)==0;
102 }
103 
strip_string(const string & str)104 string strip_string(const string &str) {
105     string res(str);
106     int i=0;
107     for (;res[i]==' ';++i);
108     res=res.substr(i);
109 #if 1
110     i=int(res.size())-1;
111     for (;i>=0;--i){
112       if (res[i]!=' ')
113    break;
114     }
115     res=res.substr(0,i+1);
116 #else
117     while (res.back()==' ') {
118         res.pop_back();
119     }
120 #endif
121     return res;
122 }
123 
make_absolute_file_path(const string & relative_path)124 string make_absolute_file_path(const string &relative_path) {
125     if (relative_path[0]=='/')
126         return relative_path;
127 #ifdef HAVE_NO_CWD
128     string path="/"+relative_path;
129 #else
130     string path=string(getcwd(0,0))+"/"+relative_path;
131 #endif
132     vector<string> tokens;
133     size_t lastpos=0,pos;
134     while ((pos=path.find_first_of('/',lastpos))!=string::npos) {
135         tokens.push_back(path.substr(lastpos,pos-lastpos));
136         lastpos=pos+1;
137     }
138     tokens.push_back(path.substr(lastpos));
139     vector<string>::iterator it;
140     for (it=tokens.begin();it!=tokens.end();++it) {
141         *it=strip_string(*it);
142     }
143     int i;
144     while ((it=find(tokens.begin(),tokens.end(),string("..")))!=tokens.end()) {
145         if (it==tokens.begin())
146             return path;
147         i=it-tokens.begin()-1;
148         tokens.erase(it);
149         tokens.erase(tokens.begin()+i);
150     }
151     string res;
152     for (it=tokens.begin();it!=tokens.end();++it) {
153         res=res+*it;
154         if (it+1!=tokens.end())
155             res=res+"/";
156     }
157     return res;
158 }
159 
160 /* returns true iff g is a graph and writes the basic info to 'display_str' */
is_graphe(const gen & g,string & disp_out,GIAC_CONTEXT)161 bool is_graphe(const gen &g,string &disp_out,GIAC_CONTEXT) {
162     if (g.type!=_VECT || g.subtype!=_GRAPH__VECT)
163         return false;
164     graphe G(contextptr);
165     if (!G.read_gen(g))
166         return false;
167     int nv=G.node_count(),ne=G.edge_count();
168     stringstream ss;
169     ss << nv;
170     string nvert(ss.str());
171     ss.str("");
172     ss << ne;
173     bool isdir=G.is_directed();
174     string nedg(ss.str());
175     string dir_spec=isdir?"directed":"undirected";
176     string weight_spec=G.is_weighted()?"weighted":"unweighted";
177     nvert=nvert+(nv==1?" vertex":" vertices");
178     nedg=nedg+(isdir?(ne==1?" arc":" arcs"):(ne==1?" edge":" edges"));
179     disp_out.clear();
180     string name=G.name();
181     if (!name.empty())
182         disp_out=name+": ";
183     disp_out=disp_out+(isdir?"a ":"an ")+dir_spec+" "+weight_spec+" graph with "+nvert+" and "+nedg;
184     return true;
185 }
186 
187 /* evaluates the given command with the given inputs and returns the result */
gt_command(gen (* gtfunc)(const gen &,const context *),const char * args,GIAC_CONTEXT)188 gen gt_command(gen (*gtfunc)(const gen &,const context *),const char *args,GIAC_CONTEXT) {
189     return (*gtfunc)(graphe::str2gen(args),contextptr);
190 }
191 
vertices_from_integer_or_vecteur(const gen & g,graphe & G)192 bool vertices_from_integer_or_vecteur(const gen &g,graphe &G) {
193     vecteur V;
194     int n;
195     if (g.is_integer()) {
196         n=g.val;
197         if (n<1)
198             return false;
199         G.make_default_labels(V,n);
200     } else if (g.type==_VECT) {
201         V=*g._VECTptr;
202         if (V.empty())
203             return false;
204         n=V.size();
205     } else return false;
206     G.add_nodes(V);
207     return true;
208 }
209 
parse_trail(graphe & G,const gen & g)210 void parse_trail(graphe &G,const gen &g) {
211     assert(g.is_symb_of_sommet(at_trail));
212     vecteur &trail=*g._SYMBptr->feuille._VECTptr;
213     int n=trail.size();
214     for (int i=0;i<n-1;++i) {
215         G.add_edge(trail[i],trail[i+1]);
216     }
217 }
218 
parse_vertex_colors(graphe & G,const gen & g,const graphe::ivector & nodes=graphe::ivector (0))219 bool parse_vertex_colors(graphe &G,const gen &g,const graphe::ivector &nodes=graphe::ivector(0)) {
220     if (g.type==_VECT) {
221         assert(int(g._VECTptr->size()==nodes.empty()?G.node_count():int(nodes.size())));
222         int k=0;
223         for (const_iterateur it=g._VECTptr->begin();it!=g._VECTptr->end();++it) {
224             if (!it->is_integer())
225                 return false;
226             G.set_node_attribute(nodes.empty()?k:nodes[k],_GT_ATTRIB_COLOR,it->val);
227             ++k;
228         }
229     } else if (g.is_integer()) {
230         if (g.val<0)
231             return false;
232         if (nodes.empty()) {
233             for (int k=G.node_count();k-->0;)
234                 G.set_node_attribute(k,_GT_ATTRIB_COLOR,g.val);
235         } else {
236             for (graphe::ivector_iter it=nodes.begin();it!=nodes.end();++it) {
237                 G.set_node_attribute(*it,_GT_ATTRIB_COLOR,g.val);
238             }
239         }
240     } else return false;
241     return true;
242 }
243 
parse_vertex_coordinates(graphe & G,const vecteur & v,bool & size_error)244 bool parse_vertex_coordinates(graphe &G,const vecteur &v,bool &size_error) {
245     vecteur c;
246     int i=0,dim=-1;
247     for (const_iterateur it=v.begin();it!=v.end();++it) {
248         if (it->is_symb_of_sommet(at_point))
249             c=*it->_SYMBptr->feuille._VECTptr;
250         else if (it->type==_CPLX) {
251             c.resize(2);
252             c[0]=*it->_CPLXptr;
253             c[1]=*(it->_CPLXptr+1);
254         } else if (it->type==_VECT) {
255             c=*it->_VECTptr;
256         } else return false;
257         if (dim<0)
258             dim=c.size();
259         else if (dim!=int(c.size())) {
260             size_error=true;
261             return false;
262         }
263         G.set_node_attribute(i++,_GT_ATTRIB_POSITION,c);
264     }
265     return true;
266 }
267 
parse_matrix(graphe & G,const matrice & m,bool is_weight_matrix,int mode,bool & size_error)268 bool parse_matrix(graphe &G,const matrice &m,bool is_weight_matrix,int mode,bool &size_error) {
269     int n=m.size();
270     size_error=false;
271     if (int(m.front()._VECTptr->size())!=n || (mode>0 && G.node_count()!=n)) {
272         size_error=true;
273         return false;
274     }
275     if (!has_num_coeff(_evalf(m,context0)))
276         return false;
277     bool isdir=G.is_directed() || m!=mtran(m),isweighted=is_weight_matrix;
278     if (mode==0) {
279         vecteur V;
280         G.make_default_labels(V,n);
281         G.add_nodes(V);
282     }
283     if (mode<2) {
284         G.set_directed(isdir);
285         for (int i=0;i<n;++i) {
286             for (int j=isdir?0:i+1;j<n;++j) {
287                 if (i==j)
288                     continue;
289                 const gen &w=m[i][j];
290                 if (!is_zero(w)) {
291                     G.add_edge(i,j);
292                     if (!is_one(w))
293                         isweighted=true;
294                 }
295             }
296         }
297     }
298     if (isweighted)
299         G.make_weighted(m);
300     return true;
301 }
302 
parse_edge_with_weight(graphe & G,const vecteur & E)303 bool parse_edge_with_weight(graphe &G,const vecteur &E) {
304     if (E.size()!=2)
305         return false;
306     const vecteur &e=*E.front()._VECTptr;
307     const gen &w=E.back();
308     if (e.size()!=2)
309         return false;
310     if (!G.is_weighted())
311         G.set_weighted(true);
312     if (e.front()==e.back())
313         return false;
314     G.add_edge(e.front(),e.back(),w);
315     return true;
316 }
317 
parse_edges(graphe & G,const vecteur & E,bool is_set)318 bool parse_edges(graphe &G,const vecteur &E,bool is_set) {
319     if (is_set) {
320         for (const_iterateur it=E.begin();it!=E.end();++it) {
321             if (it->type!=_VECT || it->_VECTptr->size()!=2)
322                 return false;
323             if (it->_VECTptr->front().type!=_VECT) {
324                 if (it->_VECTptr->front()==it->_VECTptr->back())
325                     return false;
326                 G.add_edge(it->_VECTptr->front(),it->_VECTptr->back());
327             } else {
328                 if (!parse_edge_with_weight(G,*it->_VECTptr))
329                     return false;
330             }
331         }
332     } else {
333         int n=E.size();
334         if (n<2)
335             return false;
336         if (E.front().type==_VECT) {
337             if (!parse_edge_with_weight(G,E))
338                 return false;
339         } else {
340             for (int i=0;i<n-1;++i) {
341                 if (E[i]==E[i+1])
342                     return false;
343                 G.add_edge(E[i],E[i+1]);
344             }
345         }
346     }
347     return true;
348 }
349 
delete_edges(graphe & G,const vecteur & E)350 bool delete_edges(graphe &G,const vecteur &E) {
351     if (ckmatrix(E)) {
352         if (E.front()._VECTptr->size()!=2)
353             return false;
354         for (const_iterateur it=E.begin();it!=E.end();++it) {
355             int i=G.node_index(it->_VECTptr->front()),j=G.node_index(it->_VECTptr->back());
356             if (i>=0 && j>=0)
357                 G.remove_edge(i,j);
358         }
359     } else {
360         int n=E.size();
361         if (n<2)
362             return false;
363         for (int k=0;k<n-1;++k) {
364             int i=G.node_index(E[k]),j=G.node_index(E[k+1]);
365             G.remove_edge(i,j);
366         }
367     }
368     return true;
369 }
370 
flights(const gen & g,bool arrive,bool all,GIAC_CONTEXT)371 gen flights(const gen &g,bool arrive,bool all,GIAC_CONTEXT) {
372     if (!all && g._VECTptr->front().type!=_VECT)
373         return gentypeerr(contextptr);
374     graphe G(contextptr);
375     if (!G.read_gen(all?g:g._VECTptr->front()))
376         return gt_err(_GT_ERR_NOT_A_GRAPH);
377     if (!G.is_directed())
378         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
379     int i=0;
380     if (!all) {
381         i=G.node_index(g._VECTptr->at(1));
382         if (i==-1)
383             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
384     }
385     vecteur res;
386     graphe::ivector adj;
387     do {
388         G.adjacent_nodes(i,adj);
389         vecteur v;
390         for (graphe::ivector::const_iterator it=adj.begin();it!=adj.end();++it) {
391             if (G.has_edge(arrive?*it:i,arrive?i:*it))
392                 v.push_back(G.node_label(*it));
393         }
394         if (!all)
395             return _sort(v,contextptr);
396         res.push_back(_sort(v,contextptr));
397     } while (++i<G.node_count());
398     return change_subtype(res,_LIST__VECT);
399 }
400 
randomgraph(const vecteur & gv,bool directed,GIAC_CONTEXT)401 gen randomgraph(const vecteur &gv,bool directed,GIAC_CONTEXT) {
402     graphe G(contextptr);
403     vecteur V;
404     if (gv.front().type==_VECT)
405         V=*gv.front()._VECTptr;
406     else if (gv.front().is_integer())
407         G.make_default_labels(V,gv.front().val);
408     else
409         return gentypeerr(contextptr);
410     int n=V.size();
411     G.reserve_nodes(n);
412     G.add_nodes(V);
413     if (gv.size()==2) {
414         gen p=_evalf(gv.back(),contextptr);
415         if (p.type==_DOUBLE_) {
416             if (!is_strictly_positive(p,contextptr))
417                 return gentypeerr("Expected a positive constant");
418             G.set_directed(directed);
419             G.erdos_renyi(p.DOUBLE_val());
420         } else {
421             vecteur P;
422             if (directed)
423                 return generr("This method cannot generate digraphs");
424             if (gv.back().type==_VECT) {
425                 P=*gv.back()._VECTptr;
426             } else if (gv.back().is_symb_of_sommet(at_program) || gv.back().type==_FUNC) {
427                 vecteur L(n);
428                 for (int i=0;i<n;++i) L[i]=i;
429                 P=*_apply(makesequence(gv.back(),L),contextptr)._VECTptr;
430             }
431             for (iterateur it=P.begin();it!=P.end();++it) {
432                 if (!is_real(*it,contextptr) || !is_positive(*it,contextptr))
433                     return generr("Weights must be nonnegative real numbers");
434                 *it=_evalf(*it,contextptr);
435             }
436             G.molloy_reed(P);
437         }
438     } else if (gv.size()==3) { // preferential attachment
439         if (directed)
440             return generr("This method cannot generate digraphs");
441         if (!gv[1].is_integer() || !is_strictly_positive(gv[1],contextptr))
442             return generr("Expected a positive integer");
443         if (!gv[2].is_integer() || !is_positive(gv[2],contextptr))
444             return generr("Expected a nonnegative integer");
445         int d=gv[1].val,o=gv[2].val;
446         G.preferential_attachment(d,o);
447     } else return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
448     return G.to_gen();
449 }
450 
compute_product_of_graphs(const vecteur & gv,graphe & P,bool cartesian,GIAC_CONTEXT)451 bool compute_product_of_graphs(const vecteur &gv,graphe &P,bool cartesian,GIAC_CONTEXT) {
452     stack<graphe> Gs;
453     for (const_iterateur it=gv.begin();it!=gv.end();++it) {
454         graphe G(contextptr);
455         if (!G.read_gen(*it))
456             return false;
457         Gs.push(G);
458     }
459     P=Gs.top();
460     Gs.pop();
461     while (!Gs.empty()) {
462         graphe G(P);
463         if (cartesian)
464             Gs.top().cartesian_product(G,P); // compute Cartesian product
465         else Gs.top().tensor_product(G,P); // compute tensor product
466         Gs.pop();
467     }
468     return true;
469 }
470 
add_prefix_to_vertex_label(gen & label,int prefix,stringstream & ss)471 void add_prefix_to_vertex_label(gen &label,int prefix, stringstream &ss) {
472     ss.str("");
473     ss << prefix << ":"
474        << (label.type==_STRNG?graphe::genstring2str(label):graphe::gen2str(label));
475     label=graphe::str2gen(ss.str(),true);
476 }
477 
graphunion(graphe & G,const vecteur & gv,bool disjoint)478 int graphunion(graphe &G,const vecteur &gv,bool disjoint) {
479     bool have_properties=false;
480     int k=0,i,j;
481     graphe::ipairs E;
482     stringstream ss;
483     for (const_iterateur it=gv.begin();it!=gv.end();++it) {
484         ++k;
485         graphe Gk(G.giac_context());
486         if (!Gk.read_gen(*it))
487             return _GT_ERR_NOT_A_GRAPH;
488         if (have_properties) {
489             if (G.is_directed()!=Gk.is_directed())
490                 return G.is_directed()?_GT_ERR_DIRECTED_GRAPH_REQUIRED:_GT_ERR_UNDIRECTED_GRAPH_REQUIRED;
491             if (G.is_weighted()!=Gk.is_weighted())
492                 return G.is_weighted()?_GT_ERR_WEIGHTED_GRAPH_REQUIRED:_GT_ERR_UNWEIGHTED_GRAPH_REQUIRED;
493         } else {
494             G.set_directed(Gk.is_directed());
495             G.set_weighted(Gk.is_weighted());
496             have_properties=true;
497         }
498         vecteur V=Gk.vertices();
499         for (iterateur it=V.begin();it!=V.end();++it) {
500             if (disjoint)
501                 add_prefix_to_vertex_label(*it,k,ss);
502             G.add_node(*it,Gk.node_attributes(it-V.begin()));
503         }
504         Gk.get_edges_as_pairs(E);
505         for (graphe::ipairs_iter it=E.begin();it!=E.end();++it) {
506             const gen &v=V[it->first],&w=V[it->second];
507             i=G.node_index(v); j=G.node_index(w);
508             assert(i>=0 && j>=0);
509             if (!disjoint && G.is_weighted() && G.has_edge(i,j))
510                 G.set_edge_attribute(i,j,_GT_ATTRIB_WEIGHT,G.weight(i,j)+Gk.weight(it->first,it->second));
511             if (!G.has_edge(i,j))
512                 G.add_edge(v,w,Gk.edge_attributes(it->first,it->second));
513         }
514     }
515     return -1;
516 }
517 
count_spanning_trees(const graphe & G)518 gen count_spanning_trees(const graphe &G) {
519     matrice L;
520     G.laplacian_matrix(L);
521     const context *ctx=G.giac_context();
522     return _det(_delcols(makesequence(_delrows(makesequence(L,0),ctx),0),ctx),ctx);
523 }
524 
525 // +--------------------------------------------------------------------------+
526 // |                             GIAC COMMANDS                                |
527 // +--------------------------------------------------------------------------+
528 
529 /* USAGE:   foldl(op,id,r1,r2,...)
530  *
531  * Returns the composition of the binary operator or function op, with identity
532  * or initial value id onto its arguments r1, r2, ..., associating from the
533  * left. For example, given three arguments a, b and c and an initial value id,
534  * foldl(op,id,a,b,c) is equivalent to op(op(op(id,a),b),c).
535  */
_foldl(const gen & g,GIAC_CONTEXT)536 gen _foldl(const gen &g,GIAC_CONTEXT) {
537     if (g.type==_STRNG && g.subtype==-1) return g;
538     if (g.type!=_VECT  || g.subtype!=_SEQ__VECT)
539         return gentypeerr(contextptr);
540     vecteur &gv=*g._VECTptr;
541     if (gv.size()<3)
542         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
543     gen &op=gv.front();
544     gen arg=gv[1];
545     for (const_iterateur it=gv.begin()+2;it!=gv.end();++it) {
546         arg=symbolic(at_of,makesequence(op,makesequence(arg,*it)));
547     }
548     return _eval(arg,contextptr);
549 }
550 static const char _foldl_s[]="foldl";
551 static define_unary_function_eval(__foldl,&_foldl,_foldl_s);
552 define_unary_function_ptr5(at_foldl,alias_at_foldl,&__foldl,0,true)
553 
554 /* USAGE:   foldr(op,id,r1,r2,...)
555  *
556  * Returns the composition of the binary operator or function op, with identity
557  * or initial value id onto its arguments r1, r2, ..., associating from the
558  * right. For example, given three arguments a, b and c and an initial value id,
559  * foldl(op,id,a,b,c) is equivalent to op(a,op(b,op(c,id))).
560  */
_foldr(const gen & g,GIAC_CONTEXT)561 gen _foldr(const gen &g,GIAC_CONTEXT) {
562     if (g.type==_STRNG && g.subtype==-1) return g;
563     if (g.type!=_VECT  || g.subtype!=_SEQ__VECT)
564         return gentypeerr(contextptr);
565     vecteur &gv=*g._VECTptr;
566     if (gv.size()<3)
567         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
568     gen &op=gv.front();
569     gen arg=gv[1];
570     for (int i=gv.size();i-->2;) {
571         arg=symbolic(at_of,makesequence(op,makesequence(gv[i],arg)));
572     }
573     return _eval(arg,contextptr);
574 }
575 static const char _foldr_s[]="foldr";
576 static define_unary_function_eval(__foldr,&_foldr,_foldr_s);
577 define_unary_function_ptr5(at_foldr,alias_at_foldr,&__foldr,0,true)
578 
579 /* USAGE:   randvar(f,[params])
580  *          random_variable(f,[params])
581  *          randvar(f,[range=]a..b,[V])
582  *          randvar(L,[V])
583  *
584  * Returns a random variable with the specified probability distribution.
585  */
_randvar(const gen & g,GIAC_CONTEXT)586 gen _randvar(const gen &g,GIAC_CONTEXT) {
587     if (g.type==_STRNG && g.subtype==-1) return g;
588     if (g.is_symb_of_sommet(at_program) || g.type==_FUNC) {
589         if (g==at_normal || g==at_normald || g==at_NORMALD || g==at_randNorm || g==at_randnormald)
590             return symbolic(at_normald,makesequence(0.0,1.0));
591         if (g==at_uniform || g==at_uniformd)
592             return symbolic(at_uniformd,makesequence(0.0,1.0));
593         if (g==at_exp || g==at_exponential || g==at_exponentiald || g==at_EXP)
594             return symbolic(at_exponentiald,1.0);
595         return g;
596     }
597     if (g.type!=_VECT)
598         return gentypeerr(contextptr);
599     vecteur items,weights,categories;
600     if (g.subtype==_SEQ__VECT) {
601         vecteur &gv=*g._VECTptr;
602         if (gv.front().type==_FUNC) {
603             const_iterateur it=gv.begin()+1;
604             for (;it!=gv.end();++it) {
605                 if (!graphe::is_real_number(*it) && it->type!=_IDNT) break;
606             }
607             if (it==gv.end()) {
608                 gen args=change_subtype(vecteur(gv.begin()+1,gv.end()),_SEQ__VECT);
609                 if (gv.front()==at_exp) {
610                     if (args._VECTptr->size()==1)
611                         return symbolic(at_exponentiald,args._VECTptr->front());
612                     return gensizeerr(contextptr);
613                 }
614                 if (gv.front()==at_normal) {
615                     if (args._VECTptr->size()==2)
616                         return symbolic(at_exponentiald,args._VECTptr->front());
617                     return gensizeerr(contextptr);
618                 }
619                 gen dist=symbolic(gv.front()._SYMBptr->sommet,args);
620                 int nd=is_distribution(dist);
621                 if (nd==0) return gensizeerr(contextptr);
622                 return symbolic(distribution(nd)._SYMBptr->sommet,args);
623             }
624         }
625         if (gv.size()<2)
626             return gensizeerr(contextptr);
627         gen mean(undef),sdev(undef),a(undef),b(undef),&f=gv.front();
628         int cnt=1,samples=0;
629         for (const_iterateur it=gv.begin()+1;it!=gv.end();++it,++cnt) {
630             if (it->is_symb_of_sommet(at_equal)) {
631                 gen &lh=it->_SYMBptr->feuille._VECTptr->front();
632                 gen &rh=it->_SYMBptr->feuille._VECTptr->back();
633                 if (lh==at_mean)
634                     mean=rh;
635                 else if (lh==at_stddev)
636                     sdev=rh;
637                 else if (lh==at_variance) {
638                     if (!is_positive(rh,contextptr)) return gensizeerr(contextptr);
639                     sdev=sqrt(rh,contextptr);
640                 } else if (lh==at_range) {
641                     if (rh.type==_VECT) {
642                         if (rh._VECTptr->size()!=2)
643                             return gensizeerr(contextptr);
644                         a=rh._VECTptr->front();
645                         b=rh._VECTptr->back();
646                     } else if (rh.is_symb_of_sommet(at_interval)) {
647                         a=rh._SYMBptr->feuille._VECTptr->front();
648                         b=rh._SYMBptr->feuille._VECTptr->back();
649                     } else return gensizeerr(contextptr);
650                 } else return gensizeerr(contextptr);
651             } else if (it->is_symb_of_sommet(at_interval)) {
652                 a=it->_SYMBptr->feuille._VECTptr->front();
653                 b=it->_SYMBptr->feuille._VECTptr->back();
654             } else if (it->type==_VECT) {
655                 if (f.type==_VECT) {
656                     if (cnt!=1) return gensizeerr(contextptr);
657                     items=*(it->_VECTptr);
658                 } else if (cnt==1)
659                     categories=*(it->_VECTptr);
660                 else items=*(it->_VECTptr);
661             } else if (it->is_integer() && it->val>1 && cnt==2) {
662                 samples=it->val;
663             } else return gensizeerr(contextptr);
664         }
665         if (!is_undef(sdev) && !is_strictly_positive(sdev,contextptr))
666             return gensizeerr(contextptr);
667         if (f.type==_VECT) { // custom discrete distribution
668             weights=*f._VECTptr;
669         } else if (f==at_normald || f==at_NORMALD || f==at_normal) {
670             if (is_undef(mean)) mean=0;
671             if (is_undef(sdev)) sdev=1;
672             mean=_evalf(mean,contextptr);
673             sdev=_evalf(sdev,contextptr);
674             return symbolic(at_normald,makesequence(mean,sdev));
675         } else if (f==at_uniform || f==at_uniformd) {
676             if (!is_undef(a) && !is_undef(b)) {
677                 if (_evalf(a,contextptr).type!=_DOUBLE_ || _evalf(b,contextptr).type!=_DOUBLE_ ||
678                         !is_strictly_greater(b,a,contextptr))
679                     return gensizeerr(contextptr);
680                 return makesequence(at_uniformd,a,b);
681             } else if (!is_undef(mean) && !is_undef(sdev)) {
682                 gen sqrt3=sqrt(3,contextptr);
683                 return symbolic(at_uniformd,makesequence(mean-sqrt3*sdev,mean+sqrt3*sdev));
684             } else return gensizeerr(contextptr);
685         } else if (f==at_poisson || f==at_POISSON) {
686             gen lambda;
687             if (!is_undef(mean)) {
688                 if (!is_undef(sdev) && !is_zero(_ratnormal(sq(sdev)-mean,contextptr)))
689                     return gensizeerr(contextptr);
690                 lambda=mean;
691             } else if (!is_undef(sdev)) {
692                 lambda=sq(sdev);
693             } else return gensizeerr(contextptr);
694             if (!is_strictly_positive(lambda,contextptr))
695                 return gensizeerr(contextptr);
696             return symbolic(at_poisson,lambda);
697         } else if (f==at_exp || f==at_EXP || f==at_exponential || f==at_exponentiald) {
698             gen lambda;
699             if (!is_undef(mean)) {
700                 if (!is_undef(sdev) && !is_zero(_ratnormal(sdev-mean,contextptr)))
701                     return gensizeerr(contextptr);
702                 if (!is_strictly_positive(mean,contextptr))
703                     return gensizeerr(contextptr);
704                 lambda=_inv(mean,contextptr);
705             } else if (!is_undef(sdev)) {
706                 lambda=_inv(sdev,contextptr);
707             } else return gensizeerr(contextptr);
708             if (!is_strictly_positive(lambda,contextptr))
709                 return gensizeerr(contextptr);
710             return symbolic(at_exponentiald,lambda);
711         } else if (f==at_weibull || f==at_weibulld) {
712             if (is_undef(mean) || is_undef(sdev) || !is_strictly_positive(mean,contextptr))
713                 return gensizeerr(contextptr);
714             gen var=sq(sdev);
715             if (is_zero(var))
716                 return gensizeerr(contextptr);
717             identificateur tmp(" k");
718             gen e=_Gamma(1+gen(2)/tmp,contextptr)/_Gamma(1+gen(1)/tmp,contextptr)-var/sq(mean)-1;
719             gen k=_solve(makesequence(e,symb_equal(tmp,max(1,_inv(var,contextptr),contextptr)),_NEWTON_SOLVER),contextptr);
720             gen lambda=mean/_Gamma(1+_inv(k,contextptr),contextptr);
721             return symbolic(at_weibulld,makesequence(k,lambda));
722         } else if (f==at_Gamma || f==at_gammad) {
723             if (is_undef(mean) || is_undef(sdev) || !is_strictly_positive(mean,contextptr))
724                 return gensizeerr(contextptr);
725             gen var=sq(sdev);
726             return symbolic(at_gammad,makesequence(sq(mean)/var,mean/var));
727         } else if (f==at_Beta || f==at_betad) {
728             if (is_undef(mean) || is_undef(sdev) || !is_strictly_positive(mean,contextptr) ||
729                     !is_strictly_greater(1,mean,contextptr))
730                 return gensizeerr(contextptr);
731             gen var=sq(sdev),fac=(var+sq(mean)-mean)/var;
732             gen par1=-mean*fac,par2=(mean-1)*fac;
733             if (!is_strictly_positive(par1,contextptr) || !is_strictly_positive(par2,contextptr))
734                 return gensizeerr(contextptr);
735             return symbolic(at_betad,makesequence(par1,par2));
736         } else if (f==at_geometric) {
737             gen p;
738             if (!is_undef(mean)) {
739                 if (!is_strictly_positive(mean,contextptr) ||
740                         (!is_undef(sdev) && !is_zero(_ratnormal(sq(sdev)-sq(mean)-mean,contextptr))))
741                     return gensizeerr(contextptr);
742                 p=_inv(mean,contextptr);
743             } else if (!is_undef(sdev)) {
744                 gen var=sq(sdev);
745                 p=_ratnormal((sqrt(4*var+1,contextptr)-1)/(2*var),contextptr);
746             } else return gensizeerr(contextptr);
747             return symbolic(at_geometric,p);
748         } else if (f==at_fisher || f==at_fisherd || f==at_snedecor || f==at_chisquare || f==at_chisquared ||
749                    f==at_cauchy || f==at_cauchyd || f==at_student || f==at_studentd) {
750             return generr("Specifying moments is not supported for this distribution");
751         } else if (f==at_multinomial) {
752             if (categories.empty())
753                 return gensizeerr(contextptr);
754             if (items.empty())
755                 return makesequence(at_multinomial,categories);
756             return makesequence(at_multinomial,categories,items);
757         } else if (f==at_binomial || f==at_BINOMIAL) {
758             if (is_undef(mean) || is_undef(sdev) || !is_strictly_positive(mean,contextptr) ||
759                     is_zero(ratnormal(mean-sq(sdev),contextptr)))
760                 return gensizeerr(contextptr);
761             gen tmp=mean-sq(sdev),N=_ratnormal(sq(mean)/tmp,contextptr),p=tmp/mean;
762             if (!is_zero(N-_floor(N,contextptr))) {
763                 N=_round(N,contextptr);
764                 if (is_zero(N))
765                     return gensizeerr(contextptr);
766                 p=mean/N;
767             } else N=_round(N,contextptr);
768             if (!is_greater(p,0,contextptr) || is_strictly_greater(p,1,contextptr))
769                 return gensizeerr(contextptr);
770             return symbolic(at_binomial,makesequence(N,_ratnormal(p,contextptr)));
771         } else if (f.type==_FUNC || f.is_symb_of_sommet(at_program)) { // custom discrete distribution
772             if (!is_integer(a) || !is_integer(b) || !is_strictly_greater(b,a,contextptr))
773                 return gensizeerr(contextptr);
774             if (samples>0) {
775                 identificateur k(" k");
776                 gen support=_seq(makesequence(k,symb_equal(k,symb_interval(a,b)),
777                                               _evalf((b-a)/gen(samples),contextptr)),contextptr);
778                 gen values=_apply(makesequence(f,support),contextptr);
779                 return _randvar(makesequence(values,support),contextptr);
780             }
781             int lb=a.val,ub=b.val;
782             vecteur rng(ub-lb+1);
783             for (iterateur it=rng.begin();it!=rng.end();++it) {
784                 *it=lb+int(it-rng.begin());
785             }
786             weights=*_apply(makesequence(gv.front(),rng),contextptr)._VECTptr;
787             if (items.empty()) items=rng;
788         }
789     } else if (ckmatrix(g)) {
790         if (g._VECTptr->front()._VECTptr->size()!=2)
791             return gendimerr(contextptr);
792         int n=g._VECTptr->size();
793         weights.reserve(n);
794         items.reserve(n);
795         for (int i=0;i<n;++i) {
796             items.push_back(g._VECTptr->at(i)._VECTptr->front());
797             weights.push_back(g._VECTptr->at(i)._VECTptr->back());
798         }
799     } else weights=*g._VECTptr;
800     if (weights.empty())
801         return gensizeerr(contextptr);
802     if (!items.empty() && items.size()!=weights.size())
803         return gendimerr(contextptr);
804     for (const_iterateur it=weights.begin();it!=weights.end();++it) {
805         if (!graphe::is_real_number(*it) || !is_positive(*it,contextptr))
806             return gensizeerr(contextptr);
807     }
808     graphe::ransampl rs(weights,contextptr);
809     gen ret=rs.data();
810     if (!items.empty())
811         ret=mergevecteur(*ret._VECTptr,items);
812     return symbolic(at_discreted,change_subtype(ret,_SEQ__VECT));
813 }
814 static const char _randvar_s[]="randvar";
815 static define_unary_function_eval(__randvar,&_randvar,_randvar_s);
816 define_unary_function_ptr5(at_randvar,alias_at_randvar,&__randvar,0,true)
817 
818 static const char _random_variable_s[]="random_variable";
819 static define_unary_function_eval(__random_variable,&_randvar,_random_variable_s);
820 define_unary_function_ptr5(at_random_variable,alias_at_random_variable,&__random_variable,0,true)
821 
822 /* USAGE:   trail(V)
823  *
824  * Returns a trail of vertices from sequence V (this is a dummy command, it
825  * returns itself).
826  */
_trail(const gen & g,GIAC_CONTEXT)827 gen _trail(const gen &g,GIAC_CONTEXT) {
828     if (g.type==_STRNG && g.subtype==-1) return g;
829     return symbolic(at_trail,g);
830 }
831 static const char _trail_s[]="trail";
832 static define_unary_function_eval(__trail,&_trail,_trail_s);
833 define_unary_function_ptr5(at_trail,alias_at_trail,&__trail,0,true)
834 
835 /* USAGE:   graph(V,[opts])
836  *          graph(V,E,[opts])
837  *          graph(V,E,A,[opts])
838  *          graph(V,A,[opts])
839  *          graph(A,[opts])
840  *          graph("name")
841  *
842  * Create an (un)directed (un)weighted graph from list of vertices V, set of edges
843  * E, and/or adjacency matrix A containing edge weights. All parameters are
844  * optional.
845  *
846  * 'opts' is a sequence of options containing weighted=true/false,
847  * directed=true/false, color=c or coordinates=p. Here c is
848  * integer or list of integers (color(s) to be assigned to vertices (in order))
849  * and p is list of coordinates to assign to vertices (used for drawing).
850  *
851  * A special may be created by specifying its name as a string. Supported names
852  * are: clebsch - coxeter - desargues - dodecahedron - durer - dyck - grinberg
853  * - grotzsch - harries - harries-wong - heawood - herschel - icosahedron -
854  * levi - ljubljana - mcgee - mobius-kantor - nauru - octahedron - pappus -
855  * petersen - robertson - soccerball - shrikhande - tehtrahedron
856 */
_graph(const gen & g,GIAC_CONTEXT)857 gen _graph(const gen &g,GIAC_CONTEXT) {
858     if (g.type==_STRNG && g.subtype==-1) return g;
859     if (g.type==_STRNG) {
860         // construct special graph
861         string name=graphe::genstring2str(g);
862         graphe G(name,contextptr);
863         if (G.is_null())
864             return gt_err(_GT_ERR_NAME_NOT_RECOGNIZED);
865         return G.to_gen();
866     }
867     graphe G(contextptr);
868     if (g.is_integer() && g.val>=0) {
869         vecteur V;
870         G.make_default_labels(V,g.val);
871         G.add_nodes(V);
872     } else if (is_squarematrix(g) && g._VECTptr->size()>2) {
873         // adjacency matrix is given
874         bool size_err;
875         if (!parse_matrix(G,*g._VECTptr,false,0,size_err))
876             return size_err?gendimerr(contextptr):gentypeerr(contextptr);
877     } else if (g.type==_VECT && g.subtype!=_SEQ__VECT) {
878         // list of vertices or set of edges is given
879         if (g.subtype==_SET__VECT) {
880             if (!parse_edges(G,*g._VECTptr,true))
881                 return generrtype("Failed to parse edges");
882         } else G.add_nodes(*g._VECTptr);
883     } else if (g.is_symb_of_sommet(at_trail)) {
884         // a trail is given
885         parse_trail(G,g);
886     } else {
887         if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
888             return gentypeerr(contextptr);
889         vecteur &args=*g._VECTptr;
890         int nargs=args.size(),n=nargs-1;
891         // parse options first
892         bool weighted=false,size_err;
893         while(args[n].is_symb_of_sommet(at_equal)) {
894             vecteur &sides=*args[n]._SYMBptr->feuille._VECTptr;
895             if (!sides.front().is_integer())
896                 return generr("Unrecognized option");
897             switch(sides.front().val) {
898             case _GT_DIRECTED:
899                 if (!sides.back().is_integer())
900                     return generr("Option value not supported");
901                 G.set_directed((bool)sides.back().val);
902                 break;
903             case _GT_WEIGHTED:
904                 if (!sides.back().is_integer())
905                     return generr("Option value not supported");
906                 weighted=(bool)sides.back().val;
907                 break;
908             }
909             n--;
910         }
911         // parse other arguments
912         for (int i=0;i<nargs;++i) {
913             const gen &arg=args[i];
914             if (i<=n && ckmatrix(arg) && arg.subtype!=_SET__VECT) {
915                 // adjacency or weight matrix
916                 matrice &m=*arg._VECTptr;
917                 if (!G.is_directed() && m!=mtran(m))
918                     return gt_err(_GT_ERR_MATRIX_NOT_SYMMETRIC);
919                 if (!parse_matrix(G,m,i==2 || weighted,i,size_err))
920                     return size_err?gendimerr(contextptr):generrtype("Failed to parse matrix");
921             } else if (i==0 && arg.is_integer()) {
922                 int nv=arg.val;
923                 if (nv<0)
924                     return generr("Number of vertices must be positive");
925                 vecteur V;
926                 G.make_default_labels(V,nv);
927                 G.add_nodes(V);
928             } else if (i<2 && arg.type==_VECT) {
929                 int permu_size;
930                 const vecteur &argv=*arg._VECTptr;
931                 if (arg.subtype==_SET__VECT) {
932                     // set of edges
933                     if (!parse_edges(G,argv,true))
934                         return generrtype("Failed to parse edges");
935                 } else if (i==1 && !is_zero(_is_permu(arg,contextptr)) &&
936                            (permu_size=argv.size())>0) {
937                     if (permu_size!=G.node_count())
938                         return generr("Permutation size does not match the number of vertices");
939                     // directed cycle
940                     G.set_directed(true);
941                     int offset=array_start(contextptr);
942                     for (const_iterateur it=argv.begin();it!=argv.end()-1;++it) {
943                         G.add_edge(it->val-offset,(it+1)->val-offset);
944                     }
945                     G.add_edge(argv.back().val-offset,argv.front().val-offset);
946                 } else if (i==0) // list of vertices
947                     G.add_nodes(argv);
948                 else return gentypeerr(contextptr);
949             } else if (arg.is_symb_of_sommet(at_trail)) {
950                 // trail
951                 parse_trail(G,arg);
952             } else if (i>n && arg.is_symb_of_sommet(at_equal)) {
953                 // option
954                 gen &lh=arg._SYMBptr->feuille._VECTptr->front();
955                 gen &rh=arg._SYMBptr->feuille._VECTptr->back();
956                 if (lh.is_integer()) {
957                     switch(lh.val) {
958                     case _COLOR:
959                         // vertex colors are given
960                         if (rh.type==_VECT || int(rh._VECTptr->size())!=G.node_count())
961                             return gensizeerr(contextptr);
962                         if (!parse_vertex_colors(G,rh))
963                             return generr("Failed to parse vertex colors");
964                         break;
965                     }
966                 } else if (lh==at_coordonnees) {
967                     // vertex coordinates are given
968                     if (rh.type!=_VECT)
969                         return gentypeerr(contextptr);
970                     if (int(rh._VECTptr->size())!=G.node_count())
971                         return gensizeerr(contextptr);
972                     bool size_error=false;
973                     if (!parse_vertex_coordinates(G,*rh._VECTptr,size_error))
974                         return size_error?generr("Wrong number of vertex positions;"):
975                                           generrtype("Failed to parse vertex positions");
976                 }
977             } else return gentypeerr(contextptr);
978         }
979     }
980     return G.to_gen();
981 }
982 static const char _graph_s[]="graph";
983 static define_unary_function_eval(__graph,&_graph,_graph_s);
984 define_unary_function_ptr5(at_graph,alias_at_graph,&__graph,0,true)
985 
986 /* USAGE:   digraph(V,[opts])
987  *          digraph(V,E,[opts])
988  *          digraph(A,[opts])
989  *          digraph(V,E,A,[opts])
990  *
991  * Create a directed (un)weighted graph from list of vertices V, set of edges E
992  * and/or adjacency matrix A containing edge weights. All parameters are
993  * optional.
994  *
995  * 'opts' may be one of weighted=true/false, vertexcolor=c and
996  * vertexpositions=p. Here c is integer or list of integers (color(s) to be
997  * assigned to vertices (in order)) and p is list of coordinates to assign to
998  * vertices (used for drawing).
999  */
_digraph(const gen & g,GIAC_CONTEXT)1000 gen _digraph(const gen &g,GIAC_CONTEXT) {
1001     if (g.type==_STRNG && g.subtype==-1) return g;
1002     vecteur args;
1003     if (g.type==_VECT && g.subtype==_SEQ__VECT)
1004         args=*g._VECTptr;
1005     else args.push_back(g);
1006     args.push_back(symbolic(at_equal,makesequence(_GT_DIRECTED,graphe::VRAI)));
1007     return _graph(change_subtype(args,_SEQ__VECT),contextptr);
1008 }
1009 static const char _digraph_s[]="digraph";
1010 static define_unary_function_eval(__digraph,&_digraph,_digraph_s);
1011 define_unary_function_ptr5(at_digraph,alias_at_digraph,&__digraph,0,true)
1012 
1013 /* USAGE:   export_graph(G,"path/to/graphname",[latex[=params]])
1014  *
1015  * Writes the graph G to the file 'graphname.dot' in directory 'path/to' using
1016  * dot format or store the drawing of G in latex format if third argument is
1017  * given, where params is an option or a list of options to be passed to
1018  * the draw_graph command. Returns 1 on success and 0 on failure.
1019  */
_export_graph(const gen & g,GIAC_CONTEXT)1020 gen _export_graph(const gen &g,GIAC_CONTEXT) {
1021     if (g.type==_STRNG && g.subtype==-1) return g;
1022     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
1023         return gentypeerr(contextptr);
1024     vecteur &gv=*g._VECTptr;
1025     if (gv.size()<2 || gv.size()>3)
1026         return gensizeerr(contextptr);
1027     gen &gr=gv.front(),&name=gv[1];
1028     bool to_latex=false;
1029     gen args=undef;
1030     if (gv.size()==3) {
1031         if (gv.back()==at_latex)
1032             to_latex=true;
1033         else if (gv.back().is_symb_of_sommet(at_equal)) {
1034             if (gv.back()._SYMBptr->feuille._VECTptr->front()!=at_latex)
1035                 return generrtype("Option not supported, expected \"latex\"");
1036             to_latex=true;
1037             args=gv.back()._SYMBptr->feuille._VECTptr->back();
1038         } else return gentypeerr(contextptr);
1039     }
1040     graphe G(contextptr);
1041     if (!G.read_gen(gr))
1042         return gt_err(_GT_ERR_NOT_A_GRAPH);
1043     if (name.type!=_STRNG)
1044         return gentypeerr(contextptr);
1045     string filename=graphe::genstring2str(name);
1046     filename=make_absolute_file_path(filename);
1047     if (to_latex) {
1048         if (!has_suffix(filename,".tex"))
1049             filename=filename+".tex";
1050         if (args.type==_VECT)
1051             args._VECTptr->insert(args._VECTptr->begin(),gr);
1052         else if (!is_undef(args))
1053             args=vecteur(1,args);
1054         gen drawing=_draw_graph(args.type==_VECT?change_subtype(args,_SEQ__VECT):gr,contextptr);
1055         return G.write_latex(filename,drawing);
1056     }
1057     if (!has_suffix(filename,".dot") && !has_suffix(filename,".gv"))
1058         filename=filename+".dot";
1059     return 0;//G.write_dot(filename)?1:0;
1060 }
1061 static const char _export_graph_s[]="export_graph";
1062 static define_unary_function_eval(__export_graph,&_export_graph,_export_graph_s);
1063 define_unary_function_ptr5(at_export_graph,alias_at_export_graph,&__export_graph,0,true)
1064 
1065 /* USAGE:   import_graph("path/to/graphname[.dot or .gv]")
1066  *
1067  * Returns the graph constructed from instructions in the file
1068  * 'path/to/graphname.dot' (in dot format) or undef on failure.
1069  */
_import_graph(const gen & g,GIAC_CONTEXT)1070 gen _import_graph(const gen &g,GIAC_CONTEXT) {
1071     if (g.type==_STRNG && g.subtype==-1) return g;
1072     if (g.type!=_STRNG)
1073         return gentypeerr(contextptr);
1074     graphe G(contextptr);
1075     string filename=graphe::genstring2str(g);
1076     if (filename.empty())
1077         return undef;
1078     if (!has_suffix(filename,".dot") && !has_suffix(filename,".gv"))
1079         filename=filename+".dot";
1080     filename=make_absolute_file_path(filename);
1081     if (!G.read_dot(filename)) {
1082         gt_err(_GT_ERR_READING_FAILED);
1083         return undef;
1084     }
1085     gen_map m;
1086     gen l;
1087     for (int i=0;i<G.node_count();++i) {
1088         l=G.node_label(i);
1089         if (!is_exactly_zero(m[l])) {
1090             *logptr(contextptr) << "Warning: imported graph contains equally labeled vertices\n";
1091             break;
1092         }
1093         m[l]=1;
1094     }
1095     return G.to_gen();
1096 }
1097 static const char _import_graph_s[]="import_graph";
1098 static define_unary_function_eval(__import_graph,&_import_graph,_import_graph_s);
1099 define_unary_function_ptr5(at_import_graph,alias_at_import_graph,&__import_graph,0,true)
1100 
1101 /* USAGE:   vertices(G)
1102  *
1103  * Return list of vertices of graph G.
1104  */
_graph_vertices(const gen & g,GIAC_CONTEXT)1105 gen _graph_vertices(const gen &g,GIAC_CONTEXT) {
1106     if (g.type==_STRNG && g.subtype==-1) return g;
1107     graphe G(contextptr);
1108     if (!G.read_gen(g))
1109         return gt_err(_GT_ERR_NOT_A_GRAPH);
1110     return G.vertices();
1111 }
1112 static const char _graph_vertices_s[]="graph_vertices";
1113 static define_unary_function_eval(__graph_vertices,&_graph_vertices,_graph_vertices_s);
1114 define_unary_function_ptr5(at_graph_vertices,alias_at_graph_vertices,&__graph_vertices,0,true)
1115 
1116 /* USAGE:   edges(G,[weights])
1117  *
1118  * Return list of edges of graph G. If second argument is the option 'weights',
1119  * edge weights are also returned.
1120  */
_edges(const gen & g,GIAC_CONTEXT)1121 gen _edges(const gen &g,GIAC_CONTEXT) {
1122     if (g.type==_STRNG && g.subtype==-1) return g;
1123     if (g.type!=_VECT)
1124         return gentypeerr(contextptr);
1125     bool include_weights=false;
1126     graphe G(contextptr);
1127     if (g.subtype==_SEQ__VECT) {
1128         if (int(g._VECTptr->size())!=2)
1129             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
1130         if (g._VECTptr->back().is_integer() && g._VECTptr->back().val==_GT_WEIGHTS)
1131             include_weights=true;
1132     }
1133     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
1134         return gt_err(_GT_ERR_NOT_A_GRAPH);
1135     if (include_weights && !G.is_weighted())
1136         return gt_err(_GT_ERR_WEIGHTED_GRAPH_REQUIRED);
1137     return change_subtype(G.edges(include_weights),_LIST__VECT);
1138 }
1139 static const char _edges_s[]="edges";
1140 static define_unary_function_eval(__edges,&_edges,_edges_s);
1141 define_unary_function_ptr5(at_edges,alias_at_edges,&__edges,0,true)
1142 
1143 /* USAGE:   has_edge(G,e)
1144  *
1145  * Returns true iff the edge e={v,w} is contained in the undirected graph G.
1146  */
_has_edge(const gen & g,GIAC_CONTEXT)1147 gen _has_edge(const gen &g,GIAC_CONTEXT) {
1148     if (g.type==_STRNG && g.subtype==-1) return g;
1149     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
1150         return gentypeerr(contextptr);
1151     vecteur &gv = *g._VECTptr;
1152     if (int(gv.size())!=2)
1153         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
1154     if (gv.back().type!=_VECT)
1155         return gentypeerr(contextptr);
1156     if (int(gv.back()._VECTptr->size())!=2)
1157         return gensizeerr(contextptr);
1158     vecteur &e=*gv.back()._VECTptr;
1159     graphe G(contextptr);
1160     if (!G.read_gen(gv.front()))
1161         return gt_err(_GT_ERR_NOT_A_GRAPH);
1162     if (G.is_directed())
1163         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
1164     int i=G.node_index(e.front()),j=G.node_index(e.back());
1165     if (i<0 || j<0)
1166         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
1167     return graphe::boole(G.has_edge(i,j));
1168 }
1169 static const char _has_edge_s[]="has_edge";
1170 static define_unary_function_eval(__has_edge,&_has_edge,_has_edge_s);
1171 define_unary_function_ptr5(at_has_edge,alias_at_has_edge,&__has_edge,0,true)
1172 
1173 /* USAGE:   has_arc(G,e)
1174  *
1175  * Returns true iff the arc e=[v,w] is contained in directed graph G. If
1176  * e={v,w}, true is returned if directed graph G has both edges [v,w] and
1177  * [v,w].
1178  */
_has_arc(const gen & g,GIAC_CONTEXT)1179 gen _has_arc(const gen &g,GIAC_CONTEXT) {
1180     if (g.type==_STRNG && g.subtype==-1) return g;
1181     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
1182         return gentypeerr(contextptr);
1183     vecteur &gv = *g._VECTptr;
1184     if (int(gv.size())!=2)
1185         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
1186     if (gv.back().type!=_VECT)
1187         return gentypeerr(contextptr);
1188     if (int(gv.back()._VECTptr->size())!=2)
1189         return gensizeerr(contextptr);
1190     vecteur &e=*gv.back()._VECTptr;
1191     bool undirected=gv.back().subtype==_SET__VECT;
1192     graphe G(contextptr);
1193     if (!G.read_gen(gv.front()))
1194         return gt_err(_GT_ERR_NOT_A_GRAPH);
1195     if (!G.is_directed())
1196         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
1197     int i=G.node_index(e.front()),j=G.node_index(e.back());
1198     if (i<0 || j<0)
1199         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
1200     return graphe::boole(G.has_edge(i,j) && (!undirected || G.has_edge(j,i)));
1201 }
1202 static const char _has_arc_s[]="has_arc";
1203 static define_unary_function_eval(__has_arc,&_has_arc,_has_arc_s);
1204 define_unary_function_ptr5(at_has_arc,alias_at_has_arc,&__has_arc,0,true)
1205 
1206 /* USAGE:   adjacency_matrix(G)
1207  *
1208  * Returns the adjacency matrix of a graph G whose rows and columns are indexed
1209  * by the vertices. The entry [i,j] of this matrix is 1 if there is an edge
1210  * from vertex i to vertex j and 0 otherwise.
1211  */
_adjacency_matrix(const gen & g,GIAC_CONTEXT)1212 gen _adjacency_matrix(const gen &g,GIAC_CONTEXT) {
1213     if (g.type==_STRNG && g.subtype==-1) return g;
1214     graphe G(contextptr,false);
1215     if (!G.read_gen(g))
1216         return gt_err(_GT_ERR_NOT_A_GRAPH);
1217     if (G.is_null())
1218         return vecteur(0);
1219     matrice m;
1220     G.adjacency_matrix(m);
1221     return change_subtype(m,_MATRIX__VECT);
1222 }
1223 static const char _adjacency_matrix_s[]="adjacency_matrix";
1224 static define_unary_function_eval(__adjacency_matrix,&_adjacency_matrix,_adjacency_matrix_s);
1225 define_unary_function_ptr5(at_adjacency_matrix,alias_at_adjacency_matrix,&__adjacency_matrix,0,true)
1226 
1227 /* USAGE:   incidence_matrix(G)
1228  *
1229  * Returns the incidence matrix of a graph G whose rows are indexed by the
1230  * vertices and columns by the edges (in order defined by the command 'edges').
1231  * The entry [i,j] of this matrix is 1 if the i-th vertex is incident with the
1232  * j-th edge (for directed graphs, -1 if vertex is the tail and 1 if it is the
1233  * head of arc).
1234  */
_incidence_matrix(const gen & g,GIAC_CONTEXT)1235 gen _incidence_matrix(const gen &g,GIAC_CONTEXT) {
1236     if (g.type==_STRNG && g.subtype==-1) return g;
1237     graphe G(contextptr,false);
1238     if (!G.read_gen(g))
1239         return gt_err(_GT_ERR_NOT_A_GRAPH);
1240     if (G.is_null())
1241         return vecteur(0);
1242     matrice M;
1243     G.incidence_matrix(M);
1244     return change_subtype(M,_MATRIX__VECT);
1245 }
1246 static const char _incidence_matrix_s[]="incidence_matrix";
1247 static define_unary_function_eval(__incidence_matrix,&_incidence_matrix,_incidence_matrix_s);
1248 define_unary_function_ptr5(at_incidence_matrix,alias_at_incidence_matrix,&__incidence_matrix,0,true)
1249 
1250 /* USAGE:   weight_matrix(G)
1251  *
1252  * Returns the weight matrix of graph G.
1253  */
_weight_matrix(const gen & g,GIAC_CONTEXT)1254 gen _weight_matrix(const gen &g,GIAC_CONTEXT) {
1255     if (g.type==_STRNG && g.subtype==-1) return g;
1256     graphe G(contextptr);
1257     if (!G.read_gen(g) || !G.is_weighted())
1258         return gt_err(_GT_ERR_NOT_A_GRAPH);
1259     if (G.is_null())
1260         return vecteur(0);
1261     matrice W;
1262     G.weight_matrix(W);
1263     return change_subtype(W,_MATRIX__VECT);
1264 }
1265 static const char _weight_matrix_s[]="weight_matrix";
1266 static define_unary_function_eval(__weight_matrix,&_weight_matrix,_weight_matrix_s);
1267 define_unary_function_ptr5(at_weight_matrix,alias_at_weight_matrix,&__weight_matrix,0,true)
1268 
1269 /* USAGE:   graph_complement(G)
1270  *
1271  * Return the complement of graph G, i.e. the graph with the same vertex set
1272  * as G, but whose edge (arc) set consists of the edges (arcs) not present in
1273  * G.
1274  */
_graph_complement(const gen & g,GIAC_CONTEXT)1275 gen _graph_complement(const gen &g,GIAC_CONTEXT) {
1276     if (g.type==_STRNG && g.subtype==-1) return g;
1277     graphe G(contextptr),C(contextptr);
1278     if (!G.read_gen(g))
1279         return gt_err(_GT_ERR_NOT_A_GRAPH);
1280     G.complement(C);
1281     return C.to_gen();
1282 }
1283 static const char _graph_complement_s[]="graph_complement";
1284 static define_unary_function_eval(__graph_complement,&_graph_complement,_graph_complement_s);
1285 define_unary_function_ptr5(at_graph_complement,alias_at_graph_complement,&__graph_complement,0,true)
1286 
1287 /* USAGE:   subgraph(G,E)
1288  *
1289  * Returns the subgraph of G defined by the edges in list E.
1290  */
_subgraph(const gen & g,GIAC_CONTEXT)1291 gen _subgraph(const gen &g,GIAC_CONTEXT) {
1292     if (g.type==_STRNG && g.subtype==-1) return g;
1293     if (g.type!=_VECT || g.subtype!=_SEQ__VECT || int(g._VECTptr->size())!=2 ||
1294             g._VECTptr->back().type!=_VECT)
1295         return gentypeerr(contextptr);
1296     vecteur &E=*g._VECTptr->back()._VECTptr;
1297     graphe G(contextptr),S(contextptr);
1298     if (!G.read_gen(g._VECTptr->front()))
1299         return gt_err(_GT_ERR_NOT_A_GRAPH);
1300     graphe::ipairs edges;
1301     bool notfound=false;
1302     if (!G.edges2ipairs(E,edges,notfound))
1303         return notfound?gt_err(_GT_ERR_EDGE_NOT_FOUND):gensizeerr(contextptr);
1304     G.extract_subgraph(edges,S);
1305     return S.to_gen();
1306 }
1307 static const char _subgraph_s[]="subgraph";
1308 static define_unary_function_eval(__subgraph,&_subgraph,_subgraph_s);
1309 define_unary_function_ptr5(at_subgraph,alias_at_subgraph,&__subgraph,0,true)
1310 
1311 /* USAGE:   vertex_degree(G,v)
1312  *
1313  * Returns the degree of the vertex v in graph G (number of edges incident to v).
1314  */
_vertex_degree(const gen & g,GIAC_CONTEXT)1315 gen _vertex_degree(const gen &g,GIAC_CONTEXT) {
1316     if (g.type==_STRNG && g.subtype==-1) return g;
1317     if (g.type!=_VECT || g.subtype!=_SEQ__VECT || int(g._VECTptr->size())<2)
1318         return gentypeerr(contextptr);
1319     graphe G(contextptr);
1320     if (!G.read_gen(g._VECTptr->front()))
1321         return gt_err(_GT_ERR_NOT_A_GRAPH);
1322     int i=G.node_index(g._VECTptr->at(1));
1323     if (i==-1)
1324         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
1325     return G.degree(i);
1326 }
1327 static const char _vertex_degree_s[]="vertex_degree";
1328 static define_unary_function_eval(__vertex_degree,&_vertex_degree,_vertex_degree_s);
1329 define_unary_function_ptr5(at_vertex_degree,alias_at_vertex_degree,&__vertex_degree,0,true)
1330 
1331 /* USAGE:   vertex_in_degree(G,v)
1332  *
1333  * Returns the number of arcs ending in the vertex v of graph G.
1334  */
_vertex_in_degree(const gen & g,GIAC_CONTEXT)1335 gen _vertex_in_degree(const gen &g,GIAC_CONTEXT) {
1336     if (g.type==_STRNG && g.subtype==-1) return g;
1337     if (g.type!=_VECT || g.subtype!=_SEQ__VECT || int(g._VECTptr->size())<2)
1338         return gentypeerr(contextptr);
1339     graphe G(contextptr);
1340     if (!G.read_gen(g._VECTptr->front()))
1341         return gt_err(_GT_ERR_NOT_A_GRAPH);
1342     if (!G.is_directed())
1343         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
1344     int i=G.node_index(g._VECTptr->at(1));
1345     if (i==-1)
1346         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
1347     return G.in_degree(i);
1348 }
1349 static const char _vertex_in_degree_s[]="vertex_in_degree";
1350 static define_unary_function_eval(__vertex_in_degree,&_vertex_in_degree,_vertex_in_degree_s);
1351 define_unary_function_ptr5(at_vertex_in_degree,alias_at_vertex_in_degree,&__vertex_in_degree,0,true)
1352 
1353 /* USAGE:   vertex_out_degree(G,v)
1354  *
1355  * Returns the number of arcs starting in the vertex v of graph G.
1356  */
_vertex_out_degree(const gen & g,GIAC_CONTEXT)1357 gen _vertex_out_degree(const gen &g,GIAC_CONTEXT) {
1358     if (g.type==_STRNG && g.subtype==-1) return g;
1359     if (g.type!=_VECT || g.subtype!=_SEQ__VECT || int(g._VECTptr->size())<2)
1360         return gentypeerr(contextptr);
1361     graphe G(contextptr);
1362     if (!G.read_gen(g._VECTptr->front()))
1363         return gt_err(_GT_ERR_NOT_A_GRAPH);
1364     if (!G.is_directed())
1365         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
1366     int i=G.node_index(g._VECTptr->at(1));
1367     if (i==-1)
1368         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
1369     return G.out_degree(i);
1370 }
1371 static const char _vertex_out_degree_s[]="vertex_out_degree";
1372 static define_unary_function_eval(__vertex_out_degree,&_vertex_out_degree,_vertex_out_degree_s);
1373 define_unary_function_ptr5(at_vertex_out_degree,alias_at_vertex_out_degree,&__vertex_out_degree,0,true)
1374 
1375 /* USAGE:   induced_subgraph(G,V)
1376  *
1377  * Returns the subgraph of G induced by the vertices in list V.
1378  */
_induced_subgraph(const gen & g,GIAC_CONTEXT)1379 gen _induced_subgraph(const gen &g,GIAC_CONTEXT) {
1380     if (g.type==_STRNG && g.subtype==-1) return g;
1381     if (g.type!=_VECT || g.subtype!=_SEQ__VECT || int(g._VECTptr->size())!=2 ||
1382             g._VECTptr->back().type!=_VECT)
1383         return gentypeerr(contextptr);
1384     graphe G(contextptr);
1385     if (!G.read_gen(g._VECTptr->front()))
1386         return gt_err(_GT_ERR_NOT_A_GRAPH);
1387     vecteur &V=*g._VECTptr->back()._VECTptr;
1388     int i=0,index;
1389     vector<int> vi(V.size());
1390     for (const_iterateur it=V.begin();it!=V.end();++it) {
1391         if ((index=G.node_index(*it))<0)
1392             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
1393         vi[i++]=index;
1394     }
1395     graphe S(G);
1396     G.induce_subgraph(vi,S);
1397     return S.to_gen();
1398 }
1399 static const char _induced_subgraph_s[]="induced_subgraph";
1400 static define_unary_function_eval(__induced_subgraph,&_induced_subgraph,_induced_subgraph_s);
1401 define_unary_function_ptr5(at_induced_subgraph,alias_at_induced_subgraph,&__induced_subgraph,0,true)
1402 
1403 /* USAGE:   maximum_matching(G)
1404  *
1405  * Returns the list of edges representing maximum matching for graph G. Jack
1406  * Edmonds' blossom algorithm is used.
1407  */
_maximum_matching(const gen & g,GIAC_CONTEXT)1408 gen _maximum_matching(const gen &g,GIAC_CONTEXT) {
1409     if (g.type==_STRNG && g.subtype==-1) return g;
1410     graphe G(contextptr);
1411     if (!G.read_gen(g))
1412         return gt_err(_GT_ERR_NOT_A_GRAPH);
1413     if (G.is_directed())
1414         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
1415     graphe::ipairs matching;
1416     G.find_maximum_matching(matching);
1417     vecteur res;
1418     for (graphe::ipairs_iter it=matching.begin();it!=matching.end();++it) {
1419         res.push_back(makevecteur(G.node_label(it->first),G.node_label(it->second)));
1420     }
1421     return change_subtype(res,_LIST__VECT);
1422 }
1423 static const char _maximum_matching_s[]="maximum_matching";
1424 static define_unary_function_eval(__maximum_matching,&_maximum_matching,_maximum_matching_s);
1425 define_unary_function_ptr5(at_maximum_matching,alias_at_maximum_matching,&__maximum_matching,0,true)
1426 
1427 /* USAGE:   bipartite_matching(G)
1428  *
1429  * Returns the list of edges of a maximum matching an undirected unweighted
1430  * bipartite graph G.
1431  */
_bipartite_matching(const gen & g,GIAC_CONTEXT)1432 gen _bipartite_matching(const gen &g,GIAC_CONTEXT) {
1433     if (g.type==_STRNG && g.subtype==-1) return g;
1434     graphe G(contextptr);
1435     if (!G.read_gen(g))
1436         return gt_err(_GT_ERR_NOT_A_GRAPH);
1437     if (G.is_directed())
1438         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
1439     if (G.is_weighted())
1440         return gt_err(_GT_ERR_UNWEIGHTED_GRAPH_REQUIRED);
1441     graphe::ivector p1,p2;
1442     if (!G.is_bipartite(p1,p2))
1443         return gt_err(_GT_ERR_NOT_BIPARTITE);
1444     graphe::ipairs matching;
1445     vecteur res(G.bipartite_matching(p1,p2,matching));
1446     for (graphe::ipairs_iter it=matching.begin();it!=matching.end();++it) {
1447         res[it-matching.begin()]=makevecteur(G.node_label(it->first),G.node_label(it->second));
1448     }
1449     return change_subtype(res,_LIST__VECT);
1450 }
1451 static const char _bipartite_matching_s[]="bipartite_matching";
1452 static define_unary_function_eval(__bipartite_matching,&_bipartite_matching,_bipartite_matching_s);
1453 define_unary_function_ptr5(at_bipartite_matching,alias_at_bipartite_matching,&__bipartite_matching,0,true)
1454 
1455 /* USAGE:   make_directed(G,[A])
1456  *
1457  * Returns the copy of an undirected graph G in which every edge is converted
1458  * to a pair of arcs [and with weights specified by matrix A].
1459  */
_make_directed(const gen & g,GIAC_CONTEXT)1460 gen _make_directed(const gen &g,GIAC_CONTEXT) {
1461     if (g.type==_STRNG && g.subtype==-1) return g;
1462     if (g.type!=_VECT)
1463         return gentypeerr(contextptr);
1464     vecteur &gv=*g._VECTptr;
1465     bool hasweights=g.subtype==_SEQ__VECT;
1466     if (hasweights && gv.size()!=2)
1467         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
1468     graphe G(contextptr);
1469     if (!G.read_gen(hasweights?gv.front():g))
1470         return gt_err(_GT_ERR_NOT_A_GRAPH);
1471     if (G.is_directed())
1472         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
1473     G.make_directed();
1474     if (hasweights) {
1475         if (gv.back().type!=_VECT)
1476             return gentypeerr(contextptr);
1477         G.make_weighted(*gv.back()._VECTptr);
1478     }
1479     return G.to_gen();
1480 }
1481 static const char _make_directed_s[]="make_directed";
1482 static define_unary_function_eval(__make_directed,&_make_directed,_make_directed_s);
1483 define_unary_function_ptr5(at_make_directed,alias_at_make_directed,&__make_directed,0,true)
1484 
1485 /* USAGE:   underlying_graph(G)
1486  *
1487  * Returns the underlying graph of G, i.e. the graph obtained by stripping
1488  * directions and weights from arcs (pairs of arcs connecting the same vertices
1489  * are merged to a single edge).
1490  */
_underlying_graph(const gen & g,GIAC_CONTEXT)1491 gen _underlying_graph(const gen &g,GIAC_CONTEXT) {
1492     if (g.type==_STRNG && g.subtype==-1) return g;
1493     graphe G(contextptr);
1494     if (!G.read_gen(g))
1495         return gt_err(_GT_ERR_NOT_A_GRAPH);
1496     graphe U;
1497     G.underlying(U);
1498     return U.to_gen();
1499 }
1500 static const char _underlying_graph_s[]="underlying_graph";
1501 static define_unary_function_eval(__underlying_graph,&_underlying_graph,_underlying_graph_s);
1502 define_unary_function_ptr5(at_underlying_graph,alias_at_underlying_graph,&__underlying_graph,0,true)
1503 
1504 /* USAGE:   cycle_graph(n or V)
1505  *
1506  * Returns the cyclic graph with n vertices (or with vertices from list V).
1507  */
_cycle_graph(const gen & g,GIAC_CONTEXT)1508 gen _cycle_graph(const gen &g,GIAC_CONTEXT) {
1509     if (g.type==_STRNG && g.subtype==-1) return g;
1510     graphe G(contextptr);
1511     if (!vertices_from_integer_or_vecteur(g,G))
1512         return gt_err(_GT_ERR_BAD_VERTICES);
1513     if (G.node_count()<3)
1514         return generr("At least 3 vertices are required");
1515     G.make_cycle_graph();
1516     /*
1517     stringstream ss;
1518     ss << "C" << G.node_count();
1519     G.set_name(ss.str());
1520     */
1521     return G.to_gen();
1522 }
1523 static const char _cycle_graph_s[]="cycle_graph";
1524 static define_unary_function_eval(__cycle_graph,&_cycle_graph,_cycle_graph_s);
1525 define_unary_function_ptr5(at_cycle_graph,alias_at_cycle_graph,&__cycle_graph,0,true)
1526 
1527 /* USAGE:   lcf_graph(jumps,[exp])
1528  *
1529  * Returns the graph constructed from LCF notation jumps[^exp]. Arguments are
1530  * list of integers 'jumps' [and a positive integer 'exp', by default 1].
1531  */
_lcf_graph(const gen & g,GIAC_CONTEXT)1532 gen _lcf_graph(const gen &g,GIAC_CONTEXT) {
1533     if (g.type==_STRNG && g.subtype==-1) return g;
1534     vecteur jumps;
1535     gen e;
1536     if (g.type!=_VECT)
1537         return gentypeerr(contextptr);
1538     if (g.subtype==_SEQ__VECT) {
1539         if (g._VECTptr->size()!=2)
1540             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
1541         e=g._VECTptr->at(1);
1542         if (!e.is_integer() || e.val<=0 || g._VECTptr->front().type!=_VECT)
1543             return gentypeerr(contextptr);
1544         jumps=*g._VECTptr->front()._VECTptr;
1545     } else {
1546         jumps=*g._VECTptr;
1547         e=gen(1);
1548     }
1549     if (jumps.empty())
1550         return gensizeerr(contextptr);
1551     if (!is_integer_vecteur(jumps))
1552         return gentypeerr(contextptr);
1553     graphe G(contextptr);
1554     graphe::ivector ijumps;
1555     vecteur2vector_int(jumps,0,ijumps);
1556     G.make_lcf_graph(ijumps,e.val);
1557     return G.to_gen();
1558 }
1559 static const char _lcf_graph_s[]="lcf_graph";
1560 static define_unary_function_eval(__lcf_graph,&_lcf_graph,_lcf_graph_s);
1561 define_unary_function_ptr5(at_lcf_graph,alias_at_lcf_graph,&__lcf_graph,0,true)
1562 
1563 /* USAGE:   hypercube_graph(n)
1564  *
1565  * Constructs and returns the hypercube graph in dimension n (with 2^n vertices).
1566  */
_hypercube_graph(const gen & g,GIAC_CONTEXT)1567 gen _hypercube_graph(const gen &g,GIAC_CONTEXT) {
1568     if (g.type==_STRNG && g.subtype==-1) return g;
1569     if (!g.is_integer() || g.val<=0)
1570         return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
1571     int n=g.val,N=std::pow(2,n);
1572     graphe G(contextptr);
1573     for (int i=0;i<N;++i) {
1574         G.add_node(graphe::to_binary(i,n));
1575     }
1576     for (int i=0;i<N;++i) {
1577         for (int j=i+1;j<N;++j) {
1578             if (_hamdist(makesequence(i,j),contextptr).val==1)
1579                 G.add_edge(i,j);
1580         }
1581     }
1582     return G.to_gen();
1583 }
1584 static const char _hypercube_graph_s[]="hypercube_graph";
1585 static define_unary_function_eval(__hypercube_graph,&_hypercube_graph,_hypercube_graph_s);
1586 define_unary_function_ptr5(at_hypercube_graph,alias_at_hypercube_graph,&__hypercube_graph,0,true)
1587 
1588 /* USAGE:   seidel_switch(G,V)
1589  *
1590  * Returns a copy of graph G in which edges between vertices in list V and
1591  * vertices not in V are inverted, i.e. replaced with a set of edges from V to
1592  * other vertices which is not present in G.
1593  */
_seidel_switch(const gen & g,GIAC_CONTEXT)1594 gen _seidel_switch(const gen &g,GIAC_CONTEXT) {
1595     if (g.type==_STRNG && g.subtype==-1) return g;
1596     if (g.type!=_VECT || g.subtype!=_SEQ__VECT || int(g._VECTptr->size())!=2 ||
1597             g._VECTptr->back().type!=_VECT)
1598         return gentypeerr(contextptr);
1599     graphe G(contextptr),H(contextptr);
1600     if (!G.read_gen(g._VECTptr->front()))
1601         return gt_err(_GT_ERR_NOT_A_GRAPH);
1602     if (G.is_directed())
1603         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
1604     if (G.is_weighted())
1605         return gt_err(_GT_ERR_UNWEIGHTED_GRAPH_REQUIRED);
1606     int n=G.node_count();
1607     vecteur &V=*g._VECTptr->back()._VECTptr;
1608     vector<bool> vb(n,false);
1609     for (const_iterateur it=V.begin();it!=V.end();++it) {
1610         int index=G.node_index(*it);
1611         if (index==-1)
1612             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
1613         vb[index]=true;
1614     }
1615     H.add_nodes(G.vertices());
1616     for (int i=0;i<n;++i) {
1617         for (int j=i+1;j<n;++j) {
1618             bool b=G.has_edge(i,j);
1619             if (vb[i]!=vb[j])
1620                 b=!b;
1621             if (b)
1622                 H.add_edge(i,j);
1623         }
1624     }
1625     return H.to_gen();
1626 }
1627 static const char _seidel_switch_s[]="seidel_switch";
1628 static define_unary_function_eval(__seidel_switch,&_seidel_switch,_seidel_switch_s);
1629 define_unary_function_ptr5(at_seidel_switch,alias_at_seidel_switch,&__seidel_switch,0,true)
1630 
1631 /* USAGE:   draw_graph(G,[options])
1632  *
1633  * Returns the graphic representation of graph G obtained by using
1634  * various algorithms (which can optionally be fine tuned by appending a
1635  * sequence of options after the first argument).
1636  *
1637  * Supported options are:
1638  *
1639  *  - spring: use force-directed method to draw graph G (the default)
1640  *  - tree[=r or [r1,r2,...]]: draw tree or forest G [with optional
1641  *    specification of root nodes]
1642  *  - bipartite: draw the bipartite graph G keeping the partitions separated
1643  *  - plane or planar: draw planar graph G
1644  *  - circle[=<cycle>]: draw graph G as circular using the leading cycle,
1645  *    otherwise one must be specified or all vertices are placed on a circle
1646  *  - plot3d: draw 3D representation of graph G (possible only with the
1647  *    spring method and with G connected)
1648  *  - labels=true or false: draw (the default) or suppress node labels and
1649  *    weights
1650  *
1651  * An exception is raised if a method is specified but the corresponding
1652  * necessary conditions are not met.
1653  */
_draw_graph(const gen & g,GIAC_CONTEXT)1654 gen _draw_graph(const gen &g,GIAC_CONTEXT) {
1655     if (g.type==_STRNG && g.subtype==-1) return g;
1656     if (g.type!=_VECT)
1657         return gentypeerr(contextptr);
1658     vecteur &gv=*g._VECTptr;
1659     bool has_opts=g.subtype==_SEQ__VECT;
1660     graphe G_orig(contextptr);
1661     if (!G_orig.read_gen(has_opts?gv.front():g))
1662         return gt_err(_GT_ERR_NOT_A_GRAPH);
1663     bool labels=G_orig.node_count()<=60,isdir=G_orig.is_directed();
1664     vecteur root_nodes,outer_vertices;
1665     gen coords_dest=undef;
1666     int method=_GT_STYLE_DEFAULT,opt_counter=0;
1667     if (has_opts) {
1668         // parse options
1669         for (const_iterateur it=gv.begin()+1;it!=gv.end();++it) {
1670             opt_counter++;
1671             const gen &opt=*it;
1672             if (opt.type==_IDNT) {
1673                 coords_dest=opt;
1674                 opt_counter--;
1675             } else if (opt.is_symb_of_sommet(at_equal)) {
1676                 gen &lh=opt._SYMBptr->feuille._VECTptr->front();
1677                 gen &rh=opt._SYMBptr->feuille._VECTptr->back();
1678                 if (lh.is_integer()) {
1679                     switch (lh.val) {
1680                     case _GT_TREE:
1681                         if (rh.type==_VECT)
1682                             root_nodes=*rh._VECTptr;
1683                         else
1684                             root_nodes.push_back(rh);
1685                         method=_GT_STYLE_TREE;
1686                         break;
1687                     case _LABELS:
1688                         if (!rh.is_integer())
1689                             return generrtype("Expected an integer");
1690                         labels=(bool)rh.val;
1691                         opt_counter--;
1692                         break;
1693                     }
1694                 } else if (lh==at_cercle || lh==at_convexhull) {
1695                     if (rh.type!=_VECT)
1696                         return gentypeerr(contextptr);
1697                     outer_vertices=*rh._VECTptr;
1698                     method=_GT_STYLE_CIRCLE;
1699                 }
1700             } else if (opt==at_cercle || opt==at_convexhull)
1701                 method=_GT_STYLE_CIRCLE;
1702             else if (opt==at_plan)
1703                 method=_GT_STYLE_PLANAR;
1704             else if (opt==at_plot3d)
1705                 method=_GT_STYLE_3D;
1706             else if (opt.is_integer()) {
1707                 switch (opt.val) {
1708                 case _GT_TREE:
1709                     method=_GT_STYLE_TREE;
1710                     break;
1711                 case _GT_BIPARTITE:
1712                     method=_GT_STYLE_BIPARTITE;
1713                     break;
1714                 case _GT_SPRING:
1715                     method=_GT_STYLE_SPRING;
1716                     break;
1717                 case _GT_PLANAR:
1718                     method=_GT_STYLE_PLANAR;
1719                 }
1720             }
1721         }
1722         if (opt_counter>1)
1723             return gt_err(_GT_ERR_INVALID_DRAWING_METHOD);
1724     }
1725     graphe G(contextptr);
1726     G_orig.underlying(G);
1727     int i,comp_method=method;
1728     vector<graphe> Cv;
1729     vector<graphe::layout> layouts;
1730     graphe::layout main_layout;
1731     graphe::ivector partition1,partition2;
1732     if (opt_counter==0 && G_orig.has_stored_layout(main_layout)) {
1733         ; // the graph G already has a layout, display it
1734     } else {
1735         graphe::ivectors components;
1736         G.connected_components(components);
1737         int nc=components.size();
1738         graphe::ivector hull;
1739         vecteur roots;
1740         if (!root_nodes.empty()) {
1741             // get the root nodes for forest drawing
1742             if (int(root_nodes.size())!=nc)
1743                 return gt_err(_GT_ERR_INVALID_NUMBER_OF_ROOTS);
1744             graphe::ivector indices(nc);
1745             roots.resize(nc);
1746             for (const_iterateur it=root_nodes.begin();it!=root_nodes.end();++it) {
1747                 i=G.node_index(*it);
1748                 if (i==-1)
1749                     return gt_err(*it,_GT_ERR_VERTEX_NOT_FOUND);
1750                 indices[it-root_nodes.begin()]=i;
1751             }
1752             for (int i=0;i<nc;++i) {
1753                 const graphe::ivector &comp=components[i];
1754                 graphe::ivector::iterator it=indices.begin();
1755                 for (;it!=indices.end();++it) {
1756                     if (find(comp.begin(),comp.end(),*it)!=comp.end())
1757                         break;
1758                 }
1759                 if (it==indices.end())
1760                     return gt_err(_GT_ERR_INVALID_ROOT);
1761                 roots[i]=root_nodes[it-indices.begin()];
1762                 indices.erase(it);
1763             }
1764         }
1765         layouts.resize(nc);
1766         vector<graphe::rectangle> bounding_rects(nc);
1767         bool check=method!=_GT_STYLE_DEFAULT;
1768         double sep=1.0;
1769         // draw the components separately
1770         Cv.reserve(nc);
1771         for (graphe::ivectors_iter it=components.begin();it!=components.end();++it) {
1772             i=it-components.begin();
1773             graphe C(contextptr);
1774             G.induce_subgraph(*it,C);
1775             if (!outer_vertices.empty()) {
1776                 if (nc>1)
1777                     return gt_err(_GT_ERR_CONNECTED_GRAPH_REQUIRED);
1778                 // get the outer face for circular drawing
1779                 int m=outer_vertices.size(),index;
1780                 hull.resize(m);
1781                 for (const_iterateur jt=outer_vertices.begin();jt!=outer_vertices.end();++jt) {
1782                     index=C.node_index(*jt);
1783                     if (index==-1)
1784                         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
1785                     hull[jt-outer_vertices.begin()]=index;
1786                 }
1787             }
1788             graphe::layout &x=layouts[i];
1789             if (it->size()<3)
1790                 comp_method=_GT_STYLE_SPRING;
1791             else if (method==_GT_STYLE_DEFAULT) {
1792                 if (C.is_tree())
1793                     comp_method=_GT_STYLE_TREE;
1794                 else if (C.is_bipartite(partition1,partition2) && partition1.size()>1 && partition2.size()>1)
1795                     comp_method=_GT_STYLE_BIPARTITE;
1796                 else
1797                     comp_method=_GT_STYLE_CIRCLE;
1798             }
1799             switch (comp_method) {
1800             case _GT_STYLE_SPRING:
1801                 C.make_spring_layout(x,2);
1802                 break;
1803             case _GT_STYLE_3D:
1804                 C.make_spring_layout(x,3);
1805                 break;
1806             case _GT_STYLE_TREE:
1807                 if (check && !C.is_tree())
1808                     return gt_err(_GT_ERR_NOT_A_TREE);
1809                 C.make_tree_layout(x,sep,roots.empty()?0:C.node_index(roots[i]));
1810                 break;
1811             case _GT_STYLE_PLANAR:
1812                 if (!C.make_planar_layout(x))
1813                     return gt_err(_GT_ERR_NOT_PLANAR);
1814                 break;
1815             case _GT_STYLE_CIRCLE:
1816                 if (hull.empty()) {
1817                     gen_map vmap;
1818                     for (int vi=C.node_count();vi-->0;) {
1819                         vmap[C.node_label(vi)]=vi;
1820                     }
1821                     for (gen_map::const_iterator it=vmap.begin();it!=vmap.end();++it) {
1822                         hull.push_back(it->second.val);
1823                     }
1824                 }
1825                 C.make_circular_layout(x,hull,1.25);
1826                 hull.clear();
1827                 break;
1828             case _GT_STYLE_BIPARTITE:
1829                 if (check && !C.is_bipartite(partition1,partition2))
1830                     return gt_err(_GT_ERR_NOT_BIPARTITE);
1831                 C.make_bipartite_layout(x,partition1,partition2);
1832                 break;
1833             }
1834             if (C.node_count()>2 && (comp_method==_GT_STYLE_PLANAR || comp_method==_GT_STYLE_SPRING))
1835                 C.layout_best_rotation(x);
1836             if (comp_method!=_GT_TREE)
1837                 graphe::scale_layout(x,sep*std::sqrt((double)C.node_count()));
1838             Cv.push_back(C);
1839         }
1840         // combine component layouts
1841         graphe::point dx(method==_GT_STYLE_3D?3:2,0.0);
1842         for (int i=0;i<nc;++i) {
1843             graphe::rectangle &rect=bounding_rects[i];
1844             rect=graphe::layout_bounding_rect(layouts[i],sep/PLASTIC_NUMBER_3);
1845             dx[0]=-rect.x();
1846             dx[1]=-rect.y();
1847             graphe::translate_layout(layouts[i],dx);
1848         }
1849         graphe::rectangle::comparator comp;
1850         sort(bounding_rects.begin(),bounding_rects.end(),comp);
1851         graphe::pack_rectangles(bounding_rects);
1852         for (vector<graphe::rectangle>::const_iterator it=bounding_rects.begin();it!=bounding_rects.end();++it) {
1853             dx[0]=it->x();
1854             dx[1]=it->y();
1855             graphe::translate_layout(*(it->get_layout()),dx);
1856         }
1857         int i,j;
1858         main_layout.resize(G.node_count());
1859         for (vector<graphe>::const_iterator it=Cv.begin();it!=Cv.end();++it) {
1860             graphe::layout &x=layouts[it-Cv.begin()];
1861             for (graphe::layout_iter jt=x.begin();jt!=x.end();++jt) {
1862                 const graphe::vertex &v=it->node(jt-x.begin());
1863                 i=components[it-Cv.begin()][jt-x.begin()];
1864                 main_layout[i]=*jt;
1865                 if (isdir) {
1866                     for (graphe::ivector_iter nt=v.neighbors().begin();nt!=v.neighbors().end();++nt) {
1867                         j=components[it-Cv.begin()][*nt];
1868                         const graphe::attrib &attr=v.neighbor_attributes(*nt);
1869                         if (G_orig.has_edge(i,j))
1870                             G_orig.set_edge_attribute(i,j,_GT_ATTRIB_POSITION,attr.find(_GT_ATTRIB_POSITION)->second);
1871                     }
1872                 }
1873             }
1874         }
1875     }
1876     vecteur drawing,coords;
1877     if (!is_undef(coords_dest)) {
1878         // store vertex coordinates to coords_dest
1879         coords.resize(main_layout.size());
1880         for (graphe::layout_iter it=main_layout.begin();it!=main_layout.end();++it) {
1881             coords[it-main_layout.begin()]=method==_GT_STYLE_3D?
1882                         makevecteur(it->at(0),it->at(1),it->at(2)) :
1883                         makecomplex(it->front(),it->back());
1884         }
1885         identifier_assign(*coords_dest._IDNTptr,coords,contextptr);
1886     }
1887     if (isdir || G_orig.is_weighted())
1888         G_orig.edge_labels_placement(main_layout);
1889     if (method!=_GT_STYLE_3D) {
1890         drawing.push_back(symb_equal(change_subtype(gen(_AXES),_INT_PLOT),0));
1891         drawing.push_back(symb_equal(change_subtype(gen(_GL_ORTHO),_INT_PLOT),1));
1892     }
1893     G_orig.draw_edges(drawing,main_layout);
1894     G_orig.draw_nodes(drawing,main_layout);
1895     if (labels)
1896         G_orig.draw_labels(drawing,main_layout);
1897     return drawing;
1898 }
1899 static const char _draw_graph_s[]="draw_graph";
1900 static define_unary_function_eval(__draw_graph,&_draw_graph,_draw_graph_s);
1901 define_unary_function_ptr5(at_draw_graph,alias_at_draw_graph,&__draw_graph,0,true)
1902 
1903 /* USAGE:   sierpinski_graph(n,k,[triangle])
1904  *
1905  * Returns Sierpinski (triangle) graph S(n,k) (resp. ST(n,k)).
1906  */
_sierpinski_graph(const gen & g,GIAC_CONTEXT)1907 gen _sierpinski_graph(const gen &g,GIAC_CONTEXT) {
1908     if (g.type==_STRNG && g.subtype==-1) return g;
1909     int n,k=3;
1910     bool trng=false;
1911     if (g.is_integer())
1912         n=g.val;
1913     else {
1914         if (g.type!=_VECT || g.subtype!=_SEQ__VECT ||
1915                 !g._VECTptr->front().is_integer() || !g._VECTptr->at(1).is_integer())
1916             return gentypeerr();
1917         n=g._VECTptr->front().val;
1918         k=g._VECTptr->at(1).val;
1919         if (n<=0 || k<=0)
1920             return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
1921         if (g._VECTptr->size()>2 && g._VECTptr->at(2)==at_triangle)
1922             trng=true;
1923     }
1924     graphe G(contextptr);
1925     G.make_sierpinski_graph(n,k,trng);
1926     return G.to_gen();
1927 }
1928 static const char _sierpinski_graph_s[]="sierpinski_graph";
1929 static define_unary_function_eval(__sierpinski_graph,&_sierpinski_graph,_sierpinski_graph_s);
1930 define_unary_function_ptr5(at_sierpinski_graph,alias_at_sierpinski_graph,&__sierpinski_graph,0,true)
1931 
1932 /* USAGE:   complete_graph(n or V)
1933  *          complete_graph(m,n)
1934  *          complete_graph(n1,n2,...,nk)
1935  *
1936  * Create and return a complete graph with vertices from list V or enumerated
1937  * with first n integers. When two positive integers m and n are given, a
1938  * complete bipartite graph is returned (or complete k-partite graph if k
1939  * integers n1,n2,...,nk are given).
1940  */
_complete_graph(const gen & g,GIAC_CONTEXT)1941 gen _complete_graph(const gen &g,GIAC_CONTEXT) {
1942     if (g.type==_STRNG && g.subtype==-1) return g;
1943     graphe G(contextptr);
1944     if (g.type==_VECT && g.subtype!=_SEQ__VECT) {
1945         G.add_nodes(*g._VECTptr);
1946         G.make_complete_graph();
1947     } else if (g.is_integer() && g.val>0){
1948         vecteur V;
1949         G.make_default_labels(V,g.val);
1950         G.add_nodes(V);
1951         G.make_complete_graph();
1952     } else if (g.type==_VECT && g.subtype==_SEQ__VECT) {
1953         // multipartite graph
1954         vecteur &partition_sizes_gen=*g._VECTptr;
1955         vector<int> partition_sizes;
1956         for (const_iterateur it=partition_sizes_gen.begin();it!=partition_sizes_gen.end();++it) {
1957             if (!it->is_integer() || it->val<=0)
1958                 return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
1959             partition_sizes.push_back(it->val);
1960         }
1961         graphe::layout x;
1962         G.make_complete_multipartite_graph(partition_sizes,&x);
1963         if (!x.empty())
1964             G.store_layout(x);
1965     } else
1966         return gentypeerr(contextptr);
1967     return G.to_gen();
1968 }
1969 static const char _complete_graph_s[]="complete_graph";
1970 static define_unary_function_eval(__complete_graph,&_complete_graph,_complete_graph_s);
1971 define_unary_function_ptr5(at_complete_graph,alias_at_complete_graph,&__complete_graph,0,true)
1972 
1973 /* USAGE:   petersen_graph(n,[k])
1974  *
1975  * Returns the generalized Petersen graph G(n,k), where n and k are positive
1976  * integers. Parameter k defaults to 2.
1977  */
_petersen_graph(const gen & g,GIAC_CONTEXT)1978 gen _petersen_graph(const gen &g,GIAC_CONTEXT) {
1979     if (g.type==_STRNG && g.subtype==-1) return g;
1980     int n=5,k=2;
1981     if (g.is_integer()) {
1982         n=g.val;
1983         if (n<=0)
1984             return gentypeerr(contextptr);
1985     } else if (g.type==_VECT && g.subtype==_SEQ__VECT) {
1986         if ((g._VECTptr->size())!=2)
1987             return gensizeerr(contextptr);
1988         if (!g._VECTptr->front().is_integer() || !g._VECTptr->at(1).is_integer())
1989             return gentypeerr(contextptr);
1990         n=g._VECTptr->front().val;
1991         k=g._VECTptr->at(1).val;
1992         if (n<=0 || k<=0)
1993             return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
1994     }
1995     graphe G(contextptr);
1996     graphe::layout x;
1997     G.make_petersen_graph(n,k,&x);
1998     G.store_layout(x);
1999     return G.to_gen();
2000 }
2001 static const char _petersen_graph_s[]="petersen_graph";
2002 static define_unary_function_eval(__petersen_graph,&_petersen_graph,_petersen_graph_s);
2003 define_unary_function_ptr5(at_petersen_graph,alias_at_petersen_graph,&__petersen_graph,0,true)
2004 
2005 /* USAGE:   random_graph(n or V,p)
2006  *          random_graph(n or V,m)
2007  *
2008  * Returns a random undirected unweighted graph with n vertices where two
2009  * vertices are connected with probability p. Alternatively, m edges are
2010  * created at random. Instead of number n of vertices, a list V of vertex
2011  * labels may be specified.
2012  */
_random_graph(const gen & g,GIAC_CONTEXT)2013 gen _random_graph(const gen &g,GIAC_CONTEXT) {
2014     if (g.type==_STRNG && g.subtype==-1) return g;
2015     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2016         return gentypeerr(contextptr);
2017     return randomgraph(*g._VECTptr,false,contextptr);
2018 }
2019 static const char _random_graph_s[]="random_graph";
2020 static define_unary_function_eval(__random_graph,&_random_graph,_random_graph_s);
2021 define_unary_function_ptr5(at_random_graph,alias_at_random_graph,&__random_graph,0,true)
2022 
2023 /* USAGE:   random_digraph(n or V,p)
2024  *          random_digraph(n or V,m)
2025  *
2026  * Returns a random directed unweighted graph with n vertices where two
2027  * vertices are connected with probability p. Alternatively, m edges are
2028  * created at random. Instead of number n of vertices, a list V of vertex
2029  * labels may be specified.
2030  */
_random_digraph(const gen & g,GIAC_CONTEXT)2031 gen _random_digraph(const gen &g,GIAC_CONTEXT) {
2032     if (g.type==_STRNG && g.subtype==-1) return g;
2033     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2034         return gentypeerr(contextptr);
2035     return randomgraph(*g._VECTptr,true,contextptr);
2036 }
2037 static const char _random_digraph_s[]="random_digraph";
2038 static define_unary_function_eval(__random_digraph,&_random_digraph,_random_digraph_s);
2039 define_unary_function_ptr5(at_random_digraph,alias_at_random_digraph,&__random_digraph,0,true)
2040 
2041 /* USAGE:   random_bipartite_graph(n or [a,b],p)
2042  *          random_bipartite_graph(n or [a,b],m)
2043  *
2044  * Returns a random undirected unweighted bipartite graph with n vertices where
2045  * each possible edge is present with probability p. Alternatively, m edges are
2046  * created at random. Also, when first argument is list [a,b] of integers, two
2047  * groups of vertices with sizes a and b are created.
2048  */
_random_bipartite_graph(const gen & g,GIAC_CONTEXT)2049 gen _random_bipartite_graph(const gen &g,GIAC_CONTEXT) {
2050     if (g.type==_STRNG && g.subtype==-1) return g;
2051     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2052         return gentypeerr(contextptr);
2053     vecteur &gv=*g._VECTptr;
2054     if (gv.size()!=2)
2055         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
2056     if (!is_strictly_positive(gv.back(),contextptr))
2057         return gentypeerr(contextptr);
2058     int n,a,b;
2059     vecteur V,W;
2060     graphe G(contextptr);
2061     if (gv.front().is_integer()) {
2062         n=gv.front().val;
2063         if (n<1)
2064             return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
2065         a=G.rand_integer(n-1)+1;
2066         b=n-a;
2067     } else if (gv.front().type==_VECT && gv.front()._VECTptr->size()==2) {
2068         vecteur &ab=*gv.front()._VECTptr;
2069         if (!ab.front().is_integer() || !ab.back().is_integer())
2070             return generrtype("Expected a pair of integers");
2071         a=ab.front().val;
2072         b=ab.back().val;
2073         if (a<=0 || b<=0)
2074             return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
2075     } else return gentypeerr(contextptr);
2076     int m=0;
2077     double p;
2078     if (gv.back().is_integer()) {
2079         if ((m=gv.back().val)<1)
2080             return generr("Expected a positive integer");
2081         if (m>a*b)
2082             return generr("Number of edges too large");
2083         p=m;
2084     } else p=gv.back().DOUBLE_val();
2085     G.make_default_labels(V,a,0);
2086     G.make_default_labels(W,b,a);
2087     G.reserve_nodes(a+b);
2088     G.add_nodes(mergevecteur(V,W));
2089     G.make_random_bipartite(a,b,p);
2090     return G.to_gen();
2091 }
2092 static const char _random_bipartite_graph_s[]="random_bipartite_graph";
2093 static define_unary_function_eval(__random_bipartite_graph,&_random_bipartite_graph,_random_bipartite_graph_s);
2094 define_unary_function_ptr5(at_random_bipartite_graph,alias_at_random_bipartite_graph,&__random_bipartite_graph,0,true)
2095 
2096 /* USAGE:   random_tournament(n or V)
2097  *
2098  * Returns a random tournament graph with n vertices, which may be specified as
2099  * list V of their labels.
2100  */
_random_tournament(const gen & g,GIAC_CONTEXT)2101 gen _random_tournament(const gen &g,GIAC_CONTEXT) {
2102     if (g.type==_STRNG && g.subtype==-1) return g;
2103     graphe G(contextptr);
2104     G.set_directed(true);
2105     if (!vertices_from_integer_or_vecteur(g,G))
2106         return gt_err(_GT_ERR_BAD_VERTICES);
2107     int n=G.node_count();
2108     if (n<2)
2109         return gensizeerr(contextptr);
2110     for (int i=0;i<n;++i) {
2111         for (int j=i+1;j<n;++j) {
2112             if (giac_rand(contextptr)%2==0)
2113                 G.add_edge(i,j);
2114             else
2115                 G.add_edge(j,i);
2116         }
2117     }
2118     return G.to_gen();
2119 }
2120 static const char _random_tournament_s[]="random_tournament";
2121 static define_unary_function_eval(__random_tournament,&_random_tournament,_random_tournament_s);
2122 define_unary_function_ptr5(at_random_tournament,alias_at_random_tournament,&__random_tournament,0,true)
2123 
2124 /* USAGE:   random_regular_graph(n or V,d,[connected])
2125  *
2126  * Returns a random d-regular graph with n vertices, which may be specified as
2127  * list V of their labels.
2128  */
_random_regular_graph(const gen & g,GIAC_CONTEXT)2129 gen _random_regular_graph(const gen &g,GIAC_CONTEXT) {
2130     if (g.type==_STRNG && g.subtype==-1) return g;
2131     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2132         return gentypeerr(contextptr);
2133     graphe G(contextptr);
2134     vecteur V;
2135     vecteur &gv=*g._VECTptr;
2136     if (gv.front().is_integer())
2137         G.make_default_labels(V,gv.front().val);
2138     else if (gv.front().type==_VECT)
2139         V=*gv.front()._VECTptr;
2140     else
2141         return gentypeerr(contextptr);
2142     if (gv.size()<2)
2143         return gensizeerr(contextptr);
2144     if (!gv[1].is_integer() || !is_strictly_positive(gv[1],contextptr))
2145         return gentypeerr(contextptr);
2146     int d=gv[1].val;
2147     bool connected=false;
2148     if (gv.size()>2 && gv[2].is_integer() && gv[2].val==_GT_CONNECTED)
2149         connected=true;
2150     int n=V.size();
2151     if (n<=d+1 || (n*d)%2!=0) // check the necessary condition
2152         return generr("Graph does not exist");
2153     G.reserve_nodes(V.size());
2154     G.add_nodes(V);
2155     G.make_random_regular(d,connected);
2156     return G.to_gen();
2157 }
2158 static const char _random_regular_graph_s[]="random_regular_graph";
2159 static define_unary_function_eval(__random_regular_graph,&_random_regular_graph,_random_regular_graph_s);
2160 define_unary_function_ptr5(at_random_regular_graph,alias_at_random_regular_graph,&__random_regular_graph,0,true)
2161 
2162 /* USAGE:   random_sequence_graph(List(D))
2163  *
2164  * Returns a random graph with n=|D| vertices where i-th vertex has degree D[i].
2165  */
_random_sequence_graph(const gen & g,GIAC_CONTEXT)2166 gen _random_sequence_graph(const gen &g,GIAC_CONTEXT) {
2167     if (g.type==_STRNG && g.subtype==-1) return g;
2168     if (g.type!=_VECT)
2169         return gentypeerr(contextptr);
2170     int n=g._VECTptr->size(),m=0;
2171     if (n==0)
2172         return generr("Expected a non-empty list");
2173     if (_is_graphic_sequence(g,contextptr)==graphe::FAUX)
2174         return gt_err(_GT_ERR_NOT_A_GRAPHIC_SEQUENCE);
2175     graphe::ivector deg(n);
2176     for (const_iterateur it=g._VECTptr->begin();it!=g._VECTptr->end();++it) {
2177         m+=(deg[it-g._VECTptr->begin()]=it->val);
2178     }
2179     if ((m%2)!=0)
2180         return generr("Sum of degrees must be even");
2181     graphe G(contextptr);
2182     vecteur labels;
2183     G.make_default_labels(labels,n);
2184     G.reserve_nodes(n);
2185     G.add_nodes(labels);
2186     G.make_random_sequential(deg);
2187     return G.to_gen();
2188 }
2189 static const char _random_sequence_graph_s[]="random_sequence_graph";
2190 static define_unary_function_eval(__random_sequence_graph,&_random_sequence_graph,_random_sequence_graph_s);
2191 define_unary_function_ptr5(at_random_sequence_graph,alias_at_random_sequence_graph,&__random_sequence_graph,0,true)
2192 
2193 /* USAGE:   random_tree(n or V,[d])
2194  *
2195  * Returns a random tree graph with n vertices, which may be specified as list
2196  * V of their labels. Optional parameter d is a positive integer which
2197  * represents the upper bound for degree of graph.
2198  */
_random_tree(const gen & g,GIAC_CONTEXT)2199 gen _random_tree(const gen &g,GIAC_CONTEXT) {
2200     if (g.type==_STRNG && g.subtype==-1) return g;
2201     int maxd=-1,n;
2202     bool rooted=false;
2203     vecteur V;
2204     graphe G(contextptr);
2205     if (g.is_integer()) {
2206         n=g.val;
2207     } else if (g.type==_VECT) {
2208         vecteur &gv=*g._VECTptr;
2209         if (g.subtype==_SEQ__VECT) {
2210             if (gv.size()!=2)
2211                 return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
2212             if (gv.front().is_integer()) {
2213                 n=gv.front().val;
2214             } else if (gv.front().type==_VECT) {
2215                 V=*gv.front()._VECTptr;
2216                 n=V.size();
2217             } else
2218                 return gt_err(_GT_ERR_BAD_VERTICES);
2219             if (gv.back().is_integer()) {
2220                 if ((maxd=gv.back().val)<2)
2221                     return generr("Maximum degree must be at least two");
2222             } else if (is_inf(gv.back()))
2223                 maxd=RAND_MAX;
2224             else if (gv.back()==at_maple_root)
2225                 rooted=true;
2226             else if (gv.back().is_symb_of_sommet(at_equal) &&
2227                      gv.back()._SYMBptr->feuille._VECTptr->front()==at_maple_root &&
2228                      gv.front().type==_VECT) {
2229                 gen root_label=gv.back()._SYMBptr->feuille._VECTptr->back();
2230                 iterateur it;
2231                 if ((it=find(V.begin(),V.end(),root_label))==V.end())
2232                     return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
2233                 V.erase(it);
2234                 V.insert(V.begin(),root_label);
2235                 rooted=true;
2236             }
2237             else return generr("Invalid option");
2238         } else {
2239             V=gv;
2240             n=V.size();
2241         }
2242     } else return gentypeerr(contextptr);
2243     if (n<1)
2244         return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
2245     if (V.empty())
2246         G.make_default_labels(V,n);
2247     G.reserve_nodes(V.size());
2248     G.add_nodes(V);
2249     if (maxd>0)
2250         G.make_random_tree(maxd);
2251     else if (rooted)
2252         G.make_random_rooted_tree();
2253     else
2254         G.make_random_free_tree();
2255     return G.to_gen();
2256 }
2257 static const char _random_tree_s[]="random_tree";
2258 static define_unary_function_eval(__random_tree,&_random_tree,_random_tree_s);
2259 define_unary_function_ptr5(at_random_tree,alias_at_random_tree,&__random_tree,0,true)
2260 
2261 /* USAGE:   random_planar_graph(n or V,p,[c])
2262  *
2263  * Return random biconnected planar graph with n vertices, which can also be
2264  * specified as a list V of their labels.
2265  */
_random_planar_graph(const gen & g,GIAC_CONTEXT)2266 gen _random_planar_graph(const gen &g,GIAC_CONTEXT) {
2267     if (g.type==_STRNG && g.subtype==-1) return g;
2268     double p=0.5;
2269     int connectivity=1;
2270     gen spec;
2271     if (g.is_integer() || (g.type==_VECT && g.subtype!=_SEQ__VECT))
2272         spec=g;
2273     else if (g.type==_VECT && g.subtype==_SEQ__VECT) {
2274         vecteur &gv=*g._VECTptr;
2275         if (gv.size()>1 && gv.size()<=3) {
2276             spec=gv.front();
2277             if (_evalf(gv[1],contextptr).type!=_DOUBLE_)
2278                 return gentypeerr(contextptr);
2279             p=gv[1].DOUBLE_val();
2280             if (p<0 || p>=1)
2281                 return generrtype("Invalid probability specification");
2282             if (gv.size()==3) {
2283                 if (!gv.back().is_integer() || gv.back().val<0 || gv.back().val>3)
2284                     return generrtype("Invalid connectivity specification");
2285                 connectivity=gv.back().val;
2286             }
2287         } else return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
2288     } else return gentypeerr(contextptr);
2289     graphe G(contextptr);
2290     if (!vertices_from_integer_or_vecteur(spec,G))
2291         return gt_err(_GT_ERR_BAD_VERTICES);
2292     G.make_random_planar(p,connectivity);
2293     return G.to_gen();
2294 }
2295 static const char _random_planar_graph_s[]="random_planar_graph";
2296 static define_unary_function_eval(__random_planar_graph,&_random_planar_graph,_random_planar_graph_s);
2297 define_unary_function_ptr5(at_random_planar_graph,alias_at_random_planar_graph,&__random_planar_graph,0,true)
2298 
2299 /* USAGE:   assign_edge_weights(G,m,n)
2300  *          assign_edge_weights(G,a..b)
2301  *
2302  * Assigns random edge weights to the edges of graph G and returns a modified
2303  * copy of G. If integers n and m such that n>=m are specified, weights are
2304  * integers randomly chosen in [m,n]. If an interval a..b is specified, weights
2305  * are uniformly distributed in the interval [a,b).
2306  */
_assign_edge_weights(const gen & g,GIAC_CONTEXT)2307 gen _assign_edge_weights(const gen &g,GIAC_CONTEXT) {
2308     if (g.type==_STRNG && g.subtype==-1) return g;
2309     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2310         return gentypeerr(contextptr);
2311     vecteur &gv=*g._VECTptr;
2312     graphe G(contextptr);
2313     if (!G.read_gen(gv.front()))
2314         return gt_err(_GT_ERR_NOT_A_GRAPH);
2315     G.set_weighted(true);
2316     if (gv.size()==3) {
2317         if (!gv[1].is_integer() || !gv[2].is_integer())
2318             return gentypeerr(contextptr);
2319         int m=gv[1].val,n=gv[2].val;
2320         if (m>n)
2321             return generr("Lower bound too high");
2322         G.randomize_edge_weights(m,n,true);
2323     } else if (gv.size()==2) {
2324         if (!gv.back().is_symb_of_sommet(at_interval))
2325             return generrtype("Expected an interval");
2326         gen a=gv.back()._SYMBptr->feuille._VECTptr->front(),
2327                 b=gv.back()._SYMBptr->feuille._VECTptr->back();
2328         if (!graphe::is_real_number(a) || !graphe::is_real_number(b))
2329             return generrtype("Expected an interval of reals");
2330         G.randomize_edge_weights(_evalf(a,contextptr).DOUBLE_val(),_evalf(b,contextptr).DOUBLE_val(),false);
2331     }
2332     return G.to_gen();
2333 }
2334 static const char _assign_edge_weights_s[]="assign_edge_weights";
2335 static define_unary_function_eval(__assign_edge_weights,&_assign_edge_weights,_assign_edge_weights_s);
2336 define_unary_function_ptr5(at_assign_edge_weights,alias_at_assign_edge_weights,&__assign_edge_weights,0,true)
2337 
2338 /* USAGE:   articulation_points(G)
2339  *
2340  * Returns the list of articulation points (i.e. cut vertices) of graph G.
2341  */
_articulation_points(const gen & g,GIAC_CONTEXT)2342 gen _articulation_points(const gen &g,GIAC_CONTEXT) {
2343     if (g.type==_STRNG && g.subtype==-1) return g;
2344     graphe G(contextptr);
2345     if (!G.read_gen(g))
2346         return gt_err(_GT_ERR_NOT_A_GRAPH);
2347     graphe::ivector v;
2348     if (G.is_directed()) {
2349         graphe U(contextptr,false);
2350         G.underlying(U);
2351         U.find_cut_vertices(v);
2352     } else G.find_cut_vertices(v);
2353     return G.get_node_labels(v);
2354 }
2355 static const char _articulation_points_s[]="articulation_points";
2356 static define_unary_function_eval(__articulation_points,&_articulation_points,_articulation_points_s);
2357 define_unary_function_ptr5(at_articulation_points,alias_at_articulation_points,&__articulation_points,0,true)
2358 
2359 /* USAGE:   biconnected_components(G)
2360  *
2361  * Returns the list of biconnected components of graph G. Every component is
2362  * given as a list of vertices belonging to that component.
2363  */
_biconnected_components(const gen & g,GIAC_CONTEXT)2364 gen _biconnected_components(const gen &g,GIAC_CONTEXT) {
2365     if (g.type==_STRNG && g.subtype==-1) return g;
2366     graphe G(contextptr);
2367     if (!G.read_gen(g))
2368         return gt_err(_GT_ERR_NOT_A_GRAPH);
2369     graphe::ivectors comp;
2370     if (G.is_directed()) {
2371         graphe U(contextptr,false);
2372         G.underlying(U);
2373         U.biconnected_components(comp);
2374     } else G.biconnected_components(comp);
2375     vecteur res;
2376     G.ivectors2vecteur(comp,res,true);
2377     return change_subtype(res,_LIST__VECT);
2378 }
2379 static const char _biconnected_components_s[]="biconnected_components";
2380 static define_unary_function_eval(__biconnected_components,&_biconnected_components,_biconnected_components_s);
2381 define_unary_function_ptr5(at_biconnected_components,alias_at_biconnected_components,&__biconnected_components,0,true)
2382 
2383 /* USAGE:   add_arc(G,e)
2384  *
2385  * Returns graph G (which must be directed) with added arc e (or trail or list
2386  * of arcs).
2387  */
_add_arc(const gen & g,GIAC_CONTEXT)2388 gen _add_arc(const gen &g,GIAC_CONTEXT) {
2389     if (g.type==_STRNG && g.subtype==-1) return g;
2390     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2391         return gentypeerr(contextptr);
2392     vecteur &gv=*g._VECTptr;
2393     if (g._VECTptr->size()!=2)
2394         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
2395     if (gv.back().type!=_VECT)
2396         return gt_err(_GT_ERR_INVALID_EDGE);
2397     vecteur E=gv.back().is_symb_of_sommet(at_trail)?
2398                 *gv.back()._SYMBptr->feuille._VECTptr:*gv.back()._VECTptr;
2399     graphe G(contextptr);
2400     if (!G.read_gen(gv.front()))
2401         return gt_err(_GT_ERR_NOT_A_GRAPH);
2402     if (!G.is_directed())
2403         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
2404     if (!parse_edges(G,E,ckmatrix(gv.back())))
2405         return gendimerr(contextptr);
2406     return G.to_gen();
2407 }
2408 static const char _add_arc_s[]="add_arc";
2409 static define_unary_function_eval(__add_arc,&_add_arc,_add_arc_s);
2410 define_unary_function_ptr5(at_add_arc,alias_at_add_arc,&__add_arc,0,true)
2411 
2412 /* USAGE:   delete_arc(G,e)
2413  *
2414  * Returns graph G (which must be directed) with arc e (or trail or list of
2415  * arcs) removed.
2416  */
_delete_arc(const gen & g,GIAC_CONTEXT)2417 gen _delete_arc(const gen &g,GIAC_CONTEXT) {
2418     if (g.type==_STRNG && g.subtype==-1) return g;
2419     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2420         return gentypeerr(contextptr);
2421     vecteur &gv=*g._VECTptr;
2422     if (gv.size()!=2)
2423         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
2424     if (gv.back().type!=_VECT)
2425         return gt_err(_GT_ERR_INVALID_EDGE);
2426     vecteur E=gv.back().is_symb_of_sommet(at_trail)?
2427                 *gv.back()._SYMBptr->feuille._VECTptr:*gv.back()._VECTptr;
2428     graphe G(contextptr);
2429     if (!G.read_gen(gv.front()))
2430         return gt_err(_GT_ERR_NOT_A_GRAPH);
2431     if (!G.is_directed())
2432         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
2433     if (!delete_edges(G,E))
2434         return gendimerr(contextptr);
2435     return G.to_gen();
2436 }
2437 static const char _delete_arc_s[]="delete_arc";
2438 static define_unary_function_eval(__delete_arc,&_delete_arc,_delete_arc_s);
2439 define_unary_function_ptr5(at_delete_arc,alias_at_delete_arc,&__delete_arc,0,true)
2440 
2441 /* USAGE:   add_edge(G,e)
2442  *
2443  * Returns graph G (which must be undirected) with added edge e (or trail or
2444  * list of edges).
2445  */
_add_edge(const gen & g,GIAC_CONTEXT)2446 gen _add_edge(const gen &g,GIAC_CONTEXT) {
2447     if (g.type==_STRNG && g.subtype==-1) return g;
2448     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2449         return gentypeerr(contextptr);
2450     vecteur &gv=*g._VECTptr;
2451     if (gv.size()!=2)
2452         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
2453     if (gv.back().type!=_VECT)
2454         return gt_err(_GT_ERR_INVALID_EDGE);
2455     vecteur E=gv.back().is_symb_of_sommet(at_trail)?
2456                 *gv.back()._SYMBptr->feuille._VECTptr:*gv.back()._VECTptr;
2457     graphe G(contextptr);
2458     if (!G.read_gen(gv.front()))
2459         return gt_err(_GT_ERR_NOT_A_GRAPH);
2460     if (G.is_directed())
2461         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
2462     if (!parse_edges(G,E,ckmatrix(gv.back())))
2463         return gendimerr(contextptr);
2464     return G.to_gen();
2465 }
2466 static const char _add_edge_s[]="add_edge";
2467 static define_unary_function_eval(__add_edge,&_add_edge,_add_edge_s);
2468 define_unary_function_ptr5(at_add_edge,alias_at_add_edge,&__add_edge,0,true)
2469 
2470 /* USAGE:   delete_edge(G,e)
2471  *
2472  * Returns graph G (which must be undirected) with edge e (or trail or list of
2473  * edges) removed.
2474  */
_delete_edge(const gen & g,GIAC_CONTEXT)2475 gen _delete_edge(const gen &g,GIAC_CONTEXT) {
2476     if (g.type==_STRNG && g.subtype==-1) return g;
2477     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2478         return gentypeerr(contextptr);
2479     vecteur &gv=*g._VECTptr;
2480     if (gv.size()!=2)
2481         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
2482     if (gv.back().type!=_VECT)
2483         return gt_err(_GT_ERR_INVALID_EDGE);
2484     vecteur E=gv.back().is_symb_of_sommet(at_trail)?
2485                 *gv.back()._SYMBptr->feuille._VECTptr:*gv.back()._VECTptr;
2486     graphe G(contextptr);
2487     if (!G.read_gen(gv.front()))
2488         return gt_err(_GT_ERR_NOT_A_GRAPH);
2489     if (G.is_directed())
2490         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
2491     if (!delete_edges(G,E))
2492         return gendimerr(contextptr);
2493     return G.to_gen();
2494 }
2495 static const char _delete_edge_s[]="delete_edge";
2496 static define_unary_function_eval(__delete_edge,&_delete_edge,_delete_edge_s);
2497 define_unary_function_ptr5(at_delete_edge,alias_at_delete_edge,&__delete_edge,0,true)
2498 
2499 /* USAGE:   add_vertex(G,v)
2500  *
2501  * Returns graph G with added vertex v (or vertices from v if v is a list).
2502  */
_add_vertex(const gen & g,GIAC_CONTEXT)2503 gen _add_vertex(const gen &g,GIAC_CONTEXT) {
2504     if (g.type==_STRNG && g.subtype==-1) return g;
2505     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2506         return gentypeerr(contextptr);
2507     if (g._VECTptr->size()!=2)
2508         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
2509     graphe G(contextptr);
2510     if (!G.read_gen(g._VECTptr->front()))
2511         return gt_err(_GT_ERR_NOT_A_GRAPH);
2512     gen &V=g._VECTptr->back();
2513     if (V.type==_VECT)
2514         G.add_nodes(*V._VECTptr);
2515     else
2516         G.add_node(V);
2517     return G.to_gen();
2518 }
2519 static const char _add_vertex_s[]="add_vertex";
2520 static define_unary_function_eval(__add_vertex,&_add_vertex,_add_vertex_s);
2521 define_unary_function_ptr5(at_add_vertex,alias_at_add_vertex,&__add_vertex,0,true)
2522 
2523 /* USAGE:   delete_vertex(G,v)
2524  *
2525  * Returns graph G with vertex v (or vertices from v if v is a list) removed.
2526  */
_delete_vertex(const gen & g,GIAC_CONTEXT)2527 gen _delete_vertex(const gen &g,GIAC_CONTEXT) {
2528     if (g.type==_STRNG && g.subtype==-1) return g;
2529     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2530         return gentypeerr(contextptr);
2531     if (g._VECTptr->size()!=2)
2532         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
2533     graphe G(contextptr),H(contextptr);
2534     if (!G.read_gen(g._VECTptr->front()))
2535         return gt_err(_GT_ERR_NOT_A_GRAPH);
2536     gen &V=g._VECTptr->back();
2537     graphe::iset s;
2538     if (V.type==_VECT) {
2539         G.labels2iset(*V._VECTptr,s);
2540         G.isolate_nodes(s);
2541         G.remove_isolated_nodes(s,H);
2542     } else {
2543         s.insert(G.node_index(V));
2544         if (*s.begin()==-1)
2545             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
2546         G.isolate_nodes(s);
2547         G.remove_isolated_nodes(s,H);
2548     }
2549     return H.to_gen();
2550 }
2551 static const char _delete_vertex_s[]="delete_vertex";
2552 static define_unary_function_eval(__delete_vertex,&_delete_vertex,_delete_vertex_s);
2553 define_unary_function_ptr5(at_delete_vertex,alias_at_delete_vertex,&__delete_vertex,0,true)
2554 
2555 /* USAGE:   contract_edge(G,e)
2556  *
2557  * Returns graph G (undirected) with edge e contracted (collapsed).
2558  */
_contract_edge(const gen & g,GIAC_CONTEXT)2559 gen _contract_edge(const gen &g,GIAC_CONTEXT) {
2560     if (g.type==_STRNG && g.subtype==-1) return g;
2561     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2562         return gentypeerr(contextptr);
2563     if (g._VECTptr->size()!=2)
2564         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
2565     if (g._VECTptr->back().type!=_VECT)
2566         return gt_err(_GT_ERR_INVALID_EDGE);
2567     graphe G(contextptr),H(contextptr);
2568     if (!G.read_gen(g._VECTptr->front()))
2569         return gt_err(_GT_ERR_NOT_A_GRAPH);
2570     if (G.is_directed())
2571         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
2572     vecteur &E=*g._VECTptr->back()._VECTptr;
2573     if (E.size()!=2)
2574         return gensizeerr(contextptr);
2575     int i=G.node_index(E.front()),j=G.node_index(E.back());
2576     if (i<0 || j<0 || !G.has_edge(i,j))
2577         return gt_err(_GT_ERR_EDGE_NOT_FOUND);
2578     G.contract_edge(i,j);
2579     graphe::iset s;
2580     s.insert(j);
2581     G.remove_isolated_nodes(s,H);
2582     return H.to_gen();
2583 }
2584 static const char _contract_edge_s[]="contract_edge";
2585 static define_unary_function_eval(__contract_edge,&_contract_edge,_contract_edge_s);
2586 define_unary_function_ptr5(at_contract_edge,alias_at_contract_edge,&__contract_edge,0,true)
2587 
2588 /* USAGE:   connected_components(G)
2589  *
2590  * Returns list of lists of vertices, each sublist representing a connected
2591  * component of graph G. Individual components can be made available as
2592  * subgraphs of G by applying the induced_subgraph command.
2593  */
_connected_components(const gen & g,GIAC_CONTEXT)2594 gen _connected_components(const gen &g,GIAC_CONTEXT) {
2595     if (g.type==_STRNG && g.subtype==-1) return g;
2596     graphe G(contextptr);
2597     if (!G.read_gen(g))
2598         return gt_err(_GT_ERR_NOT_A_GRAPH);
2599     graphe::ivectors comp;
2600     if (G.is_directed()) {
2601         graphe U(contextptr,false);
2602         G.underlying(U);
2603         U.connected_components(comp);
2604     } else  G.connected_components(comp);
2605     vecteur res;
2606     G.ivectors2vecteur(comp,res,true);
2607     return change_subtype(res,_LIST__VECT);
2608 }
2609 static const char _connected_components_s[]="connected_components";
2610 static define_unary_function_eval(__connected_components,&_connected_components,_connected_components_s);
2611 define_unary_function_ptr5(at_connected_components,alias_at_connected_components,&__connected_components,0,true)
2612 
2613 /* USAGE:   departures(G,[v])
2614  *
2615  * Returns the list of vertices of directed graph G which are connected by v
2616  * with arcs such that tails are in v. If v is omitted, list of departures is
2617  * computed for every vertex and a list of lists is returned.
2618  */
_departures(const gen & g,GIAC_CONTEXT)2619 gen _departures(const gen &g,GIAC_CONTEXT) {
2620     if (g.type==_STRNG && g.subtype==-1) return g;
2621     if (g.type!=_VECT)
2622         return gentypeerr(contextptr);
2623     return flights(g,false,g.subtype!=_SEQ__VECT,contextptr);
2624 }
2625 static const char _departures_s[]="departures";
2626 static define_unary_function_eval(__departures,&_departures,_departures_s);
2627 define_unary_function_ptr5(at_departures,alias_at_departures,&__departures,0,true)
2628 
2629 /* USAGE:   arrivals(G,[v])
2630  *
2631  * Returns the list of vertices of directed graph G which are connected by v
2632  * with arcs such that heads are in v. If v is omitted, list of arrivals is
2633  * computed for every vertex and a list of lists is returned.
2634  */
_arrivals(const gen & g,GIAC_CONTEXT)2635 gen _arrivals(const gen &g,GIAC_CONTEXT) {
2636     if (g.type==_STRNG && g.subtype==-1) return g;
2637     if (g.type!=_VECT)
2638         return gentypeerr(contextptr);
2639     return flights(g,true,g.subtype!=_SEQ__VECT,contextptr);
2640 }
2641 static const char _arrivals_s[]="arrivals";
2642 static define_unary_function_eval(__arrivals,&_arrivals,_arrivals_s);
2643 define_unary_function_ptr5(at_arrivals,alias_at_arrivals,&__arrivals,0,true)
2644 
2645 /* USAGE:   incident_edges(G,v)
2646  *
2647  * Returns the list of all edges incident to the vertex v (or to the vertices in
2648  * the list v).
2649  */
_incident_edges(const gen & g,GIAC_CONTEXT)2650 gen _incident_edges(const gen &g,GIAC_CONTEXT) {
2651     if (g.type==_STRNG && g.subtype==-1) return g;
2652     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2653         return gentypeerr(contextptr);
2654     if (g._VECTptr->size()!=2)
2655         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
2656     graphe G(contextptr);
2657     if (!G.read_gen(g._VECTptr->front()))
2658         return gt_err(_GT_ERR_NOT_A_GRAPH);
2659     vecteur V;
2660     if (g._VECTptr->back().type==_VECT)
2661         V=*g._VECTptr->back()._VECTptr;
2662     else
2663         V.push_back(g._VECTptr->back());
2664     graphe::ivector indices;
2665     int i;
2666     for (const_iterateur it=V.begin();it!=V.end();++it) {
2667         if ((i=G.node_index(*it))==-1)
2668             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
2669         indices.push_back(i);
2670     }
2671     graphe::edgeset E;
2672     G.incident_edges(indices,E);
2673     vecteur res;
2674     for (graphe::edgeset_iter it=E.begin();it!=E.end();++it) {
2675         res.push_back(makevecteur(G.node_label(it->first),G.node_label(it->second)));
2676     }
2677     return change_subtype(res,_LIST__VECT);
2678 }
2679 static const char _incident_edges_s[]="incident_edges";
2680 static define_unary_function_eval(__incident_edges,&_incident_edges,_incident_edges_s);
2681 define_unary_function_ptr5(at_incident_edges,alias_at_incident_edges,&__incident_edges,0,true)
2682 
2683 /* USAGE:   make_weighted(G,[M])
2684  *
2685  * Returns the copy of graph G with edge/arc weights set as specified by matrix
2686  * M. If M is omitted, a suqare matrix of ones is used. If G is undirected, M
2687  * is assumed to be symmetric.
2688  */
_make_weighted(const gen & g,GIAC_CONTEXT)2689 gen _make_weighted(const gen &g,GIAC_CONTEXT) {
2690     if (g.type==_STRNG && g.subtype==-1) return g;
2691     if (g.type!=_VECT)
2692         return gentypeerr(contextptr);
2693     bool has_matrix=g.subtype==_SEQ__VECT;
2694     if (has_matrix && g._VECTptr->size()!=2)
2695         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
2696     graphe G(contextptr);
2697     if (!G.read_gen(has_matrix?g._VECTptr->front():g))
2698         return gt_err(_GT_ERR_NOT_A_GRAPH);
2699     if (G.is_weighted()) {
2700         if (!has_matrix)
2701             return gt_err(_GT_ERR_UNWEIGHTED_GRAPH_REQUIRED);
2702         graphe::ipairs E;
2703         G.get_edges_as_pairs(E);
2704         G.set_weighted(false);
2705         for (graphe::ipairs_iter it=E.begin();it!=E.end();++it) {
2706             G.discard_edge_attribute(it->first,it->second,_GT_ATTRIB_WEIGHT);
2707         }
2708     }
2709     int n=G.node_count();
2710     matrice m=*_matrix(makesequence(n,n,1),contextptr)._VECTptr;
2711     if (has_matrix) {
2712         m=*g._VECTptr->back()._VECTptr;
2713         if (int(m.size())!=n || int(m.front()._VECTptr->size())!=n)
2714             return gendimerr(contextptr);
2715     }
2716     G.make_weighted(m);
2717     return G.to_gen();
2718 }
2719 static const char _make_weighted_s[]="make_weighted";
2720 static define_unary_function_eval(__make_weighted,&_make_weighted,_make_weighted_s);
2721 define_unary_function_ptr5(at_make_weighted,alias_at_make_weighted,&__make_weighted,0,true)
2722 
2723 /* USAGE:   set_graph_attribute(G,attr1,attr2,...)
2724  *
2725  * Stores the attributes attr1, attr2, ..., each in form tag=value, where tag
2726  * is string, and returns the modified copy of G. Attributes may also be
2727  * specified in a list or as two lists [tag1,tag2,...] and [value1,value2,...].
2728  */
_set_graph_attribute(const gen & g,GIAC_CONTEXT)2729 gen _set_graph_attribute(const gen &g,GIAC_CONTEXT) {
2730     if (g.type==_STRNG && g.subtype==-1) return g;
2731     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2732         return gentypeerr(contextptr);
2733     vecteur &gv=*g._VECTptr,attr;
2734     graphe G(contextptr);
2735     if (!G.read_gen(g._VECTptr->front()))
2736         return gt_err(_GT_ERR_NOT_A_GRAPH);
2737     if (gv.size()==2 && gv.back().type==_VECT)
2738         attr=*gv.back()._VECTptr;
2739     else if (gv.size()==3 && gv[1].type==_VECT && gv[2].type==_VECT)
2740         attr=*_zip(makesequence(at_equal,gv[1],gv[2]),contextptr)._VECTptr;
2741     else attr=vecteur(gv.begin()+1,gv.end());
2742     int key;
2743     for (const_iterateur it=attr.begin();it!=attr.end();++it) {
2744         if (!it->is_symb_of_sommet(at_equal) || it->_SYMBptr->feuille._VECTptr->front().type!=_STRNG)
2745             return gt_err(_GT_ERR_TAGVALUE_PAIR_EXPECTED);
2746         key=G.tag2index(graphe::genstring2str(it->_SYMBptr->feuille._VECTptr->front()));
2747         G.set_graph_attribute(key,it->_SYMBptr->feuille._VECTptr->back());
2748     }
2749     return G.to_gen();
2750 }
2751 static const char _set_graph_attribute_s[]="set_graph_attribute";
2752 static define_unary_function_eval(__set_graph_attribute,&_set_graph_attribute,_set_graph_attribute_s);
2753 define_unary_function_ptr5(at_set_graph_attribute,alias_at_set_graph_attribute,&__set_graph_attribute,0,true)
2754 
2755 /* USAGE:   set_vertex_attribute(G,v,attr1,attr2,...)
2756  *
2757  * Stores the attributes attr1, attr2, ..., each in form tag=value, where tag
2758  * is string, to vertex v and returns the modified copy of G. Attributes may
2759  * also be specified in a list or as two lists [tag1,tag2,...] and
2760  * [value1,value2,...].
2761  */
_set_vertex_attribute(const gen & g,GIAC_CONTEXT)2762 gen _set_vertex_attribute(const gen &g,GIAC_CONTEXT) {
2763     if (g.type==_STRNG && g.subtype==-1) return g;
2764     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2765         return gentypeerr(contextptr);
2766     vecteur &gv=*g._VECTptr,attr;
2767     if (gv.size()<2)
2768         return gensizeerr(contextptr);
2769     graphe G(contextptr);
2770     if (!G.read_gen(g._VECTptr->front()))
2771         return gt_err(_GT_ERR_NOT_A_GRAPH);
2772     int v,key;
2773     if ((v=G.node_index(gv[1]))<0)
2774         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
2775     if (gv.size()==3 && gv.back().type==_VECT)
2776         attr=*gv.back()._VECTptr;
2777     else if (gv.size()==4 && gv[2].type==_VECT && gv[3].type==_VECT)
2778         attr=*_zip(makesequence(at_equal,gv[2],gv[3]),contextptr)._VECTptr;
2779     else attr=vecteur(gv.begin()+2,gv.end());
2780     for (const_iterateur it=attr.begin();it!=attr.end();++it) {
2781         if (!it->is_symb_of_sommet(at_equal) || it->_SYMBptr->feuille._VECTptr->front().type!=_STRNG)
2782             return gt_err(_GT_ERR_TAGVALUE_PAIR_EXPECTED);
2783         key=G.tag2index(graphe::genstring2str(it->_SYMBptr->feuille._VECTptr->front()));
2784         G.set_node_attribute(v,key,it->_SYMBptr->feuille._VECTptr->back());
2785     }
2786     return G.to_gen();
2787 }
2788 static const char _set_vertex_attribute_s[]="set_vertex_attribute";
2789 static define_unary_function_eval(__set_vertex_attribute,&_set_vertex_attribute,_set_vertex_attribute_s);
2790 define_unary_function_ptr5(at_set_vertex_attribute,alias_at_set_vertex_attribute,&__set_vertex_attribute,0,true)
2791 
2792 /* USAGE:   set_edge_attribute(G,e,attr1,attr2,...)
2793  *
2794  * Stores the attributes attr1, attr2, ..., each in form tag=value, where tag
2795  * is string, to edge e and returns the modified copy of G. Attributes may also
2796  * be specified in a list or as two lists [tag1,tag2,...] and
2797  * [value1,value2,...].
2798  */
_set_edge_attribute(const gen & g,GIAC_CONTEXT)2799 gen _set_edge_attribute(const gen &g,GIAC_CONTEXT) {
2800     if (g.type==_STRNG && g.subtype==-1) return g;
2801     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2802         return gentypeerr(contextptr);
2803     vecteur &gv=*g._VECTptr,attr;
2804     if (gv.size()<2)
2805         return gensizeerr(contextptr);
2806     graphe G(contextptr);
2807     if (!G.read_gen(g._VECTptr->front()))
2808         return gt_err(_GT_ERR_NOT_A_GRAPH);
2809     int i,j,key;
2810     if (gv[1].type!=_VECT || gv[1]._VECTptr->size()!=2)
2811         return gt_err(_GT_ERR_INVALID_EDGE);
2812     if ((i=G.node_index(gv[1]._VECTptr->front()))<0 ||
2813             (j=G.node_index(gv[1]._VECTptr->back()))<0)
2814         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
2815     if (!G.has_edge(i,j))
2816         return gt_err(_GT_ERR_EDGE_NOT_FOUND);
2817     if (gv.size()==3 && gv.back().type==_VECT)
2818         attr=*gv.back()._VECTptr;
2819     else if (gv.size()==4 && gv[2].type==_VECT && gv[3].type==_VECT)
2820         attr=*_zip(makesequence(at_equal,gv[2],gv[3]),contextptr)._VECTptr;
2821     else attr=vecteur(gv.begin()+2,gv.end());
2822     for (const_iterateur it=attr.begin();it!=attr.end();++it) {
2823         if (!it->is_symb_of_sommet(at_equal) || it->_SYMBptr->feuille._VECTptr->front().type!=_STRNG)
2824             return gt_err(_GT_ERR_TAGVALUE_PAIR_EXPECTED);
2825         key=G.tag2index(graphe::genstring2str(it->_SYMBptr->feuille._VECTptr->front()));
2826         G.set_edge_attribute(i,j,key,it->_SYMBptr->feuille._VECTptr->back());
2827     }
2828     return G.to_gen();
2829 }
2830 static const char _set_edge_attribute_s[]="set_edge_attribute";
2831 static define_unary_function_eval(__set_edge_attribute,&_set_edge_attribute,_set_edge_attribute_s);
2832 define_unary_function_ptr5(at_set_edge_attribute,alias_at_set_edge_attribute,&__set_edge_attribute,0,true)
2833 
2834 /* USAGE:   get_graph_attribute(G,tag1,tag2,...)
2835  *
2836  * Get the graph attributes tag1, tag2, ..., i.e. return the sequence of values
2837  * corresponding to the given tags, which may also be specified in a list.
2838  */
_get_graph_attribute(const gen & g,GIAC_CONTEXT)2839 gen _get_graph_attribute(const gen &g,GIAC_CONTEXT) {
2840     if (g.type==_STRNG && g.subtype==-1) return g;
2841     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2842         return gentypeerr(contextptr);
2843     vecteur &gv=*g._VECTptr,tags,values;
2844     graphe G(contextptr);
2845     if (!G.read_gen(gv.front()))
2846         return gt_err(_GT_ERR_NOT_A_GRAPH);
2847     bool istagvec=gv.size()==2 && gv.back().type==_VECT;
2848     if (istagvec)
2849         tags=*gv.back()._VECTptr;
2850     else tags=vecteur(gv.begin()+1,gv.end());
2851     int key;
2852     gen value;
2853     for (const_iterateur it=tags.begin();it!=tags.end();++it) {
2854         if (it->type!=_STRNG)
2855             return gentypeerr(contextptr);
2856         key=G.tag2index(graphe::genstring2str(*it));
2857         G.get_graph_attribute(key,value);
2858         values.push_back(value);
2859     }
2860     return istagvec?values:change_subtype(values,_SEQ__VECT);
2861 }
2862 static const char _get_graph_attribute_s[]="get_graph_attribute";
2863 static define_unary_function_eval(__get_graph_attribute,&_get_graph_attribute,_get_graph_attribute_s);
2864 define_unary_function_ptr5(at_get_graph_attribute,alias_at_get_graph_attribute,&__get_graph_attribute,0,true)
2865 
2866 /* USAGE:   get_vertex_attribute(G,v,tag1,tag2,...)
2867  *
2868  * Get the attributes tag1, tag2, ... assigned to vertex v in graph G, i.e.
2869  * return the sequence of values corresponding to the given tags, which may
2870  * also be specified in a list.
2871  */
_get_vertex_attribute(const gen & g,GIAC_CONTEXT)2872 gen _get_vertex_attribute(const gen &g,GIAC_CONTEXT) {
2873     if (g.type==_STRNG && g.subtype==-1) return g;
2874     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2875         return gentypeerr(contextptr);
2876     vecteur &gv=*g._VECTptr,tags,values;
2877     if (gv.size()<2)
2878         return gensizeerr(contextptr);
2879     graphe G(contextptr);
2880     if (!G.read_gen(gv.front()))
2881         return gt_err(_GT_ERR_NOT_A_GRAPH);
2882     int v,key;
2883     if ((v=G.node_index(gv[1]))<0)
2884         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
2885     bool istagvec=gv.size()==3 && gv.back().type==_VECT;
2886     if (istagvec)
2887         tags=*gv.back()._VECTptr;
2888     else tags=vecteur(gv.begin()+2,gv.end());
2889     gen value;
2890     for (const_iterateur it=tags.begin();it!=tags.end();++it) {
2891         if (it->type!=_STRNG)
2892             return gentypeerr(contextptr);
2893         key=G.tag2index(graphe::genstring2str(*it));
2894         G.get_node_attribute(v,key,value);
2895         values.push_back(value);
2896     }
2897     return istagvec?values:change_subtype(values,_SEQ__VECT);
2898 }
2899 static const char _get_vertex_attribute_s[]="get_vertex_attribute";
2900 static define_unary_function_eval(__get_vertex_attribute,&_get_vertex_attribute,_get_vertex_attribute_s);
2901 define_unary_function_ptr5(at_get_vertex_attribute,alias_at_get_vertex_attribute,&__get_vertex_attribute,0,true)
2902 
2903 /* USAGE:   get_edge_attribute(G,e,tag1,tag2,...)
2904  *
2905  * Get the attributes tag1, tag2, ... assigned to edge e in graph G, i.e.
2906  * return the sequence of values corresponding to the given tags, which may
2907  * also be specified in a list.
2908  */
_get_edge_attribute(const gen & g,GIAC_CONTEXT)2909 gen _get_edge_attribute(const gen &g,GIAC_CONTEXT) {
2910     if (g.type==_STRNG && g.subtype==-1) return g;
2911     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2912         return gentypeerr(contextptr);
2913     vecteur &gv=*g._VECTptr,tags,values;
2914     if (gv.size()<2)
2915         return gensizeerr(contextptr);
2916     graphe G(contextptr);
2917     if (!G.read_gen(gv.front()))
2918         return gt_err(_GT_ERR_NOT_A_GRAPH);
2919     if (gv[1].type!=_VECT || gv[1]._VECTptr->size()!=2)
2920         return gt_err(_GT_ERR_INVALID_EDGE);
2921     int i,j,key;
2922     if ((i=G.node_index(gv[1]._VECTptr->front()))<0 ||
2923             (j=G.node_index(gv[1]._VECTptr->back()))<0)
2924         return gt_err(_GT_ERR_EDGE_NOT_FOUND);
2925     if (!G.has_edge(i,j))
2926         return gt_err(_GT_ERR_EDGE_NOT_FOUND);
2927     bool istagvec=gv.size()==3 && gv.back().type==_VECT;
2928     if (istagvec)
2929         tags=*gv.back()._VECTptr;
2930     else tags=vecteur(gv.begin()+2,gv.end());
2931     if (tags.empty()) {
2932         const graphe::attrib &attr=G.edge_attributes(i,j);
2933         for (graphe::attrib_iter it=attr.begin();it!=attr.end();++it) {
2934             values.push_back(symbolic(at_equal,makesequence(graphe::str2gen(G.index2tag(it->first),true),it->second)));
2935         }
2936     } else {
2937         gen value;
2938         for (const_iterateur it=tags.begin();it!=tags.end();++it) {
2939             if (it->type!=_STRNG)
2940                 return gentypeerr(contextptr);
2941             key=G.tag2index(graphe::genstring2str(*it));
2942             G.get_edge_attribute(i,j,key,value);
2943             values.push_back(value);
2944         }
2945     }
2946     return istagvec?values:change_subtype(values,_SEQ__VECT);
2947 }
2948 static const char _get_edge_attribute_s[]="get_edge_attribute";
2949 static define_unary_function_eval(__get_edge_attribute,&_get_edge_attribute,_get_edge_attribute_s);
2950 define_unary_function_ptr5(at_get_edge_attribute,alias_at_get_edge_attribute,&__get_edge_attribute,0,true)
2951 
2952 /* USAGE:   discard_graph_attribute(G,tag1,tag2,...)
2953  *
2954  * Discards the graph attributes with tags tag1, tag2, ..., which may also be
2955  * specified in a list, and returns the modified copy of G.
2956  */
_discard_graph_attribute(const gen & g,GIAC_CONTEXT)2957 gen _discard_graph_attribute(const gen &g,GIAC_CONTEXT) {
2958     if (g.type==_STRNG && g.subtype==-1) return g;
2959     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2960         return gentypeerr(contextptr);
2961     vecteur &gv=*g._VECTptr,tags;
2962     graphe G(contextptr);
2963     if (!G.read_gen(gv.front()))
2964         return gt_err(_GT_ERR_NOT_A_GRAPH);
2965     if (gv.size()==2 && gv.back().type==_VECT)
2966         tags=*gv.back()._VECTptr;
2967     else tags=vecteur(gv.begin()+1,gv.end());
2968     int key;
2969     for (const_iterateur it=tags.begin();it!=tags.end();++it) {
2970         if (it->type!=_STRNG)
2971             return gentypeerr(contextptr);
2972         key=G.tag2index(graphe::genstring2str(*it));
2973         G.discard_graph_attribute(key);
2974     }
2975     return G.to_gen();
2976 }
2977 static const char _discard_graph_attribute_s[]="discard_graph_attribute";
2978 static define_unary_function_eval(__discard_graph_attribute,&_discard_graph_attribute,_discard_graph_attribute_s);
2979 define_unary_function_ptr5(at_discard_graph_attribute,alias_at_discard_graph_attribute,&__discard_graph_attribute,0,true)
2980 
2981 /* USAGE:   discard_vertex_attribute(G,v,tag1,tag2,...)
2982  *
2983  * Discards the attributes with tags tag1, tag2, ... assigned to vertex v in
2984  * graph G, which may also be specified in a list, and returns the modified
2985  * copy of G.
2986  */
_discard_vertex_attribute(const gen & g,GIAC_CONTEXT)2987 gen _discard_vertex_attribute(const gen &g,GIAC_CONTEXT) {
2988     if (g.type==_STRNG && g.subtype==-1) return g;
2989     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
2990         return gentypeerr(contextptr);
2991     vecteur &gv=*g._VECTptr,tags;
2992     if (gv.size()<2)
2993         return gensizeerr(contextptr);
2994     graphe G(contextptr);
2995     if (!G.read_gen(gv.front()))
2996         return gt_err(_GT_ERR_NOT_A_GRAPH);
2997     int v,key;
2998     if ((v=G.node_index(gv[1]))<0)
2999         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
3000     if (gv.size()==3 && gv.back().type==_VECT)
3001         tags=*gv.back()._VECTptr;
3002     else tags=vecteur(gv.begin()+2,gv.end());
3003     for (const_iterateur it=tags.begin();it!=tags.end();++it) {
3004         if (it->type!=_STRNG)
3005             return gentypeerr(contextptr);
3006         key=G.tag2index(graphe::genstring2str(*it));
3007         G.discard_node_attribute(v,key);
3008     }
3009     return G.to_gen();
3010 }
3011 static const char _discard_vertex_attribute_s[]="discard_vertex_attribute";
3012 static define_unary_function_eval(__discard_vertex_attribute,&_discard_vertex_attribute,_discard_vertex_attribute_s);
3013 define_unary_function_ptr5(at_discard_vertex_attribute,alias_at_discard_vertex_attribute,&__discard_vertex_attribute,0,true)
3014 
3015 /* USAGE:   discard_edge_attribute(G,e,tag1,tag2,...)
3016  *
3017  * Discards the attributes with tags tag1, tag2, ... assigned to edge e in
3018  * graph G, which may also be specified in a list, and returns the modified
3019  * copy of G.
3020  */
_discard_edge_attribute(const gen & g,GIAC_CONTEXT)3021 gen _discard_edge_attribute(const gen &g,GIAC_CONTEXT) {
3022     if (g.type==_STRNG && g.subtype==-1) return g;
3023     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
3024         return gentypeerr(contextptr);
3025     vecteur &gv=*g._VECTptr,tags;
3026     if (gv.size()<2)
3027         return gensizeerr(contextptr);
3028     graphe G(contextptr);
3029     if (!G.read_gen(gv.front()))
3030         return gt_err(_GT_ERR_NOT_A_GRAPH);
3031     if (gv[1].type!=_VECT || gv[1]._VECTptr->size()!=2)
3032         return gt_err(_GT_ERR_INVALID_EDGE);
3033     int i,j,key;
3034     if ((i=G.node_index(gv[1]._VECTptr->front()))<0 ||
3035             (j=G.node_index(gv[1]._VECTptr->back()))<0)
3036         return gt_err(_GT_ERR_EDGE_NOT_FOUND);
3037     if (!G.has_edge(i,j))
3038         return gt_err(_GT_ERR_EDGE_NOT_FOUND);
3039     if (gv.size()==3 && gv.back().type==_VECT)
3040         tags=*gv.back()._VECTptr;
3041     else tags=vecteur(gv.begin()+2,gv.end());
3042     for (const_iterateur it=tags.begin();it!=tags.end();++it) {
3043         if (it->type!=_STRNG)
3044             return gentypeerr(contextptr);
3045         key=G.tag2index(graphe::genstring2str(*it));
3046         G.discard_edge_attribute(i,j,key);
3047     }
3048     return G.to_gen();
3049 }
3050 static const char _discard_edge_attribute_s[]="discard_edge_attribute";
3051 static define_unary_function_eval(__discard_edge_attribute,&_discard_edge_attribute,_discard_edge_attribute_s);
3052 define_unary_function_ptr5(at_discard_edge_attribute,alias_at_discard_edge_attribute,&__discard_edge_attribute,0,true)
3053 
3054 /* USAGE:   list_graph_attributes(G)
3055  *
3056  * Returns the list of graph attributes in form tag=value.
3057  */
_list_graph_attributes(const gen & g,GIAC_CONTEXT)3058 gen _list_graph_attributes(const gen &g,GIAC_CONTEXT) {
3059     if (g.type==_STRNG && g.subtype==-1) return g;
3060     graphe G(contextptr);
3061     if (!G.read_gen(g))
3062         return gt_err(_GT_ERR_NOT_A_GRAPH);
3063     vecteur tags,values;
3064     G.attrib2vecteurs(G.graph_attributes(),tags,values);
3065     return _zip(makesequence(at_equal,tags,values),contextptr);
3066 }
3067 static const char _list_graph_attributes_s[]="list_graph_attributes";
3068 static define_unary_function_eval(__list_graph_attributes,&_list_graph_attributes,_list_graph_attributes_s);
3069 define_unary_function_ptr5(at_list_graph_attributes,alias_at_list_graph_attributes,&__list_graph_attributes,0,true)
3070 
3071 /* USAGE:   list_vertex_attributes(G,v)
3072  *
3073  * Returns the list of attributes assigned to vertex v in form tag=value.
3074  */
_list_vertex_attributes(const gen & g,GIAC_CONTEXT)3075 gen _list_vertex_attributes(const gen &g,GIAC_CONTEXT) {
3076     if (g.type==_STRNG && g.subtype==-1) return g;
3077     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
3078         return gentypeerr(contextptr);
3079     vecteur &gv=*g._VECTptr;
3080     if (gv.size()!=2)
3081         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
3082     graphe G(contextptr);
3083     if (!G.read_gen(gv.front()))
3084         return gt_err(_GT_ERR_NOT_A_GRAPH);
3085     int i;
3086     if ((i=G.node_index(gv.back()))<0)
3087         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
3088     vecteur tags,values;
3089     G.attrib2vecteurs(G.node_attributes(i),tags,values);
3090     return _zip(makesequence(at_equal,tags,values),contextptr);
3091 }
3092 static const char _list_vertex_attributes_s[]="list_vertex_attributes";
3093 static define_unary_function_eval(__list_vertex_attributes,&_list_vertex_attributes,_list_vertex_attributes_s);
3094 define_unary_function_ptr5(at_list_vertex_attributes,alias_at_list_vertex_attributes,&__list_vertex_attributes,0,true)
3095 
3096 /* USAGE:   list_edge_attributes(G,e)
3097  *
3098  * Returns the list of attributes assigned to edge e in form tag=value.
3099  */
_list_edge_attributes(const gen & g,GIAC_CONTEXT)3100 gen _list_edge_attributes(const gen &g,GIAC_CONTEXT) {
3101     if (g.type==_STRNG && g.subtype==-1) return g;
3102     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
3103         return gentypeerr(contextptr);
3104     vecteur &gv=*g._VECTptr;
3105     if (gv.size()!=2)
3106         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
3107     graphe G(contextptr);
3108     if (!G.read_gen(gv.front()))
3109         return gt_err(_GT_ERR_NOT_A_GRAPH);
3110     if (gv.back().type!=_VECT || gv.back()._VECTptr->size()!=2)
3111         return gt_err(_GT_ERR_INVALID_EDGE);
3112     int i,j;
3113     if ((i=G.node_index(gv.back()._VECTptr->front()))<0 ||
3114             (j=G.node_index(gv.back()._VECTptr->back()))<0)
3115         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
3116     if (!G.has_edge(i,j))
3117         return gt_err(_GT_ERR_EDGE_NOT_FOUND);
3118     vecteur tags,values;
3119     G.attrib2vecteurs(G.edge_attributes(i,j),tags,values);
3120     return _zip(makesequence(at_equal,tags,values),contextptr);
3121 }
3122 static const char _list_edge_attributes_s[]="list_edge_attributes";
3123 static define_unary_function_eval(__list_edge_attributes,&_list_edge_attributes,_list_edge_attributes_s);
3124 define_unary_function_ptr5(at_list_edge_attributes,alias_at_list_edge_attributes,&__list_edge_attributes,0,true)
3125 
3126 /* USAGE:   number_of_edges(G)
3127  *
3128  * Returns the number of edges/arcs of graph G.
3129  */
_number_of_edges(const gen & g,GIAC_CONTEXT)3130 gen _number_of_edges(const gen &g,GIAC_CONTEXT) {
3131     if (g.type==_STRNG && g.subtype==-1) return g;
3132     graphe G(contextptr,false);
3133     if (!G.read_gen(g))
3134         return gt_err(_GT_ERR_NOT_A_GRAPH);
3135     return G.edge_count();
3136 }
3137 static const char _number_of_edges_s[]="number_of_edges";
3138 static define_unary_function_eval(__number_of_edges,&_number_of_edges,_number_of_edges_s);
3139 define_unary_function_ptr5(at_number_of_edges,alias_at_number_of_edges,&__number_of_edges,0,true)
3140 
3141 /* USAGE:   number_of_vertices(G)
3142  *
3143  * Returns the number of vertices of graph G.
3144  */
_number_of_vertices(const gen & g,GIAC_CONTEXT)3145 gen _number_of_vertices(const gen &g,GIAC_CONTEXT) {
3146     if (g.type==_STRNG && g.subtype==-1) return g;
3147     graphe G(contextptr,false);
3148     if (!G.read_gen(g))
3149         return gt_err(_GT_ERR_NOT_A_GRAPH);
3150     return G.node_count();
3151 }
3152 static const char _number_of_vertices_s[]="number_of_vertices";
3153 static define_unary_function_eval(__number_of_vertices,&_number_of_vertices,_number_of_vertices_s);
3154 define_unary_function_ptr5(at_number_of_vertices,alias_at_number_of_vertices,&__number_of_vertices,0,true)
3155 
3156 /* USAGE:   get_edge_weight(G,e)
3157  *
3158  * Returns the weight of the edge e in graph G (which must be weighted).
3159  */
_get_edge_weight(const gen & g,GIAC_CONTEXT)3160 gen _get_edge_weight(const gen &g,GIAC_CONTEXT) {
3161     if (g.type==_STRNG && g.subtype==-1) return g;
3162     if (g.type!=_VECT || g.subtype!=_SEQ__VECT || g._VECTptr->size()!=2)
3163         return gentypeerr(contextptr);
3164     graphe G(contextptr);
3165     if (!G.read_gen(g._VECTptr->front()))
3166         return gt_err(_GT_ERR_NOT_A_GRAPH);
3167     if (!G.is_weighted())
3168         return gt_err(_GT_ERR_WEIGHTED_GRAPH_REQUIRED);
3169     gen &E=g._VECTptr->back();
3170     if (E.type!=_VECT || E._VECTptr->size()!=2)
3171         return gt_err(_GT_ERR_INVALID_EDGE);
3172     int i=G.node_index(E._VECTptr->front()),j=G.node_index(E._VECTptr->back());
3173     if (i==-1 || j==-1)
3174         return gt_err(_GT_ERR_EDGE_NOT_FOUND);
3175     return G.weight(i,j);
3176 }
3177 static const char _get_edge_weight_s[]="get_edge_weight";
3178 static define_unary_function_eval(__get_edge_weight,&_get_edge_weight,_get_edge_weight_s);
3179 define_unary_function_ptr5(at_get_edge_weight,alias_at_get_edge_weight,&__get_edge_weight,0,true)
3180 
3181 /* USAGE:   set_edge_weight(G,e,w)
3182  *
3183  * Sets weight of the edge e in graph G (which must be weighted) to w and
3184  * returns the modified copy of G.
3185  */
_set_edge_weight(const gen & g,GIAC_CONTEXT)3186 gen _set_edge_weight(const gen &g,GIAC_CONTEXT) {
3187     if (g.type==_STRNG && g.subtype==-1) return g;
3188     if (g.type!=_VECT || g.subtype!=_SEQ__VECT || g._VECTptr->size()!=3)
3189         return gentypeerr(contextptr);
3190     graphe G(contextptr);
3191     if (!G.read_gen(g._VECTptr->front()))
3192         return gt_err(_GT_ERR_NOT_A_GRAPH);
3193     if (!G.is_weighted())
3194         return gt_err(_GT_ERR_WEIGHTED_GRAPH_REQUIRED);
3195     gen &E=g._VECTptr->at(1);
3196     if (E.type!=_VECT || E._VECTptr->size()!=2)
3197         return gt_err(_GT_ERR_INVALID_EDGE);
3198     int i=G.node_index(E._VECTptr->front()),j=G.node_index(E._VECTptr->back());
3199     if (i==-1 || j==-1)
3200         return gt_err(_GT_ERR_EDGE_NOT_FOUND);
3201     G.set_edge_attribute(i,j,_GT_ATTRIB_WEIGHT,g._VECTptr->back());
3202     return G.to_gen();
3203 }
3204 static const char _set_edge_weight_s[]="set_edge_weight";
3205 static define_unary_function_eval(__set_edge_weight,&_set_edge_weight,_set_edge_weight_s);
3206 define_unary_function_ptr5(at_set_edge_weight,alias_at_set_edge_weight,&__set_edge_weight,0,true)
3207 
3208 /* USAGE:   is_directed(G)
3209  *
3210  * Returns true iff G is a directed graph.
3211  */
_is_directed(const gen & g,GIAC_CONTEXT)3212 gen _is_directed(const gen &g,GIAC_CONTEXT) {
3213     if (g.type==_STRNG && g.subtype==-1) return g;
3214     graphe G(contextptr);
3215     if (!G.read_gen(g))
3216         return gt_err(_GT_ERR_NOT_A_GRAPH);
3217     return graphe::boole(G.is_directed());
3218 }
3219 static const char _is_directed_s[]="is_directed";
3220 static define_unary_function_eval(__is_directed,&_is_directed,_is_directed_s);
3221 define_unary_function_ptr5(at_is_directed,alias_at_is_directed,&__is_directed,0,true)
3222 
3223 /* USAGE:   neighbors(G,[v])
3224  *
3225  * Returns the list of vertices adjacent to v. If v is omitted, a list of
3226  * adjacency lists of all vertices in G is returned.
3227  */
_neighbors(const gen & g,GIAC_CONTEXT)3228 gen _neighbors(const gen &g,GIAC_CONTEXT) {
3229     if (g.type==_STRNG && g.subtype==-1) return g;
3230     if (g.type!=_VECT)
3231         return gentypeerr(contextptr);
3232     if (g.subtype==_SEQ__VECT && g._VECTptr->size()!=2) {
3233         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
3234     }
3235     graphe G(contextptr);
3236     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
3237         return gt_err(_GT_ERR_NOT_A_GRAPH);
3238     if (g.subtype==_SEQ__VECT) {
3239         gen &v=g._VECTptr->back();
3240         int i=G.node_index(v);
3241         if (i==-1)
3242             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
3243         graphe::ivector adj;
3244         G.adjacent_nodes(i,adj);
3245         return G.get_node_labels(adj);
3246     } else {
3247         vecteur res;
3248         int n=G.node_count();
3249         graphe::ivector adj;
3250         for (int i=0;i<n;++i) {
3251             G.adjacent_nodes(i,adj,false);
3252             res.push_back(_sort(G.get_node_labels(adj),contextptr));
3253         }
3254         return change_subtype(res,_LIST__VECT);
3255     }
3256 }
3257 static const char _neighbors_s[]="neighbors";
3258 static define_unary_function_eval(__neighbors,&_neighbors,_neighbors_s);
3259 define_unary_function_ptr5(at_neighbors,alias_at_neighbors,&__neighbors,0,true)
3260 
3261 /* USAGE:   minimum_degree(G)
3262  *
3263  * Returns the smallest degree among vertices in an undirected graph G.
3264  */
_minimum_degree(const gen & g,GIAC_CONTEXT)3265 gen _minimum_degree(const gen &g,GIAC_CONTEXT) {
3266     if (g.type==_STRNG && g.subtype==-1) return g;
3267     graphe G(contextptr,false);
3268     if (!G.read_gen(g))
3269         return gt_err(_GT_ERR_NOT_A_GRAPH);
3270     if (G.is_null())
3271         return gt_err(_GT_ERR_GRAPH_IS_NULL);
3272     if (G.is_directed())
3273         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
3274     return G.minimum_degree();
3275 }
3276 static const char _minimum_degree_s[]="minimum_degree";
3277 static define_unary_function_eval(__minimum_degree,&_minimum_degree,_minimum_degree_s);
3278 define_unary_function_ptr5(at_minimum_degree,alias_at_minimum_degree,&__minimum_degree,0,true)
3279 
3280 /* USAGE:   maximum_degree(G)
3281  *
3282  * Returns the largest degree among vertices in an undirected graph G.
3283  */
_maximum_degree(const gen & g,GIAC_CONTEXT)3284 gen _maximum_degree(const gen &g,GIAC_CONTEXT) {
3285     if (g.type==_STRNG && g.subtype==-1) return g;
3286     graphe G(contextptr,false);
3287     if (!G.read_gen(g))
3288         return gt_err(_GT_ERR_NOT_A_GRAPH);
3289     if (G.is_null())
3290         return gt_err(_GT_ERR_GRAPH_IS_NULL);
3291     if (G.is_directed())
3292         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
3293     return G.maximum_degree();
3294 }
3295 static const char _maximum_degree_s[]="maximum_degree";
3296 static define_unary_function_eval(__maximum_degree,&_maximum_degree,_maximum_degree_s);
3297 define_unary_function_ptr5(at_maximum_degree,alias_at_maximum_degree,&__maximum_degree,0,true)
3298 
3299 /* USAGE:   is_regular(G,[d])
3300  *
3301  * Returns true iff max and min degrees of graph G are equal [and stores that
3302  * number in d].
3303  */
_is_regular(const gen & g,GIAC_CONTEXT)3304 gen _is_regular(const gen &g,GIAC_CONTEXT) {
3305     if (g.type==_STRNG && g.subtype==-1) return g;
3306     if (g.type!=_VECT)
3307         return gentypeerr(contextptr);
3308     int d=-1,dd;
3309     gen arg(undef);
3310     if (g.subtype==_SEQ__VECT) {
3311         if (g._VECTptr->size()!=2)
3312             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
3313         arg=g._VECTptr->at(1);
3314     }
3315     graphe G(contextptr,false);
3316     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
3317         return gt_err(_GT_ERR_NOT_A_GRAPH);
3318     if (G.is_null())
3319         return gt_err(_GT_ERR_GRAPH_IS_NULL);
3320     if (!is_undef(arg)) {
3321         if (arg.is_integer()) {
3322             if ((d=arg.val)<0)
3323                 return generr("Expected a nonnegative integer");
3324         } else if (arg.type!=_IDNT)
3325             return gentypeerr(contextptr);
3326     }
3327     dd=G.is_regular(d);
3328     if (dd>=0 && !is_undef(arg) && arg.type==_IDNT)
3329         identifier_assign(*arg._IDNTptr,gen(dd),contextptr);
3330     return graphe::boole(dd>=0);
3331 }
3332 static const char _is_regular_s[]="is_regular";
3333 static define_unary_function_eval(__is_regular,&_is_regular,_is_regular_s);
3334 define_unary_function_ptr5(at_is_regular,alias_at_is_regular,&__is_regular,0,true)
3335 
3336 /* USAGE:   is_strongly_regular(G,[srg])
3337  *
3338  * Returns true iff the graph G is strongly regular and optionally outputs
3339  * srg=[k,l,m] where k is the vertex degree and l resp. m is the number of
3340  * common neighbors for adjacent resp. non-adjacent vertices.
3341  */
_is_strongly_regular(const gen & g,GIAC_CONTEXT)3342 gen _is_strongly_regular(const gen &g,GIAC_CONTEXT) {
3343     if (g.type==_STRNG && g.subtype==-1) return g;
3344     if (g.type!=_VECT)
3345         return gentypeerr(contextptr);
3346     gen srg=undef;
3347     if (g.subtype==_SEQ__VECT) {
3348         if (g._VECTptr->size()!=2)
3349             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
3350         if ((srg=g._VECTptr->back()).type!=_IDNT)
3351             return generr("Expected an identifier");
3352     }
3353     graphe G(contextptr,false);
3354     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
3355         return gt_err(_GT_ERR_NOT_A_GRAPH);
3356     if (G.is_null())
3357         return gt_err(_GT_ERR_GRAPH_IS_NULL);
3358     graphe::ipair sig;
3359     bool res=G.is_strongly_regular(sig);
3360     if (!res)
3361         return graphe::FAUX;
3362     if (!is_undef(srg))
3363         identifier_assign(*srg._IDNTptr,makevecteur(G.degree(0),sig.first,sig.second),contextptr);
3364     return graphe::VRAI;
3365 }
3366 static const char _is_strongly_regular_s[]="is_strongly_regular";
3367 static define_unary_function_eval(__is_strongly_regular,&_is_strongly_regular,_is_strongly_regular_s);
3368 define_unary_function_ptr5(at_is_strongly_regular,alias_at_is_strongly_regular,&__is_strongly_regular,0,true)
3369 
3370 /* USAGE:   isomorphic_copy(G,[sigma])
3371  *
3372  * Returns a new graph H with neigbors data reordered according to a [random]
3373  * permutation sigma.
3374  */
_isomorphic_copy(const gen & g,GIAC_CONTEXT)3375 gen _isomorphic_copy(const gen &g,GIAC_CONTEXT) {
3376     if (g.type==_STRNG && g.subtype==-1) return g;
3377     if (g.type!=_VECT)
3378         return gentypeerr(contextptr);
3379     vecteur sigma(0);
3380     if (g.subtype==_SEQ__VECT) {
3381         vecteur &gv=*g._VECTptr;
3382         if (gv.size()<2)
3383             return gensizeerr(contextptr);
3384         if (is_zero(_is_permu(gv[1],contextptr)))
3385             return generrtype("Expected a permutation");
3386         sigma=*gv[1]._VECTptr;
3387     }
3388     graphe G(contextptr);
3389     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
3390         return gt_err(_GT_ERR_NOT_A_GRAPH);
3391     if (sigma.empty())
3392         sigma=*_randperm(G.node_count(),contextptr)._VECTptr;
3393     if (int(sigma.size())!=G.node_count())
3394         return generr("Invalid permutation size");
3395     graphe::ivector v(sigma.size());
3396     int ofs=array_start(contextptr);
3397     for (const_iterateur it=sigma.begin();it!=sigma.end();++it) {
3398         v[it-sigma.begin()]=it->val-ofs;
3399     }
3400     graphe H(contextptr);
3401     if (!G.isomorphic_copy(H,v,true))
3402         return generrtype("Failed to create isomorphic copy");
3403     H.relabel_nodes(G.vertices());
3404     return H.to_gen();
3405 }
3406 static const char _isomorphic_copy_s[]="isomorphic_copy";
3407 static define_unary_function_eval(__isomorphic_copy,&_isomorphic_copy,_isomorphic_copy_s);
3408 define_unary_function_ptr5(at_isomorphic_copy,alias_at_isomorphic_copy,&__isomorphic_copy,0,true)
3409 
3410 /* USAGE:   permute_vertices(G,[V])
3411  *
3412  * Returns a copy of graph G with vertices reordered as given in V [or
3413  * randomly].
3414  */
_permute_vertices(const gen & g,GIAC_CONTEXT)3415 gen _permute_vertices(const gen &g,GIAC_CONTEXT) {
3416     if (g.type==_STRNG && g.subtype==-1) return g;
3417     if (g.type!=_VECT)
3418         return gentypeerr(contextptr);
3419     vecteur sigma(0);
3420     if (g.subtype==_SEQ__VECT) {
3421         vecteur &gv=*g._VECTptr;
3422         if (gv.size()<2)
3423             return gensizeerr(contextptr);
3424         if (gv[1].type!=_VECT)
3425             return generrtype("Expected a list of vertices");
3426         sigma=*gv[1]._VECTptr;
3427     }
3428     graphe G(contextptr);
3429     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
3430         return gt_err(_GT_ERR_NOT_A_GRAPH);
3431     vecteur V=G.vertices();
3432     if (sigma.empty())
3433         sigma=*_eval(symbolic(at_shuffle,V),contextptr)._VECTptr;
3434     if (sigma.size()!=V.size())
3435         return generr("Invalid permutation size");
3436     graphe::ivector v(sigma.size(),-1);
3437     const_iterateur jt;
3438     int i;
3439     for (const_iterateur it=sigma.begin();it!=sigma.end();++it) {
3440         if ((jt=find(V.begin(),V.end(),*it))==V.end())
3441             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
3442         i=jt-V.begin();
3443         if (find(v.begin(),v.end(),i)!=v.end())
3444             return generrtype("Expected a permutation");
3445         v[it-sigma.begin()]=i;
3446     }
3447     graphe H(contextptr);
3448     if (!G.isomorphic_copy(H,v))
3449         return gentypeerr(contextptr);
3450     return H.to_gen();
3451 }
3452 static const char _permute_vertices_s[]="permute_vertices";
3453 static define_unary_function_eval(__permute_vertices,&_permute_vertices,_permute_vertices_s);
3454 define_unary_function_ptr5(at_permute_vertices,alias_at_permute_vertices,&__permute_vertices,0,true)
3455 
3456 /* USAGE:   relabel_vertices(G,V)
3457  *
3458  * Returns a new graph H with vertex labels changed to those in V.
3459  */
_relabel_vertices(const gen & g,GIAC_CONTEXT)3460 gen _relabel_vertices(const gen &g,GIAC_CONTEXT) {
3461     if (g.type==_STRNG && g.subtype==-1) return g;
3462     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
3463         return gentypeerr(contextptr);
3464     vecteur &gv=*g._VECTptr;
3465     if (gv.size()<2)
3466         return gensizeerr(contextptr);
3467     if (gv[1].type!=_VECT)
3468         return generrtype("Expected a list of labels");
3469     graphe G(contextptr);
3470     if (!G.read_gen(gv.front()))
3471         return gt_err(_GT_ERR_NOT_A_GRAPH);
3472     vecteur &labels=*gv[1]._VECTptr;
3473     if (int(labels.size())!=G.node_count())
3474         return generr("Invalid number of labels");
3475     if (!G.relabel_nodes(labels))
3476         return generrtype("Failed to relabel vertices");
3477     return G.to_gen();
3478 }
3479 static const char _relabel_vertices_s[]="relabel_vertices";
3480 static define_unary_function_eval(__relabel_vertices,&_relabel_vertices,_relabel_vertices_s);
3481 define_unary_function_ptr5(at_relabel_vertices,alias_at_relabel_vertices,&__relabel_vertices,0,true)
3482 
3483 /* USAGE:   is_tree(G)
3484  *
3485  * Returns true iff the graph G is a tree, i.e. an undirected connected graph with
3486  * exactly n-1 edges, where n is the number of nodes of G.
3487  */
_is_tree(const gen & g,GIAC_CONTEXT)3488 gen _is_tree(const gen &g,GIAC_CONTEXT) {
3489     if (g.type==_STRNG && g.subtype==-1) return g;
3490     graphe G(contextptr,false);
3491     if (!G.read_gen(g))
3492         return gt_err(_GT_ERR_NOT_A_GRAPH);
3493     if (G.is_directed())
3494         return graphe::FAUX;
3495     return graphe::boole(G.is_tree());
3496 }
3497 static const char _is_tree_s[]="is_tree";
3498 static define_unary_function_eval(__is_tree,&_is_tree,_is_tree_s);
3499 define_unary_function_ptr5(at_is_tree,alias_at_is_tree,&__is_tree,0,true)
3500 
3501 /* USAGE:   is_forest(G)
3502  *
3503  * Returns true iff the graph G is a forest, i.e. an undirected graph whose
3504  * connected components are all trees.
3505  */
_is_forest(const gen & g,GIAC_CONTEXT)3506 gen _is_forest(const gen &g,GIAC_CONTEXT) {
3507     if (g.type==_STRNG && g.subtype==-1) return g;
3508     graphe G(contextptr,false);
3509     if (!G.read_gen(g))
3510         return gt_err(_GT_ERR_NOT_A_GRAPH);
3511     if (G.is_directed())
3512         return graphe::FAUX;
3513     return graphe::boole(G.is_forest());
3514 }
3515 static const char _is_forest_s[]="is_forest";
3516 static define_unary_function_eval(__is_forest,&_is_forest,_is_forest_s);
3517 define_unary_function_ptr5(at_is_forest,alias_at_is_forest,&__is_forest,0,true)
3518 
3519 /* USAGE:   is_tournament(G)
3520  *
3521  * Returns true iff the graph G is a tournament, i.e. a complete graph with a
3522  * direction for each edge.
3523  */
_is_tournament(const gen & g,GIAC_CONTEXT)3524 gen _is_tournament(const gen &g,GIAC_CONTEXT) {
3525     if (g.type==_STRNG && g.subtype==-1) return g;
3526     graphe G(contextptr,false);
3527     if (!G.read_gen(g))
3528         return gt_err(_GT_ERR_NOT_A_GRAPH);
3529     return graphe::boole(G.is_tournament());
3530 }
3531 static const char _is_tournament_s[]="is_tournament";
3532 static define_unary_function_eval(__is_tournament,&_is_tournament,_is_tournament_s);
3533 define_unary_function_ptr5(at_is_tournament,alias_at_is_tournament,&__is_tournament,0,true)
3534 
3535 /* USAGE:   tree_height(T,r)
3536  *
3537  * Returns the height of the tree T with r as the root node.
3538  */
_tree_height(const gen & g,GIAC_CONTEXT)3539 gen _tree_height(const gen &g,GIAC_CONTEXT) {
3540     if (g.type==_STRNG && g.subtype==-1) return g;
3541     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
3542         return gentypeerr(contextptr);
3543     vecteur &gv=*g._VECTptr;
3544     if (gv.size()!=2)
3545         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
3546     graphe G(contextptr);
3547     if (!G.read_gen(gv.front()))
3548         return gt_err(_GT_ERR_NOT_A_GRAPH);
3549     if (G.node_count()==1)
3550         return 0;
3551     if (!G.is_tree())
3552         return gt_err(_GT_ERR_NOT_A_TREE);
3553     int root;
3554     if ((root=G.node_index(gv.back()))==-1)
3555         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
3556     return G.tree_height(root);
3557 }
3558 static const char _tree_height_s[]="tree_height";
3559 static define_unary_function_eval(__tree_height,&_tree_height,_tree_height_s);
3560 define_unary_function_ptr5(at_tree_height,alias_at_tree_height,&__tree_height,0,true)
3561 
3562 /* USAGE:   number_of_triangles(G,[L])
3563  *
3564  * Returns the number of (directed) cycles of length 3 in a (di)graph G and
3565  * optionally lists the triangles in L.
3566  */
_number_of_triangles(const gen & g,GIAC_CONTEXT)3567 gen _number_of_triangles(const gen &g,GIAC_CONTEXT) {
3568     if (g.type==_STRNG && g.subtype==-1) return g;
3569     if (g.type!=_VECT)
3570         return gentypeerr(contextptr);
3571     gen dest(undef);
3572     if (g.subtype==_SEQ__VECT) {
3573         if (g._VECTptr->size()!=2)
3574             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
3575         dest=g._VECTptr->back();
3576         if (dest.type!=_IDNT)
3577             return generr("Expected an unassigned identifier");
3578     }
3579     graphe G(contextptr,!is_undef(dest));
3580     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
3581         return gt_err(_GT_ERR_NOT_A_GRAPH);
3582     if (!is_undef(dest) && G.is_directed())
3583         return generr("Undirected graph is required for triangle listing");
3584     graphe::ivectors lst;
3585     gen cnt=G.triangle_count(is_undef(dest)?NULL:&lst);
3586     if (!is_undef(dest)) {
3587         vecteur triangles;
3588         for (graphe::ivectors_iter it=lst.begin();it!=lst.end();++it) {
3589             triangles.push_back(_sort(G.get_node_labels(*it),contextptr));
3590             identifier_assign(*dest._IDNTptr,triangles,contextptr);
3591         }
3592     }
3593     return cnt;
3594 }
3595 static const char _number_of_triangles_s[]="number_of_triangles";
3596 static define_unary_function_eval(__number_of_triangles,&_number_of_triangles,_number_of_triangles_s);
3597 define_unary_function_ptr5(at_number_of_triangles,alias_at_number_of_triangles,&__number_of_triangles,0,true)
3598 
3599 /* USAGE:   is_connected(G)
3600  *
3601  * Returns true iff graph G is connected.
3602  */
_is_connected(const gen & g,GIAC_CONTEXT)3603 gen _is_connected(const gen &g,GIAC_CONTEXT) {
3604     if (g.type==_STRNG && g.subtype==-1) return g;
3605     graphe G(contextptr,false);
3606     if (!G.read_gen(g))
3607         return gt_err(_GT_ERR_NOT_A_GRAPH);
3608     return graphe::boole(G.is_connected());
3609 }
3610 static const char _is_connected_s[]="is_connected";
3611 static define_unary_function_eval(__is_connected,&_is_connected,_is_connected_s);
3612 define_unary_function_ptr5(at_is_connected,alias_at_is_connected,&__is_connected,0,true)
3613 
3614 /* USAGE:   is_biconnected(G)
3615  *
3616  * Returns true iff graph G is biconnected. Running time is O(n+m).
3617  */
_is_biconnected(const gen & g,GIAC_CONTEXT)3618 gen _is_biconnected(const gen &g,GIAC_CONTEXT) {
3619     if (g.type==_STRNG && g.subtype==-1) return g;
3620     graphe G(contextptr,false);
3621     if (!G.read_gen(g))
3622         return gt_err(_GT_ERR_NOT_A_GRAPH);
3623     return graphe::boole(G.is_biconnected());
3624 }
3625 static const char _is_biconnected_s[]="is_biconnected";
3626 static define_unary_function_eval(__is_biconnected,&_is_biconnected,_is_biconnected_s);
3627 define_unary_function_ptr5(at_is_biconnected,alias_at_is_biconnected,&__is_biconnected,0,true)
3628 
3629 /* USAGE:   is_triconnected(G)
3630  *
3631  * Returns true iff graph G is triconnected. Running time is O(n*(n+m)).
3632  */
_is_triconnected(const gen & g,GIAC_CONTEXT)3633 gen _is_triconnected(const gen &g,GIAC_CONTEXT) {
3634     if (g.type==_STRNG && g.subtype==-1) return g;
3635     graphe G(contextptr,false);
3636     if (!G.read_gen(g))
3637         return gt_err(_GT_ERR_NOT_A_GRAPH);
3638     return graphe::boole(G.is_triconnected());
3639 }
3640 static const char _is_triconnected_s[]="is_triconnected";
3641 static define_unary_function_eval(__is_triconnected,&_is_triconnected,_is_triconnected_s);
3642 define_unary_function_ptr5(at_is_triconnected,alias_at_is_triconnected,&__is_triconnected,0,true)
3643 
3644 /* USAGE:   is_weighted(G)
3645  *
3646  * Returns true iff graph G is weighted.
3647  */
_is_weighted(const gen & g,GIAC_CONTEXT)3648 gen _is_weighted(const gen &g,GIAC_CONTEXT) {
3649     if (g.type==_STRNG && g.subtype==-1) return g;
3650     graphe G(contextptr,false);
3651     if (!G.read_gen(g))
3652         return gt_err(_GT_ERR_NOT_A_GRAPH);
3653     return graphe::boole(G.is_weighted());
3654 }
3655 static const char _is_weighted_s[]="is_weighted";
3656 static define_unary_function_eval(__is_weighted,&_is_weighted,_is_weighted_s);
3657 define_unary_function_ptr5(at_is_weighted,alias_at_is_weighted,&__is_weighted,0,true)
3658 
3659 /* USAGE:   is_planar(G,[F])
3660  *
3661  * Returns true iff graph G is planar [and store the list of its faces to F if G
3662  * is biconnected].
3663  */
_is_planar(const gen & g,GIAC_CONTEXT)3664 gen _is_planar(const gen &g,GIAC_CONTEXT) {
3665     if (g.type==_STRNG && g.subtype==-1) return g;
3666     if (g.type!=_VECT)
3667         return gentypeerr(contextptr);
3668     gen F=undef;
3669     if (g.subtype==_SEQ__VECT) {
3670         if (g._VECTptr->size()!=2)
3671             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
3672         if (g._VECTptr->back().type!=_IDNT)
3673             return generrtype("Expected an identifier");
3674         F=g._VECTptr->back();
3675     }
3676     graphe G(contextptr,!is_undef(F)),U(contextptr,!is_undef(F));
3677     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
3678         return gt_err(_GT_ERR_NOT_A_GRAPH);
3679     G.underlying(U);
3680     if (!is_undef(F)) {
3681         if (!U.is_biconnected())
3682             return gt_err(_GT_ERR_BICONNECTED_GRAPH_REQUIRED);
3683         graphe::ivectors faces;
3684         if (!U.demoucron(faces))
3685             return G.boole(false);
3686         vecteur res;
3687         for (graphe::ivectors_iter it=faces.begin();it!=faces.end();++it) {
3688             res.push_back(G.get_node_labels(*it));
3689         }
3690         identifier_assign(*F._IDNTptr,res,contextptr);
3691     }
3692     return G.boole(U.is_planar());
3693 }
3694 static const char _is_planar_s[]="is_planar";
3695 static define_unary_function_eval(__is_planar,&_is_planar,_is_planar_s);
3696 define_unary_function_ptr5(at_is_planar,alias_at_is_planar,&__is_planar,0,true)
3697 
3698 /* USAGE:   complete_binary_tree(n)
3699  *
3700  * Returns the complete binary tree with depth equal to n.
3701  */
_complete_binary_tree(const gen & g,GIAC_CONTEXT)3702 gen _complete_binary_tree(const gen &g,GIAC_CONTEXT) {
3703     if (g.type==_STRNG && g.subtype==-1) return g;
3704     if (!g.is_integer() || g.val<0)
3705         return gentypeerr(contextptr);
3706     graphe G(contextptr);
3707     G.make_complete_kary_tree(2,g.val);
3708     return G.to_gen();
3709 }
3710 static const char _complete_binary_tree_s[]="complete_binary_tree";
3711 static define_unary_function_eval(__complete_binary_tree,&_complete_binary_tree,_complete_binary_tree_s);
3712 define_unary_function_ptr5(at_complete_binary_tree,alias_at_complete_binary_tree,&__complete_binary_tree,0,true)
3713 
3714 /* USAGE:   complete_kary_tree(k,n)
3715  *
3716  * Returns the complete k-ary tree with depth equal to n.
3717  */
_complete_kary_tree(const gen & g,GIAC_CONTEXT)3718 gen _complete_kary_tree(const gen &g,GIAC_CONTEXT) {
3719     if (g.type==_STRNG && g.subtype==-1) return g;
3720     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
3721         return gentypeerr(contextptr);
3722     vecteur &gv=*g._VECTptr;
3723     if (gv.size()!=2)
3724         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
3725     if (!gv.front().is_integer() || !gv.back().is_integer())
3726         return generrtype("Expected an integer");
3727     int k=gv.front().val,n=gv.back().val;
3728     if (k<2 || n<1)
3729         return gensizeerr(contextptr);
3730     graphe G(contextptr);
3731     G.make_complete_kary_tree(k,n);
3732     return G.to_gen();
3733 }
3734 static const char _complete_kary_tree_s[]="complete_kary_tree";
3735 static define_unary_function_eval(__complete_kary_tree,&_complete_kary_tree,_complete_kary_tree_s);
3736 define_unary_function_ptr5(at_complete_kary_tree,alias_at_complete_kary_tree,&__complete_kary_tree,0,true)
3737 
3738 /* USAGE:   prism_graph(n)
3739  *
3740  * Returns the prism graph of order n, i.e. the generalized Petersen graph
3741  * GP(n,1).
3742  */
_prism_graph(const gen & g,GIAC_CONTEXT)3743 gen _prism_graph(const gen &g,GIAC_CONTEXT) {
3744     if (g.type==_STRNG && g.subtype==-1) return g;
3745     if (!g.is_integer() || g.val<3)
3746         return generr("Expected an integer greater than two");
3747     return _petersen_graph(makesequence(g,1),contextptr);
3748 }
3749 static const char _prism_graph_s[]="prism_graph";
3750 static define_unary_function_eval(__prism_graph,&_prism_graph,_prism_graph_s);
3751 define_unary_function_ptr5(at_prism_graph,alias_at_prism_graph,&__prism_graph,0,true)
3752 
3753 /* USAGE:   antiprism_graph(n)
3754  *
3755  * Returns the antiprism graph of order n.
3756  */
_antiprism_graph(const gen & g,GIAC_CONTEXT)3757 gen _antiprism_graph(const gen &g,GIAC_CONTEXT) {
3758     if (g.type==_STRNG && g.subtype==-1) return g;
3759     if (!g.is_integer() || g.val<3)
3760         return generr("Expected an integer greater than two");
3761     graphe G(contextptr);
3762     graphe::layout x;
3763     G.make_antiprism_graph(g.val,&x);
3764     G.store_layout(x);
3765     return G.to_gen();
3766 }
3767 static const char _antiprism_graph_s[]="antiprism_graph";
3768 static define_unary_function_eval(__antiprism_graph,&_antiprism_graph,_antiprism_graph_s);
3769 define_unary_function_ptr5(at_antiprism_graph,alias_at_antiprism_graph,&__antiprism_graph,0,true)
3770 
3771 /* USAGE:   star_graph(n)
3772  *
3773  * Returns the star graph with n+1 vertices, i.e. the complete bipartite graph
3774  * complete_graph(1,n).
3775  */
_star_graph(const gen & g,GIAC_CONTEXT)3776 gen _star_graph(const gen &g,GIAC_CONTEXT) {
3777     if (g.type==_STRNG && g.subtype==-1) return g;
3778     if (!g.is_integer() || g.val<1)
3779         return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
3780     return _complete_graph(makesequence(1,g),contextptr);
3781 }
3782 static const char _star_graph_s[]="star_graph";
3783 static define_unary_function_eval(__star_graph,&_star_graph,_star_graph_s);
3784 define_unary_function_ptr5(at_star_graph,alias_at_star_graph,&__star_graph,0,true)
3785 
3786 /* USAGE:   wheel_graph(n)
3787  *
3788  * Returns the wheel graph with n+1 vertices.
3789  */
_wheel_graph(const gen & g,GIAC_CONTEXT)3790 gen _wheel_graph(const gen &g,GIAC_CONTEXT) {
3791     if (g.type==_STRNG && g.subtype==-1) return g;
3792     if (!g.is_integer() || g.val<3)
3793         return generr("Expected an integer greater than two");
3794     graphe G(contextptr);
3795     graphe::layout x;
3796     G.make_wheel_graph(g.val,&x);
3797     G.store_layout(x);
3798     return G.to_gen();
3799 }
3800 static const char _wheel_graph_s[]="wheel_graph";
3801 static define_unary_function_eval(__wheel_graph,&_wheel_graph,_wheel_graph_s);
3802 define_unary_function_ptr5(at_wheel_graph,alias_at_wheel_graph,&__wheel_graph,0,true)
3803 
3804 /* USAGE:   grid_graph(m,n,[triangle])
3805  *
3806  * Returns a [triangular] grid graph on m*n vertices, where m,n>=2.
3807  */
_grid_graph(const gen & g,GIAC_CONTEXT)3808 gen _grid_graph(const gen &g,GIAC_CONTEXT) {
3809     if (g.type==_STRNG && g.subtype==-1) return g;
3810     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
3811         return gentypeerr(contextptr);
3812     vecteur &gv=*g._VECTptr;
3813     if (gv.size()<2 || gv.size()>3)
3814         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
3815     bool trg=false;
3816     if (gv.size()==3) {
3817         if (gv[2]!=at_triangle)
3818             return generr("Unrecognized option");
3819         trg=true;
3820     }
3821     if (!gv[0].is_integer() || !gv[1].is_integer())
3822         return generrtype("Expected an integer");
3823     int m=gv[0].val,n=gv[1].val;
3824     if (m<2 || n<2)
3825         return generr("Expected an integer greater than one");
3826     graphe G(contextptr);
3827     if (trg) {
3828         /* create triangulated grid using strong product of digraphs */
3829         graphe X(contextptr),Y(contextptr);
3830         vecteur Xlab,Ylab;
3831         X.make_default_labels(Xlab,m);
3832         Y.make_default_labels(Ylab,n);
3833         X.add_nodes(Xlab);
3834         Y.add_nodes(Ylab);
3835         X.make_directed();
3836         Y.make_directed();
3837         for (int i=0;i<m-1;++i) X.add_edge(i,i+1);
3838         for (int i=0;i<n-1;++i) Y.add_edge(i,i+1);
3839         gen seq=makesequence(X.to_gen(),Y.to_gen());
3840         return _underlying_graph(_graph_union(makesequence(
3841                                                   _cartesian_product(seq,contextptr),
3842                                                   _tensor_product(seq,contextptr)),
3843                                               contextptr),
3844                                  contextptr);
3845     }
3846     G.make_grid_graph(m,n,false);
3847     return G.to_gen();
3848 }
3849 static const char _grid_graph_s[]="grid_graph";
3850 static define_unary_function_eval(__grid_graph,&_grid_graph,_grid_graph_s);
3851 define_unary_function_ptr5(at_grid_graph,alias_at_grid_graph,&__grid_graph,0,true)
3852 
3853 /* USAGE:   torus_grid_graph(m,n)
3854  *
3855  * Returns the torus grid graph on m*n vertices, where m,n>=3.
3856  */
_torus_grid_graph(const gen & g,GIAC_CONTEXT)3857 gen _torus_grid_graph(const gen &g,GIAC_CONTEXT) {
3858     if (g.type==_STRNG && g.subtype==-1) return g;
3859     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
3860         return gentypeerr(contextptr);
3861     vecteur &gv=*g._VECTptr;
3862     if (gv.size()!=2)
3863         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
3864     if (!gv.front().is_integer() || !gv.back().is_integer())
3865         return generrtype("Expected an integer");
3866     int m=gv.front().val,n=gv.back().val;
3867     if (m<3 || n<3)
3868         return generr("Expected an integer greater than two");
3869     graphe G(contextptr);
3870     G.make_grid_graph(m,n,true);
3871     return G.to_gen();
3872 }
3873 static const char _torus_grid_graph_s[]="torus_grid_graph";
3874 static define_unary_function_eval(__torus_grid_graph,&_torus_grid_graph,_torus_grid_graph_s);
3875 define_unary_function_ptr5(at_torus_grid_graph,alias_at_torus_grid_graph,&__torus_grid_graph,0,true)
3876 
3877 /* USAGE:   web_graph(a,b)
3878  *
3879  * Returns the web graph on a*b vertices, where a>=3 and b>=2.
3880  */
_web_graph(const gen & g,GIAC_CONTEXT)3881 gen _web_graph(const gen &g,GIAC_CONTEXT) {
3882     if (g.type==_STRNG && g.subtype==-1) return g;
3883     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
3884         return gentypeerr(contextptr);
3885     vecteur &gv=*g._VECTptr;
3886     if (gv.size()!=2)
3887         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
3888     if (!gv.front().is_integer() || !gv.back().is_integer())
3889         return generrtype("Expected an integer");
3890     int a=gv.front().val,b=gv.back().val;
3891     if (a<3 || b<2)
3892         return generr("Value too small");
3893     graphe G(contextptr);
3894     graphe::layout x;
3895     G.make_web_graph(a,b,&x);
3896     G.store_layout(x);
3897     return G.to_gen();
3898 }
3899 static const char _web_graph_s[]="web_graph";
3900 static define_unary_function_eval(__web_graph,&_web_graph,_web_graph_s);
3901 define_unary_function_ptr5(at_web_graph,alias_at_web_graph,&__web_graph,0,true)
3902 
3903 /* USAGE:   cartesian_product(G1,G2,...)
3904  *
3905  * Returns Cartesian product of graphs G1, G2, ... Vertices in the resulting
3906  * graph are labelled as "u:v:..;" where u, v, ... are vertices from G1, G2,
3907  * ..., respectively.
3908  */
_cartesian_product(const gen & g,GIAC_CONTEXT)3909 gen _cartesian_product(const gen &g,GIAC_CONTEXT) {
3910     if (g.type==_STRNG && g.subtype==-1) return g;
3911     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
3912         return gentypeerr(contextptr);
3913     vecteur &gv=*g._VECTptr;
3914     graphe P(contextptr);
3915     if (!compute_product_of_graphs(gv,P,true,contextptr))
3916         return gt_err(_GT_ERR_NOT_A_GRAPH);
3917     return P.to_gen();
3918 }
3919 static const char _cartesian_product_s[]="cartesian_product";
3920 static define_unary_function_eval(__cartesian_product,&_cartesian_product,_cartesian_product_s);
3921 define_unary_function_ptr5(at_cartesian_product,alias_at_cartesian_product,&__cartesian_product,0,true)
3922 
3923 /* USAGE:   tensor_product(G1,G2,...)
3924  *
3925  * Returns tensor product of graphs G1, G2, ... Vertices in the resulting graph
3926  * are labelled as "u:v:..." where u, v, ... are vertices from G1, G2, ...,
3927  * respectively.
3928  */
_tensor_product(const gen & g,GIAC_CONTEXT)3929 gen _tensor_product(const gen &g,GIAC_CONTEXT) {
3930     if (g.type==_STRNG && g.subtype==-1) return g;
3931     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
3932         return gentypeerr(contextptr);
3933     vecteur &gv=*g._VECTptr;
3934     graphe P(contextptr);
3935     if (!compute_product_of_graphs(gv,P,false,contextptr))
3936         return gt_err(_GT_ERR_NOT_A_GRAPH);
3937     return P.to_gen();
3938 }
3939 static const char _tensor_product_s[]="tensor_product";
3940 static define_unary_function_eval(__tensor_product,&_tensor_product,_tensor_product_s);
3941 define_unary_function_ptr5(at_tensor_product,alias_at_tensor_product,&__tensor_product,0,true)
3942 
3943 /* USAGE:   path_graph(n or V)
3944  *
3945  * Returns a path graph with n vertices, which can also be specified as list of
3946  * their labels.
3947  */
_path_graph(const gen & g,GIAC_CONTEXT)3948 gen _path_graph(const gen &g,GIAC_CONTEXT) {
3949     if (g.type==_STRNG && g.subtype==-1) return g;
3950     graphe G(contextptr);
3951     if (!vertices_from_integer_or_vecteur(g,G))
3952         return gt_err(_GT_ERR_BAD_VERTICES);
3953     int n=G.node_count();
3954     if (n<2)
3955         return generr("At least two vertices are required");
3956     for (int i=0;i<n-1;++i) {
3957         G.add_edge(i,i+1);
3958     }
3959     return G.to_gen();
3960 }
3961 static const char _path_graph_s[]="path_graph";
3962 static define_unary_function_eval(__path_graph,&_path_graph,_path_graph_s);
3963 define_unary_function_ptr5(at_path_graph,alias_at_path_graph,&__path_graph,0,true)
3964 
3965 /* USAGE:   eulerian_trail(G,[V])
3966  *
3967  * Returns true iff graph G is eulerian, i.e. if it has eulerian trail. If
3968  * identifier V is specified as the second argument, that path is written to it.
3969  */
_is_eulerian(const gen & g,GIAC_CONTEXT)3970 gen _is_eulerian(const gen &g,GIAC_CONTEXT) {
3971     if (g.type==_STRNG && g.subtype==-1) return g;
3972     if (g.type!=_VECT)
3973         return gentypeerr(contextptr);
3974     bool has_path_idnt=g.subtype==_SEQ__VECT;
3975     graphe G(contextptr);
3976     if (!G.read_gen(has_path_idnt?g._VECTptr->front():g))
3977         return gt_err(_GT_ERR_NOT_A_GRAPH);
3978     int n=G.node_count();
3979     if (n==0) return gt_err(_GT_ERR_GRAPH_IS_NULL);
3980     if (G.is_directed()) {
3981         if (has_path_idnt) return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
3982         int start=-1,finish=-1,ic=0;
3983         graphe::ivector ind,outd;
3984         G.compute_in_out_degrees(ind,outd);
3985         for (int i=0;i<n;++i) {
3986             if (outd[i]==ind[i]+1) {
3987                 if (start>=0) return graphe::FAUX;
3988                 start=i;
3989             } else if (ind[i]==outd[i]+1) {
3990                 if (finish>=0) return graphe::FAUX;
3991                 finish=i;
3992             } else if (ind[i]!=outd[i]) return graphe::FAUX;
3993         }
3994         if ((start>=0 && finish<0) || (start<0 && finish>=0)) return graphe::FAUX;
3995         if (start>=0 && finish>=0) G.add_edge(finish,start);
3996         graphe::ivectors sc;
3997         G.strongly_connected_components(sc);
3998         for (graphe::ivectors_iter it=sc.begin();it!=sc.end();++it) {
3999             if (it->size()>1) ++ic;
4000         }
4001         if (ic!=1) return graphe::FAUX;
4002         if (start>=0 && finish>=0)
4003             *logptr(contextptr) << "Input digraph has an Eulerian trail starting at "
4004                                 << G.node_label(start) << " and ending at " << G.node_label(finish) << "\n";
4005         else *logptr(contextptr) << "Input digraph has an Eulerian circuit\n";
4006         return graphe::VRAI;
4007     }
4008     graphe::ivector path;
4009     bool iscycle; // dummy
4010     if ((has_path_idnt && !G.find_eulerian_trail(path)) ||
4011             (!has_path_idnt && G.eulerian_trail_start(iscycle)<0))
4012         return graphe::FAUX;
4013     if (has_path_idnt) {
4014         if (g._VECTptr->size()!=2)
4015             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
4016         // output path as vecteur V
4017         gen V=g._VECTptr->back();
4018         if (V.type!=_IDNT)
4019             return generrtype("Expected an identifier");
4020         vecteur P(path.size());
4021         int i=0;
4022         for (graphe::ivector_iter it=path.begin();it!=path.end();++it) {
4023             P[i++]=G.node_label(*it);
4024         }
4025         identifier_assign(*V._IDNTptr,P,contextptr);
4026     }
4027     return graphe::VRAI;
4028 }
4029 static const char _is_eulerian_s[]="is_eulerian";
4030 static define_unary_function_eval(__is_eulerian,&_is_eulerian,_is_eulerian_s);
4031 define_unary_function_ptr5(at_is_eulerian,alias_at_is_eulerian,&__is_eulerian,0,true)
4032 
4033 /* USAGE:   kneser_graph(n,k)
4034  *
4035  * Returns Kneser graph K(n,k) with comb(n,k) vertices. The largest acceptable
4036  * value of n is 20. Kneser graphs with more than 10000 vertices will not be
4037  * created.
4038  */
_kneser_graph(const gen & g,GIAC_CONTEXT)4039 gen _kneser_graph(const gen &g,GIAC_CONTEXT) {
4040     if (g.type==_STRNG && g.subtype==-1) return g;
4041     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4042         return gentypeerr(contextptr);
4043     vecteur &gv=*g._VECTptr;
4044     if (gv.size()!=2)
4045         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
4046     if (!gv.front().is_integer() || !gv.back().is_integer())
4047         return generrtype("Expected an integer");
4048     int n=gv.front().val,k=gv.back().val;
4049     if (n<2 || n>20 || k<1 || k>=n)
4050         return generr("Failed to satisfy 2<n<=20 and 1<=k<n");
4051     graphe G(contextptr);
4052     if (!G.make_kneser_graph(n,k))
4053         return gensizeerr(contextptr);
4054     return G.to_gen();
4055 }
4056 static const char _kneser_graph_s[]="kneser_graph";
4057 static define_unary_function_eval(__kneser_graph,&_kneser_graph,_kneser_graph_s);
4058 define_unary_function_ptr5(at_kneser_graph,alias_at_kneser_graph,&__kneser_graph,0,true)
4059 
4060 /* USAGE:   odd_graph(n)
4061  *
4062  * Returns odd graph of order n as Kneser graph K(2n-1,n-1). The largest
4063  * acceptable value of n is 8.
4064  */
_odd_graph(const gen & g,GIAC_CONTEXT)4065 gen _odd_graph(const gen &g,GIAC_CONTEXT) {
4066     if (g.type==_STRNG && g.subtype==-1) return g;
4067     if (!g.is_integer() || !is_strictly_positive(g,contextptr))
4068         return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
4069     int n=g.val;
4070     if (n<2 || n>8)
4071         return generr("Failed to satisfy 2<n<=8");
4072     graphe G(contextptr);
4073     assert(G.make_kneser_graph(2*n-1,n-1));
4074     return G.to_gen();
4075 }
4076 static const char _odd_graph_s[]="odd_graph";
4077 static define_unary_function_eval(__odd_graph,&_odd_graph,_odd_graph_s);
4078 define_unary_function_ptr5(at_odd_graph,alias_at_odd_graph,&__odd_graph,0,true)
4079 
4080 /* USAGE:   highlight_vertex(G,V,[C])
4081  *
4082  * Change color of vertex or list of vertices V in graph G to C (or green, if C
4083  * is not specified) and return the modified graph.
4084  */
_highlight_vertex(const gen & g,GIAC_CONTEXT)4085 gen _highlight_vertex(const gen &g,GIAC_CONTEXT) {
4086     if (g.type==_STRNG && g.subtype==-1) return g;
4087     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4088         return gentypeerr(contextptr);
4089     vecteur &gv=*g._VECTptr;
4090     if (gv.size()<2)
4091         return gensizeerr(contextptr);
4092     graphe G(contextptr);
4093     if (!G.read_gen(gv.front()))
4094         return gt_err(_GT_ERR_NOT_A_GRAPH);
4095     vecteur V;
4096     if (gv[1].type==_VECT)
4097         V=*gv[1]._VECTptr;
4098     else
4099         V.push_back(gv[1]);
4100     graphe::ivector indices;
4101     int index;
4102     for (const_iterateur it=V.begin();it!=V.end();++it) {
4103         index=G.node_index(*it);
4104         if (index<0)
4105             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
4106         indices.push_back(index);
4107     }
4108     gen C=gv.size()==3?gv.back():graphe::default_highlighted_vertex_color;
4109     if (!parse_vertex_colors(G,C,indices))
4110         return generrtype("Failed to parse vertex colors");
4111     return G.to_gen();
4112 }
4113 static const char _highlight_vertex_s[]="highlight_vertex";
4114 static define_unary_function_eval(__highlight_vertex,&_highlight_vertex,_highlight_vertex_s);
4115 define_unary_function_ptr5(at_highlight_vertex,alias_at_highlight_vertex,&__highlight_vertex,0,true)
4116 
4117 /* USAGE:   highlight_edges(G,E,[C])
4118  *
4119  * Change color of edge or list of edges E in graph V to C (or red, if C is not
4120  * specified) and return the modified graph.
4121  */
_highlight_edges(const gen & g,GIAC_CONTEXT)4122 gen _highlight_edges(const gen &g,GIAC_CONTEXT) {
4123     if (g.type==_STRNG && g.subtype==-1) return g;
4124     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4125         return gentypeerr(contextptr);
4126     vecteur &gv=*g._VECTptr;
4127     if (gv.size()<2)
4128         return gensizeerr(contextptr);
4129     graphe G(contextptr);
4130     if (!G.read_gen(gv.front()))
4131         return gt_err(_GT_ERR_NOT_A_GRAPH);
4132     if (gv[1].type!=_VECT)
4133         return gt_err(_GT_ERR_INVALID_EDGE);
4134     vecteur &E=*gv[1]._VECTptr;
4135     graphe::ipairs edges;
4136     bool notfound=false;
4137     if (!G.edges2ipairs(E,edges,notfound))
4138         return notfound?gt_err(_GT_ERR_EDGE_NOT_FOUND):gensizeerr(contextptr);
4139     gen C=gv.size()==3?gv.back():graphe::default_highlighted_edge_color;
4140     for (graphe::ipairs_iter it=edges.begin();it!=edges.end();++it) {
4141         G.set_edge_attribute(it->first,it->second,_GT_ATTRIB_COLOR,C.type==_VECT?C._VECTptr->at(it-edges.begin()):C);
4142     }
4143     return G.to_gen();
4144 }
4145 static const char _highlight_edges_s[]="highlight_edges";
4146 static define_unary_function_eval(__highlight_edges,&_highlight_edges,_highlight_edges_s);
4147 define_unary_function_ptr5(at_highlight_edges,alias_at_highlight_edges,&__highlight_edges,0,true)
4148 
4149 /* USAGE:   highlight_subgraph(G,S,[C1,C2])
4150  *
4151  * Change color of edges and vertices from S (which is a subgraph of G or a
4152  * list of subgraphs of G) to C1 and C2, respectively (by default, C1=red and
4153  * C2=green).
4154  */
_highlight_subgraph(const gen & g,GIAC_CONTEXT)4155 gen _highlight_subgraph(const gen &g,GIAC_CONTEXT) {
4156     if (g.type==_STRNG && g.subtype==-1) return g;
4157     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4158         return gentypeerr(contextptr);
4159     vecteur &gv=*g._VECTptr;
4160     if (gv.size()<2)
4161         return gensizeerr(contextptr);
4162     if (gv[1].type!=_VECT)
4163         return gentypeerr(contextptr);
4164     int C1=graphe::default_highlighted_edge_color,C2=graphe::default_highlighted_vertex_color;
4165     if (gv.size()>=4) {
4166         if (!gv[2].is_integer() || !gv[3].is_integer())
4167             return generrtype("Expected an integer");
4168         C1=gv[2].val;
4169         C2=gv[3].val;
4170     }
4171     bool overwrite_weights=false;
4172     if (gv.back().is_integer() && gv.back().val==_GT_WEIGHTS)
4173         overwrite_weights=true;
4174     gen modG;
4175     if (gv[1]._VECTptr->front().type==_VECT) {
4176         modG=gv.front();
4177         for (const_iterateur it=gv[1]._VECTptr->begin();it!=gv[1]._VECTptr->end();++it) {
4178             modG=_highlight_subgraph(makesequence(modG,*it,C1,C2),contextptr);
4179         }
4180         return modG;
4181     }
4182     graphe G(contextptr),S(contextptr);
4183     if (!G.read_gen(gv[0]) || !S.read_gen(gv[1]))
4184         return gt_err(_GT_ERR_NOT_A_GRAPH);
4185     if (!S.is_subgraph(G))
4186         return gt_err(_GT_ERR_NOT_A_SUBGRAPH);
4187     vecteur V=S.vertices(),E=S.edges(false);
4188     if (overwrite_weights && G.is_weighted() && S.is_weighted()) {
4189         int i,j,k,l;if (g.type==_STRNG && g.subtype==-1) return g;
4190         for (const_iterateur it=E.begin();it!=E.end();++it) {
4191             i=G.node_index(it->_VECTptr->front());
4192             j=G.node_index(it->_VECTptr->back());
4193             k=S.node_index(it->_VECTptr->front());
4194             l=S.node_index(it->_VECTptr->back());
4195             G.set_edge_attribute(i,j,_GT_ATTRIB_WEIGHT,S.weight(k,l));
4196         }
4197     }
4198     modG=_highlight_edges(makesequence(G.to_gen(),E,C1),contextptr);
4199     return _highlight_vertex(makesequence(modG,V,C2),contextptr);
4200 }
4201 static const char _highlight_subgraph_s[]="highlight_subgraph";
4202 static define_unary_function_eval(__highlight_subgraph,&_highlight_subgraph,_highlight_subgraph_s);
4203 define_unary_function_ptr5(at_highlight_subgraph,alias_at_highlight_subgraph,&__highlight_subgraph,0,true)
4204 
4205 /* USAGE:   highlight_trail(G,T,[C])
4206  *
4207  * Change color of edges in G which lie along the trail T to C (or red, if C is
4208  * not specified).
4209  */
_highlight_trail(const gen & g,GIAC_CONTEXT)4210 gen _highlight_trail(const gen &g,GIAC_CONTEXT) {
4211     if (g.type==_STRNG && g.subtype==-1) return g;
4212     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4213         return gentypeerr(contextptr);
4214     vecteur &gv=*g._VECTptr;
4215     if (gv.size()<2)
4216         return gensizeerr(contextptr);
4217     if (gv.size()==3 && !gv.back().is_integer() && gv.back().type!=_VECT)
4218         return gentypeerr(contextptr);
4219     vecteur V;
4220     if (gv[1].type==_VECT)
4221         V=*gv[1]._VECTptr;
4222     else if (gv[1].is_symb_of_sommet(at_trail))
4223         V=*gv[1]._SYMBptr->feuille._VECTptr;
4224     if (V.empty())
4225         return gv.front();
4226     if (V.front().type!=_VECT)
4227         V=makevecteur(V);
4228     graphe G(contextptr);
4229     if (!G.read_gen(gv.front()))
4230         return gt_err(_GT_ERR_NOT_A_GRAPH);
4231     gen color=gv.size()==3?gv.back():gen(_RED);
4232     if (color.type==_VECT && !is_integer_vecteur(*color._VECTptr))
4233         return generrtype("Invalid specification of colors");
4234     if (color.type==_VECT && color._VECTptr->size()!=V.size())
4235         return generrdim("Numbers of colors and vertices do not match");
4236     int i,j;
4237     for (const_iterateur it=V.begin();it!=V.end();++it) {
4238         for (const_iterateur jt=it->_VECTptr->begin();jt!=it->_VECTptr->end()-1;++jt) {
4239             const gen &v=*jt,&w=*(jt+1);
4240             if (G.node_index(v)<0)
4241                 return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
4242             if ((i=G.node_index(v))<0 || (j=G.node_index(w))<0)
4243                 return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
4244             if (!G.has_edge(i,j))
4245                 return gt_err(_GT_ERR_EDGE_NOT_FOUND);
4246             G.set_edge_attribute(i,j,_GT_ATTRIB_COLOR,(color.type==_VECT?color._VECTptr->at(it-V.begin()):color).val);
4247         }
4248     }
4249     return G.to_gen();
4250 }
4251 static const char _highlight_trail_s[]="highlight_trail";
4252 static define_unary_function_eval(__highlight_trail,&_highlight_trail,_highlight_trail_s);
4253 define_unary_function_ptr5(at_highlight_trail,alias_at_highlight_trail,&__highlight_trail,0,true)
4254 
4255 /* USAGE:   disjoint_union(G1,G2,...)
4256  *
4257  * Returns the disjoint union of the input graphs. Vertices in the resulting
4258  * graph are labelled with "k:v", where k is index of the corresponding k-th
4259  * graph Gk and v is vertex in Gk.
4260  */
_disjoint_union(const gen & g,GIAC_CONTEXT)4261 gen _disjoint_union(const gen &g,GIAC_CONTEXT) {
4262     if (g.type==_STRNG && g.subtype==-1) return g;
4263     if (g.type!=_VECT)
4264         return gentypeerr(contextptr);
4265     graphe G(contextptr);
4266     int err;
4267     if ((err=graphunion(G,*g._VECTptr,true))>=0)
4268         return gt_err(err);
4269     return G.to_gen();
4270 }
4271 static const char _disjoint_union_s[]="disjoint_union";
4272 static define_unary_function_eval(__disjoint_union,&_disjoint_union,_disjoint_union_s);
4273 define_unary_function_ptr5(at_disjoint_union,alias_at_disjoint_union,&__disjoint_union,0,true)
4274 
4275 /* USAGE:   graph_union(G1,G2,...)
4276  *
4277  * Returns the union of the input graphs. Set of vertices of the resulting
4278  * graph is the union of the sets of vertices of the input graphs and the set
4279  * of edges of the resulting graph is the union of sets of edges of the input
4280  * graphs. If graphs G1 and G2 are both weighted, the weight of any common edge
4281  * is the sum of the weights of that edge in G1 and G2.
4282  */
_graph_union(const gen & g,GIAC_CONTEXT)4283 gen _graph_union(const gen &g,GIAC_CONTEXT) {
4284     if (g.type==_STRNG && g.subtype==-1) return g;
4285     if (g.type!=_VECT)
4286         return gentypeerr(contextptr);
4287     graphe G(contextptr);
4288     int err;
4289     if ((err=graphunion(G,*g._VECTptr,false))>=0)
4290         return gt_err(err);
4291     return G.to_gen();
4292 }
4293 static const char _graph_union_s[]="graph_union";
4294 static define_unary_function_eval(__graph_union,&_graph_union,_graph_union_s);
4295 define_unary_function_ptr5(at_graph_union,alias_at_graph_union,&__graph_union,0,true)
4296 
4297 /* USAGE:   graph_join(G,H)
4298  *
4299  * Returns the graph obtained by connecting every vertex from G with every
4300  * vertex from H. The vertex labels in the resulting graph are strings of form
4301  * "1:u" and "2:v" where u and v are vertices from G and H, respectively.
4302  */
_graph_join(const gen & g,GIAC_CONTEXT)4303 gen _graph_join(const gen &g,GIAC_CONTEXT) {
4304     if (g.type==_STRNG && g.subtype==-1) return g;
4305     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4306         return gentypeerr(contextptr);
4307     vecteur &gv=*g._VECTptr;
4308     if (gv.size()!=2)
4309         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
4310     graphe G(contextptr),G1(contextptr),G2(contextptr);
4311     if (!G1.read_gen(gv.front()) || !G2.read_gen(gv.back()))
4312         return gt_err(_GT_ERR_NOT_A_GRAPH);
4313     if (G1.is_directed() || G2.is_directed())
4314         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
4315     if (G1.is_weighted() || G2.is_weighted())
4316         return gt_err(_GT_ERR_UNWEIGHTED_GRAPH_REQUIRED);
4317     vecteur V=G1.vertices(),W=G2.vertices();
4318     stringstream ss;
4319     for (iterateur it=V.begin();it!=V.end();++it) {
4320         add_prefix_to_vertex_label(*it,1,ss);
4321     }
4322     for (iterateur it=W.begin();it!=W.end();++it) {
4323         add_prefix_to_vertex_label(*it,2,ss);
4324     }
4325     graphunion(G,gv,true);
4326     for (const_iterateur it=V.begin();it!=V.end();++it) {
4327         for (const_iterateur jt=W.begin();jt!=W.end();++jt) {
4328             G.add_edge(*it,*jt);
4329         }
4330     }
4331     return G.to_gen();
4332 }
4333 static const char _graph_join_s[]="graph_join";
4334 static define_unary_function_eval(__graph_join,&_graph_join,_graph_join_s);
4335 define_unary_function_ptr5(at_graph_join,alias_at_graph_join,&__graph_join,0,true)
4336 
4337 /* USAGE:   graph_equal(G1,G2)
4338  *
4339  * Returns true iff G1 is equal to G2, that is when the sets of vertices and
4340  * edges of G1 and G2, as well as the orderings of vertices in both graphs,
4341  * mutually coincide. If the graphs are weighted (they must both be
4342  * (un)weighted and (un)directed), weights given to the same edge in two graphs
4343  * must be equal.
4344  */
_graph_equal(const gen & g,GIAC_CONTEXT)4345 gen _graph_equal(const gen &g,GIAC_CONTEXT) {
4346     if (g.type==_STRNG && g.subtype==-1) return g;
4347     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4348         return gentypeerr(contextptr);
4349     vecteur &gv=*g._VECTptr;
4350     if (gv.size()!=2)
4351         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
4352     graphe G1(contextptr),G2(contextptr);
4353     if (!G1.read_gen(gv.front()) || !G2.read_gen(gv.back()))
4354         return gt_err(_GT_ERR_NOT_A_GRAPH);
4355     return graphe::boole(G1.is_equal(G2));
4356 }
4357 static const char _graph_equal_s[]="graph_equal";
4358 static define_unary_function_eval(__graph_equal,&_graph_equal,_graph_equal_s);
4359 define_unary_function_ptr5(at_graph_equal,alias_at_graph_equal,&__graph_equal,0,true)
4360 
4361 /* USAGE:   reverse_graph(G)
4362  *
4363  * Returns reverse graph of G, i.e. the graph G with the directions of all edges
4364  * reversed.
4365  */
_reverse_graph(const gen & g,GIAC_CONTEXT)4366 gen _reverse_graph(const gen &g,GIAC_CONTEXT) {
4367     if (g.type==_STRNG && g.subtype==-1) return g;
4368     graphe G(contextptr),H(contextptr);
4369     if (!G.read_gen(g))
4370         return gt_err(_GT_ERR_NOT_A_GRAPH);
4371     if (!G.is_directed())
4372         return G.to_gen();
4373     G.reverse(H);
4374     return H.to_gen();
4375 }
4376 static const char _reverse_graph_s[]="reverse_graph";
4377 static define_unary_function_eval(__reverse_graph,&_reverse_graph,_reverse_graph_s);
4378 define_unary_function_ptr5(at_reverse_graph,alias_at_reverse_graph,&__reverse_graph,0,true)
4379 
4380 /* USAGE:   interval_graph(a..b,c..d,...)
4381  *          interval_graph([a..b,c..d,...])
4382  *
4383  * Returns the interval graph with respect to intervals a..b, c..d, ... on the
4384  * real line. It has one vertex per interval and two vertices are connected iff
4385  * the corresponding intervals intersect.
4386  */
_interval_graph(const gen & g,GIAC_CONTEXT)4387 gen _interval_graph(const gen &g,GIAC_CONTEXT) {
4388     if (g.type==_STRNG && g.subtype==-1) return g;
4389     if (g.type!=_VECT)
4390         return gentypeerr(contextptr);
4391     vecteur &gv=*g._VECTptr;
4392     gen a,b;
4393     stringstream ss;
4394     int n=gv.size();
4395     vecteur V;
4396     V.reserve(n);
4397     for (const_iterateur it=gv.begin();it!=gv.end();++it) {
4398         if (!it->is_symb_of_sommet(at_interval))
4399             return generrtype("Expected an interval");
4400         a=it->_SYMBptr->feuille._VECTptr->front();
4401         b=it->_SYMBptr->feuille._VECTptr->back();
4402         if (!graphe::is_real_number(a) || !graphe::is_real_number(b))
4403             return generrtype("Expected an interval of reals");
4404         ss.str("");
4405         ss << a << " .. " << b;
4406         V.push_back(graphe::str2gen(ss.str(),true));
4407     }
4408     graphe G(contextptr);
4409     G.add_nodes(V);
4410     for (const_iterateur it=gv.begin();it!=gv.end();++it) {
4411         const gen &a1=it->_SYMBptr->feuille._VECTptr->front();
4412         const gen &b1=it->_SYMBptr->feuille._VECTptr->back();
4413         for (const_iterateur jt=it+1;jt!=gv.end();++jt) {
4414             const gen &a2=jt->_SYMBptr->feuille._VECTptr->front();
4415             const gen &b2=jt->_SYMBptr->feuille._VECTptr->back();
4416             if (is_greater(b2,a1,contextptr) && is_greater(b1,a2,contextptr))
4417                 G.add_edge(it-gv.begin(),jt-gv.begin());
4418         }
4419     }
4420     return G.to_gen();
4421 }
4422 static const char _interval_graph_s[]="interval_graph";
4423 static define_unary_function_eval(__interval_graph,&_interval_graph,_interval_graph_s);
4424 define_unary_function_ptr5(at_interval_graph,alias_at_interval_graph,&__interval_graph,0,true)
4425 
4426 /* USAGE:   subdivide_edges(G,E,[r])
4427  *
4428  * Inserts r (by default 1) new vertices to each edge/arc from G contained in
4429  * the list E (which may be a single edge/arc) and returns a modified copy of
4430  * G. New vertices are labeled with the smallest available integers.
4431  */
_subdivide_edges(const gen & g,GIAC_CONTEXT)4432 gen _subdivide_edges(const gen &g,GIAC_CONTEXT) {
4433     if (g.type==_STRNG && g.subtype==-1) return g;
4434     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4435         return gentypeerr(contextptr);
4436     vecteur &gv=*g._VECTptr;
4437     if (gv[1].type!=_VECT)
4438         return gt_err(_GT_ERR_INVALID_EDGE);
4439     int r=1;
4440     if(gv.size()==3 && (!gv[2].is_integer() || (r=gv[2].val)<1))
4441         return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
4442     vecteur &E=*gv[1]._VECTptr;
4443     graphe G(contextptr);
4444     if (!G.read_gen(gv.front()))
4445         return gt_err(_GT_ERR_NOT_A_GRAPH);
4446     int i,j,l=G.largest_integer_label();
4447     graphe::ipairs edges;
4448     if (ckmatrix(E)) {
4449         // a list of edges/arcs is given
4450         if (E.front()._VECTptr->size()!=2)
4451             return gensizeerr(contextptr);
4452         edges.resize(E.size());
4453         int k=0;
4454         for (const_iterateur it=E.begin();it!=E.end();++it) {
4455             const vecteur &e=*(it->_VECTptr);
4456             i=G.node_index(e.front());
4457             j=G.node_index(e.back());
4458             if (i<0 || j<0)
4459                 return gt_err(i<0?e.front():e.back(),_GT_ERR_VERTEX_NOT_FOUND);
4460             if (!G.has_edge(i,j))
4461                 return gt_err(e,_GT_ERR_EDGE_NOT_FOUND);
4462             edges[k++]=make_pair(i,j);
4463         }
4464     } else {
4465         // a single edge/arc is given
4466         if (E.size()!=2)
4467             return gt_err(_GT_ERR_INVALID_EDGE);
4468         edges.push_back(make_pair(G.node_index(E.front()),G.node_index(E.back())));
4469     }
4470     for (graphe::ipairs_iter it=edges.begin();it!=edges.end();++it) {
4471         G.subdivide_edge(*it,r,l);
4472     }
4473     return G.to_gen();
4474 }
4475 static const char _subdivide_edges_s[]="subdivide_edges";
4476 static define_unary_function_eval(__subdivide_edges,&_subdivide_edges,_subdivide_edges_s);
4477 define_unary_function_ptr5(at_subdivide_edges,alias_at_subdivide_edges,&__subdivide_edges,0,true)
4478 
4479 /* USAGE:   graph_power(G,k)
4480  *
4481  * Returns the k-th power of graph G, where two vertices are connected iff there
4482  * exists a path of length at most k in the original graph.
4483  */
_graph_power(const gen & g,GIAC_CONTEXT)4484 gen _graph_power(const gen &g,GIAC_CONTEXT) {
4485     if (g.type==_STRNG && g.subtype==-1) return g;
4486     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4487         return gentypeerr(contextptr);
4488     vecteur &gv=*g._VECTptr;
4489     if (gv.size()!=2)
4490         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
4491     int k;
4492     if (!gv.back().is_integer() || (k=gv.back().val)<1)
4493         return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
4494     graphe G(contextptr);
4495     if (!G.read_gen(gv.front()))
4496         return gt_err(_GT_ERR_NOT_A_GRAPH);
4497     int n=G.node_count();
4498     matrice m,mpow;
4499     G.adjacency_matrix(m);
4500     mpow=m;
4501     for (int i=1;i<k;++i) {
4502         for (int j=0;j<n;++j) {
4503             mpow[j]._VECTptr->at(j)+=1;
4504         }
4505         mpow=mmult(mpow,m);
4506     }
4507     for (int i=0;i<n;++i) {
4508         for (int j=0;j<n;++j) {
4509             gen &mij=mpow[i]._VECTptr->at(j);
4510             if (!is_zero(mij))
4511                 mij=1;
4512         }
4513     }
4514     gen opt=symbolic(at_equal,makesequence(_GT_DIRECTED,G.is_directed()));
4515     return _graph(makesequence(G.vertices(),mpow,opt),contextptr);
4516 }
4517 static const char _graph_power_s[]="graph_power";
4518 static define_unary_function_eval(__graph_power,&_graph_power,_graph_power_s);
4519 define_unary_function_ptr5(at_graph_power,alias_at_graph_power,&__graph_power,0,true)
4520 
4521 /* USAGE:   vertex_distance(G,s,t)
4522  *          vertex_distance(G,s,T)
4523  *
4524  * Returns the number of edges in the shortest path from vertex s to vertex t
4525  * in graph G. If such path does not exist, returns +infinity. For vector T of
4526  * vertices from G returns the list of distances from s to each vertex t in T.
4527  */
_vertex_distance(const gen & g,GIAC_CONTEXT)4528 gen _vertex_distance(const gen &g,GIAC_CONTEXT) {
4529     if (g.type==_STRNG && g.subtype==-1) return g;
4530     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4531         return gentypeerr(contextptr);
4532     vecteur &gv=*g._VECTptr;
4533     if (gv.size()!=3)
4534         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
4535     graphe G(contextptr);
4536     if (!G.read_gen(gv.front()))
4537         return gt_err(_GT_ERR_NOT_A_GRAPH);
4538     int i,j;
4539     if ((i=G.node_index(gv[1]))<0)
4540         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
4541     vecteur T;
4542     bool single=false;
4543     if (gv[2].type==_VECT)
4544         T=*gv[2]._VECTptr;
4545     else {
4546         T.push_back(gv[2]);
4547         single=true;
4548     }
4549     graphe::ivector J(T.size()),dist;
4550     for (const_iterateur it=T.begin();it!=T.end();++it) {
4551         if ((j=G.node_index(*it))<0)
4552             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
4553         J[it-T.begin()]=j;
4554     }
4555     G.distance(i,J,dist);
4556     vecteur res(T.size());
4557     for (graphe::ivector_iter it=dist.begin();it!=dist.end();++it) {
4558         res[it-dist.begin()]=*it>=0?gen(*it):graphe::plusinf();
4559     }
4560     return single?res.front():res;
4561 }
4562 static const char _vertex_distance_s[]="vertex_distance";
4563 static define_unary_function_eval(__vertex_distance,&_vertex_distance,_vertex_distance_s);
4564 define_unary_function_ptr5(at_vertex_distance,alias_at_vertex_distance,&__vertex_distance,0,true)
4565 
4566 /* USAGE:   shortest_path(G,s,t)
4567  *          shortest_path(G,s,T)
4568  *
4569  * Returns the shortest path from vertex s to vertex t in graph G. If such path
4570  * does not exist, returns an empty list. If vector T of vertices from G is
4571  * given, the list of shortest paths from s to each t int T is returned.
4572  */
_shortest_path(const gen & g,GIAC_CONTEXT)4573 gen _shortest_path(const gen &g,GIAC_CONTEXT) {
4574     if (g.type==_STRNG && g.subtype==-1) return g;
4575     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4576         return gentypeerr(contextptr);
4577     vecteur &gv=*g._VECTptr;
4578     if (gv.size()!=3)
4579         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
4580     graphe G(contextptr);
4581     if (!G.read_gen(gv.front()))
4582         return gt_err(_GT_ERR_NOT_A_GRAPH);
4583     int i,j;
4584     if ((i=G.node_index(gv[1]))<0)
4585         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
4586     vecteur T;
4587     bool single=false;
4588     if (gv[2].type==_VECT)
4589         T=*gv[2]._VECTptr;
4590     else {
4591         T.push_back(gv[2]);
4592         single=true;
4593     }
4594     graphe::ivector J(T.size()),dist;
4595     for (const_iterateur it=T.begin();it!=T.end();++it) {
4596         if ((j=G.node_index(*it))<0)
4597             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
4598         J[it-T.begin()]=j;
4599     }
4600     graphe::ivectors shortest_paths;
4601     G.distance(i,J,dist,&shortest_paths);
4602     vecteur res(T.size());
4603     for (graphe::ivectors_iter it=shortest_paths.begin();it!=shortest_paths.end();++it) {
4604         i=it-shortest_paths.begin();
4605         res[it-shortest_paths.begin()]=dist[i]>=0?G.get_node_labels(*it):vecteur(0);
4606     }
4607     return single?res.front():change_subtype(res,_LIST__VECT);
4608 }
4609 static const char _shortest_path_s[]="shortest_path";
4610 static define_unary_function_eval(__shortest_path,&_shortest_path,_shortest_path_s);
4611 define_unary_function_ptr5(at_shortest_path,alias_at_shortest_path,&__shortest_path,0,true)
4612 
4613 /* USAGE:   allpairs_distance(G)
4614  *
4615  * Returns a square matrix D of order n(=number of vertices in G) such that
4616  * D(i,j) is the distance between i-th and j-th vertex of (weighted) graph G,
4617  * computed by using Floyd-Warshall algorithm with complexity O(n^3). If For
4618  * some vertex pair no path exists, the corresponding entry in D is equal to
4619  * +infinity. Edges may have positive or negative weights but G shouldn't
4620  * contain negative cycles.
4621  */
_allpairs_distance(const gen & g,GIAC_CONTEXT)4622 gen _allpairs_distance(const gen &g,GIAC_CONTEXT) {
4623     if (g.type==_STRNG && g.subtype==-1) return g;
4624     graphe G(contextptr);
4625     if (!G.read_gen(g))
4626         return gt_err(_GT_ERR_NOT_A_GRAPH);
4627     matrice dist;
4628     if (!G.is_null())
4629         G.allpairs_distance(dist);
4630     return dist;
4631 }
4632 static const char _allpairs_distance_s[]="allpairs_distance";
4633 static define_unary_function_eval(__allpairs_distance,&_allpairs_distance,_allpairs_distance_s);
4634 define_unary_function_ptr5(at_allpairs_distance,alias_at_allpairs_distance,&__allpairs_distance,0,true)
4635 
4636 /* USAGE:   graph_diameter(G)
4637  *
4638  * Returns the diameter of graph G, i.e. the maximum distance between a pair of
4639  * vertices in G. If G is disconnected, its diameter is equal to +infinity.
4640  */
_graph_diameter(const gen & g,GIAC_CONTEXT)4641 gen _graph_diameter(const gen &g,GIAC_CONTEXT) {
4642     if (g.type==_STRNG && g.subtype==-1) return g;
4643     graphe G(contextptr);
4644     if (!G.read_gen(g))
4645         return gt_err(_GT_ERR_NOT_A_GRAPH);
4646     if (G.is_null())
4647         return gt_err(_GT_ERR_GRAPH_IS_NULL);
4648     if (!G.is_connected())
4649         return graphe::plusinf();
4650     matrice D;
4651     G.allpairs_distance(D);
4652     int n=G.node_count();
4653     gen max_dist(symbolic(at_neg,_IDNT_infinity()));
4654     for (int i=0;i<n;++i) {
4655         for (int j=0;j<n;++j) {
4656             const gen &dist=D[i][j];
4657             if (is_inf(dist))
4658                 continue;
4659             max_dist=_max(makesequence(max_dist,dist),contextptr);
4660         }
4661     }
4662     return max_dist;
4663 }
4664 static const char _graph_diameter_s[]="graph_diameter";
4665 static define_unary_function_eval(__graph_diameter,&_graph_diameter,_graph_diameter_s);
4666 define_unary_function_ptr5(at_graph_diameter,alias_at_graph_diameter,&__graph_diameter,0,true)
4667 
4668 /* USAGE:   dijkstra(G,v,w)
4669  *          dijkstra(G,v,W)
4670  *          dijkstra(G,v)
4671  *
4672  * Returns the cheapest weighted path from vertex v to w in graph G. Output is
4673  * in form [[v1,v2,...,vk],d] where v1,v2,...,vk are vertices along the path
4674  * and d is the weight of the path. If no such path exists, returns
4675  * [[],+infinity]. Also, when list W of vertices is specified, a sequence of
4676  * cheapest paths to vertices from W is returned. If W is omitted, it is
4677  * assumed that W=vertices(G). The Dijkstra's algorithm is used (nonnegative
4678  * weights).
4679  */
_dijkstra(const gen & g,GIAC_CONTEXT)4680 gen _dijkstra(const gen &g,GIAC_CONTEXT) {
4681     if (g.type==_STRNG && g.subtype==-1) return g;
4682     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4683         return gentypeerr(contextptr);
4684     vecteur &gv=*g._VECTptr;
4685     if (gv.size()<2)
4686         return gensizeerr(contextptr);
4687     graphe G(contextptr);
4688     if (!G.read_gen(gv.front()))
4689         return gt_err(_GT_ERR_NOT_A_GRAPH);
4690     if (!G.is_weighted())
4691         return gt_err(_GT_ERR_WEIGHTED_GRAPH_REQUIRED);
4692     int v,i;
4693     if ((v=G.node_index(gv[1]))<0)
4694         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
4695     int n=G.node_count();
4696     vecteur V,path_weights,paths;
4697     graphe::ivector dest;
4698     if (gv.size()==2) {
4699         V=G.vertices();
4700         dest.resize(n);
4701         for (i=0;i<n;++i) {
4702             dest[i]=i;
4703         }
4704     } else {
4705         if (gv[2].type==_VECT)
4706             V=*gv[2]._VECTptr;
4707         else V.push_back(gv[2]);
4708         dest.resize(V.size());
4709         i=0;
4710         for (const_iterateur it=V.begin();it!=V.end();++it) {
4711             if ((dest[i++]=G.node_index(*it))<0)
4712                 return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
4713         }
4714     }
4715     graphe::ivectors cheapest_paths;
4716     G.dijkstra(v,dest,path_weights,&cheapest_paths);
4717     paths.resize(dest.size());
4718     i=0;
4719     for (graphe::ivectors_iter it=cheapest_paths.begin();it!=cheapest_paths.end();++it) {
4720         vecteur &path=*(paths[i++]=vecteur(it->size()))._VECTptr;
4721         for (graphe::ivector_iter jt=it->begin();jt!=it->end();++jt) {
4722             path[jt-it->begin()]=G.node_label(*jt);
4723         }
4724     }
4725     if (gv.size()>2 && gv[2].type!=_VECT) {
4726         gen &w=path_weights.front();
4727         return makevecteur(is_inf(w)?vecteur(0):paths.front(),w);
4728     }
4729     vecteur res(dest.size());
4730     i=0;
4731     for (const_iterateur it=paths.begin();it!=paths.end();++it) {
4732         vecteur &path=*(it->_VECTptr);
4733         gen &w=path_weights[i];
4734         res[i++]=makevecteur(is_inf(w)?vecteur(0):path,w);
4735     }
4736     return change_subtype(res,_SEQ__VECT);
4737 }
4738 static const char _dijkstra_s[]="dijkstra";
4739 static define_unary_function_eval(__dijkstra,&_dijkstra,_dijkstra_s);
4740 define_unary_function_ptr5(at_dijkstra,alias_at_dijkstra,&__dijkstra,0,true)
4741 
4742 /* USAGE:   bellman_ford(G,v,w)
4743  *          bellman_ford(G,v,W)
4744  *          bellman_ford(G,v)
4745  *
4746  * Returns the cheapest weighted path from vertex v to w in graph G. Output is
4747  * in form [[v1,v2,...,vk],d] where v1,v2,...,vk are vertices along the path
4748  * and d is the weight of the path. If no such path exists, returns
4749  * [[],+infinity]. Also, when list W of vertices is specified, a sequence of
4750  * cheapest paths to vertices from W is returned. If W is omitted, it is
4751  * assumed that W=vertices(G). The Bellman-Ford algorithm is used, which
4752  * accepts negative weights but fails on graphs containing negative cycles.
4753  */
_bellman_ford(const gen & g,GIAC_CONTEXT)4754 gen _bellman_ford(const gen &g,GIAC_CONTEXT) {
4755     if (g.type==_STRNG && g.subtype==-1) return g;
4756     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
4757         return gentypeerr(contextptr);
4758     vecteur &gv=*g._VECTptr;
4759     if (gv.size()<2)
4760         return gensizeerr(contextptr);
4761     graphe G(contextptr);
4762     if (!G.read_gen(gv.front()))
4763         return gt_err(_GT_ERR_NOT_A_GRAPH);
4764     if (!G.is_weighted())
4765         return gt_err(_GT_ERR_WEIGHTED_GRAPH_REQUIRED);
4766     int v,i;
4767     if ((v=G.node_index(gv[1]))<0)
4768         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
4769     int n=G.node_count();
4770     vecteur V,path_weights,paths;
4771     graphe::ivector dest;
4772     if (gv.size()==2) {
4773         V=G.vertices();
4774         dest.resize(n);
4775         for (i=0;i<n;++i) {
4776             dest[i]=i;
4777         }
4778     } else {
4779         if (gv[2].type==_VECT)
4780             V=*gv[2]._VECTptr;
4781         else V.push_back(gv[2]);
4782         dest.resize(V.size());
4783         i=0;
4784         for (const_iterateur it=V.begin();it!=V.end();++it) {
4785             if ((dest[i++]=G.node_index(*it))<0)
4786                 return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
4787         }
4788     }
4789     graphe::ivectors cheapest_paths;
4790     if (!G.bellman_ford(v,dest,path_weights,&cheapest_paths)) {
4791         *logptr(contextptr) << "Error: graph contains a negative-weight cycle\n";
4792         return vecteur(0);
4793     }
4794     paths.resize(dest.size());
4795     i=0;
4796     for (graphe::ivectors_iter it=cheapest_paths.begin();it!=cheapest_paths.end();++it) {
4797         vecteur &path=*(paths[i++]=vecteur(it->size()))._VECTptr;
4798         for (graphe::ivector_iter jt=it->begin();jt!=it->end();++jt) {
4799             path[jt-it->begin()]=G.node_label(*jt);
4800         }
4801     }
4802     if (gv.size()>2 && gv[2].type!=_VECT) {
4803         gen &w=path_weights.front();
4804         return makevecteur(is_inf(w)?vecteur(0):paths.front(),w);
4805     }
4806     vecteur res(dest.size());
4807     i=0;
4808     for (const_iterateur it=paths.begin();it!=paths.end();++it) {
4809         vecteur &path=*(it->_VECTptr);
4810         gen &w=path_weights[i];
4811         res[i++]=makevecteur(is_inf(w)?vecteur(0):path,w);
4812     }
4813     return change_subtype(res,_SEQ__VECT);
4814 }
4815 static const char _bellman_ford_s[]="bellman_ford";
4816 static define_unary_function_eval(__bellman_ford,&_bellman_ford,_bellman_ford_s);
4817 define_unary_function_ptr5(at_bellman_ford,alias_at_bellman_ford,&__bellman_ford,0,true)
4818 
4819 /* USAGE:   topologic_sort(G)
4820  *
4821  * Returns the list of vertices sorted according to the topological ordering in
4822  * a directed acyclic graph G.
4823  */
_topologic_sort(const gen & g,GIAC_CONTEXT)4824 gen _topologic_sort(const gen &g,GIAC_CONTEXT) {
4825     if (g.type==_STRNG && g.subtype==-1) return g;
4826     graphe G(contextptr);
4827     if (!G.read_gen(g))
4828         return gt_err(_GT_ERR_NOT_A_GRAPH);
4829     if (!G.is_directed())
4830         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
4831     graphe::ivector ordering;
4832     if (!G.topologic_sort(ordering))
4833         return gt_err(_GT_ERR_NOT_ACYCLIC_GRAPH);
4834     vecteur res(ordering.size());
4835     for (graphe::ivector_iter it=ordering.begin();it!=ordering.end();++it) {
4836         res[it-ordering.begin()]=G.node_label(*it);
4837     }
4838     return res;
4839 }
4840 static const char _topologic_sort_s[]="topologic_sort";
4841 static define_unary_function_eval(__topologic_sort,&_topologic_sort,_topologic_sort_s);
4842 define_unary_function_ptr5(at_topologic_sort,alias_at_topologic_sort,&__topologic_sort,0,true)
4843 
4844 static const char _topological_sort_s[]="topological_sort";
4845 static define_unary_function_eval(__topological_sort,&_topologic_sort,_topological_sort_s);
4846 define_unary_function_ptr5(at_topological_sort,alias_at_topological_sort,&__topological_sort,0,true)
4847 
4848 /* USAGE:   is_acyclic(G)
4849  *
4850  * Returns true iff the directed graph G is acyclic, i.e. has no topological
4851  * ordering.
4852  */
_is_acyclic(const gen & g,GIAC_CONTEXT)4853 gen _is_acyclic(const gen &g,GIAC_CONTEXT) {
4854     if (g.type==_STRNG && g.subtype==-1) return g;
4855     graphe G(contextptr,false);
4856     if (!G.read_gen(g))
4857         return gt_err(_GT_ERR_NOT_A_GRAPH);
4858     if (!G.is_directed())
4859         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
4860     graphe::ivector ordering;
4861     return graphe::boole(G.topologic_sort(ordering));
4862 }
4863 static const char _is_acyclic_s[]="is_acyclic";
4864 static define_unary_function_eval(__is_acyclic,&_is_acyclic,_is_acyclic_s);
4865 define_unary_function_ptr5(at_is_acyclic,alias_at_is_acyclic,&__is_acyclic,0,true)
4866 
4867 /* USAGE:   is_clique(G)
4868  *
4869  * Returns true iff graph G is a clique (i.e. a complete graph).
4870  */
_is_clique(const gen & g,GIAC_CONTEXT)4871 gen _is_clique(const gen &g,GIAC_CONTEXT) {
4872     if (g.type==_STRNG && g.subtype==-1) return g;
4873     graphe G(contextptr,false);
4874     if (!G.read_gen(g))
4875         return gt_err(_GT_ERR_NOT_A_GRAPH);
4876     if (G.is_directed())
4877         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
4878     return graphe::boole(G.is_clique());
4879 }
4880 static const char _is_clique_s[]="is_clique";
4881 static define_unary_function_eval(__is_clique,&_is_clique,_is_clique_s);
4882 define_unary_function_ptr5(at_is_clique,alias_at_is_clique,&__is_clique,0,true)
4883 
4884 /* USAGE:   maximum_clique(G)
4885  *
4886  * Returns maximum clique of undirected graph G as a list of vertices.
4887  */
_maximum_clique(const gen & g,GIAC_CONTEXT)4888 gen _maximum_clique(const gen &g,GIAC_CONTEXT) {
4889     if (g.type==_STRNG && g.subtype==-1) return g;
4890     graphe G(contextptr);
4891     if (!G.read_gen(g))
4892         return gt_err(_GT_ERR_NOT_A_GRAPH);
4893     if (G.is_directed())
4894         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
4895     graphe::ivector clique;
4896     G.maximum_clique(clique);
4897     vecteur res=G.get_node_labels(clique);
4898     return _sort(res,contextptr);
4899 }
4900 static const char _maximum_clique_s[]="maximum_clique";
4901 static define_unary_function_eval(__maximum_clique,&_maximum_clique,_maximum_clique_s);
4902 define_unary_function_ptr5(at_maximum_clique,alias_at_maximum_clique,&__maximum_clique,0,true)
4903 
4904 /* USAGE:   clique_number(G)
4905  *
4906  * Returns the clique number of graph G, which is equal to the size of maximum
4907  * clique.
4908  */
_clique_number(const gen & g,GIAC_CONTEXT)4909 gen _clique_number(const gen &g,GIAC_CONTEXT) {
4910     if (g.type==_STRNG && g.subtype==-1) return g;
4911     graphe G(contextptr,false);
4912     if (!G.read_gen(g))
4913         return gt_err(_GT_ERR_NOT_A_GRAPH);
4914     if (G.is_directed())
4915         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
4916     graphe::ivector clique;
4917     return G.maximum_clique(clique);
4918 }
4919 static const char _clique_number_s[]="clique_number";
4920 static define_unary_function_eval(__clique_number,&_clique_number,_clique_number_s);
4921 define_unary_function_ptr5(at_clique_number,alias_at_clique_number,&__clique_number,0,true)
4922 
4923 /* USAGE:   clique_cover(G,[k])
4924  *
4925  * Returns a clique vertex cover of graph G [containing at most k cliques].
4926  */
_clique_cover(const gen & g,GIAC_CONTEXT)4927 gen _clique_cover(const gen &g,GIAC_CONTEXT) {
4928     if (g.type==_STRNG && g.subtype==-1) return g;
4929     int k=0;
4930     if (g.type==_VECT && g.subtype==_SEQ__VECT) {
4931         if (g._VECTptr->size()!=2)
4932             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
4933         if (!g._VECTptr->back().is_integer() || (k=g._VECTptr->back().val)<1)
4934             return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
4935     }
4936     graphe G(contextptr);
4937     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
4938         return gt_err(_GT_ERR_NOT_A_GRAPH);
4939     if (G.is_null())
4940         return vecteur(0);
4941     if (G.is_directed())
4942         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
4943     graphe::ivectors cover;
4944     if (!G.clique_cover(cover,k))
4945         return vecteur(0);
4946     vecteur res;
4947     G.ivectors2vecteur(cover,res,true);
4948     return change_subtype(res,_LIST__VECT);
4949 }
4950 static const char _clique_cover_s[]="clique_cover";
4951 static define_unary_function_eval(__clique_cover,&_clique_cover,_clique_cover_s);
4952 define_unary_function_ptr5(at_clique_cover,alias_at_clique_cover,&__clique_cover,0,true)
4953 
4954 /* USAGE:   clique_cover_number(G)
4955  *
4956  * Returns the clique cover number of graph G (i.e. the chromatic number of the
4957  * graph complement of G).
4958  */
_clique_cover_number(const gen & g,GIAC_CONTEXT)4959 gen _clique_cover_number(const gen &g,GIAC_CONTEXT) {
4960     if (g.type==_STRNG && g.subtype==-1) return g;
4961     graphe G(contextptr,false);
4962     if (!G.read_gen(g))
4963         return gt_err(_GT_ERR_NOT_A_GRAPH);
4964     if (G.is_null())
4965         return 0;
4966     if (G.is_directed())
4967         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
4968     graphe::ivectors cover;
4969     G.clique_cover(cover);
4970     int ncov=cover.size();
4971     if (ncov==0)
4972         return undef;
4973     return ncov;
4974 }
4975 static const char _clique_cover_number_s[]="clique_cover_number";
4976 static define_unary_function_eval(__clique_cover_number,&_clique_cover_number,_clique_cover_number_s);
4977 define_unary_function_ptr5(at_clique_cover_number,alias_at_clique_cover_number,&__clique_cover_number,0,true)
4978 
4979 /* USAGE:   chromatic_number(G,[interval or approx || cols])
4980  *
4981  * Returns the chromatic number of the input graph G. If "interval" or "approx" parameter
4982  * is given, the bounds are returned as an interval. If an identifier cols is
4983  * given, the coloring will be stored to it.
4984  */
_chromatic_number(const gen & g,GIAC_CONTEXT)4985 gen _chromatic_number(const gen &g,GIAC_CONTEXT) {
4986     if (g.type==_STRNG && g.subtype==-1) return g;
4987     if (g.type!=_VECT)
4988         return gentypeerr(contextptr);
4989     gen colors_dest=undef;
4990     bool only_provide_bounds=false;
4991     if (g.subtype==_SEQ__VECT) {
4992         vecteur &gv=*g._VECTptr;
4993         if (gv.size()!=2 || gv.size()>3)
4994             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
4995         gen &opt=g._VECTptr->back();
4996         if (opt==at_interval || opt==at_approx)
4997             only_provide_bounds=true;
4998         else if (opt.type==_IDNT)
4999             colors_dest=opt;
5000         else return gentypeerr(contextptr);
5001     }
5002     graphe G(contextptr,false);
5003     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
5004         return gt_err(_GT_ERR_NOT_A_GRAPH);
5005     if (G.is_directed())
5006         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
5007     if (only_provide_bounds) {
5008         graphe::ipair bounds=G.chromatic_number_bounds();
5009         return symbolic(at_interval,makesequence(bounds.first,bounds.second));
5010     }
5011     int ncolors=G.exact_vertex_coloring();
5012     if (ncolors==0)
5013         return undef;
5014     if (!is_undef(colors_dest)) { // store the coloring
5015         graphe::ivector colors;
5016         G.get_node_colors(colors);
5017         identifier_assign(*colors_dest._IDNTptr,vector_int_2_vecteur(colors),contextptr);
5018     }
5019     return ncolors;
5020 }
5021 static const char _chromatic_number_s[]="chromatic_number";
5022 static define_unary_function_eval(__chromatic_number,&_chromatic_number,_chromatic_number_s);
5023 define_unary_function_ptr5(at_chromatic_number,alias_at_chromatic_number,&__chromatic_number,0,true)
5024 
5025 /* USAGE:   maximum_independent_set(G)
5026  *
5027  * Returns the maximum independent vertex set of graph G (i.e. maximum clique
5028  * of the graph complement of G).
5029  */
_maximum_independent_set(const gen & g,GIAC_CONTEXT)5030 gen _maximum_independent_set(const gen &g,GIAC_CONTEXT) {
5031     if (g.type==_STRNG && g.subtype==-1) return g;
5032     graphe G(contextptr),C(contextptr);
5033     if (!G.read_gen(g))
5034         return gt_err(_GT_ERR_NOT_A_GRAPH);
5035     G.complement(C);
5036     graphe::ivector clique;
5037     C.maximum_clique(clique);
5038     vecteur res=C.get_node_labels(clique);
5039     return _sort(res,contextptr);
5040 }
5041 static const char _maximum_independent_set_s[]="maximum_independent_set";
5042 static define_unary_function_eval(__maximum_independent_set,&_maximum_independent_set,_maximum_independent_set_s);
5043 define_unary_function_ptr5(at_maximum_independent_set,alias_at_maximum_independent_set,&__maximum_independent_set,0,true)
5044 
5045 /* USAGE:   independence_number(G)
5046  *
5047  * Returns the independence number of graph G (i.e. the size of maximum
5048  * independent set).
5049  */
_independence_number(const gen & g,GIAC_CONTEXT)5050 gen _independence_number(const gen &g,GIAC_CONTEXT) {
5051     if (g.type==_STRNG && g.subtype==-1) return g;
5052     graphe G(contextptr,false),C(contextptr,false);
5053     if (!G.read_gen(g))
5054         return gt_err(_GT_ERR_NOT_A_GRAPH);
5055     G.complement(C);
5056     graphe::ivector clique;
5057     return C.maximum_clique(clique);
5058 }
5059 static const char _independence_number_s[]="independence_number";
5060 static define_unary_function_eval(__independence_number,&_independence_number,_independence_number_s);
5061 define_unary_function_ptr5(at_independence_number,alias_at_independence_number,&__independence_number,0,true)
5062 
5063 /* USAGE:   strongly_connected_components(G)
5064  *
5065  * Returns the list of strongly connected components of directed graph G.
5066  */
_strongly_connected_components(const gen & g,GIAC_CONTEXT)5067 gen _strongly_connected_components(const gen &g,GIAC_CONTEXT) {
5068     if (g.type==_STRNG && g.subtype==-1) return g;
5069     graphe G(contextptr);
5070     if (!G.read_gen(g))
5071         return gt_err(_GT_ERR_NOT_A_GRAPH);
5072     if (!G.is_directed())
5073         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
5074     graphe::ivectors comp;
5075     G.strongly_connected_components(comp);
5076     vecteur res(comp.size());
5077     G.ivectors2vecteur(comp,res,true);
5078     return change_subtype(res,_LIST__VECT);
5079 }
5080 static const char _strongly_connected_components_s[]="strongly_connected_components";
5081 static define_unary_function_eval(__strongly_connected_components,&_strongly_connected_components,_strongly_connected_components_s);
5082 define_unary_function_ptr5(at_strongly_connected_components,alias_at_strongly_connected_components,&__strongly_connected_components,0,true)
5083 
5084 /* USAGE:   condensation(G)
5085  *
5086  * Returns the graph obtained by contracting strongly connected components of
5087  * the digraph G to single vertices.
5088  */
_condensation(const gen & g,GIAC_CONTEXT)5089 gen _condensation(const gen &g,GIAC_CONTEXT) {
5090     if (g.type==_STRNG && g.subtype==-1) return g;
5091     graphe G(contextptr),H(contextptr);
5092     if (!G.read_gen(g))
5093         return gt_err(_GT_ERR_NOT_A_GRAPH);
5094     if (!G.is_directed())
5095         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
5096     G.condensation(H);
5097     return H.to_gen();
5098 }
5099 static const char _condensation_s[]="condensation";
5100 static define_unary_function_eval(__condensation,&_condensation,_condensation_s);
5101 define_unary_function_ptr5(at_condensation,alias_at_condensation,&__condensation,0,true)
5102 
5103 /* USAGE:   is_strongly_connected(G)
5104  *
5105  * Returns true iff the directed graph G is strongly connected.
5106  */
_is_strongly_connected(const gen & g,GIAC_CONTEXT)5107 gen _is_strongly_connected(const gen &g,GIAC_CONTEXT) {
5108     if (g.type==_STRNG && g.subtype==-1) return g;
5109     graphe G(contextptr,false);
5110     if (!G.read_gen(g))
5111         return gt_err(_GT_ERR_NOT_A_GRAPH);
5112     if (!G.is_directed())
5113         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
5114     graphe::ivectors components;
5115     G.strongly_connected_components(components);
5116     return graphe::boole(components.size()==1);
5117 }
5118 static const char _is_strongly_connected_s[]="is_strongly_connected";
5119 static define_unary_function_eval(__is_strongly_connected,&_is_strongly_connected,_is_strongly_connected_s);
5120 define_unary_function_ptr5(at_is_strongly_connected,alias_at_is_strongly_connected,&__is_strongly_connected,0,true)
5121 
5122 /* USAGE:   degree_sequence(G)
5123  *
5124  * Returns the list of degrees of vertices of graph G (arc directions are
5125  * ignored).
5126  */
_degree_sequence(const gen & g,GIAC_CONTEXT)5127 gen _degree_sequence(const gen &g,GIAC_CONTEXT) {
5128     if (g.type==_STRNG && g.subtype==-1) return g;
5129     graphe G(contextptr,false);
5130     if (!G.read_gen(g))
5131         return gt_err(_GT_ERR_NOT_A_GRAPH);
5132     return G.degree_sequence();
5133 }
5134 static const char _degree_sequence_s[]="degree_sequence";
5135 static define_unary_function_eval(__degree_sequence,&_degree_sequence,_degree_sequence_s);
5136 define_unary_function_ptr5(at_degree_sequence,alias_at_degree_sequence,&__degree_sequence,0,true)
5137 
5138 /* USAGE:   is_graphic_sequence(L)
5139  *
5140  * Returns true iff there exists a graph with degree sequence equal to the list
5141  * L. The algorithm is based on Erdos-Gallai theorem.
5142  */
_is_graphic_sequence(const gen & g,GIAC_CONTEXT)5143 gen _is_graphic_sequence(const gen &g,GIAC_CONTEXT) {
5144     if (g.type==_STRNG && g.subtype==-1) return g;
5145     if (g.type!=_VECT)
5146         return gentypeerr(contextptr);
5147     int n=g._VECTptr->size();
5148     for (const_iterateur it=g._VECTptr->begin();it!=g._VECTptr->end();++it) {
5149         if (!it->is_integer() || !is_positive(*it,contextptr) || !is_strictly_greater(n,*it,contextptr))
5150             return graphe::FAUX;
5151     }
5152     return graphe::boole(graphe::is_graphic_sequence(vecteur_2_vector_int(*g._VECTptr)));
5153 }
5154 static const char _is_graphic_sequence_s[]="is_graphic_sequence";
5155 static define_unary_function_eval(__is_graphic_sequence,&_is_graphic_sequence,_is_graphic_sequence_s);
5156 define_unary_function_ptr5(at_is_graphic_sequence,alias_at_is_graphic_sequence,&__is_graphic_sequence,0,true)
5157 
5158 /* USAGE:   sequence_graph(L)
5159  *
5160  * Returns an undirected graph with the degree sequence equal to the list L, as
5161  * constructed by Havel-Hakimi algorithm.
5162  */
_sequence_graph(const gen & g,GIAC_CONTEXT)5163 gen _sequence_graph(const gen &g,GIAC_CONTEXT) {
5164     if (g.type==_STRNG && g.subtype==-1) return g;
5165     if (g.type!=_VECT)
5166         return gentypeerr(contextptr);
5167     int n=g._VECTptr->size();
5168     graphe::ivector deg(n);
5169     for (const_iterateur it=g._VECTptr->begin();it!=g._VECTptr->end();++it) {
5170         if (!it->is_integer() || !is_positive(*it,contextptr) || !is_strictly_greater(n,*it,contextptr))
5171             return gt_err(_GT_ERR_NOT_A_GRAPHIC_SEQUENCE);
5172         deg[it-g._VECTptr->begin()]=it->val;
5173     }
5174     graphe G(contextptr);
5175     vecteur V;
5176     G.make_default_labels(V,deg.size());
5177     G.reserve_nodes(deg.size());
5178     G.add_nodes(V);
5179     if (!G.hakimi(deg))
5180         return gt_err(_GT_ERR_NOT_A_GRAPHIC_SEQUENCE);
5181     return G.to_gen();
5182 }
5183 static const char _sequence_graph_s[]="sequence_graph";
5184 static define_unary_function_eval(__sequence_graph,&_sequence_graph,_sequence_graph_s);
5185 define_unary_function_ptr5(at_sequence_graph,alias_at_sequence_graph,&__sequence_graph,0,true)
5186 
5187 /* USAGE:   girth(G)
5188  *
5189  * Returns the girth of undirected and unweighted graph G (i.e. the length of
5190  * the shortest cycle in G).
5191  */
_girth(const gen & g,GIAC_CONTEXT)5192 gen _girth(const gen &g,GIAC_CONTEXT) {
5193     if (g.type==_STRNG && g.subtype==-1) return g;
5194     graphe G(contextptr,false);
5195     if (!G.read_gen(g))
5196         return gt_err(_GT_ERR_NOT_A_GRAPH);
5197     if (G.is_directed())
5198         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
5199     if (G.is_weighted())
5200         return gt_err(_GT_ERR_UNWEIGHTED_GRAPH_REQUIRED);
5201     int grth=G.girth();
5202     return grth<0?graphe::plusinf():gen(grth);
5203 }
5204 static const char _girth_s[]="girth";
5205 static define_unary_function_eval(__girth,&_girth,_girth_s);
5206 define_unary_function_ptr5(at_girth,alias_at_girth,&__girth,0,true)
5207 
5208 /* USAGE:   odd_girth(G)
5209  *
5210  * Returns the length of the shortest odd cycle in undirected and unweighted
5211  * graph G.
5212  */
_odd_girth(const gen & g,GIAC_CONTEXT)5213 gen _odd_girth(const gen &g,GIAC_CONTEXT) {
5214     if (g.type==_STRNG && g.subtype==-1) return g;
5215     graphe G(contextptr,false);
5216     if (!G.read_gen(g))
5217         return gt_err(_GT_ERR_NOT_A_GRAPH);
5218     if (G.is_directed())
5219         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
5220     if (G.is_weighted())
5221         return gt_err(_GT_ERR_UNWEIGHTED_GRAPH_REQUIRED);
5222     int grth=G.girth(true);
5223     return grth<0?graphe::plusinf():gen(grth);
5224 }
5225 static const char _odd_girth_s[]="odd_girth";
5226 static define_unary_function_eval(__odd_girth,&_odd_girth,_odd_girth_s);
5227 define_unary_function_ptr5(at_odd_girth,alias_at_odd_girth,&__odd_girth,0,true)
5228 
5229 /* USAGE:   is_arborescence(G)
5230  *
5231  * Returns true iff directed and unweighted graph G is an arborescence.
5232  */
_is_arborescence(const gen & g,GIAC_CONTEXT)5233 gen _is_arborescence(const gen &g,GIAC_CONTEXT) {
5234     if (g.type==_STRNG && g.subtype==-1) return g;
5235     graphe G(contextptr,false);
5236     if (!G.read_gen(g))
5237         return gt_err(_GT_ERR_NOT_A_GRAPH);
5238     if (!G.is_directed())
5239         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
5240     return graphe::boole(G.is_arborescence());
5241 }
5242 static const char _is_arborescence_s[]="is_arborescence";
5243 static define_unary_function_eval(__is_arborescence,&_is_arborescence,_is_arborescence_s);
5244 define_unary_function_ptr5(at_is_arborescence,alias_at_is_arborescence,&__is_arborescence,0,true)
5245 
5246 /* USAGE:   graph_spectrum(G)
5247  *
5248  * Returns the graph spectrum of G. The return value is a list of lists with two
5249  * elements, each containing an eigenvalue and its multiplicity.
5250  */
_graph_spectrum(const gen & g,GIAC_CONTEXT)5251 gen _graph_spectrum(const gen &g,GIAC_CONTEXT) {
5252     if (g.type==_STRNG && g.subtype==-1) return g;
5253     graphe G(contextptr,false);
5254     if (!G.read_gen(g))
5255         return gt_err(_GT_ERR_NOT_A_GRAPH);
5256     matrice A,res;
5257     G.adjacency_matrix(A);
5258     vecteur ev=*_eigenvals(A,contextptr)._VECTptr;
5259     gen_map ev_map;
5260     for (const_iterateur it=ev.begin();it!=ev.end();++it) {
5261         ev_map[*it]+=1;
5262     }
5263     for (gen_map::const_iterator it=ev_map.begin();it!=ev_map.end();++it) {
5264         res.push_back(makevecteur(it->first,it->second));
5265     }
5266     return res;
5267 }
5268 static const char _graph_spectrum_s[]="graph_spectrum";
5269 static define_unary_function_eval(__graph_spectrum,&_graph_spectrum,_graph_spectrum_s);
5270 define_unary_function_ptr5(at_graph_spectrum,alias_at_graph_spectrum,&__graph_spectrum,0,true)
5271 
5272 /* USAGE:   seidel_spectrum(G)
5273  *
5274  * Returns the Seidel spectrum of G. The return value is a list of lists with two
5275  * elements, each containing an eigenvalue and its multiplicity.
5276  */
_seidel_spectrum(const gen & g,GIAC_CONTEXT)5277 gen _seidel_spectrum(const gen &g,GIAC_CONTEXT) {
5278     if (g.type==_STRNG && g.subtype==-1) return g;
5279     graphe G(contextptr,false);
5280     if (!G.read_gen(g))
5281         return gt_err(_GT_ERR_NOT_A_GRAPH);
5282     int n=G.node_count();
5283     matrice A,I,J,res;
5284     G.adjacency_matrix(A);
5285     I=*_idn(n,contextptr)._VECTptr;
5286     J=*_matrix(makesequence(n,n,1),contextptr)._VECTptr;
5287     vecteur ev=*_eigenvals(J-I-A-A,contextptr)._VECTptr;
5288     gen_map ev_map;
5289     for (const_iterateur it=ev.begin();it!=ev.end();++it) {
5290         ev_map[*it]+=1;
5291     }
5292     for (gen_map::const_iterator it=ev_map.begin();it!=ev_map.end();++it) {
5293         res.push_back(makevecteur(it->first,it->second));
5294     }
5295     return res;
5296 }
5297 static const char _seidel_spectrum_s[]="seidel_spectrum";
5298 static define_unary_function_eval(__seidel_spectrum,&_seidel_spectrum,_seidel_spectrum_s);
5299 define_unary_function_ptr5(at_seidel_spectrum,alias_at_seidel_spectrum,&__seidel_spectrum,0,true)
5300 
5301 /* USAGE:   graph_charpoly(G,[x])
5302  *
5303  * Returns the value p(x) of the characteristic polynomial p of an undirected
5304  * graph G. If x is omitted, a list of coefficients of p is returned.
5305  */
_graph_charpoly(const gen & g,GIAC_CONTEXT)5306 gen _graph_charpoly(const gen &g,GIAC_CONTEXT) {
5307     if (g.type==_STRNG && g.subtype==-1) return g;
5308     if (g.type!=_VECT)
5309         return gentypeerr(contextptr);
5310     identificateur x(" x");
5311     gen val(undef);
5312     graphe G(contextptr,false);
5313     if (g.subtype==_SEQ__VECT) {
5314         if (g._VECTptr->size()!=2)
5315             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
5316         val=g._VECTptr->back();
5317     }
5318     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
5319         return gt_err(_GT_ERR_NOT_A_GRAPH);
5320     if (G.is_directed())
5321         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
5322     matrice A;
5323     G.adjacency_matrix(A);
5324     if (is_undef(val))
5325         return _eval(symbolic(at_charpoly,A),contextptr);
5326     gen p=_eval(symbolic(at_charpoly,makesequence(A,x)),contextptr);
5327     return _subs(makesequence(p,x,val),contextptr);
5328 }
5329 static const char _graph_charpoly_s[]="graph_charpoly";
5330 static define_unary_function_eval(__graph_charpoly,&_graph_charpoly,_graph_charpoly_s);
5331 define_unary_function_ptr5(at_graph_charpoly,alias_at_graph_charpoly,&__graph_charpoly,0,true)
5332 
5333 /* USAGE:   is_integer_graph(G)
5334  *
5335  * Returns true iff the spectrum of graph G consists only of integers.
5336  */
_is_integer_graph(const gen & g,GIAC_CONTEXT)5337 gen _is_integer_graph(const gen &g,GIAC_CONTEXT) {
5338     if (g.type==_STRNG && g.subtype==-1) return g;
5339     graphe G(contextptr,false);
5340     if (!G.read_gen(g))
5341         return gt_err(_GT_ERR_NOT_A_GRAPH);
5342     matrice A;
5343     G.adjacency_matrix(A);
5344     return graphe::boole(is_integer_vecteur(*_eigenvals(A,contextptr)._VECTptr,true));
5345 }
5346 static const char _is_integer_graph_s[]="is_integer_graph";
5347 static define_unary_function_eval(__is_integer_graph,&_is_integer_graph,_is_integer_graph_s);
5348 define_unary_function_ptr5(at_is_integer_graph,alias_at_is_integer_graph,&__is_integer_graph,0,true)
5349 
5350 /* USAGE:   spanning_tree(G,[r])
5351  *
5352  * Returns a spanning tree of the undirected graph G [with the vertex r as the
5353  * root node].
5354  */
_spanning_tree(const gen & g,GIAC_CONTEXT)5355 gen _spanning_tree(const gen &g,GIAC_CONTEXT) {
5356     if (g.type==_STRNG && g.subtype==-1) return g;
5357     if (g.type!=_VECT)
5358         return gentypeerr(contextptr);
5359     gen root(undef);
5360     graphe G(contextptr),T(contextptr);
5361     if (g.subtype==_SEQ__VECT) {
5362         if (g._VECTptr->size()!=2)
5363             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
5364         root=g._VECTptr->back();
5365     }
5366     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
5367         return gt_err(_GT_ERR_NOT_A_GRAPH);
5368     if (G.is_directed())
5369         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
5370     int i=is_undef(root)?0:G.node_index(root);
5371     if (i<0)
5372         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
5373     G.spanning_tree(i,T);
5374     return T.to_gen();
5375 }
5376 static const char _spanning_tree_s[]="spanning_tree";
5377 static define_unary_function_eval(__spanning_tree,&_spanning_tree,_spanning_tree_s);
5378 define_unary_function_ptr5(at_spanning_tree,alias_at_spanning_tree,&__spanning_tree,0,true)
5379 
5380 /* USAGE:   number_of_spanning_trees(G)
5381  *
5382  * Returns the number of spanning trees in the undirected graph G if it is
5383  * connected, else return the number of spanning forests.
5384  */
_number_of_spanning_trees(const gen & g,GIAC_CONTEXT)5385 gen _number_of_spanning_trees(const gen &g,GIAC_CONTEXT) {
5386     if (g.type==_STRNG && g.subtype==-1) return g;
5387     graphe G(contextptr,false);
5388     if (!G.read_gen(g))
5389         return gt_err(_GT_ERR_NOT_A_GRAPH);
5390     if (G.is_null())
5391         return gt_err(_GT_ERR_GRAPH_IS_NULL);
5392     if (G.is_directed())
5393         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
5394     if (!G.is_connected()) {
5395         gen res(1);
5396         graphe C(contextptr,false);
5397         graphe::ivectors comp;
5398         G.connected_components(comp);
5399         for (graphe::ivectors_iter it=comp.begin();it!=comp.end();++it) {
5400             G.induce_subgraph(*it,C);
5401             res=res*count_spanning_trees(C);
5402         }
5403         return res;
5404     }
5405     return count_spanning_trees(G);
5406 }
5407 static const char _number_of_spanning_trees_s[]="number_of_spanning_trees";
5408 static define_unary_function_eval(__number_of_spanning_trees,&_number_of_spanning_trees,_number_of_spanning_trees_s);
5409 define_unary_function_ptr5(at_number_of_spanning_trees,alias_at_number_of_spanning_trees,&__number_of_spanning_trees,0,true)
5410 
5411 /* USAGE:   minimal_spanning_tree(G)
5412  *
5413  * Returns the minimal spanning tree of the undirected graph G.
5414  */
_minimal_spanning_tree(const gen & g,GIAC_CONTEXT)5415 gen _minimal_spanning_tree(const gen &g,GIAC_CONTEXT) {
5416     if (g.type==_STRNG && g.subtype==-1) return g;
5417     graphe G(contextptr),T(contextptr);
5418     if (!G.read_gen(g))
5419         return gt_err(_GT_ERR_NOT_A_GRAPH);
5420     if (G.is_directed())
5421         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
5422     if (!G.is_weighted())
5423         G.spanning_tree(0,T);
5424     else
5425         G.minimal_spanning_tree(T);
5426     return T.to_gen();
5427 }
5428 static const char _minimal_spanning_tree_s[]="minimal_spanning_tree";
5429 static define_unary_function_eval(__minimal_spanning_tree,&_minimal_spanning_tree,_minimal_spanning_tree_s);
5430 define_unary_function_ptr5(at_minimal_spanning_tree,alias_at_minimal_spanning_tree,&__minimal_spanning_tree,0,true)
5431 
5432 /* USAGE:   graph_rank(G,[E])
5433  *
5434  * Returns the graph rank of G. If optional set E of edges is given, rank of the
5435  * spanning subgraph of G with edge set E is returned.
5436  */
_graph_rank(const gen & g,GIAC_CONTEXT)5437 gen _graph_rank(const gen &g,GIAC_CONTEXT) {
5438     if (g.type==_STRNG && g.subtype==-1) return g;
5439     if (g.type!=_VECT)
5440         return gentypeerr(contextptr);
5441     vecteur E;
5442     if (g.subtype==_SEQ__VECT) {
5443         if (g._VECTptr->size()!=2)
5444             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
5445         if (g._VECTptr->back().type!=_VECT)
5446             return gentypeerr(contextptr);
5447         E=*g._VECTptr->back()._VECTptr;
5448     }
5449     graphe G(contextptr);
5450     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
5451         return gt_err(_GT_ERR_NOT_A_GRAPH);
5452     if (E.empty())
5453         return G.node_count()-G.connected_component_count();
5454     else {
5455         graphe::ipairs ev;
5456         bool notfound=false;
5457         if (!G.edges2ipairs(E,ev,notfound))
5458             return notfound?gt_err(_GT_ERR_EDGE_NOT_FOUND):gentypeerr(contextptr);
5459         G.set_subgraph(ev,1);
5460         return G.subgraph_size(1)-G.connected_component_count(1);
5461     }
5462 }
5463 static const char _graph_rank_s[]="graph_rank";
5464 static define_unary_function_eval(__graph_rank,&_graph_rank,_graph_rank_s);
5465 define_unary_function_ptr5(at_graph_rank,alias_at_graph_rank,&__graph_rank,0,true)
5466 
5467 /* USAGE:   lowest_common_ancestor(T,r,u,v)
5468  *          lowest_common_ancestor(T,r,L)
5469  *
5470  * Returns the lowest common ancestor of the nodes u,v in the tree T with
5471  * respect to the root node r. If a list L of node pairs is given, common
5472  * ancestors of all pairs are returned in a list. Tarjan's offline algorithm is
5473  * used.
5474  */
_lowest_common_ancestor(const gen & g,GIAC_CONTEXT)5475 gen _lowest_common_ancestor(const gen &g,GIAC_CONTEXT) {
5476     if (g.type==_STRNG && g.subtype==-1) return g;
5477     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
5478         return gentypeerr(contextptr);
5479     vecteur &gv=*g._VECTptr;
5480     if (gv.size()<3)
5481         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
5482     graphe G(contextptr);
5483     if (!G.read_gen(gv.front()))
5484         return gt_err(_GT_ERR_NOT_A_GRAPH);
5485     if (!G.is_tree())
5486         return gt_err(_GT_ERR_NOT_A_TREE);
5487     int r=G.node_index(gv[1]);
5488     if (r<0)
5489         return gt_err(gv[1],_GT_ERR_VERTEX_NOT_FOUND);
5490     if (gv.size()==4) {
5491         int i=G.node_index(gv[2]),j=G.node_index(gv[3]);
5492         if (i<0 || j<0)
5493             return gt_err(i<0?gv[1]:gv[2],_GT_ERR_VERTEX_NOT_FOUND);
5494         return G.node_label(G.lowest_common_ancestor(i,j,r));
5495     } else if (gv.size()==3) {
5496         if (!ckmatrix(gv.back()))
5497             return gentypeerr(contextptr);
5498         if (gv.back()._VECTptr->front()._VECTptr->size()!=2)
5499             return gendimerr(contextptr);
5500         graphe::ipairs p;
5501         matrice &m=*gv.back()._VECTptr;
5502         int n=m.size(),i,j;
5503         for (int k=0;k<n;++k) {
5504             vecteur &row=*m[k]._VECTptr;
5505             i=G.node_index(row.front());
5506             j=G.node_index(row.back());
5507             if (i<0 || j<0)
5508                 return gt_err(i<0?row.front():row.back(),_GT_ERR_VERTEX_NOT_FOUND);
5509             p.push_back(make_pair(i,j));
5510         }
5511         graphe::ivector lca;
5512         G.lowest_common_ancestors(r,p,lca);
5513         return G.get_node_labels(lca);
5514     } else return gensizeerr(contextptr);
5515 }
5516 static const char _lowest_common_ancestor_s[]="lowest_common_ancestor";
5517 static define_unary_function_eval(__lowest_common_ancestor,&_lowest_common_ancestor,_lowest_common_ancestor_s);
5518 define_unary_function_ptr5(at_lowest_common_ancestor,alias_at_lowest_common_ancestor,&__lowest_common_ancestor,0,true)
5519 
5520 /* USAGE:   st_ordering(G,s,t,[D],[p])
5521  *
5522  * Returns ST numbering for the biconnected graph G with source s and sink
5523  * (target) t. If an identifier D is given, the acyclic directed graph defined
5524  * by the ordering will ber constructed and stored to it. If a value of the
5525  * parameter p in [0,1] is given, the longest path in the resulting DAG will be
5526  * approcimately equal to L*p, where L is the length of the longest possible
5527  * (Hamiltonian) path.
5528  */
_st_ordering(const gen & g,GIAC_CONTEXT)5529 gen _st_ordering(const gen &g,GIAC_CONTEXT) {
5530     if (g.type==_STRNG && g.subtype==-1) return g;
5531     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
5532         return gentypeerr(contextptr);
5533     vecteur &gv=*g._VECTptr;
5534     if (gv.size()<3 && gv.size()>5)
5535         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
5536     graphe G(contextptr);
5537     if (!G.read_gen(gv.front()))
5538         return gt_err(_GT_ERR_NOT_A_GRAPH);
5539     if (!G.is_biconnected())
5540         return gt_err(_GT_ERR_BICONNECTED_GRAPH_REQUIRED);
5541     int s=G.node_index(gv[1]),t=G.node_index(gv[2]);
5542     if (s<0 || t<0)
5543         return gt_err(s<0?gv[1]:gv[2],_GT_ERR_VERTEX_NOT_FOUND);
5544     if (!G.has_edge(s,t))
5545         return gt_err(makevecteur(gv[1],gv[2]),_GT_ERR_EDGE_NOT_FOUND);
5546     double p=-1;
5547     gen gp;
5548     if ((gp=_evalf(gv.back(),contextptr)).type==_DOUBLE_) {
5549         p=gp.DOUBLE_val();
5550         if (p<0 || p>1)
5551             return generr("0<=p<=1 is required");
5552     }
5553     if (p<0)
5554         G.compute_st_numbering(s,t);
5555     else G.parametrized_st_orientation(s,t,p);
5556     vecteur st=G.get_st_numbering();
5557     if ((gv.size()==4 && p<0) || (gv.size()==5 && p>=0)) {
5558         if (gv[3].type!=_IDNT)
5559             return generrtype("Expected an identifier");
5560         G.assign_edge_directions_from_st();
5561         identifier_assign(*gv[3]._IDNTptr,G.to_gen(),contextptr);
5562     }
5563     return st;
5564 }
5565 static const char _st_ordering_s[]="st_ordering";
5566 static define_unary_function_eval(__st_ordering,&_st_ordering,_st_ordering_s);
5567 define_unary_function_ptr5(at_st_ordering,alias_at_st_ordering,&__st_ordering,0,true)
5568 
5569 /* USAGE:   greedy_color(G,[p])
5570  *
5571  * Returns the list of vertex colors (positive integers) obtained by coloring
5572  * vertices one at a time [in the order given by permutation p], assigning to
5573  * it the smallest available color.
5574  */
_greedy_color(const gen & g,GIAC_CONTEXT)5575 gen _greedy_color(const gen &g,GIAC_CONTEXT) {
5576     if (g.type==_STRNG && g.subtype==-1) return g;
5577     if (g.type!=_VECT)
5578         return gentypeerr(contextptr);
5579     graphe::ivector p,colors;
5580     if (g.subtype==_SEQ__VECT) {
5581         if (g._VECTptr->size()!=2)
5582             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
5583         if (is_zero(_is_permu(g._VECTptr->back(),contextptr)))
5584             return generrtype("Expected a permutation");
5585         p=vecteur_2_vector_int(*g._VECTptr->back()._VECTptr);
5586         int offset=array_start(contextptr);
5587         for (graphe::ivector::iterator it=p.begin();it!=p.end();++it) {
5588             *it-=offset;
5589         }
5590     }
5591     graphe G(contextptr,false);
5592     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
5593         return gt_err(_GT_ERR_NOT_A_GRAPH);
5594     if (p.empty()) { // construct the identity permutation
5595         p.resize(G.node_count());
5596         for (graphe::ivector::iterator it=p.begin();it!=p.end();++it) {
5597             *it=it-p.begin();
5598         }
5599     } else if (G.node_count()!=int(p.size()))
5600         return generr("Permutation size must match the number of vertices");
5601     G.greedy_vertex_coloring(p);
5602     G.get_node_colors(colors);
5603     return vector_int_2_vecteur(colors);
5604 }
5605 static const char _greedy_color_s[]="greedy_color";
5606 static define_unary_function_eval(__greedy_color,&_greedy_color,_greedy_color_s);
5607 define_unary_function_ptr5(at_greedy_color,alias_at_greedy_color,&__greedy_color,0,true)
5608 
5609 /* USAGE:   is_bipartite(G,[P])
5610  *
5611  * Returns true iff the graph G is bipartite [and assign to P the list of partitions
5612  * represented as lists of vertices].
5613  */
_is_bipartite(const gen & g,GIAC_CONTEXT)5614 gen _is_bipartite(const gen &g,GIAC_CONTEXT) {
5615     if (g.type==_STRNG && g.subtype==-1) return g;
5616     if (g.type!=_VECT)
5617         return gentypeerr(contextptr);
5618     gen P=undef;
5619     if (g.subtype==_SEQ__VECT) {
5620         if (g._VECTptr->size()!=2)
5621             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
5622         if ((P=g._VECTptr->back()).type!=_IDNT)
5623             return generrtype("Expected an identifier");
5624     }
5625     graphe G(contextptr);
5626     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
5627         return gt_err(_GT_ERR_NOT_A_GRAPH);
5628     graphe::ivector V1,V2;
5629     if (!G.is_bipartite(V1,V2))
5630         return G.boole(false);
5631     if (!is_undef(P)) {
5632         identifier_assign(*P._IDNTptr,
5633                           makevecteur(_sort(G.get_node_labels(V1),contextptr),
5634                                       _sort(G.get_node_labels(V2),contextptr)),
5635                           contextptr);
5636     }
5637     return G.boole(true);
5638 }
5639 static const char _is_bipartite_s[]="is_bipartite";
5640 static define_unary_function_eval(__is_bipartite,&_is_bipartite,_is_bipartite_s);
5641 define_unary_function_ptr5(at_is_bipartite,alias_at_is_bipartite,&__is_bipartite,0,true)
5642 
5643 /* USAGE:   plane_dual(G)
5644  *          plane_dual(F)
5645  *
5646  * Returns the plane dual of a biconnected planar graph G or constructed from
5647  * the list of faces F.
5648  */
_plane_dual(const gen & g,GIAC_CONTEXT)5649 gen _plane_dual(const gen &g,GIAC_CONTEXT) {
5650     if (g.type==_STRNG && g.subtype==-1) return g;
5651     if (g.type!=_VECT || g.subtype==_SEQ__VECT)
5652         return gentypeerr(contextptr);
5653     graphe::ivectors faces;
5654     graphe D(contextptr);
5655     if (g.subtype==_GRAPH__VECT) {
5656         graphe G(contextptr);
5657         if (!G.read_gen(g))
5658             return gt_err(_GT_ERR_NOT_A_GRAPH);
5659         if (!G.is_biconnected())
5660             return gt_err(_GT_ERR_BICONNECTED_GRAPH_REQUIRED);
5661         if (!G.demoucron(faces))
5662             return gt_err(_GT_ERR_NOT_PLANAR);
5663     } else {
5664         gen_map m;
5665         int k=0;
5666         vecteur &gv=*g._VECTptr;
5667         for (const_iterateur it=gv.begin();it!=gv.end();++it) {
5668             if (it->type!=_VECT)
5669                 return gentypeerr(contextptr);
5670             graphe::ivector face;
5671             for (const_iterateur jt=it->_VECTptr->begin();jt!=it->_VECTptr->end();++jt) {
5672                 if (is_zero(m[*jt]))
5673                     m[*jt]=++k;
5674                 face.push_back(m[*jt].val-1);
5675             }
5676             faces.push_back(face);
5677         }
5678     }
5679     D.make_plane_dual(faces);
5680     return D.to_gen();
5681 }
5682 static const char _plane_dual_s[]="plane_dual";
5683 static define_unary_function_eval(__plane_dual,&_plane_dual,_plane_dual_s);
5684 define_unary_function_ptr5(at_plane_dual,alias_at_plane_dual,&__plane_dual,0,true)
5685 
5686 /* USAGE:   is_vertex_colorable(G,k,[col])
5687  *
5688  * Returns true iff the vertices of graph G can be colored by using at most k
5689  * colors. If true is returned and an identifier col is given, the colors of
5690  * the vertices are stored in it.
5691  */
_is_vertex_colorable(const gen & g,GIAC_CONTEXT)5692 gen _is_vertex_colorable(const gen &g,GIAC_CONTEXT) {
5693     if (g.type==_STRNG && g.subtype==-1) return g;
5694     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
5695         return gentypeerr(contextptr);
5696     vecteur &gv=*g._VECTptr;
5697     if (gv.size()<2 || gv.size()>3)
5698         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
5699     int k;
5700     if (!gv[1].is_integer() || (k=gv[1].val)<1)
5701         return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
5702     gen colors_dest=undef;
5703     if (gv.size()>2) {
5704         if (gv.back().type!=_IDNT)
5705             return generrtype("Expected an identifier");
5706         colors_dest=gv.back();
5707     }
5708     graphe G(contextptr,false);
5709     if (!G.read_gen(gv.front()))
5710         return gt_err(_GT_ERR_NOT_A_GRAPH);
5711     if (!G.is_vertex_colorable(k))
5712         return graphe::boole(false);
5713     if (!is_undef(colors_dest)) {
5714         // store vertex colors to colors_dest
5715         graphe::ivector colors;
5716         G.get_node_colors(colors);
5717         vecteur cols=vector_int_2_vecteur(colors);
5718         identifier_assign(*colors_dest._IDNTptr,cols,contextptr);
5719     }
5720     return graphe::boole(true);
5721 }
5722 static const char _is_vertex_colorable_s[]="is_vertex_colorable";
5723 static define_unary_function_eval(__is_vertex_colorable,&_is_vertex_colorable,_is_vertex_colorable_s);
5724 define_unary_function_ptr5(at_is_vertex_colorable,alias_at_is_vertex_colorable,&__is_vertex_colorable,0,true)
5725 
5726 /* USAGE:   set_vertex_positions(G,vp)
5727  *
5728  * Sets the coordinates, given in the list vp, to the vertices of graph G and
5729  * return the modified copy of G.
5730  */
_set_vertex_positions(const gen & g,GIAC_CONTEXT)5731 gen _set_vertex_positions(const gen &g,GIAC_CONTEXT) {
5732     if (g.type==_STRNG && g.subtype==-1) return g;
5733     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
5734         return gentypeerr(contextptr);
5735     vecteur &gv=*g._VECTptr;
5736     if (gv.size()!=2)
5737         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
5738     if (gv.back().type!=_VECT)
5739         return generrtype("Expected a list of coordinates");
5740     vecteur vp=*_evalf(gv.back(),contextptr)._VECTptr;
5741     graphe G(contextptr);
5742     if (!G.read_gen(gv.front()))
5743         return gt_err(_GT_ERR_NOT_A_GRAPH);
5744     int n,d=0;
5745     if ((n=vp.size())!=G.node_count())
5746         return generr("Number of positions must match the number of vertices");
5747     graphe::layout x(n);
5748     for (int i=0;i<n;++i) {
5749         graphe::gen2point(vp[i],x[i]);
5750         if (d==0)
5751             d=x[i].size();
5752         else if (int(x[i].size())!=d)
5753             return gendimerr(contextptr);
5754     }
5755     G.store_layout(x);
5756     return G.to_gen();
5757 }
5758 static const char _set_vertex_positions_s[]="set_vertex_positions";
5759 static define_unary_function_eval(__set_vertex_positions,&_set_vertex_positions,_set_vertex_positions_s);
5760 define_unary_function_ptr5(at_set_vertex_positions,alias_at_set_vertex_positions,&__set_vertex_positions,0,true)
5761 
5762 /* USAGE:   clique_stats(G,[k or m..n])
5763  *
5764  * Returns the list of numbers of maximal cliques of size s in the graph G for
5765  * each s. If parameter k is given, the number of k-cliques is returned. If an
5766  * interval m..n is given, only cliques with size between m and n (inclusive)
5767  * are counted (m also may be +infinity).
5768  */
_clique_stats(const gen & g,GIAC_CONTEXT)5769 gen _clique_stats(const gen &g,GIAC_CONTEXT) {
5770     if (g.type==_STRNG && g.subtype==-1) return g;
5771     if (g.type!=_VECT)
5772         return gentypeerr(contextptr);
5773     int lb=0,ub=RAND_MAX;
5774     gen dest(undef);
5775     if (g.subtype==_SEQ__VECT) {
5776         int len=g._VECTptr->size();
5777         if (g._VECTptr->back().type==_IDNT) {
5778             dest=g._VECTptr->back();
5779             --len;
5780         }
5781         if (len==2) {
5782             gen &opt=g._VECTptr->at(1);
5783             if (opt.is_integer() && (lb=opt.val)>0)
5784                 ub=opt.val;
5785             else if (opt.is_symb_of_sommet(at_interval)) {
5786                 vecteur &bnds=*opt._SYMBptr->feuille._VECTptr;
5787                 if (!bnds.front().is_integer() ||
5788                         !(bnds.back().is_integer() ||
5789                           (is_inf(bnds.back()) && is_positive(bnds.back(),contextptr))))
5790                     return gentypeerr(contextptr);
5791                 lb=bnds.front().val;
5792                 ub=is_inf(bnds.back())?RAND_MAX:bnds.back().val;
5793                 if (lb<0 || ub<0 || lb>ub)
5794                     return gensizeerr(contextptr);
5795             } else return gentypeerr(contextptr);
5796         } else if (len>2)
5797             return gensizeerr(contextptr);
5798     }
5799     int mode=is_undef(dest)?0:3;
5800     graphe G(contextptr,mode==3);
5801     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
5802         return gt_err(_GT_ERR_NOT_A_GRAPH);
5803     if (G.is_null())
5804         return vecteur(0);
5805     if (G.is_directed())
5806         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
5807     map<int,int> stats,tmp;
5808     G.clique_stats(stats,mode);
5809     if (mode==3 && !stats.empty()) {
5810         const graphe::ivectors &mc=G.maximal_cliques();
5811         vecteur cg;
5812         int sz;
5813         for (graphe::ivectors_iter it=mc.begin();it!=mc.end();++it) {
5814             sz=it->size();
5815             if (sz>=lb && sz<=ub) {
5816                 cg.push_back(G.get_node_labels(*it));
5817                 ++tmp[sz];
5818             }
5819         }
5820         gen_map gm;
5821         for (map<int,int>::const_iterator it=tmp.begin();it!=tmp.end();++it) {
5822             (gm[it->first]=vecteur(0))._VECTptr->reserve(it->second);
5823         }
5824         for (const_iterateur it=cg.begin();it!=cg.end();++it) {
5825             gm[it->_VECTptr->size()]._VECTptr->push_back(*it);
5826         }
5827         cg.clear();
5828         for (gen_map::const_iterator it=gm.begin();it!=gm.end();++it) {
5829             cg.push_back(it->second);
5830         }
5831         assert(!cg.empty());
5832         identifier_assign(*dest._IDNTptr,cg.size()>1?cg:cg.front(),contextptr);
5833     }
5834     if (lb==ub)
5835         return stats[lb];
5836     vecteur res;
5837     for (map<int,int>::const_iterator it=stats.begin();it!=stats.end();++it) {
5838         if (it->first<=ub && it->first>=lb)
5839             res.push_back(makevecteur(it->first,it->second));
5840     }
5841     return res;
5842 }
5843 static const char _clique_stats_s[]="clique_stats";
5844 static define_unary_function_eval(__clique_stats,&_clique_stats,_clique_stats_s);
5845 define_unary_function_ptr5(at_clique_stats,alias_at_clique_stats,&__clique_stats,0,true)
5846 
5847 /* USAGE:   minimal_vertex_coloring(G,[sto])
5848  *
5849  * Computes minimal vertex coloring for graph G and returns the colors in order
5850  * of vertices. If optional parameter "sto" is given, the colors are assigned
5851  * to vertices and the modified copy of G is returned.
5852  */
_minimal_vertex_coloring(const gen & g,GIAC_CONTEXT)5853 gen _minimal_vertex_coloring(const gen &g,GIAC_CONTEXT) {
5854     if (g.type==_STRNG && g.subtype==-1) return g;
5855     if (g.type!=_VECT)
5856         return gentypeerr(contextptr);
5857     bool store=false;
5858     if (g.subtype==_SEQ__VECT) {
5859         if (g._VECTptr->size()!=2)
5860             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
5861         if (g._VECTptr->back()!=at_sto)
5862             return gentypeerr(contextptr);
5863         store=true;
5864     }
5865     graphe G(contextptr);
5866     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
5867         return gt_err(_GT_ERR_NOT_A_GRAPH);
5868     if (G.is_directed())
5869         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
5870     G.exact_vertex_coloring();
5871     graphe::ivector colors;
5872     G.get_node_colors(colors);
5873     vecteur cols=vector_int_2_vecteur(colors);
5874     if (store)
5875         return _highlight_vertex(makesequence(g._VECTptr->front(),G.vertices(),cols),contextptr);
5876     return cols;
5877 }
5878 static const char _minimal_vertex_coloring_s[]="minimal_vertex_coloring";
5879 static define_unary_function_eval(__minimal_vertex_coloring,&_minimal_vertex_coloring,_minimal_vertex_coloring_s);
5880 define_unary_function_ptr5(at_minimal_vertex_coloring,alias_at_minimal_vertex_coloring,&__minimal_vertex_coloring,0,true)
5881 
5882 /* USAGE:   line_graph(G)
5883  *
5884  * Returns the line graph of the undirected input graph G.
5885  */
_line_graph(const gen & g,GIAC_CONTEXT)5886 gen _line_graph(const gen &g,GIAC_CONTEXT) {
5887     if (g.type==_STRNG && g.subtype==-1) return g;
5888     graphe G(contextptr),L(contextptr);
5889     if (!G.read_gen(g))
5890         gt_err(_GT_ERR_NOT_A_GRAPH);
5891     if (G.is_directed())
5892         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
5893     graphe::ipairs E;
5894     G.line_graph(L,E);
5895     return L.to_gen();
5896 }
5897 static const char _line_graph_s[]="line_graph";
5898 static define_unary_function_eval(__line_graph,&_line_graph,_line_graph_s);
5899 define_unary_function_ptr5(at_line_graph,alias_at_line_graph,&__line_graph,0,true)
5900 
5901 /* USAGE:   transitive_closure(G,[weighted[=true or false]])
5902  *
5903  * Returns the [weighted, by default false] transitive closure of the input
5904  * graph G.
5905  */
_transitive_closure(const gen & g,GIAC_CONTEXT)5906 gen _transitive_closure(const gen &g,GIAC_CONTEXT) {
5907     if (g.type==_STRNG && g.subtype==-1) return g;
5908     if (g.type!=_VECT)
5909         return gentypeerr(contextptr);
5910     bool weighted=false;
5911     if (g.subtype==_SEQ__VECT) {
5912         if (g._VECTptr->size()!=2)
5913             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
5914         gen &opt=g._VECTptr->back();
5915         if (opt.is_integer() && opt.val==_GT_WEIGHTED)
5916             weighted=true;
5917         else if (opt.is_symb_of_sommet(at_equal)) {
5918             vecteur &args=*opt._SYMBptr->feuille._VECTptr;
5919             if (!args.front().is_integer() ||
5920                     args.front().val!=_GT_WEIGHTED ||
5921                     !args.back().is_integer())
5922                 return gentypeerr(contextptr);
5923             weighted=(bool)args.back().val;
5924         }
5925     }
5926     graphe G(contextptr),C(contextptr);
5927     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
5928         return gt_err(_GT_ERR_NOT_A_GRAPH);
5929     G.transitive_closure(C,weighted);
5930     return C.to_gen();
5931 }
5932 static const char _transitive_closure_s[]="transitive_closure";
5933 static define_unary_function_eval(__transitive_closure,&_transitive_closure,_transitive_closure_s);
5934 define_unary_function_ptr5(at_transitive_closure,alias_at_transitive_closure,&__transitive_closure,0,true)
5935 
5936 /* USAGE:   is_isomorphic(G1,G2,[isom])
5937  *
5938  * Returns true if the input graphs G1 and G2 are isomorphic, else returns
5939  * false. If an identifier 'isom' is given, the list with pairwise vertex
5940  * matching in G1 and G2 is stored to it.
5941  */
_is_isomorphic(const gen & g,GIAC_CONTEXT)5942 gen _is_isomorphic(const gen &g,GIAC_CONTEXT) {
5943     if (g.type==_STRNG && g.subtype==-1) return g;
5944     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
5945         return gentypeerr(contextptr);
5946     gen isom=undef;
5947     vecteur &gv=*g._VECTptr;
5948     if (gv.size()<2 || gv.size()>3)
5949         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
5950     graphe G1(contextptr),G2(contextptr);
5951     if (!G1.read_gen(gv[0]) || !G2.read_gen(gv[1]))
5952         return gt_err(_GT_ERR_NOT_A_GRAPH);
5953     if (G1.is_directed()!=G2.is_directed())
5954         return graphe::FAUX;
5955     if (gv.size()>2) {
5956         if ((isom=gv.back()).type!=_IDNT)
5957             return generrtype("Expected an identifier");
5958     }
5959     map<int,int> clab;
5960     int res=G1.is_isomorphic(G2,clab);
5961     if (res==0)
5962         return graphe::FAUX;
5963     if (res<0) // nauty not found
5964         return generr("nauty library is required for finding graph isomorphism");
5965     if (!is_undef(isom)) {
5966         vecteur mapping;
5967         int n=G1.node_count();
5968         for (int i=0;i<n;++i) {
5969             mapping.push_back(symbolic(at_equal,G1.node_label(i),G2.node_label(clab[i])));
5970         }
5971         identifier_assign(*isom._IDNTptr,mapping,contextptr);
5972     }
5973     return graphe::VRAI;
5974 }
5975 static const char _is_isomorphic_s[]="is_isomorphic";
5976 static define_unary_function_eval(__is_isomorphic,&_is_isomorphic,_is_isomorphic_s);
5977 define_unary_function_ptr5(at_is_isomorphic,alias_at_is_isomorphic,&__is_isomorphic,0,true)
5978 
5979 /* USAGE:   graph_automorphisms(G)
5980  *
5981  * Returns the sequence of generators of Aut(G), the automorphism group of G.
5982  * Each element is a permutation in form of list of disjoint cycles.
5983  */
_graph_automorphisms(const gen & g,GIAC_CONTEXT)5984 gen _graph_automorphisms(const gen &g,GIAC_CONTEXT) {
5985     if (g.type==_STRNG && g.subtype==-1) return g;
5986     graphe G(contextptr);
5987     if (!G.read_gen(g))
5988         return gt_err(_GT_ERR_NOT_A_GRAPH);
5989     return G.aut_generators();
5990 }
5991 static const char _graph_automorphisms_s[]="graph_automorphisms";
5992 static define_unary_function_eval(__graph_automorphisms,&_graph_automorphisms,_graph_automorphisms_s);
5993 define_unary_function_ptr5(at_graph_automorphisms,alias_at_graph_automorphisms,&__graph_automorphisms,0,true)
5994 
5995 /* USAGE:   canonical_labeling(G)
5996  *
5997  * Returns the permutation representing the canonical labeling of the input
5998  * graph G.
5999  */
_canonical_labeling(const gen & g,GIAC_CONTEXT)6000 gen _canonical_labeling(const gen &g,GIAC_CONTEXT) {
6001     if (g.type==_STRNG && g.subtype==-1) return g;
6002     graphe G(contextptr);
6003     if (!G.read_gen(g))
6004         return gt_err(_GT_ERR_NOT_A_GRAPH);
6005     graphe::ivector sigma;
6006     if (!G.canonical_labeling(sigma)) // nauty not found
6007         return generr("nauty library is required for canonical labeling");
6008     vecteur res(G.node_count());
6009     int ofs=array_start(contextptr);
6010     for (iterateur it=res.begin();it!=res.end();++it) {
6011         *it=sigma[it-res.begin()]+ofs;
6012     }
6013     return res;
6014 }
6015 static const char _canonical_labeling_s[]="canonical_labeling";
6016 static define_unary_function_eval(__canonical_labeling,&_canonical_labeling,_canonical_labeling_s);
6017 define_unary_function_ptr5(at_canonical_labeling,alias_at_canonical_labeling,&__canonical_labeling,0,true)
6018 
6019 /* USAGE:   minimal_edge_coloring(G,[sto])
6020  *
6021  * Finds the minimal edge coloring of the input graph G and returns the
6022  * sequence n,L where n is the class of G (1 for D colors and 2 for D+1 colors)
6023  * and L is the list of colors of edges of G as returned by the edges command,
6024  * or a copy of G with colored edges if the option 'sto' is specified.
6025  */
_minimal_edge_coloring(const gen & g,GIAC_CONTEXT)6026 gen _minimal_edge_coloring(const gen &g,GIAC_CONTEXT) {
6027     if (g.type==_STRNG && g.subtype==-1) return g;
6028     if (g.type!=_VECT)
6029         return gentypeerr(contextptr);
6030     bool store=false;
6031     if (g.subtype==_SEQ__VECT) {
6032         if (g._VECTptr->size()!=2)
6033             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6034         if (g._VECTptr->back()!=at_sto)
6035             return generr("Expected 'sto' as the second argument");
6036         store=true;
6037     }
6038     graphe G(contextptr);
6039     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
6040         return gt_err(_GT_ERR_NOT_A_GRAPH);
6041     graphe::ivector colors;
6042     int typ=G.exact_edge_coloring(colors);
6043     if (typ==0)
6044         return undef;
6045     if (store)
6046         return G.to_gen();
6047     return makesequence(typ,vector_int_2_vecteur(colors));
6048 }
6049 static const char _minimal_edge_coloring_s[]="minimal_edge_coloring";
6050 static define_unary_function_eval(__minimal_edge_coloring,&_minimal_edge_coloring,_minimal_edge_coloring_s);
6051 define_unary_function_ptr5(at_minimal_edge_coloring,alias_at_minimal_edge_coloring,&__minimal_edge_coloring,0,true)
6052 
6053 /* USAGE:   chromatic_index(G,[cols])
6054  *
6055  * Returns the chromatic index of the input graph G. If an identifier cols is
6056  * given, the coloring is stored to it.
6057  */
_chromatic_index(const gen & g,GIAC_CONTEXT)6058 gen _chromatic_index(const gen &g,GIAC_CONTEXT) {
6059     if (g.type==_STRNG && g.subtype==-1) return g;
6060     if (g.type!=_VECT)
6061         return gentypeerr(contextptr);
6062     gen colors_dest=undef;
6063     if (g.subtype==_SEQ__VECT) {
6064         vecteur &gv=*g._VECTptr;
6065         if (gv.size()!=2)
6066             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6067         if ((colors_dest=g._VECTptr->back()).type!=_IDNT)
6068             return generrtype("Expected an identifier");
6069     }
6070     graphe G(contextptr);
6071     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
6072         return gt_err(_GT_ERR_NOT_A_GRAPH);
6073     if (G.is_directed())
6074         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6075     graphe::ivector colors;
6076     int ncolors;
6077     G.exact_edge_coloring(colors,&ncolors);
6078     if (ncolors==0)
6079         return undef;
6080     if (!is_undef(colors_dest)) { // store the coloring
6081         identifier_assign(*colors_dest._IDNTptr,vector_int_2_vecteur(colors),contextptr);
6082     }
6083     return ncolors;
6084 }
6085 static const char _chromatic_index_s[]="chromatic_index";
6086 static define_unary_function_eval(__chromatic_index,&_chromatic_index,_chromatic_index_s);
6087 define_unary_function_ptr5(at_chromatic_index,alias_at_chromatic_index,&__chromatic_index,0,true)
6088 
6089 /* USAGE:   is_hamiltonian(G,[C])
6090  *
6091  * Returns true if the input graph G is Hamiltonian, else returns false. If an
6092  * identifier C is given, the Hamiltonian circuit is stored to it.
6093  */
_is_hamiltonian(const gen & g,GIAC_CONTEXT)6094 gen _is_hamiltonian(const gen &g,GIAC_CONTEXT) {
6095     if (g.type==_STRNG && g.subtype==-1) return g;
6096     if (g.type!=_VECT)
6097         return gentypeerr(contextptr);
6098     gen dest(undef);
6099     graphe G(contextptr);
6100     if (g.subtype==_SEQ__VECT) {
6101         if (g._VECTptr->size()!=2)
6102             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6103         dest=g._VECTptr->back();
6104         if (dest.type!=_IDNT)
6105             return generrtype("Expected an identifier");
6106     }
6107     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
6108         return gt_err(_GT_ERR_NOT_A_GRAPH);
6109     graphe::ivector hc;
6110     bool res=G.is_hamiltonian(hc);
6111     if (res && !is_undef(dest)) {
6112         if (hc.empty()) assert(G.hamcycle(hc));
6113         identifier_assign(*dest._IDNTptr,G.get_node_labels(hc),contextptr);
6114     }
6115     return graphe::boole(res);
6116 }
6117 static const char _is_hamiltonian_s[]="is_hamiltonian";
6118 static define_unary_function_eval(__is_hamiltonian,&_is_hamiltonian,_is_hamiltonian_s);
6119 define_unary_function_ptr5(at_is_hamiltonian,alias_at_is_hamiltonian,&__is_hamiltonian,0,true)
6120 
6121 /* USAGE: traveling_salesman(G,[M])
6122  *
6123  * Returns a sequence of two objects, optimal cost for traveling salesman
6124  * problem and the corresponding Hamiltonian cycle in the undirected input
6125  * graph G. If G is not weighted, its adjacency matrix is used instead.
6126  * Alternatively, weight matrix may be passed as the optional parameter M. If G
6127  * is not Hamiltonian, an error is returned. A number of options may be passed
6128  * at the end of sequence of arguments: 'approx' for approximate solution, a
6129  * nonnegative integer representing the time limit (in milliseconds) or
6130  * 'vertex_distance' to automatically determine distances between the vertices
6131  * using their positions.
6132  */
_traveling_salesman(const gen & g,GIAC_CONTEXT)6133 gen _traveling_salesman(const gen &g,GIAC_CONTEXT) {
6134     if (g.type==_STRNG && g.subtype==-1) return g;
6135     if (g.type!=_VECT)
6136         return gentypeerr(contextptr);
6137     matrice M;
6138     vecteur options;
6139     if (g.subtype==_SEQ__VECT) {
6140         int pos=1;
6141         vecteur &gv=*g._VECTptr;
6142         if (gv.size()<2)
6143             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6144         if (gv.at(1).type==_VECT) {
6145             M=*gv.at(1)._VECTptr;
6146             ++pos;
6147         }
6148         options=vecteur(gv.begin()+pos,gv.end());
6149     }
6150     graphe G(contextptr),U(contextptr);
6151     graphe::ivector h;
6152     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
6153         return gt_err(_GT_ERR_NOT_A_GRAPH);
6154     if (!M.empty() && !G.is_weighted()) {
6155         if (!is_squarematrix(M) || int(M.size())!=G.node_count())
6156             return generrdim("The given weight matrix has invalid dimensions");
6157         G.make_weighted(M);
6158     }
6159     if (G.is_directed()) {
6160         /* solve ATSP */
6161         graphe::ipairs incl;
6162         int i,j,k=1;
6163         for (const_iterateur it=options.begin();it!=options.end();++it) {
6164             if (it==options.begin() && it->is_symb_of_sommet(at_equal) &&
6165                 it->_SYMBptr->feuille._VECTptr->front()==at_is_included &&
6166                 it->_SYMBptr->feuille._VECTptr->back().type==_VECT) {
6167                 const vecteur &v=*it->_SYMBptr->feuille._VECTptr->back()._VECTptr;
6168                 if (ckmatrix(v)) {
6169                     incl.reserve(v.size());
6170                     for (const_iterateur jt=v.begin();jt!=v.end();++jt) {
6171                         if (jt->_VECTptr->size()!=2)
6172                             return generr("Expected an edge");
6173                         i=G.node_index(jt->_VECTptr->front());
6174                         j=G.node_index(jt->_VECTptr->back());
6175                         if (i<0 || j<0) return gt_err(_GT_ERR_EDGE_NOT_FOUND);
6176                         incl.push_back(make_pair(i,j));
6177                     }
6178                 } else if (v.size()==2) {
6179                     i=G.node_index(v.front());
6180                     j=G.node_index(v.back());
6181                     if (i<0 || j<0) return gt_err(_GT_ERR_EDGE_NOT_FOUND);
6182                     incl.push_back(make_pair(i,j));
6183                 } else return generr("Expected an edge or list of edges");
6184             } else if (it->is_integer() && it->val>0) {
6185                 k=it->val;
6186             } else return generr("Option not supported");
6187         }
6188         graphe::ivectors hcv;
6189         graphe::dvector costs;
6190         if (!G.find_directed_tours(k,hcv,costs,incl))
6191             return undef;
6192         if (hcv.empty())
6193             return generr("Unable to find Hamiltonian cycle");
6194         vecteur res;
6195         G.ivectors2vecteur(hcv,res,false);
6196         vecteur cv(costs.size());
6197         for (iterateur it=cv.begin();it!=cv.end();++it) *it=gen(costs[it-cv.begin()]);
6198         if (G.is_weighted())
6199             return makesequence(k==1?cv.front():cv,k==1?res.front():res);
6200         return k==1?res.front():res;
6201     }
6202     if (G.hamcond()==0)
6203         return generr("The input graph is not Hamiltonian");
6204     /* parse options */
6205     bool approximate=false,make_distances=false;
6206     int time_limit=RAND_MAX;
6207     for (const_iterateur it=options.begin();it!=options.end();++it) {
6208         if (*it==at_approx)
6209             approximate=true;
6210         else if (approximate && it->is_integer())
6211             time_limit=it->val;
6212         else if (approximate && it->is_symb_of_sommet(at_equal) &&
6213                  it->_SYMBptr->feuille._VECTptr->front()==at_limit &&
6214                  it->_SYMBptr->feuille._VECTptr->back().is_integer())
6215             time_limit=it->_SYMBptr->feuille._VECTptr->back().val;
6216         else if (*it==at_vertex_distance && M.empty())
6217             make_distances=true;
6218         else return generr("Option not supported");
6219     }
6220     if (time_limit<0)
6221         return generr("Expected a nonnegative integer");
6222     if (make_distances) {
6223         if (G.is_weighted())
6224             return gt_err(_GT_ERR_UNWEIGHTED_GRAPH_REQUIRED);
6225         if (!G.make_euclidean_distances())
6226             return generr("Some vertex positions are invalid");
6227     }
6228     G.underlying(U);
6229     int res;
6230     double cost;
6231     if (approximate) {
6232         if (!G.is_weighted())
6233             gt_err(_GT_ERR_WEIGHTED_GRAPH_REQUIRED);
6234         if (!G.is_clique())
6235             return generr("The input graph must be complete");
6236         G.traveling_salesman(h,cost,true);
6237     } else {
6238         res=U.is_biconnected()?G.traveling_salesman(h,cost):0;
6239         if (res==0)
6240             return generr("The input graph is not Hamiltonian");
6241         if (res==-1)
6242             return undef;
6243     }
6244     /* success! */
6245     return G.is_weighted()?makesequence(!G.is_weighted()?gen(int(std::floor(cost+.5))):gen(cost),G.get_node_labels(h)):
6246                            G.get_node_labels(h);
6247 }
6248 static const char _traveling_salesman_s[]="traveling_salesman";
6249 static define_unary_function_eval(__traveling_salesman,&_traveling_salesman,_traveling_salesman_s);
6250 define_unary_function_ptr5(at_traveling_salesman,alias_at_traveling_salesman,&__traveling_salesman,0,true)
6251 
6252 /* USAGE:   trail2edges(T)
6253  *
6254  * Returns the list of edges on the trail T.
6255  */
_trail2edges(const gen & g,GIAC_CONTEXT)6256 gen _trail2edges(const gen &g,GIAC_CONTEXT) {
6257     if (g.type==_STRNG && g.subtype==-1) return g;
6258     vecteur t;
6259     if (g.type==_VECT)
6260         t=*g._VECTptr;
6261     else if (g.is_symb_of_sommet(at_trail))
6262         t=*g._SYMBptr->feuille._VECTptr;
6263     else return gentypeerr(contextptr);
6264     vecteur res;
6265     int n=t.size();
6266     for (int i=0;i<n-1;++i) {
6267         res.push_back(makevecteur(t[i],t[i+1]));
6268     }
6269     return res;
6270 }
6271 static const char _trail2edges_s[]="trail2edges";
6272 static define_unary_function_eval(__trail2edges,&_trail2edges,_trail2edges_s);
6273 define_unary_function_ptr5(at_trail2edges,alias_at_trail2edges,&__trail2edges,0,true)
6274 
6275 /* USAGE:   maxflow(G,s,t,[F])
6276  *
6277  * Returns the optimal value for the max flow problem for network G with the
6278  * source s and sink t [along with an optimal flow F (as a matrix)].
6279  */
_maxflow(const gen & g,GIAC_CONTEXT)6280 gen _maxflow(const gen &g,GIAC_CONTEXT) {
6281     if (g.type==_STRNG && g.subtype==-1) return g;
6282     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
6283         return gentypeerr(contextptr);
6284     vecteur &gv=*g._VECTptr;
6285     if (gv.size()<3)
6286         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6287     gen &S=gv[1],&T=gv[2];
6288     gen M(undef);
6289     if (gv.size()==4) {
6290         M=gv[3];
6291         if (M.type!=_IDNT)
6292             return generr("Expected an identifier");
6293     }
6294     graphe G(contextptr);
6295     if (!G.read_gen(gv.front()))
6296         return gt_err(_GT_ERR_NOT_A_GRAPH);
6297     if (!G.is_directed())
6298         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
6299     int s=G.node_index(S),t=G.node_index(T);
6300     if (s<0 || t<0)
6301         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
6302     vector<map<int,gen> > flow;
6303     gen mf=G.maxflow_edmonds_karp(s,t,flow);
6304     int n=G.node_count();
6305     if (!is_undef(M)) {
6306         matrice m=*_matrix(makesequence(n,n,0),contextptr)._VECTptr;
6307         for (int i=0;i<n;++i) {
6308             map<int,gen> &f=flow[i];
6309             for (map<int,gen>::const_iterator it=f.begin();it!=f.end();++it) {
6310                 m[i]._VECTptr->at(it->first)=max(it->second,0,contextptr);
6311             }
6312         }
6313         identifier_assign(*M._IDNTptr,m,contextptr);
6314     }
6315     return mf;
6316 }
6317 static const char _maxflow_s[]="maxflow";
6318 static define_unary_function_eval(__maxflow,&_maxflow,_maxflow_s);
6319 define_unary_function_ptr5(at_maxflow,alias_at_maxflow,&__maxflow,0,true)
6320 
6321 /* USAGE:   minimum_cut(G,s,t)
6322  *
6323  * Returns the list of edges forming a minimum cut in a directed graph G with
6324  * respect to the source s and sink t.
6325  */
_minimum_cut(const gen & g,GIAC_CONTEXT)6326 gen _minimum_cut(const gen &g,GIAC_CONTEXT) {
6327     if (g.type==_STRNG && g.subtype==-1) return g;
6328     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
6329         return gentypeerr(contextptr);
6330     vecteur &gv=*g._VECTptr;
6331     if (gv.size()!=3)
6332         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6333     gen &S=gv[1],&T=gv[2];
6334     graphe G(contextptr);
6335     if (!G.read_gen(gv.front()))
6336         return gt_err(_GT_ERR_NOT_A_GRAPH);
6337     if (!G.is_directed())
6338         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
6339     int s=G.node_index(S),t=G.node_index(T);
6340     if (s<0 || t<0)
6341         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
6342     vector<map<int,gen> > flow;
6343     G.maxflow_edmonds_karp(s,t,flow);
6344     graphe::ipairs cut;
6345     G.minimum_cut(s,flow,cut);
6346     vecteur res=G.ipairs2edges(cut);
6347     return change_subtype(res,_LIST__VECT);
6348 }
6349 static const char _minimum_cut_s[]="minimum_cut";
6350 static define_unary_function_eval(__minimum_cut,&_minimum_cut,_minimum_cut_s);
6351 define_unary_function_ptr5(at_minimum_cut,alias_at_minimum_cut,&__minimum_cut,0,true)
6352 
6353 /* USAGE:   is_network(G,[s,t])
6354  *
6355  * Returns true if the graph G is a network with the source s and sink t, else
6356  * returns false. If s,t are not given, the output is a sequence of two sets of
6357  * vertices, sources and sinks (if both lists are empty then G is not a
6358  * network).
6359  */
_is_network(const gen & g,GIAC_CONTEXT)6360 gen _is_network(const gen &g,GIAC_CONTEXT) {
6361     if (g.type==_STRNG && g.subtype==-1) return g;
6362     if (g.type!=_VECT)
6363         return gentypeerr(contextptr);
6364     gen S=undef,T=undef;
6365     if (g.subtype==_SEQ__VECT) {
6366         vecteur &gv=*g._VECTptr;
6367         if (gv.size()!=3)
6368             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6369         S=gv[1];
6370         T=gv[2];
6371     }
6372     graphe G(contextptr);
6373     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
6374         return gt_err(_GT_ERR_NOT_A_GRAPH);
6375     if (!G.is_directed())
6376         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
6377     bool con=G.is_connected();
6378     vecteur sources,sinks;
6379     if (!is_undef(S) && !is_undef(T)) {
6380         int s=G.node_index(S),t=G.node_index(T);
6381         if (s<0 || t<0)
6382             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
6383         if (!con || G.in_degree(s)>0 || G.out_degree(t)>0)
6384             return graphe::FAUX;
6385         return graphe::VRAI;
6386     } else if (con) {
6387         int n=G.node_count();
6388         for (int i=0;i<n;++i) {
6389             if (G.in_degree(i)==0)
6390                 sources.push_back(G.node_label(i));
6391             else if (G.out_degree(i)==0) // can't be indeg=outdeg=0 because con=true
6392                 sinks.push_back(G.node_label(i));
6393         }
6394     }
6395     return makesequence(_sort(sources,contextptr),_sort(sinks,contextptr));
6396 }
6397 static const char _is_network_s[]="is_network";
6398 static define_unary_function_eval(__is_network,&_is_network,_is_network_s);
6399 define_unary_function_ptr5(at_is_network,alias_at_is_network,&__is_network,0,true)
6400 
6401 /* USAGE:   is_cut_set(G,E)
6402  *
6403  * Returns true if removing the edges in E from the graph G increases the
6404  * number of connected components of G, else returns false.
6405  */
_is_cut_set(const gen & g,GIAC_CONTEXT)6406 gen _is_cut_set(const gen &g,GIAC_CONTEXT) {
6407     if (g.type==_STRNG && g.subtype==-1) return g;
6408     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
6409         return gentypeerr(contextptr);
6410     vecteur &gv=*g._VECTptr;
6411     if (gv.size()!=2)
6412         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6413     graphe G(contextptr);
6414     if (!G.read_gen(gv.front()))
6415         return gt_err(_GT_ERR_NOT_A_GRAPH);
6416     if (gv.back().type!=_VECT)
6417         return gentypeerr("Expected a list of edges");
6418     vecteur &E=*gv.back()._VECTptr;
6419     graphe::ipairs edg;
6420     int i,j;
6421     for (const_iterateur it=E.begin();it!=E.end();++it) {
6422         if (it->type!=_VECT || it->_VECTptr->size()!=2)
6423             return gentypeerr("Expected an edge");
6424         const gen &u=it->_VECTptr->front(),&v=it->_VECTptr->back();
6425         i=G.node_index(u);
6426         j=G.node_index(v);
6427         if (i<0 || j<0)
6428             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
6429         if (!G.has_edge(i,j))
6430             return gt_err(_GT_ERR_EDGE_NOT_FOUND);
6431         edg.push_back(make_pair(i,j));
6432     }
6433     graphe::ivectors comp;
6434     G.connected_components(comp);
6435     int nc=comp.size();
6436     for (graphe::ipairs_iter it=edg.begin();it!=edg.end();++it) {
6437         G.remove_edge(*it);
6438     }
6439     G.connected_components(comp);
6440     return nc!=int(comp.size())?graphe::VRAI:graphe::FAUX;
6441 }
6442 static const char _is_cut_set_s[]="is_cut_set";
6443 static define_unary_function_eval(__is_cut_set,&_is_cut_set,_is_cut_set_s);
6444 define_unary_function_ptr5(at_is_cut_set,alias_at_is_cut_set,&__is_cut_set,0,true)
6445 
6446 /* USAGE:   random_network(a,b,[p],[opts])
6447  *
6448  * Returns a random network graph with b circular frames on a vertices in which
6449  * every edge appears with the probability p (by default 0.5). The source
6450  * vertex is in the bottom left corner of the first frame and the sink vertex
6451  * is in the top right corner of the b-th frame.
6452  */
_random_network(const gen & g,GIAC_CONTEXT)6453 gen _random_network(const gen &g,GIAC_CONTEXT) {
6454     if (g.type==_STRNG && g.subtype==-1) return g;
6455     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
6456         return gentypeerr(contextptr);
6457     vecteur &gv=*g._VECTptr;
6458     if (gv.size()<2)
6459         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6460     int a,b;
6461     if (!gv[0].is_integer() || !gv[1].is_integer() || (a=gv[0].val)<1 || (b=gv[1].val)<1)
6462         return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
6463     if (a+b<3)
6464         return generr("a+b>2 is required");
6465     double p=0.5;
6466     /* parse options */
6467     bool acyclic=false,weighted=false;
6468     gen lw,hw;
6469     for (const_iterateur it=gv.begin()+2;it!=gv.end();++it) {
6470         gen P=_evalf(*it,contextptr);
6471         if (P.type==_DOUBLE_ && is_strictly_positive(P,contextptr) && !is_strictly_greater(P,1,contextptr))
6472             p=P.DOUBLE_val();
6473         else if (it->is_integer()) {
6474             switch (it->val) {
6475             case _GT_ACYCLIC:
6476                 acyclic=true;
6477                 break;
6478             }
6479         } else if (it->is_symb_of_sommet(at_equal)) {
6480             gen &lh=it->_SYMBptr->feuille._VECTptr->front();
6481             gen &rh=it->_SYMBptr->feuille._VECTptr->back();
6482             if (lh.is_integer()) {
6483                 switch (lh.val) {
6484                 case _GT_ACYCLIC:
6485                     if (rh.is_integer() && rh.subtype==_INT_BOOLEAN)
6486                         acyclic=(bool)rh.val;
6487                     else return generr("Expected a boolean value");
6488                     break;
6489                 case _GT_WEIGHTS:
6490                     if (rh.is_symb_of_sommet(at_interval)) {
6491                         lw=rh._SYMBptr->feuille._VECTptr->front();
6492                         hw=rh._SYMBptr->feuille._VECTptr->back();
6493                         if (_evalf(lw,contextptr).type!=_DOUBLE_ || _evalf(hw,contextptr).type!=_DOUBLE_)
6494                             return generr("Expected a real number");
6495                         if (is_greater(lw,hw,contextptr))
6496                             return generr("Invalid range");
6497                     } else return generr("Expected an interval");
6498                     weighted=true;
6499                     break;
6500                 default:
6501                     return generr("Unrecognized option");
6502                 }
6503             }
6504         }
6505     }
6506     /* construct the network */
6507     vecteur frames;
6508     gen lab1,lab2;
6509     int s,t,ofs=array_start(contextptr);
6510     for (int k=0;k<b;++k) {
6511         graphe F(contextptr);
6512         F.make_grid_graph(a,a);
6513         if (acyclic) {
6514             lab1=graphe::colon_label(ofs,ofs);
6515             lab2=graphe::colon_label(a-1+ofs,a-1+ofs);
6516             s=F.node_index(lab1); t=F.node_index(lab2);
6517             F.add_edge(s,t);
6518             F.compute_st_numbering(s,t);
6519             F.remove_edge(s,t);
6520             F.assign_edge_directions_from_st();
6521         } else F.make_directed();
6522         frames.push_back(F.to_gen());
6523     }
6524     graphe G(contextptr);
6525     G.read_gen(_disjoint_union(frames,contextptr));
6526     graphe::ivector x,y;
6527     for (int k=1;k<b;++k) {
6528         x=vecteur_2_vector_int(*_randperm(a,contextptr)._VECTptr);
6529         y=vecteur_2_vector_int(*_randperm(a,contextptr)._VECTptr);
6530         for (int i=0;i<a;++i) {
6531             for (int j=0;j<a;++j) {
6532                 lab1=graphe::colon_label(k,i+ofs,j+ofs);
6533                 lab2=graphe::colon_label(k+1,x[i],y[j]);
6534                 G.add_edge(lab1,lab2);
6535             }
6536         }
6537     }
6538     graphe::ivector st(2);
6539     st.front()=G.node_index(graphe::colon_label(1,ofs,ofs));
6540     st.back()=G.node_index(graphe::colon_label(b,a-1+ofs,a-1+ofs));
6541     graphe::edgeset Eset;
6542     G.incident_edges(st,Eset);
6543     for (graphe::edgeset_iter it=Eset.begin();it!=Eset.end();++it) {
6544         const graphe::ipair &e=*it;
6545         if (e.first==st.back() || e.second==st.front())
6546             G.remove_edge(e);
6547     }
6548     graphe::ipairs E;
6549     G.get_edges_as_pairs(E);
6550     for (graphe::ipairs_iter it=E.begin();it!=E.end();++it) {
6551         const graphe::ipair &e=*it;
6552         if (G.rand_uniform()>p) {
6553             G.remove_edge(e);
6554             if (!G.is_connected())
6555                 G.add_edge(e);
6556         }
6557     }
6558     int n=G.node_count();
6559     vecteur labels;
6560     G.make_default_labels(labels,n);
6561     G.relabel_nodes(labels);
6562     if (weighted)
6563         G.randomize_edge_weights(_evalf(lw,contextptr).DOUBLE_val(),_evalf(hw,contextptr).DOUBLE_val(),
6564                                  lw.is_integer() && hw.is_integer());
6565     return G.to_gen();
6566 }
6567 static const char _random_network_s[]="random_network";
6568 static define_unary_function_eval(__random_network,&_random_network,_random_network_s);
6569 define_unary_function_ptr5(at_random_network,alias_at_random_network,&__random_network,0,true)
6570 
6571 /* USAGE:   tutte_polynomial(G,[x,y])
6572  *
6573  * Returns the Tutte polynomial (with x and y as its variables or
6574  * their values) of an undirected graph G. If G is weighted, all weights must
6575  * be positive integers and are interpreted as edge multiplicities.
6576  */
_tutte_polynomial(const gen & g,GIAC_CONTEXT)6577 gen _tutte_polynomial(const gen &g,GIAC_CONTEXT) {
6578     if (g.type==_STRNG && g.subtype==-1) return g;
6579     if (g.type!=_VECT)
6580         return gentypeerr(contextptr);
6581     gen x=identificateur("x"),y=identificateur("y");
6582     if (g.subtype==_SEQ__VECT) {
6583         vecteur &gv=*g._VECTptr;
6584         if (gv.size()!=3)
6585             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6586         x=gv[1];
6587         y=gv[2];
6588     }
6589     graphe G(contextptr);
6590     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
6591         return gt_err(_GT_ERR_NOT_A_GRAPH);
6592     if (G.is_null())
6593         return gt_err(_GT_ERR_GRAPH_IS_NULL);
6594     if (G.is_directed())
6595         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6596     if (G.is_weighted()) {
6597         if (!G.weights2multiedges())
6598             return generr("Some edge weights are not positive integers");
6599         G.set_weighted(false);
6600     }
6601     gen p=G.tutte_polynomial(x,y);
6602     if (p.is_symb_of_sommet(at_plus) && p._SYMBptr->feuille._VECTptr->size()>7)
6603         return _factor(p,contextptr);
6604     return _ratnormal(p,contextptr);
6605 }
6606 static const char _tutte_polynomial_s[]="tutte_polynomial";
6607 static define_unary_function_eval(__tutte_polynomial,&_tutte_polynomial,_tutte_polynomial_s);
6608 define_unary_function_ptr5(at_tutte_polynomial,alias_at_tutte_polynomial,&__tutte_polynomial,0,true)
6609 
6610 /* USAGE:   flow_polynomial(G,[x])
6611  *
6612  * Returns the flow polynomial (with x as its variable or value) of an
6613  * undirected unweighted graph G.
6614  */
_flow_polynomial(const gen & g,GIAC_CONTEXT)6615 gen _flow_polynomial(const gen &g,GIAC_CONTEXT) {
6616     if (g.type==_STRNG && g.subtype==-1) return g;
6617     if (g.type!=_VECT)
6618         return gentypeerr(contextptr);
6619     gen y=identificateur("x");
6620     if (g.subtype==_SEQ__VECT) {
6621         vecteur &gv=*g._VECTptr;
6622         if (gv.size()!=2)
6623             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6624         y=gv[1];
6625     }
6626     graphe G(contextptr);
6627     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
6628         return gt_err(_GT_ERR_NOT_A_GRAPH);
6629     if (G.is_null())
6630         return gt_err(_GT_ERR_GRAPH_IS_NULL);
6631     if (G.is_directed())
6632         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6633     if (G.is_weighted())
6634         return gt_err(_GT_ERR_UNWEIGHTED_GRAPH_REQUIRED);
6635     int n=G.node_count(),m=G.edge_count(),c=G.connected_component_count();
6636     assert(n>0 && c>0);
6637     gen p=_ratnormal(pow(gen(-1),m-n+c)*G.tutte_polynomial(0,1-y),contextptr);
6638     if (p.is_symb_of_sommet(at_plus) && p._SYMBptr->feuille._VECTptr->size()>7)
6639         return _factor(p,contextptr);
6640     return p;
6641 }
6642 static const char _flow_polynomial_s[]="flow_polynomial";
6643 static define_unary_function_eval(__flow_polynomial,&_flow_polynomial,_flow_polynomial_s);
6644 define_unary_function_ptr5(at_flow_polynomial,alias_at_flow_polynomial,&__flow_polynomial,0,true)
6645 
6646 /* USAGE:   chromatic_polynomial(G,[t])
6647  *
6648  * Returns the chromatic polynomial (with t as its variable or value) of an
6649  * undirected unweighted graph G.
6650  */
_chromatic_polynomial(const gen & g,GIAC_CONTEXT)6651 gen _chromatic_polynomial(const gen &g,GIAC_CONTEXT) {
6652     if (g.type==_STRNG && g.subtype==-1) return g;
6653     if (g.type!=_VECT)
6654         return gentypeerr(contextptr);
6655     gen x=identificateur("x");
6656     if (g.subtype==_SEQ__VECT) {
6657         vecteur &gv=*g._VECTptr;
6658         if (gv.size()!=2)
6659             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6660         x=gv[1];
6661     }
6662     graphe G(contextptr);
6663     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
6664         return gt_err(_GT_ERR_NOT_A_GRAPH);
6665     if (G.is_null())
6666         return gt_err(_GT_ERR_GRAPH_IS_NULL);
6667     if (G.is_directed())
6668         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6669     if (G.is_weighted())
6670         return gt_err(_GT_ERR_UNWEIGHTED_GRAPH_REQUIRED);
6671     int n=G.node_count(),c=G.connected_component_count();
6672     assert(n>0 && c>0);
6673     gen p=_ratnormal(pow(gen(-1),n-c)*pow(x,c)*G.tutte_polynomial(1-x,0),contextptr);
6674     if (p.is_symb_of_sommet(at_plus) && p._SYMBptr->feuille._VECTptr->size()>7)
6675         return _factor(p,contextptr);
6676     return p;
6677 }
6678 static const char _chromatic_polynomial_s[]="chromatic_polynomial";
6679 static define_unary_function_eval(__chromatic_polynomial,&_chromatic_polynomial,_chromatic_polynomial_s);
6680 define_unary_function_ptr5(at_chromatic_polynomial,alias_at_chromatic_polynomial,&__chromatic_polynomial,0,true)
6681 
6682 /* USAGE:   reliability_polynomial(G,[t])
6683  *
6684  * Returns the reliability polynomial (with t as its variable or value) of an
6685  * undirected graph G. If G is weighted, all weights must be positive integers
6686  * and are interpreted as edge multiplicities.
6687  */
_reliability_polynomial(const gen & g,GIAC_CONTEXT)6688 gen _reliability_polynomial(const gen &g,GIAC_CONTEXT) {
6689     if (g.type==_STRNG && g.subtype==-1) return g;
6690     if (g.type!=_VECT)
6691         return gentypeerr(contextptr);
6692     gen x=identificateur("x"),tmp=identificateur(" x");
6693     if (g.subtype==_SEQ__VECT) {
6694         vecteur &gv=*g._VECTptr;
6695         if (gv.size()!=2)
6696             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6697         x=gv[1];
6698     }
6699     graphe G(contextptr);
6700     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
6701         return gt_err(_GT_ERR_NOT_A_GRAPH);
6702     if (G.is_null())
6703         return gt_err(_GT_ERR_GRAPH_IS_NULL);
6704     if (G.is_directed())
6705         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6706     if (G.is_weighted()) {
6707         if (!G.weights2multiedges())
6708             return generr("Some edge weights are not positive integers");
6709     }
6710     int n=G.node_count(),m=G.edge_count(),c=G.connected_component_count();
6711     assert(n>0 && c>0);
6712     gen p=_ratnormal(_subs(makesequence(_ratnormal(pow(gen(1-tmp),n-c)*pow(tmp,m-n+c)
6713                                                    *G.tutte_polynomial(1,pow(tmp,-1)),contextptr),
6714                                         tmp,x),contextptr),contextptr);
6715     if (p.is_symb_of_sommet(at_plus) && p._SYMBptr->feuille._VECTptr->size()>7)
6716         return _factor(p,contextptr);
6717     return p;
6718 }
6719 static const char _reliability_polynomial_s[]="reliability_polynomial";
6720 static define_unary_function_eval(__reliability_polynomial,&_reliability_polynomial,_reliability_polynomial_s);
6721 define_unary_function_ptr5(at_reliability_polynomial,alias_at_reliability_polynomial,&__reliability_polynomial,0,true)
6722 
6723 /* USAGE:   laplacian_matrix(G,[normal])
6724  *
6725  * Returns the Laplacian matrix L=D-A of an undirected graph G where D resp. A
6726  * is the degree matrix resp. the adjacency matrix of G.
6727  */
_laplacian_matrix(const gen & g,GIAC_CONTEXT)6728 gen _laplacian_matrix(const gen &g,GIAC_CONTEXT) {
6729     if (g.type==_STRNG && g.subtype==-1) return g;
6730     if (g.type!=_VECT)
6731         return gentypeerr(contextptr);
6732     bool normalize=false;
6733     if (g.subtype==_SEQ__VECT) {
6734         vecteur &gv=*g._VECTptr;
6735         if (gv.size()!=2)
6736             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6737         if (gv[1]==at_normal)
6738             normalize=true;
6739         else return generr("Unrecognized option");
6740     }
6741     graphe G(contextptr,false);
6742     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
6743         return gt_err(_GT_ERR_NOT_A_GRAPH);
6744     if (G.is_null())
6745         return gt_err(_GT_ERR_GRAPH_IS_NULL);
6746     if (G.is_directed())
6747         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6748     matrice L;
6749     G.laplacian_matrix(L,normalize);
6750     return change_subtype(_ratnormal(L,contextptr),_MATRIX__VECT);
6751 }
6752 static const char _laplacian_matrix_s[]="laplacian_matrix";
6753 static define_unary_function_eval(__laplacian_matrix,&_laplacian_matrix,_laplacian_matrix_s);
6754 define_unary_function_ptr5(at_laplacian_matrix,alias_at_laplacian_matrix,&__laplacian_matrix,0,true)
6755 
6756 /* USAGE:   fundamental_cycle(G)
6757  *
6758  * Returns the unique cycle in an unicyclic graph G as a graph.
6759  */
_fundamental_cycle(const gen & g,GIAC_CONTEXT)6760 gen _fundamental_cycle(const gen &g,GIAC_CONTEXT) {
6761     if (g.type==_STRNG && g.subtype==-1) return g;
6762     graphe G(contextptr);
6763     if (!G.read_gen(g))
6764         return gt_err(_GT_ERR_NOT_A_GRAPH);
6765     if (G.is_null())
6766         return gt_err(_GT_ERR_GRAPH_IS_NULL);
6767     if (G.is_directed())
6768         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6769     if (!G.is_connected())
6770         return gt_err(_GT_ERR_CONNECTED_GRAPH_REQUIRED);
6771     graphe::ivectors cycles;
6772     G.fundamental_cycles(cycles,-1,false);
6773     if (cycles.size()!=1)
6774         return generr("The graph is not unicyclic");
6775     graphe::ivector &fc=cycles.front();
6776     return _cycle_graph(G.get_node_labels(fc),contextptr);
6777 }
6778 static const char _fundamental_cycle_s[]="fundamental_cycle";
6779 static define_unary_function_eval(__fundamental_cycle,&_fundamental_cycle,_fundamental_cycle_s);
6780 define_unary_function_ptr5(at_fundamental_cycle,alias_at_fundamental_cycle,&__fundamental_cycle,0,true)
6781 
6782 /* USAGE:   cycle_basis(G)
6783  *
6784  * Returns the list of all fundamental cycles in undirected graph G.
6785  */
_cycle_basis(const gen & g,GIAC_CONTEXT)6786 gen _cycle_basis(const gen &g,GIAC_CONTEXT) {
6787     if (g.type==_STRNG && g.subtype==-1) return g;
6788     graphe G(contextptr);
6789     if (!G.read_gen(g))
6790         return gt_err(_GT_ERR_NOT_A_GRAPH);
6791     if (G.is_null())
6792         return gt_err(_GT_ERR_GRAPH_IS_NULL);
6793     if (G.is_directed())
6794         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6795     graphe::ivectors cycles;
6796     G.fundamental_cycles(cycles);
6797     vecteur res(cycles.size());
6798     for (graphe::ivectors_iter it=cycles.begin();it!=cycles.end();++it) {
6799         res[it-cycles.begin()]=G.get_node_labels(*it);
6800     }
6801     return change_subtype(res,_LIST__VECT);
6802 }
6803 static const char _cycle_basis_s[]="cycle_basis";
6804 static define_unary_function_eval(__cycle_basis,&_cycle_basis,_cycle_basis_s);
6805 define_unary_function_ptr5(at_cycle_basis,alias_at_cycle_basis,&__cycle_basis,0,true)
6806 
6807 /* USAGE:   mycielski(G)
6808  *
6809  * Returns the Mycielski graph of an undirected graph G.
6810  */
_mycielski(const gen & g,GIAC_CONTEXT)6811 gen _mycielski(const gen &g,GIAC_CONTEXT) {
6812     if (g.type==_STRNG && g.subtype==-1) return g;
6813     graphe G(contextptr);
6814     if (!G.read_gen(g))
6815         return gt_err(_GT_ERR_NOT_A_GRAPH);
6816     if (G.is_null())
6817         return gt_err(_GT_ERR_GRAPH_IS_NULL);
6818     if (G.is_directed())
6819         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6820     graphe M(contextptr);
6821     G.mycielskian(M);
6822     return M.to_gen();
6823 }
6824 static const char _mycielski_s[]="mycielski";
6825 static define_unary_function_eval(__mycielski,&_mycielski,_mycielski_s);
6826 define_unary_function_ptr5(at_mycielski,alias_at_mycielski,&__mycielski,0,true)
6827 
6828 /* USAGE:   clustering_coefficient(G,[v])
6829  *
6830  * Returns the [local] clustering coefficient [of the vertex v or vertices from
6831  * a sequence or list v] of an undirected graph G.
6832  */
_clustering_coefficient(const gen & g,GIAC_CONTEXT)6833 gen _clustering_coefficient(const gen &g,GIAC_CONTEXT) {
6834     if (g.type==_STRNG && g.subtype==-1) return g;
6835     if (g.type!=_VECT)
6836         return gentypeerr(contextptr);
6837     vecteur V;
6838     bool apprx=false,exct=false;
6839     if (g.subtype==_SEQ__VECT) {
6840         vecteur &gv=*g._VECTptr;
6841         if (gv.size()<2)
6842             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
6843         for (const_iterateur it=gv.begin()+1;it!=gv.end();++it) {
6844             if (it->type==_VECT) {
6845                 V=*it->_VECTptr;
6846             } else if (*it==at_approx) {
6847                 apprx=true;
6848             } else if (*it==at_exact) {
6849                 exct=true;
6850             } else {
6851                 V.push_back(*it);
6852                 break;
6853             }
6854             if (it-gv.begin()>1)
6855                 return gentypeerr(contextptr);
6856         }
6857     }
6858     graphe G(contextptr,!V.empty());
6859     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
6860         return gt_err(_GT_ERR_NOT_A_GRAPH);
6861     if (G.is_null())
6862         return gt_err(_GT_ERR_GRAPH_IS_NULL);
6863     if (G.is_directed())
6864         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6865     if (V.empty())
6866         return G.clustering_coeff(apprx,exct);
6867     int i;
6868     vecteur res;
6869     for (const_iterateur it=V.begin();it!=V.end();++it) {
6870         i=G.node_index(*it);
6871         if (i<0)
6872             return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
6873         res.push_back(G.degree(i)<2?0:G.local_clustering_coeff(i));
6874     }
6875     if (res.size()==1)
6876         return res.front();
6877     return res;
6878 }
6879 static const char _clustering_coefficient_s[]="clustering_coefficient";
6880 static define_unary_function_eval(__clustering_coefficient,&_clustering_coefficient,_clustering_coefficient_s);
6881 define_unary_function_ptr5(at_clustering_coefficient,alias_at_clustering_coefficient,&__clustering_coefficient,0,true)
6882 
6883 /* USAGE:   network_transitivity(G)
6884  *
6885  * Returns the transitivity (triangle density) of a graph G.
6886  */
_network_transitivity(const gen & g,GIAC_CONTEXT)6887 gen _network_transitivity(const gen &g,GIAC_CONTEXT) {
6888     if (g.type==_STRNG && g.subtype==-1) return g;
6889     graphe G(contextptr,false);
6890     if (!G.read_gen(g))
6891         return gt_err(_GT_ERR_NOT_A_GRAPH);
6892     if (G.is_null())
6893         return gt_err(_GT_ERR_GRAPH_IS_NULL);
6894     return G.transitivity();
6895 }
6896 static const char _network_transitivity_s[]="network_transitivity";
6897 static define_unary_function_eval(__network_transitivity,&_network_transitivity,_network_transitivity_s);
6898 define_unary_function_ptr5(at_network_transitivity,alias_at_network_transitivity,&__network_transitivity,0,true)
6899 
6900 /* USAGE:   two_edge_connected_components(G)
6901  *
6902  * Returns the list of two-edge connected components of an undirected graph G.
6903  */
_two_edge_connected_components(const gen & g,GIAC_CONTEXT)6904 gen _two_edge_connected_components(const gen &g,GIAC_CONTEXT) {
6905     if (g.type==_STRNG && g.subtype==-1) return g;
6906     graphe G(contextptr);
6907     if (!G.read_gen(g))
6908         return gt_err(_GT_ERR_NOT_A_GRAPH);
6909     if (G.is_null())
6910         return vecteur(0);
6911     if (G.is_directed())
6912         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6913     graphe::ipairs bridges;
6914     G.find_bridges(bridges);
6915     for (graphe::ipairs_iter it=bridges.begin();it!=bridges.end();++it) {
6916         G.remove_edge(*it);
6917     }
6918     graphe::ivectors comp;
6919     G.connected_components(comp);
6920     vecteur res(comp.size());
6921     G.ivectors2vecteur(comp,res,true);
6922     return change_subtype(res,_LIST__VECT);
6923 }
6924 static const char _two_edge_connected_components_s[]="two_edge_connected_components";
6925 static define_unary_function_eval(__two_edge_connected_components,&_two_edge_connected_components,_two_edge_connected_components_s);
6926 define_unary_function_ptr5(at_two_edge_connected_components,alias_at_two_edge_connected_components,&__two_edge_connected_components,0,true)
6927 
6928 /* USAGE:   is_two_edge_connected(G)
6929  *
6930  * Returns true iff the undirected graph G is two-edge connected.
6931  */
_is_two_edge_connected(const gen & g,GIAC_CONTEXT)6932 gen _is_two_edge_connected(const gen &g,GIAC_CONTEXT) {
6933     if (g.type==_STRNG && g.subtype==-1) return g;
6934     graphe G(contextptr,false);
6935     if (!G.read_gen(g))
6936         return gt_err(_GT_ERR_NOT_A_GRAPH);
6937     if (G.is_null())
6938         return gt_err(_GT_ERR_GRAPH_IS_NULL);
6939     if (G.is_directed())
6940         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6941     graphe::ipairs bridges;
6942     G.find_bridges(bridges);
6943     return graphe::boole(bridges.empty());
6944 }
6945 static const char _is_two_edge_connected_s[]="is_two_edge_connected";
6946 static define_unary_function_eval(__is_two_edge_connected,&_is_two_edge_connected,_is_two_edge_connected_s);
6947 define_unary_function_ptr5(at_is_two_edge_connected,alias_at_is_two_edge_connected,&__is_two_edge_connected,0,true)
6948 
6949 /* USAGE:   edge_connectivity(G)
6950  *
6951  * Returns the edge connectivity of an undirected graph G.
6952  */
_edge_connectivity(const gen & g,GIAC_CONTEXT)6953 gen _edge_connectivity(const gen &g,GIAC_CONTEXT) {
6954     if (g.type==_STRNG && g.subtype==-1) return g;
6955     graphe G(contextptr,false);
6956     if (!G.read_gen(g))
6957         return gt_err(_GT_ERR_NOT_A_GRAPH);
6958     if (G.is_directed())
6959         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6960     if (G.node_count()<2)
6961         return generr("The graph must have at least two vertices");
6962     if (!G.is_connected())
6963         return gt_err(_GT_ERR_CONNECTED_GRAPH_REQUIRED);
6964     return G.edge_connectivity();
6965 }
6966 static const char _edge_connectivity_s[]="edge_connectivity";
6967 static define_unary_function_eval(__edge_connectivity,&_edge_connectivity,_edge_connectivity_s);
6968 define_unary_function_ptr5(at_edge_connectivity,alias_at_edge_connectivity,&__edge_connectivity,0,true)
6969 
6970 /* USAGE:   vertex_connectivity(G)
6971  *
6972  * Returns the vertex connectivity of an undirected graph G.
6973  */
_vertex_connectivity(const gen & g,GIAC_CONTEXT)6974 gen _vertex_connectivity(const gen &g,GIAC_CONTEXT) {
6975     if (g.type==_STRNG && g.subtype==-1) return g;
6976     graphe G(contextptr,false);
6977     if (!G.read_gen(g))
6978         return gt_err(_GT_ERR_NOT_A_GRAPH);
6979     if (G.is_directed())
6980         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
6981     if (G.node_count()<2)
6982         return generr("The graph must have at least two vertices");
6983     if (!G.is_connected())
6984         return gt_err(_GT_ERR_CONNECTED_GRAPH_REQUIRED);
6985     return G.vertex_connectivity();
6986 }
6987 static const char _vertex_connectivity_s[]="vertex_connectivity";
6988 static define_unary_function_eval(__vertex_connectivity,&_vertex_connectivity,_vertex_connectivity_s);
6989 define_unary_function_ptr5(at_vertex_connectivity,alias_at_vertex_connectivity,&__vertex_connectivity,0,true)
6990 
6991 /* USAGE:   tonnetz(a,b,c,[d])
6992  *
6993  * Returns the neo.Riemannian style tone network corresponding to the pitch
6994  * class [a,b,c] resp. [a,b,c,d], where n=a+b+c[+d] is the octave range.
6995  * Vertices of the resulting graph are elements of the cyclic group Zn, i.e.
6996  * 0,1,...,n-1. The neighbors of vertex v are: v+a, v-a, v+b, v-b, v+c, v-c,
6997  * [v+a+b, v-a-b, v+b+c,v-b-c].
6998  */
_tonnetz(const gen & g,GIAC_CONTEXT)6999 gen _tonnetz(const gen &g,GIAC_CONTEXT) {
7000     if (g.type==_STRNG && g.subtype==-1) return g;
7001     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
7002         return gentypeerr(contextptr);
7003     vecteur &gv=*g._VECTptr;
7004     if (gv.size()!=3 && gv.size()!=4)
7005         return gensizeerr(contextptr);
7006     for (const_iterateur it=gv.begin();it!=gv.end();++it) {
7007         if (!it->is_integer() || !is_strictly_positive(*it,contextptr))
7008             return gensizeerr(contextptr);
7009     }
7010     graphe G(contextptr);
7011     vecteur labels;
7012     int n=_sum(gv,contextptr).val;
7013     G.make_default_labels(labels,n,0,0);
7014     G.add_nodes(labels);
7015     int a=gv[0].val,b=gv[1].val,c=gv[2].val;
7016     for (int i=0;i<n;++i) {
7017         set<int> adj;
7018         adj.insert((n+i+a)%n);
7019         adj.insert((n+i-a)%n);
7020         adj.insert((n+i+b)%n);
7021         adj.insert((n+i-a)%n);
7022         adj.insert((n+i+c)%n);
7023         adj.insert((n+i-c)%n);
7024         for (set<int>::const_iterator it=adj.begin();it!=adj.end();++it) {
7025             G.add_edge(i,*it);
7026         }
7027     }
7028     if (gv.size()==4) {
7029         int d=gv[3].val;
7030         for (int i=0;i<n;++i) {
7031             set<int> adj;
7032             adj.insert((n+i+d)%n);
7033             adj.insert((n+i-d)%n);
7034             adj.insert((n+i+a+b)%n);
7035             adj.insert((n+i-a-b)%n);
7036             adj.insert((n+i+b+c)%n);
7037             adj.insert((n+i-b-c)%n);
7038             for (set<int>::const_iterator it=adj.begin();it!=adj.end();++it) {
7039                 G.add_edge(i,*it);
7040             }
7041         }
7042     }
7043     return G.to_gen();
7044 }
7045 static const char _tonnetz_s[]="tonnetz";
7046 static define_unary_function_eval(__tonnetz,&_tonnetz,_tonnetz_s);
7047 define_unary_function_ptr5(at_tonnetz,alias_at_tonnetz,&__tonnetz,0,true)
7048 
7049 /* USAGE:   truncate_graph(G)
7050  *
7051  * Returns the graph obtained by truncating the biconnected planar graph G.
7052  */
_truncate_graph(const gen & g,GIAC_CONTEXT)7053 gen _truncate_graph(const gen &g,GIAC_CONTEXT) {
7054     if (g.type==_STRNG && g.subtype==-1) return g;
7055     graphe G(contextptr,false),H(contextptr);
7056     if (!G.read_gen(g))
7057         return gt_err(_GT_ERR_NOT_A_GRAPH);
7058     if (G.is_directed())
7059         return gt_err(_GT_ERR_UNDIRECTED_GRAPH_REQUIRED);
7060     if (G.is_empty())
7061         return generr("graph is empty");
7062     if (!G.is_biconnected())
7063         return gt_err(_GT_ERR_BICONNECTED_GRAPH_REQUIRED);
7064     graphe::ivectors faces;
7065     if (!G.demoucron(faces))
7066         return gt_err(_GT_ERR_NOT_PLANAR);
7067     G.truncate(H,faces);
7068     return H.to_gen();
7069 }
7070 static const char _truncate_graph_s[]="truncate_graph";
7071 static define_unary_function_eval(__truncate_graph,&_truncate_graph,_truncate_graph_s);
7072 define_unary_function_ptr5(at_truncate_graph,alias_at_truncate_graph,&__truncate_graph,0,true)
7073 
7074 /* USAGE:   find_cycles(G,[length=k||lb..ub])
7075  *
7076  * Returns the list of elementary cycles of the digraph G. If length option is
7077  * specified, only cycles of length k resp. of length between lb and ub are
7078  * returned.
7079  */
_find_cycles(const gen & g,GIAC_CONTEXT)7080 gen _find_cycles(const gen &g,GIAC_CONTEXT) {
7081     if (g.type==_STRNG && g.subtype==-1) return g;
7082     if (g.type!=_VECT)
7083         return gentypeerr(contextptr);
7084     int lb=-1,ub=-1;
7085     if (g.subtype==_SEQ__VECT) {
7086         if (g._VECTptr->size()!=2)
7087             return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
7088         if (!g._VECTptr->at(1).is_symb_of_sommet(at_equal))
7089             return gensizeerr(contextptr);
7090         gen &opt=g._VECTptr->at(1);
7091         if (opt._SYMBptr->feuille._VECTptr->front()!=at_length)
7092             return gensizeerr(contextptr);
7093         gen &val=opt._SYMBptr->feuille._VECTptr->back();
7094         if (val.is_integer() && val.val>0)
7095             lb=ub=val.val;
7096         else if (val.is_symb_of_sommet(at_interval)) {
7097             gen &lo=val._SYMBptr->feuille._VECTptr->front();
7098             gen &hi=val._SYMBptr->feuille._VECTptr->back();
7099             if (!lo.is_integer() || !hi.is_integer())
7100                 return gensizeerr(contextptr);
7101             lb=lo.val;
7102             ub=hi.val;
7103             if (lb<0 || ub<0 || lb>ub)
7104                 return gensizeerr(contextptr);
7105         }
7106     }
7107     graphe G(contextptr);
7108     if (!G.read_gen(g.subtype==_SEQ__VECT?g._VECTptr->front():g))
7109         return gt_err(_GT_ERR_NOT_A_GRAPH);
7110     if (G.is_empty())
7111         return generr("graph is empty");
7112     if (!G.is_directed())
7113         return gt_err(_GT_ERR_DIRECTED_GRAPH_REQUIRED);
7114     graphe::ivectors cyc;
7115     G.elementary_cycles(cyc,lb,ub);
7116     vecteur res;
7117     G.ivectors2vecteur(cyc,res,false);
7118     return change_subtype(res,_LIST__VECT);
7119 }
7120 static const char _find_cycles_s[]="find_cycles";
7121 static define_unary_function_eval(__find_cycles,&_find_cycles,_find_cycles_s);
7122 define_unary_function_ptr5(at_find_cycles,alias_at_find_cycles,&__find_cycles,0,true)
7123 
7124 /* USAGE:   kspaths(G,u,v,k)
7125  *
7126  * Returns the list of k shortest paths between vertices u and v in the
7127  * (weighted) digraph G.
7128  */
_kspaths(const gen & g,GIAC_CONTEXT)7129 gen _kspaths(const gen &g,GIAC_CONTEXT) {
7130     if (g.type==_STRNG && g.subtype==-1) return g;
7131     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
7132         return gentypeerr(contextptr);
7133     const vecteur &gv=*g._VECTptr;
7134     if (gv.size()!=4)
7135         return gt_err(_GT_ERR_WRONG_NUMBER_OF_ARGS);
7136     graphe G(contextptr);
7137     if (!G.read_gen(gv.front()))
7138         return gt_err(_GT_ERR_NOT_A_GRAPH);
7139     if (G.is_empty())
7140         return generr("graph is empty");
7141     graphe::ivectors paths;
7142     int k,src,dest;
7143     src=G.node_index(gv[1]);
7144     dest=G.node_index(gv[2]);
7145     if (src<0 || dest<0)
7146         return gt_err(_GT_ERR_VERTEX_NOT_FOUND);
7147     if (src==dest)
7148         return generr("source and destination vertices must be different");
7149     if (!gv.back().is_integer() || (k=gv.back().val)<=0)
7150         return gt_err(_GT_ERR_POSITIVE_INTEGER_REQUIRED);
7151     G.yen_ksp(k,src,dest,paths);
7152     vecteur res;
7153     G.ivectors2vecteur(paths,res);
7154     return change_subtype(res,_LIST__VECT);
7155 }
7156 static const char _kspaths_s[]="kspaths";
7157 static define_unary_function_eval(__kspaths,&_kspaths,_kspaths_s);
7158 define_unary_function_ptr5(at_kspaths,alias_at_kspaths,&__kspaths,0,true)
7159 
nexcom(int n,int k,int & h,int & t,vector<int> & r,bool & mtc)7160 void nexcom(int n,int k,int &h,int &t,vector<int> &r,bool &mtc) {
7161     int i;
7162     if (mtc) goto label20;
7163     r[0]=n;
7164     t=n;
7165     h=0;
7166     if (k==1) goto label15;
7167     for (i=1;i<k;++i) {
7168         r[i]=0;
7169     }
7170 label15:
7171     mtc=r.back()!=n;
7172     return;
7173 label20:
7174     if (t>1) h=0;
7175     ++h;
7176     t=r[h-1];
7177     r[h-1]=0;
7178     r[0]=t-1;
7179     ++r[h];
7180     goto label15;
7181 }
7182 
7183 /* USAGE:   icomp(n,k,[zeros=true or false])
7184  *
7185  * Returns the list of all decompositions of n into k parts (the order of parts
7186  * matters). If zeros is set to false, the compositions containing zero are
7187  * omitted (by default, zeros=true).
7188  */
_icomp(const gen & g,GIAC_CONTEXT)7189 gen _icomp(const gen &g,GIAC_CONTEXT) {
7190     if (g.type==_STRNG && g.subtype==-1) return g;
7191     if (g.type!=_VECT || g.subtype!=_SEQ__VECT)
7192         return gentypeerr(contextptr);
7193     vecteur gv=*g._VECTptr;
7194     if (gv.size()<1)
7195         return gensizeerr(contextptr);
7196     bool zer=true;
7197     if (gv.back().is_symb_of_sommet(at_equal)) {
7198         gen &lh=gv.back()._SYMBptr->feuille._VECTptr->front();
7199         gen &rh=gv.back()._SYMBptr->feuille._VECTptr->back();
7200         if (lh!=at_zeros || !rh.is_integer())
7201             return gensizeerr(contextptr);
7202         zer=(bool)rh.val;
7203         gv.pop_back();
7204     }
7205     if (gv.size()!=2)
7206         return gensizeerr(contextptr);
7207     gen &N=gv.front(),&K=gv.back();
7208     if (!is_integer(N) || !is_integer(K) || N.val<=0 || K.val<=0 || K.val>N.val)
7209         return gensizeerr(contextptr);
7210     int n=N.val,k=K.val,h,t;
7211     vector<int> r(k);
7212     bool mtc=false;
7213     vecteur res;
7214     do {
7215         nexcom(n,k,h,t,r,mtc);
7216         res.push_back(vector_int_2_vecteur(r,contextptr));
7217     } while (mtc);
7218     if (!zer) {
7219         for (int i=res.size();i-->0;) {
7220             if (!is_zero(_count_eq(makesequence(0,res[i]),contextptr)))
7221                 res.erase(res.begin()+i);
7222         }
7223     }
7224     return res;
7225 }
7226 static const char _icomp_s[]="icomp";
7227 static define_unary_function_eval(__icomp,&_icomp,_icomp_s);
7228 define_unary_function_ptr5(at_icomp,alias_at_icomp,&__icomp,0,true)
7229 
7230 #ifndef NO_NAMESPACE_GIAC
7231 }
7232 #endif // ndef NO_NAMESPACE_GIAC
7233