text:
#include <R.h>
#include <Rdefines.h>

#include "stdlib.h"
#include "stdio.h"

#ifdef BIOLIB
  #include <biolib_R_map.h>
#endif

#define BUFFER_SIZE 1024

/*****************************************************************
 **
 **
 ** A structure for holding information in the
 ** "CDF" and "Chip" sections (basically header information)
 **
 ******************************************************************/

typedef struct {

  char *version;
  char *name;
  int rows,cols;
  int numberofunits;
  int maxunit;
  int NumQCUnits;
  char *chipreference;
} cdf_text_header;

/*****************************************************************
 **
 **
 ** A structure for holding QC probe information
 ** Note the "CYCLES" item is ignored and never parsed
 **
 ******************************************************************/

typedef struct {
  int x;
  int y;
  char *probe;
  int plen;
  int atom;
  int index;
  int match;
  int bg;
} cdf_text_qc_probe;

/*******************************************************************
 **
 ** A structure for holding QC units information. These are
 ** areas of the chip that contain probes that may or may not be useful
 ** for QC and other purposes.
 **
 **
 *******************************************************************/

typedef struct{
  int type;
  unsigned int n_probes;
  int qccontains[8];     /* either 0 or 1 for each of the eight possible fields. a 1 means that field is present.*/
  cdf_text_qc_probe *qc_probes;

} cdf_text_qc_unit;

/*******************************************************************
 **
 ** A structure for holding probe information for unit_blocks_probes
 **
 ** probes are stored within blocks
 **
 *******************************************************************/

typedef struct{
  int x;
  int y;
  char *probe;
  char *feat;
  char *qual;
  int expos;
  int pos;
  char *cbase;
  char *pbase;
  char *tbase;
  int atom;
  int index;
  int codonid;
  int codon;
  int regiontype;
  char* region;
} cdf_text_unit_block_probe;

/*******************************************************************
 **
 ** A structure holding Unit_blocks
 **
 ** blocks are stored within units.
 ** blocks contain many probes
 **
 *******************************************************************/

typedef struct{
  char *name;
  int blocknumber;
  int num_atoms;
  int num_cells;
  int start_position;
  int stop_position;
  int direction;
  cdf_text_unit_block_probe *probes;

} cdf_text_unit_block;

/*******************************************************************
 **
 ** A structure for holding "Units" AKA known as probesets
 **
 ** Each unit contains one or more blocks. Each block contains one or
 ** more probes
 **
 *******************************************************************/

typedef struct{
  char *name;
  int direction;
  int num_atoms;
  int num_cells;
  int unit_number;
  int unit_type;
  int numberblocks;
  int MutationType;
  cdf_text_unit_block *blocks;
} cdf_text_unit;

/*******************************************************************
 **
 ** A structure for holding a text CDF file
 **
 ** text cdf files consist of
 ** basic header information
 ** qcunits
 **       - qc probes
 ** units (aka probesets)
 **       - blocks
 **            - probes
 **
 **
 *******************************************************************/

typedef struct{
  cdf_text_header header;
  cdf_text_qc_unit *qc_units;
  cdf_text_unit *units;
} cdf_text;

/**************************************************************
 **
 ** The following code is for tokenizing strings
 ** originally included in read_abatch.c from the affy package.
 **
 *************************************************************/

/***************************************************************
 **
 ** tokenset
 **
 ** char **tokens  - a array of token strings
 ** int n - number of tokens in this set.
 **
 ** a structure to hold a set of tokens. Typically a tokenset is
 ** created by breaking a character string based upon a set of
 ** delimiters.
 **
 **
 **************************************************************/

typedef struct{
  char **tokens;
  int n;
} tokenset;

/******************************************************************
 **
 ** tokenset *tokenize(char *str, char *delimiters)
 **
 ** char *str - a string to break into tokens
 ** char *delimiters - delimiters to use in breaking up the line
 **
 **
 ** RETURNS a new tokenset
 **
 ** Given a string, split into tokens based on a set of delimitors
 **
 *****************************************************************/

static tokenset *tokenize(char *str, char *delimiters){

  int i=0;

  char *current_token;
  tokenset *my_tokenset = Calloc(1,tokenset);
  my_tokenset->n=0;

  my_tokenset->tokens = NULL;

  current_token = strtok(str,delimiters);
  while (current_token != NULL){
    my_tokenset->n++;
    my_tokenset->tokens = Realloc(my_tokenset->tokens,my_tokenset->n,char*);
    my_tokenset->tokens[i] = Calloc(strlen(current_token)+1,char);
    strcpy(my_tokenset->tokens[i],current_token);
    i++;
    current_token = strtok(NULL,delimiters);
  }

  return my_tokenset;
}

/******************************************************************
 **
 ** int tokenset_size(tokenset *x)
 **
 ** tokenset *x - a tokenset
 **
 ** RETURNS the number of tokens in the tokenset
 **
 ******************************************************************/

static int tokenset_size(tokenset *x){
  return x->n;
}

/******************************************************************
 **
 ** char *get_token(tokenset *x, int i)
 **
 ** tokenset *x - a tokenset
 ** int i - index of the token to return
 **
 ** RETURNS pointer to the i'th token
 **
 ******************************************************************/

static char *get_token(tokenset *x,int i){
  return x->tokens[i];
}

/******************************************************************
 **
 ** void delete_tokens(tokenset *x)
 **
 ** tokenset *x - a tokenset
 **
 ** Deallocates all the space allocated for a tokenset
 **
 ******************************************************************/

static void delete_tokens(tokenset *x){

  int i;

  for (i=0; i < x->n; i++){
    Free(x->tokens[i]);
  }
  Free(x->tokens);
  Free(x);
}

/*******************************************************************
 **
 ** int token_ends_with(char *token, char *ends)
 **
 ** char *token  -  a string to check
 ** char *ends_in   - we are looking for this string at the end of token
 **
 **
 ** returns  0 if no match, otherwise it returns the index of the first character
 ** which matchs the start of *ends.
 **
 ** Note that there must be one additional character in "token" beyond
 ** the characters in "ends". So
 **
 **  *token = "TestStr"
 **  *ends = "TestStr"
 **
 ** would return 0 but if
 **
 ** ends = "estStr"
 **
 ** we would return 1.
 **
 ** and if
 **
 ** ends= "stStr"
 ** we would return 2 .....etc
 **
 **
 ******************************************************************/

static int token_ends_with(char *token, char *ends_in){

  int tokenlength = strlen(token);
  int ends_length = strlen(ends_in);
  int start_pos;
  char *tmp_ptr;

  if (tokenlength <= ends_length){
    /* token string is too short so can't possibly end with ends */
    return 0;
  }

  start_pos = tokenlength - ends_length;

  tmp_ptr = &token[start_pos];

  if (strcmp(tmp_ptr,ends_in)==0){
    return start_pos;
  } else {
    return 0;
  }
}

/******************************************************************
 **
 ** The following code, also from read_abatch.c is more about locating
 ** sections in the file and reading it in.
 **
 ******************************************************************/

/**
 ** This reads a line from the specified file stream
 **
 **
 **/

static void ReadFileLine(char *buffer, int buffersize, FILE *currentFile){
  if (fgets(buffer, buffersize, currentFile) == NULL){
    error("End of file reached unexpectedly. Perhaps this file is truncated.\n");
  }
}

/******************************************************************
 **
 ** void findStartsWith(FILE *my_file,char *starts, char *buffer)
 **
 ** FILE *my_file - an open file to read from
 ** char *starts - the string to search for at the start of each line
 ** char *buffer - where to place the line that has been read.
 **
 **
 ** Find a line that starts with the specified character string.
 ** At exit buffer should contain that line
 **
 *****************************************************************/

static void  findStartsWith(FILE *my_file,char *starts, char *buffer){

  int starts_len = strlen(starts);
  int match = 1;

  do {
    ReadFileLine(buffer, BUFFER_SIZE, my_file);
    match = strncmp(starts, buffer, starts_len);
  } while (match != 0);
}

/******************************************************************
 **
 ** void AdvanceToSection(FILE *my_file,char *sectiontitle, char *buffer)
 **
 ** FILE *my_file - an open file
 ** char *sectiontitle - string we are searching for
 ** char *buffer - return's with line starting with sectiontitle
 **
 **
 *****************************************************************/

static void AdvanceToSection(FILE *my_file,char *sectiontitle, char *buffer){
  findStartsWith(my_file,sectiontitle,buffer);
}

/*******************************************************************
 **
 ** void read_cdf_header(FILE *infile,  cdf_text *mycdf, char* linebuffer)
 **
 ** FILE *infile - pointer to open file presumed to be a CDF file
 ** cdf_text *mycdf - structure for holding cdf file
 ** char *linebuffer - a place to store strings that are read in. Length
 **                   is given by BUFFER_SIZE
 **
 *******************************************************************/

static void read_cdf_header(FILE *infile,  cdf_text *mycdf, char* linebuffer){

  tokenset *cur_tokenset;

  /* move to the Chip section */
  AdvanceToSection(infile,"[Chip]",linebuffer);

  findStartsWith(infile,"Name",linebuffer);

  /* Read the Name */
  cur_tokenset = tokenize(linebuffer,"=\r\n");
  mycdf->header.name = Calloc(strlen(get_token(cur_tokenset,1))+1,char);
  strcpy(mycdf->header.name,get_token(cur_tokenset,1));
  delete_tokens(cur_tokenset);

  /* Read the Rows and Cols, Number of units etc  */

  findStartsWith(infile,"Rows",linebuffer);
  cur_tokenset = tokenize(linebuffer,"=");
  mycdf->header.rows = atoi(get_token(cur_tokenset,1));
  delete_tokens(cur_tokenset);

  findStartsWith(infile,"Cols",linebuffer);
  cur_tokenset = tokenize(linebuffer,"=");
  mycdf->header.cols = atoi(get_token(cur_tokenset,1));
  delete_tokens(cur_tokenset);

  findStartsWith(infile,"NumberOfUnits",linebuffer);
  cur_tokenset = tokenize(linebuffer,"=");
  mycdf->header.numberofunits = atoi(get_token(cur_tokenset,1));
  delete_tokens(cur_tokenset);

  findStartsWith(infile,"MaxUnit",linebuffer);
  cur_tokenset = tokenize(linebuffer,"=");
  mycdf->header.maxunit = atoi(get_token(cur_tokenset,1));
  delete_tokens(cur_tokenset);

  findStartsWith(infile,"NumQCUnits",linebuffer);
  cur_tokenset = tokenize(linebuffer,"=");
  mycdf->header.NumQCUnits = atoi(get_token(cur_tokenset,1));
  delete_tokens(cur_tokenset);

  findStartsWith(infile,"ChipReference",linebuffer);
  cur_tokenset = tokenize(linebuffer,"=\r\n");
  if (cur_tokenset->n > 1){
    mycdf->header.chipreference = Calloc(strlen(get_token(cur_tokenset,1))+1,char);
    strcpy(mycdf->header.chipreference,get_token(cur_tokenset,1));
  } else {
    mycdf->header.chipreference = NULL;
  }

  delete_tokens(cur_tokenset);

}

/*******************************************************************
 **
 **  void read_cdf_QCUnits_probes(FILE *infile,  cdf_text *mycdf, char* linebuffer,int index)
 **
 **  FILE *infile - an opened CDF file
 **  cdf_text *mycdf - a structure for holding cdf file
 **  char *linebuffer - temporary place to store lines of text read in
 **  int index - which QCunit.
 **
 ** This function reads in the QC unit probes from the cdf file. It is assumed that the space to
 ** store them is already allocated.
 **
 *******************************************************************/

static void read_cdf_QCUnits_probes(FILE *infile,  cdf_text *mycdf, char* linebuffer,int index){
  tokenset *cur_tokenset;
  int i;

  for (i =0; i < mycdf->qc_units[index].n_probes; i++){
    ReadFileLine(linebuffer, BUFFER_SIZE, infile);
    cur_tokenset = tokenize(linebuffer,"=\t\r\n");
    if (mycdf->qc_units[index].qccontains[0]){
      mycdf->qc_units[index].qc_probes[i].x = atoi(get_token(cur_tokenset,1));
    }
    if (mycdf->qc_units[index].qccontains[1]){
      mycdf->qc_units[index].qc_probes[i].y = atoi(get_token(cur_tokenset,2));
    }
    if (mycdf->qc_units[index].qccontains[2]){
      mycdf->qc_units[index].qc_probes[i].probe=Calloc(strlen(get_token(cur_tokenset,3))+1,char);
      strcpy(mycdf->qc_units[index].qc_probes[i].probe,get_token(cur_tokenset,3));
    }
    if (mycdf->qc_units[index].qccontains[3]){
      mycdf->qc_units[index].qc_probes[i].plen = atoi(get_token(cur_tokenset,4));
    }
    if (mycdf->qc_units[index].qccontains[4]){
      mycdf->qc_units[index].qc_probes[i].atom = atoi(get_token(cur_tokenset,5));
    }
    if (mycdf->qc_units[index].qccontains[5]){
      mycdf->qc_units[index].qc_probes[i].index = atoi(get_token(cur_tokenset,6));
    }
    if (mycdf->qc_units[index].qccontains[6]){
      mycdf->qc_units[index].qc_probes[i].match = atoi(get_token(cur_tokenset,7));
    }
    if (mycdf->qc_units[index].qccontains[7]){
      mycdf->qc_units[index].qc_probes[i].bg = atoi(get_token(cur_tokenset,8));
    }
    delete_tokens(cur_tokenset);
  }

}

/*******************************************************************
 **
 ** void read_cdf_QCUnits(FILE *infile,  cdf_text *mycdf, char* linebuffer)
 **
 **  FILE *infile - an opened CDF file
 **  cdf_text *mycdf - a structure for holding cdf file
 **  char *linebuffer - temporary place to store lines of text read in
 **
 **  Reads all the QC units. Note that it allocates the space for the probes
 **  it is assumed that the space for the actual QC units are already allocated
 **
 *******************************************************************/

static void read_cdf_QCUnits(FILE *infile,  cdf_text *mycdf, char* linebuffer){

  tokenset *cur_tokenset;
  int i,j;

  mycdf->qc_units = Calloc(mycdf->header.NumQCUnits,cdf_text_qc_unit);

  for (i =0; i < mycdf->header.NumQCUnits; i++){
    /* move to the next QC section */
    AdvanceToSection(infile,"[QC",linebuffer);
    findStartsWith(infile,"Type",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->qc_units[i].type = (unsigned short)atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);
    findStartsWith(infile,"NumberCells",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->qc_units[i].n_probes = atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);
    mycdf->qc_units[i].qc_probes = Calloc(mycdf->qc_units[i].n_probes,cdf_text_qc_probe);

    /* Figure out which fields this QC unit has */
    findStartsWith(infile,"CellHeader",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=\t\r\n");
    for (j=1; j < tokenset_size(cur_tokenset); j++){
      if(strncmp("X", get_token(cur_tokenset,j), 1) == 0){
	mycdf->qc_units[i].qccontains[0]  =1;
      } else if (strncmp("Y", get_token(cur_tokenset,j), 1) == 0){
	mycdf->qc_units[i].qccontains[1]  =1;
      } else if (strncmp("PROBE",get_token(cur_tokenset,j), 5) == 0){
	mycdf->qc_units[i].qccontains[2]  =1;
      } else if (strncmp("PLEN",get_token(cur_tokenset,j), 4) == 0){
	mycdf->qc_units[i].qccontains[3]  =1;
      } else if (strncmp("ATOM",get_token(cur_tokenset,j), 4) == 0){
	mycdf->qc_units[i].qccontains[4]  =1;
      } else if (strncmp("INDEX",get_token(cur_tokenset,j), 5) == 0){
	mycdf->qc_units[i].qccontains[5]  =1;
      } else if (strncmp("MATCH",get_token(cur_tokenset,j), 5) == 0){
	mycdf->qc_units[i].qccontains[6]  =1;
      } else if (strncmp("BG",get_token(cur_tokenset,j), 2) == 0){
	mycdf->qc_units[i].qccontains[7]  =1;
      }
    }
    delete_tokens(cur_tokenset);

    read_cdf_QCUnits_probes(infile,mycdf,linebuffer,i);

  }
}

/*******************************************************************
 **
 ** void read_cdf_unit_block_probes(FILE *infile,  cdf_text *mycdf, char* linebuffer, int unit,int block)
 **
 **  FILE *infile - an opened CDF file
 **  cdf_text *mycdf - a structure for holding cdf file
 **  char *linebuffer - temporary place to store lines of text read in from the file
 **  int unit - which unit
 **  int block - which block
 **
 ** Reads in the probes for each unit. Note that it is assumed that the
 ** space for the probes has actually been allocated.
 **
 *******************************************************************/

static void read_cdf_unit_block_probes(FILE *infile,  cdf_text *mycdf, char* linebuffer, int unit,int block){
  int i;
   tokenset *cur_tokenset;

  /* Read the Cell Header for the unit block */
  ReadFileLine(linebuffer, BUFFER_SIZE, infile);

  for (i =0; i < mycdf->units[unit].blocks[block].num_cells; i++){
    ReadFileLine(linebuffer, BUFFER_SIZE, infile);
    cur_tokenset = tokenize(linebuffer,"=\t\r\n");
    mycdf->units[unit].blocks[block].probes[i].x = atoi(get_token(cur_tokenset,1));
    mycdf->units[unit].blocks[block].probes[i].y = atoi(get_token(cur_tokenset,2));
    mycdf->units[unit].blocks[block].probes[i].probe=Calloc(strlen(get_token(cur_tokenset,3))+1,char);
    strcpy(mycdf->units[unit].blocks[block].probes[i].probe,get_token(cur_tokenset,3));
    mycdf->units[unit].blocks[block].probes[i].feat=Calloc(strlen(get_token(cur_tokenset,4))+1,char);
    strcpy(mycdf->units[unit].blocks[block].probes[i].feat,get_token(cur_tokenset,4));
    mycdf->units[unit].blocks[block].probes[i].qual=Calloc(strlen(get_token(cur_tokenset,5))+1,char);
    strcpy(mycdf->units[unit].blocks[block].probes[i].qual,get_token(cur_tokenset,5));
    mycdf->units[unit].blocks[block].probes[i].expos = atoi(get_token(cur_tokenset,6));
    mycdf->units[unit].blocks[block].probes[i].pos = atoi(get_token(cur_tokenset,7));
    mycdf->units[unit].blocks[block].probes[i].cbase = Calloc(strlen(get_token(cur_tokenset,8))+1,char);
    strcpy(mycdf->units[unit].blocks[block].probes[i].cbase,get_token(cur_tokenset,8));
    mycdf->units[unit].blocks[block].probes[i].pbase = Calloc(strlen(get_token(cur_tokenset,9))+1,char);
    strcpy(mycdf->units[unit].blocks[block].probes[i].pbase,get_token(cur_tokenset,9));
    mycdf->units[unit].blocks[block].probes[i].tbase = Calloc(strlen(get_token(cur_tokenset,10))+1,char);
    strcpy(mycdf->units[unit].blocks[block].probes[i].tbase,get_token(cur_tokenset,10));
    mycdf->units[unit].blocks[block].probes[i].atom = atoi(get_token(cur_tokenset,11));
    mycdf->units[unit].blocks[block].probes[i].index = atoi(get_token(cur_tokenset,12));
    mycdf->units[unit].blocks[block].probes[i].codonid = atoi(get_token(cur_tokenset,13));
    mycdf->units[unit].blocks[block].probes[i].codon = atoi(get_token(cur_tokenset,14));
    mycdf->units[unit].blocks[block].probes[i].regiontype = atoi(get_token(cur_tokenset,15));
    delete_tokens(cur_tokenset);
  }

}

/*******************************************************************
 **
 ** void read_cdf_unit_block(FILE *infile,  cdf_text *mycdf, char* linebuffer, int unit)
 **
 **  FILE *infile - an opened CDF file
 **  cdf_text *mycdf - a structure for holding cdf file
 **  char *linebuffer - temporary place to store lines of text read in from the file
 **  int unit - which unit
 **
 ** Reads in all the blocks for the unit. Assumes that space for the blocks are allocated
 ** already. Allocates the space for the probes and calls a function to read them in.
 **
 *******************************************************************/

static void read_cdf_unit_block(FILE *infile,  cdf_text *mycdf, char* linebuffer, int unit){
  tokenset *cur_tokenset;
  int i;

  for (i=0; i < mycdf->units[unit].numberblocks; i++){

    findStartsWith(infile,"Name",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=\r\n");
    mycdf->units[unit].blocks[i].name = Calloc(strlen(get_token(cur_tokenset,1))+1,char);
    strcpy(mycdf->units[unit].blocks[i].name,get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);
    /* Rprintf("%s\n",mycdf->units[unit].blocks[i].name); */

    findStartsWith(infile,"BlockNumber",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->units[unit].blocks[i].blocknumber = atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);
    /*  Rprintf("%d %d %d\n",unit,i,mycdf->header.numberofunits); */

    findStartsWith(infile,"NumAtoms",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->units[unit].blocks[i].num_atoms = atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);

    findStartsWith(infile,"NumCells",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->units[unit].blocks[i].num_cells = atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);

    findStartsWith(infile,"StartPosition",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->units[unit].blocks[i].start_position = atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);

    findStartsWith(infile,"StopPosition",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->units[unit].blocks[i].stop_position = atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);

    if (mycdf->units[unit].unit_type == 2){
      findStartsWith(infile,"Direction",linebuffer);
      cur_tokenset = tokenize(linebuffer,"=");
      mycdf->units[unit].blocks[i].direction = atoi(get_token(cur_tokenset,1));
      delete_tokens(cur_tokenset);
    } else {
      mycdf->units[unit].blocks[i].direction = mycdf->units[unit].direction;
    }

    mycdf->units[unit].blocks[i].probes = Calloc(mycdf->units[unit].blocks[i].num_cells,cdf_text_unit_block_probe);

    read_cdf_unit_block_probes(infile,mycdf,linebuffer,unit,i);

  }
}

/*******************************************************************
 **
 ** void read_cdf_Units(FILE *infile,  cdf_text *mycdf, char* linebuffer)
 **
 **  FILE *infile - an opened CDF file
 **  cdf_text *mycdf - a structure for holding cdf file
 **  char *linebuffer - temporary place to store lines of text read in from the file
 **
 ** Reads in all the units allocating the space for them and then calling sub functions
 ** to read each block and probes within the blocks
 **
 *******************************************************************/

static void read_cdf_Units(FILE *infile,  cdf_text *mycdf, char* linebuffer){
  tokenset *cur_tokenset;
  int i;

  mycdf->units = Calloc(mycdf->header.numberofunits,cdf_text_unit);

  for (i =0; i < mycdf->header.numberofunits; i++){
    /* move to the next Unit section */
    AdvanceToSection(infile,"[Unit",linebuffer);
    findStartsWith(infile,"Name",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=\r\n");
    mycdf->units[i].name = Calloc(strlen(get_token(cur_tokenset,1))+1,char);
    strcpy(mycdf->units[i].name,get_token(cur_tokenset,1));

    delete_tokens(cur_tokenset);

    findStartsWith(infile,"Direction",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->units[i].direction = atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);

    findStartsWith(infile,"NumAtoms",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->units[i].num_atoms = atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);

    findStartsWith(infile,"NumCells",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->units[i].num_cells = atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);

    findStartsWith(infile,"UnitNumber",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->units[i].unit_number = atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);

    findStartsWith(infile,"UnitType",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->units[i].unit_type = atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);

    findStartsWith(infile,"NumberBlocks",linebuffer);
    cur_tokenset = tokenize(linebuffer,"=");
    mycdf->units[i].numberblocks = atoi(get_token(cur_tokenset,1));
    delete_tokens(cur_tokenset);

    /*Skip MutationType since only appears on one type of array */

    mycdf->units[i].blocks = Calloc(mycdf->units[i].numberblocks,cdf_text_unit_block);

    read_cdf_unit_block(infile,mycdf,linebuffer,i);
    /*    AdvanceToSection(infile,"[Unit",linebuffer);
	  Rprintf("%d\n",i); */
  }

}

/*******************************************************************
 **
 ** int read_cdf_text(const char *filename, cdf_text *mycdf)
 **
 ** const char *filename - name of text file
 ** cdf_text *mycdf - pointer to root of structure that will contain
 **                   the contents of the CDF file at the conclusion
 **                   of the function.
 **
 ** RETURNS 0 if the function failed, otherwise returns 1
 **
 ** this function reads a text CDF file into C data structure.
 **
 *******************************************************************/

int read_cdf_text(const char *filename, cdf_text *mycdf){

  FILE *infile;

  char linebuffer[BUFFER_SIZE];  /* a character buffer */
  tokenset *cur_tokenset;

  if ((infile = fopen(filename, "r")) == NULL)
    {
      error("Unable to open the file %s",filename);
      return 0;
    }

  /* Check that is is a text CDF file */
  ReadFileLine(linebuffer, BUFFER_SIZE, infile);
  if (strncmp("[CDF]", linebuffer, 5) != 0){
      error("The file %s does not look like a text CDF file",filename);
  }

  /* Read the version number */
  ReadFileLine(linebuffer, BUFFER_SIZE, infile);

  cur_tokenset = tokenize(linebuffer,"=\r\n");
  if (strncmp("GC3.0", get_token(cur_tokenset,1), 5) != 0){
    error("The file %s does not look like a version GC3.0 CDF file",filename);
  } else {
    mycdf->header.version = Calloc(strlen(get_token(cur_tokenset,1))+1,char);
    strcpy(mycdf->header.version,get_token(cur_tokenset,1));
  }
  delete_tokens(cur_tokenset);

  read_cdf_header(infile,mycdf,linebuffer);
  read_cdf_QCUnits(infile,mycdf,linebuffer);
  read_cdf_Units(infile,mycdf,linebuffer);

  return 1;
}

/*******************************************************************
 **
 ** void dealloc_cdf_text(cdf_text *my_cdf)
 **
 ** Frees memory allocated
 **
 ******************************************************************/

void dealloc_cdf_text(cdf_text *my_cdf){
  int i,j,k;

   Free(my_cdf->header.version);
   Free(my_cdf->header.name);
   if (my_cdf->header.chipreference != NULL)
     Free(my_cdf->header.chipreference);

   for (i =0; i <  my_cdf->header.NumQCUnits; i++){
     for (j=0; j < my_cdf->qc_units[i].n_probes; j++){
       Free(my_cdf->qc_units[i].qc_probes[j].probe);
     }
     Free(my_cdf->qc_units[i].qc_probes);
   }

   for (i =0; i <  my_cdf->header.numberofunits; i++){
     for (j=0; j < my_cdf->units[i].numberblocks; j++){
       for (k=0; k < my_cdf->units[i].blocks[j].num_cells;k++){
	 Free(my_cdf->units[i].blocks[j].probes[k].probe);
	 Free(my_cdf->units[i].blocks[j].probes[k].feat);
	 Free(my_cdf->units[i].blocks[j].probes[k].qual);
	 Free(my_cdf->units[i].blocks[j].probes[k].cbase);
	 Free(my_cdf->units[i].blocks[j].probes[k].pbase);
	 Free(my_cdf->units[i].blocks[j].probes[k].tbase);
       }
       Free(my_cdf->units[i].blocks[j].probes);
       Free(my_cdf->units[i].blocks[j].name);
     }
     Free(my_cdf->units[i].blocks);
     Free(my_cdf->units[i].name);
   }

}

/*******************************************************************
 **
 ** static int isTextCDFFile(const char *filename)
 **
 ** const char *filename - name of file to check
 **
 ** checks whether the supplied file is a text CDF file or not.
 ** uses a very simple test.
 **
 ** Attempts to open the supplied filename. Then checks to see if the first
 ** 5 characters are "[CDF]" if so returns 1, otherwise 0.
 **
 **
 ******************************************************************/

int isTextCDFFile(const char *filename){

  FILE *infile;

  char linebuffer[BUFFER_SIZE];  /* a character buffer */

  if ((infile = fopen(filename, "r")) == NULL)
    {
      error("Unable to open the file %s",filename);
    }

  /* Check that is is a text CDF file */
  ReadFileLine(linebuffer, BUFFER_SIZE, infile);
  if (strncmp("[CDF]", linebuffer, 5) == 0){
    fclose(infile);
    return 1;
  }
  fclose(infile);
  return 0;
}

/*******************************************************************
 **
 ** SEXP ReadtextCDFFileIntoRList(SEXP filename)
 **
 ** SEXP filename - name of cdffile. Should be full path to file.
 **
 ** this function should be called from R. When supplied the name
 ** of a text cdf file it first parses it into a C data structure.
 **
 ** An R list structure is then constructed from the C data structure
 **
 ** The R list is then returned.
 **
 ** Note no special effort is made to reduce down the information in
 ** the text CDF file. Instead almost everything is returned, even
 ** somewhat redundant information.
 **
 ******************************************************************/

SEXP ReadtextCDFFileIntoRList(SEXP filename){

  SEXP CDFInfo;  /* this is the object that will be returned */
  SEXP CDFInfoNames;
  SEXP HEADER;  /* The file header */
  SEXP HEADERNames;
  SEXP TEMPSXP;
  SEXP TEMPSXP2;
  SEXP TEMPSXP3;
  SEXP TEMPSXP4;

  SEXP QCUNITS;
  SEXP UNITS;

  /* Basically fields (possible) for QC probes */
  SEXP QCUNITSProbeInfoX;
  SEXP QCUNITSProbeInfoY;
  SEXP QCUNITSProbeInfoPROBE;
  SEXP QCUNITSProbeInfoPL;
  SEXP QCUNITSProbeInfoATOM;
  SEXP QCUNITSProbeInfoINDEX;
  SEXP QCUNITSProbeInfoPMFLAG;
  SEXP QCUNITSProbeInfoBGFLAG;
  SEXP QCUNITSProbeInfoNames = R_NilValue;
  SEXP QCUNITSProbeInforow_names;

  /* Basically fields (possible) for  Unit Block probes */

  SEXP UNITSProbeInfoX;
  SEXP UNITSProbeInfoY;
  SEXP UNITSProbeInfoPROBE;
  SEXP UNITSProbeInfoFEAT;
  SEXP UNITSProbeInfoQUAL;
  SEXP UNITSProbeInfoEXPOS;
  SEXP UNITSProbeInfoPOS;
  SEXP UNITSProbeInfoCBASE;
  SEXP UNITSProbeInfoPBASE;
  SEXP UNITSProbeInfoTBASE;
  SEXP UNITSProbeInfoATOM;
  SEXP UNITSProbeInfoINDEX;
  SEXP UNITSProbeInfoCODONIND;
  SEXP UNITSProbeInfoCODON;
  SEXP UNITSProbeInfoREGIONTYPE;
  SEXP UNITSProbeInfoNames;
  SEXP UNITSProbeInforow_names;

  char buf[10]; /* temporary buffer for making names */
  int i,j,k,l;
  int tmpsum =0;

  cdf_text my_cdf;

  const char *cur_file_name;
  cur_file_name = CHAR(STRING_ELT(filename,0));

  if(!read_cdf_text(cur_file_name, &my_cdf)){
    error("Problem reading text cdf file %s. Possibly corrupted or truncated?\n",cur_file_name);
  }

  /* Now build the R list structure */

   /* return the full structure */
  PROTECT(CDFInfo = allocVector(VECSXP,3));
  PROTECT(CDFInfoNames = allocVector(STRSXP,3));
  SET_STRING_ELT(CDFInfoNames,0,mkChar("Chip"));
  SET_STRING_ELT(CDFInfoNames,1,mkChar("QC"));
  SET_STRING_ELT(CDFInfoNames,2,mkChar("Unit"));

  setAttrib(CDFInfo,R_NamesSymbol,CDFInfoNames);
  UNPROTECT(1);

  /* Deal with the HEADER */
  PROTECT(HEADER = allocVector(VECSXP,8));
  PROTECT(HEADERNames = allocVector(STRSXP,8));
  SET_STRING_ELT(HEADERNames,0,mkChar("Version"));
  SET_STRING_ELT(HEADERNames,1,mkChar("Name"));
  SET_STRING_ELT(HEADERNames,2,mkChar("Rows"));
  SET_STRING_ELT(HEADERNames,3,mkChar("Cols"));
  SET_STRING_ELT(HEADERNames,4,mkChar("NumberOfUnits"));
  SET_STRING_ELT(HEADERNames,5,mkChar("MaxUnit"));
  SET_STRING_ELT(HEADERNames,6,mkChar("NumQCUnits"));
  SET_STRING_ELT(HEADERNames,7,mkChar("ChipReference"));
  setAttrib(HEADER,R_NamesSymbol,HEADERNames);
  UNPROTECT(1);

  PROTECT(TEMPSXP = allocVector(STRSXP,1));
  SET_STRING_ELT(TEMPSXP,0,mkChar(my_cdf.header.version));
  SET_VECTOR_ELT(HEADER,0,TEMPSXP);
  UNPROTECT(1);

  PROTECT(TEMPSXP = allocVector(STRSXP,1));
  SET_STRING_ELT(TEMPSXP,0,mkChar(my_cdf.header.name));
  SET_VECTOR_ELT(HEADER,1,TEMPSXP);
  UNPROTECT(1);

  PROTECT(TEMPSXP = allocVector(REALSXP,1));
  NUMERIC_POINTER(TEMPSXP)[0] = (double)my_cdf.header.rows;
  SET_VECTOR_ELT(HEADER,2,TEMPSXP);
  UNPROTECT(1);

  PROTECT(TEMPSXP = allocVector(REALSXP,1));
  NUMERIC_POINTER(TEMPSXP)[0] = (double)my_cdf.header.cols;
  SET_VECTOR_ELT(HEADER,3,TEMPSXP);
  UNPROTECT(1);

  PROTECT(TEMPSXP = allocVector(REALSXP,1));
  NUMERIC_POINTER(TEMPSXP)[0] = (double)my_cdf.header.numberofunits;
  SET_VECTOR_ELT(HEADER,4,TEMPSXP);
  UNPROTECT(1);

  PROTECT(TEMPSXP = allocVector(REALSXP,1));
  NUMERIC_POINTER(TEMPSXP)[0] = (double)my_cdf.header.maxunit;
  SET_VECTOR_ELT(HEADER,5,TEMPSXP);
  UNPROTECT(1);

  PROTECT(TEMPSXP = allocVector(REALSXP,1));
  NUMERIC_POINTER(TEMPSXP)[0] = (double)my_cdf.header.NumQCUnits;
  SET_VECTOR_ELT(HEADER,6,TEMPSXP);
  UNPROTECT(1);

  PROTECT(TEMPSXP = allocVector(REALSXP,1));
  if (my_cdf.header.chipreference !=NULL){
    SET_VECTOR_ELT(TEMPSXP,0,mkChar(my_cdf.header.chipreference));
    SET_VECTOR_ELT(HEADER,7,TEMPSXP);
  }
  UNPROTECT(1);

  SET_VECTOR_ELT(CDFInfo,0,HEADER);

  PROTECT(QCUNITS = allocVector(VECSXP,my_cdf.header.NumQCUnits));
  for (i=0; i <my_cdf.header.NumQCUnits; i++){
    PROTECT(TEMPSXP=allocVector(VECSXP,3));
    PROTECT(TEMPSXP2 = allocVector(REALSXP,1));
    NUMERIC_POINTER(TEMPSXP2)[0] = (double)my_cdf.qc_units[i].type;
    SET_VECTOR_ELT(TEMPSXP,0,TEMPSXP2);
    UNPROTECT(1);
    PROTECT(TEMPSXP2 = allocVector(REALSXP,1));
    NUMERIC_POINTER(TEMPSXP2)[0] = (double)my_cdf.qc_units[i].n_probes;
    SET_VECTOR_ELT(TEMPSXP,1,TEMPSXP2);
    UNPROTECT(1);
    /*Figure out what the probe information is for this QC unit and then allocate the space */
    tmpsum=0;
    for (j=0; j < 8; j++){
      tmpsum+=my_cdf.qc_units[i].qccontains[j];
    }

    if (tmpsum == 6){
      PROTECT(TEMPSXP2 = allocVector(VECSXP,6));
    } else if (tmpsum ==8){
      PROTECT(TEMPSXP2 = allocVector(VECSXP,8));
    }

    if (tmpsum == 6){
      PROTECT(QCUNITSProbeInfoX = allocVector(INTSXP,my_cdf.qc_units[i].n_probes));
      PROTECT(QCUNITSProbeInfoY = allocVector(INTSXP,my_cdf.qc_units[i].n_probes));
      PROTECT(QCUNITSProbeInfoPROBE = allocVector(STRSXP,my_cdf.qc_units[i].n_probes));
      PROTECT(QCUNITSProbeInfoPL = allocVector(INTSXP,my_cdf.qc_units[i].n_probes));
      PROTECT(QCUNITSProbeInfoATOM = allocVector(INTSXP,my_cdf.qc_units[i].n_probes));
      PROTECT(QCUNITSProbeInfoINDEX = allocVector(INTSXP,my_cdf.qc_units[i].n_probes));
    } else if (tmpsum == 8){
      PROTECT(QCUNITSProbeInfoX = allocVector(INTSXP,my_cdf.qc_units[i].n_probes));
      PROTECT(QCUNITSProbeInfoY = allocVector(INTSXP,my_cdf.qc_units[i].n_probes));
      PROTECT(QCUNITSProbeInfoPROBE = allocVector(STRSXP,my_cdf.qc_units[i].n_probes));
      PROTECT(QCUNITSProbeInfoPL = allocVector(INTSXP,my_cdf.qc_units[i].n_probes));
      PROTECT(QCUNITSProbeInfoATOM = allocVector(INTSXP,my_cdf.qc_units[i].n_probes));
      PROTECT(QCUNITSProbeInfoINDEX = allocVector(INTSXP,my_cdf.qc_units[i].n_probes));
      PROTECT(QCUNITSProbeInfoPMFLAG = allocVector(INTSXP,my_cdf.qc_units[i].n_probes));
      PROTECT(QCUNITSProbeInfoBGFLAG = allocVector(INTSXP,my_cdf.qc_units[i].n_probes));

    }

    /* store what was read in for the QC units in the R structure */
    if (tmpsum == 6){
      for (j =0; j < my_cdf.qc_units[i].n_probes; j++){
	INTEGER_POINTER(QCUNITSProbeInfoX)[j] = my_cdf.qc_units[i].qc_probes[j].x;
	INTEGER_POINTER(QCUNITSProbeInfoY)[j] = my_cdf.qc_units[i].qc_probes[j].y;
	SET_STRING_ELT(QCUNITSProbeInfoPROBE,j,mkChar( my_cdf.qc_units[i].qc_probes[j].probe));
	INTEGER_POINTER(QCUNITSProbeInfoPL)[j] = my_cdf.qc_units[i].qc_probes[j].plen;
	INTEGER_POINTER(QCUNITSProbeInfoATOM)[j] = my_cdf.qc_units[i].qc_probes[j].atom;
	INTEGER_POINTER(QCUNITSProbeInfoINDEX)[j] = my_cdf.qc_units[i].qc_probes[j].index;

      }
    } else if (tmpsum == 8){
      for (j =0; j < my_cdf.qc_units[i].n_probes; j++){
	INTEGER_POINTER(QCUNITSProbeInfoX)[j] = my_cdf.qc_units[i].qc_probes[j].x;
	INTEGER_POINTER(QCUNITSProbeInfoY)[j] = my_cdf.qc_units[i].qc_probes[j].y;
	SET_STRING_ELT(QCUNITSProbeInfoPROBE,j,mkChar( my_cdf.qc_units[i].qc_probes[j].probe));
	INTEGER_POINTER(QCUNITSProbeInfoPL)[j] = my_cdf.qc_units[i].qc_probes[j].plen;
	INTEGER_POINTER(QCUNITSProbeInfoATOM)[j] = my_cdf.qc_units[i].qc_probes[j].atom;
	INTEGER_POINTER(QCUNITSProbeInfoINDEX)[j] = my_cdf.qc_units[i].qc_probes[j].index;
	INTEGER_POINTER(QCUNITSProbeInfoPMFLAG)[j] = my_cdf.qc_units[i].qc_probes[j].match;
	INTEGER_POINTER(QCUNITSProbeInfoBGFLAG)[j] = my_cdf.qc_units[i].qc_probes[j].bg;
      }
    }

    if (tmpsum == 6){
      SET_VECTOR_ELT(TEMPSXP2,0,QCUNITSProbeInfoX);
      SET_VECTOR_ELT(TEMPSXP2,1,QCUNITSProbeInfoY);
      SET_VECTOR_ELT(TEMPSXP2,2,QCUNITSProbeInfoPROBE);
      SET_VECTOR_ELT(TEMPSXP2,3,QCUNITSProbeInfoPL);
      SET_VECTOR_ELT(TEMPSXP2,4,QCUNITSProbeInfoATOM);
      SET_VECTOR_ELT(TEMPSXP2,5,QCUNITSProbeInfoINDEX);
    } else if (tmpsum ==8){
      SET_VECTOR_ELT(TEMPSXP2,0,QCUNITSProbeInfoX);
      SET_VECTOR_ELT(TEMPSXP2,1,QCUNITSProbeInfoY);
      SET_VECTOR_ELT(TEMPSXP2,2,QCUNITSProbeInfoPROBE);
      SET_VECTOR_ELT(TEMPSXP2,3,QCUNITSProbeInfoPL);
      SET_VECTOR_ELT(TEMPSXP2,4,QCUNITSProbeInfoATOM);
      SET_VECTOR_ELT(TEMPSXP2,5,QCUNITSProbeInfoINDEX);
      SET_VECTOR_ELT(TEMPSXP2,6,QCUNITSProbeInfoPMFLAG);
      SET_VECTOR_ELT(TEMPSXP2,7,QCUNITSProbeInfoBGFLAG);

    }

    if (tmpsum == 6){
      UNPROTECT(6);
    } else if (tmpsum == 8){
      UNPROTECT(8);
    }

    if (tmpsum == 6){
      PROTECT(QCUNITSProbeInfoNames = allocVector(STRSXP,6));
      SET_STRING_ELT(QCUNITSProbeInfoNames,0,mkChar("x"));
      SET_STRING_ELT(QCUNITSProbeInfoNames,1,mkChar("y"));
      SET_STRING_ELT(QCUNITSProbeInfoNames,2,mkChar("Probe"));
      SET_STRING_ELT(QCUNITSProbeInfoNames,3,mkChar("ProbeLength"));
      SET_STRING_ELT(QCUNITSProbeInfoNames,4,mkChar("Atom"));
      SET_STRING_ELT(QCUNITSProbeInfoNames,5,mkChar("Index"));
    } else if (tmpsum == 8){
      PROTECT(QCUNITSProbeInfoNames = allocVector(STRSXP,8));
      SET_STRING_ELT(QCUNITSProbeInfoNames,0,mkChar("x"));
      SET_STRING_ELT(QCUNITSProbeInfoNames,1,mkChar("y"));
      SET_STRING_ELT(QCUNITSProbeInfoNames,2,mkChar("Probe"));
      SET_STRING_ELT(QCUNITSProbeInfoNames,3,mkChar("ProbeLength"));
      SET_STRING_ELT(QCUNITSProbeInfoNames,4,mkChar("Atom"));
      SET_STRING_ELT(QCUNITSProbeInfoNames,5,mkChar("Index"));
      SET_STRING_ELT(QCUNITSProbeInfoNames,6,mkChar("PMFlag"));
      SET_STRING_ELT(QCUNITSProbeInfoNames,7,mkChar("BGProbeFlag"));
    }
    setAttrib(TEMPSXP2,R_NamesSymbol,QCUNITSProbeInfoNames);
    UNPROTECT(1);
    PROTECT(QCUNITSProbeInforow_names= allocVector(STRSXP,my_cdf.qc_units[i].n_probes));

    for (j=0; j < my_cdf.qc_units[i].n_probes; j++){
      sprintf(buf, "%d", j+1);
      SET_STRING_ELT(QCUNITSProbeInforow_names,j,mkChar(buf));
    }
    setAttrib(TEMPSXP2, R_RowNamesSymbol, QCUNITSProbeInforow_names);
    UNPROTECT(1);

    setAttrib(TEMPSXP2,R_ClassSymbol,mkString("data.frame"));
    SET_VECTOR_ELT(TEMPSXP,2,TEMPSXP2);
    UNPROTECT(1);

    PROTECT(TEMPSXP2=allocVector(STRSXP,3));
    SET_STRING_ELT(TEMPSXP2,0,mkChar("Type"));
    SET_STRING_ELT(TEMPSXP2,1,mkChar("NumberCells"));
    SET_STRING_ELT(TEMPSXP2,2,mkChar("QCCells"));
    setAttrib(TEMPSXP,R_NamesSymbol,TEMPSXP2);
    UNPROTECT(1);
    SET_VECTOR_ELT(QCUNITS,i,TEMPSXP);

    UNPROTECT(1);
  }
  SET_VECTOR_ELT(CDFInfo,1,QCUNITS);
  UNPROTECT(1);

  PROTECT(UNITS = allocVector(VECSXP,my_cdf.header.numberofunits));
  for (i=0; i < my_cdf.header.numberofunits; i++){
    PROTECT(TEMPSXP=allocVector(VECSXP,8));
    PROTECT(TEMPSXP2=allocVector(STRSXP,1));

    SET_STRING_ELT(TEMPSXP2,0,mkChar(my_cdf.units[i].name));
    SET_VECTOR_ELT(TEMPSXP,0,TEMPSXP2);
    UNPROTECT(1);

    PROTECT(TEMPSXP2 = allocVector(REALSXP,1));
    NUMERIC_POINTER(TEMPSXP2)[0] = (double)my_cdf.units[i].direction;
    SET_VECTOR_ELT(TEMPSXP,1,TEMPSXP2);
    UNPROTECT(1);

    PROTECT(TEMPSXP2 = allocVector(REALSXP,1));
    NUMERIC_POINTER(TEMPSXP2)[0] = (double)my_cdf.units[i].num_atoms;
    SET_VECTOR_ELT(TEMPSXP,2,TEMPSXP2);
    UNPROTECT(1);

    PROTECT(TEMPSXP2 = allocVector(REALSXP,1));
    NUMERIC_POINTER(TEMPSXP2)[0] = (double)my_cdf.units[i].num_cells;
    SET_VECTOR_ELT(TEMPSXP,3,TEMPSXP2);
    UNPROTECT(1);

    PROTECT(TEMPSXP2 = allocVector(REALSXP,1));
    NUMERIC_POINTER(TEMPSXP2)[0] = (double)my_cdf.units[i].unit_number;
    SET_VECTOR_ELT(TEMPSXP,4,TEMPSXP2);
    UNPROTECT(1);

    PROTECT(TEMPSXP2 = allocVector(REALSXP,1));
    NUMERIC_POINTER(TEMPSXP2)[0] = (double)my_cdf.units[i].unit_type;
    SET_VECTOR_ELT(TEMPSXP,5,TEMPSXP2);
    UNPROTECT(1);

    PROTECT(TEMPSXP2 = allocVector(REALSXP,1));
    NUMERIC_POINTER(TEMPSXP2)[0] = (double)my_cdf.units[i].numberblocks;
    SET_VECTOR_ELT(TEMPSXP,6,TEMPSXP2);
    UNPROTECT(1);

    PROTECT(TEMPSXP2 = allocVector(VECSXP,my_cdf.units[i].numberblocks));

    for (j=0; j <my_cdf.units[i].numberblocks; j++){
      PROTECT(TEMPSXP3 = allocVector(VECSXP,8));

      PROTECT(TEMPSXP4=allocVector(STRSXP,1));

      SET_STRING_ELT(TEMPSXP4,0,mkChar(my_cdf.units[i].blocks[j].name));
      SET_VECTOR_ELT(TEMPSXP3,0,TEMPSXP4);
      UNPROTECT(1);

      PROTECT(TEMPSXP4=allocVector(REALSXP,1));
      NUMERIC_POINTER(TEMPSXP4)[0] = (double)my_cdf.units[i].blocks[j].blocknumber;
      SET_VECTOR_ELT(TEMPSXP3,1,TEMPSXP4);
      UNPROTECT(1);

      PROTECT(TEMPSXP4=allocVector(REALSXP,1));
      NUMERIC_POINTER(TEMPSXP4)[0] = (double)my_cdf.units[i].blocks[j].num_atoms;
      SET_VECTOR_ELT(TEMPSXP3,2,TEMPSXP4);
      UNPROTECT(1);

      PROTECT(TEMPSXP4=allocVector(REALSXP,1));
      NUMERIC_POINTER(TEMPSXP4)[0] = (double)my_cdf.units[i].blocks[j].num_cells;
      SET_VECTOR_ELT(TEMPSXP3,3,TEMPSXP4);
      UNPROTECT(1);

      PROTECT(TEMPSXP4=allocVector(REALSXP,1));
      NUMERIC_POINTER(TEMPSXP4)[0] = (double)my_cdf.units[i].blocks[j].start_position;
      SET_VECTOR_ELT(TEMPSXP3,4,TEMPSXP4);
      UNPROTECT(1);

      PROTECT(TEMPSXP4=allocVector(REALSXP,1));
      NUMERIC_POINTER(TEMPSXP4)[0] = (double)my_cdf.units[i].blocks[j].stop_position;
      SET_VECTOR_ELT(TEMPSXP3,5,TEMPSXP4);
      UNPROTECT(1);

      PROTECT(TEMPSXP4=allocVector(REALSXP,1));
      NUMERIC_POINTER(TEMPSXP4)[0] = (double)my_cdf.units[i].blocks[j].direction;
      SET_VECTOR_ELT(TEMPSXP3,6,TEMPSXP4);
      UNPROTECT(1);

      PROTECT(TEMPSXP4=allocVector(VECSXP,15));

      PROTECT(UNITSProbeInfoX = allocVector(INTSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoY = allocVector(INTSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoPROBE = allocVector(STRSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoFEAT = allocVector(STRSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoQUAL = allocVector(STRSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoEXPOS = allocVector(INTSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoPOS = allocVector(INTSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoCBASE = allocVector(STRSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoPBASE = allocVector(STRSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoTBASE = allocVector(STRSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoATOM = allocVector(INTSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoINDEX = allocVector(INTSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoCODONIND = allocVector(INTSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoCODON = allocVector(INTSXP,my_cdf.units[i].blocks[j].num_cells));
      PROTECT(UNITSProbeInfoREGIONTYPE = allocVector(INTSXP,my_cdf.units[i].blocks[j].num_cells));
      for (k=0; k < my_cdf.units[i].blocks[j].num_cells; k++){
	INTEGER_POINTER(UNITSProbeInfoX)[k]=my_cdf.units[i].blocks[j].probes[k].x;
	INTEGER_POINTER(UNITSProbeInfoY)[k]=my_cdf.units[i].blocks[j].probes[k].y;
	INTEGER_POINTER(UNITSProbeInfoEXPOS)[k]=my_cdf.units[i].blocks[j].probes[k].expos;
	INTEGER_POINTER(UNITSProbeInfoPOS)[k]=my_cdf.units[i].blocks[j].probes[k].pos;
	INTEGER_POINTER(UNITSProbeInfoATOM)[k]=my_cdf.units[i].blocks[j].probes[k].atom;
	INTEGER_POINTER(UNITSProbeInfoINDEX)[k]=my_cdf.units[i].blocks[j].probes[k].index;
	INTEGER_POINTER(UNITSProbeInfoCODONIND)[k]=my_cdf.units[i].blocks[j].probes[k].codonid;
	INTEGER_POINTER(UNITSProbeInfoCODON)[k]=my_cdf.units[i].blocks[j].probes[k].codon;
	INTEGER_POINTER(UNITSProbeInfoREGIONTYPE)[k]=my_cdf.units[i].blocks[j].probes[k].regiontype;
	SET_VECTOR_ELT(UNITSProbeInfoPROBE,k,mkChar(my_cdf.units[i].blocks[j].probes[k].probe));
	SET_STRING_ELT(UNITSProbeInfoFEAT,k,mkChar(my_cdf.units[i].blocks[j].probes[k].feat));
	SET_STRING_ELT(UNITSProbeInfoQUAL,k,mkChar(my_cdf.units[i].blocks[j].probes[k].qual));
	SET_STRING_ELT(UNITSProbeInfoCBASE,k,mkChar(my_cdf.units[i].blocks[j].probes[k].cbase));
	SET_STRING_ELT(UNITSProbeInfoPBASE,k,mkChar(my_cdf.units[i].blocks[j].probes[k].pbase));
	SET_STRING_ELT(UNITSProbeInfoTBASE,k,mkChar(my_cdf.units[i].blocks[j].probes[k].tbase));
      }

      SET_VECTOR_ELT(TEMPSXP4,0,UNITSProbeInfoX);
      SET_VECTOR_ELT(TEMPSXP4,1,UNITSProbeInfoY);
      SET_VECTOR_ELT(TEMPSXP4,2,UNITSProbeInfoPROBE);
      SET_VECTOR_ELT(TEMPSXP4,3,UNITSProbeInfoFEAT);
      SET_VECTOR_ELT(TEMPSXP4,4,UNITSProbeInfoQUAL);
      SET_VECTOR_ELT(TEMPSXP4,5,UNITSProbeInfoEXPOS);
      SET_VECTOR_ELT(TEMPSXP4,6,UNITSProbeInfoPOS);
      SET_VECTOR_ELT(TEMPSXP4,7,UNITSProbeInfoCBASE);
      SET_VECTOR_ELT(TEMPSXP4,8,UNITSProbeInfoPBASE);
      SET_VECTOR_ELT(TEMPSXP4,9,UNITSProbeInfoTBASE);
      SET_VECTOR_ELT(TEMPSXP4,10,UNITSProbeInfoATOM);
      SET_VECTOR_ELT(TEMPSXP4,11,UNITSProbeInfoINDEX);
      SET_VECTOR_ELT(TEMPSXP4,12,UNITSProbeInfoCODONIND);
      SET_VECTOR_ELT(TEMPSXP4,13,UNITSProbeInfoCODON);
      SET_VECTOR_ELT(TEMPSXP4,14,UNITSProbeInfoREGIONTYPE);

      UNPROTECT(15);

      PROTECT(UNITSProbeInfoNames =allocVector(STRSXP,15));
      SET_STRING_ELT(UNITSProbeInfoNames,0,mkChar("x"));
      SET_STRING_ELT(UNITSProbeInfoNames,1,mkChar("y"));
      SET_STRING_ELT(UNITSProbeInfoNames,2,mkChar("Probe"));
      SET_STRING_ELT(UNITSProbeInfoNames,3,mkChar("Feat"));
      SET_STRING_ELT(UNITSProbeInfoNames,4,mkChar("Qual"));
      SET_STRING_ELT(UNITSProbeInfoNames,5,mkChar("Expos"));
      SET_STRING_ELT(UNITSProbeInfoNames,6,mkChar("Pos"));
      SET_STRING_ELT(UNITSProbeInfoNames,7,mkChar("cbase"));
      SET_STRING_ELT(UNITSProbeInfoNames,8,mkChar("pbase"));
      SET_STRING_ELT(UNITSProbeInfoNames,9,mkChar("tbase"));
      SET_STRING_ELT(UNITSProbeInfoNames,10,mkChar("Atom"));
      SET_STRING_ELT(UNITSProbeInfoNames,11,mkChar("Index"));
      SET_STRING_ELT(UNITSProbeInfoNames,12,mkChar("CodonInd"));
      SET_STRING_ELT(UNITSProbeInfoNames,13,mkChar("Codon"));
      SET_STRING_ELT(UNITSProbeInfoNames,14,mkChar("Regiontype"));

      setAttrib(TEMPSXP4,R_NamesSymbol,UNITSProbeInfoNames);
      UNPROTECT(1);

      PROTECT(UNITSProbeInforow_names= allocVector(STRSXP,my_cdf.units[i].blocks[j].num_cells));

      for (l=0; l < my_cdf.units[i].blocks[j].num_cells; l++){
	sprintf(buf, "%d", l+1);
	SET_STRING_ELT(UNITSProbeInforow_names,l,mkChar(buf));
      }
      setAttrib(TEMPSXP4, R_RowNamesSymbol, UNITSProbeInforow_names);
      UNPROTECT(1);

      setAttrib(TEMPSXP4,R_ClassSymbol,mkString("data.frame"));
      SET_VECTOR_ELT(TEMPSXP3,7,TEMPSXP4);
      UNPROTECT(1);

      PROTECT(TEMPSXP4=allocVector(STRSXP,8));
      SET_STRING_ELT(TEMPSXP4,0,mkChar("Name"));
      SET_STRING_ELT(TEMPSXP4,1,mkChar("BlockNumber"));
      SET_STRING_ELT(TEMPSXP4,2,mkChar("NumAtoms"));
      SET_STRING_ELT(TEMPSXP4,3,mkChar("NumCells"));
      SET_STRING_ELT(TEMPSXP4,4,mkChar("StartPosition"));
      SET_STRING_ELT(TEMPSXP4,5,mkChar("StopPosition"));
      SET_STRING_ELT(TEMPSXP4,6,mkChar("Direction"));
      SET_STRING_ELT(TEMPSXP4,7,mkChar("Unit_Block_Cells"));
      setAttrib(TEMPSXP3,R_NamesSymbol,TEMPSXP4);
      UNPROTECT(1);

      SET_VECTOR_ELT(TEMPSXP2,j,TEMPSXP3);
      UNPROTECT(1);
    }

    SET_VECTOR_ELT(TEMPSXP,7,TEMPSXP2);
    UNPROTECT(1);

    PROTECT(TEMPSXP2 = allocVector(STRSXP,8));
    SET_STRING_ELT(TEMPSXP2,0,mkChar("Name"));
    SET_STRING_ELT(TEMPSXP2,1,mkChar("Direction"));
    SET_STRING_ELT(TEMPSXP2,2,mkChar("NumAtoms"));
    SET_STRING_ELT(TEMPSXP2,3,mkChar("NumCells"));
    SET_STRING_ELT(TEMPSXP2,4,mkChar("UnitNumber"));
    SET_STRING_ELT(TEMPSXP2,5,mkChar("UnitType"));
    SET_STRING_ELT(TEMPSXP2,6,mkChar("NumberBlocks"));
    SET_STRING_ELT(TEMPSXP2,7,mkChar("Unit_Block"));
    setAttrib(TEMPSXP,R_NamesSymbol,TEMPSXP2);
    UNPROTECT(1);

    SET_VECTOR_ELT(UNITS,i,TEMPSXP);
    UNPROTECT(1);

  }
  SET_VECTOR_ELT(CDFInfo,2,UNITS);
  UNPROTECT(1);

  dealloc_cdf_text(&my_cdf);
  UNPROTECT(2);
  return CDFInfo;
}

/*************************************************************
 **
 ** SEXP CheckCDFtext(SEXP filename)
 **
 ** Takes a given file name and returns 1 if it is a text format CDF file
 ** otherwise it returns 0
 **
 *************************************************************/

SEXP CheckCDFtext(SEXP filename){
  SEXP tmp;
  int good;
  const char *cur_file_name;

  cur_file_name = CHAR(STRING_ELT(filename,0));

  good = isTextCDFFile(cur_file_name);

  PROTECT(tmp= allocVector(INTSXP,1));

  INTEGER(tmp)[0] = good;

  UNPROTECT(1);
  return tmp;
}

redpajama_set_name: RedPajamaGithub


text:
Niching Your Business With Bryn Harwood
Category: Business Tips | Dorks Delivered
What is a business niche? This is a focused area of a broader market that all kind of business can serve to differentiate themselves from the competition. As a business owner, you should find a niche in the industry that has underserved needs. Few of the strategies you can consider are: selecting your target audience, defining the unmet needs of a particular industry, researching your customer database and creating a business plan. Whether you've been handling business for a while or you just started managing one, you mustn't be only thinking about creating a steady stream of your revenue, but you also need to be aware on how to establish a loyal audience. A solid market of a particular group of customers who will buy from your business will keep you ahead of the competition. In this episode, Bryn Harwood shares his insights about niching your business.
Josh: Who out there in podcast land has been told to niche or even micro-niche. It's something that we have drilled into ourselves, you need to do this thing. And I've actually got a special guest, I've Bryn Harwood from Tradies accountants in Brisbane. And he's gonna be talking about the process and some of the past, where he's been and where he's gone and what the future looks like. So, Bryn, tell me a bit about where you came from? Like, what was your original business venture?
Bryn: Thanks for that, Joshua. Well, I started out as that generalist kind of accountant, so you know, suburban kind of firm doing 30 different industries and doing tax returns, doing company tax returns, doing a whole bunch of different kinds of things. And then one stage through the career I decided that it's kind of hard trying to really understand every different industry because you already know so much.
So when you're in general, it's really hard to give advice, specific advice to business owners because you kind of, there's only one of you. So what I decided was I was going to niche out. I looked at my database, and I had a fair few trade business owners. So I had some kind of builders, plumbers, electricians, and I liked working with those owners that had good businesses. So, at first, I thought, well, for marketing, what I'll do is I'll name it trader's accountant, but it was still part of my original firm. So that's kind of the premise on how I started the idea of niching and how I kind of got into it.
Josh: Okay, so it's kind of like a bit of a sub-brand underneath the original umbrella. And then you had the, I guess already clients are already resonating with that brand. And naturally, you'd have to learn more about what they're doing and how their business works and the different tax advantages and equipment finance things that they're doing and whatever else, and that just allows you to build out the brand from there. Is that right?
Bryn: Yeah, exactly. I started with the sub-brand, as you said, and I actually labelled it -and this isn't a joke- I labelled it GFC Tradies Accountant. And that's not a joke. That's how I started. Obviously, I didn't consult too many marketers, I just came up with it, maybe a few too many beers. And then everybody said to me, why are we putting GFC in front of these? Like, what are you trying to tell us?
Josh: Fortune tellers, not accountant at all.
Bryn: Exactly. It wasn't a great marketing pitch. But to be serious with it. What I realized really fast was that it's more than just marketing. And I think if you're going to jump into a niche, from my experience, you have to jump fully into the niche. Try to hold on to your original database and then have a separate database. From my experience, and other people might be able to do it differently and it might work, but from my experience, it didn't work. And the reason for that is your kind of alienate your older clients. And then the new clients are kind of wondering why they're the older clients, so why they're the other brands. So, I found a real disconnect. And it's almost like you've got to reach that moment where you're confident enough to say, I'm going to fully jump into this niche, and that's all I'm going to do. Which is the hard step because if you've got 100 clients on your books, you might have 30 clients that have one industry, and 70 that are in another industry that you still make a reasonable revenue. But for my experience, if you want to go into it, you need to get rid of the other 70, otherwise, it's just really a marketing campaign and you can probably just create a landing page, and do that if that's what you want to do.
IT doesn't have to be expensive. If you're starting a business and need reliable IT support and services, fill out the form to learn more about $1 IT services.
Josh: So, we haven't, I guess niched the same way you have, but I understand exactly what you're saying. So when we started our business back 13 years ago, we were the IT company that did everything. You call us up, there's a problem with the VCR. Well, I can fix that for you. I'm not even kidding. I was fixing people's VCRs at that stage, not really the same task to fix someone's TV once and that was going back in the days. It was a big cathode ray tube. So your big fat TV. So I'm always the person who did everything that knew nothing, I guess, or knew something but not enough about everything. Then as things progressed onwards, I saw a bit more of a trend and Dorks Delivered, the original brand as it started, became an IT business. And then we had business efficiency experts that did all the automation stuff. And then we had asked about marketing because it became too cloudy to see what it was that we're actually doing. And someone came to us and they said, we want to have our business marketed online. We want to have our LinkedIn marketing automated or something like that. And we'd go, yeah, okay, we can do that. And they were like, oh, we had no idea. You could do the names Dorks Delivered, and sound anything like marketing at all, and I'm like, Yeah, okay, we got to sort that out.
There's definitely like you're saying about the GFC. I'm like, I can't bring up the business name of someone that I know that just changed business names, but if they move the space character, just one spot, it sounds like a type of operation that you get to remove puss from your body as opposed to an awesome IT business. In making that transition, you said 30% of your customers were sitting in the trading sub-business banner. When you made the transition to jump out on your own as a trader's accountant, did you find that there was less friction when it came to marketing, and the overall message was more easily heard because the tradie would come to you going I know you know what I need to have done?
Bryn: Exactly right. I think that's probably the main reason you'd go to a niche is for that. But what I've learned is we've hired, you know, some really exceptional chartered accountants, and some really exceptional team members. And when you're just in one industry, you realise that the advice that you can start to give is actually really invaluable, you know, so we have knowledge on VBA. We have knowledge on QBCC, these are regulators that work in the trade space. We've worked with associations like the Australian Shopfitters Association, and through that, we've worked with a fair few different shopfitters where now we're at the point where we can quickly look at a set of financials and see what's wrong with the financials, like quickly see what's wrong with the business. So we can understand, you know, things like contractors and the business models.
If you need assistance with your automation, talk to a dork. Fill out the form below to book a free one-hour consultation.
And every industry is different. You know, we've had people come to us that, you know, wanted to set up a medical practice or that were in different industries, and we actually tell them when. So I've actually referred them to other accountants and regularly do that now, as I will regularly say, we're probably not the right fit. And the reason is, is because what we do do, we do extremely well. And that's the only thing we do.
Josh: Yep. And I think like, the big take-home there for me is when someone has something wrong with their books, you can see what's wrong. You can see if they're spending 20%, 30%, 50% higher on their staffing costs, and they're spending 50% less in their marketing costs, and they're wondering about where their money is going. You could probably more easily drill down and say, hey, you need to sort
of maybe consider taking a course doing this and dropping down some of these people's pays. Why are they getting paid so much? Is that a discussion that you could have?
Bryn: 100%. It's like, we can look at it and say, look, these are the risks. You know, if you're in the trade space, and you set up like this, this is a risk down the track, these are the regulators in that space. These are where the risks are of your company. This is the way you'd want to set your company up if you're doing XYZ, and then also just, you know, look over the financials and kind of say, look, you know, charge-out rates for these, you're probably losing money there, you're probably not as efficient as you should be here. You know, your GPs is other people in your field. So, you know, we have that real specialised knowledge now, and I mean, it's only probably been three to four years, but that's only going to increase because every time we hire people, they're only dealing with shopfitters, builders, contractors, that's all they're dealing with. So we know that industry inside out. And I think that is a big advantage with doing all these that I've done.
I've talked to different business owners that have been worried about niching because I've thought, you know, then I'm going to be reducing obviously my size, like my client database, like it's going to go smaller. But I've always had the philosophy that you almost go inch wide, but a mile deep. So no, we have clients from Melbourne, I did a webinar the other day, I had clients from Western Australia on there, clients in Sydney. So I believe that actually the trade business owners in Australia quite large, and that'll haven't actually reduced my overall client. I've increased it. But you've just got to think, I think pf it actually.
In saying that, I'm not saying that everybody needs to niche. I've got some good friends that are partners and other accounting firms. And that said, why does every guru say we've got a niche, we don't have to, we can be accountants, and I think that's fine. I think there's a space for generalist accountants, and they do a great job. And you know, if their job is to do tax returns and financial statements, that's what their job is. And I think that's fantastic. I wouldn't encourage everybody to go and say, I'm going to go niche, look at my database and do it because I don't necessarily think that it's necessary to grow your business. It's probably looking at your capabilities within and saying, you know, what can I focus on? What can I serve as externally?
Josh: Yeah, and it comes down to what your end goal is, I guess. You can own a fish and chip shop and make a lot of money, and you can own McDonald's and make a lot of money, but they're very different things that the business owner is undertaking. One's buying a job, and one's buying an investment, it depends on what you're looking to be doing. Buying a job isn't a bad thing if that's what you want to do if you want to have that flexibility.
Bryn: 100%. I speak to clients, you know, and that's one of the first things I say, what do you want? What do you want to get out of your business? Like some clients want us to work on the tools so I don't try and work clients off the tools if they don't want to do that, they might want to work on the tools, ran a crew of five people, you know, turn over a million dollars and have a couple of hundred grand profit. I don't believe the guru's and I don't listen to a lot of influences and say you know, niching is the only way to go for accounts and you need to have your own niche. I don't think you do need to have your own niche, but my advice on niching would be or from my experience, my experience share would be if you're going to do it, jump in 100%, don't put your toes in learn the niche, speak to the clients and then start to get that extra skill. And it's not a quick process, you're not going to do it in six months, and then say I'm a medical expert now and I know everything about dentists. It might take you four years until you really start to cut your teeth on it and really understand the industry. So you need to be patient.
Josh: Have you heard of the Dunning Kruger effect?
Bryn: No.
Josh: It's a cognitive bias. It's where you start doing something. Okay, for instance, we'll talk about beer brewing offline beforehand. You might brew one beer and be like, I'm a brewmaster. I'm amazing. I can do everything. I know everything, and then you bring your second beer and go, oh, that one's stuffed up for some reason. And then you brew your third beer and you go, okay, now one was alright, maybe I'm going ok again and then you start mucking around with hops, and then you start doing other bits and pieces that didn't work or that did. Then all of a sudden you realise it's a really, really deep subject. As you said, inch wide, mile deep. If you don't know how deep something is, the Dunning Kruger effect is this feeling that you've got you've… a false feeling that you know everything about something. And interestingly, when you actually do know everything about something or close to it, you have this lack of confidence around the topic because you know how deep it's gone. And that's exactly right. So when you start jumping in there and then being able to answer those questions that would, again, remove a lot of friction from sales. because somebody's like, oh, what would you do with XYZ? And you've got bang the answer straightaway for them.
Do you need help with backup and recovery? Fill out the form to get in touch with a Dork.
Bryn: 100%. And like, I'm still talking to business owners on things, like I'm still sitting in a board meeting or sitting with a builder. And they're saying, actually, we do this because of this, and this is how this happens. And I go, oh, wow, I didn't know that you know. I find myself sitting outside in shopfitters presentations with you know, Pythor or one of the products that I use and go oh, wow, that product's amazing, that actually shows the shop before they're built, I never knew that you could do that. So you know, even I've been in use for a while I think continually learning and sharpening that industry that you're in.
Josh: Getting that industry knowledge, though, as well like being told about this new cool thing you think why is no one else told me this, it gives you a fantastic piece of information, a nugget of information that you can then talk to any of your other clients about, then you become that authority.
Bryn: That's what it is. Like, I sat with a client the other day, and they were using a kind of generalist accountant and he sat with me for 30 minutes. And say I'm really happy with my accountant. But essentially, in 30 minutes, you've told me more than I kind of knew about the accounting, and he has a general accounting team. But he said, in 30 minutes, you've told me more than I knew over the last year and a half. And that doesn't mean I was going to change him. But it was just the fact that actually knowing the industry, knowing the software's he needs to use, knowing where the pain points are going to be. And knowing his next steps, as you know, if you only go to 10, these are steps. If you want to go to 20, these are the pain points you'll have. That's the advantage you'll get from niching, but you have to be patient, it will take time to get there. And the marketing around it isn't instantaneous, like if you just go out and say I'm the medical doctor, no medical accountant. I mean, there are a lot of people in the niche space and a lot of the big firms have their own specialists in different industries as well. So there is competition, a lot of competition, and probably the same with IT. I mean, I've heard there are IT providers that just do pubs and there are IT providers that just do retail, which probably isn't a good space to be in right now, unfortunately.
Josh: A great segue actually. Niching, luckily, I'm not in a single niche, but we do have a lot of shopfitters, and obviously they rely heavily upon the retail industry in the growth in the retail industry. And we have automotive businesses that we work with and we have people in the financial sector, so I'm lucky enough to say that we've got our eggs in a few baskets. But there are businesses that don't, and you haven't micronation and you've diversified enough that it'd be very unlikely that you have the entire industry disappear. But if you have done something like I don't wanna say silly, but it's not silly. But if you have done something where you're only focusing all of your efforts on to pubs, for instance, so servicing gambling machines and things like that. What do you do when everything shuts down when hospitality disappears? Where we've put in a spot where you thought there was an industry that would never die, people keep eating, people keep going out and gambling and then you're told sorry, shops are shut for the next three months or more and nothing you can do about it. How did you make sure you didn't fall into that trap?
Bryn: To be honest with you there, I was just lucky. Like when I went into it, some of the due diligence I did at the start was to look at the industry and kind of say, well, trade-based businesses most likely aren't going to be outsourced because they don't really have robots that can go and put plaster up, go and do your electrical work or go and do your drains, so a lot of them are very nearby. So I thought automation was probably not going to kill that industry. So that was one of the things, and then the construction industry is a massive industry in Australia, obviously, I think 20% of employment comes from the construction industry. So it's a very big sector. But in your point, the Black Swan effect you know, none of this would predict that there's going to be a pandemic, and that's gonna knock out a lot of industries. Fortunately for me, trade businesses were an essential service. And that wasn't by design, that was by luck. That's meant that a lot of my clients haven't suffered, although the shopfitters, in particular, have suffered, as you said, because they're linked to the retail industry. But to answer your question, if you do niche, I think you are taking, you are almost part of that industry now. So the same risks they have, you have. So if you have a range of different industries, you're diversifying that, and then if the sun goes down, you are as deeply impacted. For me, someday with some of the things, I looked at, and I kind of thought to myself, well, if we ever get to the point where there's a robot that can come to your house and do your fix your lights and do your plumbing, we're probably at the point where none of us has got jobs. The accounting's gone as well at that point. I'm like well, that risks are probably can't mitigate.
Josh: So I'm in a great industry, obviously, IT, because I'm the guy fixing the robots. But the good news is they're never gonna take everyone's jobs, because moments before you think they're gonna be able to accomplish the tasks, you'd have to turn them on and off again. I think it's kind of like when you look at the car, the car killed those horses jobs, these horses had quality jobs and now we're getting new shoes all the time and that this bloody car came along and stunk up the place, but you don't really look at it like that. That is a horse that is automated. It's a car, it's just an automated horse. And there are so many different metaphor vehicles, there are certainly other vehicles like that that have just automated the process. The calculator automated the abacus. Excel automated the calculator. It doesn't remove jobs though. I think it just shifts your focus to things that are more important.
Bryn: Exactly. I think that's the thing, isn't it? Like when one creates another industry creates from that, and I think, you know, in the accounting space, I think you know, there's a lot of automation happening. We use Hub Dock, some people use Receipt Bank or Hub Dock or one of these programs and that basically puts copies straight into the system. There are a few different programs that now sync to your job management software and put your APs or your purchase orders to match the bills and they put them straight into the system. What I found with that is this still uses behind that, because there are still people that have got to sit there and make sure it matches and press the buttons and make sure that happens. So, I don't necessarily think, and there's still a very big space for strategic accounting, and I think they're always well, you know, I think in my lifetime, there probably always will be a place where you need actual advisors. So that automation was a little bit of a risk, but I guess I looked up, and I guess every business owner out there is probably looking at that risking their business to some degree.
IT is indispensable but it shouldn't cost hundreds of dollars. Fill out the form to learn more about our budget-friendly on-demand IT solutions.
Josh: Well, if you're not automating, it's again, if you're running a fleet of sports cars and horses, it's not going to work. You need to adapt and change with the times. Automation has been around us for years and years and years. It's just it's become a bit more of a buzz term lately. I think like when you look at checkout chicks and checkout dudes, jobs that have been automated by those little telling machine things. If their job, if it can be automated with a machine that's that big, it means whatever that we're doing was not going to be advancing their life in the future anyway, it was just having them sit
there and earn some money in a brain dead job you could do with a hangover, and I'm getting ready to get some fire word, I just said then, but if you can have a machine that does this, that doesn't remove jobs, all it does is as you said, shifts the focus of the jobs to the people that are generating the software, entering the codes to make sure that it can be done, making sure that every item is weighed appropriately, and making sure that the one person is actually looking and doing everything and I'd imagine, it's the way with all industries it's going to happen.
Bryn: I think so. I think like from my experience now when I'm dealing with builders or shopfitters. contractors, like, the biggest thing we're teaching them is it's all software, these jobs now are all software already. Like, if you're a shopfitter, and you're not using software that can track GP real-time, then you're going to get burned by other shopfitters because it's amazing how, the technology like everything sinking, everything's linking, and you've got people that can see the GP on every shot that you're building at one time. And that's probably where I'm seeing the good entrepreneurs in those fields are really, really taking off. And the ones that are still on the old based systems are struggling, wondering how the quotes are coming in so cheap. You know, there's something wrong with the industry because people are taking things below cost. It's not necessarily the case. It's because these other businesses are more efficient, exactly. And they do that for people like you, like your companies that come in and look at where the efficiencies are, what systems they can use, how they can automate processes, and yeah, the clients that I work with have been doing that differently have a long way ahead above the other. The clients and on to starting to slowly kind of die-off, if that's a good way to put it.
Josh: As you said, you've got to be ready to adopt, you've got to be ready for the future. And if you got to be niching, you've got to be ready to make sure that your eggs are in multiple baskets or in a basket that is capable of some level of contingency should risk such as the pandemic come available, which brings me to my next point. So I've looked in going, okay, how deep do I want to niche? How much do I want to look into it? This is silly Josh engineering mind going into gear and I think okay, I'll look through the census data, I'll look at the growth data. I'll look through the data of the population and what different areas and sectors are doing whatever, and then try and work out where the growth pattern be from there. So you are fortunate that you had a number of trade types of clients already working with you and then you build the business upon that spot. Did you look into the census data or the growth?
Bryn: We did actually look at a bunch of different figures like how many trade businesses there were, the size of trade businesses, we did do a bit fair bit of due diligence in regards to that. And I think, you know, factor, that kind of point that you're raising around a niche is that you are kind of taking that risky industry. So you want to do the due diligence to find out as much information as possible as you can about the industry. And I think you could go further and micro-niche, like, you could be a plumber's accountant, you could be, you know, you could be an electrician's accountant, or you could go that far into a micro-niche. And then I guess you really want to look at that industry.
Like one of my mentors actually said to me, my good friend said, the best entrepreneurs in the world aren't the best entrepreneurs, they pick the best industry. And that really resonated with me because I thought about that for a while and he said look at Elon Musk, for example. He's picked renewable energy. And where's everything going in the next 10, 20 years? It's renewable energy like people don't want the pollutions out of hand down. It's cheaper and proved it's cheaper than coal now. I think PwC, can have their report that it is actually cheaper to use renewable energy. And so back to that point, I think the best entrepreneurs actually look at the industry they're in and it's not by luck that they end up in any race that makes sense. It's being able to look ahead and see well, where are things going? Obviously, right now, maybe, and, you know, maybe the retail industry, bricks and mortar are not where you want to be. Because no matter how good you are at retailing, right now, if you're not running an e-commerce business, maybe that's not space. But maybe if you're running an e-commerce business right now, you're Amazon, you're riding the right industry. And I don't think these people, I don't think people like Bezos or Musk, these people. I don't think it's by luck they've ended up in those industries. I think they have looked forward to going what's going to be the biggest thing in 10 years' time? What industry is that going to be? How are people going to leave? How are people going to behave? What are the things people are going to do? And then they focus their services around that.
Josh: Yeah. And before everyone thinks that they're obviously not fortune tellers, but I think there's probably 100,000 other people that have tried looking into the future, and then they just bet on the wrong horse. And we don't know about them, because they bet on the wrong horse.
Bryn: I would have never known that trade services were going to be an essential service in a pandemic. I never looked at that. And I read the Black Swan. I actually read the book. I went through it and I thought, well, how can you predict a black swan? I could never have predicted that, and that was luck. But some of the factors that I went through in that was that I thought that they couldn't be automated. I thought there was always space. New houses were being built continuously in Australia. The construction industry has always been a booming industry. We are still like undersupply of houses even to this day, there's an undersupply of houses in the Brisbane market, maybe the Sydney and Melbourne market as well. So if you look at those factors, I think there's going to be a lot more houses that are going to be built. And there's going to be a lot more big construction projects like intercity rail. You know, in Melbourne, there are lots of projects that are being done there.
Do you want to learn more about technology for business? Fill out the form below to get a callback.
Josh: It'd be different for living in a city that was 3000 years old, and you could knock down buildings, you had all these different restrictions, it'd be a different scope or we're in a faux landscape, beautiful land down under that we can't really be complaining too much. Even in this time of unknowing, we're in and I'm going to plug Australia obviously, we're in the best country in the world. We've got the best health care, we've got the lowest death rate mortality rate from the pandemic, and we've got all these different grants and everything else that's coming through that's helping businesses out like I can't complain. I'm pretty, pretty happy with where we're at.
Bryn: You know, in Australia, as you say, we're in the place to be. I mean, we've got obviously the mining industry, it's going to continue, we've saved a lot of money. I think we're well ahead of GDP per debts pretty low here. You know, they've got the money to spend on the population. And pumped money into construction, which is probably the best move. I know all the governments have now announced all these massive projects. And I think that's just going to get people in jobs and going to keep the economy kind of moving along as we come out of the back end of this. Just on a personal level, we went out to the coffee shop on the weekend. And, you know, as soon as I opened it up, and we were at a coffee shop, and we're at the shops, and we're spending money, and I think that's what people will do,
you know, they'll go back and support local businesses. So I think in terms of tourism, I see people travelling, you know, domestically now. Go to Northern Territory, go to Townsville, go to Cairns, you know. You can't really go to Asia or Europe or anything like that maybe for the next however long that's gonna be, but we can go and have a good holiday at the Great Barrier Reef.
Josh: All the overseas listeners hear that you can't even come to see our beautiful place, not for a bit, but it's pretty good. For any of the tradies out there that are listening, you've obviously heard a bunch of advantages to working with Bryn's fantastic business. It'd be worth jumping across the tradiesaccountant.com and checking out the voodoo that he does, and booking in some time to make sure that you're doing everything that you can be doing to automate your processes and make sure that you're getting the biggest bang for the buck in this time of the pandemic. So, before we head off, Bryn is there anything else you'd like to go through, we'll cover off on?
Bryn: The point of the podcast today was around niching. And I just say, you know, if that's something that you're thinking of doing, I think the main experiences I gave is, like I've said earlier, head into it, and do it. Research the industry thoroughly, try and although you're not Nostradamus, try and predict what's going to happen in the next 10 years and try and kind of have some something's weather, collect as much data as you can, and have the data almost make the decision more than the intuition to some degree and then speak to other people that have done it. You know, if you're an IT guy and you want to go and niche into the trade space or something like that, come and speak to me or if you're you know, whatever you're wanting to niche in, speak to other people in that industry, and get to know as much as you can. That would be my main, probably three kinds of points to share with the listeners on how to niche if you want to niche. But I would also say to that as a caveat on the end, don't listen to the influencers because you don't have to niche.
Josh: Terms and conditions apply. I agree. And that's why I want to get you on the show because we have had people that have been very against it and we have had people that are very for it, but I haven't had anyone that's actually walked the walk and talk the talk and you've got a biased opinion, obviously, you're running a business that has a niche, but it's still good hearing the journey that you've come on, how you got to where you're at and what you've seen is some of your advantages of doing that.
Bryn: Thank you for having me on. I've enjoyed it thoroughly and I've enjoyed the beers that we've had.
Josh: Oh, now everyone knows we've been having sneaky bees. Oh no. Oh, no. I've loved having you on as well, and if anyone else has anything to say, leave a review, jump across to iTunes. Give us some love and give us some feedback. Everyone else out there, stay good, stay healthy and look forward to talking to you again soon.
Download now! If you have questions, contact the Fastest Growing Australian IT Company in 2019.
Sign up for Dorks Delivered's Monthly Newsletter for SMBs
All the best technology, business growth, life and mental health advice, tips, tricks, and intel delivered to your inbox every month
We went with a cheap cloud provider. When they bankrupt, we lost everything, our emails, our backup, our website, EVERYTHING. Luckily, we found out about Dorks Delivered. They got us back up in no time. Lesson learned: being cheap is expensive.

redpajama_set_name: RedPajamaCommonCrawl


text:
An institution located at Omaha, Nebraska, U.S.A. and conducted by the Jesuit Fathers. It comprises high school and college departments, a free classical day college, and schools of medicine, dentistry, pharmacy, and law. The faculty numbered 104 members in 1907-8. There is no charge for tuition in the high school and college departments. The attendance at the university is about 800, divided among the different departments as follows: Liberal Arts, 360; Medicine, 178; Law, 51; Pharmacy, 105; Dentistry, 107. The Medical College free dispensary treats between 3000 and 4000 annually; the Dental College Infirmary, 400 or 500.
Creighton University was the first free Catholic college founded in the United States. Edward Creighton, after whom it was named, had proposed during his life to establish a free school for higher education, but he died intestate, before making provision for carrying out his project. His wife, Mary Lucretia Creighton, inheriting his fortune, determined to carry out his intention. She died 23 Jan., 1876, but her will made a bequest, which in the settlement of the estate amounted to about $200,000, one-fourth of which was devoted to the grounds and building, the balance being reserved for foundation. In accordance with the terms of this will, the executors, 1 July, 1878, conveyed the entire property and securities in trust to the Rt. Rev. James O'Connor, Bishop of Omaha. On 27 February, 1879, the Legislature of Nebraska passed an act to provide for the incorporation of universities under certain circumstances. The district Court then permitted Bishop O'Connor to turn over his trust to a corporation called the Creighton University, and he appointed five members of the Society of Jesus as the Board of Trustees, 14 August, 1879. Creighton College as such was not incorporated and the name merely represented what was left in trust by Mrs. Creighton. When the Creighton University accepted the trust, the endowment fund amounted to about $147,500. Mrs. Sarah Emily Creighton, who died 3 Sept., 1888, wife of John A. Creighton, bequeathed to Creighton University a business block, according to the same terms and conditions as were designated in the bequest of her sister, Mrs. Mary Lucretia Creighton. During 1900 John A. Creighton, desirous of making the university an institution fully equipped for its educational work, generously offered means for the completion of the college buildings. The School of Medicine was founded 30 May, 1892, and the School of Law in October, 1904. The Edward Creighton Institute, erected in 1905, is now the home of the Law Department. The Dental School, opened in 1905, is located with the Law School. The School of Pharmacy, a distinct department of the university since 1 February, 1905, took possession of its splendidly equipped new addition to the Medical Building in September, 1908.
Edward Creighton was born 31 Aug., 1820, in Belmont County, Ohio, near the present town of Barnesville; and died 5 Nov., 1874. John A. Creighton was born 15 Oct., 1831, in Licking County, Ohio, and died 7 Feb., 1907. He was educated at St. Joseph's College, Somerset, Ohio, under the Dominican Fathers, and for these teachers he always retained a feeling of gratitude. Though desirous of becoming a civil engineer, he was obliged to shorten his course of study by the necessity of earning a livelihood. He married Sarah Emily Wareham of Dayton; and her sister, Mary Lucretia, became the wife of Edward Creighton. Both these men were remarkable for courage, enterprise, and a strong sense of justice. John was one of the first members of the "Vigilance Committee" which effectually freed Montana of the desperadoes who made life and property insecure in that territory. Both also made their start in life by constructing roads and telegraph lines in the West and South; John was moreover actively engaged in mining, stock-raising, and investments in land. He left by will large bequests to Creighton University, the Creighton Memorial Hospital and other Catholic institutions in which he was interested during life. Though these sums were somewhat lessened by litigation and compromise with contestants, the university received nearly a million and a quarter dollars, the Hospital nearly a quarter of a million, and the other institutions smaller amounts. The entire revenue-producing property of the university approximates two millions and a quarter, exclusive of its buildings, grounds and equipment. The hospital takes care of about 2400 patients a year, of whom more than half are non-Catholics, and one-third absolutely free. John Creighton was honoured by Leo XIII with the order of St. Gregory and later with the title of Count of the Papal States. In 1900 he received the Laetare Medal from the University of Notre Dame .
4mm Amethyst Swarovski Sterling Silver Rosary Bracelet
Sterling Silver Rosary Bracelet - Peridot Swarovski Beads
Advent Wreath Prayer Classes
Advent Classes
We ask you, humbly, to help


text:
Angela Gigi Tsang (piano)
Angela Gigi Tsang, 16, began piano studies at age five and violin studies at age eight. Currently, she studies piano with Dorothy Su and studies violin and chamber music with Meichuan Song. Angela received numerous awards in piano from the Fremont Symphony Young Artist Competition, MTAC Piano Concerto and Solo Competition, SF Chopin Competition, USIMC, USOMC, American Protégé Concerto Competition, ENKOR, GCC, CAPMT Honors Competition, Junior Bach Festival, CM Panel Honors, and etc. An avid chamber player, Angela's groups won prizes from the ENKOR Chamber Competition, GCC Chamber Competition, Santa Cruz Baroque Festival Chamber Music Competition, Junior Bach Festival, and etc. Angela is the concertmaster of the BAYMS Orchestra and has served as concertmaster of the CODA Honor String Orchestra, CASMEC All-State String Orchestra, and ECYS Sinfonietta Orchestra. Performing biweekly at senior centers as a member of the Bay Area Youth Music Society, Angela has received the Presidential Volunteer Service Award six years in a row. Besides music, she enjoys reading, travelling, hiking, and tennis.
FREE Entries: Oct 8 - 15
FREE Entries: Nov 17 - 24
A series of int'l music competitions for young instrumentalists, singers, and ensembles. January February Mar...
RULES: Best Mendelssohn Performance
FREE Entries: Jan 8 - 15 | Deadline extension: Jan 18 We invite you to submit your video records absolutely FREE of charge. The con...
Competitions starting in January
Dec 27 – Jan 27 BEST PERFORMANCE Best Mozart Performance Jan 5 - 31 BEST PERFORMANCE Best Schubert Performance ...
DON'T MISS: Our Talent Roster
Enter the Competition
Featured Winners
An annual competition series paying tribute to some of the greatest composers of all time. All of the competitions in this series are designed in such a way that the award winners will be announced annually on the composers' birthdays.
Rising Talents of the World
The rising music stars to look out for. An annual competition series in which talents are chosen from countries in each world region.
The Music of the Centuries
An annual competition series paying tribute to the baroque, classical, romantic, and contemporary musical eras.
The Music of Europe
An annual competition series paying tribute to the classical music traditions of Europe.
The Music of America
An annual competition paying tribute to the North & South American music.
The Art of Instrumental Performance
An annual competition series paying tribute to the art of instrumental music, focusing on piano, strings, winds, percussion, and chamber music.
The Art of Vocal Performance
An annual competition series paying tribute to the art of singing, focusing on opera, sacred music, art song, and musical theatre.
Masters of Concerto
An annual competition series paying tribute to the art of the Concerto & Concertino's large-scale musical form.
Masters of Sonata
An annual competition series paying tribute to the art of the Sonata/Sonatina's large-scale musical form.
GCC Series, an international classical music competition series


Waargh! dakka dakka! It's Grimsnik's 'ouse'old boyz!
Now lissen up you stoopid grotz, if you wanna field an Ork army, dere is three units wot youse got to take. WUN! you need a warboss, an you need a big tuff one. TOO! you need a ret-in-yoo for yer boss, so 'e 'as his tuff mates an' 'is oddboyz and stuff to 'ang around and do sum fightin' wiv 'im. an' FUR-EE you need a Bigmob so you 'as some boyz to do all the shootin' and sum more fightin' if da nobz 'ave got any spare stuntiez wot need killin'. Da Bigmob has da boyz from da warboss's 'ouse'old. When 'e goes to war he 'as to take the most boyz or da uvver nobz will wanna know why their boyz is doin all da fightin' and 'is boyz are sittin at 'ome playin' with demselves. In real life ov course da warboss would take 'undreds or thousands of boyz, but cos dis is pretend war for weedy oomans dere is just twenny boyz. An' bloody 'ell paintin' dat lot was quite a mishun I can tell yer. Anyhoo, dis lot is Grimsnik's Bad Moon Bigmob. Most of 'em are proper Oldhammer Orkses but dere is a couple of noo ones in dere too makin up da numbers. Dey look 'ard but is probly well weedy. We'll make 'em go in front ha ha. OI stuntiez check out dese lads ha ha. Notice that I 'ave given 'em too 'eavy plasma guns for making big 'ot 'oles in tanks. Get a brew on!


NAHANT — Short Beach is closed again because of high levels of bacteria.
The beach was closed after regular testing turned up high bacteria levels around 10 a.m. Monday, said Nahant Emergency Management Director Michael Halley.
This is not the first time this summer the beach has been closed.
It reopened Aug. 9 after use of the property was prohibited for a few days.
All other beaches in Nahant remained open and unaffected.


We were delighted to celebrate our Investors in People Health & Wellbeing accreditation at a presentation ceremony held at Belfast Castle on 5th October.
Investors in People (IIP) sets the standard for better people management defining what it takes to lead, support and manage people well. Successful accreditation against the IIP Standard is the sign of a great employer who understands that it is people that make the difference. The Health & Wellbeing accreditation is a new IIP standard where physical wellbeing, psychological wellbeing and social wellbeing are at the heart of the framework.
Speaking at the Investors in People presentation ceremony Paul Devoy, Investors in People, said: "Organisations succeed by realising the potential of their people. The organisations receiving Investors in People accreditation today are proof of that.


text:
Ellen Schoenaerts (Wilrijk, 11 augustus 1981) is een Belgische muzikante en theatermaakster. Tevens schreef ze teksten voor muzikant Tom Pintens, haar ex-partner.

In 2013 maakte ze het het Nederlandstalige album 'Feiten', waarvoor ze werd geholpen door het Ellen Schoenaerts Kwartet (Tijs Delbeke, Ephraim Cielen, Liesa Van der Aa, Simon Lenski). Verder telt het album gastbijdragen van Tom Pintens, Hannes d'Hoine, Josse De Pauw, Eric Thielemans en Gregory Frateur.

In 2015 maakte ze de film Liebling.

In 2020 kwam haar tweede album uit: Vrijblijvend advies.

Schoenaerts is de kleindochter van Julien Schoenaerts en het nichtje van Matthias Schoenaerts.

Discografie
2013 Feiten
2020 Vrijblijvend advies

Films
2015 Liebling

Externe link
Officiële website

Vlaams musicus

redpajama_set_name: RedPajamaWikipedia


We arrive within 15 minutes of your scheduled time so you can go about your day.
Leave your bag of clothes for pick-up with your concierge, leasing office, in your garage, outside of your door, or meet us in-person.
Get email notification upon receipt at our cleaning facility, before delivery, and upon delivery so you always know the status of your order. You can also track your order status online by signing in to your account.
$10 minimum order required. More than 99% of our typical orders already meet this minimum! Learn more.
You can reschedule or cancel your pick-up or delivery 60 minutes before your scheduled time at no charge. Learn more.
$3.99 "No Show Fee" will be assessed if we are unable to pick-up or deliver your order, or if you changed your appointment later than 60 minutes before the time slot you requested. Learn more.


In May. we attended a beautiful workshop put on by The Farmer's Florist with Jenn Sanchez in Nashville, TN. The first day was focused on gaining knowledge of how to grow your own blooms, harvesting grown and foraged materials, and putting together a small bouquet for market. Day two started with foraging for materials to be used in designs, then hands-on instruction from Jenn on centerpiece design, and preparing an installation and table settings for a dreamy meal to conclude the workshop. We left inspired to continue attempting to grow a tiny portion of our own flowers used in our designs and thankful to have been able to learn from such talented ladies.
All photography was taken by the incredibly talented Jenna Powers.