Arithmetic/Rational/C

From Rosetta Code
< Arithmetic‎ | Rational(Redirected from Rational Arithmetic/C)
Arithmetic/Rational/C is part of Rational Arithmetic. You may find other members of Rational Arithmetic at Category:Rational Arithmetic.

C doesn't support classes, so a series of functions is provided that implements the arithmetic of a rational class.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>    // for fabs in converting float to rational

// a structure to contain the numerator and denominator values
typedef struct sRational {
    int numerator, denominator;
} *Rational, sRational;

int gcd( int a, int b)
{
    int g;
    if (a<b)  {
        g = a; a = b; b = g;
    }
    g = a % b;
    while (g) {
        a = b; b = g;
        g = a % b;
    }
    return abs(b);
}

int lcm( int a, int b)
{
    return (a/gcd(a,b)*b);
}

Rational NewRational( int n, int d)
{
    Rational r = (Rational)malloc(sizeof(sRational));
    int ndgcd;
    if (n!= 0)
        ndgcd = gcd(n,d);
    else {
        ndgcd = 1; d = 1;
    }
    if (r) {
        if (n>0) {
            r->numerator = n/ndgcd;
            r->denominator = d/ndgcd;
        }
        else {
            r->numerator = -n/ndgcd;
            r->denominator = -d/ndgcd;
        }
    }
    if ( d == 0) {
        printf("divide by zer0 error\n");
        exit(1);
    }
    return r;
}
#define Ratl_Delete(r) \
    {  if(r) free(r); \
    r = NULL; }

Rational Ratl_Add( Rational l, Rational r, Rational rzlt)
{
    int  denom;
    denom= lcm(l->denominator, r->denominator);
    rzlt->numerator = denom/l->denominator *l->numerator 
                   + denom/r->denominator *r->numerator ;
    rzlt->denominator = denom;
    if (rzlt->numerator == 0) {
        rzlt->denominator = 1;
    }
    else {
        int d = gcd(rzlt->numerator, rzlt->denominator);
        rzlt->numerator /= d;
        rzlt->denominator /= d;
    }
    return rzlt;
}

Rational Ratl_Negate( Rational l, Rational rzlt)
{
    rzlt->numerator = - l->numerator;
    rzlt->denominator = l->denominator;
    return rzlt;
}

Rational Ratl_Subtract(Rational l, Rational r, Rational rzlt)
{
    return Ratl_Add(l,Ratl_Negate(r, rzlt), rzlt);
}

Rational Ratl_Multiply(Rational l, Rational r, Rational rzlt)
{
    int g1 = gcd(l->numerator, r->denominator);
    int g2 = gcd(r->numerator, l->denominator);
    rzlt->numerator = l->numerator / g1 * r->numerator / g2;
    rzlt->denominator = l->denominator / g2 * r->denominator / g1;
    return rzlt;
}

Rational Ratl_Inverse(Rational l, Rational rzlt)
{
    if (l->numerator == 0) {
        printf("divide by zer0 error\n");
        exit(1);
    }
    else if (l->numerator > 0) {
        rzlt->numerator = l->denominator;
        rzlt->denominator = l->numerator;
    }
    else {
        rzlt->numerator = -l->denominator;
        rzlt->denominator = -l->numerator;
    }
    return rzlt;
}

Rational Ratl_Divide(Rational l, Rational r, Rational rzlt)
{
    return Ratl_Multiply(l, Ratl_Inverse(r, rzlt), rzlt);
}

int ipow(int base, int power )
{
    int v = base, v2 = 1;

    if (power < 0) return 0; // shouldn't happen
    while (power > 0) {
        if (power & 1) 
            v2 *=v;
        v = v*v;
        power >>= 1;
    }
    return v2;
}

Rational Ratl_Pow( Rational l, int power, Rational rzlt)
{
    if (power >= 0) {
        rzlt->numerator = ipow(l->numerator, power);
        rzlt->denominator = ipow(l->denominator, power);
    }
    else {
        rzlt->numerator = ipow(l->denominator, -power);
        rzlt->denominator = ipow(l->numerator, -power);
    }
    return rzlt;
}

int Ratl_Compare(Rational l, Rational r)
{
    int sign = (l->numerator > 0)? 1 : -1;
    sRational  comp;
    Rational pcomp;

    if ( 0 >= l->numerator * r->numerator)        // if opposite signs or one is zero
        return (l->numerator - r->numerator);
    pcomp = Ratl_Divide(l, r, &comp);

    return (pcomp->numerator - pcomp->denominator)*sign;
}

typedef enum   {
    LT, LE, EQ, GE, GT, NE } CompOp;
// boolean comparisons
int Ratl_Cpr( Rational l, CompOp compOp, Rational r)
{
    int v;
    int r1 = Ratl_Compare(l, r);
    switch(compOp) {
        case LT: v = (r1 <0)? 1 : 0; break;
        case LE: v = (r1<=0)? 1 : 0; break;
        case GT: v = (r1 >0)? 1 : 0; break;
        case GE: v = (r1>=0)? 1 : 0; break;
        case EQ: v = (r1==0)? 1 : 0; break;
        case NE: v = (r1!=0)? 1 : 0; break;
    }
    return v;
}

double Ratl_2Real(Rational l) 
{
    return l->numerator *1.0/l->denominator;
}

int Ratl_2Int(Rational l)
{
    return l->numerator /l->denominator;
}

int Ratl_2Proper(Rational l)
{
    int ipart = l->numerator/l->denominator;
    l->numerator %= l->denominator;
    return ipart;
}

char *Ratl_2String(Rational l, char *buf, int blen)
{
    char ibuf[40];
    sprintf(ibuf,"%d/%d", l->numerator, l->denominator );
    if (buf==NULL) return buf;
    if ((int)strlen(ibuf) < blen)
        strcpy(buf, ibuf);
    else  {
        strncpy(buf, ibuf, blen-4);
        strcat(buf, "...");
    }
    return buf;
}

Rational Int_2Ratl(int i, Rational rtnl)
{
    rtnl->numerator = i;
    rtnl->denominator = 1;
    return rtnl;
}

Rational Real_2Ratl(double r, double eps, Rational rtnl)
{
    int denom;
    double v1,v2;
    int isNeg = (r<0);
    if ( isNeg) r = -r;

    denom=0; 
    do {
        denom++;
        v1 = (r+eps)*denom;
        v2 = r*denom;
        v2 = fabs(v2 - (int) v1);
    } while ( v2 > denom*eps);
    rtnl->numerator = (int)v1 * ((isNeg)? -1 : 1);
    rtnl->denominator = denom;
    return rtnl;
}

Rational Ratl_Abs(Rational l, Rational rzlt)
{
    rzlt->numerator = abs(l->numerator);
    rzlt->denominator = abs(l->denominator);   // should already be nonnegative
    return rzlt;
}

Testing

void find_perfects()
{
    int n, n2, k;
    int end = 1<<19;
    sRational r1, r2, r3;
    Rational f1, f2;

    Rational sum = NewRational( 0, 1 );
    for (n=2; n< end; n++) {
        sum = Ratl_Inverse(Int_2Ratl(n, &r1), sum);
        n2 = (int)sqrt(n)+1;
        for (k=2; k<n2; k++) {
            if ( n % k == 0) {
                f1 = Ratl_Inverse(Int_2Ratl(k,&r1), &r2);
                f2 = Ratl_Inverse(Int_2Ratl(n/k,&r1),&r3);
                Ratl_Add(sum, f1,sum);
                Ratl_Add(sum, f2,sum);
            }
        }
        if (sum->denominator == 1) {
            printf("Perfect number %d sum is %d\n", n, Ratl_2Int(sum));
        }
    }
    Ratl_Delete(sum);
}


int main(int argc, char *argv[])
{
    char ratstr[32], rs1[32],rs2[32];
    double pi = 3.14159265;
    sRational rtemp1, rtemp2;
    Rational rz;
    Rational r1 = NewRational( 5,7 );
    Rational r2 = NewRational( 4,5 );
    Rational r3 = NewRational( 3,4);

    printf("r3 = %s\n", Ratl_2String(r3, ratstr,32));

    rz = Ratl_Multiply( r1,r2, &rtemp1);
    printf("%s = %s * %s\n", Ratl_2String(rz, ratstr,32),
                     Ratl_2String(r1, rs1,32), Ratl_2String(r2, rs2, 32));

    rz = Ratl_Divide( r1,r3, &rtemp2);
    printf("%s = %s / %s\n", Ratl_2String(rz, ratstr,32),
                     Ratl_2String(r1, rs1,32), Ratl_2String(r3, rs2, 32));

    rz = Ratl_Add( r2,r3, &rtemp1);
    printf("%s = %s + %s\n", Ratl_2String(rz, ratstr,32),
                     Ratl_2String(r2, rs1,32), Ratl_2String(r3, rs2, 32));

    rz = Ratl_Subtract( r2,r3, &rtemp1);
    printf("%s = %s - %s\n", Ratl_2String(rz, ratstr,32),
                     Ratl_2String(r2, rs1,32), Ratl_2String(r3, rs2, 32));

    printf("%d = %s > %s\n", Ratl_Cpr( r2, GT, &rtemp2),
                     Ratl_2String(r2, rs1,32), Ratl_2String(&rtemp2, rs2, 32));

    rz = Ratl_Pow( r2,-3, &rtemp1);
    printf("%s = %s ^ -3\n", Ratl_2String(rz, ratstr,32),
                     Ratl_2String(r2, rs1,32));

    printf("%s = %f\n", Ratl_2String( &rtemp2, ratstr, 32), Ratl_2Real( &rtemp2));

    rz =Real_2Ratl( pi, 0.000001, &rtemp2);
    printf("%10.7f ~= %s ~=%10.7f\n", pi, Ratl_2String(rz, ratstr, 32),
                                   Ratl_2Real(rz));
    find_perfects();
    return 0;
}