/**********************************************************************
 
	Copyright (C) 2005- Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/

#include	"memory_debug.h"
#include	"xl.h"
#include	"matrix.h"
#include	"recordlist64.h"
#include	"filespace64.h"
#include	"change_endian.h"
#include	"lock_level.h"

int mem_test_flag;

typedef struct mx_file_work {
	PDB64 * 		pdb_fd;
	INTEGER64		header_fofs;
	INTEGER64		subheader_fofs;
	PN64_MX_SUBHEADER	subheader;
	int			favt64_err;
	CALL_LOCK_DESCRIPTER	lock_d;
	int			lock_cnt;
	int			lock_tid;
	char *			lock_file;
	int			lock_line;
	int			oflags;
} MX_FILE_WORK;

typedef struct acc64_mx_subheader {
	INTEGER64		fofs;
	PN64_MX_SUBHEADER	h;
} ACC64_MX_SUBHEADER;

void mx_flush_subheader(MATRIX *);
int
load_raw_matrix_node(
	PN64_MX_NODE *	ret,
	PN64_MX_NODE **	org,
	INTEGER64 * 	dim_code,
	char *		bit_field,
	MATRIX * m,
	INTEGER64 fofs);
int
save_raw_matrix_node(
	MATRIX * m,
	PN64_MX_NODE *	abbr,
	PN64_MX_NODE *	org,
	INTEGER64	fofs);
void mx_dim_code_endian(MX_DIM_CODE * ix);
int delete_mx_cache_ring(PN64_MX_CACHE_RING *,MATRIX *m,INTEGER64 cr_fofs);
int insert_mx_cache_ring(MATRIX * m,INTEGER64 cr_fofs);
int mx_dim_code_cmp(MX_DIM_CODE * dc1,MX_DIM_CODE * dc2,MATRIX*);
int save_subheader(MATRIX * m,int);
int load_subheader(MATRIX * m,int);
void mx_file_lock_event(int seq,MATRIX * m);
void _finish_file_access(MATRIX * m,int);
int _xx_start_file_access(MATRIX * m,int f,char *,int);
int XX_start_file_access(MATRIX * m,char *,int);
#define start_file_access(m)	\
	XX_start_file_access(m,__FILE__,__LINE__)
void finish_file_access(MATRIX * m,int);
void finish_file_access_with_wait(MATRIX * m,int wait_key);
void _close_matrix_file(MATRIX * m);
void setup_mx_cache_ring(MATRIX * m,INTEGER64 ring,INTEGER64 node);
void mx_cache_purge(MATRIX * m,INTEGER64 cr_fofs);
void mx_file_gc(MATRIX*);
void mx_file_gc_tick();

MX_DIM_CODE_LIST *
_mx_search_level_node(MATRIX * m,INTEGER64 * last_dc,int limit);
int
_mx_file_sync_create_node(MATRIX * m);

SEM mx_file_lock;
int mx_cache_count_max = 10000;
int mx_open_files_cnt;
extern MATRIX * matrices_root;
int call_mx_file_gc;
void test_matrix(MATRIX * m);


void
test_matrix(MATRIX * m)
{
int i;
	for ( i = 0 ; i < m->p.channel_nos ; i ++ )
		if ( m->channel_info[i].data_type )
				return ;
	ss_printf(">>> %i\n",get_tid());
	sleep_sec(1);
	er_panic("test_matrix");
}

void
init_mx_file()
{
	mx_file_lock = new_lock(LL_MX_FILE);
	new_tick(mx_file_gc_tick,MLOCK_LIFE_TIME/2,0);
}

void
mx_file_gc(MATRIX * m)
{
MATRIX * m1;
MX_FILE_WORK * w;
/*
ss_printf("MX_GC %i\n",mx_open_files_cnt);
*/

	lock_task(matrix_lock);
	if ( call_mx_file_gc ) {
		unlock_task(matrix_lock,"mx_file_gc");
		return;
	}
	call_mx_file_gc = 1;
	unlock_task(matrix_lock,"mx_file_gc");
retry:
	lock_task(matrix_lock);
	for ( m1 = matrices_root->mx_prev ; m1 != matrices_root ; m1 = m1->mx_prev ) {
		if ( m1 == m )
			continue;
		w = m1->file_work;
		if ( w == 0 )
			continue;
		if ( w->pdb_fd == 0 )
			continue;
		if ( mx_open_files_cnt < MAX_OPEN_FILES_FOR_MX ) {
			if ( get_xltime() - m1->last_access < MLOCK_LIFE_TIME )
				continue;
		}
		m1->access_lock ++;
		unlock_task(matrix_lock,"mx_file_gc");
		mx_file_lock_event(0,m1);
		lock_task(matrix_lock);
		m1->access_lock --;
		if ( m1->access_lock == 0 )
			wakeup_task((int)m1);
		unlock_task(matrix_lock,"mx_file_gc");
		goto retry;
	}
	call_mx_file_gc = 0;
	unlock_task(matrix_lock,"mx_file_gc");
}

void
mx_file_gc_tick()
{
	mx_file_gc(0);
}



int
_xx_start_file_access(MATRIX * m,int f,char * __f,int __l)
{
MX_FILE_WORK * w;
L_CHAR * encoding;
int tid;

	tid = get_tid();
	w = m->file_work;
	if ( w == 0 )
		return ME_CANNOT_OPEN_FILE;
	if ( w->lock_tid != tid ) {
		for ( ; w->lock_tid ; ) {
			sleep_task((int)w,mx_file_lock);
			lock_task(mx_file_lock);
		}
		w->lock_tid = tid;
		w->lock_cnt = 1;
		w->lock_file = __f;
		w->lock_line = __l;
	}
	else {
		w->lock_cnt ++;
	}

	if ( w->pdb_fd )
		return 0;

	if ( f && cl_error_check(w->lock_d) < 0  )
		w->lock_d = call_lock(m->filename,CLT_WRITE_LOCK,mx_file_lock_event,m);


ss_printf("_start_file_access %ls\n",m->filename);

	encoding = l_string(std_cm,"utf8");
	w->pdb_fd = open_filespace64(
		n_string(std_cm,m->filename),
		w->oflags,0644,
		PF_USEFREELIST,FT_MATRIX,encoding);
	if ( w->pdb_fd == 0 ) {
		call_unlock(w->lock_d);
		_finish_file_access(m,0);
ss_printf("A\n");
		return ME_CANNOT_OPEN_FILE;
	}
check_pdb64(w->pdb_fd);

	mx_open_files_cnt ++;
	if ( w->subheader_fofs )
		load_subheader(m,1);
	if ( w->subheader.flags & MXF_WRITE ) {
		close_filespace64(w->pdb_fd);
		w->pdb_fd = 0;
		mx_open_files_cnt --;
		call_unlock(w->lock_d);
		_finish_file_access(m,0);
check_pdb64(w->pdb_fd);
ss_printf("************ DESTROY-4\n");
		return ME_DESTROY_FILE;
	}
check_pdb64(w->pdb_fd);
	w->subheader.flags |= MXF_WRITE;
	if ( w->subheader_fofs )
		save_subheader(m,1);
check_pdb64(w->pdb_fd);
ss_printf("C\n");
	return 0;
}

int
XX_start_file_access(MATRIX * m,char * __f,int __l)
{
int ret;
	lock_task(mx_file_lock);
	ret = _xx_start_file_access(m,1,__f,__l);
	unlock_task(mx_file_lock,"");
	mx_file_gc(m);
	return ret;
}

void
_finish_file_access(MATRIX * m,int free_flag)
{
MX_FILE_WORK * w;

	w = m->file_work;
	w->lock_cnt --;
	if ( w->lock_cnt == 0 ) {
		w->lock_tid = 0;
		wakeup_task((int)w);
	}
	if ( free_flag ) {
		d_f_ree(w);
		m->file_work = 0;
	}
}
void
finish_file_access(MATRIX * m,int free_flag)
{
	lock_task(mx_file_lock);
	_finish_file_access(m,free_flag);
	unlock_task(mx_file_lock,"");
}


void
finish_file_access_with_wait(MATRIX * m,int wait_key)
{
MX_FILE_WORK * w;
	lock_task(mx_file_lock);
	_finish_file_access(m,0);
	w = m->file_work;
	if ( w->lock_cnt )
		er_panic("finish_file_access_with_wait");
	sleep_task((int)wait_key,mx_file_lock);
}



void mx_file_lock_event(int seq,MATRIX * m)
{
MX_FILE_WORK * w;
	lock_task(mx_file_lock);
	_xx_start_file_access(m,0,__FILE__,__LINE__);
	unlock_task(mx_file_lock,"mx_file_lock_event");

	w = m->file_work;
	if ( w == 0 )
		goto end;
	w->subheader.flags &= ~MXF_WRITE;

	if ( w->subheader_fofs )
		save_subheader(m,0);
	close_filespace64(w->pdb_fd);
	w->pdb_fd = 0;
	mx_open_files_cnt --;
	call_unlock(w->lock_d);

end:
	finish_file_access(m,0);
}


void mx_dim_code_endian(MX_DIM_CODE * ix)
{
	change_endian(ix->fofs);
}

unsigned char *
get_compressed_code64(unsigned char * ptr,INTEGER64 in)
{
int shift;
	if ( in >= 0 ) {
		if ( (in & CONST_INT64(0xffffffffffffc000)) == 0 ) {
			shift = 8;
		}
		else if ( (in & CONST_INT64(0xffffffffe0000000)) == 0 ) {
			in |= CONST_INT64(0x80000000);
			shift = 24;
		}
		else if ( (in & CONST_INT64(0xfffff00000000000)) == 0 ) {
			in |= CONST_INT64(0xc00000000000);
			shift = 40;
		}
		else if ( (in & CONST_INT64(0xf800000000000000)) == 0 ) {
			in |= CONST_INT64(0xe000000000000000);
			shift = 56;
		}
		else {
			*ptr ++ = 0xf0;
			shift = 56;
		}
	}
	else {
		if ( (in & CONST_INT64(0xffffffffffffc000)) == 
				CONST_INT64(0xffffffffffffc000) ) {
			shift = 8;
			in &= 0x7fff;
		}
		else if ( (in & CONST_INT64(0xffffffffe0000000)) == 
				CONST_INT64(0xffffffffe0000000) ) {
			in &= CONST_INT64(0x3fffffff);
			in |= CONST_INT64(0x80000000);
			shift = 24;
		}
		else if ( (in & CONST_INT64(0xfffff00000000000)) == 
				CONST_INT64(0xfffff00000000000) ) {
			in &= CONST_INT64(0x1fffffffffff);
			in |= CONST_INT64(0xc00000000000);
			shift = 40;
		}
		else if ( (in & CONST_INT64(0xf800000000000000)) == 
				CONST_INT64(0xf800000000000000) ) {
			in &= CONST_INT64(0x0fffffffffffffff);
			in |= CONST_INT64(0xe000000000000000);
			shift = 56;
		}
		else {
			*ptr ++ = 0xf0;
			shift = 56;
		}
	}

	for ( ; shift >= 0 ; shift -= 8 )
		*ptr ++ = (in >> shift) & 0xff;
	return ptr;
}

unsigned char *
get_uncompressed_code64(INTEGER64 * retp,unsigned char * ptr)
{
unsigned char ch;
INTEGER64 mask;
INTEGER64 sign;
int len;
INTEGER64 ret;
	ch = *ptr;
	if ( ch == 0xf0 ) {
		len = 8;
		mask = CONST_INT64(0xffffffffffffffff);
		sign = 0;
		ptr ++;
	}
	else if ( (ch & 0xf8) == 0xe0 ) {
		len = 8;
		mask = CONST_INT64(0x0fffffffffffffff);
		sign = 0;
	}
	else if ( (ch & 0xf8) == 0xe8 ) {
		len = 8;
		mask = CONST_INT64(0x0fffffffffffffff);
		sign = CONST_INT64(0xf000000000000000);
	}
	else if ( (ch & 0xf0) == 0xc0 ) {
		len = 6;
		mask = CONST_INT64(0x00001fffffffffff);
		sign = 0;
	}
	else if ( (ch & 0xf0) == 0xd0 ) {
		len = 6;
		mask = CONST_INT64(0x00001fffffffffff);
		sign = CONST_INT64(0xffff700000000000);
	}
	else if ( (ch & 0xe0) == 0x80 ) {
		len = 4;
		mask = CONST_INT64(0x000000003fffffff);
		sign = 0;
	}
	else if ( (ch & 0xe0) == 0xa0 ) {
		len = 4;
		mask = CONST_INT64(0x000000003fffffff);
		sign = CONST_INT64(0xffffffffc0000000);
	}
	else if ( (ch & 0xc0) == 0 ) {
		len = 2;
		mask = CONST_INT64(0x0000000000007fff);
		sign = 0;
	}
	else {
		len = 2;
		mask = CONST_INT64(0x0000000000007fff);
		sign = CONST_INT64(0xffffffffffff8000);
	}
	ret = 0;
	for ( ; len ; len -- ) {
		ret = ret << 8;
		ret |= *ptr;
		ptr ++;
	}
	ret &= mask;
	ret |= sign;

	*retp = ret;
	return ptr;
}


unsigned char*
get_compressed_dim_code(unsigned char * ptr,INTEGER64 * dim_code,int dim)
{
int i;
	for ( i = 0 ; i <= dim ; i ++ )
		ptr = get_compressed_code64(ptr,dim_code[i]);
	*ptr ++ = 0xf1;
	return ptr;
}



unsigned char*
get_uncompressed_dim_code(
	INTEGER64 * dim_code,
	unsigned char * ptr)
{
int i;
INTEGER64 dum;
	if ( dim_code ) {
		for ( i = 0 ; *ptr != 0xf1 ; i ++ )
			ptr = get_uncompressed_code64(&dim_code[i],ptr);
	}
	else {
		for ( i = 0 ; *ptr != 0xf1 ; i ++ )
			ptr = get_uncompressed_code64(&dum,ptr);
	}
	ptr ++;
	return ptr;
}

int
get_dim_code_dimension(
	unsigned char * ptr)
{
int i;
INTEGER64 dum;
	for ( i = 0 ; *ptr != 0xf1 ; i ++ )
		ptr = get_uncompressed_code64(&dum,ptr);
	return i-1;
}

int save_subheader(MATRIX * m,int sfa)
{
PN64_MX_SUBHEADER sh;
int i,j;
MX_FILE_WORK * w;

	if ( sfa == 0 )
		if ( start_file_access(m) < 0 )
			return -1;

	w = m->file_work;
	sh = w->subheader;
	change_endian_header64(&sh.h);
	for ( i = 0 ; i < MI_MODE_MAX ; i ++ )
		for ( j = 0 ; j < MI_MAX ; j ++ ) {
			change_endian(sh.mode_pri_area[i][j]);
			change_endian(sh.mode_cal_fofs[i][j]);
		}
	change_endian(sh.node_root);
	change_endian(sh.cache_next);
	change_endian(sh.cache_prev);
	change_endian(sh.cache_count);
	change_endian(sh.flags);

	write_filespace64f(w->pdb_fd,w->subheader_fofs,&sh);

	if ( sfa == 0 )
		finish_file_access(m,0);
	return 0;
}

int
load_subheader(MATRIX * m,int sfa)
{
PN64_MX_SUBHEADER * sh;
int i,j;
MX_FILE_WORK * w;

	if ( sfa == 0 )
		if ( start_file_access(m) < 0 )
			return -1;

	w = m->file_work;
	sh = read_filespace64f(w->pdb_fd,w->subheader_fofs);
	if ( sh == 0 ) {
		finish_file_access(m,0);
		return -1;
	}
	change_endian_header64(&sh->h);
	for ( i = 0 ; i < MI_MODE_MAX ; i ++ )
		for ( j = 0 ; j < MI_MAX ; j ++ ) {
			change_endian(sh->mode_pri_area[i][j]);
			change_endian(sh->mode_cal_fofs[i][j]);
		}
	change_endian(sh->node_root);
	change_endian(sh->cache_next);
	change_endian(sh->cache_prev);
	change_endian(sh->cache_count);
	change_endian(sh->flags);

	w->subheader = *sh;
/*
	w->node_root = get_root64(&w->favt64_err,
		w->pdb_fd,
		w->subheader.node_root,
		mx_dim_code_endian);
*/
	d_f_ree(sh);

	if ( sfa == 0 )
		finish_file_access(m,0);

	return 0;
}


int
save_matrix_header(MATRIX * m)
{
RECORD_LIST64 * rl;
PN64_MX_HEADER * h;
int i;
_PN64_MX_CHANNEL_INFO * ch;
char * pixel_size, * ptr;
MATRIX_CHANNEL_INFO * ci;
PN64_HEADER sh;
FAVT64_ROOT * rt;
MX_FILE_WORK * w;
MATRIX_DATA_TYPE * tp;

	if ( m->file_work == 0 ) {
		w = d_alloc(sizeof(*w));
		memset(w,0,sizeof(*w));
		m->file_work = w;
	}
	else	w = m->file_work;

	w->oflags = O_CREAT|O_RDWR|O_TRUNC;
	if ( start_file_access(m) < 0 )
		return -1;
	w->oflags = O_RDWR;

	rl = new_recordlist64(PNT_MX_HEADER,sizeof(PN64_MX_HEADER));
	h = rl->data;
	h->channel_nos = m->p.channel_nos;
	h->dim = m->p.dim;
	h->flags = m->p.flags;
	h->total_levels = m->p.total_levels;
	h->modify_time = m->p.modify_time;

	set_recordlist_chain64(rl,
		m->dim_divide,m->p.dim,0);
	set_recordlist_chain64(rl,
		m->block_size,m->p.dim,0);

	pixel_size = d_alloc((sizeof(INTEGER64)+1)*m->p.dim);
	ptr = pixel_size;
	for ( i = 0 ; i < m->p.dim ; i ++ )
		ptr = get_compressed_code64(ptr,m->pixel_size[i]);
	set_recordlist_chain64(rl,
		pixel_size,ptr - pixel_size,1);

	ch = d_alloc(sizeof(*ch)*m->p.channel_nos);
	for ( i = 0 ; i < m->p.channel_nos ; i ++ ) {
		if ( m->channel_info[i].data_type == 0 ) {
			memset(&ch[i],0,sizeof(ch[i]));
		}
		else {
			ch[i].data_type = m->channel_info[i].data_type->type;
			ch[i].flags = m->channel_info[i].flags;
			ch[i].default_data_fofs = 0;
		}
	}

	set_recordlist_chain64(rl,
		ch,sizeof(*ch)*m->p.channel_nos,1);

	setup_recordlist64_size(rl);
	h = rl->data;

	w->header_fofs = alloc_filespace64(w->pdb_fd,rl->data);

	for ( i = 0 ; i < m->p.channel_nos ; i ++ ) {
		ci = &m->channel_info[i];
		if ( ci->default_data == 0 ) {
			change_endian(ch[i].data_type);
			change_endian(ch[i].flags);
			continue;
		}
		if ( ci->data_type->parent )
			tp = ci->data_type->parent;
		else	tp = ci->data_type;

		ci->default_data_fofs = 
			ch[i].default_data_fofs =
			save_matrix_data_1(m,0,
				ci->default_data,
				tp);

		change_endian(ch[i].data_type);
		change_endian(ch[i].flags);
		change_endian(ch[i].default_data_fofs);
	}

	w->subheader.h.type = PNT_MX_SUBHEADER;
	w->subheader.h.size = sizeof(w->subheader);
	sh = w->subheader.h;
//	change_endian_header64(&sh);
	h->subheader_fofs = w->subheader_fofs = alloc_filespace64(w->pdb_fd,&sh);
	w->subheader.cache_next =
		w->subheader.cache_prev = 
		w->subheader_fofs;
	rt = favt64_alloc_root(w->pdb_fd,FAT_MX_NODE,mx_dim_code_endian);
	w->subheader.node_root = rt->h.fofs;

	save_subheader(m,0);

	setup_recordlist64(rl);
	h = rl->data;

printf("mx_file %llx %p %p\n",h->h.size,h,rl->data);
	change_endian_header64(&h->h);
printf("mx_file %llx %p %p\n",h->h.size,h,rl->data);
	change_endian(h->channel_nos);
	change_endian(h->dim);
	change_endian(h->flags);
	change_endian(h->total_levels);
	change_endian(h->modify_time);
	change_endian(h->subheader_fofs);

	write_filespace64f(w->pdb_fd,w->header_fofs,rl->data);

	free_recordlist64(rl);

	finish_file_access(m,0);
	return 0;
}

int
load_matrix_header(MATRIX * m,int oflags,int mode)
{
MX_FILE_WORK * w;
PN64_MX_HEADER * h;
MATRIX_PARAM p;
char * ptr;
int i;
char d;
INTEGER64 d64;
_PN64_MX_CHANNEL_INFO ch;
_PN64_MX_CHANNEL_INFO * p_ch;
MATRIX_CHANNEL_INFO info;
MATRIX_DATA_TYPE * tp;
int err;

	if ( m->filename == 0 )
		return -1;

	w = d_alloc(sizeof(*w));
	memset(w,0,sizeof(*w));
	m->file_work = w;
	w->oflags = oflags;

	err = start_file_access(m);
	if ( err ) {
ss_printf("load_matrix_header-1 %i\n",err);
		d_f_ree(w);
		m->file_work = 0;
		return err;
	}

	h = get_file_record64(&w->header_fofs,
		w->pdb_fd,PNT_MX_HEADER,0);
	if ( h == 0 ) {
		close_filespace64(w->pdb_fd);
		mx_open_files_cnt --;
		w->pdb_fd = 0;
		call_unlock(w->lock_d);

		finish_file_access(m,1);
ss_printf("************ DESTROY-1\n");
		return ME_DESTROY_FILE;
	}

	change_endian_header64(&h->h);
	change_endian(h->channel_nos);
	change_endian(h->dim);
	change_endian(h->flags);
	change_endian(h->total_levels);
	change_endian(h->modify_time);
	change_endian(h->subheader_fofs);

	w->subheader_fofs = h->subheader_fofs;

	if ( load_subheader(m,0) < 0 ) {
		d_f_ree(h);
		finish_file_access(m,1);
ss_printf("************ DESTROY-2\n");
		return ME_DESTROY_FILE;
	}
	if ( w->subheader.flags & MXF_WRITE ) {
		d_f_ree(h);
		finish_file_access(m,1);
ss_printf("************ DESTROY-3 %x\n",w->subheader.flags);
		return ME_DESTROY_FILE;
	}

	memset(&p,0,sizeof(p));
	p.channel_nos = h->channel_nos;
	p.dim = h->dim;
	p.flags = h->flags;
	p.total_levels = h->total_levels;
	p.modify_time = h->modify_time;

	set_matrix_param(m,&p);

	ptr = (char*)(h+1);

	for ( i = 0 ; i < p.dim ; i ++ ) {
		d = *ptr++;
		set_matrix_dim_divide(m,i,d);
	}

	for ( i = 0 ; i < p.dim ; i ++ ) {
		d = *ptr++;
		set_matrix_block_size(m,i,d);
	}

	for ( i = 0 ; i < p.dim ; i ++ ) {
		ptr = get_uncompressed_code64(&d64,ptr);
		set_matrix_pixel_size(m,i,d64);
	}


	p_ch = (_PN64_MX_CHANNEL_INFO*)ptr;
	for ( i = 0 ; i < p.channel_nos ; i ++ ) {
		memcpy(&ch,p_ch++,sizeof(ch));
		change_endian(ch.data_type);
		change_endian(ch.flags);
		change_endian(ch.default_data_fofs);

		info.data_type = get_matrix_data_type(ch.data_type);
		if ( info.data_type == 0 )
{
if ( i == 1 )
er_panic("load");
			continue;
}
		info.flags = ch.flags;
		if ( ch.default_data_fofs ) {
			if ( info.data_type->parent )
				tp = info.data_type->parent;
			else	tp = info.data_type;
			info.default_data
				= load_matrix_data
					(0,0,
					m,
					ch.default_data_fofs,
					tp);
			if ( info.default_data == 0 ) {
				free_filespace64(w->pdb_fd,
					ch.default_data_fofs);
				info.default_data_fofs = 0;
			}
		}
		else	info.default_data = 0;
		info.default_data_fofs = ch.default_data_fofs;
		set_matrix_channel_info(m,i,&info);
	}


	w->subheader.flags |= MXF_WRITE;
	save_subheader(m,0);

	d_f_ree(h);

	finish_file_access(m,0);
	return 0;
}


INTEGER64
save_matrix_data_1(
	MATRIX * m,
	INTEGER64 fofs,
	void * target,
	MATRIX_DATA_TYPE * tp)
{
RECORD_LIST64 * rl;
PN64_HEADER * h, * h2;
INTEGER64 size;
MX_FILE_WORK * w;

	if ( start_file_access(m) < 0 )
		return -1;

	w = m->file_work;

	rl = new_recordlist64(
		PNT_MX_DATA_1,
		sizeof(PN64_MX_DATA_1));

	(*tp->convert_to_net)(tp,rl,target);

	setup_recordlist64(rl);
	if ( rl->header.size < sizeof(PN64_HEADER)+sizeof(INTEGER64) ) {
		set_recordlist_chain64
			(rl,&size,sizeof(PN64_HEADER)+sizeof(INTEGER64)
					- rl->header.size,0);
	}
	setup_recordlist64(rl);

	if( fofs == 0 ) {
	alloc:
		fofs = alloc_filespace64(w->pdb_fd,rl->data);
	}
	else {
		h = read_filespace64f_header(w->pdb_fd,fofs);
		if ( h == 0 ) {
			goto alloc;
		}
		else {
			change_endian_header64(h);
			h2 = (PN64_HEADER*)rl->data;
			size = h2->size;
			if ( h->size != size ) {
				free_filespace64(w->pdb_fd,fofs);
				goto alloc;
			}
		}
	}
	change_endian_header64(rl->data);
	write_filespace64f(w->pdb_fd,fofs,rl->data);

	free_recordlist64(rl);
	
	finish_file_access(m,0);
	return fofs;
}

INTEGER64
save_matrix_data_2(
	MATRIX * m,
	INTEGER64 fofs,
	void * target,
	INTEGER64 next_fofs,
	int id,
	MATRIX_DATA_TYPE * tp)
{
RECORD_LIST64 * rl;
PN64_HEADER * h, * h2;
INTEGER64 size;
MX_FILE_WORK * w;
PN64_MX_DATA_2 * mx;

	if ( start_file_access(m) < 0 )
		return -1;


	w = m->file_work;
check_freelist64(w->pdb_fd);

	rl = new_recordlist64(
		PNT_MX_DATA_2,
		sizeof(PN64_MX_DATA_2));
	mx = (PN64_MX_DATA_2*)rl->data;
	mx->next_fofs = next_fofs;
	mx->id = id;

	change_endian(mx->next_fofs);
	change_endian(mx->id);

	(tp->convert_to_net)(tp,rl,target);

	setup_recordlist64(rl);
	mx = (PN64_MX_DATA_2*)rl->data;


	if( fofs == 0 ) {
	alloc:
		fofs = alloc_filespace64(w->pdb_fd,rl->data);
	}
	else {
		h = read_filespace64f_header(w->pdb_fd,fofs);
		if ( h == 0 ) {
			goto alloc;
		}
		else {
			change_endian_header64(h);
			h2 = (PN64_HEADER*)rl->data;
			size = h2->size;
			if ( h->size != size ) {
				free_filespace64(w->pdb_fd,fofs);
				goto alloc;
			}
		}
	}
	change_endian_header64(&mx->h);
	write_filespace64f(w->pdb_fd,fofs,rl->data);

	free_recordlist64(rl);
	
	finish_file_access(m,0);
	return fofs;
}

void *
load_matrix_data(
	INTEGER64 * next_fofs,
	int *	id,
	MATRIX * m,
	INTEGER64 fofs,
	MATRIX_DATA_TYPE * tp)
{
PN64_HEADER * _r;
MX_FILE_WORK * w;
PN64_MX_DATA_1 * d1;
PN64_MX_DATA_2 * d2;
void * ret;

	if ( start_file_access(m) < 0 )
		return 0;

	w = m->file_work;
	_r = read_filespace64f(w->pdb_fd,fofs);
	if ( _r == 0 )
		return 0;

	change_endian(_r->type);
	switch ( _r->type ) {
	case PNT_MX_DATA_1:
		d1 = (PN64_MX_DATA_1*)_r;
		ret = (*tp->convert_to_host)(tp,d1+1,MD_DALLOC,0);
		break;
	case PNT_MX_DATA_2:
		d2 = (PN64_MX_DATA_2*)_r;
		change_endian(d2->next_fofs);
		change_endian(d2->id);
		if ( d2->id < 0 && m->p.channel_nos <= d2->id )
			goto err;

		tp = get_matrix_data_type
			(m->channel_info[d2->id].data_type->type);
		if ( tp == 0 )
			goto err;
		ret = (*tp->convert_to_host)(tp,d2+1,MD_DALLOC,0);
		if ( next_fofs )
			*next_fofs = d2->next_fofs;
		if ( id )
			*id = d2->id;
		break;
	default:
		goto err;
	}

	d_f_ree(_r);
	finish_file_access(m,0);
	return ret;
err:

	d_f_ree(_r);
	finish_file_access(m,0);
	return 0;
}

int
load_raw_matrix_node(
	PN64_MX_NODE *	ret,
	PN64_MX_NODE **	org,
	INTEGER64 * 	dim_code,
	char *		bit_field,
	MATRIX * m,
	INTEGER64 fofs)
{
PN64_MX_NODE * n;
MX_FILE_WORK * w;
char * ptr;

	if ( start_file_access(m) < 0 )
		return -1;

	w = m->file_work;
	n = read_filespace64f(w->pdb_fd,fofs);
	if ( n == 0 ) {
		finish_file_access(m,0);
		return -1;
	}
	change_endian_header64(&n->h);
	if ( n->h.type != PNT_MX_NODE ) {
		finish_file_access(m,0);
		return -2;
	}
	*ret = *n;
	if ( m->p.flags & MPF_CACHE_FILE ) {
		if ( m->dim_bit_field == 0 ) {
			*ret = *n;
			ptr = &n->d.p3.end_pos;
		}
		else {
			ret->d.p3.c = n->d.p1.c;
			ptr = &n->d.p1.end_pos;
		}
	}
	else {
		if ( m->dim_bit_field == 0 ) {
			ret->d.p3.f = n->d.p2.f;
			ptr = &n->d.p2.end_pos;
		}
		else {
			ptr = &n->d.p0.end_pos;
		}
	}
	if ( org )
		*org = n;
	else	d_f_ree(n);
	ptr = get_uncompressed_dim_code(dim_code,ptr);
	if ( m->dim_bit_field && bit_field )
		memcpy(bit_field,ptr,m->dim_bit_field);

	change_endian(ret->channel_fofs);
	change_endian(ret->d.p3.f.nlist_dim_addr);
	change_endian(ret->d.p3.c.ring_fofs);

	finish_file_access(m,0);

	return 0;
}

int
save_raw_matrix_node(
	MATRIX * m,
	PN64_MX_NODE *	abbr,
	PN64_MX_NODE *	org,
	INTEGER64	fofs)
{
MX_FILE_WORK * w;


	if ( start_file_access(m) < 0 )
		return -1;

	change_endian(abbr->channel_fofs);
	change_endian(abbr->d.p3.f.nlist_dim_addr);
	change_endian(abbr->d.p3.c.ring_fofs);

	if ( m->p.flags & MPF_CACHE_FILE ) {
		if ( m->dim_bit_field == 0 ) {
			org->d.p3.f = abbr->d.p3.f;
			org->d.p3.c = abbr->d.p3.c;
		}
		else {
			org->d.p1.c = abbr->d.p3.c;
		}
	}
	else {
		if ( m->dim_bit_field == 0 ) {
			org->d.p2.f = abbr->d.p3.f;
		}
	}

	w = m->file_work;
	write_filespace64f(w->pdb_fd,fofs,org);

	finish_file_access(m,0);
	return 0;
}


int
delete_mx_cache_ring(PN64_MX_CACHE_RING * dr,MATRIX *m,INTEGER64 cr_fofs)
{
PN64_MX_CACHE_RING * cr_buf, * cr;
MX_FILE_WORK * w;

	if ( start_file_access(m) < 0 )
		return -1;
	w = m->file_work;
	cr = read_filespace64f(
			w->pdb_fd,cr_fofs);
	change_endian_header64(&cr->h);
	if ( cr->h.type != PNT_MX_CACHE_RING )
		er_panic("delete_mx_cache_ring");
	change_endian(cr->next);
	change_endian(cr->prev);

	if ( dr ) {
		*dr = *cr;
		change_endian(dr->node);
	}

	if ( cr->prev == w->subheader_fofs ) {
		w->subheader.cache_next = cr->next;
	}
	else {
		cr_buf = read_filespace64f(
				w->pdb_fd,cr->prev);
		cr_buf->prev = cr->prev;
		change_endian(cr_buf->prev);
		write_filespace64f(
			w->pdb_fd,cr->prev,cr_buf);
		d_f_ree(cr_buf);
	}
	if ( cr->next == w->subheader_fofs ) {
		w->subheader.cache_prev = cr->prev;
	}
	else {
		cr_buf = read_filespace64f(
				w->pdb_fd,cr->next);
		cr_buf->next = cr->next;
		change_endian(cr_buf->next);
		d_f_ree(cr_buf);
	}
	w->subheader.cache_count --;
	save_subheader(m,0);
	finish_file_access(m,0);
	d_f_ree(cr);
	return 0;
}

void
setup_mx_cache_ring(MATRIX * m,INTEGER64 ring,INTEGER64 node)
{
PN64_MX_CACHE_RING * cr;
MX_FILE_WORK * w;
	w = m->file_work;
	cr = read_filespace64f(w->pdb_fd,ring);
	change_endian_header64(&cr->h);
	if ( cr->h.type != PNT_MX_CACHE_RING )
		er_panic("delete_mx_cache_ring");
	change_endian_header64(&cr->h);
	cr->node = node;
	change_endian(cr->node);
	write_filespace64f(w->pdb_fd,ring,cr);
	d_f_ree(cr);
}

void
mx_cache_purge(MATRIX * m,INTEGER64 cr_fofs)
{
MX_FILE_WORK * w;
PN64_MX_CACHE_RING dr;
PN64_MX_NODE mx_n;
INTEGER64 next;
INTEGER64 ch;
PN64_MX_DATA_2 * d2;
int er;
MX_DIM_CODE * dim_code;
FAVT64_NODE * n;
FAVT64_ROOT * rt;
	w = m->file_work;
	delete_mx_cache_ring(&dr,m,cr_fofs);

	dim_code = d_alloc(sizeof(MX_DIM_CODE)+sizeof(INTEGER64)*(m->p.dim+1));
	load_raw_matrix_node(&mx_n,0,(INTEGER64*)(dim_code+1),0,m,dr.node);
	free_filespace64(w->pdb_fd,dr.node);
	free_filespace64(w->pdb_fd,cr_fofs);
	for ( ch = mx_n.channel_fofs ; ch ; ) {
		d2 = read_filespace64f(w->pdb_fd,ch);
		if ( d2 == 0 )
			break;
		change_endian(d2->next_fofs);
		next = d2->next_fofs;
		d_f_ree(d2);
		free_filespace64(w->pdb_fd,ch);
		ch = next;
	}
	if ( mx_n.d.p3.f.nlist_dim_addr )
		free_filespace64(w->pdb_fd,mx_n.d.p3.f.nlist_dim_addr);
	rt = get_root64(&w->favt64_err,
		w->pdb_fd,
		w->subheader.node_root,
		mx_dim_code_endian);
	n = favt64_delete(&er,rt,&rt->node,dim_code,mx_dim_code_cmp,m);
	if ( n == 0 )
		er_panic("mx_cache_purge");
	favt64_free_node(n);
}


int
insert_mx_cache_ring(MATRIX * m,INTEGER64 cr_fofs)
{
MX_FILE_WORK * w;
PN64_MX_CACHE_RING * cr,* cr_buf;


	if ( start_file_access(m) < 0 )
		return -1;

	w = m->file_work;
	cr = read_filespace64f(w->pdb_fd,cr_fofs);
	change_endian_header64(&cr->h);
	if ( cr->h.type != PNT_MX_CACHE_RING )
		er_panic("delete_mx_cache_ring");

	if ( w->subheader.cache_next == w->subheader_fofs ) {
		w->subheader.cache_next = 
			w->subheader.cache_prev
			= cr_fofs;
		cr->next = cr->prev = w->subheader_fofs;
	}
	else {
		cr->prev = w->subheader_fofs;
		cr->next = w->subheader.cache_next;

		w->subheader.cache_next = cr_fofs;

		cr_buf = read_filespace64f(
				w->pdb_fd,cr->next);
		cr_buf->prev = cr_fofs;
		change_endian(cr_buf->prev);
		write_filespace64f(w->pdb_fd,cr->next,cr_buf);
		d_f_ree(cr_buf);
	}
	w->subheader.cache_count ++;
	save_subheader(m,0);

	change_endian_header64(&cr->h);
	change_endian(cr->next);
	change_endian(cr->prev);
	write_filespace64f(w->pdb_fd,cr_fofs,cr);
	d_f_ree(cr);

	for ( ; w->subheader.cache_count > mx_cache_count_max ; )
		mx_cache_purge(m,w->subheader.cache_prev);

	finish_file_access(m,0);
	return 0;
}

int
mx_dim_code_cmp(MX_DIM_CODE * dc1,MX_DIM_CODE * dc2,MATRIX * m)
{
INTEGER64 * _dc1, * _dc2;	
int dim;
int ret;
int i;
	dim = get_dim_code_dimension((char*)(dc1+1));
	if ( dim <= 0 )
		return 0;

	_dc1 = d_alloc(sizeof(INTEGER64)*(dim+1));
	_dc2 = d_alloc(sizeof(INTEGER64)*(dim+1));
	get_uncompressed_dim_code(_dc1,(char*)(dc1+1));
	get_uncompressed_dim_code(_dc2,(char*)(dc2+1));



	if ( _dc1[0] < _dc2[0] ) {
		ret = -1;
		goto end;
	}
	if ( _dc1[0] > _dc2[0] ) {
		ret =1;
		goto end;
	}
	for ( i = 1 ; i <= dim ; i ++ ) {
		_dc1[i] = _dc1[i] &
			(- (((INTEGER64)1)<<(_dc1[0]*m->dim_divide[i-1] +
				m->block_size[i-1])));
		_dc2[i] = _dc2[i] &
			(- (((INTEGER64)1)<<(_dc2[0]*m->dim_divide[i-1] +
				m->block_size[i-1])));
		if ( _dc1[i] < _dc2[i] ) {
			ret = -1;
			break;
		}
		if ( _dc1[i] > _dc2[i] ) {
			ret = 1;
			break;
		}
	}
	if ( i > dim )
		ret = 0;
end:

	d_f_ree(_dc1);
	d_f_ree(_dc2);
	return ret;
}

int
load_matrix_dim_addr(MATRIX_NODE * n,INTEGER64 addr_fofs)
{
PN64_MX_DIM_ADDR * da;
MX_FILE_WORK * w;
INTEGER64 * dim_code;
char * ptr;
MATRIX * m;

	m = n->matrix;

	if ( start_file_access(m) < 0 )
		return -1;

	w = m->file_work;
	da = read_filespace64f(w->pdb_fd,addr_fofs);
	if ( da == 0 ) {
		finish_file_access(m,0);
		return -1;
	}
	change_endian_header64(&da->h);
	if ( da->h.type != PNT_MX_DIM_ADDR ) {
		d_f_ree(da);
		finish_file_access(m,0);
		return -1;
	}
	dim_code = d_alloc((sizeof(INTEGER64)+1) * (m->p.dim + 1) + 1);
	for ( ptr = (char*)(da+1);
			(ptr - (char*)da) < da->h.size ; ) {
		if ( *ptr == (char)0xf2 )
			break;
		ptr = get_uncompressed_dim_code(dim_code,ptr);
		insert_dim_code_index(n,dim_code,0);
	}
	n->dim_addr_fofs = addr_fofs;
	n->dim_addr_size = da->h.size;
	d_f_ree(dim_code);
	d_f_ree(da);
	finish_file_access(m,0);
	return 0;
}

int
load_matrix_node(MATRIX_NODE * n)
{
MX_DIM_CODE * dc;
char * ptr;
MATRIX * m;
FAVT64_NODE * fn;
MX_FILE_WORK * w;

PN64_MX_NODE mx_n, * org;
INTEGER64 fofs,next_fofs;
int id;
void * d;
FAVT64_ROOT * rt;

	m = n->matrix;

	if ( start_file_access(m) < 0 )
		return -1;

	w = m->file_work;

	dc = d_alloc(sizeof(*dc) + (m->p.dim+1)*(sizeof(INTEGER64)+1)+1);
	dc->fofs= 0;
	ptr = (char*)(dc+1);
	ptr = get_compressed_dim_code(ptr,n->dim_code,m->p.dim);

	rt = get_root64(&w->favt64_err,
		w->pdb_fd,
		w->subheader.node_root,
		mx_dim_code_endian);
	fn = favt64_search(&w->favt64_err,
			rt,
			root_node64(&w->favt64_err,rt),
			dc,mx_dim_code_cmp,m);
	d_f_ree(dc);

	if ( fn == 0 )
		goto err;
	dc = fn->data;
	n->fofs = dc->fofs;
	wakeup_task((int)n);

	if ( m->dim_bit_field ) {
		load_raw_matrix_node(
			&mx_n,&org,0,n->nlist_dim_bit_field,m,dc->fofs);
	}
	else {
		load_raw_matrix_node(&mx_n,&org,0,0,m,dc->fofs);
		load_matrix_dim_addr(n,mx_n.d.p3.f.nlist_dim_addr);
		n->dim_addr_fofs = mx_n.d.p3.f.nlist_dim_addr;
	}

	if ( m->p.flags & MPF_CACHE_FILE ) {
		delete_mx_cache_ring(0,m,mx_n.d.p3.c.ring_fofs);
		insert_mx_cache_ring(m,mx_n.d.p3.c.ring_fofs);
		n->ring_fofs = mx_n.d.p3.c.ring_fofs;
	}
	d_f_ree(org);

	fofs = mx_n.channel_fofs;
	for ( ; fofs ; ) {
		d = load_matrix_data(
			&next_fofs,&id,m,fofs,0);
		if ( d == 0 )
			er_panic("data error");
		if ( n->channel[id].data )
			(*m->channel_info[id].data_type
				->free_data)(
				m->channel_info[id].data_type,
				n->channel[id].data);
		n->channel[id].data = d;
		n->channel[id].data_fofs = fofs;
		fofs = next_fofs;
	}

	lock_task(matrix_lock);
	n->status = MS_OK;
	wakeup_task((int)n);
	unlock_task(matrix_lock,"load_matrix_node");

	finish_file_access(m,0);
	return 0;

err:
/*
	lock_task(matrix_lock);
	n->status = MS_LOADING_ERR_1;
	wakeup_task((int)n);
	unlock_task(matrix_lock,"load_matrix_node");
*/

	finish_file_access(m,0);
	return -1;
}


#define MX_NODE_SIZE(xxx)	\
	((int)&(((PN64_MX_NODE*)0)->d.xxx.end_pos))

int
save_matrix_node(MATRIX_NODE * n)
{
MATRIX * m;
MX_FILE_WORK * w;
int id;
INTEGER64 prev_fofs,fofs;
RECORD_LIST64 * rl;
PN64_MX_CACHE_RING cr;
char * ptr;
int i;
PN64_MX_DIM_ADDR * da;
char * uc_dim_code;
INTEGER64 _ring,_dim_addr;
MX_DIM_CODE * dc;
FAVT64_NODE * fn, * fn2;
FAVT64_ROOT * rt;

PN64_HEADER * h;


	m = n->matrix;
	if ( start_file_access(m) < 0 )
		return -1;

	w = m->file_work;
check_freelist64(w->pdb_fd);

	prev_fofs = 0;
	for ( id = m->p.channel_nos-1 ; id >= 0 ; id -- ) {
		if ( !(m->channel_info[id].flags & MF_FILE) )
			continue;
		if ( n->channel[id].data == 0 )
			continue;
		fofs = save_matrix_data_2(
				m,
				n->channel[id].data_fofs,
				n->channel[id].data,
				prev_fofs,
				id,
				m->channel_info[id].data_type);
		n->channel[id].data_fofs = fofs;
		prev_fofs = fofs;
	}
check_freelist64(w->pdb_fd);

	rl = new_recordlist64(
		PNT_MX_NODE,
		sizeof(PN64_HEADER));

	change_endian(prev_fofs);
	set_recordlist_chain64(rl,&prev_fofs,sizeof(prev_fofs),0);

	if ( m->p.flags & MPF_CACHE_FILE ) {
		if ( n->ring_fofs == 0 ) {
			cr.h.type = PNT_MX_CACHE_RING;
			cr.h.size = sizeof(PN64_MX_CACHE_RING);
			cr.node = n->fofs;
			change_endian(cr.node);
			n->ring_fofs = alloc_filespace64(
				w->pdb_fd,&cr.h);
			change_endian_header64(&cr.h);
			write_filespace64f(w->pdb_fd,n->ring_fofs,&cr);
			insert_mx_cache_ring(m,n->ring_fofs);
		}

		_ring = n->ring_fofs;
		change_endian(_ring);
		set_recordlist_chain64(rl,&_ring,
			sizeof(INTEGER64),0);
	}

	if ( m->dim_bit_field == 0 ) {
		da = d_alloc(
			(sizeof(INTEGER64)+1) * (m->p.dim+1)
			* n->nlist_dim_addr_len +
			sizeof(*da));
		ptr = (char*)(da+1);
		for ( i = 0 ; i < n->nlist_dim_addr_len ; i ++ ) {
			ptr = get_compressed_dim_code(ptr,
				n->nlist_dim_addr[i],m->p.dim);
		}
		for ( ; ptr - ((char*)(da+1)) < sizeof(INTEGER64) ; ) {
			*ptr++ = 0xf2;
		}
		da->h.type = PNT_MX_DIM_ADDR;
		da->h.size = ptr - (char*)da;
		if ( n->dim_addr_size != da->h.size &&
				n->dim_addr_fofs ) {
			free_filespace64(w->pdb_fd,n->dim_addr_fofs);
			n->dim_addr_fofs = 0;
		}
		if ( n->dim_addr_fofs == 0 )
			n->dim_addr_fofs =
				alloc_filespace64(w->pdb_fd,&da->h);
		change_endian_header64(&da->h);
		write_filespace64f(w->pdb_fd,
			n->dim_addr_fofs,da);
		d_f_ree(da);

		_dim_addr = n->dim_addr_fofs;
		set_recordlist_chain64(rl,&_dim_addr,
			sizeof(INTEGER64),0);
	}

	uc_dim_code = d_alloc((sizeof(INTEGER64)+1)*(m->p.dim+1));
	ptr = uc_dim_code;
	ptr = get_compressed_dim_code(ptr,n->dim_code,m->p.dim);
	set_recordlist_chain64(rl,uc_dim_code,ptr - uc_dim_code,1);
	if ( m->dim_bit_field )
		set_recordlist_chain64(rl,n->nlist_dim_bit_field,
			m->dim_bit_field,0);

	setup_recordlist64(rl);

	if ( n->fofs == 0 ) {
		n->fofs = alloc_filespace64(w->pdb_fd,rl->data);
		if ( n->ring_fofs )
			setup_mx_cache_ring(m,n->ring_fofs,n->fofs);

		dc = d_alloc(sizeof(*dc) +
			(sizeof(INTEGER64)+1) * (m->p.dim+1));
		dc->fofs = n->fofs;
		ptr = (char*)(dc+1);
		ptr = get_compressed_dim_code(ptr,
				n->dim_code,m->p.dim);
		rt = get_root64(&w->favt64_err,
			w->pdb_fd,
			w->subheader.node_root,
			mx_dim_code_endian);
		fn = favt64_alloc_node(
			rt,
			dc,ptr - (char*)dc);
		if ( fn == 0 ) {
			w->favt64_err = -1;
			finish_file_access(m,0);
			return -1;
		}
		fn2 = favt64_insert(&w->favt64_err,
			rt,
			&rt->node,fn,
			mx_dim_code_cmp,m);
		if ( fn != fn2 )
			er_panic("save_matrix_node");
		d_f_ree(dc);
		
	}
	h = rl->data;
	change_endian_header64(h);
	write_filespace64f(w->pdb_fd,n->fofs,rl->data);
	wakeup_task((int)n);

	finish_file_access(m,0);
	return 0;
}



void
_close_matrix_file(MATRIX * m)
{
MX_FILE_WORK * w;

	if ( _xx_start_file_access(m,1,__FILE__,__LINE__) < 0 )
		return;
	w = m->file_work;
	w->subheader.flags &= ~MXF_WRITE;
	save_subheader(m,1);
	close_filespace64(w->pdb_fd);
	mx_open_files_cnt --;
	call_unlock(w->lock_d);
	d_f_ree(w);
}

void
close_matrix_file(MATRIX * m)
{
	lock_task(mx_file_lock);
	_close_matrix_file(m);
	unlock_task(mx_file_lock,"close_matrix_file");
}


int
matrix_standard_write_file(MATRIX_TOKEN * t)
{
	save_matrix_node(t->process_node);
	unlock_save(t->process_node);
	return 0;
}

int 
matrix_standard_read_file(MATRIX_TOKEN * t)
{
int ret;

	ret = load_matrix_node(t->process_node);

	return ret;
}


int
_mx_file_sync_create_node(MATRIX * m)
{
MATRIX_NODE * n;
int i;

retry:
	flush_all_dirty_file(m);
	for ( i = 0 ; i < NHASH_SIZE ; i ++ ) {
		for ( n = m->node_hash[i] ; n ; n = n->nh_next )
			if ( n->fofs == 0 && n->status <= MS_OK ) {
				finish_file_access_with_wait(m,(int)n);
				if ( start_file_access(m) < 0 )
					return -1;
				goto retry;
			}
	}
	return 0;
}


MX_DIM_CODE_LIST *
_mx_search_level_node(MATRIX * m,INTEGER64 * last_dc,int limit)
{
MX_DIM_CODE_LIST * ret, **retp, * lst;
MX_DIM_CODE * ldc_c,* max_c, * dc;
BOUND64_LIST * bl, * blp, * blp2;
INTEGER64 * max;
int i;
int err;
FAVT64_ROOT * rt;
MX_FILE_WORK * w;
int len;

	max = d_alloc(len = sizeof(INTEGER64)*(m->p.dim+1));
	for ( i = 0 ; i < m->p.dim ; i ++ )
		max[i+1] = (((U_INTEGER64)1)<<(sizeof(INTEGER64)*8-1))-1;
	max[0] = last_dc[0];

	max_c = d_alloc((sizeof(INTEGER64)+2)*(m->p.dim+1)
			+ sizeof(MX_DIM_CODE));
	ldc_c = d_alloc((sizeof(INTEGER64)+2)*(m->p.dim+1)
			+ sizeof(MX_DIM_CODE));

	get_compressed_dim_code((unsigned char*)(max_c+1),max,m->p.dim);
	get_compressed_dim_code((unsigned char*)(ldc_c+1),last_dc,
								m->p.dim);
	d_f_ree(max);


	w = m->file_work;


	rt = get_root64(&w->favt64_err,
		w->pdb_fd,
		w->subheader.node_root,
		mx_dim_code_endian);

	bl = favt64_bound_search(
		&err,rt,
		root_node64(&w->favt64_err,rt),
		ldc_c,max_c,limit,
		mx_dim_code_cmp,m);

	d_f_ree(max_c);
	d_f_ree(ldc_c);

	ret = 0;
	retp = &ret;
	for ( blp = bl ; blp ; ) {
		lst = d_alloc(sizeof(*lst));
		lst->dc = d_alloc(sizeof(INTEGER64)*(m->p.dim+1));
		dc = blp->data;
		get_uncompressed_dim_code(lst->dc,(unsigned char*)(dc+1));
		blp2 = blp;
		blp = blp->next;
		d_f_ree(blp2->data);
		d_f_ree(blp2);

		*retp = lst;
		lst->next = 0;
		retp = &lst->next;
	}
	return ret;
}


MX_DIM_CODE_LIST *
mx_search_level_node(MATRIX * m,INTEGER64 * last_dc,int limit)
{
MX_DIM_CODE_LIST * ret;
	if ( start_file_access(m) < 0 )
		return 0;
	if ( _mx_file_sync_create_node(m) < 0 )
		return 0;
	ret = _mx_search_level_node(m,last_dc,limit);
	finish_file_access(m,0);
	return ret;
}

int
load_mode_cal(XL_SEXP ** cals,int * pri,MATRIX * m,int mode)
{
MX_FILE_WORK * w;
XL_SEXP * ret, * s;
PN64_MX_CAL * c;
int i;
INTEGER64 fofs;
STREAM * st;

	if ( mode < 0 || mode >= MI_MODE_MAX ) {
		memset(pri,0,sizeof(int)*MI_MAX);
		memset(cals,0,sizeof(XL_SEXP*)*MI_MAX);
		return -1;
	}

	if ( start_file_access(m) < 0 )
		return -1;
	w = m->file_work;
	
	memcpy(pri,w->subheader.mode_pri_area[mode],sizeof(int)*MI_MAX);
	
	for ( i = 0 ; i < MI_MAX ; i ++ ) {
		fofs = w->subheader.mode_cal_fofs[mode][i];
		if ( fofs == 0 ) {
			cals[i] = 0;
			continue;
		}
		c = read_filespace64f(w->pdb_fd,fofs);
		if ( c == 0 ) {
			cals[i] = 0;
			continue;
		}
		st = s_open_string_read(c+1,&utf8_cm,c->h.size - sizeof(PN64_HEADER),1);
		ret = init_parse(st,
			l_string(std_cm,"sexp_commands"),
			l_string(std_cm,"sexp_commands"));
		for ( s = ret ; get_type(s) == XLT_PAIR ; s = cdr(s) );
		cals[i] = ret;
	}
	finish_file_access(m,0);
	return 0;
}


int
save_mode_cal(MATRIX * m,int mode,char *** cals,int * pri)
{
MX_FILE_WORK * w;
PN64_MX_CAL * c,* h;
int i,j;
INTEGER64 fofs;
RECORD_LIST64 * rl;
int f;

	if ( mode < 0 || mode >= MI_MODE_MAX )
		return -1;

	if ( start_file_access(m) < 0)
		return -1;
	w = m->file_work;
	
	memcpy(w->subheader.mode_pri_area[mode],pri,sizeof(int)*MI_MAX);
	
	f = 0;
	for ( i = 0 ; i < MI_MAX ; i ++ ) {
		if ( cals[i] ) {
			rl = new_recordlist64(PNT_MX_CAL,sizeof(PN64_MX_CAL));
			h = rl->data;

			for ( j = 0 ; cals[i][j] ; j ++ )
				set_recordlist_chain64(rl,
					cals[i][j],strlen(cals[i][j]),0);
			setup_recordlist64(rl);
			h = rl->data;
			
			fofs = w->subheader.mode_cal_fofs[mode][i];
			if ( fofs == 0 ) {
			new_alloc:
				f = 1;
				fofs = alloc_filespace64(w->pdb_fd,rl->data);
			}
			else {
				c = read_filespace64f(w->pdb_fd,fofs);
				if ( c == 0 ) {
					goto new_alloc;
				}
				change_endian_header64(&c->h);
				if ( h->h.size != c->h.size ) {
					d_f_ree(c);
					free_filespace64(w->pdb_fd,fofs);
					goto new_alloc;
				}
				d_f_ree(c);
			}
			change_endian_header64(rl->data);
			write_filespace64f(w->pdb_fd,fofs,rl->data);
			w->subheader.mode_cal_fofs[mode][i] = fofs;
			
			free_recordlist64(rl);
		}
		else {
			fofs = w->subheader.mode_cal_fofs[mode][i];
			if ( fofs ) {
				free_filespace64(w->pdb_fd,fofs);
			}
		}
	}
	if ( f )
		save_subheader(m,0);
	finish_file_access(m,0);
	return 0;
}



