aboutsummaryrefslogblamecommitdiff
path: root/types.cpp
blob: b5830610467837e514022909011264058d931759 (plain) (tree)
1
2
3
4
5



                   
               








                         

                             
 

                                                           
 








                   
                           

                    

 


                                                              

 


                                        
 















                                                         

                                                         


                                                                 
 
                                                                     
                                                                      
                    



                      





                      
                                                      
                                                              

                                                    

                              
     
                                               

                               
                                
                                                             
                                                                        



                                  
                                                                                                
             


                                                          




                                                               
                          
             
                             
                        



         



                                      








                                                 
                                                                                    


                               
                                                                                       


                                                               

                                                       
                           
                         
                                                                            
     
 
 











                                                                 



                                       
                                                                     


                                                        


















































                                                                                  
                                            
                                           






                                     
 






                                               


                    


                                                      
                        
                   
                   

 



                                   
                                           
                        


                            


                                               

                       
                  

                       

 



                                        











                                                                


























                                                   

                                                             
 
                                                                
                                                                  
 

                                  
                                                                  
                                             






                                                                       

                                                    



                             
                                                    

                                      





                                         
 













                                                                     
                                                                             






                                                                
                        
             



                                                    





                             
        
     











                                           

                    















                                                            




                                                                                      
                                                                                                    

                             

     




                               









                                                                      






                                                                         
                                               


                               






































                                                             
                                                    









                                   
                                           





                                   
                                
                                          
                                                 
                         

                           
 




                     









                                                                 





                                                                        
                                      

                         






























































                                                                    
                          






















                                                             
                                        




                                                                  
                                        











                                                                     
                                        




                                                                  
                                        








                                                                    



                                   



                                              

                                                                  



























                                                                                
                                  
                                                 

              

 
                                  
                                                 

              

 
                                  
                                                 



                              

 
                                  




                                                    



                                    

















                                                                
                    




















                                                                                  



                                   










                                                  
                                                                  
                  




















                                                                             

                                               

 

                                               

 

                                               

 

                                               

 

                        






















































                                                               
                                                                            
                           




                                  














                                                        
 
                                            
                                                         






                                            

                                                                
















                                                                       
                                 

                                               

                      
                        

           
     
                  


      
                                 

                                               

                      
                        

           
     
                  


      
                                 

                                               

              
                        

           
     
                  


      
                                 

                                               

              
                        

           
     


                                                    
















































                                               
                       
                   
                      
     
                     


      





                                                           










                                                                       
 
























                                                                             
                                            
                                                         

      




                                  



                                            
                                                                




                                       

                                            

 

                                            

 

                                            

 

                       

 
                                 

                                                               
                

 
                                 
                                                             
                                                               


                                          

 
                                 

                                                               
                

 
                                 


                                                          

 
                                 


                                                          






















                                                   



                                






                                        
#include "types.h"
#include "model.h"
#include "exc.h"
#include "consts.h"
#include "gc.h"

#include <cmath>
#include <cstdlib>
#include <sstream>
#include <iomanip>

const double EPS = 1e-16;
const int PREC = 16;

extern EmptyList *empty_list;
extern UnspecObj *unspec_obj;

Pair::Pair(EvalObj *_car, EvalObj *_cdr) : 
Container(CLS_PAIR_OBJ), car(_car), cdr(_cdr), next(NULL) {

    gc.attach(car);
    gc.attach(cdr);
}

Pair::~Pair() {
    gc.expose(car);
    gc.expose(cdr);
}

void Pair::gc_decrement() {
    GC_CYC_DEC(car);
    GC_CYC_DEC(cdr);
}

void Pair::gc_trigger(EvalObj ** &tail, EvalObjSet &visited) {
    GC_CYC_TRIGGER(car);
    GC_CYC_TRIGGER(cdr);
}

ReprCons *Pair::get_repr_cons() {
    return new PairReprCons(this, this);
}

ParseBracket::ParseBracket(unsigned char _btype) :
    FrameObj(CLS_SIM_OBJ | CLS_PAR_BRA), btype(_btype) {}

    UnspecObj::UnspecObj() : EvalObj(CLS_SIM_OBJ) {}

    ReprCons *UnspecObj::get_repr_cons() {
        return new ReprStr("#<Unspecified>");
    }

SymObj::SymObj(const string &str) :
    EvalObj(CLS_SIM_OBJ | CLS_SYM_OBJ), val(str) {}

    ReprCons *SymObj::get_repr_cons() {
        return new ReprStr(val);
    }

OptObj::OptObj(int otype) : 
    Container(otype | CLS_SIM_OBJ | CLS_OPT_OBJ, true) {}

void OptObj::gc_decrement() {}
void OptObj::gc_trigger(EvalObj ** &tail, EvalObjSet &visited) {}

ProcObj::ProcObj(Pair *_body, Environment *_envt, EvalObj *_params) :
    OptObj(CLS_CONTAINER), body(_body), params(_params), envt(_envt) {
    gc.attach(body);
    gc.attach(params);
    gc.attach(envt);
}

ProcObj::~ProcObj() {
    gc.expose(body);
    gc.expose(params);
    gc.expose(envt);
}

Pair *ProcObj::call(Pair *_args, Environment * &lenvt,
        Continuation * &cont, EvalObj ** &top_ptr, Pair *pc) {
    // Create a new continuation
    // static_cast see `call` invocation in eval.cpp
    Pair *ret_addr = cont->pc;
    if (cont->state)
    {
        Pair *nexp = TO_PAIR(cont->state->cdr);
        if (nexp == empty_list)
        {
            gc.expose(*top_ptr);
            *top_ptr++ = gc.attach(TO_PAIR(_args->cdr)->car);
            EXIT_CURRENT_EXEC(lenvt, cont, _args); // exit cont and envt
            return ret_addr->next;
        }
        else 
        {
            if (nexp->cdr == empty_list && !nexp->car->is_simple_obj())    // tail recursion opt
            {
                cont->tail = true;
                cont->state = NULL;
                top_ptr++;              // revert the cont
            }
            else
            {
                gc.attach(static_cast<EvalObj*>(*(++top_ptr)));
                cont->state = nexp;
                top_ptr++;
            }
            gc.expose(_args);
            return nexp;
        }
    }
    else
    {
        gc.expose(lenvt);
        lenvt = new Environment(envt);
        gc.attach(lenvt);

        EvalObj *ppar, *nptr;
        Pair *args = _args;
        for (ppar = params;
                ppar->is_pair_obj();
                ppar = TO_PAIR(ppar)->cdr)
        {
            if ((nptr = args->cdr) != empty_list)
                args = TO_PAIR(nptr);
            else break;
            lenvt->add_binding(static_cast<SymObj*>(TO_PAIR(ppar)->car), args->car);
        }

        if (ppar->is_sym_obj())
            lenvt->add_binding(static_cast<SymObj*>(ppar), args->cdr); // (... . var_n)
        else if (args->cdr != empty_list || ppar != empty_list)
            throw TokenError("", RUN_ERR_WRONG_NUM_OF_ARGS);

        gc.attach(static_cast<EvalObj*>(*(++top_ptr)));
        top_ptr++;
        cont->state = body;
        gc.expose(_args);
        return cont->state;               // Move pc to the proc entry point
    }
}

void ProcObj::gc_decrement() {
    GC_CYC_DEC(body);
    GC_CYC_DEC(params);
    GC_CYC_DEC(envt);
}

void ProcObj::gc_trigger(EvalObj ** &tail, EvalObjSet &visited) {
    GC_CYC_TRIGGER(body);
    GC_CYC_TRIGGER(params);
    GC_CYC_TRIGGER(envt);
}

ReprCons *ProcObj::get_repr_cons() {
    return new ReprStr("#<Procedure>");
}

SpecialOptObj::SpecialOptObj(string _name) : OptObj(), name(_name) {}
ReprCons *SpecialOptObj::get_repr_cons() {
    return new ReprStr("#<Built-in Opt: " + name + ">");
}

BoolObj::BoolObj(bool _val) : EvalObj(CLS_SIM_OBJ | CLS_BOOL_OBJ), val(_val) {}

bool BoolObj::is_true() { return val; }

ReprCons *BoolObj::get_repr_cons() {
    return new ReprStr(val ? "#t" : "#f");
}

BoolObj *BoolObj::from_string(string repr) {
    if (repr.length() != 2 || repr[0] != '#')
        return NULL;
    if (repr[1] == 't')
        return new BoolObj(true);
    else if (repr[1] == 'f')
        return new BoolObj(false);
    return NULL;
}

NumObj::NumObj(NumLvl _level, bool _exactness) :
    EvalObj(CLS_SIM_OBJ | CLS_NUM_OBJ), exactness(_exactness), level(_level) {}

    bool NumObj::is_exact() { return exactness; }

    StrObj::StrObj(string _str) : EvalObj(CLS_SIM_OBJ | CLS_STR_OBJ), str(_str) {}

    ReprCons *StrObj::get_repr_cons() {
        return new ReprStr(str);
    }

CharObj::CharObj(char _ch) : EvalObj(CLS_SIM_OBJ | CLS_CHAR_OBJ), ch(_ch) {}

CharObj *CharObj::from_string(string repr) {
    size_t len = repr.length();
    if (len < 2) return NULL;
    if (repr[0] != '#' || repr[1] != '\\') return NULL;
    if (len == 3) return new CharObj(repr[2]);
    string char_name = repr.substr(2, len - 2);
    if (char_name == "newline") return new CharObj('\n');
    if (char_name == "space") return new CharObj(' ');
    throw TokenError(char_name, RUN_ERR_UNKNOWN_CHAR_NAME);
}

ReprCons *CharObj::get_repr_cons() {
    string val = "";
    if (ch == ' ') val = "space";
    else if (ch == '\n') val = "newline";
    else val += ch;
    return new ReprStr("#\\" + val);
}

VecObj::VecObj(size_t size, EvalObj *fill) :
    Container(CLS_SIM_OBJ | CLS_VECT_OBJ) {
    vec.resize(size);
    for (size_t i = 0; i < size; i++)
    {
        vec[i] = fill;
        gc.attach(fill);
    }
}

VecObj::~VecObj() {
    for (EvalObjVec::iterator it = vec.begin();
            it != vec.end(); it++)
        gc.expose(*it);
}

EvalObj *VecObj::get(size_t idx) {
    return vec[idx];
}

void VecObj::set(size_t idx, EvalObj *obj) {
    if (idx >= get_size())
        throw NormalError(RUN_ERR_VALUE_OUT_OF_RANGE);
    gc.expose(vec[idx]);
    vec[idx] = obj;
    gc.attach(obj);
}

size_t VecObj::get_size() {
    return vec.end() - vec.begin();
}

void VecObj::push_back(EvalObj *new_elem) {
    gc.attach(new_elem);
    vec.push_back(new_elem);
}

void VecObj::fill(EvalObj *obj) {
    for (EvalObjVec::iterator it = vec.begin();
            it != vec.end(); it++)
    {
        gc.expose(*it);
        *it = obj;
        gc.attach(obj);
    }
}

ReprCons *VecObj::get_repr_cons() {
    return new VectReprCons(this, this);
}

void VecObj::gc_decrement() {
    for (EvalObjVec::iterator it = vec.begin();
            it != vec.end(); it++)
        GC_CYC_DEC(*it);
}

void VecObj::gc_trigger(EvalObj ** &tail, EvalObjSet &visited) {
    for (EvalObjVec::iterator it = vec.begin();
            it != vec.end(); it++)
        GC_CYC_TRIGGER(*it);
}

StrObj *StrObj::from_string(string repr) {
    size_t len = repr.length();
    if (repr[0] == '\"' && repr[len - 1] == '\"')
        return new StrObj(repr.substr(1, len - 2));
    return NULL;
}

bool StrObj::lt(StrObj *r) {
    return str < r->str;
}

bool StrObj::gt(StrObj *r) {
    return str > r->str;
}

bool StrObj::le(StrObj *r) {
    return str <= r->str;
}

bool StrObj::ge(StrObj *r) {
    return str >= r->str;
}

bool StrObj::eq(StrObj *r) {
    return str == r->str;
}

BuiltinProcObj::BuiltinProcObj(BuiltinProc f, string _name) :
    OptObj(), handler(f), name(_name) {}

    Pair *BuiltinProcObj::call(Pair *args, Environment * &lenvt,
            Continuation * &cont, EvalObj ** &top_ptr, Pair *pc) {

        Pair *ret_addr = cont->pc;
        gc.expose(*top_ptr);
        *top_ptr++ = gc.attach(handler(TO_PAIR(args->cdr), name));
        EXIT_CURRENT_EXEC(lenvt, cont, args);
        return ret_addr->next;          // Move to the next instruction
    }

ReprCons *BuiltinProcObj::get_repr_cons() {
    return new ReprStr("#<Builtin Procedure: " + name + ">");
}

Environment::Environment(Environment *_prev_envt) : 
    Container(), prev_envt(_prev_envt) {
    gc.attach(prev_envt);
}

Environment::~Environment() {
    for (Str2EvalObj::iterator it = binding.begin();
            it != binding.end(); it++)
        gc.expose(it->second);
    gc.expose(prev_envt);
}

ReprCons *Environment::get_repr_cons() {
    return new ReprStr("#<Environment>");
}

void Environment::gc_decrement() {
    GC_CYC_DEC(prev_envt);
    for (Str2EvalObj::iterator it = binding.begin();
            it != binding.end(); it++)
        GC_CYC_DEC(it->second);
}

void Environment::gc_trigger(EvalObj ** &tail, EvalObjSet &visited) {
    GC_CYC_TRIGGER(prev_envt);
    for (Str2EvalObj::iterator it = binding.begin();
            it != binding.end(); it++)
        GC_CYC_TRIGGER(it->second);
}

bool Environment::add_binding(SymObj *sym_obj, EvalObj *eval_obj, bool def) {
    bool found = false;
    string name(sym_obj->val);
    if (!def)
    {
        for (Environment *ptr = this; ptr; ptr = ptr->prev_envt)
        {
            bool has_key = ptr->binding.count(name);
            if (has_key)
            {
                EvalObj * &ref = ptr->binding[name];
                gc.expose(ref);
                ref = eval_obj;
                gc.attach(ref);
                found = true;
                break;
            }
        }
        return found;
    }
    else
    {
        if (!binding.count(name))
        {
            binding[name] = eval_obj;
            gc.attach(eval_obj);
        }
        else
        {
            EvalObj * &ref = binding[name];
            gc.expose(ref);
            ref = eval_obj;
            gc.attach(ref);
        }
        return true;
    }
}

EvalObj *Environment::get_obj(EvalObj *obj) {
    if (!obj->is_sym_obj()) return obj;
    SymObj *sym_obj = static_cast<SymObj*>(obj);

    string name(sym_obj->val);
    for (Environment *ptr = this; ptr; ptr = ptr->prev_envt)
    {
        bool has_key = ptr->binding.count(name);
        if (has_key) return ptr->binding[name];
    }
    // Object not found
    throw TokenError(name, RUN_ERR_UNBOUND_VAR);
}

Environment *Environment::get_prev() {
    return prev_envt;
}

Continuation::Continuation(Environment *_envt, Pair *_pc, Continuation *_prev_cont ) :
    Container(), prev_cont(_prev_cont), envt(_envt), pc(_pc), state(NULL), prog(NULL), tail(false) {
        gc.attach(prev_cont);
        gc.attach(envt);
    }

Continuation::~Continuation() {
    gc.expose(prev_cont);
    gc.expose(envt);
}

void Continuation::gc_decrement() {
    GC_CYC_DEC(prev_cont);
    GC_CYC_DEC(envt);
}

void Continuation::gc_trigger(EvalObj ** &tail, EvalObjSet &visited) {
    GC_CYC_TRIGGER(prev_cont);
    GC_CYC_TRIGGER(envt);
}

ReprCons *Continuation::get_repr_cons() {
    return new ReprStr("#<Continuation>");
}

ReprCons::ReprCons(bool _done, EvalObj *_ori) : ori(_ori), done(_done) {}
ReprStr::ReprStr(string _repr) : ReprCons(true) { repr = _repr; }
EvalObj *ReprStr::next(const string &prev) {
    fprintf(stderr, "Oops in ReprStr::next\n");
    throw NormalError(INT_ERR);
}

PairReprCons::PairReprCons(Pair *_ptr, EvalObj *_ori) :
    ReprCons(false, _ori), state(0), ptr(_ptr) {}

    EvalObj *PairReprCons::next(const string &prev) {
        repr += prev;
        EvalObj *res;
        if (state == 0)
        {
            state = 1;
            res = TO_PAIR(ptr)->car;
            if (res->is_pair_obj())
                repr += "(";
            return res;
        }
        else if (state == 1)
        {
            state = 2;
            if (TO_PAIR(ptr)->car->is_pair_obj())
                repr += ")";
            ptr = TO_PAIR(ptr)->cdr;
            if (ptr == empty_list)
                return NULL;
            repr += " ";
            if (ptr->is_simple_obj())
                repr += ". ";
            return ptr;
        }
        else
        {
            return NULL;
        }
    }

VectReprCons::VectReprCons(VecObj *_ptr, EvalObj *_ori) :
    ReprCons(false, _ori), ptr(_ptr), idx(0) { repr = "#("; }

    EvalObj *VectReprCons::next(const string &prev) {
        repr += prev;

        if (idx && ptr->get(idx - 1)->is_pair_obj())
            repr += ")";

        if (idx == ptr->get_size())
        {
            repr += ")";
            return NULL;
        }
        else
        {
            if (idx) repr += " ";
            EvalObj *res = ptr->get(idx++);
            if (res->is_pair_obj())
                repr += "(";
            return res;
        }
    }

PromObj::PromObj(EvalObj *exp) :
    Container(CLS_SIM_OBJ | CLS_PROM_OBJ),
    entry(new Pair(exp, empty_list)), mem(NULL) {
        gc.attach(entry);
        entry->next = NULL;
    }

PromObj::~PromObj() {
    gc.expose(entry);
    gc.expose(mem);
}

void PromObj::gc_decrement() {
    GC_CYC_DEC(entry);
    GC_CYC_DEC(mem);
}

void PromObj::gc_trigger(EvalObj ** &tail, EvalObjSet &visited) {
    GC_CYC_TRIGGER(entry);
    GC_CYC_TRIGGER(mem);
}

Pair *PromObj::get_entry() { return entry; }

ReprCons *PromObj::get_repr_cons() { return new ReprStr("#<Promise>"); }

EvalObj *PromObj::get_mem() { return mem; }

void PromObj::feed_mem(EvalObj *res) {
    gc.attach(mem = res);
}


string double_to_str(double val, bool force_sign = false) {
    std::stringstream ss;
    if (force_sign) ss << std::showpos;
    ss << std::setprecision(PREC);
    ss << val;
    return ss.str();
}

string int_to_str(int val) {
    std::stringstream ss;
    ss << val;
    return ss.str();
}

double str_to_double(string repr, bool &flag) {
    const char *nptr = repr.c_str();
    char *endptr;
    double val = strtod(nptr, &endptr);
    if (endptr == nptr || endptr != nptr + repr.length())
    {
        flag = false;
        return 0;
    }
    flag = true;
    return val;
}

int str_to_int(string repr, bool &flag) {
    const char *nptr = repr.c_str();
    char *endptr;
    int val = strtol(nptr, &endptr, 10);
    if (endptr == nptr || endptr != nptr + repr.length())
    {
        flag = false;
        return 0;
    }
    flag = true;
    return val;
}


int gcd(int a, int b) {
    int t;
    while (b) t = b, b = a % b, a = t;
    return abs(a);
}

bool is_zero(double x) {
    return -EPS < x && x < EPS;
}

InexactNumObj::InexactNumObj(NumLvl level) : NumObj(level, false) {}

CompNumObj::CompNumObj(double _real, double _imag) :
    InexactNumObj(NUM_LVL_COMP), real(_real), imag(_imag) {}

    CompNumObj *CompNumObj::from_string(string repr) {
        // spos: the position of the last sign
        // ipos: the position of i
        long long spos = -1, ipos = -1;
        size_t len = repr.length();
        bool sign = false;
        for (size_t i = 0; i < len; i++)
            if (repr[i] == '+' || repr[i] == '-')
            {
                spos = i;
                sign = repr[i] == '-';
            }
            else if (repr[i] == 'i' || repr[i] == 'I')
                ipos = i;

        if (spos == -1 || ipos == -1 || !(spos < ipos))
            return NULL;

        double real = 0, imag = 1;
        IntNumObj *int_ptr;
        RatNumObj *rat_ptr;
        RealNumObj *real_ptr;
        if (spos > 0)
        {
            string real_str = repr.substr(0, spos);
            if ((int_ptr = IntNumObj::from_string(real_str)))
#ifndef GMP_SUPPORT
                real = int_ptr->val;
#else
            real = int_ptr->val.get_d();
#endif
            else if ((rat_ptr = RatNumObj::from_string(real_str)))
#ifndef GMP_SUPPORT
                real = rat_ptr->a / double(rat_ptr->b);
#else
            real = rat_ptr->val.get_d();
#endif
            else if ((real_ptr = RealNumObj::from_string(real_str)))
                real = real_ptr->real;
            else return NULL;
        }
        if (ipos > spos + 1)
        {
            string imag_str = repr.substr(spos + 1, ipos - spos - 1);
            if ((int_ptr = IntNumObj::from_string(imag_str)))
#ifndef GMP_SUPPORT
                imag = int_ptr->val;
#else
            imag = int_ptr->val.get_d();
#endif
            else if ((rat_ptr = RatNumObj::from_string(imag_str)))
#ifndef GMP_SUPPORT
                imag = rat_ptr->a / double(rat_ptr->b);
#else
            imag = rat_ptr->val.get_d();
#endif
            else if ((real_ptr = RealNumObj::from_string(imag_str)))
                imag = real_ptr->real;
            else return NULL;
        }
        if (sign) imag = -imag;
        return new CompNumObj(real, imag);
    }

NumObj *CompNumObj::clone() const {
    return new CompNumObj(*this);
}

CompNumObj *CompNumObj::convert(NumObj *obj) {
    switch (obj->level)
    {
        case NUM_LVL_COMP :
            return new CompNumObj(*static_cast<CompNumObj*>(obj));
            break;
        case NUM_LVL_REAL :
            return new CompNumObj(static_cast<RealNumObj*>(obj)->real, 0);
            break;
        case NUM_LVL_RAT :
            {
                RatNumObj *rat = static_cast<RatNumObj*>(obj);
#ifndef GMP_SUPPORT
                return new CompNumObj(rat->a / double(rat->b), 0);
#else
                return new CompNumObj(rat->val.get_d(), 0);
#endif
                break;
            }
        case NUM_LVL_INT :
#ifndef GMP_SUPPORT
            return new CompNumObj(static_cast<IntNumObj*>(obj)->val, 0);
#else
            return new CompNumObj(static_cast<IntNumObj*>(obj)->val.get_d(), 0);
#endif
    }
    throw NormalError(INT_ERR);
}

#define A (real)
#define B (imag)
#define C (r->real)
#define D (r->imag)

void CompNumObj::add(NumObj *_r) {
    CompNumObj *r = static_cast<CompNumObj*>(_r);
    real += C;
    imag += D;
}

void CompNumObj::sub(NumObj *_r) {
    CompNumObj *r = static_cast<CompNumObj*>(_r);
    real -= C;
    imag -= D;
}

void CompNumObj::mul(NumObj *_r) {
    CompNumObj *r = static_cast<CompNumObj*>(_r);
    double ra = A * C - B * D;
    double rb = B * C + A * D;
    A = ra;
    B = rb;
}

void CompNumObj::div(NumObj *_r) {
    CompNumObj *r = static_cast<CompNumObj*>(_r);
    double f = C * C + D * D;
    if (f == 0)
        throw NormalError(RUN_ERR_NUMERIC_OVERFLOW);
    f = 1 / f;
    double ra = (A * C + B * D) * f;
    double rb = (B * C - A * D) * f;
    A = ra;
    B = rb;
}

bool NumObj::lt(NumObj *_r) {
    throw TokenError("a comparable number", RUN_ERR_WRONG_TYPE);
}

bool NumObj::gt(NumObj *_r) {
    throw TokenError("a comparable number", RUN_ERR_WRONG_TYPE);
}

bool NumObj::le(NumObj *_r) {
    throw TokenError("a comparable number", RUN_ERR_WRONG_TYPE);
}

bool NumObj::ge(NumObj *_r) {
    throw TokenError("a comparable number", RUN_ERR_WRONG_TYPE);
}

void NumObj::abs() {
    throw TokenError("a real number", RUN_ERR_WRONG_TYPE);
}


bool CompNumObj::eq(NumObj *_r) {
    CompNumObj *r = static_cast<CompNumObj*>(_r);
    return A == C && B == D; // TODO: more proper judgement
}


ReprCons *CompNumObj::get_repr_cons() {
    return new ReprStr(double_to_str(real) + double_to_str(imag, true) + "i");
}

#undef A
#undef B
#undef C
#undef D

RealNumObj::RealNumObj(double _real) : InexactNumObj(NUM_LVL_REAL), real(_real) {}

NumObj *RealNumObj::clone() const {
    return new RealNumObj(*this);
}

RealNumObj *RealNumObj::from_string(string repr) {
    bool flag;
    double real = str_to_double(repr, flag);
    if (!flag) return NULL;
    return new RealNumObj(real);
}

RealNumObj *RealNumObj::convert(NumObj *obj) {
    switch (obj->level)
    {
        case NUM_LVL_REAL:
            return new RealNumObj(*static_cast<RealNumObj*>(obj));
            break;
        case NUM_LVL_RAT:
            {
                RatNumObj *rat = static_cast<RatNumObj*>(obj);
#ifndef GMP_SUPPORT
                return new RealNumObj(rat->a / double(rat->b));
#else
                return new RealNumObj(rat->val.get_d());
#endif
                break;
            }
        case NUM_LVL_INT:
#ifndef GMP_SUPPORT
            return new RealNumObj(static_cast<IntNumObj*>(obj)->val);
#else
            return new RealNumObj(static_cast<IntNumObj*>(obj)->val.get_d());
#endif

    }
    throw NormalError(INT_ERR);
}

void RealNumObj::add(NumObj *_r) {
    real += static_cast<RealNumObj*>(_r)->real;
}

void RealNumObj::sub(NumObj *_r) {
    real -= static_cast<RealNumObj*>(_r)->real;
}

void RealNumObj::mul(NumObj *_r) {
    real *= static_cast<RealNumObj*>(_r)->real;
}

void RealNumObj::div(NumObj *_r) {
    real /= static_cast<RealNumObj*>(_r)->real;
}

void RealNumObj::abs() {
    real = fabs(real);
}

bool RealNumObj::eq(NumObj *_r) {
    return real == static_cast<RealNumObj*>(_r)->real;
}

bool RealNumObj::lt(NumObj *_r) {
    return real < static_cast<RealNumObj*>(_r)->real;
}

bool RealNumObj::gt(NumObj *_r) {
    return real > static_cast<RealNumObj*>(_r)->real;
}

bool RealNumObj::le(NumObj *_r) {
    return real <= static_cast<RealNumObj*>(_r)->real;
}

bool RealNumObj::ge(NumObj *_r) {
    return real >= static_cast<RealNumObj*>(_r)->real;
}


ReprCons *RealNumObj::get_repr_cons() {
    return new ReprStr(double_to_str(real));
}

ExactNumObj::ExactNumObj(NumLvl level) : NumObj(level, true) {}

#ifndef GMP_SUPPORT
RatNumObj::RatNumObj(int _a, int _b) :
    ExactNumObj(NUM_LVL_RAT), a(_a), b(_b) {
        if (b == 0)
            throw NormalError(RUN_ERR_NUMERIC_OVERFLOW);
        if (b < 0) a = -a, b = -b;
        int g = gcd(a, b);
        a /= g;
        b /= g;
    }

RatNumObj *RatNumObj::from_string(string repr) {
    int a, b;
    size_t len = repr.length();
    int pos = -1;
    for (size_t i = 0; i < len; i++)
        if (repr[i] == '/') { pos = i; break; }
    bool flag;
    a = str_to_int(repr.substr(0, pos), flag);
    if (!flag) return NULL;
    b = str_to_int(repr.substr(pos + 1, len - pos - 1), flag);
    if (!flag) return NULL;

    return new RatNumObj(a, b);
}
#else
RatNumObj::RatNumObj(mpq_class _val) : ExactNumObj(NUM_LVL_RAT), val(_val) {
        val.canonicalize();
}

NumObj *RatNumObj::clone() const {
    return new RatNumObj(*this);
}

RatNumObj *RatNumObj::from_string(string repr) {
    try
    {
        mpq_class ret(repr, 10);
        if (ret.get_den() == 0)
            throw NormalError(RUN_ERR_NUMERIC_OVERFLOW);
        ret.canonicalize();
        return new RatNumObj(ret);
    }
    catch (std::invalid_argument &e)
    {
        return NULL;
    }
}

RatNumObj::RatNumObj(const RatNumObj &ori) :
    ExactNumObj(NUM_LVL_RAT), val(ori.val.get_mpq_t()) {}
#endif


RatNumObj *RatNumObj::convert(NumObj *obj) {
    switch (obj->level)
    {
        case NUM_LVL_RAT:
            return new RatNumObj(*static_cast<RatNumObj*>(obj));
            break;
        case NUM_LVL_INT:
#ifndef GMP_SUPPORT
            return new RatNumObj(static_cast<IntNumObj*>(obj)->val, 1);
#else
            return new RatNumObj(mpq_class(
                        static_cast<IntNumObj*>(obj)->val,
                        mpz_class(1)));
#endif
    }
    throw NormalError(INT_ERR);
}

#define A (a)
#define B (b)
#define C (r->a)
#define D (r->b)

void RatNumObj::add(NumObj *_r) {
    RatNumObj *r = static_cast<RatNumObj*>(_r);
#ifndef GMP_SUPPORT
    A = A * D + B * C;
    B = B * D;
    int g = gcd(na, nb);
    A /= g;
    B /= g;
#else
    val += r->val;
#endif
}

void RatNumObj::sub(NumObj *_r) {
    RatNumObj *r = static_cast<RatNumObj*>(_r);
#ifndef GMP_SUPPORT
    A = A * D - B * C;
    B = B * D;
    int g = gcd(na, nb);
    A /= g;
    B /= g;
#else
    val -= r->val;
#endif
}

void RatNumObj::mul(NumObj *_r) {
    RatNumObj *r = static_cast<RatNumObj*>(_r);
#ifndef GMP_SUPPORT
    A = A * C;
    B = B * D;
    int g = gcd(na, nb);
    A /= g;
    B /= g;
#else
    val *= r->val;
#endif
}

void RatNumObj::div(NumObj *_r) {
    RatNumObj *r = static_cast<RatNumObj*>(_r);
#ifndef GMP_SUPPORT
    A = A * D;
    B = B * C;
    int g = gcd(na, nb);
    A /= g;
    B /= g;
#else
    if (r->val == 0)
        throw NormalError(RUN_ERR_NUMERIC_OVERFLOW);
    val /= r->val;
#endif
}

bool RatNumObj::lt(NumObj *_r) {
    RatNumObj *r = static_cast<RatNumObj*>(_r);
#ifndef GMP_SUPPORT
    return A * D < C * B;
#else
    return val < r->val;
#endif
}

bool RatNumObj::gt(NumObj *_r) {
    RatNumObj *r = static_cast<RatNumObj*>(_r);
#ifndef GMP_SUPPORT
    return A * D > C * B;
#else
    return val > r->val;
#endif
}

bool RatNumObj::le(NumObj *_r) {
    RatNumObj *r = static_cast<RatNumObj*>(_r);
#ifndef GMP_SUPPORT
    return A * D <= C * B;
#else
    return val <= r->val;
#endif
}

bool RatNumObj::ge(NumObj *_r) {
    RatNumObj *r = static_cast<RatNumObj*>(_r);
#ifndef GMP_SUPPORT
    return A * D >= C * B;
#else
    return val >= r->val;
#endif
}


bool RatNumObj::eq(NumObj *_r) {
    RatNumObj *r = static_cast<RatNumObj*>(_r);
#ifndef GMP_SUPPORT
    return A * D == C * B;
#else
    return val == r->val;
#endif
}

void RatNumObj::abs() {
#ifndef GMP_SUPPORT
    if (a < 0) a = -a;
#else
    val = ::abs(val);
#endif
}

IntNumObj *RatNumObj::to_int() {
    if (val.get_den() != 1)
        throw TokenError("an integer", RUN_ERR_WRONG_TYPE);
    return new IntNumObj(val.get_num());
}

ReprCons *RatNumObj::get_repr_cons() {
#ifndef GMP_SUPPORT
    return new ReprStr(int_to_str(A) + "/" + int_to_str(B));
#else
    return new ReprStr(val.get_str());
#endif
}


#ifndef GMP_SUPPORT
IntNumObj::IntNumObj(int _val) : ExactNumObj(NUM_LVL_INT), val(_val) {}

IntNumObj *IntNumObj::from_string(string repr) {
    int val = 0;
    for (size_t i = 0; i < repr.length(); i++)
    {
        if (!('0' <= repr[i] && repr[i] <= '9'))
            return NULL;
        val = val * 10 + repr[i] - '0';
    }
    return new IntNumObj(val);
}
int IntNumObj::get_i() { return val; }
#else
IntNumObj::IntNumObj(mpz_class _val) : ExactNumObj(NUM_LVL_INT), val(_val) {}
IntNumObj *IntNumObj::from_string(string repr) {
    try
    {
        mpz_class ret(repr, 10);
        return new IntNumObj(ret);
    }
    catch (std::invalid_argument &e)
    {
        return NULL;
    }
}
int IntNumObj::get_i() { return val.get_si(); }
IntNumObj::IntNumObj(const IntNumObj &ori) :
    ExactNumObj(NUM_LVL_INT), val(ori.val.get_mpz_t()) {}
#endif


NumObj *IntNumObj::clone() const {
    return new IntNumObj(*this);
}

IntNumObj *IntNumObj::convert(NumObj *obj) {
    switch (obj->level)
    {
        case NUM_LVL_INT :
            return new IntNumObj(*static_cast<IntNumObj*>(obj));
        default:
            throw NormalError(INT_ERR);
    }
}

void IntNumObj::add(NumObj *_r) {
    val += static_cast<IntNumObj*>(_r)->val;
}

void IntNumObj::sub(NumObj *_r) {
    val -= static_cast<IntNumObj*>(_r)->val;
}

void IntNumObj::mul(NumObj *_r) {
    val *= static_cast<IntNumObj*>(_r)->val;
}

void IntNumObj::abs() {
    val = ::abs(val);
}

void IntNumObj::rem(NumObj *_r) {
    const mpz_class &rval(static_cast<IntNumObj*>(_r)->val);
    if (rval == 0) throw NormalError(RUN_ERR_NUMERIC_OVERFLOW);
    val %= rval;
}

void IntNumObj::mod(NumObj *_r) {
    const mpz_class &rval = static_cast<IntNumObj*>(_r)->val;
    if (rval == 0) throw NormalError(RUN_ERR_NUMERIC_OVERFLOW);
    val %= rval;
    if (val != 0 && sgn(val) != sgn(rval))
        val += rval;
}

void IntNumObj::div(NumObj *_r) {
    const mpz_class &rval = static_cast<IntNumObj*>(_r)->val;
    if (rval == 0) throw NormalError(RUN_ERR_NUMERIC_OVERFLOW);
    val /= rval;
}

void IntNumObj::gcd(NumObj *_r) {
    mpz_gcd(val.get_mpz_t(), 
            val.get_mpz_t(), 
            static_cast<IntNumObj*>(_r)->val.get_mpz_t());
}

void IntNumObj::lcm(NumObj *_r) {
    mpz_lcm(val.get_mpz_t(), 
            val.get_mpz_t(), 
            static_cast<IntNumObj*>(_r)->val.get_mpz_t());
}

bool IntNumObj::lt(NumObj *_r) {
    return val < static_cast<IntNumObj*>(_r)->val;
}

bool IntNumObj::gt(NumObj *_r) {
    return val > static_cast<IntNumObj*>(_r)->val;
}

bool IntNumObj::le(NumObj *_r) {
    return val <= static_cast<IntNumObj*>(_r)->val;
}

bool IntNumObj::ge(NumObj *_r) {
    return val >= static_cast<IntNumObj*>(_r)->val;
}


bool IntNumObj::eq(NumObj *_r) {
    return val == static_cast<IntNumObj*>(_r)->val;
}

IntNumObj* IntNumObj::to_int() {
    return new IntNumObj(val);
}

ReprCons *IntNumObj::get_repr_cons() {
#ifndef GMP_SUPPORT
    return new ReprStr(int_to_str(val));
#else
    return new ReprStr(val.get_str());
#endif
}