aboutsummaryrefslogtreecommitdiff
path: root/src/maple/util.maple
blob: 1ed171d429df82bdb0f95d14c12a4f079b76061c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# util.maple -- Maple utility functions
# $Header$

#
# msum - multiple-index sum()
# arctan_xy - 2-argument arctan() function
# ssqrt - symbolic sqrt()
#
# indices_in_order - list of indices of table/array, in lexicographic order
# lexorder_integer_list - lexorder() for lists
# sort_var_list - sort a list of variables into a canonical order
# lexorder_vars - lexorder() for "interesting" variables
#
# gensym - generate a "new" symbol name
# gensym/init - reinitialize the gensym() global naming counter
#
# saveit - optionally save variables for debugging
#

################################################################################
################################################################################
################################################################################

#
# This function is identical to the standard Maple library function  sum()
# except that it allows multiple summation indices.  It's usage is best
# illustrated by a series of examples:
#
#	msum('f', 'k'=m..n)
#		= sum('f', 'k'=m..n)
#
#	msum('f', 'k1'=m1..n1, 'k2'=m2..n2)
#		= sum( 'sum('f', 'k1'=m1..n1)' , 'k2'=m2..n2 )
#
#	msum('f', 'k1'=m1..n1, 'k2'=m2..n2, 'k3'=m3..n3)
#		= sum(
#			 'sum( 'sum('f', 'k1'=m1..n1)' , 'k2'=m2..n2 )'
#		       ,
#			 'k3'=m3..n3
#		     )
#
# Note, in particular, that the summation indices are nested with the
# innermost summation appearing first.  This is the same nesting convention
# as Fortran I/O statement's implicit do loops use.
#
# Bugs:
# - The quoting in the code is pretty obscure...
#
msum :=
proc(fn::algebraic)			# varargs
local expr, sum_index;

if (nargs < 2)
   then ERROR("must have two or more arguments")
end if;

expr := fn;

	for sum_index in [args[2..nargs]]
	do
	# loop invariant:
	# msum(fn, <args processed so far>) = sum(expr, sum_index)
	expr; sum_index; expr:= 'sum'(''%%'', ''%'');
	end do;

return eval(expr);
end proc;

################################################################################

#
# This function computes the 4-quadrant arctangent of  arctan(y/x) .
# It's "just" a wrapper around the Maple 2-argument arctan function,
# providing the what-I-find-natural (x,y) argument
#
# Arguments:
# (x,y) = (In) The x,y coordinates.
#
arctan_xy :=
proc(x::algebraic, y::algebraic)
arctan(y,x);		# !!! reversedargument order !!!
end proc;

################################################################################

#
# This function computes the square root of its argument with the
# symbolic option ==> fewer branch-cut problems.
#
ssqrt :=
proc(x::algebraic)
sqrt(x, 'symbolic');
end proc;

################################################################################
################################################################################
################################################################################

#
# This function is identical to Maple's  indices()  built-in function,
# except that (a) it returns a list, not an expression sequence, and
# (b) the indices are returned in lexicographic order rather than in
# Maple's internal hash ordering.
#
# Arguments:
# T = (in) A table or array with (only) integer-sequence indices.
#
# Results:
# The lexicographic-ordered expression sequence of indices is returned
# as the function result.
#
# Bugs:
# - The function fails if the indices aren't a list of numbers.
#
indices_in_order :=
proc(T::table)
return sort([indices(T)], lexorder_integer_list);
end;

###############################################################################

#
# This function lexicographically orders lists of integers.
#
# Arguments:
# list1 = list of integers to be sorted
# list2 = list of integers to be sorted
#
# Results:
# The function returns  true  iff  list1 < list2  lexicographically,
# with the [1] list elements being most significant.
#
lexorder_integer_list :=
proc(list1::list(numeric), list2::list(numeric))
local len1, len2, k;

len1 := nops(list1);
len2 := nops(list2);

	for k from 1 to min(len1,len2)
	do
	if   (list1[k] < list2[k])
	   then return true;
	elif (list1[k] > list2[k])
	   then return false;
	fi;
	end do;

# get to here ==> the shorter list is an exact prefix of the longer one
#             ==> order the shorter one < the longer one
return evalb(len1 < len2);
end;

################################################################################

#
# This function sorts a list of names (in practice, a list of coordinates)
# into a canonical order.
#
sort_var_list :=
proc(var_list::list(name))
option remember;		# performance optimization
global lexorder_vars;

# only get to here the first time we see a given variable list
# (i.e. if it's not in the remember table)
return sort(var_list, lexorder_vars);
end;

################################################################################

#
# This function defines a lexical ordering on variable names.
#
# Arguments:
# x, y = Two names to be compared.
#
# Results:
# This function returns true iff a < b, false otherwise.
#
lexorder_vars :=
proc(x::name, y::name)
option remember;		# performance optimization
local xposn, yposn;
global
  @include "../maple/coords.minc";

if (member(x, xy_all_list, 'xposn') and member(y, xy_all_list, 'yposn'))
   then return evalb(xposn < yposn);
   else return lexorder(x, y);
fi;

end;

################################################################################
################################################################################
################################################################################

#
# This procedure generates a (presumably) new symbol name of the
# form
#	cat(base_name,count)
# where  base_name  is passed as an (optional, with a default value
# supplied if it's omitted) argument, and  count  is the current
# value of the global variable
#	`gensym/counter`
# which is postincremented after being used.  If the counter is
# unassigned on entry to this function,  `gensym/init`()  is first
# called to initialize it.
#
# `gensym/init`() may be used to (re)initialize the counter as
# desired, or `gensym/counter` may be examined or altered directly
# to otherwise modify the name sequence generated by this function.
#
# Arguments:
# base_name = (in) (optional) The base name for the name generation.
#			      This argument defaults to `temp_` ifo
#			      omitted.
#
# Bugs:
# No checks are made for whether the names in question are indeed
# unassigned.
#
gensym :=
proc(opt_base_name::string)
global `gensym/counter`;		# in out
local base_name, tn;

if (nargs >= 1)
   then base_name := opt_base_name;
   else base_name := '`temp_`';
end if;

# force an initialization if it hasn't been done before
if (not assigned(`gensym/counter`))
   then `gensym/init`();
end if;

tn := cat(base_name, `gensym/counter`);
`gensym/counter` := `gensym/counter` + 1;

tn; return '%';
end proc;

################################################################################

#
# This procedure (re)initializes the gensym() global naming counter.
#
# Arguments:
# initial_counter = (in) (optional)
#			 The naming counter value to (re)initialize with.
#			 This argument defaults to 1 if omitted.
#
`gensym/init` :=
proc(opt_initial_counter::integer)
global `gensym/counter`;		# in out
local initial_counter;

if (nargs >= 1)
   then initial_counter := opt_initial_counter;
   else initial_counter := 1;
end if;

`gensym/counter` := initial_counter;
NULL;
end proc;

################################################################################
################################################################################
################################################################################

#
# This function optionally saves a local variable to a global variable
# for later examination.  This is useful for debugging purposes, since
# Maple doesn't have a proper debugger :-( .
#
# Arguments (explicit):
# n = (in) An integer controlling how important this saveit() call is;
#	   the saving will only be done if `saveit/level` >= n, i.e.
#	   smaller values of n make the saving more likely.
# fn = (in) The calling function or subsystem's name.
# label = (in) A string identifying this saveit() call among all those
#	       within the calling function.
# expr = (in) The temporary result to save.
#
# Arguments (implicit, as global variables)
# `saveit/level` = (in) (optional) If this name is assigned (as determined
#				  by the  assigned()  function), this
#				  function does the saving.  Otherwise,
#				  this function is a nop.
#
saveit :=
proc(n::integer, fn::{procedure,string}, label::string, expr::anything)
global `saveit/level`;
local save_name;

if ( assigned(`saveit/level`)
     and type(`saveit/level`, integer)
     and (`saveit/level` >= n) )
   then
	save_name := cat(convert(fn,string),"/",label);
	printf("      --> `%s`\n", save_name);
	assign(  convert(eval(save_name,1),name) = expr  );
end if;

NULL;
end proc;