From 2d278ae34e35e08d7c7fc1633ced627462ca074e 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 | 15 ++++++--
 ginac/clifford.cpp      | 80 ++++++++++++++++++++++++++++++++++++-----
 ginac/clifford.h        | 15 ++++++--
 4 files changed, 110 insertions(+), 15 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..de9daf6f 100644
--- a/doc/tutorial/ginac.texi
+++ b/doc/tutorial/ginac.texi
@@ -3573,12 +3573,23 @@ then an exception is raised.
 @cindex @code{remove_dirac_ONE()}
 If a Clifford number happens to be a factor of
 @code{dirac_ONE()} then we can convert it to a ``real'' (non-Clifford)
-expression by the function
+expression by the functions
 
 @example
-    ex remove_dirac_ONE(const ex & e);
+    ex remove_dirac_ONE(const ex & e, bool *success, unsigned char rl = 0);
+    ex remove_dirac_ONE(const ex & e, unsigned char rl = 0);
 @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()} and an output of the
+functions is not predictable, then the first method reports this back by
+the value @code{false} of the Boolean variable @code{*success}. The
+second method in the case of a non-scalar Clifford numbers aborts with
+an exception raised. Usage of the second method may crash in Windows
+applications.
+
 @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..8ac83ed9 100644
--- a/ginac/clifford.cpp
+++ b/ginac/clifford.cpp
@@ -1142,6 +1142,57 @@ ex clifford_prime(const ex & e)
 		return e;
 }
 
+ex remove_dirac_ONE(const ex & e, bool *success, unsigned char rl, unsigned options)
+{
+	//! 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<bool*, unsigned char, unsigned> fcn(remove_dirac_ONE, success, rl, options | 1);
+	ex e1 = e;
+	if (! (options & 1) )  { // is not a child
+		*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)) {
+		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)) {
+		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)) {
+		ex base = remove_dirac_ONE(e1.op(0), success, rl, 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, success, rl,  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
+}
+
 ex remove_dirac_ONE(const ex & e, unsigned char rl, unsigned options)
 {
 	pointer_to_map_function_2args<unsigned char, unsigned> fcn(remove_dirac_ONE, rl, options | 1);
@@ -1152,13 +1203,13 @@ ex remove_dirac_ONE(const ex & e, unsigned char rl, unsigned options)
 			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 
+		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)  
+	} 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);
@@ -1177,7 +1228,7 @@ ex remove_dirac_ONE(const ex & e, unsigned char rl, unsigned options)
 			} catch (std::exception &p) {
 				need_reevaluation = true;
 			}
-	} 
+	}
 	if (need_reevaluation)
 		return remove_dirac_ONE(e, rl, options | 2);
 	return e1;
@@ -1200,7 +1251,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), &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 +1387,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 +1399,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)), &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 +1409,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)), &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 +1420,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)), &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..0dea3175 100644
--- a/ginac/clifford.h
+++ b/ginac/clifford.h
@@ -294,11 +294,22 @@ 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 
+ *  For the default value rl = 0 remove all of them. Any clifford_unit with any representation_label
+ * will be replaces by zero. An occurrence of a such clifford_unit can be reported through the false
+ * value of the success parameter.
+ *
+ *  @param e Expression to be processed
+ *  @param success It is changed to false if there is at least one clifford_unit() in the expression
+ *  @param rl Value of representation label, all dirac_ONE with this or greater value will be processed
+ *  @param options Defines some internal value for recursive calls, shall be ommited in user code */
+ex remove_dirac_ONE(const ex & e, bool *success, unsigned char rl = 0, unsigned options = 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 (raise an exception) if e contains any
  *  clifford_unit with representation_label to be removed.
  *
  *  @param e Expression to be processed
- *  @param rl Value of representation label 
+ *  @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);
 
-- 
2.20.1

