[ruby-oci8-commit] [254] trunk/ruby-oci8: * ext/oci8/.: add apiwrap.[ch] to svn: ignore property.

nobody at rubyforge.org nobody at rubyforge.org
Fri Mar 21 23:18:42 EDT 2008


Revision: 254
Author:   kubo
Date:     2008-03-21 23:18:42 -0400 (Fri, 21 Mar 2008)

Log Message:
-----------
* ext/oci8/.: add apiwrap.[ch] to svn:ignore property.
* ext/oci8/apiwrap.c.tmpl, ext/oci8/apiwrap.rb, ext/oci8/apiwrap.yml:
    add alternative code if OCINumberSetPi(), OCINumberIsInt() is
    not found.
* ext/oci8/oci8.h: delete unused macros. add LIKELY and UNLIKELY
    macros and use the former in oci8_get_errhp().
* ext/oci8/oci8.h, ext/oci8/oci8lib.c: change the scope of
    oci8_base_class to static.
* ext/oci8/ocinumber.c: fix OraNumber#** and OraNumber#<=>. raise
    exceptions when OraNumber#round_prec or OraNumber#shift is called
    and the Oracle client version is 8.0.
* test/test_oranumber.rb: add testcass for all OraNumber methods.

Modified Paths:
--------------
    trunk/ruby-oci8/ChangeLog
    trunk/ruby-oci8/ext/oci8/apiwrap.c.tmpl
    trunk/ruby-oci8/ext/oci8/apiwrap.rb
    trunk/ruby-oci8/ext/oci8/apiwrap.yml
    trunk/ruby-oci8/ext/oci8/oci8.h
    trunk/ruby-oci8/ext/oci8/oci8lib.c
    trunk/ruby-oci8/ext/oci8/ocinumber.c
    trunk/ruby-oci8/test/test_oranumber.rb

Property Changed:
----------------
    trunk/ruby-oci8/ext/oci8/

Modified: trunk/ruby-oci8/ChangeLog
===================================================================
--- trunk/ruby-oci8/ChangeLog	2008-03-19 14:02:00 UTC (rev 253)
+++ trunk/ruby-oci8/ChangeLog	2008-03-22 03:18:42 UTC (rev 254)
@@ -1,3 +1,17 @@
+2008-03-22  KUBO Takehiro  <kubo at jiubao.org>
+	* ext/oci8/.: add apiwrap.[ch] to svn:ignore property.
+	* ext/oci8/apiwrap.c.tmpl, ext/oci8/apiwrap.rb, ext/oci8/apiwrap.yml:
+	    add alternative code if OCINumberSetPi(), OCINumberIsInt() is
+	    not found.
+	* ext/oci8/oci8.h: delete unused macros. add LIKELY and UNLIKELY
+	    macros and use the former in oci8_get_errhp().
+	* ext/oci8/oci8.h, ext/oci8/oci8lib.c: change the scope of
+	    oci8_base_class to static.
+	* ext/oci8/ocinumber.c: fix OraNumber#** and OraNumber#<=>. raise
+	    exceptions when OraNumber#round_prec or OraNumber#shift is called
+	    and the Oracle client version is 8.0.
+	* test/test_oranumber.rb: add testcass for all OraNumber methods.
+
 2008-03-19  KUBO Takehiro  <kubo at jiubao.org>
 	* ext/oci8/ocinumber.c, lib/oci8/compat.rb, lib/oci8/object.rb
 	  lib/oci8/oci8.rb, test/test_oci8.rb:


Property changes on: trunk/ruby-oci8/ext/oci8
___________________________________________________________________
Name: svn:ignore
   - Makefile
mkmf.log
extconf.h
depend
oci8lib.so

   + Makefile
mkmf.log
extconf.h
depend
oci8lib.so
apiwrap.c
apiwrap.h


Modified: trunk/ruby-oci8/ext/oci8/apiwrap.c.tmpl
===================================================================
--- trunk/ruby-oci8/ext/oci8/apiwrap.c.tmpl	2008-03-19 14:02:00 UTC (rev 253)
+++ trunk/ruby-oci8/ext/oci8/apiwrap.c.tmpl	2008-03-22 03:18:42 UTC (rev 254)
@@ -109,8 +109,11 @@
 %>        return <%=f.name%>(<%=f.args.collect {|arg| arg.name}.join(', ')%>);
 <% end
 %>    } else {
-        rb_raise(rb_eRuntimeError, "undefined OCI function %s is called", "<%=f.name%>");
-    }
+<%  if f.code_if_not_found %><%=f.code_if_not_found.split("\n").collect {|line| "        " + line}.join("\n")%>
+<% else
+%>        rb_raise(rb_eRuntimeError, "undefined OCI function %s is called", "<%=f.name%>");
+<% end
+%>    }
 }
 <%
 ######################################################################

Modified: trunk/ruby-oci8/ext/oci8/apiwrap.rb
===================================================================
--- trunk/ruby-oci8/ext/oci8/apiwrap.rb	2008-03-19 14:02:00 UTC (rev 253)
+++ trunk/ruby-oci8/ext/oci8/apiwrap.rb	2008-03-22 03:18:42 UTC (rev 254)
@@ -19,6 +19,7 @@
   attr_reader :remote
   attr_reader :args
   attr_reader :ret
+  attr_reader :code_if_not_found
 
   def initialize(key, val)
     if key[-3..-1] == '_nb'
@@ -33,6 +34,7 @@
     @args = val[:args].collect do |arg|
       ArgDef.new(arg)
     end
+    @code_if_not_found = val[:code_if_not_found]
   end
 end
 

Modified: trunk/ruby-oci8/ext/oci8/apiwrap.yml
===================================================================
--- trunk/ruby-oci8/ext/oci8/apiwrap.yml	2008-03-19 14:02:00 UTC (rev 253)
+++ trunk/ruby-oci8/ext/oci8/apiwrap.yml	2008-03-22 03:18:42 UTC (rev 254)
@@ -939,6 +939,26 @@
   :args:    - OCIError *err
             - const OCINumber *number
             - boolean *result
+  :code_if_not_found: |
+    /* pseude code
+     *   work = trunc(number);
+     *   if (number == work) {
+     *      result = TRUE;
+     *   } else {
+     *      result = FALSE;
+     *   }
+     */
+    OCINumber work;
+    sword rv;
+    sword cmp;
+    rv = oci8_OCINumberTrunc(err, number, 0, &work, file, line);
+    if (rv != OCI_SUCCESS)
+        return rv;
+    rv = oci8_OCINumberCmp(err, number, &work, &cmp, file, line);
+    if (rv != OCI_SUCCESS)
+        return rv;
+    *result = (cmp == 0) ? TRUE : FALSE;
+    return OCI_SUCCESS;
 
 # round trip: 0
 OCINumberPrec:
@@ -954,6 +974,13 @@
   :ret:     void
   :args:    - OCIError *err
             - OCINumber *num
+  :code_if_not_found: |
+    static const OCINumber pi = {
+        {0x15, 0xc1, 0x04, 0x0f, 0x10, 0x5d, 0x42, 0x24, 0x5a, 0x50,
+         0x21, 0x27, 0x2f, 0x1b, 0x2c, 0x27, 0x21, 0x50, 0x33, 0x1d,
+         0x55, 0x15}
+    };
+    *num = pi;
 
 # round trip: 0
 OCINumberShift:

Modified: trunk/ruby-oci8/ext/oci8/oci8.h
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8.h	2008-03-19 14:02:00 UTC (rev 253)
+++ trunk/ruby-oci8/ext/oci8/oci8.h	2008-03-22 03:18:42 UTC (rev 254)
@@ -69,6 +69,17 @@
 
 #define IS_OCI_ERROR(v) (((v) != OCI_SUCCESS) && ((v) != OCI_SUCCESS_WITH_INFO))
 
+#if defined(__GNUC__) && ((__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96))
+/* LIKELY tells the compiler that x is 1(TRUE) in many cases.
+ * This may optimize branch prediction in the assembler code.
+ */
+#define LIKELY(x)   (__builtin_expect((x), 1))
+#define UNLIKELY(x) (__builtin_expect((x), 0))
+#else
+#define LIKELY(x)   (x)
+#define UNLIKELY(x) (x)
+#endif
+
 #if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
 /* gcc version >= 3.1 */
 #define ALWAYS_INLINE inline __attribute__((always_inline))
@@ -215,11 +226,6 @@
 #define UB4_TO_NUM UINT2NUM
 #endif
 
-/* dangerous macros */
-#define CHECK_STRING(obj) if (!NIL_P(obj)) { StringValue(obj); }
-#define TO_STRING_PTR(obj) (NIL_P(obj) ? NULL : RSTRING(obj)->ptr)
-#define TO_STRING_LEN(obj) (NIL_P(obj) ? 0 : RSTRING(obj)->len)
-
 /* env.c */
 extern OCIEnv *oci8_envhp;
 #ifdef RUBY_VM
@@ -250,7 +256,7 @@
 static inline OCIError *oci8_get_errhp()
 {
     OCIError *errhp = (OCIError *)oci8_tls_get(oci8_tls_key);
-    return (errhp != NULL) ? errhp : oci8_make_errhp();
+    return LIKELY(errhp != NULL) ? errhp : oci8_make_errhp();
 }
 
 #else
@@ -279,7 +285,6 @@
 #if defined RUNTIME_API_CHECK
 void *oci8_find_symbol(const char *symbol_name);
 #endif
-extern oci8_base_class_t oci8_base_class;
 
 /* error.c */
 extern VALUE eOCIException;

Modified: trunk/ruby-oci8/ext/oci8/oci8lib.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8lib.c	2008-03-19 14:02:00 UTC (rev 253)
+++ trunk/ruby-oci8/ext/oci8/oci8lib.c	2008-03-22 03:18:42 UTC (rev 254)
@@ -1,6 +1,6 @@
 /* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */
 /*
- * Copyright (C) 2002-2007 KUBO Takehiro <kubo at jiubao.org>
+ * Copyright (C) 2002-2008 KUBO Takehiro <kubo at jiubao.org>
  */
 
 #ifdef __linux__ /* for RTLD_DEFAULT */
@@ -15,7 +15,7 @@
 #include <signal.h>
 #endif
 
-oci8_base_class_t oci8_base_class = {
+static oci8_base_class_t oci8_base_class = {
     NULL,
     NULL,
     sizeof(oci8_base_t),

Modified: trunk/ruby-oci8/ext/oci8/ocinumber.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/ocinumber.c	2008-03-19 14:02:00 UTC (rev 253)
+++ trunk/ruby-oci8/ext/oci8/ocinumber.c	2008-03-22 03:18:42 UTC (rev 254)
@@ -5,7 +5,7 @@
  * $Author$
  * $Date$
  *
- * Copyright (C) 2005-2007 KUBO Takehiro <kubo at jiubao.org>
+ * Copyright (C) 2005-2008 KUBO Takehiro <kubo at jiubao.org>
  *
  */
 #include "oci8.h"
@@ -655,8 +655,6 @@
 {
     OCINumber n;
     OCINumber r;
-    sword sign;
-    boolean is_int;
 
     if (FIXNUM_P(rhs)) {
         oci_lc(OCINumberIntPower(oci8_errhp, _NUMBER(lhs), FIX2INT(rhs), &r));
@@ -664,12 +662,6 @@
         /* change to OCINumber */
         if (!set_oci_number_from_num(&n, rhs, 0))
             return rb_num_coerce_bin(lhs, rhs, id_power);
-        /* check whether num1 is negative. */
-        oci_lc(OCINumberSign(oci8_errhp, _NUMBER(lhs), &sign));
-        /* check whether num2 is an integral value. */
-        oci_lc(OCINumberIsInt(oci8_errhp, &n, &is_int));
-        if (sign < 0 && !is_int)
-            rb_raise(rb_eRangeError, "base is negative and exponent part is not an integral value");
         oci_lc(OCINumberPower(oci8_errhp, _NUMBER(lhs), &n, &r));
     }
     return oci8_make_ocinumber(&r);
@@ -693,7 +685,13 @@
         return rb_num_coerce_cmp(lhs, rhs, id_cmp);
     /* compare */
     oci_lc(OCINumberCmp(oci8_errhp, _NUMBER(lhs), &n, &r));
-    return INT2NUM(r);
+    if (r > 0) {
+        return INT2FIX(1);
+    } else if (r == 0) {
+        return INT2FIX(0);
+    } else {
+        return INT2FIX(-1);
+    }
 }
 
 /*
@@ -732,6 +730,10 @@
  *
  *  Rounds <i>onum</i> to the nearest <code>Integer</code> when no argument.
  *  Rounds <i>onum</i> to a specified decimal place <i>decplace</i> when one argument.
+ *
+ *   OraNumber.new(1.234).round(1)  #=> 1.2
+ *   OraNumber.new(1.234).round(2)  #=> 1.23
+ *   OraNumber.new(1.234).round(3)  #=> 1.234
  */
 static VALUE onum_round(int argc, VALUE *argv, VALUE self)
 {
@@ -770,6 +772,7 @@
  *     onum.round_prec(digits) -> oranumber
  *
  *  Rounds <i>onum</i> to a specified number of decimal digits.
+ *  This method is available on Oracle 8.1 client or upper.
  *
  *   OraNumber.new(1.234).round_prec(2)  #=> 1.2
  *   OraNumber.new(12.34).round_prec(2)  #=> 12
@@ -779,6 +782,10 @@
 {
     OCINumber r;
 
+    if (!have_OCINumberPrec) {
+        rb_raise(rb_eNotImpError, "The Oracle client version is too lower to use %s#round_prec",
+                 rb_obj_classname(self));
+    }
     oci_lc(OCINumberPrec(oci8_errhp, _NUMBER(self), NUM2INT(ndigs), &r));
     return oci8_make_ocinumber(&r);
 }
@@ -938,11 +945,16 @@
  *     onum.shift(fixnum)    -> oranumber
  *
  *  Returns <i>onum</i> * 10**<i>fixnum</i>
+ *  This method is available on Oracle 8.1 client or upper.
  */
 static VALUE onum_shift(VALUE self, VALUE exp)
 {
     OCINumber result;
 
+    if (!have_OCINumberShift) {
+        rb_raise(rb_eNotImpError, "The Oracle client version is too lower to use %s#shift",
+                 rb_obj_classname(self));
+    }
     oci_lc(OCINumberShift(oci8_errhp, _NUMBER(self), NUM2INT(exp), &result));
     return oci8_make_ocinumber(&result);
 }

Modified: trunk/ruby-oci8/test/test_oranumber.rb
===================================================================
--- trunk/ruby-oci8/test/test_oranumber.rb	2008-03-19 14:02:00 UTC (rev 253)
+++ trunk/ruby-oci8/test/test_oranumber.rb	2008-03-22 03:18:42 UTC (rev 254)
@@ -2,10 +2,11 @@
 require 'oci8'
 require 'test/unit'
 require './config'
+require 'yaml'
 
 class TestOraNumber < Test::Unit::TestCase
 
-  NUMBER_CHECK_TARGET = [
+  LARGE_RANGE_VALUES = [
     "12345678901234567890123456789012345678",
     "1234567890123456789012345678901234567",
     "1234567890123456789012345678901234567.8",
@@ -52,91 +53,425 @@
     "-1.123",
   ]
 
-  def setup
-    @conn = get_oci8_connection
-  end
+  SMALL_RANGE_VALUES = [
+    "10",
+    "3",
+    "3.14159265358979323846", # PI
+    "2",
+    "1.57079632679489661923", # PI/2
+    "0.5",
+    "0.0000000001",
+    "0",
+    "-0.0000000001",
+    "-0.5",
+    "-1.57079632679489661923", # -PI/2
+    "-2",
+    "-3.14159265358979323846", # -PI
+    "-3",
+    "-10",
+  ]
 
-  def test_to_s
-    cursor = @conn.parse("BEGIN :number := TO_NUMBER(:str); END;")
-    cursor.bind_param(:number, OraNumber)
-    cursor.bind_param(:str, nil, String, 40)
-    NUMBER_CHECK_TARGET.each do |val|
-      cursor[:str] = val
-      cursor.exec
-      assert_equal(val, cursor[:number].to_s)
+  def compare_with_float(values, rettype, proc1, proc2 = nil)
+    proc2 = proc1 if proc2.nil?
+    values.each do |x|
+      expected_val = proc1.call(x.to_f)
+      actual_val = proc2.call(OraNumber.new(x))
+      assert_kind_of(rettype, actual_val)
+      delta = [expected_val.abs * 1.0e-12, 1.0e-14].max
+      assert_in_delta(expected_val, actual_val, delta, x)
     end
   end
 
-  def test_to_i
-    cursor = @conn.parse("BEGIN :number := TO_NUMBER(:str); END;")
-    cursor.bind_param(:number, OraNumber)
-    cursor.bind_param(:str, nil, String, 40)
-    NUMBER_CHECK_TARGET.each do |val|
-      cursor[:str] = val
-      cursor.exec
-      assert_equal(val.to_i, cursor[:number].to_i)
+  def compare_with_float2(values, proc_args, proc1, proc2 = nil)
+    proc2 = proc1 if proc2.nil?
+    values.each do |x|
+      proc_args.each do |y|
+        expected_val = proc1.call(x.to_f, y)
+        actual_val = proc2.call(OraNumber.new(x), y)
+        begin
+          delta = [expected_val.abs * 1.0e-12, 1.0e-14].max
+        rescue
+          puts '-----------'
+          p x
+          p y
+          p expected_val
+          puts '-----------'
+          raise $!
+        end
+        assert_in_delta(expected_val, actual_val, delta, x)
+      end
     end
   end
 
-  def test_to_f
-    cursor = @conn.parse("BEGIN :number := TO_NUMBER(:str); END;")
-    cursor.bind_param(:number, OraNumber)
-    cursor.bind_param(:str, nil, String, 40)
-    NUMBER_CHECK_TARGET.each do |val|
-      cursor[:str] = val
-      cursor.exec
-      assert_in_delta(val.to_f, cursor[:number].to_f, val.to_f.abs * 1e-14)
+  def test_in_bind
+    conn = get_oci8_connection
+    begin
+      conn.exec("alter session set nls_numeric_characters = '.,'")
+      cursor = conn.parse("BEGIN :out := TO_CHAR(:in); END;")
+      cursor.bind_param(:out, nil, String, 40)
+      cursor.bind_param(:in, OraNumber)
+      LARGE_RANGE_VALUES.each do |val|
+        cursor[:in] = OraNumber.new(val)
+        cursor.exec
+        # convert 0.0001 and -0.0001 to .0001 and -.0001 respectively
+        val = $1+'.'+$2 if /(-?)0\.(.*)/ =~ val
+        assert_equal(val, cursor[:out])
+      end
+    ensure
+      conn.logoff
     end
   end
 
-  def test_uminus
-    cursor = @conn.parse("BEGIN :number := - TO_NUMBER(:str); END;")
-    cursor.bind_param(:number, OraNumber)
-    cursor.bind_param(:str, nil, String, 40)
-    NUMBER_CHECK_TARGET.each do |val|
-      cursor[:str] = val
-      cursor.exec
-      assert_equal(val, (- (cursor[:number])).to_s)
+  def test_out_bind
+    conn = get_oci8_connection
+    begin
+      conn.exec("alter session set nls_numeric_characters = '.,'")
+      cursor = conn.parse("BEGIN :out := TO_NUMBER(:in); END;")
+      cursor.bind_param(:out, OraNumber)
+      cursor.bind_param(:in, nil, String, 40)
+      LARGE_RANGE_VALUES.each do |val|
+        cursor[:in] = val
+        cursor.exec
+        assert_equal(OraNumber.new(val), cursor[:out])
+      end
+    ensure
+      conn.logoff
     end
   end
 
   def test_dup
-    cursor = @conn.parse("BEGIN :number := TO_NUMBER(:str); END;")
-    cursor.bind_param(:number, OraNumber)
-    cursor.bind_param(:str, nil, String, 40)
-    NUMBER_CHECK_TARGET.each do |val|
-      cursor[:str] = val
-      cursor.exec
-      n = cursor[:number]
-      assert_equal(n.to_s, n.dup.to_s)
-      assert_equal(n.to_s, n.clone.to_s)
+    LARGE_RANGE_VALUES.each do |x|
+      n = OraNumber.new(x)
+      assert_equal(n, n.dup)
+      assert_equal(n, n.clone)
     end
   end
 
   def test_marshal
-    cursor = @conn.parse("BEGIN :number := TO_NUMBER(:str); END;")
-    cursor.bind_param(:number, OraNumber)
-    cursor.bind_param(:str, nil, String, 40)
-    NUMBER_CHECK_TARGET.each do |val|
-      cursor[:str] = val
-      cursor.exec
-      n = cursor[:number]
-      assert_equal(n.to_s, Marshal.load(Marshal.dump(n)).to_s)
+    LARGE_RANGE_VALUES.each do |x|
+      n = OraNumber.new(x)
+      assert_equal(n, Marshal.load(Marshal.dump(n)))
     end
   end
 
-  def test_new_from_string
-    cursor = @conn.parse("BEGIN :number := TO_NUMBER(:str); END;")
-    cursor.bind_param(:number, OraNumber)
-    cursor.bind_param(:str, nil, String, 40)
-    (NUMBER_CHECK_TARGET + ["1.230", "1.2300", "1.23000"]).each do |val|
-      cursor[:str] = val
-      cursor.exec
-      assert_equal(cursor[:number].to_s, OraNumber.new(val).to_s)
+  def test_yaml
+    LARGE_RANGE_VALUES.each do |x|
+      n = OraNumber.new(x)
+      assert_equal(n, YAML.load(YAML.dump(n)))
     end
   end
 
-  def teardown
-    @conn.logoff
+  # OCI8::Math.acos(x) -> ocinumber
+  def test_math_acos
+    test_values = []
+    -1.0.step(1.0, 0.01) do |n|
+      test_values << n
+    end
+    compare_with_float(test_values, OraNumber,
+                       Proc.new {|n| Math::acos(n)},
+                       Proc.new {|n| OCI8::Math::acos(n)})
   end
+
+  # OCI8::Math.asin(x) -> ocinumber
+  def test_math_asin
+    test_values = []
+    -1.0.step(1.0, 0.01) do |n|
+      test_values << n
+    end
+    compare_with_float(test_values, OraNumber,
+                       Proc.new {|n| Math::asin(n)},
+                       Proc.new {|n| OCI8::Math::asin(n)})
+  end
+
+  # OCI8::Math.atan(x) -> ocinumber
+  def test_math_atan
+    compare_with_float(SMALL_RANGE_VALUES, OraNumber,
+                       Proc.new {|n| Math::atan(n)},
+                       Proc.new {|n| OCI8::Math::atan(n)})
+  end
+
+  # OCI8::Math.atan2(y, x) -> ocinumber
+  def test_math_atan2
+    compare_with_float2(SMALL_RANGE_VALUES, SMALL_RANGE_VALUES,
+                        Proc.new {|x, y| Math::atan2(x, y.to_f)},
+                        Proc.new {|x, y| OCI8::Math::atan2(x, y.to_f)})
+    compare_with_float2(SMALL_RANGE_VALUES, SMALL_RANGE_VALUES,
+                        Proc.new {|x, y| Math::atan2(y.to_f, x)},
+                        Proc.new {|x, y| OCI8::Math::atan2(y.to_f, x)})
+  end
+
+  # OCI8::Math.cos(x) -> ocinumber
+  def test_math_cos
+    compare_with_float(SMALL_RANGE_VALUES, OraNumber,
+                       Proc.new {|n| Math::cos(n)},
+                       Proc.new {|n| OCI8::Math::cos(n)})
+  end
+
+  # OCI8::Math.cosh(x) -> ocinumber
+  def test_math_cosh
+    compare_with_float(SMALL_RANGE_VALUES, OraNumber,
+                       Proc.new {|n| Math::cosh(n)},
+                       Proc.new {|n| OCI8::Math::cosh(n)})
+  end
+
+  # OCI8::Math.exp(x) -> ocinumber
+  def test_exp
+    compare_with_float(SMALL_RANGE_VALUES, OraNumber,
+                       Proc.new {|n| Math::exp(n)},
+                       Proc.new {|n| OCI8::Math::exp(n)})
+  end
+
+  # OCI8::Math.log(numeric) -> ocinumber
+  # OCI8::Math.log(numeric, base_num) -> ocinumber
+  def test_log
+    test_values = LARGE_RANGE_VALUES.reject do |x|
+      # reject minus and zero values
+      x[0,1] == '-' || x == '0'
+    end
+    compare_with_float(test_values, OraNumber,
+                       Proc.new {|n| Math::log(n)},
+                       Proc.new {|n| OCI8::Math::log(n)})
+    compare_with_float(test_values, OraNumber,
+                       Proc.new {|n| Math::log(n)/Math::log(3)},
+                       Proc.new {|n| OCI8::Math::log(n, 3)})
+  end
+
+  # OCI8::Math.log10(numeric) -> ocinumber
+  def test_log10
+    test_values = LARGE_RANGE_VALUES.reject do |x|
+      # reject minus and zero values
+      x[0,1] == '-' || x == '0'
+    end
+    compare_with_float(test_values, OraNumber,
+                       Proc.new {|n| Math::log10(n)},
+                       Proc.new {|n| OCI8::Math::log10(n)})
+  end
+
+  # OCI8::Math.sin(x) -> ocinumber
+  def test_math_sin
+    compare_with_float(SMALL_RANGE_VALUES, OraNumber,
+                       Proc.new {|n| Math::sin(n)},
+                       Proc.new {|n| OCI8::Math::sin(n)})
+  end
+
+  # OCI8::Math.sinh(x) -> ocinumber
+  def test_math_sinh
+    compare_with_float(SMALL_RANGE_VALUES, OraNumber,
+                       Proc.new {|n| Math::sinh(n)},
+                       Proc.new {|n| OCI8::Math::sinh(n)})
+  end
+
+  # OCI8::Math.sqrt(numeric) -> ocinumber
+  def test_sqrt
+    test_values = LARGE_RANGE_VALUES.reject do |x|
+      # reject minus values
+      x[0,1] == '-'
+    end
+    compare_with_float(test_values, OraNumber,
+                       Proc.new {|n| Math::sqrt(n)},
+                       Proc.new {|n| OCI8::Math::sqrt(n)})
+  end
+
+  # OCI8::Math.tan(x) -> ocinumber
+  def test_math_tan
+    test_values = SMALL_RANGE_VALUES.reject do |x|
+      # reject PI/2 and -PI/2.
+      # Those values are +inf and -info
+      radian = x.to_f
+      (radian.abs - Math::PI/2).abs < 0.000001
+    end
+    compare_with_float(test_values, OraNumber,
+                       Proc.new {|n| Math::tan(n)},
+                       Proc.new {|n| OCI8::Math::tan(n)})
+  end
+
+  # OCI8::Math.tanh() -> ocinumber
+  def test_math_tanh
+    compare_with_float(SMALL_RANGE_VALUES, OraNumber,
+                       Proc.new {|n| Math::tanh(n)},
+                       Proc.new {|n| OCI8::Math::tanh(n)})
+  end
+
+  # onum % other -> onum
+  # def test_mod
+
+  # onum * other -> onum
+  def test_mul
+    compare_with_float2(SMALL_RANGE_VALUES, SMALL_RANGE_VALUES,
+                        Proc.new {|x, y| x * y.to_f})
+    compare_with_float2(SMALL_RANGE_VALUES, SMALL_RANGE_VALUES,
+                        Proc.new {|x, y| y.to_f * x})
+  end
+
+  # onum ** other -> onum
+  def test_pow
+    base_values = SMALL_RANGE_VALUES.reject do |x|
+      # reject minus and zero values
+      x[0,1] == '-' || x == '0'
+    end
+    compare_with_float2(base_values, SMALL_RANGE_VALUES,
+                        Proc.new {|x, y| x ** y.to_f})
+    compare_with_float2(SMALL_RANGE_VALUES, base_values,
+                        Proc.new {|x, y| y.to_f ** x})
+  end
+
+  # onum + other -> onum
+  def test_add
+    compare_with_float2(SMALL_RANGE_VALUES, SMALL_RANGE_VALUES,
+                        Proc.new {|x, y| x + y.to_f})
+    compare_with_float2(SMALL_RANGE_VALUES, SMALL_RANGE_VALUES,
+                        Proc.new {|x, y| y.to_f + x})
+  end
+
+  # onum - other -> onum
+  def test_minus
+    compare_with_float2(SMALL_RANGE_VALUES, SMALL_RANGE_VALUES,
+                        Proc.new {|x, y| x - y.to_f})
+    compare_with_float2(SMALL_RANGE_VALUES, SMALL_RANGE_VALUES,
+                        Proc.new {|x, y| y.to_f - x})
+  end
+
+  # -ocinumber -> ocinumber
+  def test_uminus
+    compare_with_float(LARGE_RANGE_VALUES, OraNumber, Proc.new {|n| -n})
+  end
+
+  # onum / other -> onum
+  # TODO: test_div
+
+  # onum <=> other -> -1, 0, +1
+  def test_cmp
+    test_values = SMALL_RANGE_VALUES.collect do |x|
+      x[0,15] # donw the precision to pass this test.
+    end
+    compare_with_float2(test_values, test_values,
+                        Proc.new {|x, y| x <=> y.to_f})
+    compare_with_float2(test_values, test_values,
+                        Proc.new {|x, y| y.to_f <=> x})
+  end
+
+  # onum.abs -> ocinumber
+  def test_abs
+    compare_with_float(LARGE_RANGE_VALUES, OraNumber, Proc.new {|n| n.abs})
+  end
+
+  # onum.ceil -> integer
+  def test_ceil
+    compare_with_float(LARGE_RANGE_VALUES, Integer, Proc.new {|n| n.ceil})
+  end
+
+  # onum.floor -> integer
+  def test_floor
+    compare_with_float(LARGE_RANGE_VALUES, Integer, Proc.new {|n| n.floor})
+  end
+
+  # onum.round -> integer
+  # onum.round(decplace) -> onum
+  def test_round
+    compare_with_float(LARGE_RANGE_VALUES, Integer, Proc.new {|n| n.round})
+    compare_with_float(LARGE_RANGE_VALUES, OraNumber,
+                       Proc.new {|n| (n * 10).round * 0.1},
+                       Proc.new {|n| n.round(1)})
+    compare_with_float(LARGE_RANGE_VALUES, OraNumber,
+                       Proc.new {|n| (n * 100).round * 0.01},
+                       Proc.new {|n| n.round(2)})
+    compare_with_float(LARGE_RANGE_VALUES, OraNumber,
+                       Proc.new {|n| (n * 0.1).round * 10},
+                       Proc.new {|n| n.round(-1)})
+  end
+
+  # onum.round_prec(digits) -> ocinumber
+  def test_round_prec
+    if OCI8::oracle_client_version >= 810
+      # Oracle 8.1 client or upper
+      compare_with_float2(LARGE_RANGE_VALUES, [1, 2, 3, 5, 10, 20],
+                          Proc.new {|x, y|
+                            return 0.0 if x == 0.0
+                            factor = 10 ** (Math::log10(x.abs).to_i - y + 1)
+                            (x / factor).round * factor
+                          },
+                          Proc.new {|x, y| x.round_prec(y)})
+    else
+      # Oracle 8.0 client
+      assert_raise RuntimeError do
+        OraNumber.new(1).round_prec(1)
+      end
+    end
+  end
+
+  # onum.shift(fixnum) -> ocinumber
+  def test_shift
+    if OCI8::oracle_client_version >= 810
+      # Oracle 8.1 client or upper
+      compare_with_float2(LARGE_RANGE_VALUES, [-5, -4, -3, -1, 0, 1, 2, 3, 4, 5],
+                          Proc.new {|x, y| x * (10 ** y)},
+                          Proc.new {|x, y| x.shift(y)})
+    else
+      # Oracle 8.0 client
+      assert_raise RuntimeError do
+        OraNumber.new(1).shift(1)
+      end
+    end
+  end
+
+  # onum.to_char(fmt = nil, nls_params = nil) -> string
+  def test_to_char
+    onum = OraNumber.new(123.45)
+    assert_equal('   123.4500',   onum.to_char('99999.9999'))
+    assert_equal('  0123.4500',   onum.to_char('90000.0009'))
+    assert_equal(' 00123.4500',   onum.to_char('00000.0000'))
+    assert_equal('123.45',        onum.to_char('FM99999.9999'))
+    assert_equal('0123.450',      onum.to_char('FM90000.0009'))
+    assert_equal('00123.4500',    onum.to_char('FM00000.0000'))
+    assert_equal('  -123.4500',(-onum).to_char('99999.9999'))
+    assert_equal(' -0123.4500',(-onum).to_char('90000.0009'))
+    assert_equal('-00123.4500',(-onum).to_char('00000.0000'))
+    assert_equal('-123.45',    (-onum).to_char('FM99999.9999'))
+    assert_equal('-0123.450',  (-onum).to_char('FM90000.0009'))
+    assert_equal('-00123.4500',(-onum).to_char('FM00000.0000'))
+    assert_equal(' 0,123.4500',   onum.to_char('0G000D0000', "NLS_NUMERIC_CHARACTERS = '.,'"))
+    assert_equal(' 0.123,4500',   onum.to_char('0G000D0000', "NLS_NUMERIC_CHARACTERS = ',.'"))
+    assert_equal('Ducat123.45',    onum.to_char('FML9999.999', "NLS_CURRENCY = 'Ducat'"))
+  end
+
+  # onum.to_f -> float
+  def test_to_f
+    LARGE_RANGE_VALUES.each do |x|
+      expected_val = x.to_f
+      actual_val = OraNumber.new(x).to_f
+      delta = [expected_val.abs * 1.0e-12, 1.0e-14].max
+      assert_in_delta(expected_val, actual_val, delta, x)
+    end
+  end
+
+  # onum.to_i -> integer
+  def test_to_i
+    LARGE_RANGE_VALUES.each do |x|
+      expected_val = x.to_i
+      actual_val = OraNumber.new(x).to_i
+      assert_equal(expected_val, actual_val, x)
+    end
+  end
+
+  # onum.to_s -> string
+  def test_to_s
+    LARGE_RANGE_VALUES.each do |x|
+      expected_val = x
+      actual_val = OraNumber.new(x).to_s
+      assert_equal(expected_val, actual_val, x)
+    end
+  end
+
+  # onum.truncate -> integer
+  # onum.truncate(decplace) -> ocinumber
+  # TODO: test_truncate
+
+  # onum.zero? -> true or false
+  def test_zero_p
+    LARGE_RANGE_VALUES.each do |x|
+      expected_val = x.to_f.zero?
+      actual_val = OraNumber.new(x).zero?
+      assert_equal(expected_val, actual_val, x)
+    end
+  end
 end




More information about the ruby-oci8-commit mailing list