/* Algorithm AS 51 Appl. Statist. (1972), vol. 21, p. 218
original (C) Royal Statistical Society 1972
Performs an iterative proportional fit of the marginal totals of a
contingency table.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <math.h>
#include <stdio.h>
#include <R_ext/Memory.h>
#include <R_ext/Applic.h>
#undef max
#undef min
#undef abs
#define max(a, b) ((a) < (b) ? (b) : (a))
#define min(a, b) ((a) > (b) ? (b) : (a))
#define abs(x) ((x) >= 0 ? (x) : -(x))
static void collap(int *nvar, double *x, double *y, int *locy,
int *nx, int *ny, int *dim, int *config);
static void adjust(int *nvar, double *x, double *y, double *z,
int *locz, int *nx, int *ny, int *nz, int *dim,
int *config, double *d);
static int *lvector(int n);
/* Table of constant values */
static int c__1 = 1;
void loglin(int *nvar, int *dim, int *ncon, int *config, int *ntab,
double *table, double *fit, int *locmar, int *nmar, double *marg,
int *nu, double *u, double *maxdev, int *maxit,
double *dev, int *nlast, int *ifault)
{
int i, j, k, n, point, size, *icon, *check;
double x, y, xmax;
check = lvector(*nvar);
icon = lvector(*nvar);
/* Parameter adjustments */
--dim;
--locmar;
config -= *nvar + 1;
--fit;
--table;
--marg;
--u;
--dev;
/* Function body */
*ifault = 0;
*nlast = 0;
/* Check validity of NVAR, the number of variables, and of maxit,
the maximum number of iterations */
if (*nvar > 0 && *maxit > 0) {
goto L10;
}
L5:
*ifault = 4;
return;
/* Look at table and fit constants */
L10:
size = 1;
for (j = 1; j <= *nvar; ++j) {
if (dim[j] <= 0) {
goto L5;
}
size *= dim[j];
}
if (size <= *ntab) {
goto L40;
}
L35:
*ifault = 2;
return;
L40:
x = 0.;
y = 0.;
for (i = 1; i <= size; ++i) {
if (table[i] < 0. || fit[i] < 0.) {
goto L5;
}
x += table[i];
y += fit[i];
}
/* Make a preliminary adjustment to obtain the fit to an empty
configuration list */
if (y == 0.) {
goto L5;
}
x /= y;
for (i = 1; i <= size; ++i) {
fit[i] = x * fit[i];
}
if (*ncon <= 0 || config[*nvar + 1] == 0) {
return;
}
/* Allocate marginal tables */
point = 1;
for (i = 1; i <= *ncon; ++i) {
/* A zero beginning a configuration indicates that the list is
completed */
if (config[i * *nvar + 1] == 0) {
goto L160;
}
/* Get marginal table size. While doing this task, see if the
configuration list contains duplications or elements out of
range. */
size = 1;
for (j = 0; j < *nvar; ++j) {
check[j] = 0;
}
for (j = 1; j <= *nvar; ++j) {
k = config[j + i * *nvar];
/* A zero indicates the end of the string. */
if (k == 0) {
goto L130;
}
/* See if element is valid. */
if (k >= 0 && k <= *nvar) {
goto L100;
}
L95:
*ifault = 1;
return;
/* Check for duplication */
L100:
if (check[k - 1]) {
goto L95;
}
check[k - 1] = (1);
/* Get size */
size *= dim[k];
}
/* Since U is used to store fitted marginals, size must not
exceed NU */
L130:
if (size > *nu) {
goto L35;
}
/* LOCMAR points to marginal tables to be placed in MARG */
locmar[i] = point;
point += size;
}
/* Get N, number of valid configurations */
i = *ncon + 1;
L160:
n = i - 1;
/* See if MARG can hold all marginal tables */
if (point > *nmar + 1) {
goto L35;
}
/* Obtain marginal tables */
for (i = 1; i <= n; ++i) {
for (j = 1; j <= *nvar; ++j) {
icon[j - 1] = config[j + i * *nvar];
}
collap(nvar, &table[1], &marg[1], &locmar[i], ntab, nmar,
&dim[1], icon);
}
/* Perform iterations */
for (k = 1; k <= *maxit; ++k) {
/* XMAX is maximum deviation observed between fitted and true
marginal during a cycle */
xmax = 0.;
for (i = 1; i <= n; ++i) {
for (j = 1; j <= *nvar; ++j) {
icon[j - 1] = config[j + i * *nvar];
}
collap(nvar, &fit[1], &u[1], &c__1, ntab, nu, &dim[1],
icon);
adjust(nvar, &fit[1], &u[1], &marg[1], &locmar[i], ntab, nu,
nmar, &dim[1], icon, &xmax);
}
/* Test convergence */
dev[k] = xmax;
if (xmax < *maxdev) {
goto L240;
}
}
if (*maxit > 1) {
goto L230;
}
*nlast = 1;
return;
/* No convergence */
L230:
*ifault = 3;
*nlast = *maxit;
return;
/* Normal termination */
L240:
*nlast = k;
return;
}
/* Algorithm AS 51.1 Appl. Statist. (1972), vol. 21, p. 218
Computes a marginal table from a complete table.
All parameters are assumed valid without test.
The larger table is X and the smaller one is Y.
*/
void collap(int *nvar, double *x, double *y, int *locy, int *nx, int
*ny, int *dim, int *config)
{
int i, j, k, l, n, locu, *coord, *size;
size = lvector(*nvar + 1);
coord = lvector(*nvar);
/* Parameter adjustments */
--config;
--dim;
--x;
--y;
/* Initialize arrays */
size[0] = 1;
for (k = 1; k <= *nvar; ++k) {
l = config[k];
if (l == 0) {
goto L20;
}
size[k] = size[k - 1] * dim[l];
}
/* Find number of variables in configuration */
k = *nvar + 1;
L20:
n = k - 1;
/* Initialize Y. First cell of marginal table is at Y(LOCY) and
table has SIZE(K) elements */
locu = *locy + size[k - 1] - 1;
for (j = *locy; j <= locu; ++j) {
y[j] = 0.;
}
/* Initialize coordinates */
for (k = 0; k < *nvar; ++k) {
coord[k] = 0;
}
/* Find locations in tables */
i = 1;
L60:
j = *locy;
for (k = 1; k <= n; ++k) {
l = config[k];
j += coord[l - 1] * size[k - 1];
}
y[j] += x[i];
/* Update coordinates */
++i;
for (k = 1; k <= *nvar; ++k) {
++coord[k - 1];
if (coord[k - 1] < dim[k]) {
goto L60;
}
coord[k - 1] = 0;
}
return;
}
/* Algorithm AS 51.2 Appl. Statist. (1972), vol. 21, p. 218
Makes proportional adjustment corresponding to CONFIG.
All parameters are assumed valid without test.
*/
void adjust(int *nvar, double *x, double *y, double *z, int *locz, int
*nx, int *ny, int *nz, int *dim, int *config, double *d)
{
int i, j, k, l, n, *coord, *size;
double e;
size = lvector(*nvar + 1);
coord = lvector(*nvar);
/* Parameter adjustments */
--config;
--dim;
--x;
--y;
--z;
/* Set size array */
size[0] = 1;
for (k = 1; k <= *nvar; ++k) {
l = config[k];
if (l == 0) {
goto L20;
}
size[k] = size[k - 1] * dim[l];
}
/* Find number of variables in configuration */
k = *nvar + 1;
L20:
n = k - 1;
/* Test size of deviation */
l = size[k - 1];
j = 1;
k = *locz;
for (i = 1; i <= l; ++i) {
e = abs(z[k] - y[j]);
if (e > *d) {
*d = e;
}
++j;
++k;
}
/* Initialize coordinates */
for (k = 0; k < *nvar; ++k) {
coord[k] = 0;
}
i = 1;
/* Perform adjustment */
L50:
j = 0;
for (k = 1; k <= n; ++k) {
l = config[k];
j += coord[l - 1] * size[k - 1];
}
k = j + *locz;
++j;
/* Note that Y(J) should be non-negative */
if (y[j] <= 0.) {
x[i] = 0.;
}
if (y[j] > 0.) {
x[i] = x[i] * z[k] / y[j];
}
/* Update coordinates */
++i;
for (k = 1; k <= *nvar; ++k) {
++coord[k - 1];
if (coord[k - 1] < dim[k]) {
goto L50;
}
coord[k - 1] = 0;
}
return;
}
/* Auxiliary routine to get rid of limitations on the number of factors
in the model.
Changed to use R_alloc to avoid memory leak if routine was
interrupted.
*/
static int *lvector(int n) {
return (int *) R_alloc(n, sizeof(int));
}
syntax highlighted by Code2HTML, v. 0.9.1