#define PERL_NO_GET_CONTEXT
#define NO_XSLOCKS
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <assert.h>
#define NUM_LATTICES 4
typedef
unsigned
short
AM_SHORT;
typedef
unsigned
long
AM_LONG;
typedef
AM_LONG AM_BIG_INT[8];
#define high_bits(x) x >> 16
#define low_bits(x) x & 0xffff
#define carry(var, ind) \
var[ind + 1] += high_bits(var[ind]); \
var[ind] = low_bits(var[ind])
#define carry_pointer(p) \
*(p + 1) += high_bits(*(p)); \
*(p) = low_bits(*(p))
#define carry_replace(var, ind) \
var[ind + 1] = high_bits(var[ind]); \
var[ind] = low_bits(var[ind])
#define hash_pointer_from_stack(ind) \
(HV *) SvRV(ST(ind))
#define array_pointer_from_stack(ind) \
AvARRAY((AV *)SvRV(ST(ind)))
#define unsigned_int_from_stack(ind) \
SvUVX(ST(ind))
#define iter_supras(loop_var, supra_ptr) \
loop_var = supra_ptr + supra_ptr->next; loop_var != supra_ptr; loop_var = supra_ptr + loop_var->next
#define sublist_top(supra) \
supra->data + supra->data[0] + 1
typedef
struct
AM_supra {
AM_SHORT *data;
AM_SHORT count;
AM_SHORT next;
unsigned
char
touched;
} AM_SUPRA;
typedef
struct
AM_guts {
AM_SHORT *lattice_list[NUM_LATTICES];
AM_SUPRA *supra_list[NUM_LATTICES];
SV **lattice_sizes;
SV **classes;
SV **itemcontextchain;
HV *itemcontextchainhead;
HV *context_to_class;
HV *context_size;
HV *pointers;
HV *raw_gang;
SV **sum;
IV num_classes;
} AM_GUTS;
static
int
AMguts_mgFree(pTHX_ SV *sv, MAGIC *magic) {
int
i;
AM_GUTS *guts = (AM_GUTS *) SvPVX(magic->mg_obj);
for
(i = 0; i < NUM_LATTICES; ++i) {
Safefree(guts->lattice_list[i]);
Safefree(guts->supra_list[i][0].data);
Safefree(guts->supra_list[i]);
}
return
0;
}
MGVTBL AMguts_vtab = {
NULL,
NULL,
NULL,
NULL,
AMguts_mgFree
};
AM_LONG tens[16];
AM_LONG ones[16];
const
unsigned
int
ASCII_0 = 0x30;
const
unsigned
int
DIVIDE_SPACE = 10;
const
int
OUTSPACE_SIZE = 55;
void
normalize(pTHX_ SV *s) {
AM_LONG *p = (AM_LONG *)SvPVX(s);
AM_LONG dspace[DIVIDE_SPACE];
AM_LONG qspace[DIVIDE_SPACE];
AM_LONG *dividend, *quotient, *dptr, *qptr;
STRLEN length = SvCUR(s) /
sizeof
(AM_LONG);
assert
(length <= DIVIDE_SPACE);
char
outspace[OUTSPACE_SIZE];
char
*outptr;
outptr = outspace + (OUTSPACE_SIZE - 1);
unsigned
int
outlength = 0;
long
double
nn = 0;
for
(
int
j = 8; j; --j) {
nn = 65536.0 * nn + (
double
) *(p + j - 1);
}
dividend = &dspace[0];
quotient = &qspace[0];
Copy(p, dividend, length, AM_LONG);
while
(1) {
while
(length && (*(dividend + length - 1) == 0)) {
--length;
}
if
(length == 0) {
sv_setpvn(s, outptr, outlength);
break
;
}
dptr = dividend + length - 1;
qptr = quotient + length - 1;
AM_LONG carry = 0;
while
(dptr >= dividend) {
unsigned
int
i;
*dptr += carry << 16;
*qptr = 0;
for
(i = 16; i; ) {
--i;
if
(tens[i] <= *dptr) {
*dptr -= tens[i];
*qptr += ones[i];
}
}
carry = *dptr;
--dptr;
--qptr;
}
--outptr;
*outptr = (
char
)(ASCII_0 + *dividend) & 0x00ff;
++outlength;
AM_LONG *temp = dividend;
dividend = quotient;
quotient = temp;
}
SvNVX(s) = nn;
SvNOK_on(s);
}
unsigned
short
*intersect_supras(
AM_SHORT *intersection_list_top, AM_SHORT *subcontext_list_top, AM_SHORT *k){
while
(1) {
while
(*intersection_list_top > *subcontext_list_top) {
--intersection_list_top;
}
if
(*intersection_list_top == 0) {
break
;
}
if
(*intersection_list_top < *subcontext_list_top) {
AM_SHORT *temp = intersection_list_top;
intersection_list_top = subcontext_list_top;
subcontext_list_top = temp;
continue
;
}
*k = *intersection_list_top;
--intersection_list_top;
--subcontext_list_top;
--k;
}
return
k;
}
AM_SHORT intersect_supras_final(
AM_SHORT *intersection_list_top, AM_SHORT *subcontext_list_top,
AM_SHORT *intersect, AM_SHORT *subcontext_class){
AM_SHORT
class
= 0;
AM_SHORT length = 0;
while
(1) {
while
(*intersection_list_top > *subcontext_list_top) {
--intersection_list_top;
}
if
(*intersection_list_top == 0) {
break
;
}
if
(*intersection_list_top < *subcontext_list_top) {
AM_SHORT *temp = intersection_list_top;
intersection_list_top = subcontext_list_top;
subcontext_list_top = temp;
continue
;
}
*intersect = *intersection_list_top;
++intersect;
++length;
if
(
class
== 0) {
if
(length > 1) {
length = 0;
break
;
}
else
{
class
= subcontext_class[*intersection_list_top];
}
}
else
{
if
(
class
!= subcontext_class[*intersection_list_top]) {
length = 0;
break
;
}
}
--intersection_list_top;
--subcontext_list_top;
}
return
length;
}
void
clear_supras(AM_SUPRA **supra_list,
int
supras_length)
{
AM_SUPRA *p;
for
(
int
i = 0; i < supras_length; i++)
{
for
(iter_supras(p, supra_list[i]))
{
Safefree(p->data);
}
}
}
MODULE = Algorithm::AM PACKAGE = Algorithm::AM
PROTOTYPES: DISABLE
BOOT:
{
AM_LONG ten = 10;
AM_LONG one = 1;
AM_LONG *tensptr = &tens[0];
AM_LONG *onesptr = &ones[0];
unsigned
int
i;
for
(i = 16; i; i--) {
*tensptr = ten;
*onesptr = one;
++tensptr;
++onesptr;
ten <<= 1;
one <<= 1;
}
}
void
_xs_initialize(...)
PPCODE:
AM_GUTS guts;
HV *self = hash_pointer_from_stack(0);
SV **lattice_sizes = array_pointer_from_stack(1);
guts.classes = array_pointer_from_stack(2);
guts.itemcontextchain = array_pointer_from_stack(3);
guts.itemcontextchainhead = hash_pointer_from_stack(4);
guts.context_to_class = hash_pointer_from_stack(5);
guts.context_size = hash_pointer_from_stack(6);
guts.pointers = hash_pointer_from_stack(7);
guts.raw_gang = hash_pointer_from_stack(8);
guts.sum = array_pointer_from_stack(9);
guts.num_classes = av_len((AV *) SvRV(ST(9)));
for
(
int
i = 0; i < NUM_LATTICES; ++i) {
UV v = SvUVX(lattice_sizes[i]);
Newxz(guts.lattice_list[i], 1 << v, AM_SHORT);
Newxz(guts.supra_list[i], 1 << (v + 1), AM_SUPRA);
Newxz(guts.supra_list[i][0].data, 2, AM_SHORT);
}
SV *svguts = newSVpv((
char
*)&guts,
sizeof
(AM_GUTS));
sv_magic((SV *) self, svguts, PERL_MAGIC_ext, NULL, 0);
SvRMAGICAL_off((SV *) self);
MAGIC *magic = mg_find((SV *)self, PERL_MAGIC_ext);
magic->mg_virtual = &AMguts_vtab;
mg_magical((SV *) self);
void
_fillandcount(...)
PPCODE:
HV *self = hash_pointer_from_stack(0);
SV **lattice_sizes_input = array_pointer_from_stack(1);
UV linear_flag = unsigned_int_from_stack(2);
MAGIC *magic = mg_find((SV *)self, PERL_MAGIC_ext);
AM_GUTS *guts = (AM_GUTS *)SvPVX(magic->mg_obj);
AM_SHORT **lattice_list = guts->lattice_list;
AM_SUPRA **supra_list = guts->supra_list;
AM_SHORT nptr[NUM_LATTICES];
AM_SHORT lattice_sizes[NUM_LATTICES];
for
(
int
sublattice_index = 0; sublattice_index < NUM_LATTICES; ++sublattice_index) {
lattice_sizes[sublattice_index] = (AM_SHORT) SvUVX(lattice_sizes_input[sublattice_index]);
Zero(lattice_list[sublattice_index], 1 << lattice_sizes[sublattice_index], AM_SHORT);
supra_list[sublattice_index][0].next = 0;
nptr[sublattice_index] = 1;
for
(
int
i = 1; i < 1 << (lattice_sizes[sublattice_index] + 1); ++i) {
supra_list[sublattice_index][i].next = (AM_SHORT) i + 1;
}
}
HV *context_to_class = guts->context_to_class;
AM_SHORT subcontextnumber = (AM_SHORT)HvUSEDKEYS(context_to_class);
AM_SHORT *subcontext;
Newxz(subcontext, NUM_LATTICES *(subcontextnumber + 1), AM_SHORT);
subcontext += NUM_LATTICES * subcontextnumber;
AM_SHORT *subcontext_class;
Newxz(subcontext_class, subcontextnumber + 1, AM_SHORT);
subcontext_class += subcontextnumber;
AM_SHORT *intersectlist, *intersectlist2, *intersectlist3;
AM_SHORT *ilist2top, *ilist3top;
Newxz(intersectlist, subcontextnumber + 1, AM_SHORT);
Newxz(intersectlist2, subcontextnumber + 1, AM_SHORT);
ilist2top = intersectlist2 + subcontextnumber;
Newxz(intersectlist3, subcontextnumber + 1, AM_SHORT);
ilist3top = intersectlist3 + subcontextnumber;
hv_iterinit(context_to_class);
HE *context_to_class_entry;
while
((context_to_class_entry = hv_iternext(context_to_class))) {
AM_SHORT *contextptr = (AM_SHORT *) HeKEY(context_to_class_entry);
AM_SHORT
class
= (AM_SHORT) SvUVX(HeVAL(context_to_class_entry));
for
(
int
sublattice_index = 0; sublattice_index < NUM_LATTICES; ++sublattice_index, ++contextptr) {
AM_SHORT active = lattice_sizes[sublattice_index];
AM_SHORT *lattice = lattice_list[sublattice_index];
AM_SUPRA *supralist = supra_list[sublattice_index];
AM_SHORT nextsupra = nptr[sublattice_index];
AM_SHORT context = *contextptr;
subcontext[sublattice_index] = context;
AM_SHORT gaps[16];
if
(context == 0) {
AM_SUPRA *p;
for
(iter_supras(p, supralist)) {
AM_SHORT *data;
Newxz(data, p->data[0] + 3, AM_SHORT);
Copy(p->data + 2, data + 3, p->data[0], AM_SHORT);
data[2] = subcontextnumber;
data[0] = p->data[0] + 1;
Safefree(p->data);
p->data = data;
}
if
(lattice[context] == 0) {
AM_SHORT count = 0;
AM_SHORT ci = nptr[sublattice_index];
nptr[sublattice_index] = supralist[ci].next;
AM_SUPRA *c = supralist + ci;
c->next = supralist->next;
supralist->next = ci;
Newxz(c->data, 3, AM_SHORT);
c->data[2] = subcontextnumber;
c->data[0] = 1;
for
(
int
i = 0; i < (1 << active); ++i) {
if
(lattice[i] == 0) {
lattice[i] = ci;
++count;
}
}
c->count = count;
}
continue
;
}
AM_SHORT d = context;
AM_SHORT numgaps = 0;
for
(
int
i = 1 << (active - 1); i; i >>= 1) {
if
(!(i & context)) {
gaps[numgaps++] = i;
}
}
AM_SHORT t = 1 << numgaps;
AM_SHORT pi = lattice[context];
AM_SUPRA *p = supralist + pi;
if
(pi) {
--(p->count);
}
AM_SHORT ci = nextsupra;
nextsupra = supralist[ci].next;
p->touched = 1;
AM_SUPRA *c = supralist + ci;
c->touched = 0;
c->next = p->next;
p->next = ci;
c->count = 1;
Newxz(c->data, p->data[0] + 3, AM_SHORT);
Copy(p->data + 2, c->data + 3, p->data[0], AM_SHORT);
c->data[2] = subcontextnumber;
c->data[0] = p->data[0] + 1;
lattice[context] = ci;
while
(--t) {
AM_SHORT tt;
int
i;
for
(i = 0, tt = ~t & (t - 1); tt; tt >>= 1, ++i) {
;
}
d ^= gaps[i];
p = supralist + (pi = lattice[d]);
if
(pi) {
--(p->count);
}
switch
(p->touched) {
case
1:
++supralist[lattice[d] = p->next].count;
break
;
case
0:
ci = nextsupra;
nextsupra = supralist[ci].next;
p->touched = 1;
c = supralist + ci;
c->touched = 0;
c->next = p->next;
p->next = ci;
c->count = 1;
Newxz(c->data, p->data[0] + 3, AM_SHORT);
Copy(p->data + 2, c->data + 3, p->data[0], AM_SHORT);
c->data[2] = subcontextnumber;
c->data[0] = p->data[0] + 1;
lattice[d] = ci;
}
}
p = supralist;
p->touched = 0;
int
i;
do
{
if
(supralist[i = p->next].count == 0) {
Safefree(supralist[i].data);
p->next = supralist[i].next;
supralist[i].next = nextsupra;
nextsupra = (AM_SHORT) i;
}
else
{
p = supralist + p->next;
p->touched = 0;
}
}
while
(p->next);
nptr[sublattice_index] = nextsupra;
}
subcontext -= NUM_LATTICES;
*subcontext_class =
class
;
--subcontext_class;
--subcontextnumber;
}
HV *context_size = guts->context_size;
HV *pointers = guts->pointers;
{
AM_SUPRA * p0;
for
(iter_supras(p0, supra_list[0])) {
AM_SUPRA *p1;
for
(iter_supras(p1, supra_list[1])) {
AM_SHORT *k = intersect_supras(
sublist_top(p0),
sublist_top(p1),
ilist2top
);
if
(k == ilist2top) {
continue
;
}
*k = 0;
AM_SUPRA *p2;
for
(iter_supras(p2, supra_list[2])) {
k = intersect_supras(
ilist2top,
sublist_top(p2),
ilist3top
);
if
(k == ilist3top) {
continue
;
}
*k = 0;
AM_SUPRA *p3;
for
(iter_supras(p3, supra_list[3])) {
AM_SHORT length = intersect_supras_final(
ilist3top,
sublist_top(p3),
intersectlist,
subcontext_class
);
if
(length) {
AM_BIG_INT count = {0, 0, 0, 0, 0, 0, 0, 0};
count[0] = p0->count;
count[0] *= p1->count;
carry(count, 0);
count[0] *= p2->count;
count[1] *= p2->count;
carry(count, 0);
carry(count, 1);
count[0] *= p3->count;
count[1] *= p3->count;
count[2] *= p3->count;
carry(count, 0);
carry(count, 1);
carry(count, 2);
if
(!linear_flag){
AM_LONG pointercount = 0;
for
(
int
i = 0; i < length; ++i) {
pointercount += (AM_LONG) SvUV(*hv_fetch(context_size,
(
char
*) (subcontext + (NUM_LATTICES * intersectlist[i])), 8, 0));
}
if
(pointercount & 0xffff0000) {
AM_SHORT pchi = (AM_SHORT) (high_bits(pointercount));
AM_SHORT pclo = (AM_SHORT) (low_bits(pointercount));
AM_LONG hiprod[6];
hiprod[1] = pchi * count[0];
hiprod[2] = pchi * count[1];
hiprod[3] = pchi * count[2];
hiprod[4] = pchi * count[3];
count[0] *= pclo;
count[1] *= pclo;
count[2] *= pclo;
count[3] *= pclo;
carry(count, 0);
carry(count, 1);
carry(count, 2);
carry(count, 3);
count[1] += hiprod[1];
count[2] += hiprod[2];
count[3] += hiprod[3];
count[4] += hiprod[4];
carry(count, 1);
carry(count, 2);
carry(count, 3);
carry(count, 4);
}
else
{
count[0] *= pointercount;
count[1] *= pointercount;
count[2] *= pointercount;
count[3] *= pointercount;
carry(count, 0);
carry(count, 1);
carry(count, 2);
carry(count, 3);
}
}
for
(
int
i = 0; i < length; ++i) {
SV *final_pointers_sv = *hv_fetch(pointers,
(
char
*) (subcontext + (NUM_LATTICES * intersectlist[i])), 8, 1);
if
(!SvPOK(final_pointers_sv)) {
SvUPGRADE(final_pointers_sv, SVt_PVNV);
SvGROW(final_pointers_sv, 8 *
sizeof
(AM_LONG) + 1);
Zero(SvPVX(final_pointers_sv), 8, AM_LONG);
SvCUR_set(final_pointers_sv, 8 *
sizeof
(AM_LONG));
SvPOK_on(final_pointers_sv);
}
AM_LONG *final_pointers = (AM_LONG *) SvPVX(final_pointers_sv);
for
(
int
j = 0; j < 7; ++j) {
*(final_pointers + j) += count[j];
carry_pointer(final_pointers + j);
}
}
}
}
}
}
}
clear_supras(supra_list, 4);
HV *raw_gang = guts->raw_gang;
SV **classes = guts->classes;
SV **itemcontextchain = guts->itemcontextchain;
HV *itemcontextchainhead = guts->itemcontextchainhead;
SV **sum = guts->sum;
IV num_classes = guts->num_classes;
AM_BIG_INT grand_total = {0, 0, 0, 0, 0, 0, 0, 0};
hv_iterinit(pointers);
HE * pointers_entry;
while
((pointers_entry = hv_iternext(pointers))) {
AM_BIG_INT p;
Copy(SvPVX(HeVAL(pointers_entry)), p, 8, AM_LONG);
SV *num_examplars = *hv_fetch(context_size, HeKEY(pointers_entry), NUM_LATTICES *
sizeof
(AM_SHORT), 0);
AM_LONG count = (AM_LONG)SvUVX(num_examplars);
AM_SHORT counthi = (AM_SHORT)(high_bits(count));
AM_SHORT countlo = (AM_SHORT)(low_bits(count));
AM_BIG_INT gangcount;
gangcount[0] = 0;
for
(
int
i = 0; i < 7; ++i) {
gangcount[i] += countlo * p[i];
carry_replace(gangcount, i);
}
gangcount[7] += countlo * p[7];
if
(counthi) {
for
(
int
i = 0; i < 6; ++i) {
gangcount[i + 1] += counthi * p[i];
carry(gangcount, i + 1);
}
}
for
(
int
i = 0; i < 7; ++i) {
grand_total[i] += gangcount[i];
carry(grand_total, i);
}
grand_total[7] += gangcount[7];
normalize(aTHX_ HeVAL(pointers_entry));
SV* gang_pointers = *hv_fetch(raw_gang, HeKEY(pointers_entry), NUM_LATTICES *
sizeof
(AM_SHORT), 1);
SvUPGRADE(gang_pointers, SVt_PVNV);
sv_setpvn(gang_pointers, (
char
*) gangcount, 8 *
sizeof
(AM_LONG));
normalize(aTHX_ gang_pointers);
SV* this_class_sv = *hv_fetch(context_to_class, HeKEY(pointers_entry), NUM_LATTICES *
sizeof
(AM_SHORT), 0);
AM_SHORT this_class = (AM_SHORT) SvUVX(this_class_sv);
if
(this_class) {
SV_CHECK_THINKFIRST(sum[this_class]);
AM_LONG *s = (AM_LONG *) SvPVX(sum[this_class]);
for
(
int
i = 0; i < 7; ++i) {
*(s + i) += gangcount[i];
carry_pointer(s + i);
}
}
else
{
SV *exemplar = *hv_fetch(itemcontextchainhead, HeKEY(pointers_entry), NUM_LATTICES *
sizeof
(AM_SHORT), 0);
while
(SvIOK(exemplar)) {
IV datanum = SvIVX(exemplar);
IV ocnum = SvIVX(classes[datanum]);
SV_CHECK_THINKFIRST(sum[ocnum]);
AM_LONG *s = (AM_LONG *) SvPVX(sum[ocnum]);
for
(
int
i = 0; i < 7; ++i) {
*(s + i) += p[i];
carry_pointer(s + i);
exemplar = itemcontextchain[datanum];
}
}
}
}
for
(
int
i = 1; i <= num_classes; ++i) {
normalize(aTHX_ sum[i]);
}
SV *grand_total_entry = *hv_fetch(pointers,
"grand_total"
, 11, 1);
SvUPGRADE(grand_total_entry, SVt_PVNV);
sv_setpvn(grand_total_entry, (
char
*) grand_total, 8 *
sizeof
(AM_LONG));
normalize(aTHX_ grand_total_entry);
Safefree(subcontext);
Safefree(subcontext_class);
Safefree(intersectlist);
Safefree(intersectlist2);
Safefree(intersectlist3);
}