Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- enum Oper {
- OPER_NONE,
- OPER_POW, // prec = 0
- OPER_IMP = 3, // prec = 1
- OPER_DIV, // prec = 2
- OPER_MUL, // prec = 2
- OPER_SUB, // prec = 3
- OPER_ADD, // prec = 3
- OPER_DONE, // prec = 4
- OPER_LPAREN
- };
- typedef uint8_t oper_t;
- #define operPrec(op) ((op) >> 1)
- struct Poly {
- int degree; // This is the maximum power.
- float term[1]; // This contains the coefficient of degree + 1 terms (the [1] is for portability).
- };
- struct Poly *allocPoly(int degree) {
- struct Poly *result = calloc(2 + degree, sizeof(int));
- if (result)
- result->degree = degree;
- return result;
- }
- struct Poly *addPoly(struct Poly *left, struct Poly *right) {
- struct Poly *result = left, *other = right;
- if (left == NULL || right == NULL) {
- if (right != left)
- free(right);
- free(left);
- return NULL;
- }
- if (left->degree < right->degree) {
- result = right;
- other = left;
- }
- for (int i = 0; i <= other->degree; ++i)
- result->term[i] = left->term[i] + right->term[i];
- if (other != result)
- free(other);
- return result;
- }
- struct Poly *subPoly(struct Poly *left, struct Poly *right) {
- struct Poly *result = left, *other = right;
- if (left == NULL || right == NULL) {
- if (right != left)
- free(right);
- free(left);
- return NULL;
- }
- if (left->degree < other->degree) {
- result = right;
- other = left;
- }
- for (int i = 0; i <= left->degree; ++i)
- result->term[i] = left->term[i] - right->term[i];
- if (other != result)
- free(other);
- return result;
- }
- struct Poly *mulPoly(struct Poly *left, struct Poly *right) {
- int newDegree;
- struct Poly *result;
- if (left == NULL || right == NULL || __builtin_add_overflow(left->degree, right->degree, &newDegree) || !(result = allocPoly(newDegree))) {
- if (right != left)
- free(right);
- free(left);
- return NULL;
- }
- for (int i = 0; i <= left->degree; i++)
- for (int j = 0; j <= right->degree; j++)
- result->term[i + j] += left->term[i] * right->term[j];
- if (right != left)
- free(right);
- free(left);
- return result;
- }
- struct Poly *divPoly(struct Poly *left, struct Poly *right) {
- struct Poly *result, *remainder = left, *temp;
- int newDegree;
- if (left == NULL || right == NULL || right->degree < left->degree || __builtin_sub_overflow(left->degree, right->degree, &newDegree) || result = allocPoly(newDegree + 1)){
- if (right != left)
- free(right);
- free(left);
- return NULL;
- }
- int factor, ctr = left->degree - right->degree + 1;
- result->degree = ctr;
- while(remainder->degree > right->degree){
- temp = right;
- /* divide leading term divisor into leading term temp */
- factor = remainder->term[remainder->degree] / right->term[right->degree];
- /* Factor becomes term of result */
- result->term[ctr--] = factor;
- /* multiply factor by temp */
- for ( int i = 0; i <= temp->degree; ++i ){
- temp->term[i] = temp->term[i] * factor;
- }
- /* subtract temp from remainder */
- for ( int i = remainder->degree; i >= temp->degree; --i ){
- remainder->term[i] -= temp->term[i];
- }
- remainder->degree--;
- /* somehow we will need to adjust remainder to the correct degree to make this work */
- }
- if ( right != left )
- free( right );
- free( left );
- return result;
- }
- struct Poly *powPoly(struct Poly *left, struct Poly *right) {
- struct Poly *result = NULL;
- if (!(left == NULL || right == NULL || right->degree || right->term[0] < 0 || left->degree != 1 || left->term[0] || left->term[1] != 1 || !(result = allocPoly(right->term[0]))))
- result->term[right->term[0]] = 1;
- if (right != left)
- free(right);
- free(left);
- return result;
- }
- struct Poly *parsePoly(const char *polyIn) { // Takes an unmodifiable string, returns the poly, or NULL on error.
- static struct Poly *polyStack[operPrec(OPER_DONE) + 1];
- static oper_t operStack[operPrec(OPER_DONE) + 1];
- struct Poly **polyTop = polyStack;
- oper_t *operTop = operStack;
- struct Poly *poly = NULL;
- oper_t oper = OPER_DONE; // sentinal oper
- float dec = 1;
- float fpart = 0;
- while (true) { // loop through the string
- int sign = 1;
- *operTop = oper; // Push the current oper.
- oper = OPER_NONE; // No operator yet
- // First parse a term
- while (*polyIn == ' ' || *polyIn == '+' || *polyIn == '-') // parse sign and skip spaces
- if (*polyIn++ == '-') // check for '-' case (the ' ' and '+' cases are ignored) then skip over it.
- sign = -sign; // negate if there was a '-'
- if (isdigit(*polyIn) or *polyIn == '.') { // parse number
- *polyTop++ = poly; // Push the current poly
- poly = allocPoly(0); // Create a new poly big enough to contain a constant.
- do {
- if (__builtin_mul_overflow(poly->term[0], 10, &poly->term[0])) {
- free(poly);
- poly = NULL;
- } else {
- poly->term[0] += *polyIn++ - '0'; // parse and skip the number
- }
- } while (poly && isdigit(*polyIn));
- if ( *polyIn == '.'){
- polyIn++;
- do {
- fpart += (*polyIn++ - '0') * (dec *= .1);
- if(__builtin_mul_overflow(dec, 10, &dec)){
- free(poly);
- poly = NULL;
- }
- } while (poly && isdigit(*polyIn));
- }
- poly->term[0] += fpart;
- if (poly && sign < 0)
- poly->term[0] = -poly->term[0];
- } else if (*polyIn == 'x') { // parse a variable
- polyIn++; // skip over the x
- *polyTop++ = poly; // Push the current poly
- poly = allocPoly(1); // Create a new poly big enough to contain a constant.
- if (poly)
- poly->term[1] = sign;
- } else { // We are at the end, or the next character wasn't recognized
- if (*operTop != OPER_DONE)
- operTop--; // Ignore the top operator which is the invalid trailing operator, or OPER_IMP if there was no trailing operator
- oper = OPER_DONE; // This forces the OPER_DONE at the bottom of the operStack to eval, which exits, and prevents another operator from being parsed
- }
- // Then parse an operator
- if (oper == OPER_NONE) { // Don't parse an operator if we are done.
- while (*polyIn == ' ') polyIn++;
- switch (*polyIn) { // Look for an operator
- case '+':
- oper = OPER_ADD;
- polyIn++;
- break;
- case '-':
- oper = OPER_SUB;
- polyIn++;
- break;
- case '*':
- oper = OPER_MUL;
- polyIn++;
- break;
- case '/':
- oper = OPER_DIV;
- polyIn++;
- break;
- case '^':
- oper = OPER_POW;
- polyIn++;
- break;
- case '(':
- oper = OPER_LPAREN;
- polyIn++;
- break;
- case ')':
- oper = OPER_RPAREN;
- polyIn++;
- break;
- default: // Unknown operator, probably implicit multiplication
- oper = OPER_IMP;
- break;
- }
- }
- // Now that we know the next operator, evaluate previous operators
- while (operPrec(*operTop) <= operPrec(oper) || (oper == OPER_RPAREN && *operTop != OPER_LPAREN )) {
- struct Poly *left = *--polyTop, *right = poly; // pop the left operand
- switch (*operTop--) { // pop the oper
- case OPER_LPAREN:
- break;
- case OPER_ADD:
- poly = addPoly(left, right);
- break;
- case OPER_SUB:
- poly = subPoly(left, right);
- break;
- case OPER_IMP:
- case OPER_MUL:
- poly = mulPoly(left, right);
- break;
- case OPER_DIV:
- poly = divPoly(left, right);
- break;
- case OPER_POW:
- poly = powPoly(left, right);
- break;
- case OPER_DONE:
- if (*polyIn) { // We didn't get through the whole string, so error
- free(poly);
- return NULL;
- }
- return poly; // Otherwise, return the result
- }
- // All of the *Poly functions should free their arguments
- }
- ++operTop; // We are about to push oper (see the top of the loop)
- }
- }
- void printPoly(struct Poly *poly) {
- if (!poly)
- return;
- bool first = true;
- for (int i = poly->degree; i >= 0; --i) {
- int term = poly->term[i];
- if (term || (first && !i)) {
- char sign = '+';
- if (term < 0) {
- term = -term;
- sign = '-';
- }
- if (!first)
- putchar(' ');
- if (!first || sign == '-')
- putchar(sign);
- if (!first)
- putchar(' ');
- if (term != 1 || !i)
- printf("%u", term);
- if (i) {
- putchar('x');
- if (i > 1) {
- putchar('^');
- printf("%u", i);
- }
- }
- first = false;
- }
- }
- putchar('\n');
- }
- int opp(int argc, char **argv) {
- if (argc < 2) errx(2, "not enough args");
- struct Poly *poly = parsePoly(argv[1]);
- if (!poly) errx(1, "parse error");
- printPoly(poly);
- free(poly);
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement