diff options
author | (no author) <(no author)@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2004-04-06 08:02:56 +0000 |
---|---|---|
committer | (no author) <(no author)@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2004-04-06 08:02:56 +0000 |
commit | 5923a2c0e770515a9c7144c50e09a32350695b00 (patch) | |
tree | 66882c2ac60d7d1b25bb49ee476cb829695aa906 | |
parent | 8e773df6d3b68caf2e56e6c75a8e48bf2ccc1bd3 (diff) |
This commit was manufactured by cvs2svn to create branch 'ruby_1_8'.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@6109 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | ext/bigdecimal/bigdecimal.c | 4057 | ||||
-rw-r--r-- | lib/rss/0.9.rb | 409 | ||||
-rw-r--r-- | lib/rss/2.0.rb | 147 | ||||
-rw-r--r-- | lib/rss/converter.rb | 154 | ||||
-rw-r--r-- | lib/rss/syndication.rb | 83 | ||||
-rw-r--r-- | lib/rss/taxonomy.rb | 32 | ||||
-rw-r--r-- | lib/rss/utils.rb | 17 | ||||
-rw-r--r-- | lib/rss/xml-stylesheet.rb | 94 | ||||
-rw-r--r-- | lib/rss/xmlscanner.rb | 102 | ||||
-rw-r--r-- | test/rss/rss-assertions.rb | 127 | ||||
-rw-r--r-- | test/rss/rss-testcase.rb | 164 | ||||
-rw-r--r-- | test/rss/test_1.0.rb | 259 | ||||
-rw-r--r-- | test/rss/test_accessor.rb | 26 | ||||
-rw-r--r-- | test/rss/test_content.rb | 97 | ||||
-rw-r--r-- | test/rss/test_dublincore.rb | 126 | ||||
-rw-r--r-- | test/rss/test_syndication.rb | 125 | ||||
-rw-r--r-- | test/rss/test_trackback.rb | 137 | ||||
-rw-r--r-- | test/rss/test_xml-stylesheet.rb | 110 | ||||
-rw-r--r-- | test/ruby/test_time.rb | 73 | ||||
-rwxr-xr-x | win32/rm.bat | 9 |
20 files changed, 6348 insertions, 0 deletions
diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c new file mode 100644 index 0000000000..2af1da06ef --- /dev/null +++ b/ext/bigdecimal/bigdecimal.c @@ -0,0 +1,4057 @@ +/* + * + * Ruby BigDecimal(Variable decimal precision) extension library. + * + * Copyright(C) 2002 by Shigeo Kobayashi(shigeo@tinyforest.gr.jp) + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the README file + * of this BigDecimal distribution. + * + * NOTE: Change log in this source removed to reduce source code size. + * See rev. 1.25 if needed. + * + */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <float.h> +#include <math.h> +#include "ruby.h" +#include "math.h" +#include "version.h" + +/* #define ENABLE_NUMERIC_STRING */ + +VALUE rb_cBigDecimal; + +#include "bigdecimal.h" + +/* MACRO's to guard objects from GC by keeping them in stack */ +#define ENTER(n) volatile VALUE vStack[n];int iStack=0 +#define PUSH(x) vStack[iStack++] = (unsigned long)(x); +#define SAVE(p) PUSH(p->obj); +#define GUARD_OBJ(p,y) {p=y;SAVE(p);} + +/* + * ================== Ruby Interface part ========================== + */ +#define DoSomeOne(x,y) rb_num_coerce_bin(x,y) + +/* + * **** BigDecimal version **** + */ +static VALUE +BigDecimal_version(VALUE self) +{ + /* + * 1.0.0: Ruby 1.8.0 + * 1.0.1: Ruby 1.8.1 + */ + return rb_str_new2("1.0.1"); +} + +/* + * VP routines used in BigDecimal part + */ +static unsigned short VpGetException(void); +static void VpSetException(unsigned short f); +static void VpInternalRound(Real *c,int ixDigit,U_LONG vPrev,U_LONG v); +static int VpLimitRound(Real *c,U_LONG ixDigit); + +/* + * **** BigDecimal part **** + */ + +static void +BigDecimal_delete(Real *pv) +{ + VpFree(pv); +} + +static VALUE +ToValue(Real *p) +{ + if(VpIsNaN(p)) { + VpException(VP_EXCEPTION_NaN,"Computation results to 'NaN'(Not a Number)",0); + } else if(VpIsPosInf(p)) { + VpException(VP_EXCEPTION_INFINITY,"Computation results to 'Infinity'",0); + } else if(VpIsNegInf(p)) { + VpException(VP_EXCEPTION_INFINITY,"Computation results to '-Infinity'",0); + } + return p->obj; +} + +static Real * +GetVpValue(VALUE v, int must) +{ + Real *pv; + VALUE bg; + char szD[128]; + + switch(TYPE(v)) + { + case T_DATA: + if(RDATA(v)->dfree ==(void *) BigDecimal_delete) { + Data_Get_Struct(v, Real, pv); + return pv; + } else { + goto SomeOneMayDoIt; + } + break; + case T_FIXNUM: + sprintf(szD, "%ld", FIX2LONG(v)); + return VpCreateRbObject(VpBaseFig() * 2 + 1, szD); + +#ifdef ENABLE_NUMERIC_STRING + case T_STRING: + SafeStringValue(v); + return VpCreateRbObject(strlen(RSTRING(v)->ptr) + VpBaseFig() + 1, + RSTRING(v)->ptr); +#endif /* ENABLE_NUMERIC_STRING */ + + case T_BIGNUM: + bg = rb_big2str(v, 10); + return VpCreateRbObject(strlen(RSTRING(bg)->ptr) + VpBaseFig() + 1, + RSTRING(bg)->ptr); + default: + goto SomeOneMayDoIt; + } + +SomeOneMayDoIt: + if(must) { + rb_raise(rb_eTypeError, "%s can't be coerced into BigDecimal", + rb_special_const_p(v)? + RSTRING(rb_inspect(v))->ptr: + rb_obj_classname(v) + ); + } + return NULL; /* NULL means to coerce */ +} + +static VALUE +BigDecimal_double_fig(VALUE self) +{ + return INT2FIX(VpDblFig()); +} + +static VALUE +BigDecimal_prec(VALUE self) +{ + ENTER(1); + Real *p; + VALUE obj; + + GUARD_OBJ(p,GetVpValue(self,1)); + obj = rb_assoc_new(INT2NUM(p->Prec*VpBaseFig()), + INT2NUM(p->MaxPrec*VpBaseFig())); + return obj; +} + +static VALUE +BigDecimal_hash(VALUE self) +{ + ENTER(1); + Real *p; + U_LONG hash,i; + + GUARD_OBJ(p,GetVpValue(self,1)); + hash = (U_LONG)p->sign; + /* hash!=2: the case for 0(1),NaN(0) or +-Infinity(3) is sign itself */ + if(hash==2) { + for(i = 0; i < p->Prec;i++) { + hash = 31 * hash + p->frac[i]; + hash ^= p->frac[i]; + } + hash += p->exponent; + } + return INT2FIX(hash); +} + +static VALUE +BigDecimal_dump(int argc, VALUE *argv, VALUE self) +{ + ENTER(5); + char sz[50]; + Real *vp; + char *psz; + VALUE dummy; + rb_scan_args(argc, argv, "01", &dummy); + GUARD_OBJ(vp,GetVpValue(self,1)); + sprintf(sz,"%lu:",VpMaxPrec(vp)*VpBaseFig()); + psz = ALLOCA_N(char,(unsigned int)VpNumOfChars(vp,"E")+strlen(sz)); + sprintf(psz,"%s",sz); + VpToString(vp, psz+strlen(psz), 0, 0); + return rb_str_new2(psz); +} + +static VALUE +BigDecimal_load(VALUE self, VALUE str) +{ + ENTER(2); + Real *pv; + unsigned char *pch; + unsigned char ch; + unsigned long m=0; + + SafeStringValue(str); + pch = RSTRING(str)->ptr; + /* First get max prec */ + while((*pch)!=(unsigned char)'\0' && (ch=*pch++)!=(unsigned char)':') { + if(!ISDIGIT(ch)) { + rb_raise(rb_eTypeError, "Load failed: invalid character in the marshaled string"); + } + m = m*10 + (unsigned long)(ch-'0'); + } + if(m>VpBaseFig()) m -= VpBaseFig(); + GUARD_OBJ(pv,VpNewRbClass(m,pch,self)); + m /= VpBaseFig(); + if(m && pv->MaxPrec>m) pv->MaxPrec = m+1; + return ToValue(pv); +} + +static VALUE +BigDecimal_mode(int argc, VALUE *argv, VALUE self) +{ + VALUE which; + VALUE val; + unsigned long f,fo; + + if(rb_scan_args(argc,argv,"11",&which,&val)==1) val = Qnil; + + Check_Type(which, T_FIXNUM); + f = (unsigned long)FIX2INT(which); + + if(f&VP_EXCEPTION_ALL) { + /* Exception mode setting */ + fo = VpGetException(); + if(val==Qnil) return INT2FIX(fo); + if(val!=Qfalse && val!=Qtrue) { + rb_raise(rb_eTypeError, "The second argument must be true or false."); + return Qnil; /* Not reached */ + } + if(f&VP_EXCEPTION_INFINITY) { + VpSetException((unsigned short)((val==Qtrue)?(fo|VP_EXCEPTION_INFINITY): + (fo&(~VP_EXCEPTION_INFINITY)))); + } + if(f&VP_EXCEPTION_NaN) { + VpSetException((unsigned short)((val==Qtrue)?(fo|VP_EXCEPTION_NaN): + (fo&(~VP_EXCEPTION_NaN)))); + } + fo = VpGetException(); + return INT2FIX(fo); + } + if(VP_ROUND_MODE==f) { + /* Rounding mode setting */ + fo = VpGetRoundMode(); + if(val==Qnil) return INT2FIX(fo); + Check_Type(val, T_FIXNUM); + if(!VpIsRoundMode(FIX2INT(val))) { + rb_raise(rb_eTypeError, "Invalid rounding mode."); + return Qnil; + } + fo = VpSetRoundMode((unsigned long)FIX2INT(val)); + return INT2FIX(fo); + } + rb_raise(rb_eTypeError, "The first argument for BigDecimal#mode is invalid."); + return Qnil; +} + +static U_LONG +GetAddSubPrec(Real *a, Real *b) +{ + U_LONG mxs; + U_LONG mx = a->Prec; + S_INT d; + + if(!VpIsDef(a) || !VpIsDef(b)) return (-1L); + if(mx < b->Prec) mx = b->Prec; + if(a->exponent!=b->exponent) { + mxs = mx; + d = a->exponent - b->exponent; + if(d<0) d = -d; + mx = mx+(U_LONG)d; + if(mx<mxs) { + return VpException(VP_EXCEPTION_INFINITY,"Exponent overflow",0); + } + } + return mx; +} + +static S_INT +GetPositiveInt(VALUE v) +{ + S_INT n; + Check_Type(v, T_FIXNUM); + n = FIX2INT(v); + if(n < 0) { + rb_raise(rb_eArgError, "argument must be positive"); + } + return n; +} + +VP_EXPORT Real * +VpNewRbClass(U_LONG mx, char *str, VALUE klass) +{ + Real *pv = VpAlloc(mx,str); + pv->obj = (VALUE)Data_Wrap_Struct(klass, 0, BigDecimal_delete, pv); + return pv; +} + +VP_EXPORT Real * +VpCreateRbObject(U_LONG mx, char *str) +{ + Real *pv = VpAlloc(mx,str); + pv->obj = (VALUE)Data_Wrap_Struct(rb_cBigDecimal, 0, BigDecimal_delete, pv); + return pv; +} + + +static VALUE +BigDecimal_IsNaN(VALUE self) +{ + Real *p = GetVpValue(self,1); + if(VpIsNaN(p)) return Qtrue; + return Qfalse; +} + +static VALUE +BigDecimal_IsInfinite(VALUE self) +{ + Real *p = GetVpValue(self,1); + if(VpIsPosInf(p)) return INT2FIX(1); + if(VpIsNegInf(p)) return INT2FIX(-1); + return Qnil; +} + +static VALUE +BigDecimal_IsFinite(VALUE self) +{ + Real *p = GetVpValue(self,1); + if(VpIsNaN(p)) return Qfalse; + if(VpIsInf(p)) return Qfalse; + return Qtrue; +} + +static VALUE +BigDecimal_to_i(VALUE self) +{ + ENTER(5); + int e,n,i,nf; + U_LONG v,b,j; + char *psz,*pch; + Real *p; + + GUARD_OBJ(p,GetVpValue(self,1)); + + /* Infinity or NaN not converted. */ + if(VpIsNaN(p)) { + VpException(VP_EXCEPTION_NaN,"Computation results to 'NaN'(Not a Number)",0); + return Qnil; + } else if(VpIsPosInf(p)) { + VpException(VP_EXCEPTION_INFINITY,"Computation results to 'Infinity'",0); + return Qnil; + } else if(VpIsNegInf(p)) { + VpException(VP_EXCEPTION_INFINITY,"Computation results to '-Infinity'",0); + return Qnil; + } + + e = VpExponent10(p); + if(e<=0) return INT2FIX(0); + nf = VpBaseFig(); + if(e<=nf) { + e = VpGetSign(p)*p->frac[0]; + return INT2FIX(e); + } + psz = ALLOCA_N(char,(unsigned int)(e+nf+2)); + + n = (e+nf-1)/nf; + pch = psz; + if(VpGetSign(p)<0) *pch++ = '-'; + for(i=0;i<n;++i) { + b = VpBaseVal()/10; + if(i>=(int)p->Prec) { + while(b) { + *pch++ = '0'; + b /= 10; + } + continue; + } + v = p->frac[i]; + while(b) { + j = v/b; + *pch++ = (char)(j + '0'); + v -= j*b; + b /= 10; + } + } + *pch++ = 0; + return rb_cstr2inum(psz,10); +} + +static VALUE +BigDecimal_induced_from(VALUE self, VALUE x) +{ + Real *p = GetVpValue(x,1); + return p->obj; +} + +static VALUE +BigDecimal_to_f(VALUE self) +{ + ENTER(1); + Real *p; + double d, d2; + S_LONG e; + + GUARD_OBJ(p,GetVpValue(self,1)); + if(VpVtoD(&d, &e, p)!=1) return rb_float_new(d); + errno = 0; + d2 = pow(10.0,(double)e); + if((errno == ERANGE && e>0) || (d2>1.0 && (fabs(d) > (DBL_MAX / d2)))) { + VpException(VP_EXCEPTION_OVERFLOW,"BigDecimal to Float conversion.",0); + if(d>0.0) return rb_float_new(DBL_MAX); + else return rb_float_new(-DBL_MAX); + } + return rb_float_new(d*d2); +} + +static VALUE +BigDecimal_coerce(VALUE self, VALUE other) +{ + ENTER(2); + VALUE obj; + Real *b; + if(TYPE(other) == T_FLOAT) { + obj = rb_assoc_new(other, BigDecimal_to_f(self)); + } else { + GUARD_OBJ(b,GetVpValue(other,1)); + obj = rb_assoc_new(b->obj, self); + } + return obj; +} + +static VALUE +BigDecimal_uplus(VALUE self) +{ + return self; +} + +static VALUE +BigDecimal_add(VALUE self, VALUE r) +{ + ENTER(5); + Real *c, *a, *b; + U_LONG mx; + GUARD_OBJ(a,GetVpValue(self,1)); + b = GetVpValue(r,0); + if(!b) return DoSomeOne(self,r); + SAVE(b); + if(VpIsNaN(b)) return b->obj; + if(VpIsNaN(a)) return a->obj; + mx = GetAddSubPrec(a,b); + if(mx==(-1L)) { + GUARD_OBJ(c,VpCreateRbObject(VpBaseFig() + 1, "0")); + VpAddSub(c, a, b, 1); + } else { + GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0")); + if(!mx) { + VpSetInf(c,VpGetSign(a)); + } else { + VpAddSub(c, a, b, 1); + } + } + return ToValue(c); +} + +static VALUE +BigDecimal_sub(VALUE self, VALUE r) +{ + ENTER(5); + Real *c, *a, *b; + U_LONG mx; + + GUARD_OBJ(a,GetVpValue(self,1)); + b = GetVpValue(r,0); + if(!b) return DoSomeOne(self,r); + SAVE(b); + + if(VpIsNaN(b)) return b->obj; + if(VpIsNaN(a)) return a->obj; + + mx = GetAddSubPrec(a,b); + if(mx==(-1L)) { + GUARD_OBJ(c,VpCreateRbObject(VpBaseFig() + 1, "0")); + VpAddSub(c, a, b, -1); + } else { + GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0")); + if(!mx) { + VpSetInf(c,VpGetSign(a)); + } else { + VpAddSub(c, a, b, -1); + } + } + return ToValue(c); +} + +static VALUE +BigDecimalCmp(VALUE self, VALUE r,char op) +{ + ENTER(5); + S_INT e; + Real *a, *b; + GUARD_OBJ(a,GetVpValue(self,1)); + b = GetVpValue(r,0); + if(!b) return rb_num_coerce_cmp(self,r); + SAVE(b); + e = VpComp(a, b); + if(e==999) return Qnil; + switch(op) + { + case '*': return INT2FIX(e); /* any op */ + case '=': if(e==0) return Qtrue ; return Qfalse; + case '!': if(e!=0) return Qtrue ; return Qfalse; + case 'G': if(e>=0) return Qtrue ; return Qfalse; + case '>': if(e> 0) return Qtrue ; return Qfalse; + case 'L': if(e<=0) return Qtrue ; return Qfalse; + case '<': if(e< 0) return Qtrue ; return Qfalse; + } + rb_bug("Undefined operation in BigDecimalCmp()"); +} + +static VALUE +BigDecimal_zero(VALUE self) +{ + Real *a = GetVpValue(self,1); + return VpIsZero(a) ? Qtrue : Qfalse; +} + +static VALUE +BigDecimal_nonzero(VALUE self) +{ + Real *a = GetVpValue(self,1); + return VpIsZero(a) ? Qnil : self; +} + +static VALUE +BigDecimal_comp(VALUE self, VALUE r) +{ + return BigDecimalCmp(self, r, '*'); +} + +static VALUE +BigDecimal_eq(VALUE self, VALUE r) +{ + return BigDecimalCmp(self, r, '='); +} + +static VALUE +BigDecimal_ne(VALUE self, VALUE r) +{ + return BigDecimalCmp(self, r, '!'); +} + +static VALUE +BigDecimal_lt(VALUE self, VALUE r) +{ + return BigDecimalCmp(self, r, '<'); +} + +static VALUE +BigDecimal_le(VALUE self, VALUE r) +{ + return BigDecimalCmp(self, r, 'L'); +} + +static VALUE +BigDecimal_gt(VALUE self, VALUE r) +{ + return BigDecimalCmp(self, r, '>'); +} + +static VALUE +BigDecimal_ge(VALUE self, VALUE r) +{ + return BigDecimalCmp(self, r, 'G'); +} + +static VALUE +BigDecimal_neg(VALUE self, VALUE r) +{ + ENTER(5); + Real *c, *a; + GUARD_OBJ(a,GetVpValue(self,1)); + GUARD_OBJ(c,VpCreateRbObject(a->Prec *(VpBaseFig() + 1), "0")); + VpAsgn(c, a, -1); + return ToValue(c); +} + +static VALUE +BigDecimal_mult(VALUE self, VALUE r) +{ + ENTER(5); + Real *c, *a, *b; + U_LONG mx; + + GUARD_OBJ(a,GetVpValue(self,1)); + b = GetVpValue(r,0); + if(!b) return DoSomeOne(self,r); + SAVE(b); + + mx = a->Prec + b->Prec; + GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0")); + VpMult(c, a, b); + return ToValue(c); +} + +static VALUE +BigDecimal_divide(Real **c, Real **res, Real **div, VALUE self, VALUE r) +/* For c = self.div(r): with round operation */ +{ + ENTER(5); + Real *a, *b; + U_LONG mx; + + GUARD_OBJ(a,GetVpValue(self,1)); + b = GetVpValue(r,0); + if(!b) return DoSomeOne(self,r); + SAVE(b); + *div = b; + mx =(a->MaxPrec + b->MaxPrec + 1) * VpBaseFig(); + GUARD_OBJ((*c),VpCreateRbObject(mx, "#0")); + GUARD_OBJ((*res),VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0")); + VpDivd(*c, *res, a, b); + return (VALUE)0; +} + +static VALUE +BigDecimal_div(VALUE self, VALUE r) +/* For c = self/r: with round operation */ +{ + ENTER(5); + Real *c=NULL, *res=NULL, *div = NULL; + r = BigDecimal_divide(&c, &res, &div, self, r); + if(r!=(VALUE)0) return r; /* coerced by other */ + SAVE(c);SAVE(res);SAVE(div); + /* a/b = c + r/b */ + /* c xxxxx + r 00000yyyyy ==> (y/b)*BASE >= HALF_BASE + */ + /* Round */ + if(VpHasVal(div)) { /* frac[0] must be zero for NaN,INF,Zero */ + VpInternalRound(c,0,c->frac[c->Prec-1],(VpBaseVal()*res->frac[0])/div->frac[0]); + } + return ToValue(c); +} + +/* + * %: mod = a%b = a - (a.to_f/b).floor * b + * div = (a.to_f/b).floor + */ +static VALUE +BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod) +{ + ENTER(8); + Real *c=NULL, *d=NULL, *res=NULL; + Real *a, *b; + U_LONG mx; + + GUARD_OBJ(a,GetVpValue(self,1)); + b = GetVpValue(r,0); + if(!b) return DoSomeOne(self,r); + SAVE(b); + + if(VpIsNaN(a) || VpIsNaN(b)) goto NaN; + if(VpIsInf(a) || VpIsInf(b)) goto NaN; + if(VpIsZero(b)) goto NaN; + if(VpIsZero(a)) { + GUARD_OBJ(c,VpCreateRbObject(1, "0")); + GUARD_OBJ(d,VpCreateRbObject(1, "0")); + *div = d; + *mod = c; + return (VALUE)0; + } + + mx = a->Prec; + if(mx<b->Prec) mx = b->Prec; + mx =(mx + 1) * VpBaseFig(); + GUARD_OBJ(c,VpCreateRbObject(mx, "0")); + GUARD_OBJ(res,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0")); + VpDivd(c, res, a, b); + mx = c->Prec *(VpBaseFig() + 1); + GUARD_OBJ(d,VpCreateRbObject(mx, "0")); + VpActiveRound(d,c,VP_ROUND_DOWN,0); + VpMult(res,d,b); + VpAddSub(c,a,res,-1); + if(!VpIsZero(c) && (VpGetSign(a)*VpGetSign(b)<0)) { + VpAddSub(res,d,VpOne(),-1); + VpAddSub(d ,c,b, 1); + *div = res; + *mod = d; + } else { + *div = d; + *mod = c; + } + return (VALUE)0; + +NaN: + GUARD_OBJ(c,VpCreateRbObject(1, "NaN")); + GUARD_OBJ(d,VpCreateRbObject(1, "NaN")); + *div = d; + *mod = c; + return (VALUE)0; +} + +static VALUE +BigDecimal_mod(VALUE self, VALUE r) /* %: a%b = a - (a.to_f/b).floor * b */ +{ + ENTER(3); + VALUE obj; + Real *div=NULL, *mod=NULL; + + obj = BigDecimal_DoDivmod(self,r,&div,&mod); + if(obj!=(VALUE)0) return obj; + SAVE(div);SAVE(mod); + return ToValue(mod); +} + +static VALUE +BigDecimal_divremain(VALUE self, VALUE r, Real **dv, Real **rv) +{ + ENTER(10); + U_LONG mx; + Real *a=NULL, *b=NULL, *c=NULL, *res=NULL, *d=NULL, *rr=NULL, *ff=NULL; + Real *f=NULL; + + GUARD_OBJ(a,GetVpValue(self,1)); + b = GetVpValue(r,0); + if(!b) return DoSomeOne(self,r); + SAVE(b); + + mx =(a->MaxPrec + b->MaxPrec) *VpBaseFig(); + GUARD_OBJ(c ,VpCreateRbObject(mx, "0")); + GUARD_OBJ(res,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0")); + GUARD_OBJ(rr ,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0")); + GUARD_OBJ(ff ,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0")); + + VpDivd(c, res, a, b); + + mx = c->Prec *(VpBaseFig() + 1); + + GUARD_OBJ(d,VpCreateRbObject(mx, "0")); + GUARD_OBJ(f,VpCreateRbObject(mx, "0")); + + VpActiveRound(d,c,VP_ROUND_DOWN,0); /* 0: round off */ + + VpFrac(f, c); + VpMult(rr,f,b); + VpAddSub(ff,res,rr,1); + + *dv = d; + *rv = ff; + return (VALUE)0; +} + +static VALUE +BigDecimal_remainder(VALUE self, VALUE r) /* remainder */ +{ + VALUE f; + Real *d,*rv; + f = BigDecimal_divremain(self,r,&d,&rv); + if(f!=(VALUE)0) return f; + return ToValue(rv); +} + +static VALUE +BigDecimal_divmod(VALUE self, VALUE r) +{ + ENTER(5); + VALUE obj; + Real *div=NULL, *mod=NULL; + + obj = BigDecimal_DoDivmod(self,r,&div,&mod); + if(obj!=(VALUE)0) return obj; + SAVE(div);SAVE(mod); + obj = rb_assoc_new(ToValue(div), ToValue(mod)); + return obj; +} + +static VALUE +BigDecimal_div2(int argc, VALUE *argv, VALUE self) +{ + ENTER(5); + VALUE b,n; + int na = rb_scan_args(argc,argv,"11",&b,&n); + if(na==1) { /* div in Float sense */ + VALUE obj; + Real *div=NULL; + Real *mod; + obj = BigDecimal_DoDivmod(self,b,&div,&mod); + if(obj!=(VALUE)0) return obj; + return ToValue(div); + } else { /* div in BigDecimal sense */ + U_LONG ix = (U_LONG)GetPositiveInt(n); + if(ix==0) return BigDecimal_div(self,b); + else { + Real *res=NULL; + Real *av=NULL, *bv=NULL, *cv=NULL; + U_LONG mx = (ix+VpBaseFig()*2); + U_LONG pl = VpSetPrecLimit(0); + + GUARD_OBJ(cv,VpCreateRbObject(mx,"0")); + GUARD_OBJ(av,GetVpValue(self,1)); + GUARD_OBJ(bv,GetVpValue(b,1)); + mx = av->Prec + bv->Prec + 2; + if(mx <= cv->MaxPrec) mx = cv->MaxPrec+1; + GUARD_OBJ(res,VpCreateRbObject((mx * 2 + 2)*VpBaseFig(), "#0")); + VpDivd(cv,res,av,bv); + VpSetPrecLimit(pl); + VpLeftRound(cv,VpGetRoundMode(),ix); + return ToValue(cv); + } + } +} + +static VALUE +BigDecimal_add2(VALUE self, VALUE b, VALUE n) +{ + ENTER(2); + Real *cv; + U_LONG mx = (U_LONG)GetPositiveInt(n); + if(mx==0) return BigDecimal_add(self,b); + else { + U_LONG pl = VpSetPrecLimit(0); + VALUE c = BigDecimal_add(self,b); + VpSetPrecLimit(pl); + GUARD_OBJ(cv,GetVpValue(c,1)); + VpLeftRound(cv,VpGetRoundMode(),mx); + return ToValue(cv); + } +} + +static VALUE +BigDecimal_sub2(VALUE self, VALUE b, VALUE n) +{ + ENTER(2); + Real *cv; + U_LONG mx = (U_LONG)GetPositiveInt(n); + if(mx==0) return BigDecimal_sub(self,b); + else { + U_LONG pl = VpSetPrecLimit(0); + VALUE c = BigDecimal_sub(self,b); + VpSetPrecLimit(pl); + GUARD_OBJ(cv,GetVpValue(c,1)); + VpLeftRound(cv,VpGetRoundMode(),mx); + return ToValue(cv); + } +} + +static VALUE +BigDecimal_mult2(VALUE self, VALUE b, VALUE n) +{ + ENTER(2); + Real *cv; + U_LONG mx = (U_LONG)GetPositiveInt(n); + if(mx==0) return BigDecimal_mult(self,b); + else { + U_LONG pl = VpSetPrecLimit(0); + VALUE c = BigDecimal_mult(self,b); + VpSetPrecLimit(pl); + GUARD_OBJ(cv,GetVpValue(c,1)); + VpLeftRound(cv,VpGetRoundMode(),mx); + return ToValue(cv); + } +} + +static VALUE +BigDecimal_abs(VALUE self) +{ + ENTER(5); + Real *c, *a; + U_LONG mx; + + GUARD_OBJ(a,GetVpValue(self,1)); + mx = a->Prec *(VpBaseFig() + 1); + GUARD_OBJ(c,VpCreateRbObject(mx, "0")); + VpAsgn(c, a, 1); + VpChangeSign(c,(S_INT)1); + return ToValue(c); +} + +static VALUE +BigDecimal_sqrt(VALUE self, VALUE nFig) +{ + ENTER(5); + Real *c, *a; + S_INT mx, n; + + GUARD_OBJ(a,GetVpValue(self,1)); + mx = a->Prec *(VpBaseFig() + 1); + + n = GetPositiveInt(nFig) + VpDblFig() + 1; + if(mx <= n) mx = n; + GUARD_OBJ(c,VpCreateRbObject(mx, "0")); + VpSqrt(c, a); + return ToValue(c); +} + +static VALUE +BigDecimal_fix(VALUE self) +{ + ENTER(5); + Real *c, *a; + U_LONG mx; + + GUARD_OBJ(a,GetVpValue(self,1)); + mx = a->Prec *(VpBaseFig() + 1); + GUARD_OBJ(c,VpCreateRbObject(mx, "0")); + VpActiveRound(c,a,VP_ROUND_DOWN,0); /* 0: round off */ + return ToValue(c); +} + +static VALUE +BigDecimal_round(int argc, VALUE *argv, VALUE self) +{ + ENTER(5); + Real *c, *a; + int iLoc; + U_LONG mx; + VALUE vLoc; + VALUE vRound; + U_LONG pl; + + int sw = VpGetRoundMode(); + + int na = rb_scan_args(argc,argv,"02",&vLoc,&vRound); + switch(na) { + case 0: + iLoc = 0; + break; + case 1: + Check_Type(vLoc, T_FIXNUM); + iLoc = FIX2INT(vLoc); + break; + case 2: + Check_Type(vLoc, T_FIXNUM); + iLoc = FIX2INT(vLoc); + Check_Type(vRound, T_FIXNUM); + sw = FIX2INT(vRound); + if(!VpIsRoundMode(sw)) { + rb_raise(rb_eTypeError, "Invalid rounding mode."); + return Qnil; + } + break; + } + + pl = VpSetPrecLimit(0); + GUARD_OBJ(a,GetVpValue(self,1)); + mx = a->Prec *(VpBaseFig() + 1); + GUARD_OBJ(c,VpCreateRbObject(mx, "0")); + VpSetPrecLimit(pl); + VpActiveRound(c,a,sw,iLoc); + return ToValue(c); +} + +static VALUE +BigDecimal_truncate(int argc, VALUE *argv, VALUE self) +{ + ENTER(5); + Real *c, *a; + int iLoc; + U_LONG mx; + VALUE vLoc; + U_LONG pl = VpSetPrecLimit(0); + + if(rb_scan_args(argc,argv,"01",&vLoc)==0) { + iLoc = 0; + } else { + Check_Type(vLoc, T_FIXNUM); + iLoc = FIX2INT(vLoc); + } + + GUARD_OBJ(a,GetVpValue(self,1)); + mx = a->Prec *(VpBaseFig() + 1); + GUARD_OBJ(c,VpCreateRbObject(mx, "0")); + VpSetPrecLimit(pl); + VpActiveRound(c,a,VP_ROUND_DOWN,iLoc); /* 0: truncate */ + return ToValue(c); +} + +static VALUE +BigDecimal_frac(VALUE self) +{ + ENTER(5); + Real *c, *a; + U_LONG mx; + + GUARD_OBJ(a,GetVpValue(self,1)); + mx = a->Prec *(VpBaseFig() + 1); + GUARD_OBJ(c,VpCreateRbObject(mx, "0")); + VpFrac(c, a); + return ToValue(c); +} + +static VALUE +BigDecimal_floor(int argc, VALUE *argv, VALUE self) +{ + ENTER(5); + Real *c, *a; + U_LONG mx; + int iLoc; + VALUE vLoc; + U_LONG pl = VpSetPrecLimit(0); + + if(rb_scan_args(argc,argv,"01",&vLoc)==0) { + iLoc = 0; + } else { + Check_Type(vLoc, T_FIXNUM); + iLoc = FIX2INT(vLoc); + } + + GUARD_OBJ(a,GetVpValue(self,1)); + mx = a->Prec *(VpBaseFig() + 1); + GUARD_OBJ(c,VpCreateRbObject(mx, "0")); + VpSetPrecLimit(pl); + VpActiveRound(c,a,VP_ROUND_FLOOR,iLoc); + return ToValue(c); +} + +static VALUE +BigDecimal_ceil(int argc, VALUE *argv, VALUE self) +{ + ENTER(5); + Real *c, *a; + U_LONG mx; + int iLoc; + VALUE vLoc; + U_LONG pl = VpSetPrecLimit(0); + + if(rb_scan_args(argc,argv,"01",&vLoc)==0) { + iLoc = 0; + } else { + Check_Type(vLoc, T_FIXNUM); + iLoc = FIX2INT(vLoc); + } + + GUARD_OBJ(a,GetVpValue(self,1)); + mx = a->Prec *(VpBaseFig() + 1); + GUARD_OBJ(c,VpCreateRbObject(mx, "0")); + VpSetPrecLimit(pl); + VpActiveRound(c,a,VP_ROUND_CEIL,iLoc); + return ToValue(c); +} + +static VALUE +BigDecimal_to_s(int argc, VALUE *argv, VALUE self) +{ + ENTER(5); + int fmt=0; /* 0:E format */ + int fPlus=0; /* =0:default,=1: set ' ' before digits ,set '+' before digits. */ + Real *vp; + char *psz; + char ch; + U_LONG nc; + S_INT mc = 0; + VALUE f; + + GUARD_OBJ(vp,GetVpValue(self,1)); + + if(rb_scan_args(argc,argv,"01",&f)==1) { + if(TYPE(f)==T_STRING) { + SafeStringValue(f); + psz = RSTRING(f)->ptr; + if(*psz==' ') { + fPlus = 1; psz++; + } else if(*psz=='+') { + fPlus = 2; psz++; + } + while(ch=*psz++) { + if(ISSPACE(ch)) continue; + if(!ISDIGIT(ch)) { + if(ch=='F' || ch=='f') fmt = 1; /* F format */ + break; + } + mc = mc * 10 + ch - '0'; + } + } else { + mc = GetPositiveInt(f); + } + } + if(fmt) { + nc = VpNumOfChars(vp,"F"); + } else { + nc = VpNumOfChars(vp,"E"); + } + if(mc>0) nc += (nc + mc - 1) / mc + 1; + + psz = ALLOCA_N(char,(unsigned int)nc); + + if(fmt) { + VpToFString(vp, psz, mc, fPlus); + } else { + VpToString (vp, psz, mc, fPlus); + } + return rb_str_new2(psz); +} + +static VALUE +BigDecimal_split(VALUE self) +{ + ENTER(5); + Real *vp; + VALUE obj,obj1; + S_LONG e; + S_LONG s; + char *psz1; + + GUARD_OBJ(vp,GetVpValue(self,1)); + psz1 = ALLOCA_N(char,(unsigned int)VpNumOfChars(vp,"E")); + VpSzMantissa(vp,psz1); + s = 1; + if(psz1[0]=='-') { + s = -1; ++psz1; + } + if(psz1[0]=='N') s=0; /* NaN */ + e = VpExponent10(vp); + obj1 = rb_str_new2(psz1); + obj = rb_ary_new2(4); + rb_ary_push(obj, INT2FIX(s)); + rb_ary_push(obj, obj1); + rb_ary_push(obj, INT2FIX(10)); + rb_ary_push(obj, INT2NUM(e)); + return obj; +} + +static VALUE +BigDecimal_exponent(VALUE self) +{ + S_LONG e = VpExponent10(GetVpValue(self,1)); + return INT2NUM(e); +} + +static VALUE +BigDecimal_inspect(VALUE self) +{ + ENTER(5); + Real *vp; + VALUE obj; + unsigned int nc; + char *psz1; + char *pszAll; + + GUARD_OBJ(vp,GetVpValue(self,1)); + nc = VpNumOfChars(vp,"E"); + nc +=(nc + 9) / 10; + + psz1 = ALLOCA_N(char,nc); + pszAll = ALLOCA_N(char,nc+256); + VpToString(vp, psz1, 10, 0); + sprintf(pszAll,"#<BigDecimal:%lx,'%s',%lu(%lu)>",self,psz1,VpPrec(vp)*VpBaseFig(),VpMaxPrec(vp)*VpBaseFig()); + obj = rb_str_new2(pszAll); + return obj; +} + +static VALUE +BigDecimal_power(VALUE self, VALUE p) +{ + ENTER(5); + Real *x, *y; + S_LONG mp, ma, n; + + Check_Type(p, T_FIXNUM); + n = FIX2INT(p); + ma = n; + if(ma < 0) ma = -ma; + if(ma == 0) ma = 1; + + GUARD_OBJ(x,GetVpValue(self,1)); + if(VpIsDef(x)) { + mp = x->Prec *(VpBaseFig() + 1); + GUARD_OBJ(y,VpCreateRbObject(mp *(ma + 1), "0")); + } else { + GUARD_OBJ(y,VpCreateRbObject(1, "0")); + } + VpPower(y, x, n); + return ToValue(y); +} + +static VALUE +BigDecimal_global_new(int argc, VALUE *argv, VALUE self) +{ + ENTER(5); + Real *pv; + S_LONG mf; + VALUE nFig; + VALUE iniValue; + + if(rb_scan_args(argc,argv,"11",&iniValue,&nFig)==1) { + mf = 0; + } else { + mf = GetPositiveInt(nFig); + } + SafeStringValue(iniValue); + GUARD_OBJ(pv,VpCreateRbObject(mf, RSTRING(iniValue)->ptr)); + return ToValue(pv); +} + +static VALUE +BigDecimal_new(int argc, VALUE *argv, VALUE self) +{ + ENTER(5); + Real *pv; + S_LONG mf; + VALUE nFig; + VALUE iniValue; + + if(rb_scan_args(argc,argv,"11",&iniValue,&nFig)==1) { + mf = 0; + } else { + mf = GetPositiveInt(nFig); + } + SafeStringValue(iniValue); + GUARD_OBJ(pv,VpNewRbClass(mf, RSTRING(iniValue)->ptr,self)); + return ToValue(pv); +} + +static VALUE +BigDecimal_limit(int argc, VALUE *argv, VALUE self) +{ + VALUE nFig; + VALUE nCur = INT2NUM(VpGetPrecLimit()); + + if(rb_scan_args(argc,argv,"01",&nFig)==1) { + int nf; + if(nFig==Qnil) return nCur; + Check_Type(nFig, T_FIXNUM); + nf = FIX2INT(nFig); + if(nf<0) { + rb_raise(rb_eArgError, "argument must be positive"); + } + VpSetPrecLimit(nf); + } + return nCur; +} + +static VALUE +BigDecimal_sign(VALUE self) +{ /* sign */ + int s = GetVpValue(self,1)->sign; + return INT2FIX(s); +} + +void +Init_bigdecimal(void) +{ + /* Initialize VP routines */ + VpInit((U_LONG)0); + + /* Class and method registration */ + rb_cBigDecimal = rb_define_class("BigDecimal",rb_cNumeric); + + /* Global function */ + rb_define_global_function("BigDecimal", BigDecimal_global_new, -1); + + /* Class methods */ + rb_define_singleton_method(rb_cBigDecimal, "new", BigDecimal_new, -1); + rb_define_singleton_method(rb_cBigDecimal, "mode", BigDecimal_mode, -1); + rb_define_singleton_method(rb_cBigDecimal, "limit", BigDecimal_limit, -1); + rb_define_singleton_method(rb_cBigDecimal, "double_fig", BigDecimal_double_fig, 0); + rb_define_singleton_method(rb_cBigDecimal, "induced_from",BigDecimal_induced_from, 1); + rb_define_singleton_method(rb_cBigDecimal, "_load", BigDecimal_load, 1); + rb_define_singleton_method(rb_cBigDecimal, "ver", BigDecimal_version, 0); + + /* Constants definition */ + rb_define_const(rb_cBigDecimal, "BASE", INT2FIX((S_INT)VpBaseVal())); + + /* Exceptions */ + rb_define_const(rb_cBigDecimal, "EXCEPTION_ALL",INT2FIX(VP_EXCEPTION_ALL)); + rb_define_const(rb_cBigDecimal, "EXCEPTION_NaN",INT2FIX(VP_EXCEPTION_NaN)); + rb_define_const(rb_cBigDecimal, "EXCEPTION_INFINITY",INT2FIX(VP_EXCEPTION_INFINITY)); + rb_define_const(rb_cBigDecimal, "EXCEPTION_UNDERFLOW",INT2FIX(VP_EXCEPTION_UNDERFLOW)); + rb_define_const(rb_cBigDecimal, "EXCEPTION_OVERFLOW",INT2FIX(VP_EXCEPTION_OVERFLOW)); + rb_define_const(rb_cBigDecimal, "EXCEPTION_ZERODIVIDE",INT2FIX(VP_EXCEPTION_ZERODIVIDE)); + + /* Computation mode */ + rb_define_const(rb_cBigDecimal, "ROUND_MODE",INT2FIX(VP_ROUND_MODE)); + rb_define_const(rb_cBigDecimal, "ROUND_UP",INT2FIX(VP_ROUND_UP)); + rb_define_const(rb_cBigDecimal, "ROUND_DOWN",INT2FIX(VP_ROUND_DOWN)); + rb_define_const(rb_cBigDecimal, "ROUND_HALF_UP",INT2FIX(VP_ROUND_HALF_UP)); + rb_define_const(rb_cBigDecimal, "ROUND_HALF_DOWN",INT2FIX(VP_ROUND_HALF_DOWN)); + rb_define_const(rb_cBigDecimal, "ROUND_CEILING",INT2FIX(VP_ROUND_CEIL)); + rb_define_const(rb_cBigDecimal, "ROUND_FLOOR",INT2FIX(VP_ROUND_FLOOR)); + rb_define_const(rb_cBigDecimal, "ROUND_HALF_EVEN",INT2FIX(VP_ROUND_HALF_EVEN)); + + /* Constants for sign value */ + rb_define_const(rb_cBigDecimal, "SIGN_NaN",INT2FIX(VP_SIGN_NaN)); + rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_ZERO",INT2FIX(VP_SIGN_POSITIVE_ZERO)); + rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_ZERO",INT2FIX(VP_SIGN_NEGATIVE_ZERO)); + rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_FINITE",INT2FIX(VP_SIGN_POSITIVE_FINITE)); + rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_FINITE",INT2FIX(VP_SIGN_NEGATIVE_FINITE)); + rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_INFINITE",INT2FIX(VP_SIGN_POSITIVE_INFINITE)); + rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_INFINITE",INT2FIX(VP_SIGN_NEGATIVE_INFINITE)); + + /* instance methods */ + rb_define_method(rb_cBigDecimal, "precs", BigDecimal_prec, 0); + rb_define_method(rb_cBigDecimal, "add", BigDecimal_add2, 2); + rb_define_method(rb_cBigDecimal, "sub", BigDecimal_sub2, 2); + rb_define_method(rb_cBigDecimal, "mult", BigDecimal_mult2, 2); + rb_define_method(rb_cBigDecimal, "div",BigDecimal_div2, -1); + rb_define_method(rb_cBigDecimal, "hash", BigDecimal_hash, 0); + rb_define_method(rb_cBigDecimal, "to_s", BigDecimal_to_s, -1); + rb_define_method(rb_cBigDecimal, "to_i", BigDecimal_to_i, 0); + rb_define_method(rb_cBigDecimal, "to_int", BigDecimal_to_i, 0); + rb_define_method(rb_cBigDecimal, "split", BigDecimal_split, 0); + rb_define_method(rb_cBigDecimal, "+", BigDecimal_add, 1); + rb_define_method(rb_cBigDecimal, "-", BigDecimal_sub, 1); + rb_define_method(rb_cBigDecimal, "+@", BigDecimal_uplus, 0); + rb_define_method(rb_cBigDecimal, "-@", BigDecimal_neg, 0); + rb_define_method(rb_cBigDecimal, "*", BigDecimal_mult, 1); + rb_define_method(rb_cBigDecimal, "/", BigDecimal_div, 1); + rb_define_method(rb_cBigDecimal, "quo", BigDecimal_div, 1); + rb_define_method(rb_cBigDecimal, "%", BigDecimal_mod, 1); + rb_define_method(rb_cBigDecimal, "modulo", BigDecimal_mod, 1); + rb_define_method(rb_cBigDecimal, "remainder", BigDecimal_remainder, 1); + rb_define_method(rb_cBigDecimal, "divmod", BigDecimal_divmod, 1); + /* rb_define_method(rb_cBigDecimal, "dup", BigDecimal_dup, 0); */ + rb_define_method(rb_cBigDecimal, "to_f", BigDecimal_to_f, 0); + rb_define_method(rb_cBigDecimal, "abs", BigDecimal_abs, 0); + rb_define_method(rb_cBigDecimal, "sqrt", BigDecimal_sqrt, 1); + rb_define_method(rb_cBigDecimal, "fix", BigDecimal_fix, 0); + rb_define_method(rb_cBigDecimal, "round", BigDecimal_round, -1); + rb_define_method(rb_cBigDecimal, "frac", BigDecimal_frac, 0); + rb_define_method(rb_cBigDecimal, "floor", BigDecimal_floor, -1); + rb_define_method(rb_cBigDecimal, "ceil", BigDecimal_ceil, -1); + rb_define_method(rb_cBigDecimal, "power", BigDecimal_power, 1); + rb_define_method(rb_cBigDecimal, "**", BigDecimal_power, 1); + rb_define_method(rb_cBigDecimal, "<=>", BigDecimal_comp, 1); + rb_define_method(rb_cBigDecimal, "==", BigDecimal_eq, 1); + rb_define_method(rb_cBigDecimal, "===", BigDecimal_eq, 1); + rb_define_method(rb_cBigDecimal, "eql?", BigDecimal_eq, 1); + rb_define_method(rb_cBigDecimal, "!=", BigDecimal_ne, 1); + rb_define_method(rb_cBigDecimal, "<", BigDecimal_lt, 1); + rb_define_method(rb_cBigDecimal, "<=", BigDecimal_le, 1); + rb_define_method(rb_cBigDecimal, ">", BigDecimal_gt, 1); + rb_define_method(rb_cBigDecimal, ">=", BigDecimal_ge, 1); + rb_define_method(rb_cBigDecimal, "zero?", BigDecimal_zero, 0); + rb_define_method(rb_cBigDecimal, "nonzero?", BigDecimal_nonzero, 0); + rb_define_method(rb_cBigDecimal, "coerce", BigDecimal_coerce, 1); + rb_define_method(rb_cBigDecimal, "inspect", BigDecimal_inspect, 0); + rb_define_method(rb_cBigDecimal, "exponent", BigDecimal_exponent, 0); + rb_define_method(rb_cBigDecimal, "sign", BigDecimal_sign, 0); + rb_define_method(rb_cBigDecimal, "nan?", BigDecimal_IsNaN, 0); + rb_define_method(rb_cBigDecimal, "infinite?", BigDecimal_IsInfinite, 0); + rb_define_method(rb_cBigDecimal, "finite?", BigDecimal_IsFinite, 0); + rb_define_method(rb_cBigDecimal, "truncate", BigDecimal_truncate, -1); + rb_define_method(rb_cBigDecimal, "_dump", BigDecimal_dump, -1); +} + +/* + * + * ============================================================================ + * + * vp_ routines begin from here. + * + * ============================================================================ + * + */ +#ifdef _DEBUG +/*static int gfDebug = 1;*/ /* Debug switch */ +static int gfCheckVal = 1; /* Value checking flag in VpNmlz() */ +#endif /* _DEBUG */ + +static U_LONG gnPrecLimit = 0; /* Global upper limit of the precision newly allocated */ +static U_LONG gfRoundMode = VP_ROUND_HALF_UP; /* Mode for general rounding operation */ + +static U_LONG BASE_FIG = 4; /* =log10(BASE) */ +static U_LONG BASE = 10000L; /* Base value(value must be 10**BASE_FIG) */ + /* The value of BASE**2 + BASE must be represented */ + /* within one U_LONG. */ +static U_LONG HALF_BASE = 5000L;/* =BASE/2 */ +static S_LONG DBLE_FIG = 8; /* figure of double */ +static U_LONG BASE1 = 1000L; /* =BASE/10 */ + +static Real *VpConstOne; /* constant 1.0 */ +static Real *VpPt5; /* constant 0.5 */ +static U_LONG maxnr = 100; /* Maximum iterations for calcurating sqrt. */ + /* used in VpSqrt() */ + +/* ETC */ +#define MemCmp(x,y,z) memcmp(x,y,z) +#define StrCmp(x,y) strcmp(x,y) + +static int VpIsDefOP(Real *c,Real *a,Real *b,int sw); +static int AddExponent(Real *a,S_INT n); +static U_LONG VpAddAbs(Real *a,Real *b,Real *c); +static U_LONG VpSubAbs(Real *a,Real *b,Real *c); +static U_LONG VpSetPTR(Real *a,Real *b,Real *c,U_LONG *a_pos,U_LONG *b_pos,U_LONG *c_pos,U_LONG *av,U_LONG *bv); +static int VpNmlz(Real *a); +static void VpFormatSt(char *psz,S_INT fFmt); +static int VpRdup(Real *m,U_LONG ind_m); + +#ifdef _DEBUG +static int gnAlloc=0; /* Memory allocation counter */ +#endif /* _DEBUG */ + +VP_EXPORT void * +VpMemAlloc(U_LONG mb) +{ + void *p = xmalloc((unsigned int)mb); + if(!p) { + VpException(VP_EXCEPTION_MEMORY,"failed to allocate memory",1); + } + memset(p,0,mb); +#ifdef _DEBUG + gnAlloc++; /* Count allocation call */ +#endif /* _DEBUG */ + return p; +} + +VP_EXPORT void +VpFree(Real *pv) +{ + if(pv != NULL) { + xfree(pv); +#ifdef _DEBUG + gnAlloc--; /* Decrement allocation count */ + if(gnAlloc==0) { + printf(" *************** All memories allocated freed ****************"); + getchar(); + } + if(gnAlloc<0) { + printf(" ??????????? Too many memory free calls(%d) ?????????????\n",gnAlloc); + getchar(); + } +#endif /* _DEBUG */ + } +} + +/* + * EXCEPTION Handling. + */ +static unsigned short gfDoException = 0; /* Exception flag */ + +static unsigned short +VpGetException (void) +{ + return gfDoException; +} + +static void +VpSetException(unsigned short f) +{ + gfDoException = f; +} + +/* These 2 functions added at v1.1.7 */ +VP_EXPORT U_LONG +VpGetPrecLimit(void) +{ + return gnPrecLimit; +} + +VP_EXPORT U_LONG +VpSetPrecLimit(U_LONG n) +{ + U_LONG s = gnPrecLimit; + gnPrecLimit = n; + return s; +} + +VP_EXPORT unsigned long +VpGetRoundMode(void) +{ + return gfRoundMode; +} + +VP_EXPORT int +VpIsRoundMode(unsigned long n) +{ + if(n==VP_ROUND_UP || n!=VP_ROUND_DOWN || + n==VP_ROUND_HALF_UP || n!=VP_ROUND_HALF_DOWN || + n==VP_ROUND_CEIL || n!=VP_ROUND_FLOOR || + n==VP_ROUND_HALF_EVEN + ) return 1; + return 0; +} + +VP_EXPORT unsigned long +VpSetRoundMode(unsigned long n) +{ + if(VpIsRoundMode(n)) gfRoundMode = n; + return gfRoundMode; +} + +/* + * 0.0 & 1.0 generator + * These gZero_..... and gOne_..... can be any name + * referenced from nowhere except Zero() and One(). + * gZero_..... and gOne_..... must have global scope + * (to let the compiler know they may be changed in outside + * (... but not actually..)). + */ +volatile double gZero_ABCED9B1_CE73__00400511F31D = 0.0; +volatile double gOne_ABCED9B4_CE73__00400511F31D = 1.0; +static double +Zero(void) +{ + return gZero_ABCED9B1_CE73__00400511F31D; +} + +static double +One(void) +{ + return gOne_ABCED9B4_CE73__00400511F31D; +} + +VP_EXPORT U_LONG +VpBaseFig(void) +{ + return BASE_FIG; +} + +VP_EXPORT U_LONG +VpDblFig(void) +{ + return DBLE_FIG; +} + +VP_EXPORT U_LONG +VpBaseVal(void) +{ + return BASE; +} + +/* + ---------------------------------------------------------------- + Value of sign in Real structure is reserved for future use. + short sign; + ==0 : NaN + 1 : Positive zero + -1 : Negative zero + 2 : Positive number + -2 : Negative number + 3 : Positive infinite number + -3 : Negative infinite number + ---------------------------------------------------------------- +*/ + +VP_EXPORT double +VpGetDoubleNaN(void) /* Returns the value of NaN */ +{ + static double fNaN = 0.0; + if(fNaN==0.0) fNaN = Zero()/Zero(); + return fNaN; +} + +VP_EXPORT double +VpGetDoublePosInf(void) /* Returns the value of +Infinity */ +{ + static double fInf = 0.0; + if(fInf==0.0) fInf = One()/Zero(); + return fInf; +} + +VP_EXPORT double +VpGetDoubleNegInf(void) /* Returns the value of -Infinity */ +{ + static double fInf = 0.0; + if(fInf==0.0) fInf = -(One()/Zero()); + return fInf; +} + +VP_EXPORT double +VpGetDoubleNegZero(void) /* Returns the value of -0 */ +{ + static double nzero = 1000.0; + if(nzero!=0.0) nzero = (One()/VpGetDoubleNegInf()); + return nzero; +} + +VP_EXPORT int +VpIsNegDoubleZero(double v) +{ + double z = VpGetDoubleNegZero(); + return MemCmp(&v,&z,sizeof(v))==0; +} + +VP_EXPORT int +VpException(unsigned short f,char *str,int always) +{ + VALUE exc; + int fatal=0; + + if(f==VP_EXCEPTION_OP || f==VP_EXCEPTION_MEMORY) always = 1; + + if(always||(gfDoException&f)) { + switch(f) + { + /* + case VP_EXCEPTION_ZERODIVIDE: + case VP_EXCEPTION_OVERFLOW: + */ + case VP_EXCEPTION_INFINITY: + exc = rb_eFloatDomainError; + goto raise; + case VP_EXCEPTION_NaN: + exc = rb_eFloatDomainError; + goto raise; + case VP_EXCEPTION_UNDERFLOW: + exc = rb_eFloatDomainError; + goto raise; + case VP_EXCEPTION_OP: + exc = rb_eFloatDomainError; + goto raise; + case VP_EXCEPTION_MEMORY: + fatal = 1; + goto raise; + default: + fatal = 1; + goto raise; + } + } + return 0; /* 0 Means VpException() raised no exception */ + +raise: + if(fatal) rb_fatal(str); + else rb_raise(exc,str); + return 0; +} + +/* Throw exception or returns 0,when resulting c is Inf or NaN */ +/* sw=1:+ 2:- 3:* 4:/ */ +static int +VpIsDefOP(Real *c,Real *a,Real *b,int sw) +{ + if(VpIsNaN(a) || VpIsNaN(b)) { + /* at least a or b is NaN */ + VpSetNaN(c); + goto NaN; + } + + if(VpIsInf(a)) { + if(VpIsInf(b)) { + switch(sw) + { + case 1: /* + */ + if(VpGetSign(a)==VpGetSign(b)) { + VpSetInf(c,VpGetSign(a)); + goto Inf; + } else { + VpSetNaN(c); + goto NaN; + } + case 2: /* - */ + if(VpGetSign(a)!=VpGetSign(b)) { + VpSetInf(c,VpGetSign(a)); + goto Inf; + } else { + VpSetNaN(c); + goto NaN; + } + break; + case 3: /* * */ + VpSetInf(c,VpGetSign(a)*VpGetSign(b)); + goto Inf; + break; + case 4: /* / */ + VpSetNaN(c); + goto NaN; + } + VpSetNaN(c); + goto NaN; + } + /* Inf op Finite */ + switch(sw) + { + case 1: /* + */ + case 2: /* - */ + VpSetInf(c,VpGetSign(a)); + break; + case 3: /* * */ + if(VpIsZero(b)) { + VpSetNaN(c); + goto NaN; + } + VpSetInf(c,VpGetSign(a)*VpGetSign(b)); + break; + case 4: /* / */ + VpSetInf(c,VpGetSign(a)*VpGetSign(b)); + } + goto Inf; + } + + if(VpIsInf(b)) { + switch(sw) + { + case 1: /* + */ + VpSetInf(c,VpGetSign(b)); + break; + case 2: /* - */ + VpSetInf(c,-VpGetSign(b)); + break; + case 3: /* * */ + if(VpIsZero(a)) { + VpSetNaN(c); + goto NaN; + } + VpSetInf(c,VpGetSign(a)*VpGetSign(b)); + break; + case 4: /* / */ + VpSetZero(c,VpGetSign(a)*VpGetSign(b)); + } + goto Inf; + } + return 1; /* Results OK */ + +Inf: + return VpException(VP_EXCEPTION_INFINITY,"Computation results to 'Infinity'",0); +NaN: + return VpException(VP_EXCEPTION_NaN,"Computation results to 'NaN'",0); +} + +/* + ---------------------------------------------------------------- +*/ + +/* + * returns number of chars needed to represent vp in specified format. + */ +VP_EXPORT U_LONG +VpNumOfChars(Real *vp,char *pszFmt) +{ + S_INT ex; + U_LONG nc; + + if(vp == NULL) return BASE_FIG*2+6; + if(!VpIsDef(vp)) return 32; /* not sure,may be OK */ + + switch(*pszFmt) + { + case 'F': + nc = BASE_FIG*(vp->Prec + 1)+2; + ex = vp->exponent; + if(ex<0) { + nc += BASE_FIG*(-ex); + } else { + if(ex > (S_INT)vp->Prec) { + nc += BASE_FIG*(ex - (S_INT)vp->Prec); + } + } + break; + case 'E': + default: + nc = BASE_FIG*(vp->Prec + 2)+6; /* 3: sign + exponent chars */ + } + return nc; +} + +/* + * Initializer for Vp routines and constants used. + * [Input] + * BaseVal: Base value(assigned to BASE) for Vp calculation. + * It must be the form BaseVal=10**n.(n=1,2,3,...) + * If Base <= 0L,then the BASE will be calcurated so + * that BASE is as large as possible satisfying the + * relation MaxVal <= BASE*(BASE+1). Where the value + * MaxVal is the largest value which can be represented + * by one U_LONG word(LONG) in the computer used. + * + * [Returns] + * DBLE_FIG ... OK + */ +VP_EXPORT U_LONG +VpInit(U_LONG BaseVal) +{ + U_LONG w; + double v; + + /* Setup +/- Inf NaN -0 */ + VpGetDoubleNaN(); + VpGetDoublePosInf(); + VpGetDoubleNegInf(); + VpGetDoubleNegZero(); + + if(BaseVal <= 0) { + /* Base <= 0, then determine Base by calcuration. */ + BASE = 1; + while( + (BASE > 0) && + ((w = BASE *(BASE + 1)) > BASE) &&((w / BASE) ==(BASE + 1)) + ) { + BaseVal = BASE; + BASE = BaseVal * 10L; + } + } + /* Set Base Values */ + BASE = BaseVal; + HALF_BASE = BASE / 2; + BASE1 = BASE / 10; + BASE_FIG = 0; + while(BaseVal /= 10) ++BASE_FIG; + /* Allocates Vp constants. */ + VpConstOne = VpAlloc((U_LONG)1, "1"); + VpPt5 = VpAlloc((U_LONG)1, ".5"); + +#ifdef _DEBUG + gnAlloc = 0; +#endif /* _DEBUG */ + + /* Determine # of digits available in one 'double'. */ + + v = 1.0; + DBLE_FIG = 0; + while(v + 1.0 > 1.0) { + ++DBLE_FIG; + v /= 10; + } + +#ifdef _DEBUG + if(gfDebug) { + printf("VpInit: BaseVal = %lu\n", BaseVal); + printf(" BASE = %lu\n", BASE); + printf(" HALF_BASE = %lu\n", HALF_BASE); + printf(" BASE1 = %lu\n", BASE1); + printf(" BASE_FIG = %lu\n", BASE_FIG); + printf(" DBLE_FIG = %lu\n", DBLE_FIG); + } +#endif /* _DEBUG */ + + return DBLE_FIG; +} + +VP_EXPORT Real * +VpOne() +{ + return VpConstOne; +} + +/* If exponent overflows,then raise exception or returns 0 */ +static int +AddExponent(Real *a,S_INT n) +{ + S_INT e = a->exponent; + S_INT m = e+n; + S_INT eb,mb; + if(e>0) { + if(n>0) { + mb = m*BASE_FIG; + eb = e*BASE_FIG; + if(mb<eb) goto overflow; + } + } else if(n<0) { + mb = m*BASE_FIG; + eb = e*BASE_FIG; + if(mb>eb) goto underflow; + } + a->exponent = m; + return 1; + +/* Overflow/Underflow ==> Raise exception or returns 0 */ +underflow: + VpSetZero(a,VpGetSign(a)); + return VpException(VP_EXCEPTION_UNDERFLOW,"Exponent underflow",0); + +overflow: + VpSetInf(a,VpGetSign(a)); + return VpException(VP_EXCEPTION_OVERFLOW,"Exponent overflow",0); +} + +/* + * Allocates variable. + * [Input] + * mx ... allocation unit, if zero then mx is determined by szVal. + * The mx is the number of effective digits can to be stored. + * szVal ... value assigned(char). If szVal==NULL,then zero is assumed. + * If szVal[0]=='#' then Max. Prec. will not be considered(1.1.7), + * full precision specified by szVal is allocated. + * + * [Returns] + * Pointer to the newly allocated variable, or + * NULL be returned if memory allocation is failed,or any error. + */ +VP_EXPORT Real * +VpAlloc(U_LONG mx, char *szVal) +{ + U_LONG i, ni, ipn, ipf, nf, ipe, ne, nalloc; + char v,*psz; + int sign=1; + Real *vp = NULL; + U_LONG mf = VpGetPrecLimit(); + + mx = (mx + BASE_FIG - 1) / BASE_FIG + 1; /* Determine allocation unit. */ + if(szVal) { + while(ISSPACE(*szVal)) szVal++; + if(*szVal!='#') { + if(mf) { + mf = (mf + BASE_FIG - 1) / BASE_FIG + 2; /* Needs 1 more for div */ + if(mx>mf) { + mx = mf; + } + } + } else { + ++szVal; + } + } else { + /* necessary to be able to store */ + /* at least mx digits. */ + /* szVal==NULL ==> allocate zero value. */ + vp = (Real *) VpMemAlloc(sizeof(Real) + mx * sizeof(U_LONG)); + /* xmalloc() alway returns(or throw interruption) */ + vp->MaxPrec = mx; /* set max precision */ + VpSetZero(vp,1); /* initialize vp to zero. */ + return vp; + } + + /* Skip all spaces */ + psz = ALLOCA_N(char,strlen(szVal)+1); + i = 0; + ipn = 0; + while(psz[i]=szVal[ipn]) { + if(ISSPACE(szVal[ipn])) {ipn++;continue;} + ++i; ++ipn; + } + szVal = psz; + + /* Check on Inf & NaN */ + if(StrCmp(szVal,SZ_PINF)==0 || + StrCmp(szVal,SZ_INF)==0 ) { + vp = (Real *) VpMemAlloc(sizeof(Real) + sizeof(U_LONG)); + vp->MaxPrec = 1; /* set max precision */ + VpSetPosInf(vp); + return vp; + } + if(StrCmp(szVal,SZ_NINF)==0) { + vp = (Real *) VpMemAlloc(sizeof(Real) + sizeof(U_LONG)); + vp->MaxPrec = 1; /* set max precision */ + VpSetNegInf(vp); + return vp; + } + if(StrCmp(szVal,SZ_NaN)==0) { + vp = (Real *) VpMemAlloc(sizeof(Real) + sizeof(U_LONG)); + vp->MaxPrec = 1; /* set max precision */ + VpSetNaN(vp); + return vp; + } + + /* check on number szVal[] */ + ipn = i = 0; + if (szVal[i] == '-') {sign=-1;++i;} + else if(szVal[i] == '+') ++i; + /* Skip digits */ + ni = 0; /* digits in mantissa */ + while(v = szVal[i]) { + if(!ISDIGIT(v)) break; + ++i; + ++ni; + } + nf = 0; + ipf = 0; + ipe = 0; + ne = 0; + if(v) { + /* other than digit nor \0 */ + if(szVal[i] == '.') { /* xxx. */ + ++i; + ipf = i; + while(v = szVal[i]) { /* get fraction part. */ + if(!ISDIGIT(v)) break; + ++i; + ++nf; + } + } + ipe = 0; /* Exponent */ + + switch(szVal[i]) { + case '\0': break; + case 'e': + case 'E': + case 'd': + case 'D': + ++i; + ipe = i; + v = szVal[i]; + if((v == '-') ||(v == '+')) ++i; + while(v=szVal[i]) { + if(!ISDIGIT(v)) break; + ++i; + ++ne; + } + break; + default: + break; + } + } + nalloc =(ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */ + /* units for szVal[] */ + if(mx <= 0) mx = 1; + nalloc = Max(nalloc, mx); + mx = nalloc; + vp =(Real *) VpMemAlloc(sizeof(Real) + mx * sizeof(U_LONG)); + /* xmalloc() alway returns(or throw interruption) */ + vp->MaxPrec = mx; /* set max precision */ + VpSetZero(vp,sign); + VpCtoV(vp, &(szVal[ipn]), ni, &(szVal[ipf]), nf, &(szVal[ipe]), ne); + return vp; +} + +/* + * Assignment(c=a). + * [Input] + * a ... RHSV + * isw ... switch for assignment. + * c = a when isw > 0 + * c = -a when isw < 0 + * if c->MaxPrec < a->Prec,then round operation + * will be performed. + * [Output] + * c ... LHSV + */ +VP_EXPORT int +VpAsgn(Real *c, Real *a, int isw) +{ + U_LONG n; + if(VpIsNaN(a)) { + VpSetNaN(c); + return 0; + } + if(VpIsInf(a)) { + VpSetInf(c,isw*VpGetSign(a)); + return 0; + } + + /* check if the RHS is zero */ + if(!VpIsZero(a)) { + c->exponent = a->exponent; /* store exponent */ + VpSetSign(c,(isw*VpGetSign(a))); /* set sign */ + n =(a->Prec < c->MaxPrec) ?(a->Prec) :(c->MaxPrec); + c->Prec = n; + memcpy(c->frac, a->frac, n * sizeof(U_LONG)); + /* Needs round ? */ + if(isw!=10) { + /* Not in ActiveRound */ + if(c->Prec < a->Prec) { + VpInternalRound(c,n,(n>0)?a->frac[n-1]:0,a->frac[n]); + } else { + VpLimitRound(c,0); + } + } + } else { + /* The value of 'a' is zero. */ + VpSetZero(c,isw*VpGetSign(a)); + return 1; + } + return c->Prec*BASE_FIG; +} + +/* + * c = a + b when operation = 1 or 2 + * = a - b when operation = -1 or -2. + * Returns number of significant digits of c + */ +VP_EXPORT int +VpAddSub(Real *c, Real *a, Real *b, int operation) +{ + S_INT sw, isw; + Real *a_ptr, *b_ptr; + U_LONG n, na, nb, i; + U_LONG mrv; + +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, "VpAddSub(enter) a=% \n", a); + VPrint(stdout, " b=% \n", b); + printf(" operation=%d\n", operation); + } +#endif /* _DEBUG */ + + if(!VpIsDefOP(c,a,b,(operation>0)?1:2)) return 0; /* No significant digits */ + + /* check if a or b is zero */ + if(VpIsZero(a)) { + /* a is zero,then assign b to c */ + if(!VpIsZero(b)) { + VpAsgn(c, b, operation); + } else { + /* Both a and b are zero. */ + if(VpGetSign(a)<0 && operation*VpGetSign(b)<0) { + /* -0 -0 */ + VpSetZero(c,-1); + } else { + VpSetZero(c,1); + } + return 1; /* 0: 1 significant digits */ + } + return c->Prec*BASE_FIG; + } + if(VpIsZero(b)) { + /* b is zero,then assign a to c. */ + VpAsgn(c, a, 1); + return c->Prec*BASE_FIG; + } + + if(operation < 0) sw = -1; + else sw = 1; + + /* compare absolute value. As a result,|a_ptr|>=|b_ptr| */ + if(a->exponent > b->exponent) { + a_ptr = a; + b_ptr = b; + } /* |a|>|b| */ + else if(a->exponent < b->exponent) { + a_ptr = b; + b_ptr = a; + } /* |a|<|b| */ + else { + /* Exponent part of a and b is the same,then compare fraction */ + /* part */ + na = a->Prec; + nb = b->Prec; + n = Min(na, nb); + for(i=0;i < n; ++i) { + if(a->frac[i] > b->frac[i]) { + a_ptr = a; + b_ptr = b; + goto end_if; + } else if(a->frac[i] < b->frac[i]) { + a_ptr = b; + b_ptr = a; + goto end_if; + } + } + if(na > nb) { + a_ptr = a; + b_ptr = b; + goto end_if; + } else if(na < nb) { + a_ptr = b; + b_ptr = a; + goto end_if; + } + /* |a| == |b| */ + if(VpGetSign(a) + sw *VpGetSign(b) == 0) { + VpSetZero(c,1); /* abs(a)=abs(b) and operation = '-' */ + return c->Prec*BASE_FIG; + } + a_ptr = a; + b_ptr = b; + } + +end_if: + isw = VpGetSign(a) + sw *VpGetSign(b); + /* + * isw = 0 ...( 1)+(-1),( 1)-( 1),(-1)+(1),(-1)-(-1) + * = 2 ...( 1)+( 1),( 1)-(-1) + * =-2 ...(-1)+(-1),(-1)-( 1) + * If isw==0, then c =(Sign a_ptr)(|a_ptr|-|b_ptr|) + * else c =(Sign ofisw)(|a_ptr|+|b_ptr|) + */ + if(isw) { /* addition */ + VpSetSign(c,(S_INT)1); + mrv = VpAddAbs(a_ptr, b_ptr, c); + VpSetSign(c,isw / 2); + } else { /* subtraction */ + VpSetSign(c,(S_INT)1); + mrv = VpSubAbs(a_ptr, b_ptr, c); + if(a_ptr == a) { + VpSetSign(c,VpGetSign(a)); + } else { + VpSetSign(c,VpGetSign(a_ptr) * sw); + } + } + VpInternalRound(c,0,(c->Prec>0)?c->frac[c->Prec-1]:0,mrv); + +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, "VpAddSub(result) c=% \n", c); + VPrint(stdout, " a=% \n", a); + VPrint(stdout, " b=% \n", b); + printf(" operation=%d\n", operation); + } +#endif /* _DEBUG */ + return c->Prec*BASE_FIG; +} + +/* + * Addition of two variable precisional variables + * a and b assuming abs(a)>abs(b). + * c = abs(a) + abs(b) ; where |a|>=|b| + */ +static U_LONG +VpAddAbs(Real *a, Real *b, Real *c) +{ + U_LONG word_shift; + U_LONG carry; + U_LONG ap; + U_LONG bp; + U_LONG cp; + U_LONG a_pos; + U_LONG b_pos; + U_LONG c_pos; + U_LONG av, bv, mrv; + +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, "VpAddAbs called: a = %\n", a); + VPrint(stdout, " b = %\n", b); + } +#endif /* _DEBUG */ + + word_shift = VpSetPTR(a, b, c, &ap, &bp, &cp, &av, &bv); + a_pos = ap; + b_pos = bp; + c_pos = cp; + if(word_shift==-1L) return 0; /* Overflow */ + if(b_pos == -1L) goto Assign_a; + + mrv = av + bv; /* Most right val. Used for round. */ + + /* Just assign the last few digits of b to c because a has no */ + /* corresponding digits to be added. */ + while(b_pos + word_shift > a_pos) { + --c_pos; + if(b_pos > 0) { + c->frac[c_pos] = b->frac[--b_pos]; + } else { + --word_shift; + c->frac[c_pos] = 0; + } + } + + /* Just assign the last few digits of a to c because b has no */ + /* corresponding digits to be added. */ + bv = b_pos + word_shift; + while(a_pos > bv) { + c->frac[--c_pos] = a->frac[--a_pos]; + } + carry = 0; /* set first carry be zero */ + + /* Now perform addition until every digits of b will be */ + /* exhausted. */ + while(b_pos > 0) { + c->frac[--c_pos] = a->frac[--a_pos] + b->frac[--b_pos] + carry; + if(c->frac[c_pos] >= BASE) { + c->frac[c_pos] -= BASE; + carry = 1; + } else { + carry = 0; + } + } + + /* Just assign the first few digits of a with considering */ + /* the carry obtained so far because b has been exhausted. */ + while(a_pos > 0) { + c->frac[--c_pos] = a->frac[--a_pos] + carry; + if(c->frac[c_pos] >= BASE) { + c->frac[c_pos] -= BASE; + carry = 1; + } else { + carry = 0; + } + } + if(c_pos) c->frac[c_pos - 1] += carry; + goto Exit; + +Assign_a: + VpAsgn(c, a, 1); + mrv = 0; + +Exit: + +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, "VpAddAbs exit: c=% \n", c); + } +#endif /* _DEBUG */ + return mrv; +} + +/* + * c = abs(a) - abs(b) + */ +static U_LONG +VpSubAbs(Real *a, Real *b, Real *c) +{ + U_LONG word_shift; + U_LONG mrv; + U_LONG borrow; + U_LONG ap; + U_LONG bp; + U_LONG cp; + U_LONG a_pos; + U_LONG b_pos; + U_LONG c_pos; + U_LONG av, bv; + +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, "VpSubAbs called: a = %\n", a); + VPrint(stdout, " b = %\n", b); + } +#endif /* _DEBUG */ + + word_shift = VpSetPTR(a, b, c, &ap, &bp, &cp, &av, &bv); + a_pos = ap; + b_pos = bp; + c_pos = cp; + if(word_shift==-1L) return 0; /* Overflow */ + if(b_pos == -1L) goto Assign_a; + + if(av >= bv) { + mrv = av - bv; + borrow = 0; + } else { + mrv = 0; + borrow = 1; + } + + /* Just assign the values which are the BASE subtracted by */ + /* each of the last few digits of the b because the a has no */ + /* corresponding digits to be subtracted. */ + if(b_pos + word_shift > a_pos) { + borrow = 1; + while(b_pos + word_shift > a_pos) { + --c_pos; + if(b_pos > 0) { + c->frac[c_pos] = BASE - b->frac[--b_pos] - borrow; + } else { + --word_shift; + c->frac[c_pos] = BASE - borrow; + } + } + } + /* Just assign the last few digits of a to c because b has no */ + /* corresponding digits to subtract. */ + + bv = b_pos + word_shift; + while(a_pos > bv) { + c->frac[--c_pos] = a->frac[--a_pos]; + } + + /* Now perform subtraction until every digits of b will be */ + /* exhausted. */ + while(b_pos > 0) { + --c_pos; + if(a->frac[--a_pos] < b->frac[--b_pos] + borrow) { + c->frac[c_pos] = BASE + a->frac[a_pos] - b->frac[b_pos] - borrow; + borrow = 1; + } else { + c->frac[c_pos] = a->frac[a_pos] - b->frac[b_pos] - borrow; + borrow = 0; + } + } + + /* Just assign the first few digits of a with considering */ + /* the borrow obtained so far because b has been exhausted. */ + while(a_pos > 0) { + --c_pos; + if(a->frac[--a_pos] < borrow) { + c->frac[c_pos] = BASE + a->frac[a_pos] - borrow; + borrow = 1; + } else { + c->frac[c_pos] = a->frac[a_pos] - borrow; + borrow = 0; + } + } + if(c_pos) c->frac[c_pos - 1] -= borrow; + goto Exit; + +Assign_a: + VpAsgn(c, a, 1); + mrv = 0; + +Exit: +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, "VpSubAbs exit: c=% \n", c); + } +#endif /* _DEBUG */ + return mrv; +} + +/* + * Note: If(av+bv)>= HALF_BASE,then 1 will be added to the least significant + * digit of c(In case of addition). + * ------------------------- figure of output ----------------------------------- + * a = xxxxxxxxxxx + * b = xxxxxxxxxx + * c =xxxxxxxxxxxxxxx + * word_shift = | | + * right_word = | | (Total digits in RHSV) + * left_word = | | (Total digits in LHSV) + * a_pos = | + * b_pos = | + * c_pos = | + */ +static U_LONG +VpSetPTR(Real *a, Real *b, Real *c, U_LONG *a_pos, U_LONG *b_pos, U_LONG *c_pos, U_LONG *av, U_LONG *bv) +{ + U_LONG left_word, right_word, word_shift; + c->frac[0] = 0; + *av = *bv = 0; + word_shift =((a->exponent) -(b->exponent)); + left_word = b->Prec + word_shift; + right_word = Max((a->Prec),left_word); + left_word =(c->MaxPrec) - 1; /* -1 ... prepare for round up */ + /* + * check if 'round' is needed. + */ + if(right_word > left_word) { /* round ? */ + /*--------------------------------- + * Actual size of a = xxxxxxAxx + * Actual size of b = xxxBxxxxx + * Max. size of c = xxxxxx + * Round off = |-----| + * c_pos = | + * right_word = | + * a_pos = | + */ + *c_pos = right_word = left_word + 1; /* Set resulting precision */ + /* be equal to that of c */ + if((a->Prec) >=(c->MaxPrec)) { + /* + * a = xxxxxxAxxx + * c = xxxxxx + * a_pos = | + */ + *a_pos = left_word; + *av = a->frac[*a_pos]; /* av is 'A' shown in above. */ + } else { + /* + * a = xxxxxxx + * c = xxxxxxxxxx + * a_pos = | + */ + *a_pos = a->Prec; + } + if((b->Prec + word_shift) >= c->MaxPrec) { + /* + * a = xxxxxxxxx + * b = xxxxxxxBxxx + * c = xxxxxxxxxxx + * b_pos = | + */ + if(c->MaxPrec >=(word_shift + 1)) { + *b_pos = c->MaxPrec - word_shift - 1; + *bv = b->frac[*b_pos]; + } else { + *b_pos = -1L; + } + } else { + /* + * a = xxxxxxxxxxxxxxxx + * b = xxxxxx + * c = xxxxxxxxxxxxx + * b_pos = | + */ + *b_pos = b->Prec; + } + } else { /* The MaxPrec of c - 1 > The Prec of a + b */ + /* + * a = xxxxxxx + * b = xxxxxx + * c = xxxxxxxxxxx + * c_pos = | + */ + *b_pos = b->Prec; + *a_pos = a->Prec; + *c_pos = right_word + 1; + } + c->Prec = *c_pos; + c->exponent = a->exponent; + if(!AddExponent(c,(S_LONG)1)) return (-1L); + return word_shift; +} + +/* + * Return number og significant digits + * c = a * b , Where a = a0a1a2 ... an + * b = b0b1b2 ... bm + * c = c0c1c2 ... cl + * a0 a1 ... an * bm + * a0 a1 ... an * bm-1 + * . . . + * . . . + * a0 a1 .... an * b0 + * +_____________________________ + * c0 c1 c2 ...... cl + * nc <---| + * MaxAB |--------------------| + */ +VP_EXPORT int +VpMult(Real *c, Real *a, Real *b) +{ + U_LONG MxIndA, MxIndB, MxIndAB, MxIndC; + U_LONG ind_c, i, ii, nc; + U_LONG ind_as, ind_ae, ind_bs, ind_be; + U_LONG Carry, s; + Real *w; + +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, "VpMult(Enter): a=% \n", a); + VPrint(stdout, " b=% \n", b); + } +#endif /* _DEBUG */ + + if(!VpIsDefOP(c,a,b,3)) return 0; /* No significant digit */ + + if(VpIsZero(a) || VpIsZero(b)) { + /* at least a or b is zero */ + VpSetZero(c,VpGetSign(a)*VpGetSign(b)); + return 1; /* 0: 1 significant digit */ + } + + if(VpIsOne(a)) { + VpAsgn(c, b, VpGetSign(a)); + goto Exit; + } + if(VpIsOne(b)) { + VpAsgn(c, a, VpGetSign(b)); + goto Exit; + } + if((b->Prec) >(a->Prec)) { + /* Adjust so that digits(a)>digits(b) */ + w = a; + a = b; + b = w; + } + w = NULL; + MxIndA = a->Prec - 1; + MxIndB = b->Prec - 1; + MxIndC = c->MaxPrec - 1; + MxIndAB = a->Prec + b->Prec - 1; + + if(MxIndC < MxIndAB) { /* The Max. prec. of c < Prec(a)+Prec(b) */ + w = c; + c = VpAlloc((U_LONG)((MxIndAB + 1) * BASE_FIG), "#0"); + MxIndC = MxIndAB; + } + + /* set LHSV c info */ + + c->exponent = a->exponent; /* set exponent */ + if(!AddExponent(c,b->exponent)) return 0; + VpSetSign(c,VpGetSign(a)*VpGetSign(b)); /* set sign */ + Carry = 0; + nc = ind_c = MxIndAB; + memset(c->frac, 0, (nc + 1) * sizeof(U_LONG)); /* Initialize c */ + c->Prec = nc + 1; /* set precision */ + for(nc = 0; nc < MxIndAB; ++nc, --ind_c) { + if(nc < MxIndB) { /* The left triangle of the Fig. */ + ind_as = MxIndA - nc; + ind_ae = MxIndA; + ind_bs = MxIndB; + ind_be = MxIndB - nc; + } else if(nc <= MxIndA) { /* The middle rectangular of the Fig. */ + ind_as = MxIndA - nc; + ind_ae = MxIndA -(nc - MxIndB); + ind_bs = MxIndB; + ind_be = 0; + } else if(nc > MxIndA) { /* The right triangle of the Fig. */ + ind_as = 0; + ind_ae = MxIndAB - nc - 1; + ind_bs = MxIndB -(nc - MxIndA); + ind_be = 0; + } + + for(i = ind_as; i <= ind_ae; ++i) { + s =((a->frac[i]) *(b->frac[ind_bs--])); + Carry = s / BASE; + s = s -(Carry * BASE); + c->frac[ind_c] += s; + if(c->frac[ind_c] >= BASE) { + s = c->frac[ind_c] / BASE; + Carry += s; + c->frac[ind_c] -= (s * BASE); + } + if(Carry) { + ii = ind_c; + while((--ii) >= 0) { + c->frac[ii] += Carry; + if(c->frac[ii] >= BASE) { + Carry = c->frac[ii] / BASE; + c->frac[ii] -=(Carry * BASE); + } else { + break; + } + } + } + } + } + if(w != NULL) { /* free work variable */ + VpNmlz(c); + VpAsgn(w, c, 1); + VpFree(c); + c = w; + } else { + VpLimitRound(c,0); + } + +Exit: +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, "VpMult(c=a*b): c=% \n", c); + VPrint(stdout, " a=% \n", a); + VPrint(stdout, " b=% \n", b); + } +#endif /*_DEBUG */ + return c->Prec*BASE_FIG; +} + +/* + * c = a / b, remainder = r + */ +VP_EXPORT int +VpDivd(Real *c, Real *r, Real *a, Real *b) +{ + U_LONG word_a, word_b, word_c, word_r; + U_LONG i, n, ind_a, ind_b, ind_c, ind_r; + U_LONG nLoop; + U_LONG q, b1, b1p1, b1b2, b1b2p1, r1r2; + U_LONG borrow, borrow1, borrow2, qb; + +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, " VpDivd(c=a/b) a=% \n", a); + VPrint(stdout, " b=% \n", b); + } +#endif /*_DEBUG */ + + VpSetNaN(r); + if(!VpIsDefOP(c,a,b,4)) goto Exit; + if(VpIsZero(a)&&VpIsZero(b)) { + VpSetNaN(c); + return VpException(VP_EXCEPTION_NaN,"(VpDivd) 0/0 not defined(NaN)",0); + } + if(VpIsZero(b)) { + VpSetInf(c,VpGetSign(a)*VpGetSign(b)); + return VpException(VP_EXCEPTION_ZERODIVIDE,"(VpDivd) Divide by zero",0); + } + if(VpIsZero(a)) { + /* numerator a is zero */ + VpSetZero(c,VpGetSign(a)*VpGetSign(b)); + VpSetZero(r,VpGetSign(a)*VpGetSign(b)); + goto Exit; + } + if(VpIsOne(b)) { + /* divide by one */ + VpAsgn(c, a, VpGetSign(b)); + VpSetZero(r,VpGetSign(a)); + goto Exit; + } + + word_a = a->Prec; + word_b = b->Prec; + word_c = c->MaxPrec; + word_r = r->MaxPrec; + + ind_c = 0; + ind_r = 1; + + if(word_a >= word_r) goto space_error; + + r->frac[0] = 0; + while(ind_r <= word_a) { + r->frac[ind_r] = a->frac[ind_r - 1]; + ++ind_r; + } + + while(ind_r < word_r) r->frac[ind_r++] = 0; + while(ind_c < word_c) c->frac[ind_c++] = 0; + + /* initial procedure */ + b1 = b1p1 = b->frac[0]; + if(b->Prec <= 1) { + b1b2p1 = b1b2 = b1p1 * BASE; + } else { + b1p1 = b1 + 1; + b1b2p1 = b1b2 = b1 * BASE + b->frac[1]; + if(b->Prec > 2) ++b1b2p1; + } + + /* */ + /* loop start */ + ind_c = word_r - 1; + nLoop = Min(word_c,ind_c); + ind_c = 1; + while(ind_c < nLoop) { + if(r->frac[ind_c] == 0) { + ++ind_c; + continue; + } + r1r2 = r->frac[ind_c] * BASE + r->frac[ind_c + 1]; + if(r1r2 == b1b2) { + /* The first two word digits is the same */ + ind_b = 2; + ind_a = ind_c + 2; + while(ind_b < word_b) { + if(r->frac[ind_a] < b->frac[ind_b]) goto div_b1p1; + if(r->frac[ind_a] > b->frac[ind_b]) break; + ++ind_a; + ++ind_b; + } + /* The first few word digits of r and b is the same and */ + /* the first different word digit of w is greater than that */ + /* of b, so quotinet is 1 and just subtract b from r. */ + borrow = 0; /* quotient=1, then just r-b */ + ind_b = b->Prec - 1; + ind_r = ind_c + ind_b; + if(ind_r >= word_r) goto space_error; + n = ind_b; + for(i = 0; i <= n; ++i) { + if(r->frac[ind_r] < b->frac[ind_b] + borrow) { + r->frac[ind_r] +=(BASE -(b->frac[ind_b] + borrow)); + borrow = 1; + } else { + r->frac[ind_r] = r->frac[ind_r] - b->frac[ind_b] - borrow; + borrow = 0; + } + --ind_r; + --ind_b; + } + ++(c->frac[ind_c]); + goto carry; + } + /* The first two word digits is not the same, */ + /* then compare magnitude, and divide actually. */ + if(r1r2 >= b1b2p1) { + q = r1r2 / b1b2p1; + c->frac[ind_c] += q; + ind_r = b->Prec + ind_c - 1; + goto sub_mult; + } + +div_b1p1: + if(ind_c + 1 >= word_c) goto out_side; + q = r1r2 / b1p1; + c->frac[ind_c + 1] += q; + ind_r = b->Prec + ind_c; + +sub_mult: + borrow1 = borrow2 = 0; + ind_b = word_b - 1; + if(ind_r >= word_r) goto space_error; + n = ind_b; + for(i = 0; i <= n; ++i) { + /* now, perform r = r - q * b */ + qb = q *(b->frac[ind_b]); + if(qb < BASE) borrow1 = 0; + else { + borrow1 = qb / BASE; + qb = qb - borrow1 * BASE; + } + if(r->frac[ind_r] < qb) { + r->frac[ind_r] +=(BASE - qb); + borrow2 = borrow2 + borrow1 + 1; + } else { + r->frac[ind_r] -= qb; + borrow2 += borrow1; + } + if(borrow2) { + if(r->frac[ind_r - 1] < borrow2) { + r->frac[ind_r - 1] +=(BASE - borrow2); + borrow2 = 1; + } else { + r->frac[ind_r - 1] -= borrow2; + borrow2 = 0; + } + } + --ind_r; + --ind_b; + } + + r->frac[ind_r] -= borrow2; +carry: + ind_r = ind_c; + while(c->frac[ind_r] >= BASE) { + c->frac[ind_r] -= BASE; + --ind_r; + ++(c->frac[ind_r]); + } + } + /* End of operation, now final arrangement */ +out_side: + c->Prec = word_c; + c->exponent = a->exponent; + if(!AddExponent(c,(S_LONG)2)) return 0; + if(!AddExponent(c,-(b->exponent))) return 0; + + VpSetSign(c,VpGetSign(a)*VpGetSign(b)); + VpNmlz(c); /* normalize c */ + r->Prec = word_r; + r->exponent = a->exponent; + if(!AddExponent(r,(S_LONG)1)) return 0; + VpSetSign(r,VpGetSign(a)); + VpNmlz(r); /* normalize r(remainder) */ + goto Exit; + +space_error: +#ifdef _DEBUG + if(gfDebug) { + printf(" word_a=%lu\n", word_a); + printf(" word_b=%lu\n", word_b); + printf(" word_c=%lu\n", word_c); + printf(" word_r=%lu\n", word_r); + printf(" ind_r =%lu\n", ind_r); + } +#endif /* _DEBUG */ + rb_bug("ERROR(VpDivd): space for remainder too small."); + +Exit: +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, " VpDivd(c=a/b), c=% \n", c); + VPrint(stdout, " r=% \n", r); + } +#endif /* _DEBUG */ + return c->Prec*BASE_FIG; +} + +/* + * Input a = 00000xxxxxxxx En(5 preceeding zeros) + * Output a = xxxxxxxx En-5 + */ +static int +VpNmlz(Real *a) +{ + U_LONG ind_a, i; + + if(!VpIsDef(a)) goto NoVal; + if(VpIsZero(a)) goto NoVal; + + ind_a = a->Prec; + while(ind_a--) { + if(a->frac[ind_a]) { + a->Prec = ind_a + 1; + i = 0; + while(a->frac[i] == 0) ++i; /* skip the first few zeros */ + if(i) { + a->Prec -= i; + if(!AddExponent(a,-((S_INT)i))) return 0; + memmove(&(a->frac[0]),&(a->frac[i]),(a->Prec)*sizeof(U_LONG)); + } + return 1; + } + } + /* a is zero(no non-zero digit) */ + VpSetZero(a,VpGetSign(a)); + return 0; + +NoVal: + a->frac[0] = 0; + a->Prec=1; + return 0; +} + +/* + * VpComp = 0 ... if a=b, + * Pos ... a>b, + * Neg ... a<b. + * 999 ... result undefined(NaN) + */ +VP_EXPORT int +VpComp(Real *a, Real *b) +{ + int val; + U_LONG mx, ind; + int e; + val = 0; + if(VpIsNaN(a)||VpIsNaN(b)) return 999; + if(!VpIsDef(a)) { + if(!VpIsDef(b)) e = a->sign - b->sign; + else e = a->sign; + if(e>0) return 1; + else if(e<0) return -1; + else return 0; + } + if(!VpIsDef(b)) { + e = -b->sign; + if(e>0) return 1; + else return -1; + } + /* Zero check */ + if(VpIsZero(a)) { + if(VpIsZero(b)) return 0; /* both zero */ + val = -VpGetSign(b); + goto Exit; + } + if(VpIsZero(b)) { + val = VpGetSign(a); + goto Exit; + } + + /* compare sign */ + if(VpGetSign(a) > VpGetSign(b)) { + val = 1; /* a>b */ + goto Exit; + } + if(VpGetSign(a) < VpGetSign(b)) { + val = -1; /* a<b */ + goto Exit; + } + + /* a and b have same sign, && signe!=0,then compare exponent */ + if((a->exponent) >(b->exponent)) { + val = VpGetSign(a); + goto Exit; + } + if((a->exponent) <(b->exponent)) { + val = -VpGetSign(b); + goto Exit; + } + + /* a and b have same exponent, then compare significand. */ + mx =((a->Prec) <(b->Prec)) ?(a->Prec) :(b->Prec); + ind = 0; + while(ind < mx) { + if((a->frac[ind]) >(b->frac[ind])) { + val = VpGetSign(a); + goto Exit; + } + if((a->frac[ind]) <(b->frac[ind])) { + val = -VpGetSign(b); + goto Exit; + } + ++ind; + } + if((a->Prec) >(b->Prec)) { + val = VpGetSign(a); + } else if((a->Prec) <(b->Prec)) { + val = -VpGetSign(b); + } + +Exit: + if (val> 1) val = 1; + else if(val<-1) val = -1; + +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, " VpComp a=%\n", a); + VPrint(stdout, " b=%\n", b); + printf(" ans=%d\n", val); + } +#endif /* _DEBUG */ + return (int)val; +} + +#ifdef _DEBUG +/* + * cntl_chr ... ASCIIZ Character, print control characters + * Available control codes: + * % ... VP variable. To print '%', use '%%'. + * \n ... new line + * \b ... backspace + * ... tab + * Note: % must must not appear more than once + * a ... VP variable to be printed + */ +VP_EXPORT int +VPrint(FILE *fp, char *cntl_chr, Real *a) +{ + U_LONG i, j, nc, nd, ZeroSup; + U_LONG n, m, e, nn; + + /* Check if NaN & Inf. */ + if(VpIsNaN(a)) { + fprintf(fp,SZ_NaN); + return 8; + } + if(VpIsPosInf(a)) { + fprintf(fp,SZ_INF); + return 8; + } + if(VpIsNegInf(a)) { + fprintf(fp,SZ_NINF); + return 9; + } + if(VpIsZero(a)) { + fprintf(fp,"0.0"); + return 3; + } + + j = 0; + nd = nc = 0; /* nd : number of digits in fraction part(every 10 digits, */ + /* nd<=10). */ + /* nc : number of caracters printed */ + ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */ + while(*(cntl_chr + j)) { + if((*(cntl_chr + j) == '%') &&(*(cntl_chr + j + 1) != '%')) { + nc = 0; + if(!VpIsZero(a)) { + if(VpGetSign(a) < 0) { + fprintf(fp, "-"); + ++nc; + } + nc += fprintf(fp, "0."); + n = a->Prec; + for(i=0;i < n;++i) { + m = BASE1; + e = a->frac[i]; + while(m) { + nn = e / m; + if((!ZeroSup) || nn) { + nc += fprintf(fp, "%lu", nn); /* The reading zero(s) */ + /* as 0.00xx will not */ + /* be printed. */ + ++nd; + ZeroSup = 0; /* Set to print succeeding zeros */ + } + if(nd >= 10) { /* print ' ' after every 10 digits */ + nd = 0; + nc += fprintf(fp, " "); + } + e = e - nn * m; + m /= 10; + } + } + nc += fprintf(fp, "E%ld", VpExponent10(a)); + } else { + nc += fprintf(fp, "0.0"); + } + } else { + ++nc; + if(*(cntl_chr + j) == '\\') { + switch(*(cntl_chr + j + 1)) { + case 'n': + fprintf(fp, "\n"); + ++j; + break; + case 't': + fprintf(fp, "\t"); + ++j; + break; + case 'b': + fprintf(fp, "\n"); + ++j; + break; + default: + fprintf(fp, "%c", *(cntl_chr + j)); + break; + } + } else { + fprintf(fp, "%c", *(cntl_chr + j)); + if(*(cntl_chr + j) == '%') ++j; + } + } + j++; + } + return (int)nc; +} +#endif /* _DEBUG */ + +static void +VpFormatSt(char *psz,S_INT fFmt) +{ + U_LONG ie; + U_LONG i; + S_INT nf = 0; + char ch; + + if(fFmt<=0) return; + + ie = strlen(psz); + for(i = 0; i < ie; ++i) { + ch = psz[i]; + if(!ch) break; + if(ISSPACE(ch) || ch=='-' || ch=='+') continue; + if(ch == '.') { nf = 0;continue;} + if(ch == 'E') break; + nf++; + if(nf > fFmt) { + memmove(psz + i + 1, psz + i, ie - i + 1); + ++ie; + nf = 0; + psz[i] = ' '; + } + } +} + +VP_EXPORT S_LONG +VpExponent10(Real *a) +{ + S_LONG ex; + U_LONG n; + + if(!VpHasVal(a)) return 0; + + ex =(a->exponent) * BASE_FIG; + n = BASE1; + while((a->frac[0] / n) == 0) { + --ex; + n /= 10; + } + return ex; +} + +VP_EXPORT void +VpSzMantissa(Real *a,char *psz) +{ + U_LONG i, ZeroSup; + U_LONG n, m, e, nn; + + if(VpIsNaN(a)) { + sprintf(psz,SZ_NaN); + return; + } + if(VpIsPosInf(a)) { + sprintf(psz,SZ_INF); + return; + } + if(VpIsNegInf(a)) { + sprintf(psz,SZ_NINF); + return; + } + + ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */ + if(!VpIsZero(a)) { + if(VpGetSign(a) < 0) *psz++ = '-'; + n = a->Prec; + for(i=0;i < n;++i) { + m = BASE1; + e = a->frac[i]; + while(m) { + nn = e / m; + if((!ZeroSup) || nn) { + sprintf(psz, "%lu", nn); /* The reading zero(s) */ + psz += strlen(psz); + /* as 0.00xx will be ignored. */ + ZeroSup = 0; /* Set to print succeeding zeros */ + } + e = e - nn * m; + m /= 10; + } + } + *psz = 0; + while(psz[-1]=='0') *(--psz) = 0; + } else { + if(VpIsPosZero(a)) sprintf(psz, "0"); + else sprintf(psz, "-0"); + } +} + +VP_EXPORT int +VpToSpecialString(Real *a,char *psz,int fPlus) +/* fPlus =0:default, =1: set ' ' before digits , =2: set '+' before digits. */ +{ + if(VpIsNaN(a)) { + sprintf(psz,SZ_NaN); + return 1; + } + + if(VpIsPosInf(a)) { + if(fPlus==1) { + *psz++ = ' '; + } else if(fPlus==2) { + *psz++ = '+'; + } + sprintf(psz,SZ_INF); + return 1; + } + if(VpIsNegInf(a)) { + sprintf(psz,SZ_NINF); + return 1; + } + if(VpIsZero(a)) { + if(VpIsPosZero(a)) { + if(fPlus==1) sprintf(psz, " 0.0"); + else if(fPlus==2) sprintf(psz, "+0.0"); + else sprintf(psz, "0.0"); + } else sprintf(psz, "-0.0"); + return 1; + } + return 0; +} + +VP_EXPORT void +VpToString(Real *a,char *psz,int fFmt,int fPlus) +/* fPlus =0:default, =1: set ' ' before digits , =2:set '+' before digits. */ +{ + U_LONG i, ZeroSup; + U_LONG n, m, e, nn; + char *pszSav = psz; + S_LONG ex; + + if(VpToSpecialString(a,psz,fPlus)) return; + + ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */ + + if(VpGetSign(a) < 0) *psz++ = '-'; + else if(fPlus==1) *psz++ = ' '; + else if(fPlus==2) *psz++ = '+'; + + *psz++ = '0'; + *psz++ = '.'; + n = a->Prec; + for(i=0;i < n;++i) { + m = BASE1; + e = a->frac[i]; + while(m) { + nn = e / m; + if((!ZeroSup) || nn) { + sprintf(psz, "%lu", nn); /* The reading zero(s) */ + psz += strlen(psz); + /* as 0.00xx will be ignored. */ + ZeroSup = 0; /* Set to print succeeding zeros */ + } + e = e - nn * m; + m /= 10; + } + } + ex =(a->exponent) * BASE_FIG; + n = BASE1; + while((a->frac[0] / n) == 0) { + --ex; + n /= 10; + } + while(psz[-1]=='0') *(--psz) = 0; + sprintf(psz, "E%ld", ex); + if(fFmt) VpFormatSt(pszSav, fFmt); +} + +VP_EXPORT void +VpToFString(Real *a,char *psz,int fFmt,int fPlus) +/* fPlus =0:default,=1: set ' ' before digits ,set '+' before digits. */ +{ + U_LONG i; + U_LONG n, m, e, nn; + char *pszSav = psz; + S_LONG ex; + + if(VpToSpecialString(a,psz,fPlus)) return; + + if(VpGetSign(a) < 0) *psz++ = '-'; + else if(fPlus==1) *psz++ = ' '; + else if(fPlus==2) *psz++ = '+'; + + n = a->Prec; + ex = a->exponent; + if(ex<=0) { + *psz++ = '0';*psz++ = '.'; + while(ex<0) { + for(i=0;i<BASE_FIG;++i) *psz++ = '0'; + ++ex; + } + ex = -1; + } + + for(i=0;i < n;++i) { + --ex; + if(i==0 && ex >= 0) { + sprintf(psz, "%lu", a->frac[i]); + psz += strlen(psz); + } else { + m = BASE1; + e = a->frac[i]; + while(m) { + nn = e / m; + *psz++ = (char)(nn + '0'); + e = e - nn * m; + m /= 10; + } + } + if(ex == 0) *psz++ = '.'; + } + while(--ex>=0) { + m = BASE; + while(m/=10) *psz++ = '0'; + if(ex == 0) *psz++ = '.'; + } + *psz = 0; + while(psz[-1]=='0') *(--psz) = 0; + if(psz[-1]=='.') sprintf(psz, "0"); + if(fFmt) VpFormatSt(pszSav, fFmt); +} + +/* + * [Output] + * a[] ... variable to be assigned the value. + * [Input] + * int_chr[] ... integer part(may include '+/-'). + * ni ... number of characters in int_chr[],not including '+/-'. + * frac[] ... fraction part. + * nf ... number of characters in frac[]. + * exp_chr[] ... exponent part(including '+/-'). + * ne ... number of characters in exp_chr[],not including '+/-'. + */ +VP_EXPORT int +VpCtoV(Real *a, char *int_chr, U_LONG ni, char *frac, U_LONG nf, char *exp_chr, U_LONG ne) +{ + U_LONG i, j, ind_a, ma, mi, me; + U_LONG loc; + S_INT e,es, eb, ef; + S_INT sign, signe; + /* get exponent part */ + e = 0; + ma = a->MaxPrec; + mi = ni; + me = ne; + signe = 1; + memset(a->frac, 0, ma * sizeof(U_LONG)); + if(ne > 0) { + i = 0; + if(exp_chr[0] == '-') { + signe = -1; + ++i; + ++me; + } else if(exp_chr[0] == '+') { + ++i; + ++me; + } + while(i < me) { + es = e*((S_INT)BASE_FIG); + e = e * 10 + exp_chr[i] - '0'; + if(es>e*((S_INT)BASE_FIG)) { + return VpException(VP_EXCEPTION_INFINITY,"Exponent overflow",0); + } + ++i; + } + } + + /* get integer part */ + i = 0; + sign = 1; + if(ni > 0) { + if(int_chr[0] == '-') { + sign = -1; + ++i; + ++mi; + } else if(int_chr[0] == '+') { + ++i; + ++mi; + } + } + + e = signe * e; /* e: The value of exponent part. */ + e = e + ni; /* set actual exponent size. */ + + if(e > 0) signe = 1; + else signe = -1; + + /* Adjust the exponent so that it is the multiple of BASE_FIG. */ + j = 0; + ef = 1; + while(ef) { + if(e>=0) eb = e; + else eb = -e; + ef = eb / ((S_INT)BASE_FIG); + ef = eb - ef * ((S_INT)BASE_FIG); + if(ef) { + ++j; /* Means to add one more preceeding zero */ + ++e; + } + } + + eb = e / ((S_INT)BASE_FIG); + + ind_a = 0; + while(i < mi) { + a->frac[ind_a] = 0; + while((j < (U_LONG)BASE_FIG) &&(i < mi)) { + a->frac[ind_a] = a->frac[ind_a] * 10 + int_chr[i] - '0'; + ++j; + ++i; + } + if(i < mi) { + ++ind_a; + if(ind_a >= ma) goto over_flow; + j = 0; + } + } + loc = 1; + + /* get fraction part */ + + i = 0; + while(i < nf) { + while((j < (U_LONG)BASE_FIG) &&(i < nf)) { + a->frac[ind_a] = a->frac[ind_a] * 10 + frac[i] - '0'; + ++j; + ++i; + } + if(i < nf) { + ++ind_a; + if(ind_a >= ma) goto over_flow; + j = 0; + } + } + goto Final; + +over_flow: + rb_warn("Conversion from String to BigDecimal overflow (last few digits discarded)."); + +Final: + if(ind_a >= ma) ind_a = ma - 1; + while(j < (U_LONG)BASE_FIG) { + a->frac[ind_a] = a->frac[ind_a] * 10; + ++j; + } + a->Prec = ind_a + 1; + a->exponent = eb; + VpSetSign(a,sign); + VpNmlz(a); + return 1; +} + +/* + * [Input] + * *m ... Real + * [Output] + * *d ... fraction part of m(d = 0.xxxxxxx). where # of 'x's is fig. + * *e ... U_LONG,exponent of m. + * DBLE_FIG ... Number of digits in a double variable. + * + * m -> d*10**e, 0<d<BASE + * [Returns] + * 0 ... Zero + * 1 ... Normal + * 2 ... Infinity + * -1 ... NaN + */ +VP_EXPORT int +VpVtoD(double *d, S_LONG *e, Real *m) +{ + U_LONG ind_m, mm, fig; + double div; + int f = 1; + + if(VpIsNaN(m)) { + *d = VpGetDoubleNaN(); + *e = 0; + f = -1; /* NaN */ + goto Exit; + } else + if(VpIsPosZero(m)) { + *d = 0.0; + *e = 0; + f = 0; + goto Exit; + } else + if(VpIsNegZero(m)) { + *d = VpGetDoubleNegZero(); + *e = 0; + f = 0; + goto Exit; + } else + if(VpIsPosInf(m)) { + *d = VpGetDoublePosInf(); + *e = 0; + f = 2; + goto Exit; + } else + if(VpIsNegInf(m)) { + *d = VpGetDoubleNegInf(); + *e = 0; + f = 2; + goto Exit; + } + /* Normal number */ + fig =(DBLE_FIG + BASE_FIG - 1) / BASE_FIG; + ind_m = 0; + mm = Min(fig,(m->Prec)); + *d = 0.0; + div = 1.; + while(ind_m < mm) { + div /=(double)((S_INT)BASE); + *d = *d +((double) ((S_INT)m->frac[ind_m++])) * div; + } + *e = m->exponent * ((S_INT)BASE_FIG); + *d *= VpGetSign(m); + +Exit: +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, " VpVtoD: m=%\n", m); + printf(" d=%e * 10 **%ld\n", *d, *e); + printf(" DBLE_FIG = %ld\n", DBLE_FIG); + } +#endif /*_DEBUG */ + return f; +} + +/* + * m <- d + */ +VP_EXPORT void +VpDtoV(Real *m, double d) +{ + U_LONG i, ind_m, mm; + U_LONG ne; + double val, val2; + + if(isnan(d)) { + VpSetNaN(m); + goto Exit; + } + if(isinf(d)) { + if(d>0.0) VpSetPosInf(m); + else VpSetNegInf(m); + goto Exit; + } + + if(d == 0.0) { + VpSetZero(m,1); + goto Exit; + } + val =(d > 0.) ? d :(-d); + ne = 0; + if(val >= 1.0) { + while(val >= 1.0) { + val /=(double)((S_INT)BASE); + ++ne; + } + } else { + val2 = 1.0 /(double)((S_INT)BASE); + while(val < val2) { + val *=(double)((S_INT)BASE); + --ne; + } + } + /* Now val = 0.xxxxx*BASE**ne */ + + mm = m->MaxPrec; + memset(m->frac, 0, mm * sizeof(U_LONG)); + for(ind_m = 0;val > 0.0 && ind_m < mm;ind_m++) { + val *=(double)((S_INT)BASE); + i =(U_LONG) val; + val -=(double)((S_INT)i); + m->frac[ind_m] = i; + } + if(ind_m >= mm) ind_m = mm - 1; + if(d > 0.0) { + VpSetSign(m, (S_INT)1); + } else { + VpSetSign(m,-(S_INT)1); + } + m->Prec = ind_m + 1; + m->exponent = ne; + + VpInternalRound(m,0,(m->Prec>0)?m->frac[m->Prec-1]:0, + (U_LONG)(val*((double)((S_INT)BASE)))); + +Exit: +#ifdef _DEBUG + if(gfDebug) { + printf("VpDtoV d=%30.30e\n", d); + VPrint(stdout, " m=%\n", m); + } +#endif /* _DEBUG */ + return; +} + +/* + * m <- ival + */ +VP_EXPORT void +VpItoV(Real *m, S_INT ival) +{ + U_LONG mm, ind_m; + U_LONG val, v1, v2, v; + int isign; + S_INT ne; + + if(ival == 0) { + VpSetZero(m,1); + goto Exit; + } + isign = 1; + val = ival; + if(ival < 0) { + isign = -1; + val =(U_LONG)(-ival); + } + ne = 0; + ind_m = 0; + mm = m->MaxPrec; + while(ind_m < mm) { + m->frac[ind_m] = 0; + ++ind_m; + } + ind_m = 0; + while(val > 0) { + if(val) { + v1 = val; + v2 = 1; + while(v1 >= BASE) { + v1 /= BASE; + v2 *= BASE; + } + val = val - v2 * v1; + v = v1; + } else { + v = 0; + } + m->frac[ind_m] = v; + ++ind_m; + ++ne; + } + m->Prec = ind_m - 1; + m->exponent = ne; + VpSetSign(m,isign); + VpNmlz(m); + +Exit: +#ifdef _DEBUG + if(gfDebug) { + printf(" VpItoV i=%d\n", ival); + VPrint(stdout, " m=%\n", m); + } +#endif /* _DEBUG */ + return; +} + +/* + * y = SQRT(x), y*y - x =>0 + */ +VP_EXPORT int +VpSqrt(Real *y, Real *x) +{ + Real *f = NULL; + Real *r = NULL; + S_LONG y_prec, f_prec; + S_LONG n; + S_LONG e; + S_LONG prec; + S_LONG nr; + double val; + + /* Zero, NaN or Infinity ? */ + if(!VpHasVal(x)) { + if(VpIsZero(x)||VpGetSign(x)>0) { + VpAsgn(y,x,1); + goto Exit; + } + VpSetNaN(y); + return VpException(VP_EXCEPTION_OP,"(VpSqrt) SQRT(NaN or negative value)",0); + goto Exit; + } + + /* Negative ? */ + if(VpGetSign(x) < 0) { + VpSetNaN(y); + return VpException(VP_EXCEPTION_OP,"(VpSqrt) SQRT(negative value)",0); + } + + /* One ? */ + if(VpIsOne(x)) { + VpSetOne(y); + goto Exit; + } + + n = (S_LONG)y->MaxPrec; + if((S_LONG)x->MaxPrec > n) n = (S_LONG)x->MaxPrec; + /* allocate temporally variables */ + f = VpAlloc(y->MaxPrec *(BASE_FIG + 2), "#1"); + r = VpAlloc((n + n) *(BASE_FIG + 2), "#1"); + + nr = 0; + y_prec = (S_LONG)y->MaxPrec; + f_prec = (S_LONG)f->MaxPrec; + + prec = x->exponent; + if(prec > 0) ++prec; + else --prec; + prec = prec - (S_LONG)y->MaxPrec; + VpVtoD(&val, &e, x); /* val <- x */ + e /= ((S_LONG)BASE_FIG); + n = e / 2; + if(e - n * 2 != 0) { + val /=(double)((S_INT)BASE); + n =(e + 1) / 2; + } + VpDtoV(y, sqrt(val)); /* y <- sqrt(val) */ + y->exponent += n; + n = (DBLE_FIG + BASE_FIG - 1) / BASE_FIG; + y->MaxPrec = (U_LONG)Min(n , y_prec); + f->MaxPrec = y->MaxPrec + 1; + n = y_prec*((S_LONG)BASE_FIG); + if((U_LONG)n<maxnr) n = (U_LONG)maxnr; + do { + y->MaxPrec *= 2; + if(y->MaxPrec > (U_LONG)y_prec) y->MaxPrec = (U_LONG)y_prec; + f->MaxPrec = y->MaxPrec; + VpDivd(f, r, x, y); /* f = x/y */ + VpAddSub(r, f, y, -1); /* r = f - y */ + VpMult(f, VpPt5, r); /* f = 0.5*r */ + if(VpIsZero(f)) goto converge; + VpAddSub(r, f, y, 1); /* r = y + f */ + VpAsgn(y, r, 1); /* y = r */ + if(f->exponent <= prec) goto converge; + } while(++nr < n); + /* */ +#ifdef _DEBUG + if(gfDebug) { + printf("ERROR(VpSqrt): did not converge within %ld iterations.\n", + nr); + } +#endif /* _DEBUG */ + y->MaxPrec = y_prec; + +converge: + VpChangeSign(y,(S_INT)1); +#ifdef _DEBUG + if(gfDebug) { + VpMult(r, y, y); + VpAddSub(f, x, r, -1); + printf("VpSqrt: iterations = %lu\n", nr); + VPrint(stdout, " y =% \n", y); + VPrint(stdout, " x =% \n", x); + VPrint(stdout, " x-y*y = % \n", f); + } +#endif /* _DEBUG */ + y->MaxPrec = y_prec; + +Exit: + VpFree(f); + VpFree(r); + return 1; +} + +/* + * + * f = 0: Round off/Truncate, 1: round up, 2:ceil, 3: floor, 4: Banker's rounding + * nf: digit position for operation. + * + */ +VP_EXPORT int +VpMidRound(Real *y, int f, int nf) +/* + * Round reletively from the decimal point. + * f: rounding mode + * nf: digit location to round from the the decimal point. + */ +{ + int n,i,ix,ioffset; + U_LONG v; + U_LONG div; + + nf += y->exponent*((int)BASE_FIG); + /* ix: x->fraq[ix] contains round position */ + ix = nf/(int)BASE_FIG; + if(ix<0 || ((U_LONG)ix)>=y->Prec) return 0; /* Unable to round */ + ioffset = nf - ix*((int)BASE_FIG); + + memset(y->frac+ix+1, 0, (y->Prec - (ix+1)) * sizeof(U_LONG)); + v = y->frac[ix]; + /* drop digits after pointed digit */ + n = BASE_FIG - ioffset - 1; + for(i=0;i<n;++i) v /= 10; + div = v/10; + v = v - div*10; + switch(f) { + case VP_ROUND_DOWN: /* Truncate */ + break; + case VP_ROUND_UP: /* Roundup */ + if(v) ++div; + break; + case VP_ROUND_HALF_UP: /* Round half up */ + if(v>=5) ++div; + break; + case VP_ROUND_HALF_DOWN: /* Round half down */ + if(v>=6) ++div; + break; + case VP_ROUND_CEIL: /* ceil */ + if(v && (VpGetSign(y)>0)) ++div; + break; + case VP_ROUND_FLOOR: /* floor */ + if(v && (VpGetSign(y)<0)) ++div; + break; + case VP_ROUND_HALF_EVEN: /* Banker's rounding */ + if(v>5) ++div; + else if(v==5) { + if((U_LONG)i==(BASE_FIG-1)) { + if(ix && (y->frac[ix-1]%2)) ++div; + } else { + if(div%2) ++div; + } + } + break; + } + for(i=0;i<=n;++i) div *= 10; + if(div>=BASE) { + if(ix) { + y->frac[ix] = 0; + VpRdup(y,ix); + } else { + S_INT s = VpGetSign(y); + VpSetOne(y); + VpSetSign(y,s); + } + } else { + y->frac[ix] = div; + VpNmlz(y); + } + return 1; +} + +VP_EXPORT int +VpLeftRound(Real *y, int f, int nf) +/* + * Round from the left hand side of the digits. + */ +{ + U_LONG v; + if(!VpHasVal(y)) return 0; /* Unable to round */ + v = y->frac[0]; + nf -= VpExponent(y)*BASE_FIG; + while(v=v/10) nf--; + nf += (BASE_FIG-1); + return VpMidRound(y,f,nf); +} + +VP_EXPORT int +VpActiveRound(Real *y, Real *x, int f, int nf) +{ + /* First,assign whole value in truncation mode */ + if(VpAsgn(y, x, 10)<=1) return 0; /* Zero,NaN,or Infinity */ + return VpMidRound(y,f,nf); +} + +static int +VpLimitRound(Real *c,U_LONG ixDigit) +{ + U_LONG ix = VpGetPrecLimit(); + if(!VpNmlz(c)) return -1; + if(!ix) return 0; + if(!ixDigit) ixDigit = c->Prec-1; + if((ix+BASE_FIG-1)/BASE_FIG > ixDigit+1) return 0; + return VpLeftRound(c,VpGetRoundMode(),ix); +} + +static void +VpInternalRound(Real *c,int ixDigit,U_LONG vPrev,U_LONG v) +{ + int f = 0; + + if(VpLimitRound(c,ixDigit)) return; + if(!v) return; + + v /= BASE1; + switch(gfRoundMode) { + case VP_ROUND_DOWN: + break; + case VP_ROUND_UP: + if(v) f = 1; + break; + case VP_ROUND_HALF_UP: + if(v >= 5) f = 1; + break; + case VP_ROUND_HALF_DOWN: + if(v >= 6) f = 1; + break; + case VP_ROUND_CEIL: /* ceil */ + if(v && (VpGetSign(c)>0)) f = 1; + break; + case VP_ROUND_FLOOR: /* floor */ + if(v && (VpGetSign(c)<0)) f = 1; + break; + case VP_ROUND_HALF_EVEN: /* Banker's rounding */ + if(v>5) f = 1; + else if(v==5 && vPrev%2) f = 1; + break; + } + if(f) { + VpRdup(c,ixDigit); /* round up */ + VpNmlz(c); + } +} + +/* + * Rounds up m(plus one to final digit of m). + */ +static int +VpRdup(Real *m,U_LONG ind_m) +{ + U_LONG carry; + + if(!ind_m) ind_m = m->Prec; + + carry = 1; + while(carry > 0 && (ind_m--)) { + m->frac[ind_m] += carry; + if(m->frac[ind_m] >= BASE) m->frac[ind_m] -= BASE; + else carry = 0; + } + if(carry > 0) { /* Overflow,count exponent and set fraction part be 1 */ + if(!AddExponent(m,(S_LONG)1)) return 0; + m->Prec = m->frac[0] = 1; + } else { + VpNmlz(m); + } + return 1; +} + +/* + * y = x - fix(x) + */ +VP_EXPORT void +VpFrac(Real *y, Real *x) +{ + U_LONG my, ind_y, ind_x; + + if(!VpHasVal(x)) { + VpAsgn(y,x,1); + goto Exit; + } + + if(x->exponent > 0 && (U_LONG)x->exponent >= x->Prec) { + VpSetZero(y,VpGetSign(x)); + goto Exit; + } else if(x->exponent <= 0) { + VpAsgn(y, x, 1); + goto Exit; + } + + y->Prec = x->Prec -(U_LONG) x->exponent; + y->Prec = Min(y->Prec, y->MaxPrec); + y->exponent = 0; + VpSetSign(y,VpGetSign(x)); + ind_y = 0; + my = y->Prec; + ind_x = x->exponent; + while(ind_y < my) { + y->frac[ind_y] = x->frac[ind_x]; + ++ind_y; + ++ind_x; + } + VpNmlz(y); + +Exit: +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, "VpFrac y=%\n", y); + VPrint(stdout, " x=%\n", x); + } +#endif /* _DEBUG */ + return; +} + +/* + * y = x ** n + */ +VP_EXPORT int +VpPower(Real *y, Real *x, S_INT n) +{ + U_LONG s, ss; + S_LONG sign; + Real *w1 = NULL; + Real *w2 = NULL; + + if(VpIsZero(x)) { + if(n==0) { + VpSetOne(y); + goto Exit; + } + sign = VpGetSign(x); + if(n<0) { + n = -n; + if(sign<0) sign = (n%2)?(-1):(1); + VpSetInf (y,sign); + } else { + if(sign<0) sign = (n%2)?(-1):(1); + VpSetZero(y,sign); + } + goto Exit; + } + if(!VpIsDef(x)) { + VpSetNaN(y); /* Not sure !!! */ + goto Exit; + } + + if((x->exponent == 1) &&(x->Prec == 1) &&(x->frac[0] == 1)) { + /* abs(x) = 1 */ + VpSetOne(y); + if(VpGetSign(x) > 0) goto Exit; + if((n % 2) == 0) goto Exit; + VpSetSign(y,-(S_INT)1); + goto Exit; + } + + if(n > 0) sign = 1; + else if(n < 0) { + sign = -1; + n = -n; + } else { + VpSetOne(y); + goto Exit; + } + + /* Allocate working variables */ + + w1 = VpAlloc((y->MaxPrec + 2) * BASE_FIG, "#0"); + w2 = VpAlloc((w1->MaxPrec * 2 + 1) * BASE_FIG, "#0"); + /* calculation start */ + + VpAsgn(y, x, 1); + --n; + while(n > 0) { + VpAsgn(w1, x, 1); + s = 1; +loop1: ss = s; + s += s; + if(s >(U_LONG) n) goto out_loop1; + VpMult(w2, w1, w1); + VpAsgn(w1, w2, 1); + goto loop1; +out_loop1: + n -= ss; + VpMult(w2, y, w1); + VpAsgn(y, w2, 1); + } + if(sign < 0) { + VpDivd(w1, w2, VpConstOne, y); + VpAsgn(y, w1, 1); + } + +Exit: +#ifdef _DEBUG + if(gfDebug) { + VPrint(stdout, "VpPower y=%\n", y); + VPrint(stdout, "VpPower x=%\n", x); + printf(" n=%d\n", n); + } +#endif /* _DEBUG */ + VpFree(w2); + VpFree(w1); + return 1; +} + +#ifdef _DEBUG +int +VpVarCheck(Real * v) +/* + * Checks the validity of the Real variable v. + * [Input] + * v ... Real *, variable to be checked. + * [Returns] + * 0 ... correct v. + * other ... error + */ +{ + U_LONG i; + + if(v->MaxPrec <= 0) { + printf("ERROR(VpVarCheck): Illegal Max. Precision(=%lu)\n", + v->MaxPrec); + return 1; + } + if((v->Prec <= 0) ||((v->Prec) >(v->MaxPrec))) { + printf("ERROR(VpVarCheck): Illegal Precision(=%lu)\n", v->Prec); + printf(" Max. Prec.=%lu\n", v->MaxPrec); + return 2; + } + for(i = 0; i < v->Prec; ++i) { + if((v->frac[i] >= BASE)) { + printf("ERROR(VpVarCheck): Illegal fraction\n"); + printf(" Frac[%ld]=%lu\n", i, v->frac[i]); + printf(" Prec. =%lu\n", v->Prec); + printf(" Exp. =%d\n", v->exponent); + printf(" BASE =%lu\n", BASE); + return 3; + } + } + return 0; +} +#endif /* _DEBUG */ diff --git a/lib/rss/0.9.rb b/lib/rss/0.9.rb new file mode 100644 index 0000000000..bb3cc23beb --- /dev/null +++ b/lib/rss/0.9.rb @@ -0,0 +1,409 @@ +require "rss/parser" + +module RSS + + module RSS09 + NSPOOL = {} + ELEMENTS = [] + end + + class Rss < Element + + include RSS09 + include RootElementMixin + include XMLStyleSheetMixin + + [ + ["channel", nil], + ].each do |tag, occurs| + install_model(tag, occurs) + end + + %w(channel).each do |x| + install_have_child_element(x) + end + + attr_accessor :rss_version, :version, :encoding, :standalone + + def initialize(rss_version, version=nil, encoding=nil, standalone=nil) + super + end + + def items + if @channel + @channel.items + else + [] + end + end + + def image + if @channel + @channel.image + else + nil + end + end + + def to_s(convert=true) + rv = <<-EOR +#{xmldecl} +#{xml_stylesheet_pi}<rss version="#{@rss_version}"#{ns_declaration}> +#{channel_element(false)} +#{other_element(false, "\t")} +</rss> +EOR + rv = @converter.convert(rv) if convert and @converter + rv + end + + private + def children + [@channel] + end + + class Channel < Element + + include RSS09 + + [ + ["title", nil], + ["link", nil], + ["description", nil], + ["language", nil], + ["copyright", "?"], + ["managingEditor", "?"], + ["webMaster", "?"], + ["rating", "?"], + ["docs", "?"], + ["skipDays", "?"], + ["skipHours", "?"], + ].each do |x, occurs| + install_text_element(x) + install_model(x, occurs) + end + + [ + ["pubDate", "?"], + ["lastBuildDate", "?"], + ].each do |x, occurs| + install_date_element(x, 'rfc822') + install_model(x, occurs) + end + + [ + ["image", nil], + ["textInput", "?"], + ["cloud", "?"] + ].each do |x, occurs| + install_have_child_element(x) + install_model(x, occurs) + end + + [ + ["item", "*"] + ].each do |x, occurs| + install_have_children_element(x) + install_model(x, occurs) + end + + def initialize() + super() + end + + def to_s(convert=true) + rv = <<-EOT + <channel> + #{title_element(false)} + #{link_element(false)} + #{description_element(false)} + #{language_element(false)} + #{copyright_element(false)} + #{managingEditor_element(false)} + #{webMaster_element(false)} + #{rating_element(false)} + #{pubDate_element(false)} + #{lastBuildDate_element(false)} + #{docs_element(false)} + #{skipDays_element(false)} + #{skipHours_element(false)} + #{image_element(false)} +#{item_elements(false)} + #{textInput_element(false)} +#{other_element(false, "\t\t")} + </channel> +EOT + rv = @converter.convert(rv) if convert and @converter + rv + end + + private + def children + [@image, @textInput, @cloud, *@item] + end + + class Image < Element + + include RSS09 + + %w(url title link width height description).each do |x| + install_text_element(x) + end + + def to_s(convert=true) + rv = <<-EOT + <image> + #{url_element(false)} + #{title_element(false)} + #{link_element(false)} + #{width_element(false)} + #{height_element(false)} + #{description_element(false)} +#{other_element(false, "\t\t\t\t")} + </image> +EOT + rv = @converter.convert(rv) if convert and @converter + rv + end + + end + + class Cloud < Element + + include RSS09 + + [ + ["domain", nil, false], + ["port", nil, false], + ["path", nil, false], + ["registerProcedure", nil, false], + ["protocol", nil ,false], + ].each do |name, uri, required| + install_get_attribute(name, uri, required) + end + + def to_s(convert=true) + rv = <<-EOT + <cloud + domain="#{h @domain}" + port="#{h @port}" + path="#{h @path}" + registerProcedure="#{h @registerProcedure}" + protocol="#{h @protocol}"/> +EOT + rv = @converter.convert(rv) if convert and @converter + rv + end + + end + + class Item < Element + + include RSS09 + + %w(title link description author comments).each do |x| + install_text_element(x) + end + + %w(category source enclosure).each do |x| + install_have_child_element(x) + end + + [ + ["title", '?'], + ["link", '?'], + ["description", '?'], + ["author", '?'], + ["comments", '?'], + ["category", '?'], + ["source", '?'], + ["enclosure", '?'], + ].each do |tag, occurs| + install_model(tag, occurs) + end + + def to_s(convert=true) + rv = <<-EOT + <item> + #{title_element(false)} + #{link_element(false)} + #{description_element(false)} + #{author_element(false)} + #{category_element(false)} + #{comments_element(false)} + #{enclosure_element(false)} + #{source_element(false)} +#{other_element(false, "\t\t\t\t")} + </item> +EOT + rv = @converter.convert(rv) if convert and @converter + rv + end + + class Source < Element + + include RSS09 + + [ + ["url", nil, true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required) + end + + content_setup + + def initialize(url=nil, content=nil) + super() + @url = url + @content = content + end + + def to_s(convert=true) + if @url + rv = %Q! <source url="#{@url}">! + rv << %Q!#{@content}</source>! + rv = @converter.convert(rv) if convert and @converter + rv + else + '' + end + end + + private + def _attrs + [ + ["url", true] + ] + end + + end + + class Enclosure < Element + + include RSS09 + + [ + ["url", nil, true], + ["length", nil, true], + ["type", nil, true], + ].each do |name, uri, required| + install_get_attribute(name, uri, required) + end + + def initialize(url=nil, length=nil, type=nil) + super() + @url = url + @length = length + @type = type + end + + def to_s(convert=true) + if @url and @length and @type + rv = %Q!<enclosure url="#{h @url}" ! + rv << %Q!length="#{h @length}" type="#{h @type}"/>! + rv = @converter.convert(rv) if convert and @converter + rv + else + '' + end + end + + private + def _attrs + [ + ["url", true], + ["length", true], + ["type", true], + ] + end + + end + + class Category < Element + + include RSS09 + + [ + ["domain", nil, true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required) + end + + content_setup + + def initialize(domain=nil, content=nil) + super() + @domain = domain + @content = content + end + + def to_s(convert=true) + if @domain + rv = %Q!<category domain="#{h @domain}">! + rv << %Q!#{h @content}</category>! + rv = @converter.convert(rv) if convert and @converter + rv + else + '' + end + end + + private + def _attrs + [ + ["domain", true] + ] + end + + end + + end + + class TextInput < Element + + include RSS09 + + %w(title description name link).each do |x| + install_text_element(x) + end + + def to_s(convert=true) + rv = <<-EOT + <textInput> + #{title_element(false)} + #{description_element(false)} + #{name_element(false)} + #{link_element(false)} +#{other_element(false, "\t\t\t\t")} + </textInput> +EOT + rv = @converter.convert(rv) if convert and @converter + rv + end + + end + + end + + end + + RSS09::ELEMENTS.each do |x| + BaseListener.install_get_text_element(x, nil, "#{x}=") + end + + module ListenerMixin + private + def start_rss(tag_name, prefix, attrs, ns) + check_ns(tag_name, prefix, ns, nil) + + @rss = Rss.new(attrs['version'], @version, @encoding, @standalone) + @rss.xml_stylesheets = @xml_stylesheets + @last_element = @rss + @proc_stack.push Proc.new { |text, tags| + @rss.validate_for_stream(tags) if @do_validate + } + end + + end + +end diff --git a/lib/rss/2.0.rb b/lib/rss/2.0.rb new file mode 100644 index 0000000000..c83fb2c393 --- /dev/null +++ b/lib/rss/2.0.rb @@ -0,0 +1,147 @@ +require "rss/0.9" + +module RSS + + class Rss + +# URI = "http://backend.userland.com/rss2" + +# install_ns('', URI) + +# def self.required_uri +# URI +# end + + class Channel + +# def self.required_uri +# URI +# end + + %w(generator ttl).each do |x| + install_text_element(x) + end + + %w(category).each do |x| + install_have_child_element(x) + end + + [ + ["image", "?"], + ].each do |x, occurs| + install_model(x, occurs) + end + + def other_element(convert, indent='') + rv = <<-EOT +#{indent}#{category_element(convert)} +#{indent}#{generator_element(convert)} +#{indent}#{ttl_element(convert)} +EOT + rv << super + end + + Category = Item::Category +# def Category.required_uri +# URI +# end + + class Item + +# def self.required_uri +# URI +# end + + [ + ["pubDate", '?'], + ].each do |x, occurs| + install_date_element(x, 'rfc822') + install_model(x, occurs) + end + + [ + ["guid", '?'], + ].each do |x, occurs| + install_have_child_element(x) + install_model(x, occurs) + end + + def other_element(convert, indent='') + rv = <<-EOT +#{indent}#{pubDate_element(false)} +#{indent}#{guid_element(false)} +EOT + rv << super + end + + class Guid < Element + + include RSS09 + +# def self.required_uri +# URI +# end + + [ + ["isPermaLink", nil, false] + ].each do |name, uri, required| + install_get_attribute(name, uri, required) + end + + content_setup + + def initialize(isPermaLink=nil, content=nil) + super() + @isPermaLink = isPermaLink + @content = content + end + + def to_s(convert=true) + if @content + rv = %Q!<guid! + rv << %Q! isPermaLink="#{h @isPermaLink}"! if @isPermaLink + rv << %Q!>#{h @content}</guid>! + rv = @converter.convert(rv) if convert and @converter + rv + else + '' + end + end + + private + def _attrs + [ + ["isPermaLink", false] + ] + end + + end + + end + + end + + end + + RSS09::ELEMENTS.each do |x| +# BaseListener.install_get_text_element(x, Rss::URI, "#{x}=") + BaseListener.install_get_text_element(x, nil, "#{x}=") + end + + module ListenerMixin + private + alias start_rss09 start_rss + def start_rss(tag_name, prefix, attrs, ns) +# check_ns(tag_name, prefix, ns, Rss::URI) + + @rss = Rss.new(attrs['version'], @version, @encoding, @standalone) + @rss.xml_stylesheets = @xml_stylesheets + @last_element = @rss + @proc_stack.push Proc.new { |text, tags| + @rss.validate_for_stream(tags) if @do_validate + } + end + + end + +end diff --git a/lib/rss/converter.rb b/lib/rss/converter.rb new file mode 100644 index 0000000000..9a62431a9e --- /dev/null +++ b/lib/rss/converter.rb @@ -0,0 +1,154 @@ +require "rss/utils" + +module RSS + + class Converter + + include Utils + + def initialize(to_enc, from_enc=nil) + to_enc = to_enc.downcase.gsub(/-/, '_') + from_enc ||= 'utf-8' + from_enc = from_enc.downcase.gsub(/-/, '_') + if to_enc == from_enc + def_same_enc() + else + if respond_to?("def_to_#{to_enc}_from_#{from_enc}") + send("def_to_#{to_enc}_from_#{from_enc}") + else + def_else_enc(to_enc, from_enc) + end + end + end + + def convert(value) + value + end + + def def_convert(depth=0) + instance_eval(<<-EOC, *get_file_and_line_from_caller(depth)) + def convert(value) + if value.kind_of?(String) + #{yield('value')} + else + value + end + end + EOC + end + + def def_iconv_convert(to_enc, from_enc, depth=0) + begin + require "iconv" + def_convert(depth+1) do |value| + <<-EOC + @iconv ||= Iconv.new("#{to_enc}", "#{from_enc}") + begin + @iconv.iconv(#{value}) + rescue Iconv::Failure + raise ConversionError.new(#{value}, "#{to_enc}", "#{from_enc}") + end + EOC + end + rescue LoadError, ArgumentError, SystemCallError + raise UnknownConversionMethodError.new(to_enc, from_enc) + end + end + + def def_else_enc(to_enc, from_enc) + raise UnknownConversionMethodError.new(to_enc, from_enc) + end + + def def_same_enc() + def_convert do |value| + value + end + end + + def def_uconv_convert_if_can(meth, to_enc, from_enc) + begin + require "uconv" + def_convert(1) do |value| + <<-EOC + begin + Uconv.#{meth}(#{value}) + rescue Uconv::Error + raise ConversionError.new(#{value}, "#{to_enc}", "#{from_enc}") + end + EOC + end + rescue LoadError + def_iconv_convert(to_enc, from_enc, 1) + end + end + + def def_to_euc_jp_from_utf_8 + def_uconv_convert_if_can('u8toeuc', 'EUC-JP', 'UTF-8') + end + + def def_to_utf_8_from_euc_jp + def_uconv_convert_if_can('euctou8', 'UTF-8', 'EUC-JP') + end + + def def_to_shift_jis_from_utf_8 + def_uconv_convert_if_can('u8tosjis', 'Shift_JIS', 'UTF-8') + end + + def def_to_utf_8_from_shift_jis + def_uconv_convert_if_can('sjistou8', 'UTF-8', 'Shift_JIS') + end + + def def_to_euc_jp_from_shift_jis + require "nkf" + def_convert do |value| + "NKF.nkf('-Se', #{value})" + end + end + + def def_to_shift_jis_from_euc_jp + require "nkf" + def_convert do |value| + "NKF.nkf('-Es', #{value})" + end + end + + def def_to_euc_jp_from_iso_2022_jp + require "nkf" + def_convert do |value| + "NKF.nkf('-Je', #{value})" + end + end + + def def_to_iso_2022_jp_from_euc_jp + require "nkf" + def_convert do |value| + "NKF.nkf('-Ej', #{value})" + end + end + + def def_to_utf_8_from_iso_8859_1 + def_convert do |value| + "#{value}.unpack('C*').pack('U*')" + end + end + + def def_to_iso_8859_1_from_utf_8 + def_convert do |value| + <<-EOC + array_utf8 = #{value}.unpack('U*') + array_enc = [] + array_utf8.each do |num| + if num <= 0xFF + array_enc << num + else + array_enc.concat "&\#\#{num};".unpack('C*') + end + end + array_enc.pack('C*') + EOC + end + end + + end + +end diff --git a/lib/rss/syndication.rb b/lib/rss/syndication.rb new file mode 100644 index 0000000000..8c10688b57 --- /dev/null +++ b/lib/rss/syndication.rb @@ -0,0 +1,83 @@ +require "rss/1.0" + +module RSS + + SY_PREFIX = 'sy' + SY_URI = "http://purl.org/rss/1.0/modules/syndication/" + + RDF.install_ns(SY_PREFIX, SY_URI) + + module SyndicationModel + + extend BaseModel + + ELEMENTS = [] + + def self.included(mod) + mod.module_eval(<<-EOC) + %w(updatePeriod updateFrequency).each do |x| + install_text_element("\#{SY_PREFIX}_\#{x}") + end + + %w(updateBase).each do |x| + install_date_element("\#{SY_PREFIX}_\#{x}", 'w3cdtf', x) + end + + alias_method(:_sy_updatePeriod=, :sy_updatePeriod=) + def sy_updatePeriod=(new_value) + new_value = new_value.strip + validate_sy_updatePeriod(new_value) if @do_validate + self._sy_updatePeriod = new_value + end + + alias_method(:_sy_updateFrequency=, :sy_updateFrequency=) + def sy_updateFrequency=(new_value) + new_value = new_value.strip + validate_sy_updateFrequency(new_value) if @do_validate + self._sy_updateFrequency = new_value.to_i + end + EOC + end + + def sy_validate(tags) + counter = {} + ELEMENTS.each do |x| + counter[x] = 0 + end + + tags.each do |tag| + key = "#{SY_PREFIX}_#{tag}" + raise UnknownTagError.new(tag, SY_URI) unless counter.has_key?(key) + counter[key] += 1 + raise TooMuchTagError.new(tag, tag_name) if counter[key] > 1 + end + end + + private + SY_UPDATEPERIOD_AVAILABLE_VALUES = %w(hourly daily weekly monthly yearly) + def validate_sy_updatePeriod(value) + unless SY_UPDATEPERIOD_AVAILABLE_VALUES.include?(value) + raise NotAvailableValueError.new("updatePeriod", value) + end + end + + SY_UPDATEFREQUENCY_AVAILABLE_RE = /\A\s*\+?\d+\s*\z/ + def validate_sy_updateFrequency(value) + if SY_UPDATEFREQUENCY_AVAILABLE_RE !~ value + raise NotAvailableValueError.new("updateFrequency", value) + end + end + + end + + class RDF + class Channel; include SyndicationModel; end + end + + prefix_size = SY_PREFIX.size + 1 + SyndicationModel::ELEMENTS.uniq! + SyndicationModel::ELEMENTS.each do |x| + BaseListener.install_get_text_element(x[prefix_size..-1], SY_URI, "#{x}=") + end + +end diff --git a/lib/rss/taxonomy.rb b/lib/rss/taxonomy.rb new file mode 100644 index 0000000000..5b11d338e0 --- /dev/null +++ b/lib/rss/taxonomy.rb @@ -0,0 +1,32 @@ +# Experimental + +require "rss/1.0" + +module RSS + + TAXO_PREFIX = "taxo" + TAXO_NS = "http://purl.org/rss/1.0/modules/taxonomy/" + + Element.install_ns(TAXO_PREFIX, TAXO_NS) + + TAXO_ELEMENTS = [] + + %w(link).each do |x| + if const_defined? :Listener + Listener.install_get_text_element(x, TAXO_NS, "#{TAXO_PREFIX}_#{x}=") + end + TAXO_ELEMENTS << "#{TAXO_PREFIX}_#{x}" + end + + module TaxonomyModel + attr_writer(*%w(title description creator subject publisher + contributor date format identifier source + language relation coverage rights).collect{|x| "#{TAXO_PREFIX}_#{x}"}) + end + + class Channel; extend TaxonomyModel; end + class Item; extend TaxonomyModel; end + class Image; extend TaxonomyModel; end + class TextInput; extend TaxonomyModel; end + +end diff --git a/lib/rss/utils.rb b/lib/rss/utils.rb new file mode 100644 index 0000000000..ae6f69bcf1 --- /dev/null +++ b/lib/rss/utils.rb @@ -0,0 +1,17 @@ +module RSS + + module Utils + + def get_file_and_line_from_caller(i=0) + file, line, = caller[i].split(':') + [file, line.to_i] + end + + def html_escape(s) + s.to_s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/</, "<") + end + alias h html_escape + + end + +end diff --git a/lib/rss/xml-stylesheet.rb b/lib/rss/xml-stylesheet.rb new file mode 100644 index 0000000000..7862c4f278 --- /dev/null +++ b/lib/rss/xml-stylesheet.rb @@ -0,0 +1,94 @@ +require "rss/utils" + +module RSS + + module XMLStyleSheetMixin + attr_accessor :xml_stylesheets + def initialize(*args) + super + @xml_stylesheets = [] + end + + private + def xml_stylesheet_pi + xsss = @xml_stylesheets.collect do |xss| + pi = xss.to_s + pi = nil if /\A\s*\z/ =~ pi + pi + end.compact + xsss.push("") unless xsss.empty? + xsss.join("\n") + end + end + + class XMLStyleSheet + + include Utils + + ATTRIBUTES = %w(href type title media charset alternate) + + GUESS_TABLE = { + "xsl" => "text/xsl", + "css" => "text/css", + } + + attr_accessor(*ATTRIBUTES) + attr_accessor(:do_validate) + def initialize(*attrs) + @do_validate = true + ATTRIBUTES.each do |attr| + self.send("#{attr}=", nil) + end + vars = ATTRIBUTES.dup + vars.unshift(:do_validate) + attrs.each do |name, value| + if vars.include?(name.to_s) + self.send("#{name}=", value) + end + end + end + + def to_s + rv = "" + if @href + rv << %Q[<?xml-stylesheet] + ATTRIBUTES.each do |name| + if self.send(name) + rv << %Q[ #{name}="#{h self.send(name)}"] + end + end + rv << %Q[?>] + end + rv + end + + remove_method(:href=) + def href=(value) + @href = value + if @href and @type.nil? + @type = guess_type(@href) + end + @href + end + + remove_method(:alternate=) + def alternate=(value) + if value.nil? or /\A(?:yes|no)\z/ =~ value + @alternate = value + else + if @do_validate + args = ["?xml-stylesheet?", %Q[alternate="#{value}"]] + raise NotAvailableValueError.new(*args) + end + end + @alternate + end + + private + def guess_type(filename) + /\.([^.]+)/ =~ filename + GUESS_TABLE[$1] + end + + end +end diff --git a/lib/rss/xmlscanner.rb b/lib/rss/xmlscanner.rb new file mode 100644 index 0000000000..4ab997062d --- /dev/null +++ b/lib/rss/xmlscanner.rb @@ -0,0 +1,102 @@ +require 'xmlscan/scanner' + +module RSS + + class XMLScanParser < BaseParser + + private + def listener + XMLScanListener + end + + def _parse + begin + XMLScan::XMLScanner.new(@listener).parse(@rss) + rescue XMLScan::Error => e + raise NotWellFormedError.new(e.lineno){e.message} + end + end + + end + + class XMLScanListener < BaseListener + + include XMLScan::Visitor + include ListenerMixin + + ENTITIES = { + 'lt' => '<', + 'gt' => '>', + 'amp' => '&', + 'quot' => '"', + 'apos' => '\'' + } + + def on_xmldecl_version(str) + @version = str + end + + def on_xmldecl_encoding(str) + @encoding = str + end + + def on_xmldecl_standalone(str) + @standalone = str + end + + def on_xmldecl_end + xmldecl(@version, @encoding, @standalone) + end + + alias_method(:on_pi, :instruction) + alias_method(:on_chardata, :text) + alias_method(:on_cdata, :text) + + def on_etag(name) + tag_end(name) + end + + def on_entityref(ref) + text(ENTITIES[ref]) + end + + def on_charref(code) + text([code].pack('U')) + end + + alias_method(:on_charref_hex, :on_charref) + + def on_stag(name) + @attrs = {} + end + + def on_attribute(name) + @attrs[name] = @current_attr = '' + end + + def on_attr_value(str) + @current_attr << str + end + + def on_attr_entityref(ref) + @current_attr << ENTITIES[ref] + end + + def on_attr_charref(code) + @current_attr << [code].pack('U') + end + + alias_method(:on_attr_charref_hex, :on_attr_charref) + + def on_stag_end(name) + tag_start(name, @attrs) + end + + def on_stag_end_empty(name) + tag_start(name, @attrs) + tag_end(name) + end + + end + +end diff --git a/test/rss/rss-assertions.rb b/test/rss/rss-assertions.rb new file mode 100644 index 0000000000..db9c39dd28 --- /dev/null +++ b/test/rss/rss-assertions.rb @@ -0,0 +1,127 @@ +# -*- tab-width: 2 -*- vim: ts=2 + +module RSS + module Assertions + + def assert_parse(rss, assert_method, *args) + send("assert_#{assert_method}", *args) do + ::RSS::Parser.parse(rss) + end + send("assert_#{assert_method}", *args) do + ::RSS::Parser.parse(rss, false).validate + end + end + + def assert_ns(prefix, uri) + _wrap_assertion do + begin + yield + flunk("Not raise NSError") + rescue ::RSS::NSError => e + assert_equal(prefix, e.prefix) + assert_equal(uri, e.uri) + end + end + end + + def assert_missing_tag(tag, parent) + _wrap_assertion do + begin + yield + flunk("Not raise MissingTagError") + rescue ::RSS::MissingTagError => e + assert_equal(tag, e.tag) + assert_equal(parent, e.parent) + end + end + end + + def assert_too_much_tag(tag, parent) + _wrap_assertion do + begin + yield + flunk("Not raise TooMuchTagError") + rescue ::RSS::TooMuchTagError => e + assert_equal(tag, e.tag) + assert_equal(parent, e.parent) + end + end + end + + def assert_missing_attribute(tag, attrname) + _wrap_assertion do + begin + yield + flunk("Not raise MissingAttributeError") + rescue ::RSS::MissingAttributeError => e + assert_equal(tag, e.tag) + assert_equal(attrname, e.attribute) + end + end + end + + def assert_not_excepted_tag(tag, parent) + _wrap_assertion do + begin + yield + flunk("Not raise NotExceptedTagError") + rescue ::RSS::NotExceptedTagError => e + assert_equal(tag, e.tag) + assert_equal(parent, e.parent) + end + end + end + + def assert_not_available_value(tag, value) + _wrap_assertion do + begin + yield + flunk("Not raise NotAvailableValueError") + rescue ::RSS::NotAvailableValueError => e + assert_equal(tag, e.tag) + assert_equal(value, e.value) + end + end + end + + def assert_xml_stylesheet_attrs(xsl, attrs) + _wrap_assertion do + normalized_attrs = {} + attrs.each do |name, value| + normalized_attrs[name.to_s] = value + end + ::RSS::XMLStyleSheet::ATTRIBUTES.each do |name| + assert_equal(normalized_attrs[name], xsl.send(name)) + end + end + end + + def assert_xml_stylesheet(target, xsl, attrs) + _wrap_assertion do + if attrs.has_key?(:href) + if !attrs.has_key?(:type) and attrs.has_key?(:guess_type) + attrs[:type] = attrs[:guess_type] + end + assert_equal("xml-stylesheet", target) + assert_xml_stylesheet_attrs(xsl, attrs) + else + assert_nil(target) + assert_equal("", xsl.to_s) + end + end + end + + def assert_xml_stylesheet_pis(attrs_ary) + rdf = ::RSS::RDF.new() + xss_strs = [] + attrs_ary.each do |attrs| + xss = ::RSS::XMLStyleSheet.new(*attrs) + xss_strs.push(xss.to_s) + rdf.xml_stylesheets.push(xss) + end + pi_str = rdf.to_s.gsub(/<\?xml .*\n/, "").gsub(/\s*<rdf:RDF.*\z/m, "") + assert_equal(xss_strs.join("\n"), pi_str) + end + + end +end diff --git a/test/rss/rss-testcase.rb b/test/rss/rss-testcase.rb new file mode 100644 index 0000000000..349e3d4dc1 --- /dev/null +++ b/test/rss/rss-testcase.rb @@ -0,0 +1,164 @@ +# -*- tab-width: 2 -*- vim: ts=2 + +require "test/unit" +require 'rss-assertions' + +module RSS + class TestCase < Test::Unit::TestCase + + include RSS + include Assertions + + XMLDECL_VERSION = "1.0" + XMLDECL_ENCODING = "UTF-8" + XMLDECL_STANDALONE = "no" + + RDF_ABOUT = "http://www.xml.com/xml/news.rss" + RDF_RESOURCE = "http://xml.com/universal/images/xml_tiny.gif" + TITLE_VALUE = "XML.com" + LINK_VALUE = "http://xml.com/pub" + URL_VALUE = "http://xml.com/universal/images/xml_tiny.gif" + NAME_VALUE = "hogehoge" + DESCRIPTION_VALUE = " + XML.com features a rich mix of information and services + for the XML community. + " + RESOURCES = [ + "http://xml.com/pub/2000/08/09/xslt/xslt.html", + "http://xml.com/pub/2000/08/09/rdfdb/index.html", + ] + + def default_test + # This class isn't tested + end + + private + def make_xmldecl(v=XMLDECL_VERSION, e=XMLDECL_ENCODING, s=XMLDECL_STANDALONE) + rv = "<?xml version='#{v}'" + rv << " encoding='#{e}'" if e + rv << " standalone='#{s}'" if s + rv << "?>" + rv + end + + def make_RDF(content=nil, xmlns=[]) + <<-EORSS +#{make_xmldecl} +<rdf:RDF xmlns="#{URI}" xmlns:rdf="#{RDF::URI}" +#{xmlns.collect {|pre, uri| "xmlns:#{pre}='#{uri}'"}.join(' ')}> +#{block_given? ? yield : content} +</rdf:RDF> +EORSS + end + + def make_channel(content=nil) + <<-EOC +<channel rdf:about="#{RDF_ABOUT}"> + <title>#{TITLE_VALUE}</title> + <link>#{LINK_VALUE}</link> + <description>#{DESCRIPTION_VALUE}</description> + + <image rdf:resource="#{RDF_RESOURCE}" /> + + <items> + <rdf:Seq> +#{RESOURCES.collect do |res| '<rdf:li resource="' + res + '" />' end.join("\n")} + </rdf:Seq> + </items> + + <textinput rdf:resource="#{RDF_RESOURCE}" /> + +#{block_given? ? yield : content} +</channel> +EOC + end + + def make_image(content=nil) + <<-EOI +<image rdf:about="#{RDF_ABOUT}"> + <title>#{TITLE_VALUE}</title> + <url>#{URL_VALUE}</url> + <link>#{LINK_VALUE}</link> +#{block_given? ? yield : content} +</image> +EOI + end + + def make_item(content=nil) + <<-EOI +<item rdf:about="#{RDF_ABOUT}"> + <title>#{TITLE_VALUE}</title> + <link>#{LINK_VALUE}</link> + <description>#{DESCRIPTION_VALUE}</description> +#{block_given? ? yield : content} +</item> +EOI + end + + def make_textinput(content=nil) + <<-EOT +<textinput rdf:about="#{RDF_ABOUT}"> + <title>#{TITLE_VALUE}</title> + <description>#{DESCRIPTION_VALUE}</description> + <name>#{NAME_VALUE}</name> + <link>#{LINK_VALUE}</link> +#{block_given? ? yield : content} +</textinput> +EOT + end + + def make_sample_RDF + make_RDF(<<-EOR) +#{make_channel} +#{make_image} +#{make_item} +#{make_textinput} +EOR + end + + def make_Rss2(content=nil, xmlns=[]) + <<-EORSS +#{make_xmldecl} +<rss version="2.0" +#{xmlns.collect {|pre, uri| "xmlns:#{pre}='#{uri}'"}.join(' ')}> +#{block_given? ? yield : content} +</rss> +EORSS + end + + def make_channel2(content=nil) + <<-EOC +<channel> + <title>#{TITLE_VALUE}</title> + <link>#{LINK_VALUE}</link> + <description>#{DESCRIPTION_VALUE}</description> + + <image> + <url>#{RDF_RESOURCE}</url> + <title>#{TITLE_VALUE}</title> + <link>#{LINK_VALUE}</link> + </image> + +#{RESOURCES.collect do |res| '<item><link>' + res + '</link></item>' end.join("\n")} + + <textInput> + <link>#{RDF_RESOURCE}</link> + </textInput> + +#{block_given? ? yield : content} +</channel> +EOC + end + + def make_item2(content=nil) + <<-EOI +<item> + <title>#{TITLE_VALUE}</title> + <link>#{LINK_VALUE}</link> + <description>#{DESCRIPTION_VALUE}</description> +#{block_given? ? yield : content} +</item> +EOI + end + end +end diff --git a/test/rss/test_1.0.rb b/test/rss/test_1.0.rb new file mode 100644 index 0000000000..6ed4d90b15 --- /dev/null +++ b/test/rss/test_1.0.rb @@ -0,0 +1,259 @@ +# -*- tab-width: 2 -*- vim: ts=2 + +require "rexml/document" + +require "rss-testcase" + +require "rss/1.0" + +module RSS + class TestCore < TestCase + + def setup + + @rdf_prefix = "rdf" + @rdf_uri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + @uri = "http://purl.org/rss/1.0/" + + end + + def test_RDF + + version = "1.0" + encoding = "UTF-8" + standalone = "no" + + rdf = RDF.new(version, encoding, standalone) + + doc = REXML::Document.new(rdf.to_s(false)) + + xmldecl = doc.xml_decl + + %w(version encoding standalone).each do |x| + assert_equal(instance_eval(x), xmldecl.send(x)) + end + + assert_equal(@rdf_uri, doc.root.namespace) + + end + + def test_not_displayed_xml_stylesheets + rdf = RDF.new() + plain_rdf = rdf.to_s + 3.times do + rdf.xml_stylesheets.push(XMLStyleSheet.new) + assert_equal(plain_rdf, rdf.to_s) + end + end + + def test_xml_stylesheets + [ + [{:href => "a.xsl", :type => "text/xsl"}], + [ + {:href => "a.xsl", :type => "text/xsl"}, + {:href => "a.css", :type => "text/css"}, + ], + ].each do |attrs_ary| + assert_xml_stylesheet_pis(attrs_ary) + end + end + + def test_channel + about = "http://hoge.com" + title = "fugafuga" + link = "http://hoge.com" + description = "fugafugafugafuga" + resource = "http://hoge.com/hoge.png" + image = RDF::Channel::Image.new(resource) + items = RDF::Channel::Items.new + textinput = RDF::Channel::Textinput.new(resource) + + channel = RDF::Channel.new(about) + %w(title link description image items textinput).each do |x| + channel.send("#{x}=", instance_eval(x)) + end + + doc = REXML::Document.new(make_RDF(channel.to_s)) + c = doc.root.elements[1] + + assert_equal(about, c.attributes["about"]) + %w(title link description image textinput).each do |x| + elem = c.elements[x] + assert_equal(x, elem.name) + assert_equal(@uri, elem.namespace) + if x == "image" or x == "textinput" + excepted = resource + res = elem.attributes.get_attribute("resource") + assert_equal(@rdf_uri, res.namespace) + value = res.value + else + excepted = instance_eval(x) + value = elem.text + end + assert_equal(excepted, value) + end + assert_equal(@uri, c.elements["items"].namespace) + assert_equal("items", c.elements["items"].name) + + end + + def test_channel_image + + resource = "http://hoge.com/hoge.png" + image = RDF::Channel::Image.new(resource) + + doc = REXML::Document.new(make_RDF(image.to_s)) + i = doc.root.elements[1] + + assert_equal("image", i.name) + assert_equal(@uri, i.namespace) + + res = i.attributes.get_attribute("resource") + + assert_equal(@rdf_uri, res.namespace) + assert_equal(resource, res.value) + + end + + def test_channel_textinput + + resource = "http://hoge.com/hoge.png" + textinput = RDF::Channel::Textinput.new(resource) + + doc = REXML::Document.new(make_RDF(textinput.to_s)) + t = doc.root.elements[1] + + assert_equal("textinput", t.name) + assert_equal(@uri, t.namespace) + + res = t.attributes.get_attribute("resource") + + assert_equal(@rdf_uri, res.namespace) + assert_equal(resource, res.value) + + end + + def test_items + + items = RDF::Channel::Items.new + + doc = REXML::Document.new(make_RDF(items.to_s)) + i = doc.root.elements[1] + + assert_equal("items", i.name) + assert_equal(@uri, i.namespace) + + assert_equal(1, i.elements.size) + assert_equal("Seq", i.elements[1].name) + assert_equal(@rdf_uri, i.elements[1].namespace) + + end + + def test_seq + + seq = RDF::Seq.new + + doc = REXML::Document.new(make_RDF(seq.to_s)) + s = doc.root.elements[1] + + assert_equal("Seq", s.name) + assert_equal(@rdf_uri, s.namespace) + + end + + def test_li + + resource = "http://hoge.com/" + li = RDF::Li.new(resource) + + doc = REXML::Document.new(make_RDF(li.to_s)) + l = doc.root.elements[1] + + assert_equal("li", l.name) + assert_equal(@rdf_uri, l.namespace(l.prefix)) + + res = l.attributes.get_attribute("resource") + + assert_equal('', res.instance_eval("@prefix")) + assert_equal(resource, res.value) + + end + + def test_image + + about = "http://hoge.com" + title = "fugafuga" + url = "http://hoge.com/hoge" + link = "http://hoge.com/fuga" + + image = RDF::Image.new(about) + %w(title url link).each do |x| + image.send("#{x}=", instance_eval(x)) + end + + doc = REXML::Document.new(make_RDF(image.to_s)) + i = doc.root.elements[1] + + assert_equal(about, i.attributes["about"]) + %w(title url link).each do |x| + elem = i.elements[x] + assert_equal(x, elem.name) + assert_equal(@uri, elem.namespace) + assert_equal(instance_eval(x), elem.text) + end + + end + + def test_item + + about = "http://hoge.com" + title = "fugafuga" + link = "http://hoge.com/fuga" + description = "hogehogehoge" + + item = RDF::Item.new(about) + %w(title link description).each do |x| + item.send("#{x}=", instance_eval(x)) + end + + doc = REXML::Document.new(make_RDF(item.to_s)) + i = doc.root.elements[1] + + assert_equal(about, i.attributes["about"]) + %w(title link description).each do |x| + elem = i.elements[x] + assert_equal(x, elem.name) + assert_equal(@uri, elem.namespace) + assert_equal(instance_eval(x), elem.text) + end + + end + + def test_textinput + + about = "http://hoge.com" + title = "fugafuga" + link = "http://hoge.com/fuga" + name = "foo" + description = "hogehogehoge" + + textinput = RDF::Textinput.new(about) + %w(title link name description).each do |x| + textinput.send("#{x}=", instance_eval(x)) + end + + doc = REXML::Document.new(make_RDF(textinput.to_s)) + t = doc.root.elements[1] + + assert_equal(about, t.attributes["about"]) + %w(title link name description).each do |x| + elem = t.elements[x] + assert_equal(x, elem.name) + assert_equal(@uri, elem.namespace) + assert_equal(instance_eval(x), elem.text) + end + + end + + end +end diff --git a/test/rss/test_accessor.rb b/test/rss/test_accessor.rb new file mode 100644 index 0000000000..08f565e596 --- /dev/null +++ b/test/rss/test_accessor.rb @@ -0,0 +1,26 @@ +# -*- tab-width: 2 -*- vim: ts=2 + +require "rss-testcase" + +require "rss/1.0" +require "rss/2.0" + +module RSS + class TestAccessor < TestCase + + def test_date + channel = Rss::Channel.new + channel.do_validate = false + channel.pubDate = nil + assert_nil(channel.pubDate) + + time = Time.now + channel.pubDate = time + assert_equal(time, channel.pubDate) + + channel.pubDate = nil + assert_nil(channel.pubDate) + end + + end +end diff --git a/test/rss/test_content.rb b/test/rss/test_content.rb new file mode 100644 index 0000000000..24877d3ec9 --- /dev/null +++ b/test/rss/test_content.rb @@ -0,0 +1,97 @@ +# -*- tab-width: 2 -*- vim: ts=2 + +require "cgi" +require "rexml/document" + +require "rss-testcase" + +require "rss/1.0" +require "rss/content" + +module RSS + class TestContent < TestCase + + def setup + @prefix = "content" + @uri = "http://purl.org/rss/1.0/modules/content/" + + @parents = %w(item) + + @elems = { + :encoded => "<em>ATTENTION</em>", + } + + @content_nodes = @elems.collect do |name, value| + "<#{@prefix}:#{name}>#{CGI.escapeHTML(value.to_s)}</#{@prefix}:#{name}>" + end.join("\n") + + @rss_source = make_RDF(<<-EOR, {@prefix => @uri}) +#{make_channel()} +#{make_image()} +#{make_item(@content_nodes)} +#{make_textinput()} +EOR + + @rss = Parser.parse(@rss_source) + end + + def test_parser + + assert_nothing_raised do + Parser.parse(@rss_source) + end + + @elems.each do |tag, value| + assert_too_much_tag(tag.to_s, "item") do + Parser.parse(make_RDF(<<-EOR, {@prefix => @uri})) +#{make_channel()} +#{make_item(("<" + @prefix + ":" + tag.to_s + ">" + + CGI.escapeHTML(value.to_s) + + "</" + @prefix + ":" + tag.to_s + ">") * 2)} +EOR + end + end + + end + + def test_accessor + + new_value = { + :encoded => "<![CDATA[<it>hoge</it>]]>", + } + + @elems.each do |name, value| + @parents.each do |parent| + meth = "#{RSS::CONTENT_PREFIX}_#{name}" + assert_equal(value, @rss.send(parent).send(meth)) + @rss.send(parent).send("#{meth}=", new_value[name].to_s) + assert_equal(new_value[name], @rss.send(parent).send(meth)) + end + end + + end + + def test_to_s + + @elems.each do |name, value| + excepted = "<#{@prefix}:#{name}>#{CGI.escapeHTML(value)}</#{@prefix}:#{name}>" + @parents.each do |parent| + meth = "#{RSS::CONTENT_PREFIX}_#{name}_element" + assert_equal(excepted, @rss.send(parent).send(meth)) + end + end + + REXML::Document.new(@rss_source).root.each_element do |parent| + if @parents.include?(parent.name) + parent.each_element do |elem| + if elem.namespace == @uri + assert_equal(elem.text, @elems[elem.name.intern].to_s) + end + end + end + end + + end + + end +end diff --git a/test/rss/test_dublincore.rb b/test/rss/test_dublincore.rb new file mode 100644 index 0000000000..bf5a672837 --- /dev/null +++ b/test/rss/test_dublincore.rb @@ -0,0 +1,126 @@ +# -*- tab-width: 2 -*- vim: ts=2 + +require "cgi" +require "rexml/document" + +require "rss-testcase" + +require "rss/1.0" +require "rss/dublincore" + +module RSS + class TestDublinCore < TestCase + + def setup + @prefix = "dc" + @uri = "http://purl.org/dc/elements/1.1/" + + @parents = %w(channel image item textinput) + + t = Time.iso8601("2000-01-01T12:00:05+00:00") + class << t + alias_method(:to_s, :iso8601) + end + + @elems = { + :title => "hoge", + :description => + " XML is placing increasingly heavy loads on + the existing technical infrastructure of the Internet.", + :creator => "Rael Dornfest (mailto:rael@oreilly.com)", + :subject => "XML", + :publisher => "The O'Reilly Network", + :contributor => "hogehoge", + :type => "fugafuga", + :format => "hohoho", + :identifier => "fufufu", + :source => "barbar", + :language => "ja", + :relation => "cococo", + :rights => "Copyright (c) 2000 O'Reilly & Associates, Inc.", + :date => t, + } + + @dc_nodes = @elems.collect do |name, value| + "<#{@prefix}:#{name}>#{value}</#{@prefix}:#{name}>" + end.join("\n") + + @rss_source = make_RDF(<<-EOR, {@prefix => @uri}) +#{make_channel(@dc_nodes)} +#{make_image(@dc_nodes)} +#{make_item(@dc_nodes)} +#{make_textinput(@dc_nodes)} +EOR + + @rss = Parser.parse(@rss_source) + end + + def test_parser + + assert_nothing_raised do + Parser.parse(@rss_source) + end + + @elems.each do |tag, value| + assert_too_much_tag(tag.to_s, "channel") do + Parser.parse(make_RDF(<<-EOR, {@prefix => @uri})) +#{make_channel(("<" + @prefix + ":" + tag.to_s + ">" + + value.to_s + + "</" + @prefix + ":" + tag.to_s + ">") * 2)} +#{make_item} +EOR + end + end + + end + + def test_accessor + + new_value = "hoge" + + @elems.each do |name, value| + @parents.each do |parent| + parsed_value = @rss.send(parent).send("dc_#{name}") + if parsed_value.kind_of?(String) + parsed_value = CGI.escapeHTML(parsed_value) + end + assert_equal(value, parsed_value) + if name == :date + t = Time.iso8601("2003-01-01T02:30:23+09:00") + class << t + alias_method(:to_s, :iso8601) + end + @rss.send(parent).send("dc_#{name}=", t.iso8601) + assert_equal(t, @rss.send(parent).send("dc_#{name}")) + else + @rss.send(parent).send("dc_#{name}=", new_value) + assert_equal(new_value, @rss.send(parent).send("dc_#{name}")) + end + end + end + + end + + def test_to_s + + @elems.each do |name, value| + excepted = "<#{@prefix}:#{name}>#{value}</#{@prefix}:#{name}>" + @parents.each do |parent| + assert_equal(excepted, @rss.send(parent).send("dc_#{name}_element")) + end + end + + REXML::Document.new(@rss_source).root.each_element do |parent| + if @parents.include?(parent.name) + parent.each_element do |elem| + if elem.namespace == @uri + assert_equal(CGI.escapeHTML(elem.text), @elems[elem.name.intern].to_s) + end + end + end + end + + end + + end +end diff --git a/test/rss/test_syndication.rb b/test/rss/test_syndication.rb new file mode 100644 index 0000000000..0eec3bacc6 --- /dev/null +++ b/test/rss/test_syndication.rb @@ -0,0 +1,125 @@ +# -*- tab-width: 2 -*- vim: ts=2 + +require "cgi" +require "rexml/document" + +require "rss-testcase" + +require "rss/1.0" +require "rss/syndication" + +module RSS + class TestSyndication < TestCase + + def setup + @prefix = "sy" + @uri = "http://purl.org/rss/1.0/modules/syndication/" + + @parents = %w(channel) + + t = Time.iso8601("2000-01-01T12:00:05+00:00") + class << t + alias_method(:to_s, :iso8601) + end + + @elems = { + :updatePeriod => "hourly", + :updateFrequency => 2, + :updateBase => t, + } + + @sy_nodes = @elems.collect do |name, value| + "<#{@prefix}:#{name}>#{CGI.escapeHTML(value.to_s)}</#{@prefix}:#{name}>" + end.join("\n") + + @rss_source = make_RDF(<<-EOR, {@prefix => @uri}) +#{make_channel(@sy_nodes)} +#{make_image()} +#{make_item()} +#{make_textinput()} +EOR + + @rss = Parser.parse(@rss_source) + end + + def test_parser + + assert_nothing_raised do + Parser.parse(@rss_source) + end + + @elems.each do |tag, value| + assert_too_much_tag(tag.to_s, "channel") do + Parser.parse(make_RDF(<<-EOR, {@prefix => @uri})) +#{make_channel(("<" + @prefix + ":" + tag.to_s + ">" + + CGI.escapeHTML(value.to_s) + + "</" + @prefix + ":" + tag.to_s + ">") * 2)} +#{make_item} +EOR + end + end + + end + + def test_accessor + + t = Time.iso8601("2003-01-01T12:00:23+09:00") + class << t + alias_method(:to_s, :iso8601) + end + + new_value = { + :updatePeriod => "daily", + :updateFrequency => +11, + :updateBase => t, + } + + @elems.each do |name, value| + @parents.each do |parent| + assert_equal(value, @rss.send(parent).send("sy_#{name}")) + @rss.send(parent).send("sy_#{name}=", new_value[name].to_s) + assert_equal(new_value[name], @rss.send(parent).send("sy_#{name}")) + end + end + + %w(hourly daily weekly monthly yearly).each do |x| + @parents.each do |parent| + assert_nothing_raised do + @rss.send(parent).sy_updatePeriod = x + end + end + end + + %w(-2 0.3 -0.4).each do |x| + @parents.each do |parent| + assert_not_available_value("updateBase", x) do + @rss.send(parent).sy_updateBase = x + end + end + end + + end + + def test_to_s + + @elems.each do |name, value| + excepted = "<#{@prefix}:#{name}>#{value}</#{@prefix}:#{name}>" + @parents.each do |parent| + assert_equal(excepted, @rss.send(parent).send("sy_#{name}_element")) + end + end + + REXML::Document.new(@rss_source).root.each_element do |parent| + if @parents.include?(parent.name) + parent.each_element do |elem| + if elem.namespace == @uri + assert_equal(elem.text, @elems[elem.name.intern].to_s) + end + end + end + end + + end + + end +end diff --git a/test/rss/test_trackback.rb b/test/rss/test_trackback.rb new file mode 100644 index 0000000000..141ef50779 --- /dev/null +++ b/test/rss/test_trackback.rb @@ -0,0 +1,137 @@ +# -*- tab-width: 2 -*- vim: ts=2 + +require "cgi" +require "rexml/document" + +require "rss-testcase" + +require "rss/1.0" +require "rss/2.0" +require "rss/trackback" + +module RSS + class TestTrackBack < TestCase + + def setup + @prefix = "trackback" + @uri = "http://madskills.com/public/xml/rss/module/trackback/" + + @parents = %w(item) + + @elems = { + :ping => "http://bar.com/tb.cgi?tb_id=rssplustrackback", + :about => "http://foo.com/trackback/tb.cgi?tb_id=20020923", + } + + @content_nodes = @elems.collect do |name, value| + "<#{@prefix}:#{name} rdf:resource=\"#{CGI.escapeHTML(value.to_s)}\"/>" + end.join("\n") + + @content_nodes2 = @elems.collect do |name, value| + "<#{@prefix}:#{name}>#{CGI.escapeHTML(value.to_s)}</#{@prefix}:#{name}>" + end.join("\n") + + @rss_source = make_RDF(<<-EOR, {@prefix => @uri}) +#{make_channel()} +#{make_image()} +#{make_item(@content_nodes)} +#{make_textinput()} +EOR + + @rss = Parser.parse(@rss_source) + + @rss2_source = make_Rss2(nil, {@prefix => @uri}) do + make_channel2(nil) do + make_item2(@content_nodes2) + end + end + + @rss2 = Parser.parse(@rss2_source, false) + end + + def test_parser + + assert_nothing_raised do + Parser.parse(@rss_source) + end + + @elems.find_all{|k, v| k == :ping}.each do |tag, value| + assert_too_much_tag(tag.to_s, "item") do + Parser.parse(make_RDF(<<-EOR, {@prefix => @uri})) +#{make_channel()} +#{make_item(("<" + @prefix + ":" + tag.to_s + " rdf:resource=\"" + + CGI.escapeHTML(value.to_s) + + "\"/>") * 2)} +EOR + end + end + + @elems.find_all{|k, v| k == :about}.each do |tag, value| + assert_missing_tag("trackback:ping", "item") do + Parser.parse(make_RDF(<<-EOR, {@prefix => @uri})) +#{make_channel()} +#{make_item(("<" + @prefix + ":" + tag.to_s + " rdf:resource=\"" + + CGI.escapeHTML(value.to_s) + + "\"/>") * 2)} +EOR + end + + end + + end + + def test_accessor + + new_value = { + :ping => "http://baz.com/trackback/tb.cgi?tb_id=20030808", + :about => "http://hoge.com/trackback/tb.cgi?tb_id=90030808", + } + + @elems.each do |name, value| + @parents.each do |parent| + accessor = "#{RSS::TRACKBACK_PREFIX}_#{name}" + target_accessor = "resource" + target = @rss.send(parent).send(accessor) + target2 = @rss2.channel.send(parent, -1) + assert_equal(value, target.send(target_accessor)) + assert_equal(value, target2.send(accessor)) + target.send("#{target_accessor}=", new_value[name].to_s) + if name == :about + # abount is zero or more + target2.send("#{accessor}=", 0, new_value[name].to_s) + else + target2.send("#{accessor}=", new_value[name].to_s) + end + assert_equal(new_value[name], target.send(target_accessor)) + assert_equal(new_value[name], target2.send(accessor)) + end + end + + end + + def test_to_s + + @elems.each do |name, value| + excepted = %Q!<#{@prefix}:#{name} rdf:resource="#{CGI.escapeHTML(value)}"/>! + @parents.each do |parent| + meth = "#{RSS::TRACKBACK_PREFIX}_#{name}_element" + meth << "s" if name == :about + assert_equal(excepted, @rss.send(parent).send(meth)) + end + end + + REXML::Document.new(@rss_source).root.each_element do |parent| + if @parents.include?(parent.name) + parent.each_element do |elem| + if elem.namespace == @uri + assert_equal(elem.attributes["resource"], @elems[elem.name.intern]) + end + end + end + end + + end + + end +end + diff --git a/test/rss/test_xml-stylesheet.rb b/test/rss/test_xml-stylesheet.rb new file mode 100644 index 0000000000..ed056257d6 --- /dev/null +++ b/test/rss/test_xml-stylesheet.rb @@ -0,0 +1,110 @@ +# -*- tab-width: 2 -*- vim: ts=2 + +require "rexml/document" + +require "rss-testcase" + +require "rss/1.0" +require "rss/xml-stylesheet" + +module RSS + class TestXMLStyleSheet < TestCase + + def test_accessor + [ + {:href => "a.xsl", :type => "text/xsl"}, + {:media => "print", :title => "FOO"}, + {:charset => "UTF-8", :alternate => "yes"}, + ].each do |attrs| + assert_xml_stylesheet_attrs(XMLStyleSheet.new(*attrs), attrs) + end + end + + def test_to_s + [ + {:href => "a.xsl", :type => "text/xsl"}, + {:type => "text/xsl"}, + {:href => "a.xsl", :guess_type => "text/xsl"}, + {:href => "a.css", :type => "text/css"}, + {:href => "a.css", :type => "text/xsl", + :guess_type => "text/css"}, + {:href => "a.xsl", :type => "text/xsl", + :title => "sample", :media => "printer", + :charset => "UTF-8", :alternate => "yes"}, + {:href => "a.css", :guess_type => "text/css", + :alternate => "no"}, + {:type => "text/xsl", :title => "sample", + :media => "printer", :charset => "UTF-8", + :alternate => "yes"}, + ].each do |attrs| + target, contents = parse_pi(XMLStyleSheet.new(*attrs).to_s) + assert_xml_stylesheet(target, XMLStyleSheet.new(*contents), attrs) + end + end + + def test_bad_alternate + %w(a ___ ??? BAD_ALTERNATE).each do |value| + xss = XMLStyleSheet.new + assert_raise(NotAvailableValueError) do + xss.alternate = value + end + xss.do_validate = false + assert_nothing_raised do + xss.alternate = value + end + assert_nil(xss.alternate) + end + end + + def test_parse + [ + [{:href => "a.xsl", :type => "text/xsl"},], + [{:media => "print", :title => "FOO"},], + [{:charset => "UTF-8", :alternate => "yes"},], + [{:href => "a.xsl", :type => "text/xsl"}, + {:type => "text/xsl"}, + {:href => "a.xsl", :guess_type => "text/xsl"}, + {:href => "a.css", :type => "text/css"}, + {:href => "a.css", :type => "text/xsl", + :guess_type => "text/css"}, + {:href => "a.xsl", :type => "text/xsl", + :title => "sample", :media => "printer", + :charset => "UTF-8", :alternate => "yes"}, + {:href => "a.css", :guess_type => "text/css", + :alternate => "no"}, + {:type => "text/xsl", :title => "sample", + :media => "printer", :charset => "UTF-8", + :alternate => "yes"},], + ].each do |xsss| + doc = REXML::Document.new(make_sample_RDF) + root = doc.root + xsss.each do |xss| + content = xss.collect do |key, name| + %Q[#{key}="#{name}"] + end.join(" ") + pi = REXML::Instruction.new("xml-stylesheet", content) + root.previous_sibling = pi + end + rss = Parser.parse(doc.to_s) + have_href_xsss = xsss.find_all {|xss| xss.has_key?(:href)} + assert_equal(have_href_xsss.size, rss.xml_stylesheets.size) + rss.xml_stylesheets.each_with_index do |stylesheet, i| + target, = parse_pi(stylesheet.to_s) + assert_xml_stylesheet(target, stylesheet, have_href_xsss[i]) + end + end + end + + def parse_pi(pi) + /\A\s*<\?(\S+)([^(?:\?>)]+)\?>\s*\z/ =~ pi + target = $1 + dummy = REXML::Document.new("<dummy #{$2}/>").root + contents = {} + dummy.attributes.each do |name, value| + contents[name] = value + end + [target, contents] + end + + end +end diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb new file mode 100644 index 0000000000..dc706653a1 --- /dev/null +++ b/test/ruby/test_time.rb @@ -0,0 +1,73 @@ +require 'test/unit' + +class TestTime < Test::Unit::TestCase + def test_time_add() + assert_equal(Time.utc(2000, 3, 21, 3, 30) + 3 * 3600, + Time.utc(2000, 3, 21, 6, 30)) + assert_equal(Time.utc(2000, 3, 21, 3, 30) + (-3 * 3600), + Time.utc(2000, 3, 21, 0, 30)) + assert_equal(0, (Time.at(1.1) + 0.9).usec) + end + + def test_time_subt() + assert_equal(Time.utc(2000, 3, 21, 3, 30) - 3 * 3600, + Time.utc(2000, 3, 21, 0, 30)) + assert_equal(Time.utc(2000, 3, 21, 3, 30) - (-3 * 3600), + Time.utc(2000, 3, 21, 6, 30)) + assert_equal(900000, (Time.at(1.1) - 0.2).usec) + end + + def test_time_time() + assert_equal(Time.utc(2000, 3, 21, 3, 30) \ + -Time.utc(2000, 3, 21, 0, 30), 3*3600) + assert_equal(Time.utc(2000, 3, 21, 0, 30) \ + -Time.utc(2000, 3, 21, 3, 30), -3*3600) + end + + def negative_time_t? + begin + Time.at(-1) + true + rescue ArgumentError + false + end + end + + def test_timegm + if negative_time_t? + assert_equal(-0x80000000, Time.utc(1901, 12, 13, 20, 45, 52).tv_sec) + assert_equal(-2, Time.utc(1969, 12, 31, 23, 59, 58).tv_sec) + assert_equal(-1, Time.utc(1969, 12, 31, 23, 59, 59).tv_sec) + end + + assert_equal(0, Time.utc(1970, 1, 1, 0, 0, 0).tv_sec) # the Epoch + assert_equal(1, Time.utc(1970, 1, 1, 0, 0, 1).tv_sec) + assert_equal(31535999, Time.utc(1970, 12, 31, 23, 59, 59).tv_sec) + assert_equal(31536000, Time.utc(1971, 1, 1, 0, 0, 0).tv_sec) + assert_equal(78796799, Time.utc(1972, 6, 30, 23, 59, 59).tv_sec) + + # 1972-06-30T23:59:60Z is the first leap second. + if Time.utc(1972, 7, 1, 0, 0, 0) - Time.utc(1972, 6, 30, 23, 59, 59) == 1 + # no leap second. + assert_equal(78796800, Time.utc(1972, 7, 1, 0, 0, 0).tv_sec) + assert_equal(78796801, Time.utc(1972, 7, 1, 0, 0, 1).tv_sec) + assert_equal(946684800, Time.utc(2000, 1, 1, 0, 0, 0).tv_sec) + assert_equal(0x7fffffff, Time.utc(2038, 1, 19, 3, 14, 7).tv_sec) + else + # leap seconds supported. + assert_equal(78796800, Time.utc(1972, 6, 30, 23, 59, 60).tv_sec) + assert_equal(78796801, Time.utc(1972, 7, 1, 0, 0, 0).tv_sec) + assert_equal(78796802, Time.utc(1972, 7, 1, 0, 0, 1).tv_sec) + assert_equal(946684822, Time.utc(2000, 1, 1, 0, 0, 0).tv_sec) + end + end + + def test_huge_difference # [ruby-dev:22619] + if negative_time_t? + assert_equal(Time.at(-0x80000000), Time.at(0x7fffffff) - 0xffffffff) + assert_equal(Time.at(-0x80000000), Time.at(0x7fffffff) + (-0xffffffff)) + assert_equal(Time.at(0x7fffffff), Time.at(-0x80000000) + 0xffffffff) + assert_equal(Time.at(0x7fffffff), Time.at(-0x80000000) - (-0xffffffff)) + end + end +end diff --git a/win32/rm.bat b/win32/rm.bat new file mode 100755 index 0000000000..c7307617ee --- /dev/null +++ b/win32/rm.bat @@ -0,0 +1,9 @@ +@echo off
+::: $Id: rm.bat,v 1.1 2004/03/21 23:21:30 nobu Exp $
+if "%1" == "-f" shift
+:begin
+if "%1" == "" goto :end
+if exist "%1" del "%1"
+shift
+goto :begin
+:end
|