// Basic ART2 Object provides compressed acces routines
// and system parameter

#import  <objc/Object.h>
#import  "ART2_Basic.h"
#import  "ART_STM.h"
#import  "ART_LTM.h"
#include "ART_Func.h"
// only for the Alert checking usw.
#import <appkit/Application.h>
#import <appkit/Control.h>
#import <appkit/TextField.h>
#import <appkit/Window.h>
#import <appkit/Panel.h>
#import <appkit/Cursor.h>
#import <appkit/Matrix.h>
#import <appkit/Cell.h>
#import <stdlib.h>
#import <string.h>
#import <objc/NXStringTable.h>
/*
#define ART_STABLE_LEARNING 1
#define ART_ONE_LEARNING 0
#define ART_STD_MODE 0
#define ART_EXT_MODE 1
#define ART_F2_OFF -1
#define ART_MIX 0
#define ART_SUB 1
#define ART_SUP 2
#define ART_IDE 3
*/


#define ART_READ(x) NXRead(stream,&x,sizeof(x))
#define ART_WRITE(x) NXWrite(stream,&x,sizeof(x))


// translate the V array in an inververs array inv_V
void invert_of(int M,float *V ,float *inv_V, float theta);
void mulVec(int M,float *mul1 ,float *mul2,float *prod);
// build the magic R array and returns its norm
float calc_R_with(int M,float *Q ,float *P, float *R, float c);




@implementation ART2_Basic : Object

+ new
{    
     self=[super new];
     return self;
}
- saveinto:(NXStream *)stream
{
     ART_WRITE(M);
     ART_WRITE(N);
     ART_WRITE(rho);
     ART_WRITE(rhoset);
     ART_WRITE(theta);
     ART_WRITE(a);
     ART_WRITE(b);
     ART_WRITE(c);
     ART_WRITE(d);
     ART_WRITE(h);
     ART_WRITE(ac_diff);
     ART_WRITE(z_diff);
     ART_WRITE(BU_inst);
     ART_WRITE(exor_thresh);
     ART_WRITE(i_min);
     ART_WRITE(i_max);
     ART_WRITE(art2_mode);
     ART_WRITE(learn_mode);
     [LTM saveinto:stream as:ART_LTM_BIN];
     return self;
}
- loadfrom:(NXStream *)stream
{
     int i,n;
     [super init];
     ART_READ(M);
     ART_READ(N);
     ART_READ(rho);
     ART_READ(rhoset);
     ART_READ(theta);
     ART_READ(a);
     ART_READ(b);
     ART_READ(c);
     ART_READ(d);
     ART_READ(h);
     ART_READ(ac_diff);
     ART_READ(z_diff);
     ART_READ(BU_inst);
     ART_READ(exor_thresh);
     ART_READ(i_min);
     ART_READ(i_max);
     ART_READ(art2_mode);
     ART_READ(learn_mode);

     [self build_ART2];
     [self resetLTM];
     R =NXZoneMalloc([self zone],M*sizeof(float));
     PatQueue=NXZoneMalloc([self zone],N*sizeof(int));
     zeroP=NXZoneMalloc([self zone],M*sizeof(float));
     for (i=0;i<M;i++) zeroP[i]=0.0;
     [LTM loadfrom:stream];
     return self;
}
- init_M:(int)Vec_dim N:(int)F2_dim
{
     int i;
     [super init];
     M=Vec_dim;
     N=F2_dim;
     [self build_ART2];
     [self setdefault_config];
     [self resetLTM];
     R =NXZoneMalloc([self zone],M*sizeof(float));
     PatQueue=NXZoneMalloc([self zone],N*sizeof(int));
     zeroP=NXZoneMalloc([self zone],M*sizeof(float));
     for (i=0;i<M;i++) zeroP[i]=0.0;
     return self;
}
- free
{
     [F0 free];
     [F1 free];
     [F2 free];
     [LTM free];
     [F0x free];
     NXZoneFree([self zone],R);
     NXZoneFree([self zone],zeroP);
     NXZoneFree([self zone],PatQueue);
     return [super free];
}

- build_ART2
{
     F0=[ART_STM new];
     F1=[ART_STM new];
     F2=[ART_STM new];
     LTM=[ART_LTM new];
     F0x=[ART_STM new];

     [F0 init:M];
     [F1 init:M];
     [F2 init:N];
     [LTM init:M of:N];
     [F0x init:M];
     return self;
}
- (BOOL)resizeART
{
     int i,j, extN, ac;
     float *TMP;
     char buf1[255],buf2[255];
     
     if ([LTM estabNodes]==N){
       // new dimension of F2
       extN=N+5;
       // free and realloc the pattern queue
       NXZoneFree([self zone],PatQueue);
       PatQueue=NXZoneMalloc([self zone],extN*sizeof(int));
       // generate a new LTM object
       tmpLTM=[ART_LTM new];

       [tmpLTM init:M of:extN];
       [tmpLTM set_d:d h:h];
       [tmpLTM set_zero_with:BU_inst];
       [tmpLTM setestabNodes:N];
       [tmpLTM newPattern];

       // translate the TD templates
       for (j=0;j<N;j++)
         [tmpLTM set:[LTM TD_of:j] of:M to_TD_of:j];
       // emty the pattern queue
       for (j=0;j<extN;j++)
         PatQueue[j]=ART_F2_OFF;
       // translate the BU templates form the old N
       for (i=0;i<M;i++)
         [tmpLTM set:[LTM BU_of:i] of:N to_BU_of:i];
       // free LTM and switch tmpLTM to LTM
       [LTM free];
       // free F2 and install again with extN
       tmpF2=[ART_STM new];
       [tmpF2 init:extN];
       // save the last active node
       ac=[F2 activeNode];
       [F2 free];
       F2=tmpF2;
       TMP=[F2 get_U];
       if (ac >= 0) TMP[ac]=1;
       [F2 MaxAct_with:extN]; 
       LTM=tmpLTM;
       // switch to the new F2 size of N
       N=extN;                     
       return YES;
       }
     return NO;
}

- setdefault_config
{
     // system parameter
     rho=0.955;
     rhoset=0.95;
     theta=1.1/sqrt( (float)M );
     c=0.225; d=0.8; h=1.0;
     a=5.0; b=5.0;
     ac_diff=0.01; z_diff=0.01;
     BU_inst=0.1; exor_thresh=0.98;
     sigmoWidth=0.001;
     i_min=2; i_max=100;

     // performance parameter
     art2_mode=ART_STD_MODE;
     learn_mode=ART_ONE_LEARNING;
     set_type=ART_MIX;
     return self;

}
- update_syspara
{
    [F0 set_a:a b:b theta:theta d:0.0];
    [F0 setIterationmin:i_min max:i_max stable:ac_diff];
    [F0 set_NoLinFunc:ART_STM_SIGMO];

    [F1 set_a:a b:b theta:theta d:d];
    [F1 setIterationmin:i_min max:i_max stable:ac_diff];
    [F1 set_NoLinFunc:ART_STM_SIGMO];

    [F2 set_a:a b:b theta:theta d:0.0];
    [F2 setIterationmin:i_min max:i_max stable:ac_diff];
    [F2 set_NoLinFunc:ART_STM_SIGMO];

    [F0x set_a:a b:b theta:theta d:0.0];
    [F0x setIterationmin:i_min max:i_max stable:ac_diff];
    [F0x set_NoLinFunc:ART_STM_SIGMO];

    [LTM set_d:d h:h];
    return self;
}

- zeroSTMs
{
    [F0 set_zero];
    [F1 set_zero];
    [F2 set_zero];
    [F0x set_zero]; 
    return self;
}


- resetLTM
{
     [LTM set_zero_with:BU_inst];
     return self;
}

// set the input pattern and initialize the STM's
- setPattern:(float *)In
{
     int j;
     InPat=In;
     [LTM newPattern];
     for(j=0;j<N;j++)
       PatQueue[j]=ART_F2_OFF;
     set_type=ART_MIX;
     return self;
}

// preprocessing
- (int)preproc
{
     [F0 set_I:InPat and_E:zeroP];
     return [F0 iterate];
}

// chooseF2
- (int)chooseF2
{
     [F1 set_zero];
     [F1 set_I:[F0 get_P] and_E:zeroP];       
     [F1 iterate];
     // looking for the winner at F2 an store for output
     [LTM korrelate_with:[F1 get_P] to:[F2 get_U]];
     [F2 MaxAct_with:[LTM estabNodes]+1];
     if (art2_mode)
       set_type=[self exorproc];
     return [F2 activeNode];
}
-  compair
{
     [F1 set_I:[F0 get_P] and_E:[LTM TD_of:[F2 activeNode]] ];
     [F1 iterate];
     return self;
}

// perform F1 with TD template an return the match
- (BOOL)match
{
     // switch to the required rho for vigilance
     // in extended mode tmprho is set to rhoset in case of
     // a super or subset presentation
     //tmprho= (art2_mode) ? ( (set_type) ? rho:rhoset ):rho);
     if (art2_mode)
       if (set_type!=ART_MIX)
         tmprho=rhoset;
       else
         tmprho=rho;
     else
       tmprho=rho;
     // store the rho value in r for display
     r=calc_R_with(M,[F0 get_P],[F1 get_P],R,c);
     return (tmprho > r) ? NO:YES;
}
// perform learning with the stable criterion as return
- (float)learning
{  
     return [LTM learnPattern:[F1 get_P] toNode:[F2 activeNode]];
}
// perform the magic exor processing returns the type of set
- (int)exorproc
{
     int F2_on,i;
     float tmp;
     float sigE,sigI,sigS;
     if ([self newNode]) return (int)ART_SUB;
     F2_on=[F2 activeNode];
     // preprocessing the template
     [F0x set_zero];
     [F0x set_I:[LTM TD_of:F2_on] and_E:zeroP];
     [F0x iterate];

     // art norm of I and the template's preprocessed
     sigE=[self significance_of:[F0x get_Q]];
     sigI=[self significance_of:[F0  get_P]];

     // note that S = O*inv(T) is written to p of F0x
     // w of act as a buffer
     if ( sigE < sigI) {
       // the input I could be a superset of E
       invert_of(M,[F0x get_Q],[F0x get_W],theta);
       mulVec(M,[F0  get_P],[F0x get_W],[F0x get_P]);
       sigS=[self significance_of:[F0x get_P]];

       // analysing EXOR pattern and decide of real superset
       if ( fabs(sigS+sigE-sigI)< 1.0 ) return (int)ART_SUP;
       }
     else { 
       // the input I could be a subset of E
       invert_of(M,[F0 get_P],[F0x get_W],theta);
       mulVec(M,[F0x  get_W],[F0x get_Q],[F0x get_P]);
       sigS=[self significance_of:[F0x get_P]];

       // Input and teplate have the same features
       if (sigS==0.0) return(int)ART_IDE;
       // analysing EXOR pattern and decide of real subset
       if ( fabs(sigS+sigI-sigE)< 1.0 )
         return (int)ART_SUB;
       }
     return (int)ART_MIX;

}

-(BOOL)setExorPattern
{
     // Note that the EXOR Pattern S is the part of the input
     // that the template need additional to figure the input.
     // Hence the EXOR pattern should be presented to the system
     // virually as a new pattern. This pattern is also orthogonal
     // to its source.
     // The input Pattern should be :
     //   1. superset
     //
     if ( (set_type==ART_SUP) )
       // in this case the Pointer to F0x is set to F1 so that
       // for further search the vector P of F0x, containing
       // the processed EXOR-pattern is going to be treated as
       // a new pattern with full ART ability.
       {
       L2_norm_Vec([F0x get_P],[F0  get_P],M);
       return YES;
       }
     else return NO;
}



- (BOOL)newNode
{
     if ([F2 activeNode]==[LTM estabNodes])
       return YES;
     else
       return NO;
}
- markActiveNode
{
     int j;
     for(j=0;PatQueue[j]!=ART_F2_OFF;j++);
     PatQueue[j]=[F2 activeNode];
     return self;
}

// additionl procedures
float calc_R_with(int M,float *Q, float *P,float *R,float c)
{
     int i;
     float tmp;
     tmp=c*L2_norm(P,M)+L2_norm(Q,M);
     tmp=(tmp >0.1)? tmp: 1;
     for (i=0;i<M;i++) R[i]=(Q[i]+P[i]*c)/tmp;
     return L2_norm(R,M) ;
}

void invert_of(int M,float *V ,float *inv_V,float theta)
{
     int i;
     float tmp=L2_norm(V,M);
     for (i=0; i<M; i++)
       inv_V[i]= ( (V[i]/tmp) < theta) ? 1.0 : 0.0 ;
     return;
}

void mulVec(int M,float *mul1 ,float *mul2,float *prod)
{
     int i;
     for (i=0; i<M; i++)
       prod[i]=mul1[i]*mul2[i];
     return;
}
// calculating the special ART-norm of a vector
- (float)significance_of:(float *)V
{
     float tmp=L2_norm(V,M);
     int i,s=0;

     for (i=0; i<M; i++)
       if ( (V[i]/tmp) > theta) s++;
     return (float)s;
}

// direct access methods
- F0 { return F0; }
- F1 { return F1; }
- F2 { return F2; }
- F0x { return F0x; }
- LTM { return LTM; }
@end