Sindbad~EG File Manager
/* operators from simpsums menu */
/* except orderterms, which is in order.c */
/*
M. Beeson
11.21.90 original date
6.26.98 last modified
*/
#include <string.h>
#include <ctype.h>
#include <assert.h>
#define POLYVAL_DLL
#include "globals.h"
#include "ops.h" /* prototypes of operators */
#include "prover.h"
#include "order.h"
#include "cancel.h"
#include "integral.h"
#include "factor.h" /* ratpart2 */
#include "mpmem.h"
#include "deval.h"
#include "pvalaux.h" /* summands */
#include "simpsums.h"
static int calc_combines(unsigned short, term, term, term *);
static int cancel_integral(term *buffer,int k, int i,int j,term *ans);
/*____________________________________________________________________*/
static void actuallycancel(term *buffer1, int n, int i, int j, term *buffer2)
/* having determined that buffer1[i] and buffer1[j] have different
signs, produce buffer2 by making a copy of buffer1 omitting those two
terms. n is the dimension of buffer1. */
{ int p,max,min;
min = ( (i<j) ? i : j );
max = ( (i<j) ? j : i );
for(p = 0; p < min; p++)
buffer2[p]= buffer1[p];
for(p=min;p+1<max;p++)
buffer2[p] = buffer1[p+1];
for(p=max-1;p<n-2;p++)
buffer2[p] = buffer1[p+2];
}
/*_____________________________________________________________________*/
static int combines(term a,term b, term *ans)
/* combine like terms such as 2xy^2 and 3xy^2 */
/* combine axy^2 and bxy^2 to (a+b)xy^2 if a and b are constant */
/* but don't collect a and b in a + b + x^2 to get (a+b) + xy^2, as the
term will just be flattened again, creating an infinite loop */
/* return 1 for success, putting the answer in *ans */
/* return 0 for failure */
/* return -1 for a cancellation */
{ term na,ca,sa,nb,cb,sb;
term temp,val,temp2,temp3,temp4;
aflag arithflag = get_arithflag();
int err;
ncs(a,&na,&ca,&sa);
ncs(b,&nb,&cb,&sb);
if(!equals(sa,sb))
{ if(ATOMIC(a) || ATOMIC(b))
return 0;
if(
( contains(a,INTEGRAL) && contains(b,INTEGRAL) && calc_combines(INTEGRAL, a,b,ans))
|| ( contains(a,DIFF) && contains(b,DIFF) && calc_combines(DIFF, a,b,ans))
)
{ HIGHLIGHT(*ans);
return 1;
}
if(FUNCTOR(sa) == FUNCTOR(sb))
{ unsigned f = FUNCTOR(sa);
if(f==ABS && eqtest(ARG(0,sa),ARG(0,sb)))
sb = sa; /* and go on */
else if(f == '^' && equals(ARG(1,sa),ARG(1,sb)) && INTEGERP(ARG(1,sa)))
{ int r = eqtest(ARG(0,sa),ARG(0,sb));
if(!r)
return 0;
sb = sa;
if(ISODD(ARG(1,sa)) && r == -1)
nb = tnegate(nb);
/* and go on */
}
else
return 0; /* failure */
}
else
return 0; /* failure */
}
if(equals(ca,cb)) /* a common case */
/* then ans = (na + nb)(ca * sa) */
/* unless na + nb = 0, in which case this is a cancellation */
{ if(ZERO(na))
{ *ans = product3(nb,ca,sa);
return 1;
}
if(ZERO(nb))
{ *ans = product3(na,ca,sa);
return 1;
}
if(equals(ca,infinity))
{ /* don't cancel infinity - infinity = 0;
also don't cancel 2 infinity - infinity = infinity
*/
double z,w;
deval(na,&z);
if(z == BADVAL)
return 0; /* failure to combine */
deval(nb,&w);
if(w == BADVAL)
return 0;
if(z >= 0.0 && w <= 0.0)
return 0;
if(z <= 0.0 && w >= 0.0)
return 0;
}
temp = sum(na,nb);
/* if arithflag.comdenom==0, we don't get the value zero below
in order to cancel a term with a fractional numerical part;
therefore check it directly */
if(
(NEGATIVE(na) && equals(ARG(0,na),nb)) ||
(NEGATIVE(nb) && equals(ARG(0,nb),na))
)
{ *ans = zero;
return -1; /* a cancellation */
}
if(equals(na,nb))
{ value(product(two,na),&nb);
*ans = product3(nb,ca,sa);
HIGHLIGHT(*ans);
return 1;
}
if(get_polyvalfunctionflag())
{ /* let's be sure to cancel sqrt(2) and 2^(1/2) etc. */
double z;
long kk;
deval(temp,&z);
if(z != BADVAL && nearint(z,&kk) && kk==0)
{ *ans= zero;
return -1; /* a cancellation */
}
}
err = value(temp,&val); /* val is a term representing a number */
if(!err && ZERO(val))
{ *ans = zero;
return -1; /* a cancellation */
}
if(arithflag.comdenom==0 &&
contains(temp,'/')
)
/* for example, 1/x and 1/2x don't combine under
the stated conditions; automode will use
common denominators; also 1/2x and 1/2x or even
1/2 and 1/2 do not combine; automode will use
addfractions.
When not in automode, "collect like terms" will
set arithflag.comdenom to 1 first so that the user
can collect such terms if desired; this code is
to control automode when adding numerical fractions.
*/
return 0;
if(err && !(ONE(ca) && ONE(sa)))
{ *ans = product3(temp,ca,sa);
/* example: x + sqrt(5)x combines to (1+sqrt 5) x */
HIGHLIGHT(*ans);
return 1;
}
if(err) /* && ONE(ca) && ONE(sa) */
/* e.g. temp = 1 + sqrt 5; they don't really combine.
But, consider temp = sqrt 5 + 2 sqrt 5; these DO combine to 3 sqrt 5.
*/
{ term c,nc,sc,d,qq;
int i;
assert(FUNCTOR(temp) == '+');
naive_gcd(ARG(0,temp),ARG(1,temp),&c);
if(FUNCTOR(c) == '*' && (INTEGERP(ARG(0,c)) || RATIONALP(ARG(0,c))))
{ ratpart2(c,&nc,&sc);
c = sc; /* reject any rational part of the gcd */
}
/* example, (1/12) sqrt 5 + (1/3) sqrt 5; naive_gcd
returns (1/3) sqrt 5 and we only want c = sqrt 5 */
if(ONE(c) || equals(minusone,c) || RATIONALP(c))
/* the last RATIONALP to prevent (1/2) u + (1/2) v = (u + v)(1/2)
where u and v are e.g. cube roots of complicated numerical
expressions */
{ RELEASE(temp);
return 0; /* they don't combine */
}
else
{ int saveit = arithflag.comdenom;
term copyofc;
copy(c,©ofc);
qq = make_term('+',2);
for(i=0;i<2;i++)
{ polyval(make_fraction(ARG(i,temp),i ? copyofc : c),ARGPTR(qq)+i);
/* use copyofc so that qq will be a tree not a DAG */
}
if( contains(ARG(0,qq),'/') && contains(ARG(1,qq),'/'))
{ arithflag.comdenom = 0;
set_arithflag(arithflag);
}
/* (1/2) sqrt 5 + (1/3) sqrt 5 = (1/2 + 1/3) sqrt 5, because
the following code will fail and contentfactor will
get called. But this code will succeed on
sqrt 5 + (1/3) sqrt 5 = (4/3) sqrt 5 */
err = value(qq,&d);
arithflag.comdenom = saveit;
set_arithflag(arithflag);
if(err!=0 && err !=2)
{ /* after all they don't combine */
RELEASE(temp);
RELEASE(qq);
if(!OBJECT(c))
destroy_term(copyofc);
return 0;
}
err = value(product(d,c),&val);
if(err)
val = product(d,c);
/* That's the numerical part. Don't forget
the constant and symbolic parts. */
*ans = product3(val,ca,sa);
HIGHLIGHT(*ans);
return 1; /* they do combine */
}
}
if(ONE(ca) && ONE(sa)) /* then this is really arithmetic */
/* In automode, arithmetic will be called first, so you
won't get here while collecting like terms. However, this
is also called by polyval, which needs it to do arithmetic. */
{ *ans = val;
return 1; /* succeed */
}
if(ZERO(val)) /* then this is a cancellation, not a combination */
{*ans = zero;
return -1;
}
if(ONE(ca) && ONE(sa))
*ans = val;
else if(ONE(ca))
mfracts(val,sa,ans);
else if(ONE(sa))
mfracts(val,ca,ans);
else
{ mfracts(ca,sa,&temp2); /* multiply ca and sa and flatten */
mfracts(val,temp2,ans);
}
RELEASE(temp); /* allocated above in this function */
HIGHLIGHT(*ans);
if(PROTECTED(a) || PROTECTED(b))
PROTECT(*ans);
return 1; /* success */
}
/* symbolic parts equal, constant parts not equal */
else if(! ONE(sa)) /* don't combine stand-alone constants, see above */
{ /* form (na*ca + nb*cb) * sa */
mfracts(na,ca,&temp2);
mfracts(nb,cb,&temp3);
at(temp2,temp3,&temp4);
mfracts(temp4,sa,ans);
HIGHLIGHT(*ans);
return 1;
}
return 0; /* e.g. standalone constants fall through to here */
}
/*____________________________________________________________________*/
#define MAXNOMEM 6 /* max number of terms to collect without memory management */
#define MAXCOLLECT 60 /* max number of terms to collect at all */
MEXPORT_POLYVAL int collect1(int initiali, term t, term *next, int *cancel)
/* collect one group of like terms, among the args of t numbered
initiali or greater. t must be a sum.
ARG(*index,*next) will be the arg that is newly created.
*cancel will be 1 if a cancellation of several terms occurred,
2 if a cancellation of only two terms occurred,
0 if only a combination.
return 0 for success, 1 for failure to find anything to do,
in which case *next and *cancel are garbage.
*/
{ unsigned short i,j,k;
unsigned short n = ARITY(t);
term a,b;
void *savenode;
unsigned long nbytes;
int *scratch; /* scratch[j] = 1 if j-th arg combines with i-th one */
int count; /* how many terms besides ARG(i,t) combine with ARG(i,t) */
*cancel=-1; /* Nothing has happened yet */
if(FUNCTOR(t) != '+')
return 1; /* inapplicable */
scratch = (int *) callocate(n,sizeof(int));
if(scratch==NULL)
nospace();
if(n > MAXCOLLECT)
return 1; /* too many terms, don't risk running out of memory */
if(n > MAXNOMEM)
{ nbytes = mycoreleft();
if(nbytes < 24576) /* leave 24K protected */
return 1; /* don't run out of memory. Better to leave terms
uncollected! */
savenode = heapmax();
}
for(i=initiali;i<n; i++) /* see what collects with ARG(i,t) */
{ a = ARG(i,t);
count = 0;
for(k=0; k<n; k++)
scratch[k] = 0;
for(j=i+1;j<n;j++)
{ if(combines(a,ARG(j,t),&b))
{ scratch[j] = 1;
if(ZERO(b) && *cancel == -1) /* this a isn't the result of a previous combination */
{ *cancel = 2; /* so this is a binary cancellation */
a=b;
++count;
break;
}
else if(ZERO(b))
{ *cancel = 1;
a=b;
++count;
break;
}
else
*cancel = 0; /* Now don't call it a binary cancellation
if something later cancels with b */
a=b;
++count;
}
}
if(count) /* found a combination */
{ HIGHLIGHT(a);
if(n==count+1) /* all the terms combined into one term */
{ *next = a;
free2(scratch);
if(ZERO(a))
*cancel = 1;
goto success;
}
assert(n-count >= 2);
if(!ZERO(a))
{ *next = make_term('+',(unsigned short)(n-count));
ARGREP(*next,i,a);
}
else if(n-count-1 == 1) /* only one term will be left */
{ if(i) /* the first term is the one left over */
*next = ARG(0,t);
else
{ for(j=1;j<n;j++)
{ if(scratch[j] == 0)
/* the j-th term did not combine
so it is the leftover term */
break;
}
*next = ARG(j,t);
}
free2(scratch);
HIGHLIGHT(*next);
if(*cancel != 2)
*cancel = 1;
goto success;
}
else /* don't put zero in explicitly */
*next = make_term('+',(unsigned short)(n-count-1));
for(k=0;k<i;k++)
ARGREP(*next,k,ARG(k,t));
j = i+1;
k = ZERO(a) ? i : i+1;
while(k < ARITY(*next))
{ if(! scratch[j])
{ ARGREP(*next,k,ARG(j,t));
++k;
}
++j;
}
if(ZERO(a) && *cancel != 2)
*cancel = 1;
free2(scratch);
goto success;
}
}
if(n > MAXNOMEM)
reset_heap(savenode);
return 1; /* if you get here there's no combination */
success:
if(n > MAXNOMEM)
save_and_reset(*next,savenode,next);
return 0;
}
/*_____________________________________________________________________*/
MEXPORT_POLYVAL void collect_aux2(term t, term *next, int *cancelflag, int *collectflag)
/* perform all possible collections and cancellations on t,
returning in cancelflag and collectflag how many of each were done;
like collect_aux, but it doesn't bother with reason.
/* Works on sums, or on both sides of an equation or inequality, or on
all args of an AND */
{ int err,cancel_occurred;
term temp,new;
unsigned short f = FUNCTOR(t);
unsigned short n = ARITY(t);
unsigned short i,j;
int pp,qq;
temp = t;
err=0;
*cancelflag=*collectflag=0;
if(f == AND || f == '=' || f == '<' || f == '>' || f == LE || f == GE)
{ *next = make_term(f,n);
for(i=0;i<n;i++)
{ collect_aux2(ARG(i,t),ARGPTR(*next)+i,&pp,&qq);
*cancelflag +=pp;
*collectflag +=qq;
}
if(collectflag ==0 && cancelflag==0)
{ RELEASE(*next);
*next = t;
}
return;
}
if(f != '+')
{ *next = t;
return;
}
j = 0;
while(err==0 && j < ARITY(temp)-1)
{ err = collect1(j,temp,&new,&cancel_occurred);
if(err == 0)
{ temp = new;
*cancelflag += cancel_occurred;
++*collectflag;
if(!cancel_occurred)
++j;
}
}
if( !*cancelflag && !*collectflag) /* nothing done */
*next = t;
else
{ *next = temp;
if(FUNCTOR(*next) == '+')
additive_sortargs(*next); /* put the args in order if something else is done */
}
return;
}
/*_____________________________________________________________________*/
#define ZZ(t) (ZERO(t) || (OBJECT(t) && TYPE(t) == DOUBLE && nearint(DOUBLEDATA(t),&kk) && kk==0))
MEXPORT_POLYVAL int dropzero(term t, term arg, term *next, char *reason)
/* x + 0 = x; drop all 0's or -0's from a sum */
/* Also does x - 0 = x. */
{ int i,j,m;
int k= -1; /* index of the first non-zero summand */
long kk;
int n = ARITY(t);
if(FUNCTOR(t) != '+')
return 1; /* inapplicable */
/* count how many terms must be dropped */
for(i=j=0; i<n;i++)
{ if(ZZ(ARG(i,t)))
++j;
else if(FUNCTOR(ARG(i,t)) == '-' && ZZ(ARG(0,ARG(i,t))))
++j;
else if(k < 0)
k=i; /* mark the first nonzero summand */
}
if(j==0)
return 1; /* no zeroes to be dropped */
if(j==n) /* every summand was � 0 */
{ *next = zero;
strcpy(reason, "0 � 0 = 0");
HIGHLIGHT(*next);
return 0;
}
if(j==n-1) /* all but one term was 0 */
{ *next = ARG(k,t);
if (k > 0)
strcpy(reason,"0+x = x");
else
strcpy(reason,"x+0 = x");
HIGHLIGHT(*next);
return 0;
}
/* Now, with at least 2 nonzero summands, we must make a new sum */
*next = make_term('+',(unsigned short)(n-j));
for(i=m=0;i<n;i++)
/* put the i-th old term, if nonzero, in position m in the new term */
{ if(! ZERO(ARG(i,t)) &&
! (FUNCTOR(ARG(i,t)) == '-' && ZZ(ARG(0,ARG(i,t))) )
)
{ ARGREP(*next,m,ARG(i,t));
++m;
}
}
if (k > 0)
strcpy(reason,"0+x = x");
else if(n == 2 && NEGATIVE(ARG(1,t)))
strcpy(reason,"x-0 = x");
else
strcpy(reason,"x+0 = x");
HIGHLIGHT(*next);
return 0;
}
/*____________________________________________________________________*/
MEXPORT_POLYVAL int collect(term t, term *next )
/* NOT an operator; performs all collections and cancellations in sum t */
/* Zero return means nothing collects or cancels */
/* return value 1 means no cancellation, only collection;
return value 2 means cancellation, no collection;
return value 3 means both occurred */
/* Will work simultaneously on both sides of an equation or inequality,
and on all equations (or inequalities) in an AND */
{ int cancelflag,collectflag;
int retval = 0;
char localbuf[80];
int err;
unsigned short n;
term temp;
long nbytes;
if(FUNCTOR(t) != '+')
{ *next = t;
return 0;
}
n = ARITY(t);
if(n > MAXNOMEM && !mvpoly2(t))
{ nbytes = mycoreleft();
if(nbytes < 24576)
{ /* less than 24K available, don't risk running out of memory */
*next = t;
return 0;
}
}
collect_aux2(t,&temp,&cancelflag,&collectflag);
if(collectflag)
retval |= 1;
if(cancelflag)
{ retval |= 2; /* and there can be zeroes in temp, which we now remove */
err = dropzero(temp,zero,next,localbuf);
if(err)
*next = temp;
}
else
*next = temp;
return retval;
}
/*_______________________________________________________________*/
static int calc_combines(unsigned short f, term a, term b, term *ans)
/* combine terms with the same INTEGRAL or same DIFF as a factor.
f is either INTEGRAL or DIFF. The terms are assumed not to have
the same symbolic part and both contain the functor f somewhere;
and they aren't atomic.
Return 1 if the terms combine (and the combined term in *ans);
return 0 for failure */
{ term c,newa,newb,trash;
int err;
naive_gcd(a,b,&c);
if(!contains(c,f))
return 0;
err = cancel(a,c,&trash,&newa);
if(err)
return 0;
err = cancel(b,c,&trash,&newb);
if(err)
return 0;
*ans = product(c,sum(newa,newb));
sortargs(*ans);
return 1;
}
/*____________________________________________________________________*/
MEXPORT_POLYVAL int additivecancel_aux(term *buffer1, unsigned short k, term *buffer2, unsigned short *m, term *cancelled)
/* because needed in simpsum.c */
/* perform an additive cancellation on the terms in buffer1 (of dimension k)
and put the remaining terms in buffer2, setting *m to the number of
terms placed in this buffer. On failure, *m must be set to k, but
buffer2 need not actually contain a copy of buffer1, it can be garbage.
Buffer2 is assumed to have dimension at least k at input.
/* return 0 if cancellation is possible, 1 if not */
{ int i,j,q;
term s; /* for the term to be cancelled */
int err;
for(i=0;i<k;i++)
{ if(FUNCTOR(buffer1[i])== '-' ) /* a candidate for cancellation */
{ s = ARG(0,buffer1[i]);
for (j=0;j<k; j++)
{ if(j != i && equals(buffer1[j],s))
/* s apparently cancels, but if it involves an
indefinite integral, we are allowed to
cancel it only by introducing a constant
of integration that depends on all the other
variables in the integral except the variable of
integration. (Unless another integral is in buffer1).
Also, the term may be undefined; you may have
to make an assumption.
*/
{ if( !contains2(s,INTEGRAL,2) ) /* the most usual case */
/* note, DEFINITE integrals are ok; we're checking
only for INDEFINITE integrals */
{ err = get_polyvaldomainflag() ? check(domain(s)) : 0;
if(err==0) /* inferred or assumed it */
{ actuallycancel(buffer1,k,i,j,buffer2);
*m = k-2;
*cancelled = s;
return 0;
}
}
else /* s did contain an indefinite INTEGRAL */
{ term temp;
err = cancel_integral(buffer1,k,i,j,&temp);
if(err)
{ *m = k;
return 1;
}
if(FUNCTOR(temp)=='+')
{ for(q=0;q<ARITY(temp);q++)
buffer2[q] = ARG(q,temp);
*m = ARITY(temp);
}
else
{ buffer2[0] = temp;
*m = 1;
}
*cancelled = s;
return 0;
}
} /* disposing of the apparent cancellation buffer1[i],buffer1[j] */
/* if we get here, it was an illegal cancellation */
} /* close the j-loop */
} /* close the 'if' on buffer[i] */
} /* close the i-loop */
/* if we get here, there was no cancellation */
*m = k;
return 1;
}
/*___________________________________________________________________*/
static int cancel_integral(term *buffer,int k, int i,int j,term *ans)
/* buffer is an array of terms of dimension k (originally the args of a sum).
buffer[i] and buffer[j] are two terms which apparently cancel,
i.e. are negations of each other, but they contain an indefinite
integral, so in cancelling them, we may have to introduce a constant of
integration (if there are no other integrals in buffer1);
and the cancellation can only be performed if
(a) the term to be cancelled is the integral itself
(b) the term to be cancelled is a product, one of whose factors is the
integral.
*ans is the result of performing the cancellation.
Return 0 for success; return 1 if the integral is nested too deeply;
you can only cancel an integral or a product one of whose factors is
an integral; you can't e.g. cancel a product of sums one of whose
summands is an integral. I don't think such terms can ever arise anyway.
*/
{ term s,temp,newterm,q;
int p,cneeded,flag;
int l,ll,max,min;
min = ((i<j) ? i: j);
max = ((i<j) ? j: i);
if(FUNCTOR(buffer[i])== '-')
s = ARG(0,buffer[i]);
else
{ s = ARG(0,buffer[j]);
assert(FUNCTOR(buffer[j])== '-');
}
if(k==2 && FUNCTOR(s)==INTEGRAL) /* two cancelling integrals */
{ *ans = constant_of_integration(ARG(0,s),ARG(1,s));
return 0;
}
/* First determine whether or not a constant of integration will
be required. */
for(l=0;l<k;l++)
{ if(l==i || l==j)
continue;
temp = buffer[l];
if(contains2(temp,INTEGRAL,2))
break;
}
if(l==k)
cneeded = 1; /* constant of integration needed */
else
cneeded = 0;
if(!cneeded)
{ if(k==3)
{ if(i==0)
l = j==1 ? 2 : 1;
else if (i==1)
l = j==0 ? 2 : 0;
else if (i==2)
l = j==0 ? 1 : 0;
*ans = buffer[l];
return 0;
}
assert(k>=4);
*ans = make_term('+',(unsigned short)(k-2));
ll=0;
for(l=0;l<k;l++)
{ if(l==i || l==j)
continue;
ARGREP(*ans,ll,buffer[l]);
++ll;
}
return 0;
}
while(
(FRACTION(s) && constant(ARG(1,s))) ||
NEGATIVE(s)
)
s = ARG(0,s);
/* Now we need a constant of integration */
if(FUNCTOR(s)==INTEGRAL)
/* integral(u,x) - integral(u,x) = c */
{ *ans = make_term('+',(unsigned short)(k-1)); /* arity is at least two since k==2 is handled above */
q = constant_of_integration(ARG(0,s),ARG(1,s));
ARGREP(*ans,min,q);
for(p=0;p<min;p++)
ARGREP(*ans,p,buffer[p]);
for(p=min+1;p<max;p++)
ARGREP(*ans,p,buffer[p]);
for(p=max; p<k-1;p++)
ARGREP(*ans,p,buffer[p+1]);
return 0;
}
if(FUNCTOR(s) != '*')
assert(0);
/* indefinite integrals can only be in sums and products, or possibly
numerators of fractions with constant denoms, but we took care of
that above. */
/* y integral(u,x) - y integral(u,x) = yc */
newterm = make_term('*',ARITY(s));
flag=0;
for(p=0;p<ARITY(s);p++)
{ temp = ARG(p,s);
if(FUNCTOR(temp)==INTEGRAL && flag==0)
{ q = constant_of_integration(ARG(0,temp),ARG(1,temp));
ARGREP(newterm,p,q);
flag = 1;
}
else
ARGREP(newterm,p,ARG(p,s));
}
if(flag == 0)
return 1; /* failure, integrals nested too deeply */
if(k==2) /* cancelling products of integrals */
{ *ans = newterm;
return 0;
}
*ans = make_term('+',(unsigned short)(k-1));
ARGREP(*ans,min,newterm);
for(p=0;p<min;p++)
ARGREP(*ans,p,buffer[p]);
for(p=min+1;p<max;p++)
ARGREP(*ans,p,buffer[p]);
for(p=max; p<k-1;p++)
ARGREP(*ans,p,buffer[p+1]);
return 0;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists