From 72c974de523815378a95c7157648622eb2c44190 Mon Sep 17 00:00:00 2001
From: "Vladimir V. Kisil" <kisilv@maths.leeds.ac.uk>
Date: Tue, 14 May 2019 20:42:50 +0100
Subject: [PATCH 3/3] Modify remove_dirac_ONE() to eliminate exceptions.

Previously remote_dirac_ONE() reported that an expression is not a
Clifford scalar by throwing an exception. This produced crashes in Qt
applications on Windows. Now exceptions are only used within the recursive
calls of remote_dirac_ONE(). There is a possibility to pass the information
on non-scalar expression through a Boolean variable.

This patch also adds some additional test to exam_clifford.cpp and
improves the tutorial description of the method.

Signed-off-by: Vladimir V. Kisil <kisilv@maths.leeds.ac.uk>
---
 check/exam_clifford.cpp | 15 +++++--
 doc/tutorial/ginac.texi |  9 +++-
 ginac/clifford.cpp      | 93 +++++++++++++++++++++++++----------------
 ginac/clifford.h        | 12 +++---
 4 files changed, 85 insertions(+), 44 deletions(-)

diff --git a/check/exam_clifford.cpp b/check/exam_clifford.cpp
index b067438b..9f1361fa 100644
--- a/check/exam_clifford.cpp
+++ b/check/exam_clifford.cpp
@@ -417,9 +417,18 @@ template <typename IDX> unsigned clifford_check6(const matrix &A)
 	realsymbol s("s"), t("t"), x("x"), y("y"), z("z");
 
 	ex c = clifford_unit(nu, A, 1);
-	e = lst_to_clifford(lst{t, x, y, z}, mu, A, 1) * lst_to_clifford(lst{1, 2, 3, 4}, c);
-	e1 = clifford_inverse(e);
-	result += check_equal_simplify_term2((e*e1).simplify_indexed(), dirac_ONE(1));
+	lst elem = { dirac_ONE(1), /* Clifford scalar */
+				 lst_to_clifford(lst{1, 0, 0, 0}, mu, A, 1),	/* Clifford vector */
+				 lst_to_clifford(lst{t, x, y, z}, mu, A, 1) * lst_to_clifford(lst{1, 2, 3, 4}, c) /* Clifford bi-vector */
+	};
+
+	for (int i = 0; i < 3; ++i) {
+		e1 = clifford_inverse(elem[i]);
+		result += check_equal_simplify_term2((elem[i]*e1).simplify_indexed(), dirac_ONE(1));
+		e =  3*pow(2*Pi, i)*elem[i];
+		e1 = clifford_inverse(e);
+		result += check_equal_simplify_term2((e*e1).simplify_indexed(), dirac_ONE(1));
+	}
 
 /* lst_to_clifford() and clifford_to_lst()  check for vectors*/
 	e = lst{t, x, y, z};
diff --git a/doc/tutorial/ginac.texi b/doc/tutorial/ginac.texi
index 65ca5918..cae5cb00 100644
--- a/doc/tutorial/ginac.texi
+++ b/doc/tutorial/ginac.texi
@@ -3576,9 +3576,16 @@ If a Clifford number happens to be a factor of
 expression by the function
 
 @example
-    ex remove_dirac_ONE(const ex & e);
+    ex remove_dirac_ONE(const ex & e, unsigned char rl = 0, bool *success = new bool);
 @end example
 
+The functions removes only @code{dirac_ONE()} with a representation
+label not smaller than @code{rl}. Correspondingly, the default value of
+@code{rl=0} removes all @code{dirac_ONE()}.  If the first argument is
+not a scalar multiple of @code{dirac_ONE()}, then the output of the function
+is not predictable, this situation is reported back by the value @code{false} of
+the Boolean variable @code{*success}.
+
 @cindex @code{canonicalize_clifford()}
 The function @code{canonicalize_clifford()} works for a
 generic Clifford algebra in a similar way as for Dirac gammas.
diff --git a/ginac/clifford.cpp b/ginac/clifford.cpp
index 860cce46..1fb473e7 100644
--- a/ginac/clifford.cpp
+++ b/ginac/clifford.cpp
@@ -1142,45 +1142,55 @@ ex clifford_prime(const ex & e)
 		return e;
 }
 
-ex remove_dirac_ONE(const ex & e, unsigned char rl, unsigned options)
+ex remove_dirac_ONE(const ex & e, unsigned char rl, bool *success, unsigned options)
 {
-	pointer_to_map_function_2args<unsigned char, unsigned> fcn(remove_dirac_ONE, rl, options | 1);
-	bool need_reevaluation = false;
+	//! For speed we do not evaluate a child any terms if a previous term raised non-success flag
+	if ((options & 1) && !(options & 2) && !(*success))
+		return 0;
+
+	pointer_to_map_function_3args<unsigned char, bool*, unsigned> fcn(remove_dirac_ONE, rl, success, options | 1);
 	ex e1 = e;
 	if (! (options & 1) )  { // is not a child
-		if (options & 2)
+		*success = true;
+		if (options & 2) // Dummy index summation need to be expanded
 			e1 = expand_dummy_sum(e, true);
 		e1 = canonicalize_clifford(e1);
 	}
-	
-	if (is_a<clifford>(e1) && ex_to<clifford>(e1).get_representation_label() >= rl) {
-		if (is_a<diracone>(e1.op(0)))
-			return 1;
-		else 
-			throw(std::invalid_argument("remove_dirac_ONE(): expression is a non-scalar Clifford number!"));
-	} else if (is_a<add>(e1) || is_a<ncmul>(e1) || is_a<mul>(e1)  
+
+	if (is_a<clifford>(e1)) {
+		if (is_a<diracone>(e1.op(0)) && ex_to<clifford>(e1).get_representation_label() >= rl) {
+			return 1; // dirac_ONE is removed
+		} else if (ex_to<clifford>(e1).get_representation_label() >= rl) {
+			*success = false;
+			return 0; // presence of clifford_units which is not balanced by removed dirac_ONE is dangerous
+		} else
+			return e1;  // other clifford_unit is not harmful
+	} else if (is_a<add>(e1) || is_a<ncmul>(e1) || is_a<mul>(e1)
 			   || is_a<matrix>(e1) || e1.info(info_flags::list)) {
-		if (options & 3) // is a child or was already expanded
-			return e1.map(fcn);
-		else
-			try {
-				return e1.map(fcn);
-			} catch (std::exception &p) {
-				need_reevaluation = true;
-			}
+		ex res = e1.map(fcn);
+		if (*success)
+			return res;
+		else if ((is_a<ncmul>(e1) || is_a<mul>(e1)) && (options & 2)) //! Products with unremovable clifford_units shall vanish to prevent crashes in add
+			return 0;
+		else if (options & 2) // no hope for a re-evaluation
+			return e1;
 	} else if (is_a<power>(e1)) {
-		if (options & 3) // is a child or was already expanded
-			return pow(remove_dirac_ONE(e1.op(0), rl, options | 1), e1.op(1));
-		else
-			try {
-				return pow(remove_dirac_ONE(e1.op(0), rl, options | 1), e1.op(1));
-			} catch (std::exception &p) {
-				need_reevaluation = true;
-			}
-	} 
-	if (need_reevaluation)
-		return remove_dirac_ONE(e, rl, options | 2);
-	return e1;
+		ex base = remove_dirac_ONE(e1.op(0), rl, success, options | 1);
+		if (*success)
+			return pow(base, e1.op(1));
+		else if (options & 2) // no hope for a re-evaluation
+			return e1;
+	}
+
+	if (!(options & 3) && !(*success)) {// try dummy summation index expansion of it was not done before
+		*success = true;
+		return  remove_dirac_ONE(e, rl, success,  2);
+	}
+
+	if (*success) // No problem to return the expression in the canonicalized form
+		return e1;
+	else //! If removal was only partially successful an inconsistent expression can appear
+		return 0; //! thus the expression is removed
 }
 
 int clifford_max_label(const ex & e, bool ignore_ONE)
@@ -1200,7 +1210,12 @@ int clifford_max_label(const ex & e, bool ignore_ONE)
 
 ex clifford_norm(const ex & e)
 {
-	return sqrt(remove_dirac_ONE(e * clifford_bar(e)));
+	bool success;
+	ex res = remove_dirac_ONE(e * clifford_bar(e), 0, &success);
+	if (success)
+		return sqrt(res);
+	else
+		throw(std::invalid_argument("clifford_norm(): norm is not scalar, the Clifford number is not a product of vectors?"));
 }
 	
 ex clifford_inverse(const ex & e)
@@ -1331,6 +1346,8 @@ lst clifford_to_lst(const ex & e, const ex & c, bool algebraic)
 {
 	GINAC_ASSERT(is_a<clifford>(c));
 	ex mu = c.op(1);
+	bool success;
+
 	if (! ex_to<idx>(mu).is_dim_numeric())
 		throw(std::invalid_argument("clifford_to_lst(): index should have a numeric dimension"));
 	unsigned int D = ex_to<numeric>(ex_to<idx>(mu).get_dim()).to_int();
@@ -1341,7 +1358,9 @@ lst clifford_to_lst(const ex & e, const ex & c, bool algebraic)
 				|| (! is_a<numeric>(pow(c.subs(mu == i, subs_options::no_pattern), 2))))
 				algebraic = false;
 	lst V; 
-	ex v0 = remove_dirac_ONE(canonicalize_clifford(e+clifford_prime(e)))/2;
+	ex v0 = remove_dirac_ONE(canonicalize_clifford(e+clifford_prime(e)), 0, &success)/2;
+	if (!success)
+		throw(std::invalid_argument("clifford_to_lst(): the argument is not a Clifford vector"));
 	if (! v0.is_zero())
 		V.append(v0);
 	ex e1 = canonicalize_clifford(e - v0 * dirac_ONE(ex_to<clifford>(c).get_representation_label())); 
@@ -1349,7 +1368,9 @@ lst clifford_to_lst(const ex & e, const ex & c, bool algebraic)
 		for (unsigned int i = 0; i < D; i++) 
 			V.append(remove_dirac_ONE(
 						simplify_indexed(canonicalize_clifford(e1 * c.subs(mu == i, subs_options::no_pattern) +  c.subs(mu == i, subs_options::no_pattern) * e1))
-						/ (2*pow(c.subs(mu == i, subs_options::no_pattern), 2))));
+						/ (2*pow(c.subs(mu == i, subs_options::no_pattern), 2)), 0, &success));
+		if (!success)
+			throw(std::invalid_argument("clifford_to_lst(): the argument is not a Clifford vector"));
 	} else {
 		try {
 			for (unsigned int i = 0; i < D; i++)
@@ -1358,7 +1379,9 @@ lst clifford_to_lst(const ex & e, const ex & c, bool algebraic)
 			/* Try to expand dummy summations to simplify the expression*/
 			e1 = canonicalize_clifford(expand_dummy_sum(e, true));
 			V.remove_all();
-			v0 = remove_dirac_ONE(canonicalize_clifford(e1+clifford_prime(e1)))/2;
+			v0 = remove_dirac_ONE(canonicalize_clifford(e1+clifford_prime(e1)), 0, &success)/2;
+			if (!success)
+				throw(std::invalid_argument("clifford_to_lst(): the argument is not a Clifford vector"));
 			if (! v0.is_zero()) {
 				V.append(v0);
 				e1 = canonicalize_clifford(e1 - v0 * dirac_ONE(ex_to<clifford>(c).get_representation_label())); 
diff --git a/ginac/clifford.h b/ginac/clifford.h
index c076d615..992adda7 100644
--- a/ginac/clifford.h
+++ b/ginac/clifford.h
@@ -294,13 +294,15 @@ inline ex clifford_bar(const ex & e) { return clifford_star_bar(e, true, 0); }
 inline ex clifford_star(const ex & e) { return clifford_star_bar(e, false, 0); }
 
 /** Replaces dirac_ONE's (with a representation_label no less than rl) in e with 1.
- *  For the default value rl = 0 remove all of them. Aborts if e contains any 
- *  clifford_unit with representation_label to be removed.
+ *  For the default value rl = 0 remove all of them. Any clifford_unit with any representation_label
+ * will be replaces by zero. An accurence of a such clifford_unit can be reported through the false
+ * value of the success parameter.
  *
  *  @param e Expression to be processed
- *  @param rl Value of representation label 
- *  @param options Defines some internal use */
-ex remove_dirac_ONE(const ex & e, unsigned char rl = 0, unsigned options = 0);
+ *  @param rl Value of representation label, all dirac_ONE with this or greater value will be processed
+ *  @param success It is changed to false if there is at least one clifford_unit() in the expression
+ *  @param options Defines some internal value for recursive calls, shall be ommited in user code */
+ex remove_dirac_ONE(const ex & e, unsigned char rl = 0, bool *success = new bool, unsigned options = 0);
 
 /** Returns the maximal representation label of a clifford object 
  *  if e contains at least one, otherwise returns -1 
-- 
2.20.1

