Re: [PATCHES] Numeric improvements

2013-03-06 Thread Ludovic Courtès
Hi,

Mark H Weaver m...@netris.org skribis:

 Here are ten patches to improve numerics.  Among other things, this
 eliminates the known obstacles to linking with mini-gmp
 http://bugs.gnu.org/10519, fixes several problems with our number
 printer http://bugs.gnu.org/13757, adds 'round-ash', and speeds up
 handling of exact rationals.

Ten patches!

 Still to be done: add more tests to ensure full coverage of all code
 paths.  It's possible that there are some bugs lurking.  However, I
 wanted to post it now to allow review and possible work on mini-gmp
 integration.

Which of these patches are needed for mini-gmp integration?  It would
probably be easier to discuss things separately, by small chunks.

Overall I think I’m mostly incompetent on the numeric stuff, so I’d
mostly comment on form.

At first sight, it seems that there are few tests added, in particular
for the number printer.  I think tests must be added along with the
patches that claim to fix something.

 From cd9784ed33d78e6647a752123bf7be91d65b5c96 Mon Sep 17 00:00:00 2001
 From: Mark H Weaver m...@netris.org
 Date: Sun, 3 Mar 2013 04:34:17 -0500
 Subject: [PATCH 01/10] Improve code in scm_gcd for inum/inum case

 * libguile/numbers.c (scm_gcd): Improve implementation of inum/inum case
   to be more clear and efficient.

This one looks OK, and should be covered by the tests, according to
http://hydra.nixos.org/build/4268423/download/2/coverage/libguile/numbers.c.gcov.html.

Did you measure the performance difference?

 From f6201616f7304979a31ab41814e2b297f74a3484 Mon Sep 17 00:00:00 2001
 From: Mark H Weaver m...@netris.org
 Date: Sun, 3 Mar 2013 04:34:50 -0500
 Subject: [PATCH 02/10] Optimize and simplify fractions code

 * libguile/numbers.c (scm_exact_integer_quotient): New internal static
   function that computes the quotient of two exact integers when the
   remainder is known in advance to be zero.  For large integers this can
   be implemented more efficiently than when the remainder is unknown.

   (scm_i_make_ratio_already_reduced): New internal static function that
   creates a ratio when the numerator and denominator are already known
   to be reduced to lowest terms (i.e. when their gcd is 1).  This can be
   used in several places to avoid unnecessary gcd computations.

   (scm_i_make_ratio): Rewrite in terms of
   scm_i_make_ratio_already_reduced.  Don't bother checking to see if the
   denominator divides the numerator evenly.  This is wasted effort in
   the common case.  Instead, compute the gcd, reduce to lowest terms
   (using scm_exact_integer_quotient), and let
   scm_i_make_ratio_already_reduced do the integer check (by checking for
   a unit denominator).

   (scm_integer_expt): Optimize fraction case by (a/b)^n == (a^n)/(b^n),
   to avoid needless effort to reduce intermediate products to lowest
   terms.  If a and b have no common factors, then a^n and b^n have no
   common factors.  Use scm_i_make_ratio_already_reduced to construct the
   final result, so that no gcd computations are needed to exponentiate a
   fraction.

   (scm_abs, scm_magnitude, scm_difference): Use
   scm_i_make_ratio_already_reduced to avoid wasteful gcd computations
   when negating fractions.

   (do_divide): Use scm_i_make_ratio_already_reduced to avoid wasteful
   gcd computations when taking the reciprocal of a fraction or integer.

Can you fold the explanations as comments?  In particular those that
explain the implementation strategy, and why it’s more efficient this way.

 +/* scm_i_make_ratio_already_reduced makes a ratio more efficiently,
 +   when the following conditions are known in advance:
 +
 +1. numerator and denominator are exact integers
 +2. numerator and denominator are reduced to lowest terms: gcd(n,d) == 1
 +*/
  static SCM
 -scm_i_make_ratio (SCM numerator, SCM denominator)
 -#define FUNC_NAME make-ratio
 +scm_i_make_ratio_already_reduced (SCM numerator, SCM denominator)

Please use imperative-tense sentences above functions to describe what
they do, without repeating the function name; also make sure to
introduce the arguments in the description, written in capital letters
(info (standards) Comments).

Some helper functions introduced by the patches lack a comment.

 +@deffn {Scheme Procedure} round-ash n count
 +@deffnx {C Function} scm_round_ash (n, count)
 +Return @math{round(@var{n} * 2^@var{count})}, but computed
 +more efficiently.  @var{n} and @var{count} must be exact
 +integers.
 +
 +With @var{n} viewed as an infinite-precision twos-complement
 +integer, @code{round-ash} means a left shift introducing zero
 +bits when @var{count} is positive, or a right shift rounding
 +to the nearest integer (with ties going to the nearest even
 +integer) when @var{count} is negative.  This is a rounded
 +``arithmetic'' shift.
 +
 +@lisp
 +(number-string (round-ash #b1 3) 2) @result{} \1000\
 +(number-string (round-ash #b1010 -1) 2) @result{} \101\
 +(number-string (round-ash #b1010 -2) 2) @result{} \10\
 

Re: My Guile 2.0.8 TODO list

2013-03-06 Thread Ludovic Courtès
Mark H Weaver m...@netris.org skribis:

 FYI, here's what I'm hoping to get into Guile 2.0.8.

Thanks for sharing.

 * #!optional and #!rest reader handling.

As a reader option, right?

 * Add command-line option to augment %load-compiled-path.

+1

 * [Ludovic?] Fix par-map and par-for-each to not overflow the stack for
   large lists.

Yes, I’ll look into it.  I’ll also look at other bugs I reported over
the weeks.

Ludo’.




Re: goops - accessors, methods and generics

2013-03-06 Thread Ludovic Courtès
David Pirotte da...@altosw.be skribis:

 if at least guile designers and goops implementors would have provided a 
 'switch'
 so that we could ask that any and all goops related stuff being in a single 
 name
 space available 'anywhere' at all time [such as guile core functionality is], 
 it
 would be great, but reading you, it sounds it is more like a religion 
 problem: not
 much we can do when it comes to that. 

One problem with GOOPS is that it changes Guile’s behavior globally,
which is not always desirable.

It also introduces run-time and memory overhead here and there.  For
instance, “guile -c '(use-modules (oop goops))'” executes in ~70 ms on
my machine, vs. ~25 ms for “guile -c 1”.

My 2¢,
Ludo’.




Re: My Guile 2.0.8 TODO list

2013-03-06 Thread Andrew Gaylard

On 03/05/13 23:14, Mark H Weaver wrote:

FYI, here's what I'm hoping to get into Guile 2.0.8.

 Mark


2.0.8 TODO
==

* [SUBMITTED] Refactor pending numerics patches.

* [SUBMITTED] Implement Dybvig and Burger's algorithm for printing
   floats.

* [NEEDS REVISION] Fix BOM handling.

* #!optional and #!rest reader handling.

* Add command-line option to augment %load-compiled-path.

* Change 'sqrt' to return an exact rational when possible.

* Optimize overflow check in scm_product (i.e. avoid the division).

* Add call/ec and let/ec.

* Implement optional arguments to vector-copy (SRFI-43)

* Make sure that 'syntax-parameterize' reports an error (or at least a
   warning) if the associated identifier is not defined as a syntax
   parameter.

* [Ludovic?] Fix par-map and par-for-each to not overflow the stack for
   large lists.

* [Daniel Hartwig?] Support Relative URIs in (web uri)

* [Ian Price?] Fix flawed index range check in bytevector accessors.


Hi Mark,

Two suggestions for 2.0.8:

It'd be great to get Andy's (oop goops save) patch in.
See http://lists.gnu.org/archive/html/guile-user/2013-01/msg00088.html

Also, Nala's colourised REPL patch is pretty cool; see
https://lists.gnu.org/archive/html/guile-devel/2012-12/msg00062.html

--
Andrew




Re: Better generator and iterator support in guile w.r.t. speed

2013-03-06 Thread Stefan Israelsson Tampe
Hi all, 

In my previous mail I did some discussions about what structure to use
in order to allow efficient implementatin og generators and one main
observation is that the curretn rtl strategy can be used.

Now Have you all thought about the stack beeing a must to execute
funcitons. Well we have threads but we can get lightweight in process
threads quite naturally in the rtl version of guile. We could call it
a coroutine and let it be an object

co = (code,stack,ip,outer-stack)

The idea here is that the code will have a fixed sized stack that is
just enough room to store all locals and intermediate variables
e.g. all the registers needed to execute the function. Any call would
then need to hook onto the outer stack. The stack is typically of the
form
stack =
 
[IP
 OUTER-FP
 LOCALS
 ...]

At the creation, IP points to the starting code position and then
after each exit the next ip is stored in IP and so on.

To support this we need a few new operations
* MULTIMOVE,   from the local stack to outer stack
* MULTIMOVE,   to the local stack from the outer stack
* OUTER-TAIL-CALL  This re-uses the outer call frame used to call this
   coroutine and resets the IP to the initial value
* OUTER-CALL   This will setup a new call frame on the outer stack
   execute the function there
* RESET-IP RESET the IP value to the initial start value
* STORE-IP Store an local ip adress in IP using a label.
* OUTER-RETURN Simply return using the OUTER-FP and make sure 

* CALL-CO  This will call a CO routine by setting up the 
* TAIL-CALL-CO call frame or reusing the current one (in tail 
context)
   then set the OUTER-FP and jump to the IP value if a
   number else throw an error. And then setting IP to
   #f and start executing
  


This will probably work quite ok. But the question is how well it play
with call/cc and classic prompts as well as usage of the same object
many times. 

1. This object can only be located one time in the stack because when
in use IP = #f and another application of the object down the stack
would throw an error at the application

2. call/cc prompt code dynwinds and fluids inside co needs to have
special versions

3. When one store the stack space one need to make sure that the co
routines also get copied.

To note is that these objects should be able to live immersed in the
stack space for quick allocation when we inline them inside functions.

--
Anyway I will explore this concept semewhat more and see if all scheme
features can be made to interop in a good way with it.
--

I would also come back to the question if we should have a special
tree-il concept that can be used to model this behavior. I'm now
inclined to concider something like.

(lexical-prompts
((g #:exits (a ...) 
#:body  (th thunk)
#:exit-case (((a) (lambda (k kind arg ...) code2)) ...))
 ...)
  code ...)

The default expansion of this woule then be something like
(letrec  ((g . l)   (let* ((tag (make-tag))
 (th  thunk))
 (call-with-prompts tag
 (apply th l)
 (lambda (k kind arg ...)
 (case kind ((a) code2 ...) ...)
  ...)
   code ...)

So if we can prove:
1. The only references of g ... inside th is through e.g. (a g arg ...)
   where we mean ( g arg ...) = (abort-to-prompt tag 'a g arg ...).

2. these (a ...) forms is not included in any lambda definitions or
   entering any funciton via it's argumnets.

3. g is never feeded as an argument to a function or located inside a
   closure definition anywhere in the form.

4. in ((code2 ...) ...) ... any applications of g ... is located in
   tail position.

5.th is always setted to either k or the initial thunk or never setted
  in any of the ((a) ...) forms.

with these things satisfied we should be able to inline the co routine
or generator inside of the function with quite good
characteristic. This is interesting this should be a logically step up
in generality from CL's tagbody.

Of cause there is a lot of substructure and combination of the general 
construct.

From the rtl code we would manage just ok with the old instructions
just fine except for the need to be be able to store a label value and
goto to a stored label value aka named goto.

WDYT?

/Stefan


  






Re: [PATCHES] Numeric improvements

2013-03-06 Thread Mark H Weaver
Hi Ludovic,

l...@gnu.org (Ludovic Courtès) writes:

 Which of these patches are needed for mini-gmp integration?  It would
 probably be easier to discuss things separately, by small chunks.

Mini-gmp integration depends directly on patches 5 and 7, and those
depend on patches 2, 3, and 4.

 Overall I think I’m mostly incompetent on the numeric stuff, so I’d
 mostly comment on form.

 At first sight, it seems that there are few tests added, in particular
 for the number printer.  I think tests must be added along with the
 patches that claim to fix something.

Agreed.  I'll work on that.

 From cd9784ed33d78e6647a752123bf7be91d65b5c96 Mon Sep 17 00:00:00 2001
 From: Mark H Weaver m...@netris.org
 Date: Sun, 3 Mar 2013 04:34:17 -0500
 Subject: [PATCH 01/10] Improve code in scm_gcd for inum/inum case

 * libguile/numbers.c (scm_gcd): Improve implementation of inum/inum case
   to be more clear and efficient.

 This one looks OK, and should be covered by the tests, according to
 http://hydra.nixos.org/build/4268423/download/2/coverage/libguile/numbers.c.gcov.html.

 Did you measure the performance difference?

Yes.  On my x86_64 machine it improves gcd performance on fixnums by
about 4.25%.  I went ahead and pushed this one.

 Can you fold the explanations as comments?
[...]
 Please use imperative-tense sentences above functions to describe what
 they do, without repeating the function name; also make sure to
 introduce the arguments in the description, written in capital letters
 (info (standards) Comments).

 Some helper functions introduced by the patches lack a comment.

Will do.

 +@deffn {Scheme Procedure} round-ash n count
 +@deffnx {C Function} scm_round_ash (n, count)
 +Return @math{round(@var{n} * 2^@var{count})}, but computed
 +more efficiently.  @var{n} and @var{count} must be exact
 +integers.
 +
 +With @var{n} viewed as an infinite-precision twos-complement
 +integer, @code{round-ash} means a left shift introducing zero
 +bits when @var{count} is positive, or a right shift rounding
 +to the nearest integer (with ties going to the nearest even
 +integer) when @var{count} is negative.  This is a rounded
 +``arithmetic'' shift.
 +
 +@lisp
 +(number-string (round-ash #b1 3) 2) @result{} \1000\
 +(number-string (round-ash #b1010 -1) 2) @result{} \101\
 +(number-string (round-ash #b1010 -2) 2) @result{} \10\
 +(number-string (round-ash #b1011 -2) 2) @result{} \11\
 +(number-string (round-ash #b1101 -2) 2) @result{} \11\
 +(number-string (round-ash #b1110 -2) 2) @result{} \100\
 +@end lisp
 +@end deffn

 What was the rationale for ‘round-ash’?

Well, we need it internally to efficiently convert large integers to
floating-point with proper rounding (see the call to
'round_right_shift_exact_integer' in patch 5).  Given that, it seemed
reasonable to export it to the user, since it's not possible to do that
job efficiently with our current primitives.  However, I don't feel
strongly about it.

 It seems to address to do two things differently from ‘ash’: it’s more
 efficient, and it rounds when right-shifting.  The “more efficient” bit
 is an implementation detail that should also benefit to ‘ash’ (as a user
 I don’t want to have to choose between efficient and non-rounding.)

No, 'round-ash' is not more efficient than 'ash'.  It's more efficient
than the equivalent (round (* n (expt 2 count))).

Thanks for looking through the patches.  I'll work on improving them.

 Mark



Re: [PATCHES] Numeric improvements

2013-03-06 Thread Mark H Weaver
I pushed two of the patches: the 'gcd' patch and the one that verifies
that FLT_RADIX is 2.  Of the remaining 8 patches, I've attached improved
versions of the first 4.  Reviews welcome.

Regards,
  Mark


From f32e8c5ffd789a6dbee48be74f5bbf32978382c3 Mon Sep 17 00:00:00 2001
From: Mark H Weaver m...@netris.org
Date: Sun, 3 Mar 2013 04:34:50 -0500
Subject: [PATCH 1/4] Optimize and simplify fractions code.

* libguile/numbers.c (scm_exact_integer_quotient,
  scm_i_make_ratio_already_reduced): New static functions.

  (scm_i_make_ratio): Rewrite in terms of
  'scm_i_make_ratio_already_reduced'.

  (scm_integer_expt): Optimize fraction case.

  (scm_abs, scm_magnitude, scm_difference, do_divide): Use
  'scm_i_make_ratio_already_reduced'.

* test-suite/tests/numbers.test (expt, integer-expt): Add tests.
---
 libguile/numbers.c|  244 ++---
 test-suite/tests/numbers.test |6 +
 2 files changed, 159 insertions(+), 91 deletions(-)

diff --git a/libguile/numbers.c b/libguile/numbers.c
index 393cf64..e63c17a 100644
--- a/libguile/numbers.c
+++ b/libguile/numbers.c
@@ -442,96 +442,56 @@ scm_i_mpz2num (mpz_t b)
 /* this is needed when we want scm_divide to make a float, not a ratio, even if passed two ints */
 static SCM scm_divide2real (SCM x, SCM y);
 
+/* Make the ratio NUMERATOR/DENOMINATOR, where:
+1. NUMERATOR and DENOMINATOR are exact integers
+2. NUMERATOR and DENOMINATOR are reduced to lowest terms: gcd(n,d) == 1 */
 static SCM
-scm_i_make_ratio (SCM numerator, SCM denominator)
-#define FUNC_NAME make-ratio
+scm_i_make_ratio_already_reduced (SCM numerator, SCM denominator)
 {
-  /* First make sure the arguments are proper.
-   */
-  if (SCM_I_INUMP (denominator))
+  /* Flip signs so that the denominator is positive. */
+  if (scm_is_false (scm_positive_p (denominator)))
 {
-  if (scm_is_eq (denominator, SCM_INUM0))
+  if (SCM_UNLIKELY (scm_is_eq (denominator, SCM_INUM0)))
 	scm_num_overflow (make-ratio);
-  if (scm_is_eq (denominator, SCM_INUM1))
-	return numerator;
-}
-  else 
-{
-  if (!(SCM_BIGP(denominator)))
-	SCM_WRONG_TYPE_ARG (2, denominator);
-}
-  if (!SCM_I_INUMP (numerator)  !SCM_BIGP (numerator))
-SCM_WRONG_TYPE_ARG (1, numerator);
-
-  /* Then flip signs so that the denominator is positive.
-   */
-  if (scm_is_true (scm_negative_p (denominator)))
-{
-  numerator = scm_difference (numerator, SCM_UNDEFINED);
-  denominator = scm_difference (denominator, SCM_UNDEFINED);
-}
-
-  /* Now consider for each of the four fixnum/bignum combinations
- whether the rational number is really an integer.
-  */
-  if (SCM_I_INUMP (numerator))
-{
-  scm_t_inum x = SCM_I_INUM (numerator);
-  if (scm_is_eq (numerator, SCM_INUM0))
-	return SCM_INUM0;
-  if (SCM_I_INUMP (denominator))
+  else
 	{
-	  scm_t_inum y;
-	  y = SCM_I_INUM (denominator);
-	  if (x == y)
-	return SCM_INUM1;
-	  if ((x % y) == 0)
-	return SCM_I_MAKINUM (x / y);
+	  numerator = scm_difference (numerator, SCM_UNDEFINED);
+	  denominator = scm_difference (denominator, SCM_UNDEFINED);
 	}
-  else
-{
-  /* When x == SCM_MOST_NEGATIVE_FIXNUM we could have the negative
- of that value for the denominator, as a bignum.  Apart from
- that case, abs(bignum)  abs(inum) so inum/bignum is not an
- integer.  */
-  if (x == SCM_MOST_NEGATIVE_FIXNUM
-   mpz_cmp_ui (SCM_I_BIG_MPZ (denominator),
- - SCM_MOST_NEGATIVE_FIXNUM) == 0)
-	return SCM_I_MAKINUM(-1);
-}
 }
-  else if (SCM_BIGP (numerator))
+
+  /* Check for the integer case */
+  if (scm_is_eq (denominator, SCM_INUM1))
+return numerator;
+
+  return scm_double_cell (scm_tc16_fraction,
+			  SCM_UNPACK (numerator),
+			  SCM_UNPACK (denominator), 0);
+}
+
+static SCM scm_exact_integer_quotient (SCM x, SCM y);
+
+/* Make the ratio NUMERATOR/DENOMINATOR */
+static SCM
+scm_i_make_ratio (SCM numerator, SCM denominator)
+#define FUNC_NAME make-ratio
+{
+  /* Make sure the arguments are proper */
+  if (!SCM_LIKELY (SCM_I_INUMP (numerator) || SCM_BIGP (numerator)))
+SCM_WRONG_TYPE_ARG (1, numerator);
+  else if (!SCM_LIKELY (SCM_I_INUMP (denominator) || SCM_BIGP (denominator)))
+SCM_WRONG_TYPE_ARG (2, denominator);
+  else
 {
-  if (SCM_I_INUMP (denominator))
+  SCM the_gcd = scm_gcd (numerator, denominator);
+  if (!(scm_is_eq (the_gcd, SCM_INUM1)))
 	{
-	  scm_t_inum yy = SCM_I_INUM (denominator);
-	  if (mpz_divisible_ui_p (SCM_I_BIG_MPZ (numerator), yy))
-	return scm_divide (numerator, denominator);
-	}
-  else
-	{
-	  if (scm_is_eq (numerator, denominator))
-	return SCM_INUM1;
-	  if (mpz_divisible_p (SCM_I_BIG_MPZ (numerator),
-			   SCM_I_BIG_MPZ (denominator)))
-	return scm_divide(numerator, denominator);
+	  /* Reduce to lowest terms */
+	  numerator = scm_exact_integer_quotient 

What's the plan of Guile on GSoC 2013?

2013-03-06 Thread Nala Ginrut
Hi folks!
I've noticed Jose E. Marchesi mentioned GSoC 2013 for GNU, and I checked
out the GSoC page of GNU Octave:
http://wiki.octave.org/Summer_of_Code_Project_Ideas
Do we have such a page for GSoC ideas?

And what's the plan for it?

Thanks!




Re: [PATCHES] Numeric improvements

2013-03-06 Thread Mark H Weaver
Here are improved versions of the patches needed to enable mini-gmp
integration.  I think these are ready to commit.  Reviews welcome.

 Mark


From f32e8c5ffd789a6dbee48be74f5bbf32978382c3 Mon Sep 17 00:00:00 2001
From: Mark H Weaver m...@netris.org
Date: Sun, 3 Mar 2013 04:34:50 -0500
Subject: [PATCH 1/5] Optimize and simplify fractions code.

* libguile/numbers.c (scm_exact_integer_quotient,
  scm_i_make_ratio_already_reduced): New static functions.

  (scm_i_make_ratio): Rewrite in terms of
  'scm_i_make_ratio_already_reduced'.

  (scm_integer_expt): Optimize fraction case.

  (scm_abs, scm_magnitude, scm_difference, do_divide): Use
  'scm_i_make_ratio_already_reduced'.

* test-suite/tests/numbers.test (expt, integer-expt): Add tests.
---
 libguile/numbers.c|  244 ++---
 test-suite/tests/numbers.test |6 +
 2 files changed, 159 insertions(+), 91 deletions(-)

diff --git a/libguile/numbers.c b/libguile/numbers.c
index 393cf64..e63c17a 100644
--- a/libguile/numbers.c
+++ b/libguile/numbers.c
@@ -442,96 +442,56 @@ scm_i_mpz2num (mpz_t b)
 /* this is needed when we want scm_divide to make a float, not a ratio, even if passed two ints */
 static SCM scm_divide2real (SCM x, SCM y);
 
+/* Make the ratio NUMERATOR/DENOMINATOR, where:
+1. NUMERATOR and DENOMINATOR are exact integers
+2. NUMERATOR and DENOMINATOR are reduced to lowest terms: gcd(n,d) == 1 */
 static SCM
-scm_i_make_ratio (SCM numerator, SCM denominator)
-#define FUNC_NAME make-ratio
+scm_i_make_ratio_already_reduced (SCM numerator, SCM denominator)
 {
-  /* First make sure the arguments are proper.
-   */
-  if (SCM_I_INUMP (denominator))
+  /* Flip signs so that the denominator is positive. */
+  if (scm_is_false (scm_positive_p (denominator)))
 {
-  if (scm_is_eq (denominator, SCM_INUM0))
+  if (SCM_UNLIKELY (scm_is_eq (denominator, SCM_INUM0)))
 	scm_num_overflow (make-ratio);
-  if (scm_is_eq (denominator, SCM_INUM1))
-	return numerator;
-}
-  else 
-{
-  if (!(SCM_BIGP(denominator)))
-	SCM_WRONG_TYPE_ARG (2, denominator);
-}
-  if (!SCM_I_INUMP (numerator)  !SCM_BIGP (numerator))
-SCM_WRONG_TYPE_ARG (1, numerator);
-
-  /* Then flip signs so that the denominator is positive.
-   */
-  if (scm_is_true (scm_negative_p (denominator)))
-{
-  numerator = scm_difference (numerator, SCM_UNDEFINED);
-  denominator = scm_difference (denominator, SCM_UNDEFINED);
-}
-
-  /* Now consider for each of the four fixnum/bignum combinations
- whether the rational number is really an integer.
-  */
-  if (SCM_I_INUMP (numerator))
-{
-  scm_t_inum x = SCM_I_INUM (numerator);
-  if (scm_is_eq (numerator, SCM_INUM0))
-	return SCM_INUM0;
-  if (SCM_I_INUMP (denominator))
+  else
 	{
-	  scm_t_inum y;
-	  y = SCM_I_INUM (denominator);
-	  if (x == y)
-	return SCM_INUM1;
-	  if ((x % y) == 0)
-	return SCM_I_MAKINUM (x / y);
+	  numerator = scm_difference (numerator, SCM_UNDEFINED);
+	  denominator = scm_difference (denominator, SCM_UNDEFINED);
 	}
-  else
-{
-  /* When x == SCM_MOST_NEGATIVE_FIXNUM we could have the negative
- of that value for the denominator, as a bignum.  Apart from
- that case, abs(bignum)  abs(inum) so inum/bignum is not an
- integer.  */
-  if (x == SCM_MOST_NEGATIVE_FIXNUM
-   mpz_cmp_ui (SCM_I_BIG_MPZ (denominator),
- - SCM_MOST_NEGATIVE_FIXNUM) == 0)
-	return SCM_I_MAKINUM(-1);
-}
 }
-  else if (SCM_BIGP (numerator))
+
+  /* Check for the integer case */
+  if (scm_is_eq (denominator, SCM_INUM1))
+return numerator;
+
+  return scm_double_cell (scm_tc16_fraction,
+			  SCM_UNPACK (numerator),
+			  SCM_UNPACK (denominator), 0);
+}
+
+static SCM scm_exact_integer_quotient (SCM x, SCM y);
+
+/* Make the ratio NUMERATOR/DENOMINATOR */
+static SCM
+scm_i_make_ratio (SCM numerator, SCM denominator)
+#define FUNC_NAME make-ratio
+{
+  /* Make sure the arguments are proper */
+  if (!SCM_LIKELY (SCM_I_INUMP (numerator) || SCM_BIGP (numerator)))
+SCM_WRONG_TYPE_ARG (1, numerator);
+  else if (!SCM_LIKELY (SCM_I_INUMP (denominator) || SCM_BIGP (denominator)))
+SCM_WRONG_TYPE_ARG (2, denominator);
+  else
 {
-  if (SCM_I_INUMP (denominator))
+  SCM the_gcd = scm_gcd (numerator, denominator);
+  if (!(scm_is_eq (the_gcd, SCM_INUM1)))
 	{
-	  scm_t_inum yy = SCM_I_INUM (denominator);
-	  if (mpz_divisible_ui_p (SCM_I_BIG_MPZ (numerator), yy))
-	return scm_divide (numerator, denominator);
-	}
-  else
-	{
-	  if (scm_is_eq (numerator, denominator))
-	return SCM_INUM1;
-	  if (mpz_divisible_p (SCM_I_BIG_MPZ (numerator),
-			   SCM_I_BIG_MPZ (denominator)))
-	return scm_divide(numerator, denominator);
+	  /* Reduce to lowest terms */
+	  numerator = scm_exact_integer_quotient (numerator, the_gcd);
+	  denominator = scm_exact_integer_quotient