• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tueda / form / 15241916852

25 May 2025 08:59PM UTC coverage: 47.908% (-2.8%) from 50.743%
15241916852

push

github

tueda
ci: build arm64-windows binaries

39009 of 81425 relevant lines covered (47.91%)

1079780.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.48
/sources/function.c
1
/** @file function.c
2
 * 
3
 *  The file with the central routines for the pattern matching of
4
 *        functions and their arguments.
5
 *        The file also contains the routines for the execution of the
6
 *        Symmetrize statement and its variations (like antisymmetrize etc).
7
 */
8
/* #[ License : */
9
/*
10
 *   Copyright (C) 1984-2023 J.A.M. Vermaseren
11
 *   When using this file you are requested to refer to the publication
12
 *   J.A.M.Vermaseren "New features of FORM" math-ph/0010025
13
 *   This is considered a matter of courtesy as the development was paid
14
 *   for by FOM the Dutch physics granting agency and we would like to
15
 *   be able to track its scientific use to convince FOM of its value
16
 *   for the community.
17
 *
18
 *   This file is part of FORM.
19
 *
20
 *   FORM is free software: you can redistribute it and/or modify it under the
21
 *   terms of the GNU General Public License as published by the Free Software
22
 *   Foundation, either version 3 of the License, or (at your option) any later
23
 *   version.
24
 *
25
 *   FORM is distributed in the hope that it will be useful, but WITHOUT ANY
26
 *   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
27
 *   FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
28
 *   details.
29
 *
30
 *   You should have received a copy of the GNU General Public License along
31
 *   with FORM.  If not, see <http://www.gnu.org/licenses/>.
32
 */
33
/* #] License : */ 
34
/*
35
          #[ Includes : function.c
36
*/
37

38
#include "form3.h"
39

40
/*
41
          #] Includes : 
42
         #[ Utilities :
43
                 #[ MakeDirty :
44

45
                Routine finds the function with the address x in it
46
                and mark all arguments that contain x as dirty.
47
                if par == 0 term is a full term, else term is the start of a 
48
                function
49
*/
50

51
WORD MakeDirty(WORD *term, WORD *x, WORD par)
12✔
52
{
53
        WORD *next, *n;
12✔
54
        if ( !par ) {
12✔
55
                next = term; next += *term;
×
56
                next -= ABS(next[-1]);
×
57
                term++;
×
58
                if ( x < term ) return(0);
×
59
                if ( x >= next ) return(0);
×
60
                while ( term < next ) {
×
61
                        n = term + term[1];
×
62
                        if ( x < n ) break;
×
63
                        term = n;
64
                }
65
/*                next = n; */
66
        }
67
        else {
68
                next = term + term[1];
12✔
69
                if ( x < term || x >= next ) return(0);
12✔
70
        }
71
        if ( *term < FUNCTION ) return(0);
12✔
72
        if ( functions[*term-FUNCTION].spec >= TENSORFUNCTION ) return(0);
12✔
73
        term += FUNHEAD;
12✔
74
        if ( x < term ) return(0);
12✔
75
        next = term; NEXTARG(next)
12✔
76
        while ( x >= next ) { term = next; NEXTARG(next) }
12✔
77
        if ( *term < 0 ) return(0);
12✔
78
        term[1] = 1;
×
79
        term += ARGHEAD;
×
80
        if ( x < term ) return(1);
×
81
        next = term + *term;
×
82
        while ( x >= next ) { term = next; next += *next; }
×
83
        MakeDirty(term,x,0);
×
84
        return(1);
×
85
}
86

87
/*
88
                 #] MakeDirty : 
89
                 #[ MarkDirty :
90

91
                Routine marks all functions dirty with the given flags.
92
                Is to be used when there is a possibility that symmetrization
93
                properties of functions may have changed. In that case we play
94
                it safe.
95
*/
96

97
void MarkDirty(WORD *term, WORD flags)
66✔
98
{
99
        WORD *t, *r, *m, *tstop;
66✔
100
        GETSTOP(term,tstop);
66✔
101
        t = term+1;
66✔
102
        while ( t < tstop ) {
129✔
103
                if ( *t < FUNCTION ) { t += t[1]; continue; }
63✔
104
                t[2] |= flags;
3✔
105
                if ( *t < FUNCTION+WILDOFFSET && functions[*t-FUNCTION].spec > 0 ) {
3✔
106
                        t += t[1]; continue;
×
107
                }
108
                if ( *t >= FUNCTION+WILDOFFSET && functions[*t-FUNCTION-WILDOFFSET].spec > 0 ) {
3✔
109
                        t += t[1]; continue;
×
110
                }
111
                r = t + FUNHEAD;
3✔
112
                t += t[1];
3✔
113
                while ( r < t ) {
33✔
114
                        if ( *r <= 0 ) {
30✔
115
                                if ( *r <= -FUNCTION ) r++;
30✔
116
                                else r += 2;
30✔
117
                                continue;
30✔
118
                        }
119
                        r[1] |= DIRTYFLAG;
×
120
                        m = r + ARGHEAD;
×
121
                        r += *r;
×
122
                        while ( m < r ) {
×
123
                                MarkDirty(m,flags);
×
124
                                m += *m;
×
125
                        }
126
                }
127
        }
128
}
66✔
129

130
/*
131
                 #] MarkDirty : 
132
                 #[ PolyFunDirty :
133

134
                Routine marks the PolyFun or the PolyRatFun dirty.
135
                This is used when there is modular calculus and the modulus
136
                has changed for the current module.
137
*/
138

139
void PolyFunDirty(PHEAD WORD *term)
318,351✔
140
{
141
        GETBIDENTITY
142
        WORD *t, *tstop, *endarg;
318,351✔
143
        tstop = term + *term;
318,351✔
144
        tstop -= ABS(tstop[-1]);
318,351✔
145
        t = term+1;
318,351✔
146
        while ( t < tstop ) {
954,912✔
147
                if ( *t == AR.PolyFun ) {
636,561✔
148
                        if ( AR.PolyFunType == 2 ) t[2] |= MUSTCLEANPRF;
1,638✔
149
                        endarg = t + t[1];
1,638✔
150
                        t[2] |= DIRTYFLAG;
1,638✔
151
                        t += FUNHEAD;
1,638✔
152
                        while ( t < endarg ) {
1,638✔
153
                                if ( *t > 0 ) {
3,276✔
154
                                        t[1] |= DIRTYFLAG;
3,081✔
155
                                }
156
                                NEXTARG(t);
8,190✔
157
                        }
158
                }
159
                else {
160
                        t += t[1];
634,923✔
161
                }
162
        }
163
}
318,351✔
164

165
/*
166
                 #] PolyFunDirty : 
167
                 #[ PolyFunClean :
168

169
                Routine marks the PolyFun or the PolyRatFun clean.
170
                This is used when there is modular calculus and the modulus
171
                has changed for the current module.
172
*/
173

174
void PolyFunClean(PHEAD WORD *term)
3,414✔
175
{
176
        GETBIDENTITY
177
        WORD *t, *tstop;
3,414✔
178
        tstop = term + *term;
3,414✔
179
        tstop -= ABS(tstop[-1]);
3,414✔
180
        t = term+1;
3,414✔
181
        while ( t < tstop ) {
11,850✔
182
                if ( *t == AR.PolyFun ) {
8,436✔
183
                        t[2] &= ~MUSTCLEANPRF;
3,378✔
184
                }
185
                t += t[1];
8,436✔
186
        }
187
}
3,414✔
188

189
/*
190
                 #] PolyFunClean : 
191
                 #[ Symmetrize :
192

193
                (Anti)Symmetrizes the arguments of a function. 
194
                Nlist tells of how many arguments are involved.
195
                Nlist == 0                All arguments must be sorted.
196
                Nlist > 0                Arguments mentioned are to be sorted, rest skipped.
197
                type = SYMMETRIC       Full symmetrization
198
                type = ANTISYMMETRIC:  Full symmetrization
199
                type = CYCLESYMMETRIC: Cyclic
200
                type = RCYCLESYMMETRIC:Cyclic or reverse
201
                Return value: OR of:
202
                        0 even, 1 odd
203
                        2 equal groups
204
                        4 there was a permutation.
205

206
                The information in Lijst tells what grouping is to be applied.
207
                The information is:
208
                ngroups number of groups
209
                gsize size of groups
210
                Lijst[0]....  The groups.
211
*/
212

213
WORD Symmetrize(PHEAD WORD *func, WORD *Lijst, WORD ngroups, WORD gsize,
1,182✔
214
                WORD type)
215
{
216
        GETBIDENTITY
217
        WORD **args,**arg,nargs;
1,182✔
218
        WORD *to, *r, *fstop;
1,182✔
219
        WORD i, j, k, ff, exch, nexch, neq;
1,182✔
220
        WORD *a1, *a2, *a3;
1,182✔
221
        WORD reverseorder;
1,182✔
222
        if ( ( type & REVERSEORDER ) != 0 ) reverseorder = -1;
1,182✔
223
        else                                reverseorder = 1;
1,182✔
224
        type &= ~REVERSEORDER;
1,182✔
225

226
        ff = ( *func > FUNCTION ) ? functions[*func-FUNCTION].spec: 0;
1,182✔
227

228
        if ( 2*func[1] > AN.arglistsize ) {
1,182✔
229
                if ( AN.arglist ) M_free(AN.arglist,"Symmetrize");
60✔
230
                AN.arglistsize = 2*func[1] + 8;
60✔
231
                AN.arglist = (WORD **)Malloc1(AN.arglistsize*sizeof(WORD *),"Symmetrize");
60✔
232
        }
233
        arg = args = AN.arglist;
1,182✔
234
        to = AT.WorkPointer;
1,182✔
235
        r = func;
1,182✔
236
        fstop = r + r[1];
1,182✔
237
        r += FUNHEAD;
1,182✔
238
        nargs = 0;
1,182✔
239
        while ( r < fstop ) {        /* Make list of arguments */
1,182✔
240
                *arg++ = r;
2,649✔
241
                nargs++;
2,649✔
242
                if ( ff ) {
2,649✔
243
                        if ( *r == FUNNYWILD ) r++;
102✔
244
                        r++;
102✔
245
                }
246
                else { NEXTARG(r); }
6,378✔
247
        }
248
        exch = 0;
1,182✔
249
        nexch = 0;
1,182✔
250
        neq = 0;
1,182✔
251
        a1 = Lijst;
1,182✔
252
        if ( type == SYMMETRIC || type == ANTISYMMETRIC ) {
1,182✔
253
        for ( i = 1; i < ngroups; i++ ) {
2,532✔
254
                a3 = a2 = a1 + gsize;
1,392✔
255
                k = reverseorder*CompGroup(BHEAD ff,args,a1,a2,gsize);
1,392✔
256
                if ( k < 0 ) {
1,392✔
257
                        j = i-1;
435✔
258
                        for(;;) {
1,467✔
259
                                for ( k = 0; k < gsize; k++ ) {
1,902✔
260
                                        r = args[a1[k]]; args[a1[k]] = args[a2[k]]; args[a2[k]] = r;
951✔
261
                                }
262
                                exch ^= 1;
951✔
263
                                nexch = 4;
951✔
264
                                if ( j <= 0 ) break;
951✔
265
                                a1 -= gsize;
699✔
266
                                a2 -= gsize;
699✔
267
                                k = reverseorder*CompGroup(BHEAD ff,args,a1,a2,gsize);
699✔
268
                                if ( k == 0 ) neq = 2;
699✔
269
                                if ( k >= 0 ) break;
558✔
270
                                j--;
516✔
271
                        }
272
                }
273
                else if ( k == 0 ) neq = 2;
957✔
274
                a1 = a3;
1,392✔
275
        }
276
        }
277
        else if ( type == CYCLESYMMETRIC || type == RCYCLESYMMETRIC ) {
42✔
278
                WORD rev = 0, jmin = 0, ii, iimin;
279
recycle:
42✔
280
                for ( j = 1; j < ngroups; j++ ) {
117✔
281
                        for ( i = 0; i < ngroups; i++ ) {
75✔
282
                                iimin = jmin + i;
75✔
283
                                if ( iimin >= ngroups ) iimin -= ngroups;
75✔
284
                                ii = j + i;
75✔
285
                                if ( ii >= ngroups ) ii -= ngroups;
75✔
286
                                k = reverseorder*CompGroup(BHEAD ff,args,Lijst+gsize*iimin,Lijst+gsize*ii,gsize);
75✔
287
                                if ( k > 0 ) break;
75✔
288
                                if ( k < 0 ) { jmin = j; nexch = 4; break; }
×
289
                        }
290
                }
291
                if ( type == RCYCLESYMMETRIC && rev == 0 && ngroups > 1 ) {
42✔
292
                        for ( j = 0; j < ngroups; j++ ) {
×
293
                                for ( i = 0; i < ngroups; i++ ) {
×
294
                                        iimin = jmin + i;
×
295
                                        if ( iimin >= ngroups ) iimin -= ngroups;
×
296
                                        ii = j - i;
×
297
                                        if ( ii < 0 ) ii += ngroups;
×
298
                                        k = reverseorder*CompGroup(BHEAD ff,args,Lijst+gsize*iimin,Lijst+gsize*ii,gsize);
×
299
                                        if ( k > 0 ) break;
×
300
                                        if ( k < 0 ) {
×
301
                                                nexch = 4;
×
302
                                                jmin = 0;
×
303
                                                a1 = Lijst;
×
304
                                                a2 = Lijst + gsize * (ngroups-1);
×
305
                                                while ( a2 > a1 ) {
×
306
                                                        for ( k = 0; k < gsize; k++ ) {
×
307
                                                                r = args[a1[k]];
×
308
                                                                args[a1[k]] = args[a2[k]];
×
309
                                                                args[a2[k]] = r;
×
310
                                                        }
311
                                                        a1 += gsize; a2 -= gsize;
×
312
                                                }
313
                                                rev = 1;
×
314
                                                goto recycle;
×
315
                                        }
316
                                }
317
                        }
318
                }
319
                if ( jmin != 0 ) {
42✔
320
                        arg = AN.arglist + func[1];
×
321
                        a1 = Lijst + gsize * jmin;
×
322
                        k = gsize * ngroups;
×
323
                        a2 = Lijst + k;
×
324
                        for ( i = 0; i < k; i++ ) {
×
325
                                if ( a1 >= a2 ) a1 = Lijst;
×
326
                                *arg++ = args[*a1++];
×
327
                        }
328
                        arg = AN.arglist + func[1];
×
329
                        a1 = Lijst;
×
330
                        for ( i = 0; i < k; i++ ) args[*a1++] = *arg++;
×
331
                }
332
        }
333
        r = func;
1,182✔
334
        i = FUNHEAD;
1,182✔
335
        NCOPY(to,r,i);
4,728✔
336
        for ( i = 0; i < nargs; i++ ) {
3,831✔
337
                if ( ff ) {
2,649✔
338
                        if ( *(args[i]) == FUNNYWILD ) {
102✔
339
                                *to++ = *(args[i]);
18✔
340
                                *to++ = args[i][1];
18✔
341
                        }
342
                        else *to++ = *(args[i]);
84✔
343
                }
344
                else if ( ( j = *args[i] ) < 0 ) {
2,547✔
345
                        *to++ = j;
2,493✔
346
                        if ( j > -FUNCTION ) *to++ = args[i][1];
2,493✔
347
                }
348
                else {
349
                        r = args[i];
350
                        NCOPY(to,r,j);
6,654✔
351
                }
352
        }
353
        i = func[1];
1,182✔
354
        to = func;
1,182✔
355
        r = AT.WorkPointer;
1,182✔
356
        NCOPY(to,r,i);
13,839✔
357
        return ( exch | nexch | neq );
1,182✔
358
}
359

360
/*
361
                 #] Symmetrize : 
362
                 #[ CompGroup :
363

364
                        Routine compares two groups of arguments
365
                        The arguments are in args[a1[i]] and args[a2[i]]
366
                        for i = 0 to num
367
                        type indicates the type of function.
368
                        return value: -1 if there should be an exchange
369
                        0 if they are equal
370
                        1 if they are OK.
371
*/
372

373
WORD CompGroup(PHEAD WORD type, WORD **args, WORD *a1, WORD *a2, WORD num)
2,166✔
374
{
375
        GETBIDENTITY
376
        WORD *t1, *t2, i1, i2, n, k;
2,166✔
377

378
        for ( n = 0; n < num; n++ ) {
2,757✔
379
                t1 = args[a1[n]]; t2 = args[a2[n]];
2,166✔
380
                if ( type >= TENSORFUNCTION ) {
2,166✔
381
                        if ( AR.Eside == LHSIDE || AR.Eside == LHSIDEX ) {
66✔
382
                                if ( *t1 == FUNNYWILD ) {
24✔
383
                                        if ( *t2 == FUNNYWILD ) {
×
384
                                                if ( t1[1] < t2[1] ) return(1);
×
385
                                                if ( t1[1] > t2[1] ) return(-1);
×
386
                                        }
387
                                        return(-1);
388
                                }
389
                                else if ( *t2 == FUNNYWILD ) {
24✔
390
                                        return(1);
391
                                }
392
                                else {
393
                                        if ( *t1 < *t2 ) return(1);
6✔
394
                                        if ( *t1 > *t2 ) return(-1);
×
395
                                }
396
                        }
397
                        else {
398
                                if ( *t1 < *t2 ) return(1);
42✔
399
                                if ( *t1 > *t2 ) return(-1);
×
400
                        }
401
                }
402
                else if ( type == 0 ) {
2,100✔
403
                        if ( AC.properorderflag ) {
2,100✔
404
                                k = CompArg(t1,t2);
×
405
                                if ( k < 0 ) return(1);
×
406
                                if ( k > 0 ) return(-1);
×
407
                                NEXTARG(t1)
×
408
                                NEXTARG(t2)
591✔
409
                        }
410
                        else {
411
                                if ( *t1 > 0 ) {
2,100✔
412
                                        i1 = *t1 - ARGHEAD - 1;
726✔
413
                                        t1 += ARGHEAD + 1;
726✔
414
                                        if ( *t2 > 0 ) {
726✔
415
                                                i2 = *t2 - ARGHEAD - 1;
60✔
416
                                                t2 += ARGHEAD + 1;
60✔
417
                                                while ( i1 > 0 && i2 > 0 ) {
336✔
418
                                                        if ( *t1 > *t2 ) return(-1);
330✔
419
                                                        else if ( *t1 < *t2 ) return(1);
297✔
420
                                                        i1--; i2--; t1++; t2++;
276✔
421
                                                }
422
                                                if ( i1 > 0 ) return(-1);
6✔
423
                                                else if ( i2 > 0 ) return(1);
6✔
424
                                        }
425
/*
426
                                        This seems to be a bug. Reported by Aneesh Monahar, 28-sep-2005
427
                                        else return(1);
428
*/
429
                                        else return(-1);
430
                                }
431
                                else if ( *t2 > 0 ) return(1);
1,374✔
432
                                else {
433
                                        if ( *t1 != *t2 ) {
1,368✔
434
                                                if ( *t1 <= -FUNCTION && *t2 <= -FUNCTION ) {
141✔
435
                                                        if ( *t1 < *t2 ) return(-1);
×
436
                                                        return(1);
×
437
                                                }
438
                                                else {
439
                                                        if ( *t1 < *t2 ) return(1);
141✔
440
                                                        return(-1);
6✔
441
                                                }
442
                                        }
443
                                        if ( *t1 > -FUNCTION ) {
1,227✔
444
                                                if ( t1[1] != t2[1] ) {
1,227✔
445
                                                        if ( t1[1] < t2[1] ) return(1);
639✔
446
                                                        return(-1);
246✔
447
                                                }
448
                                        }
449
                                }
450
                        }
451
                }
452
        }
453
        return(0);
454
}
455

456
/*
457
                 #] CompGroup : 
458
                 #[ FullSymmetrize :
459

460
                Relay function for Normalize to execute a full symmetrization
461
                of a function fun. It hooks into Symmetrize according to the
462
                calling conventions for it.
463
                type = 0: Symmetrize
464
                type = 1: AntiSymmetrize
465
                type = 2: CycleSymmetrize
466
                type = 3: RCycleSymmetrize
467
                Return values:
468
                bit 0: odd permutation
469
                bit 1: identical arguments
470
                bit 2: there was a permutation.
471
*/
472

473
int FullSymmetrize(PHEAD WORD *fun, int type)
2,844✔
474
{
475
        GETBIDENTITY
476
        WORD *Lijst, count = 0;
2,844✔
477
        WORD *t, *funstop, i;
2,844✔
478
        int retval;
2,844✔
479

480
        if ( functions[*fun-FUNCTION].spec > 0 ) {
2,844✔
481
                count = fun[1] - FUNHEAD;
36✔
482
                for ( i = fun[1]-1; i >= FUNHEAD; i-- ) {
156✔
483
                        if ( fun[i] == FUNNYWILD ) count--;
120✔
484
                }
485
        }
486
        else {
487
                funstop = fun + fun[1];
2,808✔
488
                t = fun + FUNHEAD;
2,808✔
489
                while ( t < funstop ) { count++; NEXTARG(t) }
6,804✔
490
        }
491
        if ( count < 2 ) {
2,844✔
492
                fun[2] &= ~DIRTYSYMFLAG;
1,689✔
493
                return(0);
1,689✔
494
        }
495
        Lijst = AT.WorkPointer;
1,155✔
496
        for ( i = 0; i < count; i++ ) Lijst[i] = i;
3,564✔
497
        AT.WorkPointer += count;
1,155✔
498
        retval = Symmetrize(BHEAD fun,Lijst,count,1,type);
1,155✔
499
        fun[2] &= ~DIRTYSYMFLAG;
1,155✔
500
        AT.WorkPointer = Lijst;
1,155✔
501
        return(retval);
1,155✔
502
}
503

504
/*
505
                 #] FullSymmetrize : 
506
                 #[ SymGen :
507

508
                Routine does the outer work in the symmetrization.
509
                It locates the function(s) and loads up the parameters.
510
                It also studies the result.
511

512
                if params[4] = -1 and no extra -> all
513
                                      extra -> strip groups with elements too large
514
                               0  -> if group with element too large: nofun
515
                                           >0 -> must have right number of arguments
516
*/
517

518
WORD SymGen(PHEAD WORD *term, WORD *params, WORD num, WORD level)
12✔
519
{
520
        GETBIDENTITY
521
        WORD *t, *r, *m;
12✔
522
        WORD i, j, k, c1, c2, ngroup;
12✔
523
        WORD *rstop, Nlist, *inLijst, *Lijst, sign = 1, sumch = 0, count;
12✔
524
        DUMMYUSE(num);
12✔
525
        c1 = params[3];                /* function number */
12✔
526
        c2 = FUNCTION + WILDOFFSET;
12✔
527
        Nlist = params[4];
12✔
528
        if ( Nlist < 0 ) Nlist = 0;
12✔
529
        else Nlist = params[0] - 7;
12✔
530
        t = term;
12✔
531
        m = t + *t;
12✔
532
        m -= ABS(m[-1]);
12✔
533
        t++;
12✔
534
        while ( t < m ) {
24✔
535
                if ( *t == c1 || c1 > c2 ) {        /* Candidate function */
12✔
536
                        if ( *t >= FUNCTION && functions[*t-FUNCTION].spec
12✔
537
                        >= TENSORFUNCTION ) {
538
                                count = t[1] - FUNHEAD;
×
539
                        }
540
                        else {
541
                                count = 0;
12✔
542
                                r = t;
12✔
543
                                rstop = t + t[1];
12✔
544
                                r += FUNHEAD;
12✔
545
                                while ( r < rstop ) { count++; NEXTARG(r) }
36✔
546
                        }
547
                        if ( ( j = params[4] ) > 0 && j != count ) goto NextFun;
12✔
548
                        if ( j == 0 ) {
12✔
549
                                inLijst = params+7;
12✔
550
                                for ( i = 0; i < Nlist; i++ )
12✔
551
                                        if ( inLijst[i] > count-1 ) goto NextFun;
×
552
                        }
553

554
                        if ( Nlist > (params[0] - 7) ) Nlist = params[0] - 7;
12✔
555
                        Lijst = AT.WorkPointer;
12✔
556
                        inLijst = params + 7;
12✔
557
                        ngroup = params[5];
12✔
558
                        if ( Nlist > 0 && j < 0 ) {
12✔
559
                                k = 0;
560
                                for ( i = 0; i < ngroup; i++ ) {
×
561
                                        for ( j = 0; j < params[6]; j++ ) {
×
562
                                                if ( inLijst[j] > count+1 ) {
×
563
                                                        inLijst += params[6];
×
564
                                                        goto NextGroup;
×
565
                                                }
566
                                        }
567
                                        j = params[6];
568
                                        NCOPY(Lijst,inLijst,j);
×
569
                                        k++;
×
570
NextGroup:;
×
571
                                }
572
                                if ( k <= 1 ) goto NextFun;
×
573
                                ngroup = k;
×
574
                                inLijst = AT.WorkPointer;
×
575
                                AT.WorkPointer = Lijst;
×
576
                                Lijst = inLijst;
×
577
                        }
578
                        else if ( Nlist == 0 ) {
12✔
579
                                for ( i = 0; i < count; i++ ) Lijst[i] = i;
36✔
580
                                AT.WorkPointer += count;
12✔
581
                                ngroup = count;
12✔
582
                        }
583
                        else {
584
                                for ( i = 0; i < Nlist; i++ ) Lijst[i] = inLijst[i];
×
585
                                AT.WorkPointer += Nlist;
×
586
                        }
587
                        j = Symmetrize(BHEAD t,Lijst,ngroup,params[6],params[2]);
12✔
588
                        AT.WorkPointer = Lijst;
12✔
589
                        if ( params[2] == 4 ) { /* antisymmetric */
12✔
590
                                if ( ( j & 1 ) != 0 ) sign = -sign;
×
591
                                if ( ( j & 2 ) != 0 ) return(0); /* equal arguments */
×
592
                        }
593
                        if ( ( j & 4 ) != 0 ) sumch++;
12✔
594
                        t[2] &= ~DIRTYSYMFLAG;
12✔
595
                }
596
NextFun:
×
597
                t += t[1];
12✔
598
        }
599
        if ( sign < 0 ) {
12✔
600
                t = term;
×
601
                t += *t - 1;
×
602
                *t = -*t;
×
603
        }
604
        if ( sumch ) {
12✔
605
                if ( Normalize(BHEAD term) ) {
6✔
606
                        MLOCK(ErrorMessageLock);
×
607
                        MesCall("SymGen");
×
608
                        MUNLOCK(ErrorMessageLock);
×
609
                        return(-1);
×
610
                }
611
                if ( !*term ) return(0);
6✔
612
                *AN.RepPoint = 1;
6✔
613
                AR.expchanged = 1;
6✔
614
                if ( AR.CurDum > AM.IndDum && AR.sLevel <= 0 ) ReNumber(BHEAD term);
6✔
615
        }
616
        return(Generator(BHEAD term,level));
12✔
617
}
618

619
/*
620
                 #] SymGen : 
621
                 #[ SymFind :
622

623
                There is a certain amount of double work here, as this routine
624
                finds the function to be treated, while the SymGen routine has
625
                to find it again. Note however that this way things remain
626
                uniform and simple. Moreover this avoids problems with actions
627
                on more than one function simultaneously.
628
                Output in AT.TMout:
629
                Number,sym/anti,fun,lenpar,ngroups,gsize,fields
630

631
*/
632

633
WORD SymFind(PHEAD WORD *term, WORD *params)
12✔
634
{
635
        GETBIDENTITY
636
        WORD *t, *r, *m;
12✔
637
        WORD j, c1, c2, count;
12✔
638
        WORD *rstop;
12✔
639
        c1 = params[4];                /* function number */
12✔
640
        c2 = FUNCTION + WILDOFFSET;
12✔
641
        t = term;
12✔
642
        m = t + *t;
12✔
643
        m -= ABS(m[-1]);
12✔
644
        t++;
12✔
645
        while ( t < m ) {
12✔
646
                if ( *t == c1 || c1 > c2 ) {        /* Candidate function */
12✔
647
                        if ( *t >= FUNCTION && functions[*t-FUNCTION].spec
12✔
648
                                >= TENSORFUNCTION ) { count = t[1] - FUNHEAD; }
×
649
                        else {
650
                                count = 0;
12✔
651
                                r = t;
12✔
652
                                rstop = t + t[1];
12✔
653
                                r += FUNHEAD;
12✔
654
                                while ( r < rstop ) { count++; NEXTARG(r) }
36✔
655
                        }
656
                        if ( ( j = params[5] ) > 0 && j != count ) goto NextFun;
12✔
657
                        if ( j == 0 ) {
12✔
658
                                r = params + 8;
12✔
659
                                rstop = params + params[1];
12✔
660
                                while ( r < rstop ) {
12✔
661
                                        if ( *r > count + 1 ) goto NextFun;
×
662
                                        r++;
×
663
                                }
664
                        }
665
                        
666
                        t = AT.TMout;
12✔
667
                        r = params;
12✔
668
                        j = r[1] - 1;
12✔
669
                        *t++ = j;
12✔
670
                        *t++ = SYMMETRIZE;
12✔
671
                        r += 3;
12✔
672
                        j--;
12✔
673
                        NCOPY(t,r,j);
84✔
674
                        return(1);
675
                }
676
NextFun:
×
677
                t += t[1];
×
678
        }
679
        return(0);
680
}
681

682
/*
683
                 #] SymFind : 
684
                 #[ ChainIn :
685

686
                Equivalent to repeat id f(?a)*f(?b) = f(?a,?b);
687

688
                This one always takes less space.
689
*/
690

691
int ChainIn(PHEAD WORD *term, WORD funnum)
123✔
692
{
693
        GETBIDENTITY
694
        WORD *t, *tend, *m, *tt, *ts;
123✔
695
        int action;
123✔
696
        if ( funnum < 0 ) {        /* Dollar to be expanded */
123✔
697
                funnum = DolToFunction(BHEAD -funnum);
×
698
                if ( AN.ErrorInDollar || funnum <= 0 ) {
×
699
                        MLOCK(ErrorMessageLock);
×
700
                        MesPrint("Dollar variable does not evaluate to function in ChainIn statement");
×
701
                        MUNLOCK(ErrorMessageLock);
×
702
                        return(-1);
×
703
                }
704
        }
705
        do {
210✔
706
                action = 0;
210✔
707
                tend = term+*term;
210✔
708
                tend -= ABS(tend[-1]);
210✔
709
                t = term+1;
210✔
710
                while ( t < tend ) {
453✔
711
                        if ( *t != funnum ) { t += t[1]; continue; }
330✔
712
                        m = t;
207✔
713
                        t += t[1];
207✔
714
                        tt = t;
207✔
715
                        if ( t >= tend || *t != funnum ) continue;
207✔
716
                        action = 1;
198✔
717
                        while ( t < tend && *t == funnum ) {
198✔
718
                                ts = t + t[1];
111✔
719
                                t += FUNHEAD;
111✔
720
                                while ( t < ts ) *tt++ = *t++;
333✔
721
                        }
722
                        m[1] = tt - m;
87✔
723
                        ts = term + *term;
87✔
724
                        while ( t < ts ) *tt++ = *t++;
1,338✔
725
                        *term = tt - term;
87✔
726
                        break;
87✔
727
                }
728
        } while ( action );
210✔
729
        return(0);
730
}
731

732
/*
733
                 #] ChainIn : 
734
                 #[ ChainOut :
735

736
                Equivalent to repeat id f(x1?,x2?,?a) = f(x1)*f(x2,?a);
737
*/
738

739
int ChainOut(PHEAD WORD *term, WORD funnum)
771✔
740
{
741
        GETBIDENTITY
742
        WORD *t, *tend, *tt, *ts, *w, *ws;
771✔
743
        int flag = 0, i;
771✔
744
        if ( funnum < 0 ) {        /* Dollar to be expanded */
771✔
745
                funnum = DolToFunction(BHEAD -funnum);
×
746
                if ( AN.ErrorInDollar || funnum <= 0 ) {
×
747
                        MLOCK(ErrorMessageLock);
×
748
                        MesPrint("Dollar variable does not evaluate to function in ChainOut statement");
×
749
                        MUNLOCK(ErrorMessageLock);
×
750
                        return(-1);
×
751
                }
752
        }
753
        tend = term+*term;
771✔
754
        if ( AT.WorkPointer < tend ) AT.WorkPointer = tend;
771✔
755
        tend -= ABS(tend[-1]);
771✔
756
        t = term+1; tt = term; w = AT.WorkPointer;
771✔
757
        while ( t < tend ) {
1,542✔
758
                if ( *t != funnum || t[1] == FUNHEAD ) { t += t[1]; continue; }
771✔
759
                flag = 1;
12✔
760
                while ( tt < t ) *w++ = *tt++;
12✔
761
                ts = t + t[1];
6✔
762
                t += FUNHEAD;
6✔
763
                while ( t < ts ) {
192✔
764
                        ws = w;
765
                        for ( i = 0; i < FUNHEAD; i++ ) *w++ = tt[i];
744✔
766
                        if ( functions[*tt-FUNCTION].spec >= TENSORFUNCTION ) {
186✔
767
                                *w++ = *t++;
×
768
                        }
769
                        else if ( *t < 0 ) {
186✔
770
                                if ( *t <= -FUNCTION ) *w++ = *t++;
162✔
771
                                else { *w++ = *t++; *w++ = *t++; }
162✔
772
                        }
773
                        else {
774
                                i = *t; NCOPY(w,t,i);
1,818✔
775
                        }
776
                        ws[1] = w - ws;
186✔
777
                }
778
                tt = t;
779
        }
780
        if ( flag == 1 ) {
771✔
781
                ts = term + *term;
6✔
782
                while ( tt < ts ) *w++ = *tt++;
24✔
783
                *AT.WorkPointer = w - AT.WorkPointer;
6✔
784
                t = term; w = AT.WorkPointer; i = *w;
6✔
785
                NCOPY(t,w,i)
2,706✔
786
                AT.WorkPointer = term + *term;
6✔
787
                Normalize(BHEAD term);
6✔
788
        }
789
        return(0);
790
}
791

792
/*
793
                 #] ChainOut : 
794
          #] Utilities : 
795
        #[ Patterns :
796
                 #[ MatchFunction :                        WORD MatchFunction(pattern,interm,wilds)
797

798
                The routine assumes that the function numbers are the same.
799
                The contents are compared and a possible wildcard assignment
800
                is made. Note that it may be necessary to use a wildcard
801
                assignment stack to do things right.
802
                The routine can become arbitrarily complicated as there is
803
                no end to the possible wildcarding.
804
                Examples:
805
                -        a:        No wildcarding -> straight match
806
                -        b:        Individual arguments (object -> object)
807
                -        c:        whole arguments (object to subexpression)
808
                -        d:        any argumentlist
809
                        e:        part of an argument (object inside subexpression)
810

811
                The ones with a minus sign in front have been implemented.
812

813
                Note: the argument wilds allows backtracking when multiple
814
                ?a,?b give a match that later turns out to be useless.
815
*/
816

817
WORD MatchFunction(PHEAD WORD *pattern, WORD *interm, WORD *wilds)
1,442,901✔
818
{
819
        GETBIDENTITY
820
        WORD *m, *t, *r, i;
1,442,901✔
821
        WORD *mstop = 0, *tstop = 0;
1,442,901✔
822
        WORD *argmstop, *argtstop;
1,442,901✔
823
        WORD *mtrmstop, *ttrmstop;
1,442,901✔
824
        WORD *msubstop, *mnextsub;
1,442,901✔
825
        WORD msizcoef, mcount, tcount, newvalue, j;
1,442,901✔
826
        WORD *oldm, *oldt;
1,442,901✔
827
        WORD *OldWork, numofwildarg;
1,442,901✔
828
        WORD nwstore, tobeeaten, reservevalue = 0, resernum = 0, withwild;
1,442,901✔
829
        WORD *wildargtaken;
1,442,901✔
830
        CBUF *C = cbuf+AT.ebufnum;
1,442,901✔
831
        int ntwa = AN.NumTotWildArgs;
1,442,901✔
832
        LONG oldcpointer = C->Pointer - C->Buffer;
1,442,901✔
833
/*
834
        Test first for a straight match
835
*/
836
        AN.RepFunList[AN.RepFunNum+1] = 0;
1,442,901✔
837
        if ( *wilds == 0 ) {
1,442,901✔
838
                m = pattern; t = interm;
1,442,895✔
839

840
                if ( *m != *t ) {
1,442,895✔
841
                        if ( *m < (FUNCTION + WILDOFFSET) ) return(0);
999✔
842
                        if ( *t < FUNCTION ) return(0);
999✔
843
                        if ( functions[*t-FUNCTION].spec !=
999✔
844
                        functions[*m-FUNCTION-WILDOFFSET].spec ) return(0);
999✔
845
                }
846
                i = m[1];
1,442,895✔
847
                if ( *m >= (FUNCTION + WILDOFFSET) ) { i--; m++; t++; }
1,442,895✔
848
                do { if ( *m++ != *t++ ) break; } while ( --i > 0 );
4,226,676✔
849
                if ( i <= 0 ) {                        /* Arguments match */
1,442,895✔
850
                        if ( AN.SignCheck && AN.ExpectedSign ) return(0);
111✔
851
                        i = *pattern - WILDOFFSET;
111✔
852
                        if ( i >= FUNCTION ) {
111✔
853
                                if ( *interm != GAMMA
×
854
#ifdef WITHFLOAT
855
                                && ( *interm != FLOATFUN )
×
856
#endif
857

858
                                && !CheckWild(BHEAD i,FUNTOFUN,*interm,&newvalue) ) {
×
859
                                        AddWild(BHEAD i,FUNTOFUN,newvalue);
×
860
                                        return(1);
×
861
                                }
862
                                return(0);
×
863
                        }
864
                        else return(1);
865
                }
866
        }
867
/*
868
        Store the current Wildcard assignments
869
*/
870
        t = wildargtaken = OldWork = AT.WorkPointer;
1,442,790✔
871
        t += ntwa;
1,442,790✔
872
        m = AN.WildValue;
1,442,790✔
873
        nwstore = i = (m[-SUBEXPSIZE+1]-SUBEXPSIZE)/4;
1,442,790✔
874
        if ( i > 0 ) {
1,442,790✔
875
                r = AT.WildMask;
1,440,348✔
876
                do {
1,670,658✔
877
                        *t++ = *m++; *t++ = *m++; *t++ = *m++; *t++ = *m++; *t++ = *r++;
1,670,658✔
878
                } while ( --i > 0 );
1,670,658✔
879
                *t++ = C->numrhs;
1,440,348✔
880
        }
881
        if ( t >= AT.WorkTop ) {
1,442,790✔
882
                MLOCK(ErrorMessageLock);
×
883
                MesWork();
×
884
                MUNLOCK(ErrorMessageLock);
×
885
                Terminate(-1);
×
886
        }
887
        AT.WorkPointer = t;
1,442,790✔
888

889
        if ( *wilds ) {
1,442,790✔
890
                if ( *wilds == 1 ) goto endoloop;
6✔
891
                else               goto enloop;                        /* tensors = 2 */
×
892
        }
893
        m = pattern; t = interm;
1,442,784✔
894
/*
895
        Single out the specials
896
*/
897
        if ( *t == GAMMA ) {
1,442,784✔
898
/*
899
                 #[ GAMMA :
900

901
                For the gamma's we need to do two things:
902
                a:        Find that there is a match
903
                b:        Find where the match occurs in the string
904
                This last thing cannot be stored in the current conventions,
905
                but once the wildcard assignments have been made it is much
906
                easier to find it back.
907
                Alternative: replace the function number in the term temporarily
908
                by the offset inside the string. This makes things maybe easier.
909
*/
910
                if ( *m != GAMMA ) goto NoCaseB;
×
911
                i = t[1] - m[1];
×
912
                if ( m[1] == FUNHEAD+1 ) {
×
913
                        if ( i ) goto NoCaseB;
×
914
                        if ( m[FUNHEAD] < (AM.OffsetIndex+WILDOFFSET) ||
×
915
                        t[FUNHEAD] >= (AM.OffsetIndex+WILDOFFSET) ) goto NoCaseB;
×
916

917
                        if ( CheckWild(BHEAD m[FUNHEAD]-WILDOFFSET,INDTOIND,t[FUNHEAD],&newvalue) ) goto NoCaseB;
×
918
                        AddWild(BHEAD m[FUNHEAD]-WILDOFFSET,INDTOIND,newvalue);
×
919
                        
920
                        AT.WorkPointer = OldWork;
×
921
                        if ( AN.SignCheck && AN.ExpectedSign ) return(0);
×
922
                        return(1);                /* m was eaten. we have a match! */
×
923
                }
924
                if ( i < 0 ) goto NoCaseB;        /* Pattern longer than target */
×
925
                mstop = m + m[1];
×
926
                tstop = t + t[1];
×
927
                m += FUNHEAD; t += FUNHEAD;
×
928
                if ( *m >= (AM.OffsetIndex+WILDOFFSET) && *t < (AM.OffsetIndex+WILDOFFSET) ) {
×
929
                        if ( CheckWild(BHEAD *m-WILDOFFSET,INDTOIND,*t,&newvalue) ) goto NoCaseB;
×
930
                        reservevalue = newvalue;
×
931
                        withwild = 1;
×
932
                        resernum = *m-WILDOFFSET;
×
933
                        AddWild(BHEAD *m-WILDOFFSET,INDTOIND,newvalue);
×
934
                }
935
                else if ( *m != *t ) goto NoCaseB;
×
936
                else withwild = 0;
937
                m++; t++;
×
938
                oldm = m; argtstop = oldt = t;
×
939
                j = 0;                                        /* No wildcard assignments yet */
×
940
                while ( i >= 0 ) {
×
941
                        if ( *m == *t ) {
×
942
WithGamma:                m++; t++;
×
943
                                if ( m >= mstop ) {
×
944
                                        if ( t < tstop && mstop < AN.patstop ) {
×
945
                                                WORD k;
×
946
                                                mnextsub = pattern + pattern[1];
×
947
                                                k = *mnextsub;
×
948
                                                while ( k == GAMMA && mnextsub[FUNHEAD]
×
949
                                                != pattern[FUNHEAD] ) {
×
950
                                                        mnextsub += mnextsub[1];
×
951
                                                        if ( mnextsub >= AN.patstop ) goto FullOK;
×
952
                                                        k = *mnextsub;
×
953
                                                }
954
                                                if ( k >= FUNCTION ) {
×
955
                                                        if ( k > (FUNCTION + WILDOFFSET) ) k -= WILDOFFSET;
×
956
                                                        if ( functions[k-FUNCTION].commute ) goto NoGamma;
×
957
                                                }
958
                                        }
959
FullOK:                                if ( AN.SignCheck && AN.ExpectedSign ) goto NoGamma;
×
960
                                        AN.RepFunList[AN.RepFunNum+1] = WORDDIF(oldt,argtstop);
×
961
                                        return(1);
×
962
                                }
963
                                if ( t >= tstop ) goto NoCaseB;
×
964
                        }
965
                        else if ( *m >= (AM.OffsetIndex+WILDOFFSET)
×
966
                        && *m < (AM.OffsetIndex + (WILDOFFSET<<1)) && ( *t >= 0 ||
×
967
                        *t < MINSPEC ) ) {                        /* Wildcard index */
968
                                if ( !CheckWild(BHEAD *m-WILDOFFSET,INDTOIND,*t,&newvalue) ) {
×
969
                                        AddWild(BHEAD *m-WILDOFFSET,INDTOIND,newvalue);
×
970
                                        j = 1;
×
971
                                        goto WithGamma;
×
972
                                }
973
                                else goto NoGamma;
×
974
                        }
975
                        else if ( *m < MINSPEC && *m >= (AM.OffsetVector+WILDOFFSET)
×
976
                        && *t < MINSPEC ) {                        /* Wildcard vector */
×
977
                                if ( !CheckWild(BHEAD *m-WILDOFFSET,VECTOVEC,*t,&newvalue) ) {
×
978
                                        AddWild(BHEAD *m-WILDOFFSET,VECTOVEC,newvalue);
×
979
                                        j = 1;
×
980
                                        goto WithGamma;
×
981
                                }
982
                                else goto NoGamma;
×
983
                        }
984
                        else {
985
NoGamma:
×
986
                                if ( j ) {                /* Undo wildcards */
×
987
                                        m = AN.WildValue;
×
988
                                        t = OldWork + AN.NumTotWildArgs; r = AT.WildMask; j = nwstore;
×
989
                                        if ( j > 0 ) {
×
990
                                                do {
×
991
                                                        *m++ = *t++; *m++ = *t++;
×
992
                                                        *m++ = *t++; *m++ = *t++; *r++ = *t++;
×
993
                                                } while ( --j > 0 );
×
994
                                                C->numrhs = *t++;
×
995
                                                C->Pointer = C->Buffer + oldcpointer;
×
996
                                        }
997
                                        j = 0;
998
                                }
999
                                m = oldm; t = ++oldt; i--;
×
1000
                                if ( withwild ) {
×
1001
                                        AddWild(BHEAD resernum,INDTOIND,reservevalue);
×
1002
                                }
1003
                        }
1004
                }
1005
                goto NoCaseB;
×
1006
/*
1007
                 #] GAMMA : 
1008
                 #[ Tensors :
1009
*/
1010
        }
1011
        else if ( *t >= FUNCTION && functions[*t-FUNCTION].spec >= TENSORFUNCTION ) {
1,442,784✔
1012
                mstop = m + m[1];
12✔
1013
                tstop = t + t[1];
12✔
1014
                mcount = 0;
12✔
1015
                m += FUNHEAD;
12✔
1016
                t += FUNHEAD;
12✔
1017
                AN.WildArgs = 0;
12✔
1018
                tcount = WORDDIF(tstop,t);
12✔
1019
                while ( m < mstop ) {
30✔
1020
                        if ( *m == FUNNYWILD ) { m++; AN.WildArgs++; }
18✔
1021
                        m++; mcount++;
18✔
1022
                }
1023
                tobeeaten = tcount - mcount + AN.WildArgs;
12✔
1024
                if ( tobeeaten ) {
12✔
1025
                        if ( tobeeaten < 0 || AN.WildArgs == 0 ) {
12✔
1026
                                AT.WorkPointer = OldWork;
×
1027
                                return(0);        /* Cannot match */
×
1028
                        }
1029
                }
1030
                AT.WildArgTaken[0] = AN.WildEat = tobeeaten;
12✔
1031
                for ( i = 1; i < AN.WildArgs; i++ ) AT.WildArgTaken[i] = 0;
12✔
1032
toploop:
12✔
1033
                numofwildarg = 0;
12✔
1034

1035
                m = pattern; t = interm;
12✔
1036
                mstop = m + m[1];
12✔
1037
                if ( *m != *t ) {
12✔
1038
                        i = *m - WILDOFFSET;
×
1039
                        if ( CheckWild(BHEAD i,FUNTOFUN,*t,&newvalue) ) goto NoCaseB;
×
1040
                        AddWild(BHEAD i,FUNTOFUN,newvalue);
×
1041
                }
1042
                m += FUNHEAD;
12✔
1043
                t += FUNHEAD;
12✔
1044
                while ( m < mstop ) {
30✔
1045
/*
1046
                        First test for an exact match
1047
*/
1048
                        if ( *m == *t ) { m++; t++; continue; }
18✔
1049
/*
1050
                        No exact match. Try ARGWILD
1051
*/
1052
                        AN.argaddress = t;
18✔
1053
                        if ( *m == FUNNYWILD ) {
18✔
1054
                                tobeeaten = AT.WildArgTaken[numofwildarg++];
12✔
1055
                                if ( CheckWild(BHEAD m[1],ARGTOARG|EATTENSOR,tobeeaten,t) ) goto endloop;
12✔
1056
                                AddWild(BHEAD m[1],ARGTOARG|EATTENSOR,tobeeaten);
12✔
1057
                                m += 2;
12✔
1058
                                t += tobeeaten;
12✔
1059
                                continue;
12✔
1060
                        }
1061
/*
1062
                        Now the various cases:
1063
*/
1064
                        i = *m;
6✔
1065
                        if ( i < MINSPEC ) {
6✔
1066
                                if ( *t != i ) {
×
1067
                                        if ( *t >= MINSPEC ) goto endloop;
×
1068
                                        i -= WILDOFFSET;
×
1069
                                        if ( i < AM.OffsetVector ) goto endloop;
×
1070
                                        if ( CheckWild(BHEAD i,VECTOVEC,*t,&newvalue) )
×
1071
                                                goto endloop;
×
1072
                                        AddWild(BHEAD i,VECTOVEC,newvalue);
×
1073
                                }
1074
                        }
1075
                        else if ( i >= AM.OffsetIndex ) {                        /* Index */
6✔
1076
                                if ( i < ( AM.OffsetIndex + WILDOFFSET ) ) goto endloop;
6✔
1077
                                if ( i >= ( AM.OffsetIndex + (WILDOFFSET<<1) ) ) {
6✔
1078
                                                                                                /* Summed over index */
1079
                                        goto endloop;                                /* For the moment */
×
1080
                                }
1081
                                i -= WILDOFFSET;
6✔
1082
                                if ( CheckWild(BHEAD i,INDTOIND,*t,&newvalue) )
6✔
1083
                                        goto endloop;                /* Assignment not allowed */
×
1084
                                AddWild(BHEAD i,INDTOIND,newvalue);
6✔
1085
                        }
1086
                        else goto endloop;
×
1087
                        m++; t++;
6✔
1088
                }
1089
                if ( AN.SignCheck && AN.ExpectedSign ) goto endloop;
12✔
1090
                AT.WorkPointer = OldWork;
12✔
1091
                if ( AN.WildArgs > 1 ) *wilds = 2;
12✔
1092
                return(1);                /* m was eaten. we have a match! */
12✔
1093

1094
endloop:;
×
1095
/*
1096
        restore the current Wildcard assignments
1097
*/
1098
                i = nwstore;
×
1099
                if ( i > 0 ) {
×
1100
                        m = AN.WildValue;
×
1101
                        t = OldWork + ntwa; r = AT.WildMask;
×
1102
                        do {
×
1103
                                *m++ = *t++; *m++ = *t++; *m++ = *t++; *m++ = *t++; *r++ = *t++;
×
1104
                        } while ( --i > 0 );
×
1105
                        C->numrhs = *t++;
×
1106
                        C->Pointer = C->Buffer + oldcpointer;
×
1107
                }
1108
enloop:;
×
1109
                i = AN.WildArgs - 1;
×
1110
                if ( i <= 0 ) {
×
1111
                        AT.WorkPointer = OldWork;
×
1112
                        return(0);
×
1113
                }
1114
                while ( --i >= 0 ) {
×
1115
                        if ( AT.WildArgTaken[i] == 0 ) {
×
1116
                                if ( i == 0 ) {
×
1117
                                        AT.WorkPointer = OldWork;
×
1118
                                        *wilds = 0;
×
1119
                                        return(0);
×
1120
                                }
1121
                        }
1122
                        else {
1123
                                (AT.WildArgTaken[i])--;
×
1124
                                numofwildarg = 0;
×
1125
                                for ( j = 0; j <= i; j++ ) {
×
1126
                                        numofwildarg += AT.WildArgTaken[j];
×
1127
                                }
1128
                                AT.WildArgTaken[j] = AN.WildEat-numofwildarg;
×
1129
                                for ( j++; j < AN.WildArgs; j++ ) AT.WildArgTaken[j] = 0;
×
1130
                                break;
1131
                        }
1132
                }
1133
                goto toploop;
×
1134
/*
1135
                 #] Tensors : 
1136
*/
1137
        }
1138
/*
1139
        Count the number of arguments. Either equal or an argument wildcard.
1140
*/
1141
        mstop = m + m[1];
1,442,772✔
1142
        tstop = t + t[1];
1,442,772✔
1143
        mcount = 0; tcount = 0;
1,442,772✔
1144
        m += FUNHEAD; t += FUNHEAD;
1,442,772✔
1145
        while ( t < tstop ) { tcount++; NEXTARG(t) }
3,363,033✔
1146
        AN.WildArgs = 0;
1,442,772✔
1147
        while ( m < mstop ) {
3,019,926✔
1148
                mcount++;
1,577,154✔
1149
                if ( *m == -ARGWILD ) AN.WildArgs++;
1,577,154✔
1150
                NEXTARG(m)
1,577,154✔
1151
        }
1152
        tobeeaten = tcount - mcount + AN.WildArgs;
1,442,772✔
1153
        if ( tobeeaten ) {
1,442,772✔
1154
                if ( tobeeaten < 0 || AN.WildArgs == 0 ) {
18,024✔
1155
                        AT.WorkPointer = OldWork;
4,407✔
1156
                        return(0);        /* Cannot match */
4,407✔
1157
                }
1158
        }
1159
/*
1160
        Set up the array AT.WildArgTaken for the number of arguments that each
1161
        wildarg eats.
1162
*/
1163
        AT.WildArgTaken[0] = AN.WildEat = tobeeaten;
1,438,365✔
1164
        for ( i = 1; i < AN.WildArgs; i++ ) AT.WildArgTaken[i] = 0;
1,447,716✔
1165
topofloop:
1,438,365✔
1166
        numofwildarg = 0;
4,269,210✔
1167
/*
1168
        Test for single wildcard object/argument
1169
*/
1170
        m = pattern; t = interm;
4,269,210✔
1171
        if ( *m != *t ) {
4,269,210✔
1172
                i = *m - WILDOFFSET;
999✔
1173
                if ( CheckWild(BHEAD i,FUNTOFUN,*t,&newvalue) ) goto NoCaseB;
999✔
1174
                AddWild(BHEAD i,FUNTOFUN,newvalue);
999✔
1175
        }
1176
        mstop = m + m[1];
4,269,210✔
1177
/*        tstop = t + t[1];  */
1178
        m += FUNHEAD;
4,269,210✔
1179
        t += FUNHEAD;
4,269,210✔
1180
        while ( m < mstop ) {
14,248,983✔
1181
                argmstop = oldm = m;
12,838,536✔
1182
                argtstop = oldt = t;
12,838,536✔
1183
                NEXTARG(argmstop)
12,838,536✔
1184
                NEXTARG(argtstop)
12,838,536✔
1185
                if ( t == tstop ) { /* This concerns a very rare bug */
12,838,536✔
1186
                        if ( *m == -ARGWILD ) goto ArgAll;
598✔
1187
                        goto endofloop;
×
1188
                }
1189
                if ( *m < 0 && *t < 0 ) {
12,837,938✔
1190
                        if ( *t <= -FUNCTION ) {
12,076,700✔
1191
                                if ( *t == *m ) {}
6✔
1192
                                else if ( *m <= -FUNCTION-WILDOFFSET
6✔
1193
                                && functions[-*t-FUNCTION].spec
×
1194
                                == functions[-*m-FUNCTION-WILDOFFSET].spec ) {
×
1195
                                        i = -*m - WILDOFFSET;
×
1196
                                        if ( CheckWild(BHEAD i,FUNTOFUN,-*t,&newvalue) ) goto endofloop;
×
1197
                                        AddWild(BHEAD i,FUNTOFUN,newvalue);
×
1198
                                }
1199
                                else if ( *m == -SYMBOL && m[1] >= 2*MAXPOWER ) {
6✔
1200
                                        i = m[1] - 2*MAXPOWER;
×
1201
                                        AN.argaddress = AT.FunArg;
×
1202
                                        AT.FunArg[ARGHEAD+1] = -*t;
×
1203
                                        if ( CheckWild(BHEAD i,SYMTOSUB,1,AN.argaddress) ) goto endofloop;
×
1204
                                        AddWild(BHEAD i,SYMTOSUB,0);
×
1205
                                }
1206
                                else if ( *m == -ARGWILD ) {
6✔
1207
ArgAll:                                i = AT.WildArgTaken[numofwildarg++];
6✔
1208
                                        AN.argaddress = t;
5,681,877✔
1209
                                        if ( CheckWild(BHEAD m[1],ARGTOARG,i,t) ) goto endofloop;
5,681,877✔
1210
                                        AddWild(BHEAD m[1],ARGTOARG,i);
5,681,877✔
1211
/*                                        m += 2; */
1212
                                        while ( --i >= 0 ) { NEXTARG(t) }
157,606,488✔
1213
                                        argtstop = t;
1214
                                }
1215
                                else goto endofloop;
×
1216
                        }
1217
                        else if ( *t == *m ) {
12,076,694✔
1218
                                if ( t[1] == m[1] ) {}
5,679,042✔
1219
                                else if ( *t == -SYMBOL ) {
5,675,778✔
1220
                                        j = SYMTOSYM;
1221
SymAll:
712,422✔
1222
                                        if ( ( i = m[1] - 2*MAXPOWER ) < 0 ) goto endofloop;
712,422✔
1223
                                        if ( CheckWild(BHEAD i,j,t[1],&newvalue) ) goto endofloop;
712,422✔
1224
                                        AddWild(BHEAD i,j,newvalue);
692,160✔
1225
                                }
1226
                                else if ( *t == -INDEX ) {
5,675,577✔
1227
IndAll:                                i = m[1] - WILDOFFSET;
18✔
1228
                                        if ( i < AM.OffsetIndex || i >= WILDOFFSET+AM.OffsetIndex )
834✔
1229
                                                                                                                        goto endofloop;
×
1230
                                                                /* We kill the summed over indices here */
1231
                                        if ( CheckWild(BHEAD i,INDTOIND,t[1],&newvalue) ) goto endofloop;
834✔
1232
                                        AddWild(BHEAD i,INDTOIND,newvalue);
810✔
1233
                                }
1234
                                else if ( *t == -VECTOR || *t == -MINVECTOR ) {
5,675,559✔
1235
                                        i = m[1] - WILDOFFSET;
5,668,662✔
1236
                                        if ( i < AM.OffsetVector ) goto endofloop;
5,668,662✔
1237
                                        if ( CheckWild(BHEAD i,VECTOVEC,t[1],&newvalue) ) goto endofloop;
5,668,662✔
1238
                                        AddWild(BHEAD i,VECTOVEC,newvalue);
2,839,434✔
1239
                                }
1240
                                else goto endofloop;
6,897✔
1241
                        }
1242
                        else if ( *m == -ARGWILD ) goto ArgAll;
6,397,652✔
1243
                        else if ( *m == -INDEX && m[1] >= AM.OffsetIndex+WILDOFFSET
719,553✔
1244
                        && m[1] < AM.OffsetIndex+(WILDOFFSET<<1) ) {
897✔
1245
                                if ( *t == -VECTOR ) goto IndAll;
897✔
1246
                                if ( *t == -SNUMBER && t[1] >= 0 && t[1] < AM.OffsetIndex ) goto IndAll;
891✔
1247
                                if ( *t == -MINVECTOR ) {
81✔
1248
                                        i = m[1] - WILDOFFSET;
×
1249
                                        AN.argaddress = AT.MinVecArg;
×
1250
                                        AT.MinVecArg[ARGHEAD+3] = t[1];
×
1251
                                        if ( CheckWild(BHEAD i,INDTOSUB,1,AN.argaddress) ) goto endofloop;
×
1252
                                        AddWild(BHEAD i,INDTOSUB,(WORD)0);
×
1253
                                }
1254
                                else goto endofloop;
81✔
1255
                        }
1256
                        else if ( *m == -SYMBOL && m[1] >= 2*MAXPOWER && *t == -SNUMBER ) {
718,656✔
1257
                                j = SYMTONUM;
712,221✔
1258
                                goto SymAll;
712,221✔
1259
                        }
1260
                        else if ( *m == -VECTOR && *t == -MINVECTOR &&
6,435✔
1261
                        ( i = m[1] - WILDOFFSET ) >= AM.OffsetVector ) {
2,115✔
1262
/*
1263
================================
1264
                                AN.argaddress = AT.MinVecArg;
1265
                                AT.MinVecArg[ARGHEAD+3] = t[1];
1266
                                if ( CheckWild(BHEAD i,VECTOSUB,1,AN.argaddress) ) goto endofloop;
1267
                                AddWild(BHEAD i,VECTOSUB,(WORD)0);
1268
================================
1269
*/
1270
                                if ( CheckWild(BHEAD i,VECTOMIN,t[1],&newvalue) ) goto endofloop;
2,115✔
1271
                                AddWild(BHEAD i,VECTOMIN,newvalue);
2,109✔
1272

1273
                        }
1274
                        else if ( *m == -MINVECTOR && *t == -VECTOR &&
4,320✔
1275
                        ( i = m[1] - WILDOFFSET ) >= AM.OffsetVector ) {
2,106✔
1276
/*
1277
================================
1278
                                AN.argaddress = AT.MinVecArg;
1279
                                AT.MinVecArg[ARGHEAD+3] = t[1];
1280
                                if ( CheckWild(BHEAD i,VECTOSUB,1,AN.argaddress) ) goto endofloop;
1281
                                AddWild(BHEAD i,VECTOSUB,(WORD)0);
1282
================================
1283
*/
1284
                                if ( CheckWild(BHEAD i,VECTOMIN,t[1],&newvalue) ) goto endofloop;
2,106✔
1285
                                AddWild(BHEAD i,VECTOMIN,newvalue);
2,100✔
1286
                        }
1287
                        else goto endofloop;
2,214✔
1288
                }
1289
                else if ( *t <= -FUNCTION && *m > 0 ) {
761,238✔
1290
                        if ( ( m[ARGHEAD]+ARGHEAD == *m ) && m[*m-1] == 3
18✔
1291
                        && m[*m-2] == 1 && m[*m-3] == 1 && m[ARGHEAD+1] >= FUNCTION
18✔
1292
                        && m[ARGHEAD+2] == *m-ARGHEAD-4 ) { /* Check for f(?a) etc */
18✔
1293
                                WORD *mmmst, *mmm;
18✔
1294
                                if ( m[ARGHEAD+1] >= FUNCTION+WILDOFFSET ) {
18✔
1295
/*                                        i = *m - WILDOFFSET; */
1296
                                        i = m[ARGHEAD+1] - WILDOFFSET;
18✔
1297
                                        if ( CheckWild(BHEAD i,FUNTOFUN,-*t,&newvalue) ) goto endofloop;
18✔
1298
                                        AddWild(BHEAD i,FUNTOFUN,newvalue);
18✔
1299
                                }
1300
                                else if ( m[ARGHEAD+1] != -*t ) goto endofloop;
×
1301
/*
1302
                                        Only arguments allowed are ?a etc.
1303
*/
1304
                                mmmst = m+*m-3;
18✔
1305
                                mmm = m + ARGHEAD + FUNHEAD + 1;
18✔
1306
                                while ( mmm < mmmst ) {
36✔
1307
                                        if ( *mmm != -ARGWILD ) goto endofloop;
18✔
1308
                                        i = 0;
18✔
1309
                                        AN.argaddress = t;
18✔
1310
                                        if ( CheckWild(BHEAD mmm[1],ARGTOARG,i,t) ) goto endofloop;
18✔
1311
                                        AddWild(BHEAD mmm[1],ARGTOARG,i);
18✔
1312
                                        mmm += 2;
18✔
1313
                                }
1314
                        }
1315
                        else goto endofloop;
×
1316
                }
1317
                else if ( *m < 0 && *t > 0 ) {
761,220✔
1318
                        if ( *m == -SYMBOL ) {                        /* SYMTOSUB */
760,653✔
1319
                                if ( m[1] < 2*MAXPOWER ) goto endofloop;
757,461✔
1320
                                i = m[1] - 2*MAXPOWER;
757,452✔
1321
                                AN.argaddress = t;
757,452✔
1322
                                if ( CheckWild(BHEAD i,SYMTOSUB,1,AN.argaddress) ) goto endofloop;
757,452✔
1323
                                AddWild(BHEAD i,SYMTOSUB,0);
757,443✔
1324
                        }
1325
                        else if ( *m == -VECTOR ) {
3,192✔
1326
                                if ( ( i = m[1] - WILDOFFSET ) < AM.OffsetVector )
×
1327
                                                                                                                        goto endofloop;
×
1328
                                AN.argaddress = t;
×
1329
                                if ( CheckWild(BHEAD i,VECTOSUB,1,t) ) goto endofloop;
×
1330
                                AddWild(BHEAD i,VECTOSUB,(WORD)0);
×
1331
                        }
1332
                        else if ( *m == -INDEX ) {
3,192✔
1333
                                if ( ( i = m[1] - WILDOFFSET ) < AM.OffsetIndex ) goto endofloop;
×
1334
                                if ( i >= AM.OffsetIndex + WILDOFFSET ) goto endofloop;
×
1335
                                AN.argaddress = t;
×
1336
                                if ( CheckWild(BHEAD i,INDTOSUB,1,AN.argaddress) ) goto endofloop;
×
1337
                                AddWild(BHEAD i,INDTOSUB,(WORD)0);
×
1338
                        }
1339
                        else if ( *m == -ARGWILD ) goto ArgAll;
3,192✔
1340
                        else goto endofloop;
18✔
1341
                }
1342
                else if ( *m > 0 && *t > 0 ) {
567✔
1343
                        WORD ii = *t-*m;
1344
                        i = *m;
873✔
1345
                        do { if ( *m++ != *t++ ) break; } while ( --i > 0 );
873✔
1346
                        if ( i == 1 && ii == 0 ) {        /* sign difference */
567✔
1347
                                goto endofloop;
3✔
1348
                        }
1349
                        else if ( i > 0 ) {
564✔
1350
                                WORD *cto, *cfrom, *csav, ci;
564✔
1351
                                WORD oRepFunNum;
564✔
1352
                                WORD *oRepFunList;
564✔
1353
                                WORD *oterstart,*oterstop,*opatstop;
564✔
1354
                                WORD oExpectedSign;
564✔
1355
                                WORD wildargs, wildeat;
564✔
1356
/*
1357
                                Not an exact match here.
1358
                                We have to hope that the pattern contains a composite wildcard.
1359
*/
1360
                                m = oldm; t = oldt;
564✔
1361
                                m += ARGHEAD; t += ARGHEAD;                        /* Point at (first?) term */
564✔
1362
                                mtrmstop = m + *m;
564✔
1363
                                ttrmstop = t + *t;
564✔
1364
                                if ( mtrmstop < argmstop ) goto endofloop;/* More than one term */
564✔
1365
                                msizcoef = mtrmstop[-1];
564✔
1366
                                if ( msizcoef < 0 ) msizcoef = -msizcoef;
564✔
1367
                                msubstop = mtrmstop - msizcoef;
564✔
1368
                                m++;
564✔
1369
                                if ( m >= msubstop ) goto endofloop;        /* Only coefficient */
564✔
1370
/*
1371
                                Here we have a composite term. It can match provided it
1372
                                matches the entire argument. This argument must be a
1373
                                single term also and the coefficients should match
1374
                                (more or less).
1375
                                The matching takes:
1376
                                1:        Match the functions etc. Nothing can be left.
1377
                                2:        Match dotproducts and symbols. ONLY must match
1378
                                        and nothing may be left.
1379
                                For safety it is best to take the term out and put it
1380
                                in workspace.
1381
*/
1382

1383
                                if ( argtstop > ttrmstop ) goto endofloop;
564✔
1384
                                m--;
564✔
1385
                                oterstart = AN.terstart;
564✔
1386
                                oterstop = AN.terstop;
564✔
1387
                                opatstop = AN.patstop;
564✔
1388
                                oRepFunList = AN.RepFunList;
564✔
1389
                                oRepFunNum = AN.RepFunNum;
564✔
1390
                                AN.RepFunNum = 0;
564✔
1391
                                AN.RepFunList = AT.WorkPointer;
564✔
1392
                        AT.WorkPointer = (WORD *)(((UBYTE *)(AT.WorkPointer)) + AM.MaxTer);
564✔
1393
                                if ( AT.WorkPointer+*t+5 > AT.WorkTop ) {
564✔
1394
                                        MLOCK(ErrorMessageLock);
×
1395
                                        MesWork();
×
1396
                                        MUNLOCK(ErrorMessageLock);
×
1397
                                        return(-1);
×
1398
                                }
1399
                                csav = cto = AT.WorkPointer;
1400
                                cfrom = t;
1401
                                ci = *t;
1402
                                while ( --ci >= 0 ) *cto++ = *cfrom++;
12,288✔
1403
                                AT.WorkPointer = cto;
564✔
1404
                                ci = msizcoef;
564✔
1405
                                cfrom = mtrmstop;
564✔
1406
                                --ci;
564✔
1407
                                if ( abs(*--cfrom) != abs(*--cto) ) {
564✔
1408
                                        AT.WorkPointer = csav;
×
1409
                                        AN.RepFunList = oRepFunList;
×
1410
                                        AN.RepFunNum = oRepFunNum;
×
1411
                                        AN.terstart = oterstart;
×
1412
                                        AN.terstop = oterstop;
×
1413
                                        AN.patstop = opatstop;
×
1414
                                        goto endofloop;
×
1415
                                }
1416
                                i = (*cfrom != *cto) ? 1 : 0; /* buffer AN.ExpectedSign until we are beyond the goto */
564✔
1417
                                while ( --ci >= 0 ) {
1,692✔
1418
                                        if ( *--cfrom != *--cto ) {
1,128✔
1419
                                                AT.WorkPointer = csav;
×
1420
                                                AN.RepFunList = oRepFunList;
×
1421
                                                AN.RepFunNum = oRepFunNum;
×
1422
                                                AN.terstart = oterstart;
×
1423
                                                AN.terstop = oterstop;
×
1424
                                                AN.patstop = opatstop;
×
1425
                                                goto endofloop;
×
1426
                                        }
1427
                                }
1428
                                oExpectedSign =  AN.ExpectedSign; /* buffer AN.ExpectedSign until we are beyond FindRest/FindOnly */
564✔
1429
                                AN.ExpectedSign = i;
564✔
1430
                                *m -= msizcoef;
564✔
1431
                                wildargs = AN.WildArgs;
564✔
1432
                                wildeat = AN.WildEat;
564✔
1433
                                for ( i = 0; i < wildargs; i++ ) wildargtaken[i] = AT.WildArgTaken[i];
564✔
1434
                                AN.ForFindOnly = 0; AN.UseFindOnly = 1;
564✔
1435
                                AN.nogroundlevel++;
564✔
1436
                                if ( FindRest(BHEAD csav,m) && ( AN.UsedOtherFind || FindOnly(BHEAD csav,m) ) ) {}
564✔
1437
                                else {
1438
nomatch:
6✔
1439
                                        *m += msizcoef;
6✔
1440
                                        AT.WorkPointer = csav;
6✔
1441
                                        AN.RepFunList = oRepFunList;
6✔
1442
                                        AN.RepFunNum = oRepFunNum;
6✔
1443
                                        AN.terstart = oterstart;
6✔
1444
                                        AN.terstop = oterstop;
6✔
1445
                                        AN.patstop = opatstop;
6✔
1446
                                        AN.WildArgs = wildargs;
6✔
1447
                                        AN.WildEat = wildeat;
6✔
1448
                                        AN.ExpectedSign = oExpectedSign;
6✔
1449
                                        AN.nogroundlevel--;
6✔
1450
                                        for ( i = 0; i < wildargs; i++ ) AT.WildArgTaken[i] = wildargtaken[i];
6✔
1451
                                        goto endofloop;
6✔
1452
                                }
1453
/*                                if ( *m == 1 || m[1] < FUNCTION || functions[m[1]-FUNCTION].spec >= TENSORFUNCTION ) { */
1454
                                if ( *m == 1 || m[1] < FUNCTION ) {
558✔
1455
                                        if ( AN.ExpectedSign ) goto nomatch;
×
1456
                                }
1457
                                else {
1458
                                        if ( m[1] > FUNCTION + WILDOFFSET ) {
558✔
1459
                                                if ( functions[m[1]-FUNCTION-WILDOFFSET].spec >= TENSORFUNCTION ) {
552✔
1460
                                                        if ( AN.ExpectedSign != AN.RepFunList[AN.RepFunNum-1] ) goto nomatch;
×
1461
                                                }
1462
                                        }
1463
                             else {
1464
                                                if ( AN.ExpectedSign != AN.RepFunList[AN.RepFunNum-1] ) goto nomatch;
6✔
1465
/*
1466
                                                if ( functions[m[1]-FUNCTION].spec >= TENSORFUNCTION ) {
1467
                                                        if ( AN.ExpectedSign != AN.RepFunList[AN.RepFunNum-1] ) goto nomatch;
1468
                                                }
1469
*/
1470
                                        }
1471
                                }
1472
                                AN.nogroundlevel--;
558✔
1473
                                AN.ExpectedSign = oExpectedSign;
558✔
1474
                                AN.WildArgs = wildargs;
558✔
1475
                                AN.WildEat = wildeat;
558✔
1476
                                for ( i = 0; i < wildargs; i++ ) AT.WildArgTaken[i] = wildargtaken[i];
558✔
1477
                                Substitute(BHEAD csav,m,1);
558✔
1478
                                cto = csav;
558✔
1479
                                cfrom = cto + *cto - msizcoef;
558✔
1480
                                cto++;
558✔
1481
                                *m += msizcoef;
558✔
1482
                                AT.WorkPointer = csav;
558✔
1483
                                AN.RepFunList = oRepFunList;
558✔
1484
                                AN.RepFunNum = oRepFunNum;
558✔
1485
                                AN.terstart = oterstart;
558✔
1486
                                AN.terstop = oterstop;
558✔
1487
                                AN.patstop = opatstop;
558✔
1488
                                if ( *cto != SUBEXPRESSION ) goto endofloop;
558✔
1489
                                cto += cto[1];
558✔
1490
                                if ( cto < cfrom ) goto endofloop;
558✔
1491
                        }
1492
                }
1493
                else goto endofloop;
×
1494

1495
                t = argtstop;                                                /* Next argument */
1496
                m = argmstop;
1497
        }
1498
        if ( AN.SignCheck && AN.ExpectedSign ) goto endofloop;
1,410,447✔
1499
        AT.WorkPointer = OldWork;
1,410,447✔
1500
        if ( AN.WildArgs > 1 ) *wilds = 1;
1,410,447✔
1501
        if ( AN.SignCheck && AN.ExpectedSign ) return(0);
1,410,447✔
1502
        return(1);                /* m was eaten. we have a match! */
1503

1504
endofloop:;
2,858,763✔
1505
/*
1506
        restore the current Wildcard assignments
1507
*/
1508
        i = nwstore;
2,858,763✔
1509
        if ( i > 0 ) {
2,858,763✔
1510
                m = AN.WildValue;
2,856,423✔
1511
                t = OldWork + ntwa; r = AT.WildMask;
2,856,423✔
1512
                do {
11,524,482✔
1513
                        *m++ = *t++; *m++ = *t++; *m++ = *t++; *m++ = *t++; *r++ = *t++;
11,524,482✔
1514
                } while ( --i > 0 );
11,524,482✔
1515
                C->numrhs = *t++;
2,856,423✔
1516
                C->Pointer = C->Buffer + oldcpointer;
2,856,423✔
1517
        }
1518

1519
endoloop:;
2,340✔
1520
        i = AN.WildArgs-1;
2,858,769✔
1521
        if ( i <= 0 ) {
2,858,769✔
1522
                AT.WorkPointer = OldWork;
26,742✔
1523
                return(0);
26,742✔
1524
        }
1525
        while ( --i >= 0 ) {
2,958,513✔
1526
                if ( AT.WildArgTaken[i] == 0 ) {
2,958,513✔
1527
                        if ( i == 0 ) {
127,668✔
1528
                                AT.WorkPointer = OldWork;
1,182✔
1529
                                return(0);
1,182✔
1530
                        }
1531
                }
1532
                else {
1533
                        (AT.WildArgTaken[i])--;
2,830,845✔
1534
                        numofwildarg = 0;
2,830,845✔
1535
                        for ( j = 0; j <= i; j++ ) {
8,365,536✔
1536
                                numofwildarg += AT.WildArgTaken[j];
5,534,691✔
1537
                        }
1538
                        AT.WildArgTaken[j] = AN.WildEat-numofwildarg;
2,830,845✔
1539
/* ----> bug to be replaced in other source code */
1540
                        for ( j++; j < AN.WildArgs; j++ ) AT.WildArgTaken[j] = 0;
2,956,167✔
1541
                        break;
1542
                }
1543
        }
1544
        goto topofloop;
2,830,845✔
1545
NoCaseB:
×
1546
/*
1547
        Restore the old Wildcard assignments
1548
*/
1549
        i = nwstore;
×
1550
        if ( i > 0 ) {
×
1551
                m = AN.WildValue;
×
1552
                t = OldWork + ntwa; r = AT.WildMask;
×
1553
                do {
×
1554
                        *m++ = *t++; *m++ = *t++; *m++ = *t++; *m++ = *t++; *r++ = *t++;
×
1555
                } while ( --i > 0 );
×
1556
                C->numrhs = *t++;
×
1557
                C->Pointer = C->Buffer + oldcpointer;
×
1558
        }
1559
        AT.WorkPointer = OldWork;
×
1560
        return(0);                /* no match */
×
1561
}
1562

1563
/*
1564
                 #] MatchFunction : 
1565
                 #[ ScanFunctions :                        WORD ScanFunctions(inpat,inter,par)
1566

1567
                Finds in which functions to look for a match.
1568
                inpat is the start of the pattern still to be matched.
1569
                inter is the start of the term still to be matched.
1570
                par gives information about commutativity.
1571
                        par = 0: nothing special
1572
                        par = 1: regular noncommuting function
1573
                        par = 2: GAMMA function
1574

1575
                AN.patstop: end of the functions field in the search pattern
1576
                AN.terstop: end of the functions field in the target pattern
1577
                AN.terstart: address of entire term;
1578

1579
                The actual matching of the functions and their arguments is done
1580
                in a number of different routines. Mainly MatchFunction when there
1581
                are no symmetry properties.
1582
                Also: MatchE
1583
                      MatchCy
1584
                      FunMatchSy
1585
                      FunMatchCy
1586

1587
                The main problem here is backtracking, ie continuing with wildcard
1588
                possibilities when a first assignment doesn't work.
1589
                Important note: this was completely forgotten in the symmetric
1590
                functions till 6-jan-2009. As of the moment this still has to
1591
                be fixed.  ??????21-mar-2023????? Is this still unfixed?????
1592

1593
                Functions inside functions can cause problems when antisymmetric
1594
                functions are involved. The sign of the term may be at stake.
1595
                At the lowest level this is no problem but in f(-fas(n2,n1)) this
1596
                plays a role. Next is when we have a product of functions inside
1597
                an argument. The strategy must be that we test the sign only at the
1598
                last function. Hence, when inpat+inpat[1] >= AN.patstop.
1599
                We might relax that to the last antisymmetric function at a later stage.
1600

1601
        New scheme to be implemented for non-commuting objects:
1602
        When we are matching a second (or higher) function, any match can only
1603
        be directly after the last matched non-commuting function or a commuting
1604
        function. This will take care of whatever happens in MatchE etc.
1605
*/
1606

1607
WORD ScanFunctions(PHEAD WORD *inpat, WORD *inter, WORD par)
2,201,022✔
1608
{
1609
        GETBIDENTITY
1610
        WORD i, *m, *t, *r, sym, psym;
2,201,022✔
1611
        WORD *newpat, *newter, *instart, *oinpat = 0, *ointer = 0;
2,201,022✔
1612
        WORD nwstore, offset, *OldWork, SetStop = 0, oRepFunNum = AN.RepFunNum;
2,201,022✔
1613
        WORD wilds, wildargs = 0, wildeat = 0, *wildargtaken;
2,201,022✔
1614
        WORD *Oterfirstcomm = AN.terfirstcomm;
2,201,022✔
1615
        CBUF *C = cbuf+AT.ebufnum;
2,201,022✔
1616
        int ntwa = AN.NumTotWildArgs;
2,201,022✔
1617
        LONG oldcpointer = C->Pointer - C->Buffer;
2,201,022✔
1618
        WORD oldSignCheck = AN.SignCheck;
2,201,022✔
1619
        instart = inter;
2,201,022✔
1620
/*
1621
        Only active for the last function in the pattern.
1622
        The actual test on the sign is in MatchFunction or the symmetric functions
1623
*/
1624
        if ( AN.nogroundlevel ) {
2,201,022✔
1625
                AN.SignCheck = ( inpat + inpat[1] >= AN.patstop ) ? 1 : 0;
558✔
1626
        }
1627
        else {
1628
                AN.SignCheck = 0;
2,200,464✔
1629
        }
1630
/*
1631
                        Store the current Wildcard assignments
1632
*/
1633
        t = wildargtaken = OldWork = AT.WorkPointer;
2,201,022✔
1634
        t += ntwa;
2,201,022✔
1635
        m = AN.WildValue;
2,201,022✔
1636
        nwstore = i = (m[-SUBEXPSIZE+1]-SUBEXPSIZE)/4;
2,201,022✔
1637
        if ( i > 0 ) {
2,201,022✔
1638
                r = AT.WildMask;
2,197,374✔
1639
                do {
2,667,714✔
1640
                        *t++ = *m++; *t++ = *m++; *t++ = *m++; *t++ = *m++; *t++ = *r++;
2,667,714✔
1641
                } while ( --i > 0 );
2,667,714✔
1642
                *t++ = C->numrhs;
2,197,374✔
1643
        }
1644
        if ( t >= AT.WorkTop ) {
2,201,022✔
1645
                MLOCK(ErrorMessageLock);
×
1646
                MesWork();
×
1647
                MUNLOCK(ErrorMessageLock);
×
1648
                Terminate(-1);
×
1649
        }
1650
        AT.WorkPointer = t;
2,201,022✔
1651
        do {
3,764,091✔
1652
#ifndef NEWCOMMUTE
1653
/*
1654
                Find an eligible unsubstituted function
1655
*/
1656
                if ( AN.RepFunNum > 0 ) {
3,764,091✔
1657
/*
1658
                        First try a non-commuting function, just after the last
1659
                        substituted non-commuting function.
1660
*/
1661
                        if ( *inter >= FUNCTION && functions[*inter-FUNCTION].commute ) {
3,636✔
1662
                                do {
3✔
1663
                                        offset = WORDDIF(inter,AN.terstart);
3✔
1664
                                        for ( i = 0; i < AN.RepFunNum; i += 2 ) {
6✔
1665
                                                if ( AN.RepFunList[i] >= offset ) break;
3✔
1666
                                        }
1667
                                        if ( i >= AN.RepFunNum ) break;
3✔
1668
                                        inter += inter[1];
×
1669
                                } while ( inter < AN.terfirstcomm );
×
1670
                                if ( inter < AN.terfirstcomm ) { /* Check that it is directly after */
3✔
1671
                                        for ( i = 0; i < AN.RepFunNum; i += 2 ) {
3✔
1672
                                                if ( functions[AN.terstart[AN.RepFunList[i]]-FUNCTION].commute
3✔
1673
                                                && AN.RepFunList[i]+AN.terstart[AN.RepFunList[i]+1] == offset ) break;
3✔
1674
                                        }
1675
                                        if ( i < AN.RepFunNum ) goto trythis;
3✔
1676
                                }
1677
                                inter = AN.terfirstcomm;
1678
                        }
1679
/*
1680
                        Now try one of the commuting functions
1681
*/
1682
                        while ( inter < AN.terstop ) {
6,900✔
1683
                                offset = WORDDIF(inter,AN.terstart);
6,717✔
1684
                                for ( i = 0; i < AN.RepFunNum; i += 2 ) {
10,365✔
1685
                                        if ( AN.RepFunList[i] == offset ) break;
6,915✔
1686
                                }
1687
                                if ( i >= AN.RepFunNum ) break;
6,717✔
1688
                                inter += inter[1];
3,267✔
1689
                        }
1690
                        if ( inter >= AN.terstop ) goto Failure;
3,633✔
1691
trythis:;
3,450✔
1692
                }
1693
                else {
1694
/*
1695
                        The first function can be anywhere. We have no problems.
1696
*/
1697
                        offset = WORDDIF(inter,AN.terstart);
3,760,455✔
1698
                }
1699
#else
1700
                /* first find an unsubstituted function */
1701
                do {
1702
                        offset = WORDDIF(inter,AN.terstart);
1703
                        for ( i = 0; i < AN.RepFunNum; i += 2 ) {
1704
                                if ( AN.RepFunList[i] == offset ) break;
1705
                        }
1706
                        if ( i >= AN.RepFunNum ) break;
1707
                        inter += inter[1];
1708
                } while ( inter < AN.terstop );
1709
                if ( inter >= AN.terstop ) goto Failure;
1710
#endif
1711
                wilds = 0;
3,763,908✔
1712
                /* We found one */
1713
                if ( *inter >= FUNCTION && *inpat >= FUNCTION ) {
3,763,908✔
1714
                        if ( *inpat == *inter || *inpat >= FUNCTION + WILDOFFSET ) {
3,411,993✔
1715
/*
1716
                                if ( inter[1] == FUNHEAD ) goto rewild;
1717
*/
1718
                                if ( functions[*inter-FUNCTION].spec >= TENSORFUNCTION
1,444,020✔
1719
                                && ( *inter == *inpat ||
36✔
1720
                                functions[*inpat-FUNCTION-WILDOFFSET].spec >= TENSORFUNCTION ) ) {
×
1721
                                        sym = functions[*inter-FUNCTION].symmetric & ~REVERSEORDER;
36✔
1722
                                        if ( *inpat == *inter ) psym = sym;
36✔
1723
                                        else psym = functions[*inpat-FUNCTION-WILDOFFSET].symmetric & ~REVERSEORDER;
×
1724
                                        if ( sym == ANTISYMMETRIC || sym == SYMMETRIC
36✔
1725
                                        || psym == SYMMETRIC || psym == ANTISYMMETRIC ) {
36✔
1726
                                                if ( sym == ANTISYMMETRIC && psym == SYMMETRIC ) goto rewild;
×
1727
                                                if ( sym == SYMMETRIC && psym == ANTISYMMETRIC ) goto rewild;
×
1728
/*
1729
                                                Special function call for (anti)symmetric tensors
1730
*/
1731
                                                if ( MatchE(BHEAD inpat,inter,instart,par) ) goto OnSuccess;
×
1732
                                        }
1733
                                        else if ( sym == CYCLESYMMETRIC || sym == RCYCLESYMMETRIC
36✔
1734
                                        || psym == CYCLESYMMETRIC || psym == RCYCLESYMMETRIC ) {
12✔
1735
/*
1736
                                                Special function call for (r)cyclic tensors
1737
*/
1738
                                                if ( MatchCy(BHEAD inpat,inter,instart,par) ) goto OnSuccess;
24✔
1739
                                        }
1740
                                        else goto rewild;
12✔
1741
                                }
1742
                                else if ( functions[*inter-FUNCTION].spec <= 0
1,443,984✔
1743
                                && ( *inter == *inpat ||
1,443,984✔
1744
                                functions[*inpat-FUNCTION-WILDOFFSET].spec <= 0 ) ) {
2,322✔
1745
                                        sym = functions[*inter-FUNCTION].symmetric & ~REVERSEORDER;
1,443,984✔
1746
                                        if ( *inpat == *inter ) psym = sym;
1,443,984✔
1747
                                        else psym = functions[*inpat-FUNCTION-WILDOFFSET].symmetric & ~REVERSEORDER;
2,322✔
1748
                                        if ( psym == SYMMETRIC || sym == SYMMETRIC
1,443,984✔
1749
/*
1750
                                        The next statement was commented out. Why????
1751
                                        Werkt nog niet. Teken wordt nog niet bijgehouden.
1752
                                        5-nov-2001
1753
*/
1754
                                        || psym == ANTISYMMETRIC || sym == ANTISYMMETRIC
1,441,917✔
1755
                                        ) {
1756
                                                if ( sym == ANTISYMMETRIC && psym == SYMMETRIC ) goto rewild;
2,073✔
1757
                                                if ( sym == SYMMETRIC && psym == ANTISYMMETRIC ) goto rewild;
1,632✔
1758
                                                if ( FunMatchSy(BHEAD inpat,inter,instart,par) ) goto OnSuccess;
1,632✔
1759
                                        }
1760
                                        else
1761
                                                if ( sym == CYCLESYMMETRIC || sym == RCYCLESYMMETRIC
1,441,911✔
1762
                                        || psym == CYCLESYMMETRIC || psym == RCYCLESYMMETRIC ) {
1,441,911✔
1763
                                                if ( FunMatchCy(BHEAD inpat,inter,instart,par) ) goto OnSuccess;
×
1764
                                        }
1765
                                        else goto rewild;
1,441,911✔
1766
                                }
1767
                                else goto rewild;
×
1768
                                AN.terfirstcomm = Oterfirstcomm;
387✔
1769
                        }
1770
                        else if ( par > 0 ) { SetStop = 1; goto maybenext; }
1,967,973✔
1771
                }
1772
                else {
1773
rewild:
351,915✔
1774
                AN.terfirstcomm = Oterfirstcomm;
1,794,285✔
1775
                if ( *inter != SUBEXPRESSION && MatchFunction(BHEAD inpat,inter,&wilds) ) {
1,794,285✔
1776
                        AN.terfirstcomm = Oterfirstcomm;
1,410,039✔
1777
                        if ( wilds ) {
1,410,039✔
1778
/*
1779
                                Store wildcards to continue in MatchFunction if the current
1780
                                wildcards do not work out.
1781
*/
1782
                                wildargs = AN.WildArgs;
4,011✔
1783
                                wildeat = AN.WildEat;
4,011✔
1784
                                for ( i = 0; i < wildargs; i++ ) wildargtaken[i] = AT.WildArgTaken[i];
15,033✔
1785
                                oinpat = inpat; ointer = inter;
1786
                        }
1787
                        if ( par && *inter == GAMMA && AN.RepFunList[AN.RepFunNum+1] ) {
1,410,039✔
1788
                                SetStop = 1; goto NoMat;
×
1789
                        }
1790
                        if ( par == 2 ) {
6✔
1791
                                if ( *inter < FUNCTION || functions[*inter-FUNCTION].commute ) {
×
1792
                                        goto NoMat;
×
1793
                                }
1794
                                par = 1;
1795
                        }
1796
                        AN.RepFunList[AN.RepFunNum] = offset;
1,410,039✔
1797
                        AN.RepFunNum += 2;
1,410,039✔
1798
                        newpat = inpat + inpat[1];
1,410,039✔
1799
                        if ( newpat >= AN.patstop ) {
1,410,039✔
1800
                                if ( AN.UseFindOnly == 0 ) {
1,406,916✔
1801
                                        if ( FindOnce(BHEAD AN.findTerm,AN.findPattern) ) {
27✔
1802
                                                AN.UsedOtherFind = 1;
27✔
1803
                                                goto OnSuccess;
27✔
1804
                                        }
1805
                                        AN.RepFunNum -= 2;
×
1806
                                        goto NoMat;
×
1807
                                }
1808
                                goto OnSuccess;
1,406,889✔
1809
                        }
1810
                        if ( *inter < FUNCTION || functions[*inter-FUNCTION].commute ) {
3,123✔
1811
                                newter = inter + inter[1];
6✔
1812
                                if ( newter >= AN.terstop ) goto Failure;
6✔
1813
                                if ( *inter == GAMMA && inpat[1] <
6✔
1814
                                inter[1] - AN.RepFunList[AN.RepFunNum-1] ) {
×
1815
                                        if ( ScanFunctions(BHEAD newpat,newter,2) ) goto OnSuccess;
×
1816
                                        AN.terfirstcomm = Oterfirstcomm;
×
1817
                                }
1818
                                else if ( *newter ==  SUBEXPRESSION ) {}
6✔
1819
                                else if ( functions[*inter-FUNCTION].commute ) {
6✔
1820
                                        if ( ScanFunctions(BHEAD newpat,newter,1) ) goto OnSuccess;
6✔
1821
                                        AN.terfirstcomm = Oterfirstcomm;
×
1822
                                        if ( ( *newpat < (FUNCTION+WILDOFFSET)
×
1823
                                                && ( functions[*newpat-FUNCTION].commute == 0 ) ) ||
×
1824
                                                ( *newpat >= (FUNCTION+WILDOFFSET)
1825
                                                && ( functions[*newpat-FUNCTION-WILDOFFSET].commute == 0 ) ) ) {
×
1826
                                                newter = AN.terfirstcomm;
×
1827
                                                if ( newter < AN.terstop && ScanFunctions(BHEAD newpat,newter,1) ) goto OnSuccess;
×
1828
                                        }
1829
                                }
1830
                                else {
1831
                                        if ( ScanFunctions(BHEAD newpat,instart,1) ) goto OnSuccess;
×
1832
                                        AN.terfirstcomm = Oterfirstcomm;
×
1833
                                }
1834
                                SetStop = par;
1835
                        }
1836
                        else {
1837
/*
1838
                                Shouldn't this be newpat instead of inpat?????
1839
*/
1840
                                if ( par && inter > instart && ( ( *newpat < (FUNCTION+WILDOFFSET)
3,117✔
1841
                                && functions[*newpat-FUNCTION].commute ) ||
×
1842
                                ( *newpat >= (FUNCTION+WILDOFFSET)
1843
                                && functions[*newpat-FUNCTION-WILDOFFSET].commute ) ) ) {
×
1844
                                        SetStop = 1;
1845
                                }
1846
                                else {
1847
                                        newter = instart;
3,117✔
1848
                                        if ( ScanFunctions(BHEAD newpat,newter,par) ) goto OnSuccess;
3,117✔
1849
                                        AN.terfirstcomm = Oterfirstcomm;
1,122✔
1850
                                }
1851
                        }
1852
/*
1853
                        Restore the old Wildcard assignments
1854
*/
1855
NoMat:
1,140✔
1856
                        i = nwstore;
1,140✔
1857
                        if ( i > 0 ) {
1,140✔
1858
                                m = AN.WildValue;
1,140✔
1859
                                t = OldWork + ntwa; r = AT.WildMask;
1,140✔
1860
                                do {
2,382✔
1861
                                        *m++ = *t++; *m++ = *t++; *m++ = *t++; *m++ = *t++; *r++ = *t++;
2,382✔
1862
                                } while ( --i > 0 );
2,382✔
1863
                                C->numrhs = *t++;
1,140✔
1864
                                C->Pointer = C->Buffer + oldcpointer;
1,140✔
1865
                        }
1866
/*                        AN.RepFunNum -= 2; */
1867
                        AN.RepFunNum = oRepFunNum;
1,140✔
1868
                        if ( wilds ) {
1,140✔
1869
                                inter = ointer; inpat = oinpat;
6✔
1870
                                AN.WildArgs = wildargs;
6✔
1871
                                AN.WildEat = wildeat;
6✔
1872
                                for ( i = 0; i < wildargs; i++ ) AT.WildArgTaken[i] = wildargtaken[i];
18✔
1873
                                goto rewild;
6✔
1874
                        }
1875
                        if ( SetStop ) break;
1,134✔
1876
                }
1877
                else if ( par ) {
384,246✔
1878
maybenext:
×
1879
                        if ( *inpat < (FUNCTION+WILDOFFSET) ) {
×
1880
                                if ( *inpat < FUNCTION ||
×
1881
                                functions[*inpat-FUNCTION].commute ) break;
×
1882
                        }
1883
                        else {
1884
                                if ( functions[*inpat-FUNCTION-WILDOFFSET].commute ) break;
×
1885
                        }
1886
                }}
1887
                inter += inter[1];
2,353,740✔
1888
        } while ( inter < AN.terstop );
2,353,740✔
1889
Failure:
790,671✔
1890
    AN.SignCheck = oldSignCheck;
790,854✔
1891
        AT.WorkPointer = OldWork;
790,854✔
1892
        return(0);
790,854✔
1893
OnSuccess:
1,410,186✔
1894
        if ( AT.idallflag && AN.nogroundlevel <= 0 ) {
1,410,186✔
1895
                if ( AT.idallmaxnum > 0 && AT.idallnum >= AT.idallmaxnum ) {
18✔
1896
                        AN.terfirstcomm = Oterfirstcomm;
×
1897
                    AN.SignCheck = oldSignCheck;
×
1898
                        AT.WorkPointer = OldWork;
×
1899
                        return(0);
×
1900
                }
1901
                SubsInAll(BHEAD0);
18✔
1902
                AT.idallnum++;
18✔
1903
                if ( AT.idallmaxnum == 0 || AT.idallnum < AT.idallmaxnum ) goto NoMat;
18✔
1904
        }
1905
        AN.terfirstcomm = Oterfirstcomm;
1,410,168✔
1906
    AN.SignCheck = oldSignCheck;
1,410,168✔
1907
/*
1908
        Now the disorder test
1909
*/
1910
        if ( AN.DisOrderFlag && AN.RepFunNum >= 4 ) {
1,410,168✔
1911
                WORD k, kk;
1912
                for ( i = 2; i < AN.RepFunNum; i += 2 ) {
×
1913
/*
1914
------------> We still have to copy the code from Normalize wrt properorderflag
1915
*/
1916
                        m = AN.terstart + AN.RepFunList[i-2];
×
1917
                        t = AN.terstart + AN.RepFunList[i];
×
1918
                        if ( *m != *t ) {
×
1919
                                if ( *m > *t ) continue;
×
1920
                                goto doesmatch;
×
1921
                        }
1922
                        if ( *m >= FUNCTION && functions[*m-FUNCTION].spec >=
×
1923
                                TENSORFUNCTION ) {
1924
                                k = m[1] - FUNHEAD;
×
1925
                                kk = t[1] - FUNHEAD;
×
1926
                                m += FUNHEAD;
×
1927
                                t += FUNHEAD;
×
1928
                        }
1929
                        else {
1930
                                k = m[1] - FUNHEAD;
×
1931
                                kk = t[1] - FUNHEAD;
×
1932
                                m += FUNHEAD;
×
1933
                                t += FUNHEAD;
×
1934
                        }
1935
                        while ( k > 0 && kk > 0 ) {
×
1936
                                if ( *m < *t ) goto NextFor;
×
1937
                                else if ( *m++ > *t++ ) goto doesmatch;
×
1938
                                k--; kk--;
×
1939
                        }
1940
                        if ( k > 0 ) goto doesmatch;
×
1941
NextFor:;
×
1942
                }
1943
                SetStop = 1;
×
1944
                goto NoMat;
×
1945
        }
1946
doesmatch:
1,410,168✔
1947
        AT.WorkPointer = OldWork;
1,410,168✔
1948
        return(1);
1,410,168✔
1949
}
1950

1951
/*
1952
                 #] ScanFunctions : 
1953
        #] Patterns :
1954
*/
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc