1 /*
2 
3 Cadabra: a field-theory motivated computer algebra system.
4 Copyright (C) 2001-2015  Kasper Peeters <kasper.peeters@phi-sci.com>
5 
6 This program is free software: you can redistribute it and/or
7 	modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation, either version 3 of the
9 License, or (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 GNU
14 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 
21 #include <stddef.h>
22 #include "Algorithm.hh"
23 #include "Storage.hh"
24 #include "Props.hh"
25 #include "Cleanup.hh"
26 #include <typeinfo>
27 #include <boost/version.hpp>
28 #if BOOST_VERSION > 105500
29 #include <boost/core/demangle.hpp>
30 #endif
31 //#include <boost/stacktrace.hpp>
32 
33 #include "properties/Accent.hh"
34 #include "properties/Derivative.hh"
35 #include "properties/Indices.hh"
36 #include "properties/Coordinate.hh"
37 #include "properties/Symbol.hh"
38 #include "properties/Trace.hh"
39 #include "properties/DependsBase.hh"
40 
41 #include <sstream>
42 
43 //#define DEBUG
44 
45 using namespace cadabra;
46 
Algorithm(const Kernel & k,Ex & tr_)47 Algorithm::Algorithm(const Kernel& k, Ex& tr_)
48 	: IndexClassifier(k),
49 	  interrupted(false),
50 	  number_of_calls(0), number_of_modifications(0),
51 	  suppress_normal_output(false),
52 	  discard_command_node(false),
53 	  tr(tr_),
54 	  pm(0),
55 	  traverse_ldots(false)
56 	{
57 	}
58 
~Algorithm()59 Algorithm::~Algorithm()
60 	{
61 	}
62 
set_progress_monitor(ProgressMonitor * pm_)63 void Algorithm::set_progress_monitor(ProgressMonitor *pm_)
64 	{
65 	pm=pm_;
66 	}
67 
apply_pre_order(bool repeat)68 Algorithm::result_t Algorithm::apply_pre_order(bool repeat)
69 	{
70 #if BOOST_VERSION > 105500
71 	if(pm)
72 		pm->group(boost::core::demangle(typeid(*this).name()).c_str());
73 #else
74 	if(pm)
75 		pm->group(typeid(*this).name());
76 #endif
77 
78 	result_t ret=result_t::l_no_action;
79 	Ex::iterator start=tr.begin();
80 	while(start!=tr.end()) {
81 		if(traverse_ldots || !tr.is_hidden(start)) {
82 			if(start->is_index()==false) {
83 				auto aor=apply_once(start);
84 				if(aor==result_t::l_applied || aor==result_t::l_applied_no_new_dummies) {
85 					ret=result_t::l_applied;
86 					// Need to cleanup on the entire tree above us.
87 
88 					if(!repeat) {
89 						start.skip_children();
90 						++start;
91 						}
92 					}
93 				else ++start;
94 				}
95 			else ++start;
96 			}
97 		else {
98 			++start;
99 			}
100 		}
101 
102 	cleanup_dispatch_deep(kernel, tr);
103 
104 	if(pm) pm->group();
105 	return ret;
106 	}
107 
apply_generic(bool deep,bool repeat,unsigned int depth)108 Algorithm::result_t Algorithm::apply_generic(bool deep, bool repeat, unsigned int depth)
109 	{
110 	auto it = tr.begin();
111 	return apply_generic(it, deep, repeat, depth);
112 	}
113 
apply_generic(Ex::iterator & it,bool deep,bool repeat,unsigned int depth)114 Algorithm::result_t Algorithm::apply_generic(Ex::iterator& it, bool deep, bool repeat, unsigned int depth)
115 	{
116 #if BOOST_VERSION > 105500
117 	if(pm)
118 		pm->group(boost::core::demangle(typeid(*this).name()).c_str());
119 #else
120 	if(pm)
121 		pm->group(typeid(*this).name());
122 #endif
123 
124 	result_t ret=result_t::l_no_action;
125 
126 	Ex::fixed_depth_iterator start=tr.begin_fixed(it, depth, false);
127 #ifdef DEBUG
128 	std::cerr << "apply_generic at " << it.node << " " << *it->name << " " << *start->name << std::endl;
129 #endif
130 
131 	while(tr.is_valid(start)) {
132 		//		std::cerr << "evaluate main loop at " << *start->name << std::endl;
133 #ifdef DEBUG
134 		std::cerr << "main loop for " << typeid(*this).name() << ":\n" << Ex(start) << std::endl;
135 #endif
136 
137 		result_t thisret=result_t::l_no_action;
138 		Ex::iterator enter(start);
139 		Ex::fixed_depth_iterator next(start);
140 		++next;
141 		//		if(tr.is_valid(next))
142 		//			std::cerr << "next = " << *next->name << std::endl;
143 		do {
144 			//			std::cout << "apply at " << *enter->name << std::endl;
145 			bool work_is_topnode=(enter==it);
146 			if(deep && depth==0)
147 				thisret = apply_deep(enter);
148 			else
149 				thisret = apply_once(enter);
150 
151 			if(work_is_topnode)
152 				it=enter;
153 
154 			// FIXME: handle l_error or remove
155 			if(thisret==result_t::l_applied || thisret==result_t::l_applied_no_new_dummies)
156 				ret=result_t::l_applied;
157 			}
158 		while(depth==0 && repeat && (thisret==result_t::l_applied || thisret==result_t::l_applied_no_new_dummies));
159 
160 		if(depth==0) {
161 			// std::cerr << "break " << std::endl;
162 			break;
163 			}
164 		else {
165 			// std::cerr << "no break " << std::endl;
166 			}
167 		start=next;
168 		}
169 
170 	// std::cerr << "pre-exit node " << it.node << std::endl;
171 
172 	// If we are acting at fixed depth, we will not have gone up in the
173 	// tree, so missed one cleanup action. Do it now.
174 	if(depth>0) {
175 		Ex::fixed_depth_iterator start=tr.begin_fixed(it, depth-1, false);
176 		while(tr.is_valid(start)) {
177 			Ex::iterator work=start;
178 			++start;
179 			bool cpy=false;
180 			if(work==it) cpy=true;
181 			cleanup_dispatch(kernel, tr, work);
182 			if(cpy) it=work;
183 			}
184 		}
185 
186 	// std::cerr << "exit node " << it.node << std::endl;
187 
188 	//	if(tr.is_valid(it)) {
189 	//		std::cerr << "exit " << *it->name << std::endl;
190 	//		std::cerr << "exit apply_generic\n" << Ex(it) << std::endl;
191 	//		}
192 
193 	if(pm) pm->group();
194 	return ret;
195 	}
196 
apply_once(Ex::iterator & it)197 Algorithm::result_t Algorithm::apply_once(Ex::iterator& it)
198 	{
199 	// std::cerr << "=== apply_once ===" << std::endl;
200 	if(traverse_ldots || !tr.is_hidden(it)) {
201 		if(can_apply(it)) {
202 			result_t res=apply(it);
203 			// std::cerr << "apply algorithm at " << *it->name << std::endl;
204 			if(res==result_t::l_applied || res==result_t::l_applied_no_new_dummies) {
205 				cleanup_dispatch(kernel, tr, it);
206 				return res;
207 				}
208 			}
209 		}
210 
211 	return result_t::l_no_action;
212 	}
213 
apply_deep(Ex::iterator & it)214 Algorithm::result_t Algorithm::apply_deep(Ex::iterator& it)
215 	{
216 	// This recursive algorithm walks the tree depth-first
217 	// (parent-after-child). The algorithm is applied on each node if
218 	// can_apply returns true. When the iterator goes up one level
219 	// (i.e. from a child to a parent), and any changes have been made
220 	// so far at the child level level, cleanup and simplification
221 	// routines will be called. The only nodes that can be removed from
222 	// the tree are nodes at a lower level than the simplification
223 	// node.
224 
225 	// std::cout << "=== apply_deep ===" << std::endl;
226 	//	tr.print_recursive_treeform(std::cout, it);
227 
228 	post_order_iterator current=it;
229 	current.descend_all();
230 	post_order_iterator last=it;
231 	int deepest_action = -1;
232 	//	std::cout << "apply_deep: it = " << *it->name << std::endl;
233 	bool stop_after_this_one=false;
234 	result_t some_changes_somewhere=result_t::l_no_action;
235 
236 	for(;;) {
237 		//		std::cout << "reached " << *current->name << std::endl;
238 #ifdef DEBUG
239 		std::cout << "apply_deep " << typeid(*this).name() << ": current = " << *current->name << std::endl;
240 #endif
241 
242 		if(current.node==last.node) {
243 			//			std::cout << "stop after this one" << std::endl;
244 			stop_after_this_one=true;
245 			}
246 
247 		if(deepest_action > tr.depth(current)) {
248 #ifdef DEBUG
249 			std::cerr << "simplify; we are at " << *(current->name) << std::endl;
250 #endif
251 			iterator work=current;
252 			bool work_is_topnode=(work==it);
253 			cleanup_dispatch(kernel, tr, work);
254 			current=work;
255 			if(work_is_topnode)
256 				it=work;
257 #ifdef DEBUG
258 			std::cerr << "current now " << *(current->name) << std::endl;
259 			tr.print_recursive_treeform(std::cerr, current);
260 #endif
261 			deepest_action = tr.depth(current); // needs to propagate upwards
262 			}
263 
264 		if((traverse_ldots || !tr.is_hidden(current)) && can_apply(current)) {
265 #ifdef DEBUG
266 			std::cout << "acting at " << *current->name << std::endl;
267 #endif
268 			iterator work=current;
269 			post_order_iterator next(current);
270 			++next;
271 			bool work_is_topnode=(work==it);
272 			result_t res = apply(work);
273 			if(res==Algorithm::result_t::l_applied || res==Algorithm::result_t::l_applied_no_new_dummies) {
274 				some_changes_somewhere=result_t::l_applied;
275 				if(res==Algorithm::result_t::l_applied) {
276 //					std::cerr << "rename replacement on " << work << std::endl;
277 					rename_replacement_dummies(work, true);
278 					}
279 				deepest_action=tr.depth(work);
280 				// If we got a zero at 'work', we need to propagate this up the tree and
281 				// then restart our post-order traversal such that everything that has
282 				// been removed from the tree by this zero will no longer be considered.
283 				if(*work->multiplier==0) {
284 #ifdef DEBUG
285 					std::cerr << "propagate zero up the tree" << std::endl;
286 					tr.print_recursive_treeform(std::cerr, it);
287 #endif
288 					post_order_iterator moved_next=work;
289 					propagate_zeroes(moved_next, it);
290 #ifdef DEBUG
291 					tr.print_recursive_treeform(std::cerr, it);
292 					std::cerr << Ex(it) << std::endl;
293 #endif
294 					next=moved_next;
295 					}
296 
297 				// The 'work' iterator can now point to a new node. If we were acting at the
298 				// top node, we need to propagate the change in 'work' to 'it' so the caller
299 				// knows where the new top node is.
300 				if(work_is_topnode)
301 					it=work;
302 				}
303 			// The algorithm may have replaced the 'work' node, so instead of walking from
304 			// there, we continue at the node which was next in line before we called 'apply'.
305 			current=next;
306 			}
307 		else {
308 			++current;
309 			}
310 
311 		if(stop_after_this_one)
312 			break;
313 
314 		}
315 
316 #ifdef DEBUG
317 	std::cerr << "recursive end **" << std::endl;
318 #endif
319 
320 	return some_changes_somewhere;
321 	}
322 
propagate_zeroes(post_order_iterator & it,const iterator & topnode)323 void Algorithm::propagate_zeroes(post_order_iterator& it, const iterator& topnode)
324 	{
325 	assert(*it->multiplier==0);
326 	if(it==topnode) return;
327 	if(tr.is_head(it)) return;
328 
329 	iterator walk=tr.parent(it);
330 #ifdef DEBUG
331 	std::cerr << "propagate_zeroes at " << *walk->name << std::endl;
332 #endif
333 //	if(!tr.is_valid(walk))
334 //		return;
335 
336 	const Derivative *der=kernel.properties.get<Derivative>(walk);
337 	const Trace *trace=kernel.properties.get<Trace>(walk);
338 	if(*walk->name=="\\prod" || der || trace) {
339 		if(der && it->is_index()) return;
340 		walk->multiplier=rat_set.insert(0).first;
341 		it=walk;
342 		propagate_zeroes(it, topnode);
343 		// Removing happens in the next step.
344 		}
345 	else if(*walk->name=="\\pow") {
346 		if(tr.index(it)==0) { // the argument
347 			walk->multiplier=rat_set.insert(0).first;
348 			it=walk;
349 			propagate_zeroes(it, topnode);
350 			}
351 		else {   // the exponent
352 			rset_t::iterator rem=walk->multiplier;
353 			tr.erase(it);
354 			tr.flatten(walk);
355 			it=tr.erase(walk);
356 			node_one(it);
357 			it->multiplier=rem;
358 			}
359 		}
360 	else if(*walk->name=="\\sum") {
361 		if(tr.number_of_children(walk)>2) {
362 			if(tr.is_valid(tr.next_sibling(it))) {
363 				it=tr.erase(it);
364 				it.descend_all();
365 				}
366 			else {
367 				iterator ret=tr.parent(it);
368 				tr.erase(it);
369 				it=ret;
370 				}
371 			}
372 		else {
373 			// If the sum is the top node, we cannot flatten it because
374 			// we are not allowed to invalidate the topnode iterator
375 			if(walk==topnode) {
376 #ifdef DEBUG
377 				std::cerr << "\\sum at top, cannot flatten" << std::endl;
378 #endif
379 				//				it=tr.next_sibling(it); // Added but wrong?
380 				return;
381 				}
382 
383 			tr.erase(it);
384 			iterator singlearg=tr.begin(walk);
385 			if(singlearg!=tr.end(walk)) {
386 				singlearg->fl.bracket=walk->fl.bracket; // to remove brackets of the sum
387 				if(*tr.parent(walk)->name=="\\prod") {
388 					multiply(tr.parent(walk)->multiplier, *singlearg->multiplier);
389 					cadabra::one(singlearg->multiplier);
390 					}
391 				}
392 			tr.flatten(walk);
393 			it=tr.erase(walk);
394 			if(*it->name=="\\prod" && *tr.parent(it)->name=="\\prod") {
395 				tr.flatten(it);
396 				it=tr.erase(it);
397 				}
398 			}
399 		}
400 	else {
401 		iterator nn=tr.insert_after(it, str_node("1"));
402 		nn->fl.parent_rel=it->fl.parent_rel;
403 		nn->fl.bracket=it->fl.bracket;
404 		it=tr.erase(it);
405 		zero(it->multiplier);
406 		}
407 
408 	return;
409 	}
410 
pushup_multiplier(iterator it)411 void Algorithm::pushup_multiplier(iterator it)
412 	{
413 	if(!tr.is_valid(it)) return;
414 	if(*it->multiplier!=1) {
415 		if(*it->name=="\\sum") {
416 			//			txtout << "SUM" << std::endl;
417 			sibling_iterator sib=tr.begin(it);
418 			while(sib!=tr.end(it)) {
419 				multiply(sib->multiplier, *it->multiplier);
420 				//				txtout << "going up" << std::endl;
421 				if(tr.is_head(it)==false)
422 					pushup_multiplier(tr.parent(it));
423 				//				txtout << "back and back up" << std::endl;
424 				pushup_multiplier(sib);
425 				//				txtout << "back" << std::endl;
426 				++sib;
427 				}
428 			::one(it->multiplier);
429 			}
430 		else {
431 			//			txtout << "PUSHUP: " << *it->name << std::endl;
432 			if(tr.is_head(it)==false) {
433 				//				txtout << "test propinherit" << std::endl;
434 				//				iterator tmp=tr.parent(it);
435 				// tmp not always valid?!? This one crashes hard with a loop!?!
436 				//				txtout << " of " << *tmp->name << std::endl;
437 				const PropertyInherit *pin=kernel.properties.get<PropertyInherit>(tr.parent(it));
438 				if(pin || *(tr.parent(it)->name)=="\\prod") {
439 					multiply(tr.parent(it)->multiplier, *it->multiplier);
440 					::one(it->multiplier); // moved up, was at end of block, correct?
441 					//					txtout << "going up" << std::endl;
442 					pushup_multiplier(tr.parent(it));
443 					//					txtout << "back" << std::endl;
444 					}
445 				//				else txtout << "not relevant" << std::endl;
446 				}
447 			}
448 		}
449 	}
450 
node_zero(iterator it)451 void Algorithm::node_zero(iterator it)
452 	{
453 	::zero(it->multiplier);
454 	tr.erase_children(it);
455 	it->name=name_set.insert("1").first;
456 	}
457 
node_one(iterator it)458 void Algorithm::node_one(iterator it)
459 	{
460 	::one(it->multiplier);
461 	tr.erase_children(it);
462 	it->name=name_set.insert("1").first;
463 	}
464 
node_integer(iterator it,int num)465 void Algorithm::node_integer(iterator it, int num)
466 	{
467 	::one(it->multiplier);
468 	tr.erase_children(it);
469 	it->name=name_set.insert("1").first;
470 	::multiply(it->multiplier, num);
471 	}
472 
index_parity(iterator it) const473 int Algorithm::index_parity(iterator it) const
474 	{
475 	sibling_iterator frst=tr.begin(tr.parent(it));
476 	sibling_iterator fnd(it);
477 	int sgn=1;
478 	while(frst!=fnd) {
479 		sgn=-sgn;
480 		++frst;
481 		}
482 	return sgn;
483 	}
484 
485 
number_of_indices(iterator it)486 unsigned int Algorithm::number_of_indices(iterator it)
487 	{
488 	unsigned int res=0;
489 	index_iterator indit=begin_index(it);
490 	while(indit!=end_index(it)) {
491 		++res;
492 		++indit;
493 		}
494 	return res;
495 	}
496 
get_index_set_name(iterator it) const497 std::string Algorithm::get_index_set_name(iterator it) const
498 	{
499 	const Indices *ind=kernel.properties.get<Indices>(it, true);
500 	if(ind) {
501 		return ind->set_name;
502 		// TODO: The logic was once as below, but it is no longer clear to
503 		// me why that would ever make sense.
504 		//		if(ind->parent_name!="") return ind->parent_name;
505 		//		else                     return ind->set_name;
506 		}
507 	else return " undeclared";
508 	}
509 
begin_index(iterator it) const510 index_iterator Algorithm::begin_index(iterator it) const
511 	{
512 	return index_iterator::begin(kernel.properties, it);
513 	}
514 
end_index(iterator it) const515 index_iterator Algorithm::end_index(iterator it) const
516 	{
517 	return index_iterator::end(kernel.properties, it);
518 	}
519 
520 
521 
522 
check_index_consistency(iterator it) const523 bool Algorithm::check_index_consistency(iterator it) const
524 	{
525 	index_map_t ind_free, ind_dummy;
526 	classify_indices(it,ind_free,ind_dummy);
527 	return true;
528 	}
529 
check_degree_consistency(iterator) const530 bool Algorithm::check_degree_consistency(iterator ) const
531 	{
532 	return true; // FIXME: this needs to be implemented.
533 	}
534 
check_consistency(iterator it) const535 bool Algorithm::check_consistency(iterator it) const
536 	{
537 	Stopwatch w1;
538 	w1.start();
539 	//	debugout << "checking consistency ... " << std::flush;
540 	assert(tr.is_head(it)==false);
541 	//	iterator entry=it;
542 	iterator end=it;
543 	end.skip_children();
544 	++end;
545 	while(it!=end) {
546 		if(interrupted)
547 			throw InterruptionException("check_consistency");
548 
549 		if(*it->name=="\\sum") {
550 			if(*it->multiplier!=1)
551 				throw ConsistencyException("Found \\sum node with non-unit multiplier.");
552 			else if(Ex::number_of_children(it)<2)
553 				throw ConsistencyException("Found a \\sum node with 0 or 1 child nodes.");
554 			else {
555 				sibling_iterator sumch=it.begin();
556 				str_node::bracket_t firstbracket=sumch->fl.bracket;
557 				while(*sumch->name=="\\sum" || *sumch->name=="\\prod") {
558 					++sumch;
559 					if(sumch==it.end()) break;
560 					else                  firstbracket=sumch->fl.bracket;
561 					}
562 				sumch=it.begin();
563 				while(sumch!=it.end()) {
564 					if(*sumch->name!="\\sum" && *sumch->name!="\\prod") {
565 						if(sumch->fl.bracket!=firstbracket)
566 							throw ConsistencyException("Found a \\sum node with different brackets on its children.");
567 						}
568 					//					else if(*sumch->name=="\\sum") {
569 					//						sibling_iterator sumchch=sumch.begin();
570 					//						while(sumchch!=sumch.end()) {
571 					//							if(sumchch->fl.bracket==str_node::b_none) {
572 					//								tr.print_recursive_treeform(debugout, entry);
573 					//								throw ConsistencyException("Found a sum node with \\sum child without bracketed children.");
574 					//								}
575 					//							++sumchch;
576 					//							}
577 					//						}
578 					++sumch;
579 					}
580 				}
581 			}
582 		else if(*it->name=="\\prod") {
583 			if(Ex::number_of_children(it)<=1)
584 				throw ConsistencyException("Found \\prod node with only 0 or 1 children.");
585 			sibling_iterator ch=it.begin();
586 			str_node::bracket_t firstbracket=ch->fl.bracket;
587 			while(*ch->name=="\\sum" || *ch->name=="\\prod") {
588 				++ch;
589 				if(ch==it.end())   break;
590 				else               firstbracket=ch->fl.bracket;
591 				}
592 			ch=it.begin();
593 			while(ch!=it.end()) {
594 				if(*ch->name!="\\prod" && *ch->name!="\\sum") {
595 					if(ch->fl.bracket!=firstbracket)
596 						throw ConsistencyException("Found \\prod node with different brackets on its children.");
597 					}
598 				if(*ch->multiplier!=1) {
599 					throw ConsistencyException("Found \\prod child with non-unit multiplier.");
600 					//					debugout << "node name " << *ch->name << ", multiplier " << *ch->multiplier << std::endl;
601 					//					inconsistent=true;
602 					//					break;
603 					}
604 				++ch;
605 				}
606 			}
607 		else if(*it->name=="\\sequence") {
608 			if(Ex::number_of_children(it)!=2)
609 				throw ConsistencyException("Found \\sequence node with incorrect (non-2) number of children.");
610 			}
611 		++it;
612 		}
613 
614 	w1.stop();
615 	//	debugout << "checking done..." << w1 << std::endl;
616 	return true;
617 	}
618 
report_progress(const std::string &,int,int,int count)619 void Algorithm::report_progress(const std::string&, int, int, int count)
620 	{
621 //	bool display=false;
622 //	if(count==2) display=true;
623 //	else {
624 //		if(report_progress_stopwatch.stopped()) {
625 //			display=true;
626 //			report_progress_stopwatch.start();
627 //			}
628 //		else {
629 //			if(report_progress_stopwatch.seconds()>0 || report_progress_stopwatch.useconds()>300000L) {
630 //				display=true;
631 //				report_progress_stopwatch.reset();
632 //				}
633 //			}
634 //		}
635 
636 	//	if(display) { // prevents updates at a rate of more than one per second
637 	//		if(eo->output_format==Ex_output::out_xcadabra) {
638 	//			txtout << "<progress>" << std::endl
639 	//					 << str << std::endl
640 	//					 << todo << std::endl
641 	//					 << done << std::endl
642 	//					 << count << std::endl
643 	//					 << "</progress>" << std::endl;
644 	//			}
645 	//		else {
646 	//			if(count==2)
647 	//				txtout << str << " (" << done << " of " << todo << " completed)" << std::endl;
648 	//			}
649 	//		}
650 	}
651 
rename_replacement_dummies(iterator two,bool still_inside_algo)652 bool Algorithm::rename_replacement_dummies(iterator two, bool still_inside_algo)
653 	{
654 #ifdef DEBUG
655 	std::cerr << "renaming in " << two << std::endl;
656 #endif
657 	//	std::cout << "full story " << *two->name << std::endl;
658 	//	print_classify_indices(two);
659 	//	std::cout << "replacement" << std::endl;
660 	//	print_classify_indices(std::cout, two);
661 
662 	index_map_t ind_free, ind_dummy;
663 	index_map_t ind_free_full, ind_dummy_full;
664 
665 	if(false && still_inside_algo) {
666 		if(tr.is_head(two)==false)
667 			classify_indices_up(tr.parent(two), ind_free_full, ind_dummy_full);
668 		}
669 	else {
670 		classify_indices_up(two, ind_free_full, ind_dummy_full); // the indices in everything except the replacement
671 		}
672 	classify_indices(two, ind_free, ind_dummy); // the indices in the replacement subtree
673 #ifdef DEBUG
674 	std::cerr << "dummies of " << *two->name << std::endl;
675 	for(auto& ii: ind_dummy)
676 		std::cerr << ii.first << std::endl;
677 	std::cerr << "free indices above us" << std::endl;
678 	for(auto& ii: ind_free_full)
679 		std::cerr << ii.first << std::endl;
680 	std::cerr << "dummy indices above us" << std::endl;
681 	for(auto& ii: ind_dummy_full)
682 		std::cerr << ii.first << std::endl;
683 #endif
684 
685 	index_map_t must_be_empty;
686 	index_map_t newly_generated;
687 
688 	// Catch double index pairs
689 	determine_intersection(ind_dummy_full, ind_dummy, must_be_empty);
690 	index_map_t::iterator it=must_be_empty.begin();
691 	while(it!=must_be_empty.end()) {
692 #ifdef DEBUG
693 		std::cerr << "double index pair" << std::endl;
694 #endif
695 		Ex the_key=(*it).first;
696 		const Indices *dums=kernel.properties.get<Indices>(it->second, true);
697 		if(!dums)
698 			throw ConsistencyException("Failed to find dummy property for $"+*it->second->name+"$ while renaming dummies.");
699 		//			txtout << "failed to find dummy property for " << *it->second->name << std::endl;
700 		assert(dums);
701 		Ex relabel
702 		   =get_dummy(dums, &ind_dummy_full, &ind_dummy, &ind_free_full, &ind_free, &newly_generated);
703 		newly_generated.insert(index_map_t::value_type(Ex(relabel),(*it).second));
704 		//		txtout << " renamed to " << *relabel << std::endl;
705 		do {
706 			tr.replace_index((*it).second, relabel.begin(), true);
707 			//			(*it).second->name=relabel;
708 			++it;
709 			}
710 		while(it!=must_be_empty.end() && tree_exact_equal(&kernel.properties, (*it).first,the_key, 1, true, -2, true));
711 		}
712 
713 	// Catch triple indices (two cases: dummy pair in replacement, free index elsewhere and
714 	// dummy elsewhere, free index in replacement)
715 	must_be_empty.clear();
716 	//	newly_generated.clear(); // DO NOT ERASE, IDIOT!
717 
718 	determine_intersection(ind_free_full, ind_dummy, must_be_empty);
719 	//for(auto& ii: must_be_empty) {
720 	//	std::cerr << ii.first << std::endl;
721 	//	}
722 	it=must_be_empty.begin();
723 	while(it!=must_be_empty.end()) {
724 		//std::cerr << "triple index pair " << it->first << std::endl;
725 		Ex the_key=(*it).first;
726 		const Indices *dums=kernel.properties.get<Indices>(it->second, true);
727 		if(!dums)
728 			throw ConsistencyException("Failed to find dummy property for $"+*it->second->name+"$ while renaming dummies.");
729 		assert(dums);
730 		Ex relabel
731 		   =get_dummy(dums, &ind_dummy_full, &ind_dummy, &ind_free_full, &ind_free, &newly_generated);
732 		relabel.begin()->fl.parent_rel=it->second->fl.parent_rel;
733 		newly_generated.insert(index_map_t::value_type(relabel,(*it).second));
734 		do {
735 			tr.replace_index((*it).second, relabel.begin(), true);
736 			++it;
737 			}
738 		while(it!=must_be_empty.end() && tree_exact_equal(&kernel.properties, (*it).first,the_key, 1, true, -2, true));
739 		}
740 
741 	must_be_empty.clear();
742 	//	newly_generated.clear();
743 	determine_intersection(ind_free, ind_dummy_full, must_be_empty);
744 	it=must_be_empty.begin();
745 	while(it!=must_be_empty.end()) {
746 		// std::cerr << "triple index pair 2" << std::endl;
747 		Ex the_key=(*it).first;
748 		const Indices *dums=kernel.properties.get<Indices>(it->second, true);
749 		if(!dums)
750 			throw ConsistencyException("Failed to find dummy property for $"+*it->second->name+"$ while renaming dummies.");
751 		assert(dums);
752 		Ex relabel
753 		   =get_dummy(dums, &ind_dummy_full, &ind_dummy, &ind_free_full, &ind_free, &newly_generated);
754 		relabel.begin()->fl.parent_rel=it->second->fl.parent_rel;
755 		newly_generated.insert(index_map_t::value_type(relabel,(*it).second));
756 		do {
757 			tr.replace_index((*it).second, relabel.begin(), true);
758 			++it;
759 			}
760 		while(it!=must_be_empty.end() && tree_exact_equal(&kernel.properties, (*it).first,the_key, 1, true, -2, true));
761 		}
762 
763 	return true;
764 	}
765 
766 
767 
contains(sibling_iterator from,sibling_iterator to,sibling_iterator arg)768 bool Algorithm::contains(sibling_iterator from, sibling_iterator to, sibling_iterator arg)
769 	{
770 	while(from!=to) {
771 		if(from->name==arg->name) return true;
772 		++from;
773 		}
774 	return false;
775 	}
776 
find_arg_superset(range_vector_t & ran,sibling_iterator it)777 Algorithm::range_vector_t::iterator Algorithm::find_arg_superset(range_vector_t& ran,
778       sibling_iterator it)
779 	{
780 	sibling_iterator nxt=it;
781 	++nxt;
782 	return find_arg_superset(ran, it, nxt);
783 	}
784 
785 //void Algorithm::find_argument_lists(range_vector_t& ran, bool only_comma_lists) const
786 //	{
787 //	sibling_iterator argit=args_begin();
788 //	while(argit!=args_end()) {
789 //		if(*argit->name=="\\comma") {
790 //			ran.push_back(range_t(tr.begin(argit), tr.end(argit)));
791 //			}
792 //		else if(!only_comma_lists) {
793 //			sibling_iterator argnxt=argit; ++argnxt;
794 //			ran.push_back(range_t(argit, argnxt));
795 //			}
796 //		++argit;
797 //		}
798 //	}
799 
800 template<class Iter>
find_arg_superset(range_vector_t & ran,Iter st,Iter nd)801 Algorithm::range_vector_t::iterator Algorithm::find_arg_superset(range_vector_t& ran, Iter st, Iter nd)
802 	{
803 	range_vector_t::iterator ranit=ran.begin();
804 	while(ranit!=ran.end()) {
805 		sibling_iterator findthese=st;
806 		bool contained=true;
807 		while(findthese!=nd) {
808 			if(contains((*ranit).first, (*ranit).second, findthese)) {
809 				++findthese;
810 				}
811 			else {
812 				contained=false;
813 				break;
814 				}
815 			}
816 		if(contained) return ranit;
817 		++ranit;
818 		}
819 	return ran.end();
820 	}
821 
is_termlike(iterator it)822 bool Algorithm::is_termlike(iterator it)
823 	{
824 	if(*it->name=="\\equals") return false;
825 
826 	if(!is_factorlike(it))
827 		if(*it->name!="\\sum")
828 			if(it->fl.parent_rel==str_node::p_none)
829 				return true;
830 
831 	return false;
832 	}
833 
is_factorlike(iterator it)834 bool Algorithm::is_factorlike(iterator it)
835 	{
836 	if(Ex::is_head(it)) return false;
837 	if(*Ex::parent(it)->name=="\\prod")
838 		return true;
839 	return false;
840 	}
841 
842 // This only returns true if the indicated node is a single non-reserved node (non-prod, non-sum, ...)
843 // at the top level of an expression (real top, top of equation lhs/rhs, top of integral argument, ...).
844 
is_single_term(iterator it)845 bool Algorithm::is_single_term(iterator it)
846 	{
847 	if(*it->name!="\\prod" && *it->name!="\\sum" && *it->name!="\\asymimplicit"
848 	      && *it->name!="\\comma" && *it->name!="\\equals" && *it->name!="\\arrow") {
849 
850 		if(tr.is_head(it) || *tr.parent(it)->name=="\\equals" || *tr.parent(it)->name=="\\int") return true;
851 		else if(*tr.parent(it)->name=="\\sum")
852 			return true;
853 		else if(*tr.parent(it)->name!="\\prod" && it->fl.parent_rel==str_node::parent_rel_t::p_none
854 		        && kernel.properties.get<Accent>(tr.parent(it))==0 ) {
855 #ifdef DEBUG
856 			std::cerr << "Found single term in " << tr.parent(it) << std::endl;
857 #endif
858 			return true;
859 			}
860 		}
861 	return false;
862 	}
863 
is_nonprod_factor_in_prod(iterator it)864 bool Algorithm::is_nonprod_factor_in_prod(iterator it)
865 	{
866 	if(*it->name!="\\prod" && *it->name!="\\sum" && *it->name!="\\asymimplicit" && *it->name!="\\comma"
867 	      && *it->name!="\\equals") {
868 		try {
869 			if(tr.is_head(it)==false && *tr.parent(it)->name=="\\prod")
870 				return true;
871 			}
872 		catch(navigation_error& ex) {
873 			// no parent, ignore
874 			}
875 		//		else return true;
876 		}
877 	return false;
878 	}
879 
prod_wrap_single_term(iterator & it)880 bool Algorithm::prod_wrap_single_term(iterator& it)
881 	{
882 	if(is_single_term(it)) {
883 		force_node_wrap(it, "\\prod");
884 		return true;
885 		}
886 	else return false;
887 	}
888 
sum_wrap_single_term(iterator & it)889 bool Algorithm::sum_wrap_single_term(iterator& it)
890 	{
891 	if(is_single_term(it)) {
892 		force_node_wrap(it, "\\sum");
893 		return true;
894 		}
895 	else return false;
896 	}
897 
force_node_wrap(iterator & it,std::string nm)898 void Algorithm::force_node_wrap(iterator& it, std::string nm)
899 	{
900 	iterator prodnode=tr.insert(it, str_node(nm));
901 	sibling_iterator fr=it, to=it;
902 	++to;
903 	tr.reparent(prodnode, fr, to);
904 	prodnode->fl.bracket=it->fl.bracket;
905 	it->fl.bracket=str_node::b_none;
906 	if(nm!="\\sum") { // multipliers should sit on terms in a sum
907 		prodnode->multiplier=it->multiplier;
908 		one(it->multiplier);
909 		}
910 	it=prodnode;
911 	}
912 
prod_unwrap_single_term(iterator & it)913 bool Algorithm::prod_unwrap_single_term(iterator& it)
914 	{
915 	if((*it->name)=="\\prod") {
916 		if(tr.number_of_children(it)==1) {
917 			multiply(tr.begin(it)->multiplier, *it->multiplier);
918 			tr.begin(it)->fl.bracket=it->fl.bracket;
919 			tr.begin(it)->multiplier=it->multiplier;
920 			tr.flatten(it);
921 			it=tr.erase(it);
922 			return true;
923 			}
924 		}
925 	return false;
926 	}
927 
sum_unwrap_single_term(iterator & it)928 bool Algorithm::sum_unwrap_single_term(iterator& it)
929 	{
930 	if((*it->name)=="\\sum") {
931 		if(tr.number_of_children(it)==1) {
932 			multiply(tr.begin(it)->multiplier, *it->multiplier);
933 			tr.begin(it)->fl.bracket=it->fl.bracket;
934 			tr.begin(it)->multiplier=it->multiplier;
935 			tr.flatten(it);
936 			it=tr.erase(it);
937 			return true;
938 			}
939 		}
940 	return false;
941 	}
942 
separated_by_derivative(iterator i1,iterator i2,iterator check_dependence) const943 bool Algorithm::separated_by_derivative(iterator i1, iterator i2, iterator check_dependence) const
944 	{
945 	iterator lca = tr.lowest_common_ancestor(i1, i2);
946 
947 	// Walk up the tree from the first node until the LCA, flag any derivatives
948 	// with which we do not commute.
949 
950 	struct {
951 		bool operator()(const Kernel& kernel, Ex& tr, iterator walk, iterator lca, iterator check_dependence)
952 			{
953 			const Properties& pr=kernel.properties;
954 			do {
955 				walk=Ex::parent(walk);
956 				if(walk == lca) break;
957 				const Derivative *der=pr.get<Derivative>(walk);
958 				if(der) {
959 					if(tr.is_valid(check_dependence) ) {
960 						const DependsBase *dep = pr.get<DependsBase>(check_dependence);
961 						if(dep) {
962 							Ex deps=dep->dependencies(kernel, check_dependence);
963 							sibling_iterator depobjs=deps.begin(deps.begin());
964 							while(depobjs!=deps.end(deps.begin())) {
965 								if(walk->name == depobjs->name) {
966 									return true;
967 									}
968 								else {
969 									// compare all indices
970 									sibling_iterator indit=tr.begin(walk);
971 									while(indit!=tr.end(walk)) {
972 										if(indit->is_index()) {
973 											if(subtree_exact_equal(&pr, indit, depobjs))
974 												return true;
975 											}
976 										++indit;
977 										}
978 									}
979 								++depobjs;
980 								}
981 							return false; // Dependence found but not relevant here.
982 							}
983 						else return false;   // No dependence property found at all.
984 						}
985 					else return true;   // Should not check for dependence.
986 					}
987 				}
988 			while(walk != lca);
989 			return false;
990 			}
991 		} one_run;
992 
993 	if(one_run(kernel, tr, i1, lca, check_dependence)) return true;
994 	if(one_run(kernel, tr, i2, lca, check_dependence)) return true;
995 
996 	return false;
997 	}
998 
999 
1000 // bool Algorithm::cleanup_anomalous_products(Ex& tr, Ex::iterator& it)
1001 // 	{
1002 // 	if(*(it->name)=="\\prod") {
1003 // 		 if(tr.number_of_children(it)==0) {
1004 // 			  it->name=name_set.insert("1").first;
1005 // 			  return true;
1006 // 			  }
1007 // 		 else if(tr.number_of_children(it)==1) {
1008 // 			  tr.begin(it)->fl.bracket=it->fl.bracket;
1009 // 			  tr.begin(it)->multiplier=it->multiplier;
1010 // 			  tr.flatten(it);
1011 // 			  Ex::iterator tmp=tr.erase(it);
1012 // //			  txtout << "HERRE?" << std::endl;
1013 // 			  pushup_multiplier(tmp);
1014 // 			  it=tmp;
1015 // 			  return true;
1016 // 			  }
1017 // 		 }
1018 // 	return false;
1019 // 	}
1020 //
1021 
locate_single_object(Ex::iterator obj_to_find,Ex::iterator st,Ex::iterator nd,std::vector<unsigned int> & store)1022 unsigned int Algorithm::locate_single_object(Ex::iterator obj_to_find,
1023       Ex::iterator st, Ex::iterator nd,
1024       std::vector<unsigned int>& store)
1025 	{
1026 	unsigned int count=0;
1027 	unsigned int index=0;
1028 	while(st!=nd) {
1029 		Ex::iterator it1=st;
1030 		it1.skip_children();
1031 		++it1;
1032 		if(tr.equal(st, it1, obj_to_find, Algorithm::compare_)) {
1033 			++count;
1034 			store.push_back(index);
1035 			}
1036 		++st;
1037 		++index;
1038 		}
1039 	return count;
1040 	}
1041 
locate_object_set(const Ex & objs,Ex::iterator st,Ex::iterator nd,std::vector<unsigned int> & store)1042 bool Algorithm::locate_object_set(const Ex& objs,
1043                                   Ex::iterator st, Ex::iterator nd,
1044                                   std::vector<unsigned int>& store)
1045 	{
1046 	// Locate the objects in which to symmetrise. We use an integer
1047 	// index (offset wrt. 'st') rather than an iterator because the
1048 	// latter only apply to a single tree, not to its copies.
1049 
1050 	// We accept only a tree with a \comma node at the top.
1051 	Ex::iterator top=objs.begin();
1052 	if(*top->name!="\\comma")
1053 		top = objs.begin(objs.begin());
1054 
1055 	assert(*top->name=="\\comma");
1056 
1057 	Ex::sibling_iterator fst=objs.begin(top);
1058 	Ex::sibling_iterator fnd=objs.end(top);
1059 	while(fst!=fnd) {
1060 		Ex::iterator aim=fst;
1061 		if((*aim->name)=="\\comma") {
1062 			// Objects can themselves be lists of other objects (for instance
1063 			// when we want to symmetrise in index pairs).
1064 			if(locate_object_set(aim, st, nd, store)==false)
1065 				return false;
1066 			}
1067 		else {
1068 			if((*aim->name).size()==0 && tr.number_of_children(aim)==1)
1069 				aim=tr.begin(aim);
1070 			if(locate_single_object(aim, st, nd, store)!=1)
1071 				return false;
1072 			}
1073 		++fst;
1074 		}
1075 	return true;
1076 	}
1077 
1078 
1079 namespace cadabra {
1080 
1081 	// static functions
1082 
number_of_indices(const Properties & pr,iterator it)1083 	unsigned int Algorithm::number_of_indices(const Properties& pr, iterator it)
1084 		{
1085 		unsigned int res=0;
1086 		index_iterator indit=index_iterator::begin(pr, it);
1087 		while(indit!=index_iterator::end(pr, it)) {
1088 			++res;
1089 			++indit;
1090 			}
1091 		return res;
1092 		}
1093 
number_of_direct_indices(iterator it)1094 	unsigned int Algorithm::number_of_direct_indices(iterator it)
1095 		{
1096 		unsigned int res=0;
1097 		sibling_iterator sib=Ex::begin(it);
1098 		while(sib!=Ex::end(it)) {
1099 			if(sib->fl.parent_rel==str_node::p_sub || sib->fl.parent_rel==str_node::p_super)
1100 				++res;
1101 			++sib;
1102 			}
1103 		return res;
1104 		}
1105 
less_without_numbers(nset_t::iterator it1,nset_t::iterator it2)1106 	bool Algorithm::less_without_numbers(nset_t::iterator it1, nset_t::iterator it2)
1107 		{
1108 		std::string::const_iterator ch1=(*it1).begin();
1109 		std::string::const_iterator ch2=(*it2).begin();
1110 
1111 		while(ch1!=(*it1).end() && ch2!=(*it2).end()) {
1112 			if(isdigit(*ch1)) return true;   // bla1  < blaq
1113 			if(isdigit(*ch2)) return false;  // blaa !< bla1
1114 			if(*ch1>=*ch2)    return false;
1115 			++ch1;
1116 			++ch2;
1117 			}
1118 		if(ch1==(*it1).end()) {
1119 			if(ch2==(*it2).end())
1120 				return false;
1121 			else
1122 				return true;
1123 			}
1124 		return false;
1125 		}
1126 
equal_without_numbers(nset_t::iterator it1,nset_t::iterator it2)1127 	bool Algorithm::equal_without_numbers(nset_t::iterator it1, nset_t::iterator it2)
1128 		{
1129 		std::string::const_iterator ch1=(*it1).begin();
1130 		std::string::const_iterator ch2=(*it2).begin();
1131 
1132 		while(ch1!=(*it1).end() && ch2!=(*it2).end()) {
1133 			if(isdigit(*ch1)) {
1134 				if(isdigit(*ch2))
1135 					return true;
1136 				else
1137 					return false;
1138 				}
1139 			if(*ch1!=*ch2) return false;
1140 			++ch1;
1141 			++ch2;
1142 			}
1143 		if(ch1==(*it1).end()) {
1144 			if(ch2==(*it2).end())
1145 				return true;
1146 			else
1147 				return false;
1148 			}
1149 		return false;
1150 		}
1151 
compare_(const str_node & one,const str_node & two)1152 	bool Algorithm::compare_(const str_node& one, const str_node& two)
1153 		{
1154 		// If the obj->name is empty, this means that we look for a tree with
1155 		// anything as root, but the required index structure in obj.  This
1156 		// requires a slightly different 'equal_to' (one that always matches
1157 		// an empty node with a non-empty node).
1158 
1159 		if(/* one.fl.bracket!=two.fl.bracket || */ one.fl.parent_rel!=two.fl.parent_rel)
1160 			return false;
1161 
1162 		if((*two.name).size()==0)
1163 			return true;
1164 		else if(one.name==two.name)
1165 			return true;
1166 		return false;
1167 		}
1168 
1169 	}
1170