1# This files contains the functions:
2#   DTP_SolveEquation
3#   DTP_Inverse
4#	DTP_Exp
5#   DTP_NormalForm
6#   DTP_Order
7#
8#	DTP_DetermineMultiplicationFunction
9#   DTP_IsInNormalForm
10#	_DTP_DetermineNormalForm
11#   _DTP_DetermineOrder
12#
13#   DTP_PCP_SolveEquation
14#	DTP_PCP_Inverse
15#   DTP_PCP_Exp
16#   DTP_PCP_NormalForm
17#   DTP_PCP_Order
18##############################################################################
19
20# Each application (DTP_SolveEquation, DTP_NormalForm, DTP_Order, ...) may
21# take an DTObj where DTObj![PC_DTPPolynomials] is either the output of the
22# function DTP_DTpols_rs or the output of DTP_DTpols_r. Let
23# polynomials := DTObj![PC_DTPPolynomials].
24# Since the condition IsInt(polynomials[1][1][1]) is true if and only if
25# "polynomials" is the ouput of DTP_DTpols_r, we can decide which
26# multiplication function we should use for computations. Explanation:
27# For DTP_DTpols_r:
28#	polynomials[1] <-> f_1
29#	polynomials[1][1] <-> first g_alpha in f_1
30#	polynomials[1][1][1] <-> constant coefficient in first g_alpha in f_1
31#
32# For DTP_DTpols_rs:
33#	polynomials[1] <-> list of the n polynomials f_{r, 1} (1 <= r <= n)
34#	polynomials[1][1] <-> polynomial f_{1, 1}
35#	polynomials[1][1][1] <-> first g_alpha in f_{1, 1}, this is a list
36#
37# Hence, we can determine the suitable multiplication function "multiply"
38# by using
39# 	if IsInt(DTObj![PC_DTPPolynomials][1][1][1]) then
40# 		# version f_r
41# 		multiply := DTP_Multiply_r;
42# 	else
43# 		# version f_rs
44# 		multiply := DTP_Multiply_rs;
45# 	fi;
46# Input: 	DTObj
47# Output: 	function DTP_Multiply_r or DTP_Multiply_rs depending on whether
48#			polynomials f_r or f_rs were computed for DTObj.
49InstallGlobalFunction( DTP_DetermineMultiplicationFunction,
50function(DTObj)
51	if IsInt(DTObj![PC_DTPPolynomials][1][1][1]) then
52		# version f_r
53		return DTP_Multiply_r;
54	else
55		# version f_rs
56		return DTP_Multiply_rs;
57	fi;
58end) ;
59
60# Input: 	- exponent vectors x, z
61#			- DTObj
62# Output:	exponent vector y such that for the corresponding elements
63#			x * y = z. If DTObj![PC_DTPConfluent] = true, y describes a normal
64#			form.
65InstallGlobalFunction( DTP_SolveEquation,
66function(x, z, DTObj)
67	local y, i, n, multiply;
68
69	multiply := DTP_DetermineMultiplicationFunction(DTObj);
70
71	n := DTObj![PC_NUMBER_OF_GENERATORS];
72	y := [];
73
74	# both exponent vectors must have length n
75	if Length(x) <> n or Length(z) <> n then
76		Error("Exponent vectors x and z must have length ", n);
77	fi;
78
79	for i in [1 .. n] do
80		# Loop invariant:
81		# x = a_1^z_1 ... a_{i-1}^{z_{i-1}} * a_i^x_i ... a_n^x_n
82		y[i] := z[i] - x[i];
83		# calculate x * a_i^y_i
84		x := multiply(x, ExponentsByObj(DTObj, [i, y[i]]), DTObj);
85	od;
86	# Now by the loop invariant: x = z
87	# On the other hand: x = x * y by construction
88
89	if DTObj![PC_DTPConfluent] then
90		return DTP_NormalForm(y, DTObj);
91	else
92		return y;
93	fi;
94end) ;
95
96# Input: 	pcp elements pcp1, pcp2 belonging to the same collector
97# Output:	pcp element res such that pcp1 * res = pcp2
98InstallGlobalFunction( DTP_PCP_SolveEquation,
99function(pcp1, pcp2)
100	local dtobj;
101
102	dtobj := Collector(pcp1);
103
104	if not IsDTObj(dtobj) then
105		Error("Collector(pcp1) must be a DTObj");
106	elif not Collector(pcp2) = dtobj then
107		Error("pcp1 and pcp2 must belong to the same DTObj");
108	else
109		return PcpElementByExponentsNC(dtobj, DTP_SolveEquation(Exponents(pcp1), Exponents(pcp2), dtobj));
110	fi;
111
112end );
113
114# Input: 	- exponent vector x
115#			- DTObj
116# Output:	exponent vector of x^{-1}. If DTObj![PC_DTPConfluent] = true, y
117#			describes a normal form.
118InstallGlobalFunction( DTP_Inverse,
119function(x, DTObj)
120	local n;
121	n := DTObj![PC_NUMBER_OF_GENERATORS];
122	return DTP_SolveEquation(x, [1 .. n] * 0, DTObj);
123end) ;
124
125# Input: 	pcp element pcp
126# Output:	inverse of pcp as pcp element
127InstallGlobalFunction( DTP_PCP_Inverse,
128function(pcp)
129	local dtobj;
130
131	dtobj := Collector(pcp);
132
133	if not IsDTObj(dtobj) then
134		Error("Collector(pcp) must be a DTObj");
135	else
136		return PcpElementByExponentsNC(dtobj, DTP_Inverse(Exponents(pcp), dtobj));
137	fi;
138
139end );
140
141# DTP_IsInNormalFrom checks whether the element described by the exponent
142# vector x is in normal form or not.
143# It returns "true", if x describes a normal form and otherwise
144# the smallest generator index for which the condition
145# 	r[i] <> 0 and (x[i] < 0 or x[i] >= r[i])
146# is NOT fulfilled is returned. Here, r = RelativeOrders(coll).
147InstallGlobalFunction( DTP_IsInNormalForm,
148function(x, coll)
149	local i, r;
150
151	r := RelativeOrders(coll);
152	# If the relative order r[i] is finite, check whether
153	# 0 <= x[i] < r[i] is fulfilled.
154	for i in [1 .. coll![PC_NUMBER_OF_GENERATORS]] do
155		if r[i] <> 0 then
156			if x[i] < 0 or x[i] >= r[i] then
157				return i;
158			fi;
159		fi;
160	od;
161
162	return true;
163end) ;
164
165
166# Input: 	- exponent vector x
167#			- integer q
168#			- DTObj
169# Output: 	exponent vector of x^q. If DTObj![PC_DTPConfluent] = true, then
170#			the result is in normal form.
171InstallGlobalFunction( DTP_Exp,
172function(x, q, DTObj)
173	local multiply, y;
174
175	multiply := DTP_DetermineMultiplicationFunction(DTObj);
176	y := [1 .. NumberOfGenerators(DTObj)] *0; # y = 1
177	if q < 0 then
178		x := DTP_Inverse(x, DTObj);
179		q := -q;
180	fi;
181	while q > 0 do
182		if q mod 2 = 1 then
183			y := multiply(y, x, DTObj);
184		fi;
185		q := QuoInt(q, 2);
186		x := multiply(x, x, DTObj);
187	od;
188	return y;
189end) ;
190
191# Input: 	- pcp element pcp
192#			- integer q
193# Output:	pcp^q
194InstallGlobalFunction( DTP_PCP_Exp,
195function(pcp, q)
196	local dtobj, exp;
197
198	dtobj := Collector(pcp);
199
200	if not IsDTObj(dtobj) then
201		Error("Collector(pcp) must be a DTObj");
202	else
203		exp := ShallowCopy(Exponents(pcp));
204		return PcpElementByExponentsNC(dtobj, DTP_Exp(exp, q, dtobj));
205	fi;
206
207end );
208
209# Input: 	- exponent vector x
210#			- DTObj
211#			- empty list nf = [] where normal form is stored
212#			- function multiply which is either DTP_Multiply_r or
213#			DTP_Multiply_rs depending the polynomials used in DTObj
214# Output: 	exponent vector of the normal form of x
215_DTP_DetermineNormalForm := function(x, DTObj, nf, multiply)
216	local n, j, i, q, r, q_list, l, z, pwr, k, w1, w2, w;
217
218	# DTObj![PC_DTPPolynomials] = DTpols(coll)
219	n := DTObj![PC_NUMBER_OF_GENERATORS];
220
221	# find j = min{ 1 <= i <= n | s_i < infinity and
222	#  							(x_i < 0 or x_i >= s_i) } \cup {infinity}
223	j := DTP_IsInNormalForm(x, DTObj);
224
225	if j <> true then
226		# replace a_j^x[j] by suitable power of the power relation a_j^s_j
227
228		# x[j] = q * rel_orders[j] + r, 0 <= r < rel_orders[j]:
229		r := x[j] mod RelativeOrders(DTObj)[j];
230		q := (x[j] - r)/RelativeOrders(DTObj)[j];
231
232		# In the following, compute w = w1 * w2, where
233		# w1 = ( a_j^rel_orders[j] )^q
234		#		 = ( a_{j + 1}^{c_{j, j, j + 1}} ... a_n^{c_{j, j, n}} )^q
235		# and
236		# w2 = a_{j + 1}^{x_{j + 1}} ... a_n^{x_n}
237		# Then w only depends on the generators a_{j + 1}, ..., a_n and
238		# its normal form can be computed (recursively).
239
240		pwr := GetPower(DTObj, j);
241		# z = a_{j + 1}^c_{j, j, j + 1} ... a_n^{c_{j, j, n}}
242		z := [1 .. n] * 0;
243		for k in [1, 3 .. (Length(pwr) - 1)] do
244			z[pwr[k]] := pwr[k + 1];
245		od;
246
247		# Compute w1 = z^q
248		w1 := DTP_Exp(z, q, DTObj);
249
250		# w2 = a_{j + 1}^{x_{j + 1}} ... a_n^{x_n}
251		w2 := [1 .. j] * 0; # [0, ..., 0] of length j
252		for i in [(j + 1) .. n] do
253			Add(w2, x[i]);
254		od;
255
256		# w = w1 * w2
257		w := multiply(w1, w2, DTObj);
258
259		# At this point, nf is a list describing the beginning of the
260		# exponent vector of the normal form of x. If some relative orders
261		# are infinite, the corresponding exponents in x are simply added,
262		# and lastly the exponent r of the generator a_j is added. Afterwards,
263		# nf is a list of length j, which must be completed in the following
264		# recursion steps, in which the normal form of "the rest" w" of x is
265		# computed.
266		for i in [(Length(nf) + 1) .. (j - 1)] do
267			Add(nf, x[i]);
268		od;
269		Add(nf, r); # j-th entry
270
271		return _DTP_DetermineNormalForm(w, DTObj, nf, multiply);
272	else
273		# Since all further relative orders are infinite, the word is
274		# now in normal form and we simply append the exponents to the
275		# already calculated normal form nf.
276		for i in [(Length(nf) + 1) .. n] do
277			Add(nf, x[i]);
278		od;
279		return nf;
280	fi;
281end;
282
283# Input: 	- exponent vector x
284#			- DTObj
285# Output: 	exponent vector of normal form of x
286InstallGlobalFunction( DTP_NormalForm,
287function(x, DTObj)
288	local multiply;
289
290	multiply := DTP_DetermineMultiplicationFunction(DTObj);
291
292	return _DTP_DetermineNormalForm(x, DTObj, [], multiply);
293end );
294
295
296# Input: 	pcp element
297# Output: 	pcp element in normal form
298InstallGlobalFunction( DTP_PCP_NormalForm,
299function(pcp)
300	local dtobj;
301
302	dtobj := Collector(pcp);
303
304	if not IsDTObj(dtobj) then
305		Error("Collector(pcp) must be a DTObj");
306	elif not dtobj![PC_DTPConfluent] = true then
307		Error("collector must be confluent");
308	else
309		return PcpElementByExponentsNC(dtobj, DTP_NormalForm(Exponents(pcp), dtobj));
310	fi;
311
312end );
313
314# Input:	- exponent vector x (must describe a normal form)
315#			- DTObj
316# Output: 	order of x in group of coll
317_DTP_DetermineOrder := function(x, DTObj, multiply)
318	local j, s, ord;
319	ord := 1;
320	# DTObj![PC_DTPPolynomials] = DTpols(coll)
321
322	while x <> [1 .. DTObj![PC_NUMBER_OF_GENERATORS]] * 0 do
323		j := 1;
324		while x[j] = 0 do
325			j := j + 1; # exists, since x <> neutral element
326		od;
327		if RelativeOrders(DTObj)[j] = 0 then # relative order s_j = infinity
328			return infinity;
329		else
330			# s = s_j/gcd(s_j, x_j)
331			s := RelativeOrders(DTObj)[j]/Gcd(RelativeOrders(DTObj)[j], x[j]);
332
333			# ord(x) = infinity <=> ord(x^s) = infinity
334			# ord(x) < infinity => s | ord(x)
335
336			# Compute y = x^s:
337			x := DTP_Exp(x, s, DTObj);
338			# The normal form of x is needed, i.e. isConfl = true!
339			# (Otherwise one may run into a infinite recursion, since the
340			# termination of the algorithm needs the normal form of x to
341			# begin with a generator with index greater than j.)
342			ord := ord * s;
343		fi;
344	od;
345	return ord;
346end;
347
348# Input: 	- exponent vector x
349#			- DTObj
350# Output: 	order of x in group of coll
351InstallGlobalFunction( DTP_Order,
352function(x, DTObj)
353	local multiply;
354
355	multiply := DTP_DetermineMultiplicationFunction(DTObj);
356
357	# check whether x is in normal form, if not call _DTP_DetermineNormalForm
358	# DTP_IsInNormalForm returns true or an intger, if not in normal form
359	if DTP_IsInNormalForm(x, DTObj) <> true then
360		x := _DTP_DetermineNormalForm(x, DTObj, [], multiply);
361	fi;
362
363	return _DTP_DetermineOrder(x, DTObj, multiply);
364end );
365
366# Input: 	pcp element
367# Output: 	order of pcp
368InstallGlobalFunction( DTP_PCP_Order,
369function(pcp)
370	local dtobj;
371
372	dtobj := Collector(pcp);
373
374	if not IsDTObj(dtobj) then
375		Error("Collector(pcp) must be a DTObj");
376	elif not dtobj![PC_DTPConfluent] = true then
377		Error("collector must be confluent");
378	else
379		return DTP_Order(Exponents(pcp), dtobj);
380	fi;
381
382end );
383