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