cirilisp/util.c

248 lines
4.7 KiB
C
Raw Normal View History

#include <stdlib.h>
#include <string.h>
#include "util.h"
int properList(object list)
{
object *current = &list;
while (TYPE(*current) == consObject)
{
current = &CDR(*current);
}
return TYPE(*current) == nilObject;
}
int listLength(object list)
{
object *current = &list;
int i = 0;
while (TYPE(*current) != nilObject)
{
current = &CDR(*current);
++i;
}
return i;
}
void deleteObject(object input)
{
if ((TYPE(input) == symbolObject) && SYM(input) != NULL)
{
free(SYM(input));
SYM(input) = NULL;
}
else if (TYPE(input) == consObject)
{
deleteObject(CAR(input));
deleteObject(CDR(input));
free(CONS(input));
CONS(input) = NULL;
}
TYPE(input) = nilObject;
}
object copyObject(object input)
{
object result;
TYPE(result) = TYPE(input);
if (TYPE(input) == errorObject)
{
ERR(result) = ERR(input);
}
else if (TYPE(input) == numberObject)
{
NUM(result) = NUM(input);
}
else if (TYPE(input) == symbolObject)
{
SYM(result) =
malloc(sizeof(char) * (strlen(SYM(input)) + 1));
strcpy(SYM(result), SYM(input));
}
else if (TYPE(input) == consObject)
{
CONS(result) = malloc(sizeof(cons));
CAR(result) = copyObject(CAR(input));
CDR(result) = copyObject(CDR(input));
}
return result;
}
object longlongToNumber(long long int input)
{
object result;
TYPE(result) = numberObject;
NUM_TYPE(result) = fractionNum;
NUM_NUMER(result) = input;
NUM_DENOM(result) = 1LL;
return result;
}
object exactToInexactNum(object a)
{
object result = copyObject(a);
if (TYPE(result) == numberObject && NUM_TYPE(result) == fractionNum)
{
NUM_TYPE(result) = realNum;
NUM_REAL(result) = (long double) NUM_NUMER(result) /
(long double) NUM_DENOM(result);
}
return result;
}
long long int gcd(long long int a, long long int b)
/* највећи заједнички делилац */
{
if (b == 0LL)
{
return a;
}
else
{
return gcd(b, a - b * (a / b));
}
}
long long int lcm(long long int a, long long int b)
/* најмањи заједнички садржалац */
{
if (a == 0LL && b == 0LL)
{
return 0L;
}
else
{
return llabs(a * b) / gcd(a, b);
}
}
object shortenFractionNum(object a)
/* скраћује разломак, враћа грешку ако је неправилан разломак, уколико улаз
* заиста јесте дат као разломак */
{
object result = copyObject(a);
if (TYPE(result) == numberObject && NUM_TYPE(result) == fractionNum)
{
if (NUM_DENOM(result) == 0)
{
deleteObject(result);
TYPE(result) = errorObject;
ERR(result) = divisionByZeroError;
}
else if (NUM_NUMER(result) == 0)
{
NUM_DENOM(result) = 1;
}
else
{
long long int divisor =
gcd(NUM_NUMER(result), NUM_DENOM(result));
NUM_NUMER(result) /= divisor;
NUM_DENOM(result) /= divisor;
}
}
return result;
}
object plusNum(object a, object b)
{
object result;
TYPE(result) = numberObject;
if (NUM_TYPE(a) == fractionNum && NUM_TYPE(b) == fractionNum)
{
NUM_TYPE(result) = fractionNum;
NUM_NUMER(result) = NUM_NUMER(a) * NUM_DENOM(b) + NUM_NUMER(b) * NUM_DENOM(a);
NUM_DENOM(result) = NUM_DENOM(a) * NUM_DENOM(b);
/*
* TODO: имплементирати оптималнији начин множења разломака
long long int denominator = lcm(NUM_DENOM(a), NUM_DENOM(b));
NUM_NUMER(result) =
NUM_NUMER(a) * (denominator / NUM_DENOM(a)) +
NUM_NUMER(b) * (denominator / NUM_DENOM(b));
*/
result = shortenFractionNum(result);
}
else
{
NUM_TYPE(result) = realNum;
NUM_REAL(result) = NUM_REAL(exactToInexactNum(a))
+ NUM_REAL(exactToInexactNum(b));
}
return result;
}
object minusNum(object a)
{
object result;
result = copyObject(a);
if (NUM_TYPE(result) == fractionNum)
{
NUM_NUMER(result) = -NUM_NUMER(result);
}
else if (NUM_TYPE(result) == realNum)
{
NUM_REAL(result) = -NUM_REAL(result);
}
return result;
}
object timesNum(object a, object b)
{
object result;
TYPE(result) = numberObject;
if (NUM_TYPE(a) == fractionNum && NUM_TYPE(b) == fractionNum)
{
NUM_TYPE(result) = fractionNum;
NUM_NUMER(result) = NUM_NUMER(a) * NUM_NUMER(b);
NUM_DENOM(result) = NUM_DENOM(a) * NUM_DENOM(b);
result = shortenFractionNum(result);
}
else
{
NUM_TYPE(result) = realNum;
NUM_REAL(result) = NUM_REAL(exactToInexactNum(a)) *
NUM_REAL(exactToInexactNum(b));
}
return result;
}
object inverseNum(object a)
{
object result;
result = copyObject(a);
if (NUM_TYPE(result) == fractionNum)
{
if (NUM_NUMER(result) == 0)
{
deleteObject(result);
TYPE(result) = errorObject;
ERR(result) = divisionByZeroError;
}
else
{
NUM_NUMER(result) = NUM_DENOM(a);
NUM_DENOM(result) = NUM_NUMER(a);
}
}
else if (NUM_TYPE(result) == realNum)
{
NUM_REAL(result) = 1.0L/NUM_REAL(result);
}
return result;
}