Newsgroups: comp.lang.apl
Path: watmath!watserv2.uwaterloo.ca!torn!utnut!cs.utexas.edu!wupost!csus.edu!sfsuvax1.sfsu.edu!vpcsc4
From: vpcsc4@sfsuvax1.sfsu.edu (Emmett McLean)
Subject: LinkJ fractions hack
Message-ID: <1993May27.021703.29684@csus.edu>
Sender: news@csus.edu
Organization: San Francisco State University
Date: Thu, 27 May 1993 02:17:03 GMT
Lines: 186

  
Here is a Bob Craig's program, frac, (from research.att.com at
netlib/c/frac.Z) which I've interfaced with J via LinkJ. Which
when given a noun, v, outputs a boxed result d and n, such that
+--+--+
| n| d|
+--+--+
to accuracy epsilon = | (v - n/d) / v | <= (error = 5.820766091e-11)
For example :
   10!:0 (17 % 217)
+--------+
|+--+---+|
||17|217||
|+--+---+|
+--------+
Here is an example where frac might be useful :
Suppose
   s =. ,~ $ (4 _1&,)@(,&_1)@(-&2 # 0:)
   ] s 5
 4 _1  0  0  0
_1  4 _1  0  0
 0 _1  4 _1  0
 0  0 _1  4 _1
 0  0  0 _1  4
And
   ] b =. %. s 5
  0.267949  0.0717949 0.0192308 0.00512821 0.00128205
 0.0717949   0.287179 0.0769231  0.0205128 0.00512821
 0.0192308  0.0769231  0.288462  0.0769231  0.0192308
0.00512821  0.0205128 0.0769231   0.287179  0.0717949
0.00128205 0.00512821 0.0192308  0.0717949   0.267949
We wish to find a pattern in the numbers of b. So :
   10!:0 b
+---------+--------+-------+--------+---------+
|+---+---+|+--+---+|+-+--+ |+-+---+ |+-+---+  |
||209|780|||14|195|||1|52| ||1|195| ||1|780|  |
|+---+---+|+--+---+|+-+--+ |+-+---+ |+-+---+  |
+---------+--------+-------+--------+---------+
|+--+---+ |+--+---+|+-+--+ |+-+---+ |+-+---+  |
||14|195| ||56|195|||1|13| ||4|195| ||1|195|  |
|+--+---+ |+--+---+|+-+--+ |+-+---+ |+-+---+  |
+---------+--------+-------+--------+---------+
|+-+--+   |+-+--+  |+--+--+|+-+--+  |+-+--+   |
||1|52|   ||1|13|  ||15|52|||1|13|  ||1|52|   |
|+-+--+   |+-+--+  |+--+--+|+-+--+  |+-+--+   |
+---------+--------+-------+--------+---------+
|+-+---+  |+-+---+ |+-+--+ |+--+---+|+--+---+ |
||1|195|  ||4|195| ||1|13| ||56|195|||14|195| |
|+-+---+  |+-+---+ |+-+--+ |+--+---+|+--+---+ |
+---------+--------+-------+--------+---------+
|+-+---+  |+-+---+ |+-+--+ |+--+---+|+---+---+|
||1|780|  ||1|195| ||1|52| ||14|195|||209|780||
|+-+---+  |+-+---+ |+-+--+ |+--+---+|+---+---+|
+---------+--------+-------+--------+---------+
Now we can multiply by 780 and from there try to
get the pattern
   780 * b
209  56  15   4   1
 56 224  60  16   4
 15  60 225  60  15
  4  16  60 224  56
  1   4  15  56 209

--------------main.c----------------  
#include <stdio.h>
#include <string.h>
#include "lj.h" 
#include <math.h>
#define LJF1RANK(m,f,self)    {RZ(   y); if(m<AR(y))            \
                                 R rank1ex(  y,(A)self,(I)m,     f);}
static F1(frmd){A z;I b=1,n,d;D frac(),e=5.820766091e-11;
  LJF1RANK(0,frmd,0); 
  ASSERT((BOOL+INT+FL)&AT(y),EVDOMAIN);
  if(FL!=AT(y))y=(A)cvt(FL,y);
  if(0>*(D*)AV(y)){y=(A)tymes(sc(-1),y);b= -1L;};
  frac(*(D*)AV(y),&n,&d,5.820766091e-11);
/*  z=(A)reshape(sc(2),jma(INT,2L,1L));  */
  z=(A)box(link(sc(b*n),sc(d)));
  R(z);
 }
C jc(k,f1,f2)I k;AF*f1,*f2;
{ switch(k){
  case 0:
   *f1=frmd;
   *f2=NULL;
   R 1;    
  default:
   ASSERT(0,EVNONCE); 
  }
}
main()
{A t;C s[456];
 jinit();
 while(1)
 {
  printf("   ");
  if(NULL==gets(s)||'\004'==*(s+strlen(s)-1))exit(0);
  t=(A)jx(s);
  if(jerr)printf("jerr: %d\n",jerr); else if(!asgn)jpr(t);
 }
}
--------------fr.c----------------  
/* The following values are used to test for too small
   and too large values of v for an integer fraction to
   represent.

*/

#define max 1.7014118346046923e38
#define min 5.8774717541114377e-39

double
frac(v, n, d, error)
double v, error;
int *n, *d;
{
	/*
	  given a number, v, this function outputs two integers,
	  d and n, such that

		v = n / d

	  to accuracy
		epsilon = | (v - n/d) / v | <= error

	  input:  v = decimal number you want replaced by fraction.
		  error = accuracy to which the fraction should
			  represent the number v.

	  output:  n = numerator of representing fraction.
		   d = denominator of representing fraction.

	  return value:  -1.0 if (v < MIN || v > MAX || error < 0.0)
			 | (v - n/d) / v | otherwise.

	  Note:  This program only works for positive numbers, v.

	  reference:  Jerome Spanier and Keith B. Oldham, "An Atlas
		of Functions," Springer-Verlag, 1987, pp. 665-7.
	*/

	int D, N, t;
	double epsilon, r, m, fabs();


	if (v < min || v > max || error < 0.0)
		return(-1.0);
	*d = D = 1;
	*n = (int)v;
	N = (*n) + 1;
	goto three;

one:	if (r > 1.0)
		goto two;
	r = 1.0/r;
two:	N += (*n)*(int)r;
	D += (*d)*(int)r;
	(*n) += N;
	(*d) += D;
three:	r = 0.0;
	if (v*(*d) == (double)(*n))
		goto four;
	r = (N - v*D)/(v*(*d) - (*n));
	if (r > 1.0)
		goto four;
	t = N;
	N = (*n);
	*n = t;
	t = D;
	D = (*d);
	*d = t;
/* four:	printf("%d/%d", *n, *d); */
four: epsilon = fabs(1.0 - (*n)/(v*(*d)));
	if (epsilon <= error)
		goto six;
	m = 1.0;
	do {
		m *= 10.0;
	} while (m*epsilon < 1.0);
	epsilon = 1.0/m * ((int)(0.5 + m*epsilon));
/* six:	printf("	epsilon = %e\n", epsilon); */
six:	if (epsilon <= error)
		return(epsilon);
	if (r != 0.0)
		goto one;
}
