<commit_before>#include "myfile.h"
#include "utils.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <assert.h>

//platform-specific defines

#ifdef WIN32

inline FILE_POINTER FileOpen(const char* fn, int openmode)
{
	if(openmode & FILEREAD)
	{
		if(openmode & FILEWRITE)
		        return CreateFile(fn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
		else
		        return CreateFile(fn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	}
	else if(openmode & FILEWRITE)
	{
		return CreateFile(fn, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	}
	abort();
}

inline void FileClose(FILE_POINTER x)
{
  CloseHandle(x);
}

inline int FilePosition(FILE_POINTER x)
{
  return SetFilePointer(x, 0, 0, FILE_CURRENT);
}

inline bool FileSeek(FILE_POINTER x, int p, int from)
{
  return (SetFilePointer(x, p, 0, from) != 0);
}

inline int FileLength(FILE_POINTER x) {
  return GetFileSize(x, NULL);
}

inline bool FileRead(FILE_POINTER x, void* d, int size)
{
  DWORD s;
  if(!ReadFile(x, d, size, &s, NULL))
    return false;
  return (size == s);
}

inline bool FileWrite(FILE_POINTER x, const void* d, int size)
{
  DWORD s;
  if(!WriteFile(x, d, size, &s, NULL))
    return false;
  return (size == s);
}

#else

inline FILE_POINTER FileOpen(const char* fn, int openmode) {
  if(openmode & FILEREAD) {
    if(openmode & FILEWRITE)
      return fopen(fn,"r+b");
    else
      return fopen(fn,"rb");
  }
  else {
    return fopen(fn,"wb");
  }
}

inline void FileClose(FILE_POINTER x)
{
  fclose(x);
}

inline int FilePosition(FILE_POINTER x)
{
  return ftell(x);
}

inline bool FileSeek(FILE_POINTER x, int p, int from)
{
  return fseek(x,p,from);
}

inline int FileLength(FILE_POINTER x)
{
  long pos=ftell(x);
  fseek(x,0,SEEK_END);
  long len=ftell(x);
  fseek(x,pos,SEEK_SET);
  return len;
}

inline bool FileRead(FILE_POINTER x, void* d, int size)
{
  return (int)fread(d,1,size,x)==size;
}

inline bool FileWrite(FILE_POINTER x, const void* d, int size)
{
  return (int)fwrite(d,1,size,x)==size;
}

#endif

enum { MODE_NONE,
	MODE_MYFILE, //internally managed file
	MODE_EXTFILE, //external file reference
	MODE_MYDATA, //internally managed data
	MODE_EXTDATA //external data reference
};

File::File()
:mode(0),srctype(MODE_NONE),
file(INVALID_FILE_POINTER),
datafile(NULL),datapos(0),datasize(0)
{
}

File::~File()
{
	Close();
}

void File::Close()
{
        if(srctype == MODE_MYFILE && file != INVALID_FILE_POINTER) FileClose(file);
	if(srctype == MODE_MYDATA && datafile != NULL) free(datafile);

	srctype = MODE_NONE;
	mode = 0;
	file = INVALID_FILE_POINTER;
	datafile = NULL;
	datapos = 0;
	datasize = 0;
}

bool File::OpenData(void* data, int size, int openmode)
{
	Close();

	if(!data)
		return false;
	if(size < 0)
		return false;

	srctype = MODE_EXTDATA;
	if(openmode == 0)
		return false;

	datafile = (unsigned char*)data;
	datapos = 0;
	datasize = size;
	mode = openmode;
	return true;
}

bool File::OpenData(int openmode)
{
	Close();

	srctype = MODE_MYDATA;
	if(openmode == 0)
		return false;

	ResizeDataBuffer(64);
	mode = openmode;
	return true;
}

void* File::GetDataBuffer() const
{
	assert(srctype == MODE_MYDATA || srctype == MODE_EXTDATA);
	return datafile;
}

void File::ResizeDataBuffer(int size)
{
	unsigned char* olddata=datafile;
	datafile=(unsigned char*)malloc(size);
	memcpy(datafile,olddata,datasize);
	free(olddata);
	datasize = size;
}

bool File::Open(const char* fn, int openmode)
{
	Close();

	if(openmode == 0)
		return false;

	file=FileOpen(fn,openmode);

	if(file == INVALID_FILE_POINTER)
		return false;
	srctype = MODE_MYFILE;
	mode = openmode;
	return true;
}

bool File::Open(FILE_POINTER f, int openmode)
{
	Close();

	srctype = MODE_EXTFILE;
	if(openmode == 0)
		return false;

	file = f;
	mode = openmode;
	return true;
}

int File::Position() const
{
	switch(srctype)
	{
	case MODE_MYFILE:
	case MODE_EXTFILE:
	        return FilePosition(file);
	case MODE_MYDATA:
	case MODE_EXTDATA:
		return datapos;
	}
	return -1;
}

bool File::Seek(int p, int from)
{
	switch(srctype)
	{
	case MODE_MYFILE:
	case MODE_EXTFILE:
	       return FileSeek(file,p,from);
		break;
	case MODE_MYDATA:
	case MODE_EXTDATA:
		switch (from)
		{
		//case SEEK_CUR:
		case FILESEEKCURRENT:
			if(datapos + p >= datasize || datapos + p < 0)
				return false;
			datapos += p;
			break;
		//case SEEK_SET:
		case FILESEEKSTART:
			if(p >= datasize || p < 0)
				return false;
			datapos = p;
			break;
		//case SEEK_END:
		case FILESEEKEND:
			if(datasize + p < 0 || p > 0)
				return false;
			datapos = datasize + p;
			break;
		}
	}
	return true;
}

int File::Length()
{
	switch(srctype)
	{
	case MODE_MYFILE:
	case MODE_EXTFILE:
	        return FileLength(file);
	case MODE_MYDATA:
	case MODE_EXTDATA:
		return datasize;
	}
	return -1;
}

bool File::ReadData(void* d, int size)
{
	if(mode & FILEREAD)
	{
		switch(srctype)
		{
		case MODE_MYFILE:
		case MODE_EXTFILE:
			return FileRead(file,d,size);
		case MODE_MYDATA:
		case MODE_EXTDATA:
			if(datapos + size > datasize)
				return false;
			memcpy(d, datafile+datapos, size);
			datapos += size;
			return true;
		}
	}
	return false;
}

bool File::WriteData(const void* d, int size)
{
	if(mode & FILEWRITE)
	{
		switch(srctype)
		{
		case MODE_MYFILE:
		case MODE_EXTFILE:
			return FileWrite(file,d,size);
		case MODE_MYDATA:		//resize if buffer's not big enough
			if(datapos + size > datasize) {
				int a=datapos+size,b=datasize*2;
				ResizeDataBuffer(Max(a,b));
			}
			memcpy(datafile+datapos, d, size);
			datapos += size;
			return true;
		case MODE_EXTDATA:
			if(datapos + size > datasize)
				return false;
			memcpy(datafile+datapos, d, size);
			datapos += size;
			return true;
		}
	}
	return false;
}

int ReadChar(FILE_POINTER file)
{
	char c;
	if(!FileRead(file, &c, 1)) return EOF;
	return c;
}

bool File::ReadString(char* str, int bufsize)
{
	if(mode & FILEREAD)
	{
		int i,c;
		switch(srctype)
		{
		case MODE_MYFILE:
		case MODE_EXTFILE:
			for(i=0; i<bufsize; i++)
			{
				c = ReadChar(file);
				if(c==EOF)
					return false;
				str[i]=c;
				if(c==0)
					return true;
			}
			break;
		case MODE_MYDATA:
		case MODE_EXTDATA:
			for(i=0; i<bufsize; i++)
			{
				if(datapos >= datasize)
					return false;
				str[i]=datafile[datapos];
				datapos++;
				if(str[i]==0)
					return true;
			}
			break;
		}
	}
	return false;
}

bool File::WriteString(const char* str)
{
	return WriteData(str, (int)(strlen(str)+1));
}

_DEFINE_READ_WRITE_FILE_BASIC(bool);
_DEFINE_READ_WRITE_FILE_BASIC(char);
_DEFINE_READ_WRITE_FILE_BASIC(signed char);
_DEFINE_READ_WRITE_FILE_BASIC(unsigned char);
_DEFINE_READ_WRITE_FILE_BASIC(short);
_DEFINE_READ_WRITE_FILE_BASIC(unsigned short);
_DEFINE_READ_WRITE_FILE_BASIC(int);
_DEFINE_READ_WRITE_FILE_BASIC(unsigned int);
_DEFINE_READ_WRITE_FILE_BASIC(long);
_DEFINE_READ_WRITE_FILE_BASIC(unsigned long);
_DEFINE_READ_WRITE_FILE_BASIC(long long);
_DEFINE_READ_WRITE_FILE_BASIC(unsigned long long);
_DEFINE_READ_WRITE_FILE_BASIC(float);
_DEFINE_READ_WRITE_FILE_BASIC(double);
_DEFINE_READ_WRITE_FILE_BASIC(long double);
<commit_msg>Added support for a basic socket protocol<commit_after>#include "myfile.h"
#include "utils.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <assert.h>

//BSD socket stuff
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#ifndef WIN32
#include <unistd.h>
#endif

//platform-specific defines

#ifdef WIN32

inline FILE_POINTER FileOpen(const char* fn, int openmode)
{
	if(openmode & FILEREAD)
	{
		if(openmode & FILEWRITE)
		        return CreateFile(fn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
		else
		        return CreateFile(fn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	}
	else if(openmode & FILEWRITE)
	{
		return CreateFile(fn, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	}
	abort();
}

inline void FileClose(FILE_POINTER x)
{
  CloseHandle(x);
}

inline int FilePosition(FILE_POINTER x)
{
  return SetFilePointer(x, 0, 0, FILE_CURRENT);
}

inline bool FileSeek(FILE_POINTER x, int p, int from)
{
  return (SetFilePointer(x, p, 0, from) != 0);
}

inline int FileLength(FILE_POINTER x) {
  return GetFileSize(x, NULL);
}

inline bool FileRead(FILE_POINTER x, void* d, int size)
{
  DWORD s;
  if(!ReadFile(x, d, size, &s, NULL))
    return false;
  return (size == s);
}

inline bool FileWrite(FILE_POINTER x, const void* d, int size)
{
  DWORD s;
  if(!WriteFile(x, d, size, &s, NULL))
    return false;
  return (size == s);
}

#else

inline FILE_POINTER FileOpen(const char* fn, int openmode) {
  if(openmode & FILEREAD) {
    if(openmode & FILEWRITE)
      return fopen(fn,"r+b");
    else
      return fopen(fn,"rb");
  }
  else {
    return fopen(fn,"wb");
  }
}

inline void FileClose(FILE_POINTER x)
{
  fclose(x);
}

inline int FilePosition(FILE_POINTER x)
{
  return ftell(x);
}

inline bool FileSeek(FILE_POINTER x, int p, int from)
{
  return fseek(x,p,from);
}

inline int FileLength(FILE_POINTER x)
{
  long pos=ftell(x);
  fseek(x,0,SEEK_END);
  long len=ftell(x);
  fseek(x,pos,SEEK_SET);
  return len;
}

inline bool FileRead(FILE_POINTER x, void* d, int size)
{
  return (int)fread(d,1,size,x)==size;
}

inline bool FileWrite(FILE_POINTER x, const void* d, int size)
{
  return (int)fwrite(d,1,size,x)==size;
}

#endif

enum { MODE_NONE,
       MODE_MYFILE, //internally managed file
       MODE_EXTFILE, //external file reference
       MODE_MYDATA, //internally managed data
       MODE_EXTDATA, //external data reference
       MODE_TCPSOCKET, //TCP socket
       MODE_UDPSOCKET //UDP socket
};

File::File()
:mode(0),srctype(MODE_NONE),
file(INVALID_FILE_POINTER),
datafile(NULL),datapos(0),datasize(0)
{
}

File::~File()
{
	Close();
}

void File::Close()
{
        if(srctype == MODE_MYFILE && file != INVALID_FILE_POINTER) FileClose(file);
	if(srctype == MODE_MYDATA && datafile != NULL) free(datafile);
	if((srctype == MODE_TCPSOCKET || srctype==MODE_UDPSOCKET) && file > 0) close((int)file);

	srctype = MODE_NONE;
	mode = 0;
	file = INVALID_FILE_POINTER;
	datafile = NULL;
	datapos = 0;
	datasize = 0;
}

bool File::OpenData(void* data, int size, int openmode)
{
	Close();

	if(!data)
		return false;
	if(size < 0)
		return false;

	srctype = MODE_EXTDATA;
	if(openmode == 0)
		return false;

	datafile = (unsigned char*)data;
	datapos = 0;
	datasize = size;
	mode = openmode;
	return true;
}

bool File::OpenData(int openmode)
{
	Close();

	srctype = MODE_MYDATA;
	if(openmode == 0)
		return false;

	ResizeDataBuffer(64);
	mode = openmode;
	return true;
}

void* File::GetDataBuffer() const
{
	assert(srctype == MODE_MYDATA || srctype == MODE_EXTDATA);
	return datafile;
}

void File::ResizeDataBuffer(int size)
{
	unsigned char* olddata=datafile;
	datafile=(unsigned char*)malloc(size);
	memcpy(datafile,olddata,datasize);
	free(olddata);
	datasize = size;
}

///Caller must ensure that protocol and host are large enough to handle the
///items.  Simple way of doing this is to allocate to size strlen(addr)
bool ParseAddr(const char* addr,char* protocol,char* host,int& port)
{
  const char* pos=strstr(addr,"://");
  if(pos == NULL) return false;
  //parse protocol
  int spos = pos-addr;
  strncpy(protocol,addr,spos);
  protocol[spos] = 0;
  //parse address and port
  pos += 3;
  spos += 3;
  const char* colonpos = strstr(pos,":");
  if(colonpos==NULL) {
    strcpy(host,pos);
  }
  else {
    strncpy(host,pos,colonpos-pos);
    host[colonpos-pos]=0;
  }
  port = -1;
  //default http port
  if(strcmp(protocol,"http")==0)
    port = 80;
  //default ftp port
  if(strcmp(protocol,"ftp")==0)
    port = 21;

  if(colonpos != NULL) {
    //parse port
    colonpos ++;
    char* endptr;
    long int res = strtol(colonpos,&endptr,0);
    if(res==0 && endptr==colonpos) {
      fprintf(stderr,"ParseAddr: address did not contain valid port\n");
      return false;
    }
    if(res < 0 || res > 0xffff) {
      fprintf(stderr,"ParseAddr: address did not contain valid port\n");
      return false;
    }
    port = (int)res;
  }

  if(port < 0) {
    fprintf(stderr,"ParseAddr: address did not contain valid port\n");
    return false;
  }
  return true;
}

bool File::Open(const char* fn, int openmode)
{
	Close();

	if(openmode == 0)
		return false;

	const char* addrpos=strstr(fn,"://");
	if(addrpos != NULL) {
	  //open TCP or UDP socket
	  //parse fn into host and port
	  char* protocol = new char[strlen(fn)];
	  char* host = new char[strlen(fn)];
	  int port;
	  if(!ParseAddr(fn,protocol,host,port)) {
	    fprintf(stderr,"Error parsing address %s\n",fn);
	    delete [] protocol;
	    delete [] host;
	    return false;
	  }

	  struct sockaddr_in serv_addr;
	  struct hostent *server;

	  int sockettype = SOCK_STREAM;
	  int socketsrctype = MODE_TCPSOCKET;
	  if(0==strcmp(protocol,"udp")) {
	    sockettype = SOCK_DGRAM;
	    socketsrctype = MODE_UDPSOCKET;
	  }
	  delete [] protocol;

	  int sockfd = socket(AF_INET, sockettype, 0);
	  if (sockfd < 0) {
	    fprintf(stderr,"File::Open: Error creating socket\n");
	    delete [] host;
	    return false;
	  }
	  server = gethostbyname(host);
	  delete [] host;
	  if (server == NULL) {
	    fprintf(stderr,"File::Open: Error, no such host %s:%d\n",host,port);
	    close(sockfd);
	    sockfd = -1;
	    return false;
	  }
	  memset(&serv_addr, 0, sizeof(serv_addr));
	  serv_addr.sin_family = AF_INET;
	  memcpy(&serv_addr.sin_addr.s_addr,
		 server->h_addr,
		 server->h_length);
	  serv_addr.sin_port = htons(port);
	  if (connect(sockfd,(sockaddr*)&serv_addr,sizeof(serv_addr)) < 0) {
	    fprintf(stderr,"File::Open: Connect to %s:%d failed\n",host,port);
	    perror("");
	    close(sockfd);
	    return false;
	  }

	  if(sockfd == 0) {
	    fprintf(stderr,"File::Open: socket open returned a 0 file descriptor, this is incompatible\n");
	    close(sockfd);
	    return false;
	  }

	  file = (FILE_POINTER)sockfd;
	  srctype = socketsrctype;
	  mode = openmode;
	  printf("File::Open %s succeeded\n",fn);
	  return true;
	}

	file=FileOpen(fn,openmode);

	if(file == INVALID_FILE_POINTER)
		return false;
	srctype = MODE_MYFILE;
	mode = openmode;
	return true;
}

bool File::Open(FILE_POINTER f, int openmode)
{
	Close();

	srctype = MODE_EXTFILE;
	if(openmode == 0)
		return false;

	file = f;
	mode = openmode;
	return true;
}

int File::Position() const
{
	switch(srctype)
	{
	case MODE_MYFILE:
	case MODE_EXTFILE:
	        return FilePosition(file);
	case MODE_MYDATA:
	case MODE_EXTDATA:
		return datapos;
	}
	return -1;
}

bool File::Seek(int p, int from)
{
	switch(srctype)
	{
	case MODE_MYFILE:
	case MODE_EXTFILE:
	       return FileSeek(file,p,from);
		break;
	case MODE_MYDATA:
	case MODE_EXTDATA:
		switch (from)
		{
		//case SEEK_CUR:
		case FILESEEKCURRENT:
			if(datapos + p >= datasize || datapos + p < 0)
				return false;
			datapos += p;
			break;
		//case SEEK_SET:
		case FILESEEKSTART:
			if(p >= datasize || p < 0)
				return false;
			datapos = p;
			break;
		//case SEEK_END:
		case FILESEEKEND:
			if(datasize + p < 0 || p > 0)
				return false;
			datapos = datasize + p;
			break;
		}
	}
	return true;
}

int File::Length()
{
	switch(srctype)
	{
	case MODE_MYFILE:
	case MODE_EXTFILE:
	        return FileLength(file);
	case MODE_MYDATA:
	case MODE_EXTDATA:
		return datasize;
	}
	return -1;
}

bool File::ReadData(void* d, int size)
{
  if(size < 0)
    fprintf(stderr,"File::ReadData: invalid size %d\n",size);
	if(mode & FILEREAD)
	{
		switch(srctype)
		{
		case MODE_MYFILE:
		case MODE_EXTFILE:
			return FileRead(file,d,size);
		case MODE_MYDATA:
		case MODE_EXTDATA:
			if(datapos + size > datasize)
				return false;
			memcpy(d, datafile+datapos, size);
			datapos += size;
			return true;
		case MODE_TCPSOCKET:
		case MODE_UDPSOCKET:
		  {
		    char* buffer = (char*)d;
		    int totalread = 0;
		    while(totalread < size) {
		      int n=read((int)file,buffer+totalread,size-totalread);
		      if(n < 0)
			return false;
		      totalread += n;
		    }
		    assert(totalread == size);
		    return true;
		  }
		}
	}
	return false;
}

bool File::WriteData(const void* d, int size)
{
	if(mode & FILEWRITE)
	{
		switch(srctype)
		{
		case MODE_MYFILE:
		case MODE_EXTFILE:
			return FileWrite(file,d,size);
		case MODE_MYDATA:		//resize if buffer's not big enough
			if(datapos + size > datasize) {
				int a=datapos+size,b=datasize*2;
				ResizeDataBuffer(Max(a,b));
			}
			memcpy(datafile+datapos, d, size);
			datapos += size;
			return true;
		case MODE_EXTDATA:
			if(datapos + size > datasize)
				return false;
			memcpy(datafile+datapos, d, size);
			datapos += size;
			return true;
		case MODE_TCPSOCKET:
		case MODE_UDPSOCKET:
		  {
		    const char* msg = (const char*)d;
		    int totalsent = 0;
		    while(totalsent < size) {
		      int n = write((int)file,msg+totalsent,size-totalsent);
		      if(n <= 0) {
			return false;
		      }
		      totalsent += n;
		    }
		    assert(totalsent == size);
		    return true;
		  }
		}
	}
	return false;
}

int ReadChar(FILE_POINTER file)
{
	char c;
	if(!FileRead(file, &c, 1)) return EOF;
	return c;
}

bool File::ReadString(char* str, int bufsize)
{
	if(mode & FILEREAD)
	{
		int i,c;
		switch(srctype)
		{
		case MODE_MYFILE:
		case MODE_EXTFILE:
			for(i=0; i<bufsize; i++)
			{
				c = ReadChar(file);
				if(c==EOF)
					return false;
				str[i]=c;
				if(c==0)
					return true;
			}
			break;
		case MODE_MYDATA:
		case MODE_EXTDATA:
			for(i=0; i<bufsize; i++)
			{
				if(datapos >= datasize)
					return false;
				str[i]=datafile[datapos];
				datapos++;
				if(str[i]==0)
					return true;
			}
			break;
		case MODE_TCPSOCKET:
		case MODE_UDPSOCKET:
		  {
		    int slen;
		    if(!ReadData(&slen,4)) {
		      fprintf(stderr,"File::ReadString read length failed\n");
		      return false;
		    }
		    if(slen < 0) {
		      fprintf(stderr,"File::ReadString read length %d is negative\n",slen);
		      return false;
		    }
		    if(slen+1 > bufsize) {
		      fprintf(stderr,"File::ReadString read length %d is greater than buffer size %d\n",slen,bufsize);
		      return false;
		    }
		    if(!ReadData(str,slen)) {
		      fprintf(stderr,"File::ReadString read string failed\n");
		      return false;
		    }
		    str[slen] = 0;
		    return true;
		  }
		default:
		  fprintf(stderr,"File::ReadString: unknown file type %d\n",srctype);
		  break;
		}
	}
	else
	  fprintf(stderr,"File::ReadString: file not in FILEREAD mode\n");
	return false;
}

bool File::WriteString(const char* str)
{
  switch(srctype) {
  case MODE_TCPSOCKET:
  case MODE_UDPSOCKET:
    if(strlen(str) > 0xffffffff) {
      fprintf(stderr,"File::WriteString: string must be no longer than 65536\n");
      return false;
    }
    else {
      assert(sizeof(int)==4);
      int slen = (int)strlen(str);
      if(!WriteData(&slen,4)) {
	return false;
      }
      return WriteData(&slen,(int)strlen(str));
    }
    break;
  default:
    return WriteData(str, (int)(strlen(str)+1));
  }
}

_DEFINE_READ_WRITE_FILE_BASIC(bool);
_DEFINE_READ_WRITE_FILE_BASIC(char);
_DEFINE_READ_WRITE_FILE_BASIC(signed char);
_DEFINE_READ_WRITE_FILE_BASIC(unsigned char);
_DEFINE_READ_WRITE_FILE_BASIC(short);
_DEFINE_READ_WRITE_FILE_BASIC(unsigned short);
_DEFINE_READ_WRITE_FILE_BASIC(int);
_DEFINE_READ_WRITE_FILE_BASIC(unsigned int);
_DEFINE_READ_WRITE_FILE_BASIC(long);
_DEFINE_READ_WRITE_FILE_BASIC(unsigned long);
_DEFINE_READ_WRITE_FILE_BASIC(long long);
_DEFINE_READ_WRITE_FILE_BASIC(unsigned long long);
_DEFINE_READ_WRITE_FILE_BASIC(float);
_DEFINE_READ_WRITE_FILE_BASIC(double);
_DEFINE_READ_WRITE_FILE_BASIC(long double);
<|endoftext|>


<commit_before>#ifndef matrix_hpp
#define matrix_hpp

#include <cmath>
#include <vector>
#include <algorithm>

// fwd declaration: in order to use enable_if
template<typename, typename = void> struct matrix;

// use matrices only for types that are arithmetic (i.e number types)
template<typename T>
struct matrix<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {

    matrix(unsigned N, unsigned M)
    : vec(N * M),
      rows(N),
      columns(M)
    {

    }

    matrix(unsigned N, unsigned M, T filler)
    : vec(N * M),
      rows(N),
      columns(M)
    {
        std::fill(vec.begin(), vec.end(), filler);
    }

    matrix(const matrix& other)
    : vec(other.vec),
      rows(other.rows),
      columns(other.columns)
    {

    }

    matrix(unsigned N, unsigned M, std::initializer_list<T> lst)
    : vec(N * M),
      rows(N),
      columns(M)
    {
        // only copy if they're the same size as the vector
        if (lst.size() == N * M) {
            std::copy(lst.begin(), lst.end(), vec.begin());
        }
    }

    T& operator()(unsigned int i, unsigned int j)
    {
        return vec[i * columns + j];
    }

    const T& operator()(unsigned int i, unsigned int j) const
    {
        return vec[i * columns + j];
    }

    unsigned int rowCount() const
    {
        return rows;
    }

    unsigned int colCount() const
    {
        return columns;
    }

    // data interface: return internal data
    std::vector<T> data() const
    {
        return vec;
    }

private:
    std::vector<T> vec;
    const unsigned int rows;
    const unsigned int columns;
};

// forward declaration
template<typename, typename = void> struct nvector;

// partial specialization of matrix in order to add some familiar operator[] syntax
// rather than using operator() for both matrices and vectors
template<typename T>
struct nvector<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {

    nvector(unsigned N)
    : vec(N, 1)
    {

    }

    nvector(unsigned N, T filler)
    : vec(N, 1, filler)
    {

    }

    nvector(const nvector& other)
    : vec(other.vec)
    {

    }

    nvector(unsigned N, std::initializer_list<T> lst)
    : vec(N, 1, lst)
    {

    }

    T& operator[](unsigned int i)
    {
        return vec(i, 0);
    }

    const T& operator[](unsigned int i) const
    {
        return vec(i, 0);
    }

    unsigned int size() const
    {
        return vec.rowCount();
    }

    typename std::vector<T>::const_iterator begin() const
    {
        return vec.data().cbegin();
    }

    typename std::vector<T>::const_iterator end() const
    {
        return vec.data().cend();
    }
private:
    matrix<T> vec;
};

// regular back-substitution without any regard for pivoting:
// this is to be used only when we do no pivoting: otherwise it's incorrect.
template<typename T>
nvector<T> backsub(const matrix<T>& U, const nvector<T>& b)
{
    nvector<T> solution(b.size());

    const auto n = U.rowCount();
    for (int i = n - 1; i >= 0; --i) {
        solution[i] = b[i];
        for (int j = i + 1; j < n; ++j) {
            solution[i] = solution[i] - U(i, j) * solution[j];
        }
        solution[i] /= U(i, i);
    }

    return solution;
}

// back-substitution overload that takes in a row pivot vector
// can be called if you don't use pivoting but you need to give it
// a vanilla row pivot vector (I think you can do this)
template<typename T>
nvector<T> backsub(const matrix<T>& U, const nvector<T>& b, std::vector<int> piv)
{
    nvector<T> solution(b.size());

    const auto n = U.rowCount();
    for (int i = n - 1; i >= 0; --i) {
        solution[piv[i]] = b[piv[i]];
        for (int j = i + 1; j < n; ++j) {
            solution[piv[i]] = solution[piv[i]] - U(piv[i], j) * solution[piv[j]];
        }
        solution[piv[i]] /= U(piv[i], i);
    }

    // translate the pivots back
    nvector<T> solution2(b.size());
    for (int i = 0; i < piv.size(); ++i)
    {
        solution2[i] = solution[piv[i]];
    }

    return solution2;
}

// overload of back-substitution for complete pivoting.
// When doing complete pivoting, one _must_ call this template function
// otherwise, nothing will look right (pretty sure)
template<typename T>
nvector<T> backsub(const matrix<T>& U, const nvector<T>& b, std::vector<int> piv, std::vector<int> cpiv)
{
    nvector<T> solution(b.size());

    const auto n = U.rowCount();
    for (int i = n - 1; i >= 0; --i) {
        solution[piv[i]] = b[piv[i]];
        for (int j = i + 1; j < n; ++j) {
            solution[piv[i]] = solution[piv[i]] - U(piv[i], cpiv[j]) * solution[piv[j]];
        }
        solution[piv[i]] /= U(piv[i], cpiv[i]);
    }

    // translate the pivots back
    nvector<T> solution2(b.size());
    for (int i = 0; i < piv.size(); ++i)
    {
        solution2[i] = solution[piv[cpiv[i]]];
    }

    return solution2;
}

template<typename T>
nvector<T> gaussian_no_pivoting(const matrix<T>& A, nvector<T> b)
{
    matrix<T> partial(A); // to row reduce to upper triangular

    // code here
    const auto nMinus1 = partial.colCount() - 1;
    const auto n = partial.rowCount();
    for (int i = 0; i < nMinus1; ++i) {
        for (int j = i + 1; j < n; ++j) {
            // calculate the ratio
            auto below = partial(j, i);
            auto diag = partial(i, i);
            auto ratio = partial(j, i) / partial(i, i);
            for (int k = i; k < n; ++k) {
                // modify matrix entry
                auto pjk = partial(j, k);
                auto pik = partial(i, k);
                partial(j, k) = partial(j, k) - ratio * partial(i, k);
            }

            // modify result vector
            auto rj0 = b[j];
            auto bi0 = b[i];
            b[j] = b[j] - ratio * b[i];
            auto rjAfter = b[j];
        }
    }

    return backsub(partial, b);
}

template<typename T>
nvector<T> gaussian_partial_pivoting(const matrix<T>& A, nvector<T> b)
{
    matrix<T> partial(A); // to row reduce to upper triangular form
    std::vector<int> piv(A.rowCount()); // row pivot vector

    // init the pivot vector with the default (no pivots)
    for (int i = 0; i < piv.size(); ++i) {
        piv[i] = i;
    }

    const auto nMinus1 = partial.colCount() - 1;
    const auto n = partial.rowCount();
    for (int i = 0; i < nMinus1; ++i) {

        // do the pivot first
        T magnitude = 0;
        int index = -1;
        for (int j = i; j <= nMinus1; ++j) {
            if (std::abs(partial(piv[j], i)) > magnitude) {
                magnitude = std::abs(partial(piv[j], i));
                index = j;
            }
        }

        if (index != -1) {
            std::swap(piv[i], piv[index]);
        }

        for (int j = i + 1; j < n; ++j) {
            // calculate the ratio
            auto below = partial(piv[j], i);
            auto diag = partial(piv[i], i);
            auto ratio = partial(piv[j], i) / partial(piv[i], i);
            for (int k = i; k < n; ++k) {
                // modify matrix entry
                auto pjk = partial(piv[j], k);
                auto pik = partial(piv[i], k);
                partial(piv[j], k) = partial(piv[j], k) - ratio * partial(piv[i], k);
            }

            // modify result vector
            auto rj0 = b[piv[j]];
            auto bi0 = b[piv[i]];
            b[piv[j]] = b[piv[j]] - ratio * b[piv[i]];
            auto rjAfter = b[piv[j]];
        }
    }

    // call overloaded backsub for partial pivoting
    return backsub(partial, b, piv);
}

template<typename T>
nvector<T> gaussian_complete_pivoting(const matrix<T>& A, nvector<T> b)
{
    matrix<T> partial(A); // to row reduce to upper triangular form
    std::vector<int> piv(A.rowCount()); // row pivot vector
    std::vector<int> cpiv(A.colCount()); // column pivot vector

    // init the pivot vectors with the default (no pivots)
    for (int i = 0; i < piv.size(); ++i) {
        piv[i] = i;
        cpiv[i] = i;
    }

    const auto nMinus1 = partial.colCount() - 1;
    const auto n = partial.rowCount();
    for (int i = 0; i < nMinus1; ++i) {

        // do the pivot first
        T magnitude = 0;
        int row_index = -1;
        int col_index = -1;
        for (int j = i; j <= nMinus1; ++j) {
            for (int k = i; k <= nMinus1; ++k)
            {
                if (std::abs(partial(piv[j], piv[k])) > magnitude) {
                    magnitude = std::abs(partial(piv[j], piv[k]));
                    row_index = j;
                    col_index = k;
                }
            }
        }

        if (row_index != -1) {
            std::swap(piv[i], piv[row_index]);
        }

        if (col_index != -1) {
            std::swap(cpiv[i], cpiv[col_index]);
        }

        for (int j = i + 1; j < n; ++j) {
            // calculate the ratio
            auto below = partial(piv[j], cpiv[i]);
            auto diag = partial(piv[i], cpiv[i]);
            auto ratio = partial(piv[j], cpiv[i]) / partial(piv[i], cpiv[i]);
            for (int k = i; k < n; ++k) {
                // modify matrix entry
                auto pjk = partial(piv[j], cpiv[k]);
                auto pik = partial(piv[i], cpiv[k]);
                partial(piv[j], cpiv[k]) = partial(piv[j], cpiv[k]) - ratio * partial(piv[i], cpiv[k]);
            }

            // modify result vector
            auto rj0 = b[piv[j]];
            auto bi0 = b[piv[i]];
            b[piv[j]] = b[piv[j]] - ratio * b[piv[i]];
            auto rjAfter = b[piv[j]];
        }
    }

    // call overloaded backsub for complete pivoting
    return backsub(partial, b, piv, cpiv);
}

#endif /* matrix_hpp */
<commit_msg>Removing all the unnecessary temporaries that clutter the code: Clion couldn’t find  them so I had to do this manually (the horror!)<commit_after>#ifndef matrix_hpp
#define matrix_hpp

#include <cmath>
#include <vector>
#include <algorithm>

// fwd declaration: in order to use enable_if
template<typename, typename = void> struct matrix;

// use matrices only for types that are arithmetic (i.e number types)
template<typename T>
struct matrix<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {

    matrix(unsigned N, unsigned M)
    : vec(N * M),
      rows(N),
      columns(M)
    {

    }

    matrix(unsigned N, unsigned M, T filler)
    : vec(N * M),
      rows(N),
      columns(M)
    {
        std::fill(vec.begin(), vec.end(), filler);
    }

    matrix(const matrix& other)
    : vec(other.vec),
      rows(other.rows),
      columns(other.columns)
    {

    }

    matrix(unsigned N, unsigned M, std::initializer_list<T> lst)
    : vec(N * M),
      rows(N),
      columns(M)
    {
        // only copy if they're the same size as the vector
        if (lst.size() == N * M) {
            std::copy(lst.begin(), lst.end(), vec.begin());
        }
    }

    T& operator()(unsigned int i, unsigned int j)
    {
        return vec[i * columns + j];
    }

    const T& operator()(unsigned int i, unsigned int j) const
    {
        return vec[i * columns + j];
    }

    unsigned int rowCount() const
    {
        return rows;
    }

    unsigned int colCount() const
    {
        return columns;
    }

    // data interface: return internal data
    std::vector<T> data() const
    {
        return vec;
    }

private:
    std::vector<T> vec;
    const unsigned int rows;
    const unsigned int columns;
};

// forward declaration
template<typename, typename = void> struct nvector;

// partial specialization of matrix in order to add some familiar operator[] syntax
// rather than using operator() for both matrices and vectors
template<typename T>
struct nvector<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {

    nvector(unsigned N)
    : vec(N, 1)
    {

    }

    nvector(unsigned N, T filler)
    : vec(N, 1, filler)
    {

    }

    nvector(const nvector& other)
    : vec(other.vec)
    {

    }

    nvector(unsigned N, std::initializer_list<T> lst)
    : vec(N, 1, lst)
    {

    }

    T& operator[](unsigned int i)
    {
        return vec(i, 0);
    }

    const T& operator[](unsigned int i) const
    {
        return vec(i, 0);
    }

    unsigned int size() const
    {
        return vec.rowCount();
    }

    typename std::vector<T>::const_iterator begin() const
    {
        return vec.data().cbegin();
    }

    typename std::vector<T>::const_iterator end() const
    {
        return vec.data().cend();
    }
private:
    matrix<T> vec;
};

// regular back-substitution without any regard for pivoting:
// this is to be used only when we do no pivoting: otherwise it's incorrect.
template<typename T>
nvector<T> backsub(const matrix<T>& U, const nvector<T>& b)
{
    nvector<T> solution(b.size());

    const auto n = U.rowCount();
    for (int i = n - 1; i >= 0; --i) {
        solution[i] = b[i];
        for (int j = i + 1; j < n; ++j) {
            solution[i] = solution[i] - U(i, j) * solution[j];
        }
        solution[i] /= U(i, i);
    }

    return solution;
}

// back-substitution overload that takes in a row pivot vector
// can be called if you don't use pivoting but you need to give it
// a vanilla row pivot vector (I think you can do this)
template<typename T>
nvector<T> backsub(const matrix<T>& U, const nvector<T>& b, std::vector<int> piv)
{
    nvector<T> solution(b.size());

    const auto n = U.rowCount();
    for (int i = n - 1; i >= 0; --i) {
        solution[piv[i]] = b[piv[i]];
        for (int j = i + 1; j < n; ++j) {
            solution[piv[i]] = solution[piv[i]] - U(piv[i], j) * solution[piv[j]];
        }
        solution[piv[i]] /= U(piv[i], i);
    }

    // translate the pivots back
    nvector<T> solution2(b.size());
    for (int i = 0; i < piv.size(); ++i)
    {
        solution2[i] = solution[piv[i]];
    }

    return solution2;
}

// overload of back-substitution for complete pivoting.
// When doing complete pivoting, one _must_ call this template function
// otherwise, nothing will look right (pretty sure)
template<typename T>
nvector<T> backsub(const matrix<T>& U, const nvector<T>& b, std::vector<int> piv, std::vector<int> cpiv)
{
    nvector<T> solution(b.size());

    const auto n = U.rowCount();
    for (int i = n - 1; i >= 0; --i) {
        solution[piv[i]] = b[piv[i]];
        for (int j = i + 1; j < n; ++j) {
            solution[piv[i]] = solution[piv[i]] - U(piv[i], cpiv[j]) * solution[piv[j]];
        }
        solution[piv[i]] /= U(piv[i], cpiv[i]);
    }

    // translate the pivots back
    nvector<T> solution2(b.size());
    for (int i = 0; i < piv.size(); ++i)
    {
        solution2[i] = solution[piv[cpiv[i]]];
    }

    return solution2;
}

template<typename T>
nvector<T> gaussian_no_pivoting(const matrix<T>& A, nvector<T> b)
{
    matrix<T> partial(A); // to row reduce to upper triangular

    // code here
    const auto nMinus1 = partial.colCount() - 1;
    const auto n = partial.rowCount();
    for (int i = 0; i < nMinus1; ++i) {
        for (int j = i + 1; j < n; ++j) {
            // calculate the ratio
            auto ratio = partial(j, i) / partial(i, i);
            for (int k = i; k < n; ++k) {
                // modify matrix entry
                partial(j, k) = partial(j, k) - ratio * partial(i, k);
            }

            // modify result vector
            b[j] = b[j] - ratio * b[i];
            auto rjAfter = b[j];
        }
    }

    return backsub(partial, b);
}

template<typename T>
nvector<T> gaussian_partial_pivoting(const matrix<T>& A, nvector<T> b)
{
    matrix<T> partial(A); // to row reduce to upper triangular form
    std::vector<int> piv(A.rowCount()); // row pivot vector

    // init the pivot vector with the default (no pivots)
    for (int i = 0; i < piv.size(); ++i) {
        piv[i] = i;
    }

    const auto nMinus1 = partial.colCount() - 1;
    const auto n = partial.rowCount();
    for (int i = 0; i < nMinus1; ++i) {

        // do the pivot first
        T magnitude = 0;
        int index = -1;
        for (int j = i; j <= nMinus1; ++j) {
            if (std::abs(partial(piv[j], i)) > magnitude) {
                magnitude = std::abs(partial(piv[j], i));
                index = j;
            }
        }

        if (index != -1) {
            std::swap(piv[i], piv[index]);
        }

        for (int j = i + 1; j < n; ++j) {
            // calculate the ratio
            auto ratio = partial(piv[j], i) / partial(piv[i], i);
            for (int k = i; k < n; ++k) {
                // modify matrix entry
                partial(piv[j], k) = partial(piv[j], k) - ratio * partial(piv[i], k);
            }

            // modify result vector
            b[piv[j]] = b[piv[j]] - ratio * b[piv[i]];
        }
    }

    // call overloaded backsub for partial pivoting
    return backsub(partial, b, piv);
}

template<typename T>
nvector<T> gaussian_complete_pivoting(const matrix<T>& A, nvector<T> b)
{
    matrix<T> partial(A); // to row reduce to upper triangular form
    std::vector<int> piv(A.rowCount()); // row pivot vector
    std::vector<int> cpiv(A.colCount()); // column pivot vector

    // init the pivot vectors with the default (no pivots)
    for (int i = 0; i < piv.size(); ++i) {
        piv[i] = i;
        cpiv[i] = i;
    }

    const auto nMinus1 = partial.colCount() - 1;
    const auto n = partial.rowCount();
    for (int i = 0; i < nMinus1; ++i) {

        // do the pivot first
        T magnitude = 0;
        int row_index = -1;
        int col_index = -1;
        for (int j = i; j <= nMinus1; ++j) {
            for (int k = i; k <= nMinus1; ++k)
            {
                if (std::abs(partial(piv[j], piv[k])) > magnitude) {
                    magnitude = std::abs(partial(piv[j], piv[k]));
                    row_index = j;
                    col_index = k;
                }
            }
        }

        if (row_index != -1) {
            std::swap(piv[i], piv[row_index]);
        }

        if (col_index != -1) {
            std::swap(cpiv[i], cpiv[col_index]);
        }

        for (int j = i + 1; j < n; ++j) {
            // calculate the ratio
            auto ratio = partial(piv[j], cpiv[i]) / partial(piv[i], cpiv[i]);
            for (int k = i; k < n; ++k) {
                // modify matrix entry
                partial(piv[j], cpiv[k]) = partial(piv[j], cpiv[k]) - ratio * partial(piv[i], cpiv[k]);
            }

            // modify result vector
            b[piv[j]] = b[piv[j]] - ratio * b[piv[i]];
        }
    }

    // call overloaded backsub for complete pivoting
    return backsub(partial, b, piv, cpiv);
}

#endif /* matrix_hpp */
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2006  Justin Karneges
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 *
 */

#include "netinterface.h"

#include "irisnetplugin.h"
#include "irisnetglobal_p.h"

#include <QWaitCondition>
#include <QPointer>
#include <QDebug>

namespace XMPP {

//----------------------------------------------------------------------------
// NetTracker
//----------------------------------------------------------------------------
class NetTracker : public QObject {
	Q_OBJECT
public:
	QList<NetInterfaceProvider::Info> getInterfaces() {
		QMutexLocker locker(&m);

		return info;
	}

	NetTracker() {
		QList<IrisNetProvider*> list = irisNetProviders();

		c = 0;
		foreach(IrisNetProvider* p, list) {
			c = p->createNetInterfaceProvider();
			if(c) break;
		}
		Q_ASSERT(c); // we have built-in support, so this should never fail
		connect(c, SIGNAL(updated()), SLOT(c_updated()));

		c->start();
		info = filterList(c->interfaces());
	}

	~NetTracker() {
		delete c;
	}

signals:
	void updated();
private:

	static QList<NetInterfaceProvider::Info> filterList(const QList<NetInterfaceProvider::Info> &in) {
		QList<NetInterfaceProvider::Info> out;
		for(int n = 0; n < in.count(); ++n)
		{
			if(!in[n].isLoopback) out += in[n];
		}
		return out;
	}

private slots:
	void c_updated() {
		{
			QMutexLocker locker(&m);
			info = filterList(c->interfaces());
		}
		emit updated();
	}

private:
	// this are all protected by m
	NetInterfaceProvider *c;
	QMutex m;
	QList<NetInterfaceProvider::Info> info;

};

// Global because static getRef needs this too.
Q_GLOBAL_STATIC(QMutex, nettracker_mutex)

class NetTrackerThread : public QThread {
	Q_OBJECT
public:
	/** Get a reference to the NetTracker singleton.
	    Calls to getInterfaces will immediately give valid results
	 */
	static NetTrackerThread* getRef() {
		QMutexLocker locker(nettracker_mutex());

		if (!self) {
			self = new NetTrackerThread();
		}
		self->refs++;
		return self;
	}

	/** Release reference.
	 */
	void releaseRef() {
		QMutexLocker locker(nettracker_mutex());

		Q_ASSERT(refs > 0);
		refs--;
		if (refs <= 0) {
			exit(0);
			wait();
			delete this;
			self = 0;
		}
	}

	QList<NetInterfaceProvider::Info> getInterfaces() {
		return nettracker->getInterfaces();
	}

	~NetTrackerThread() {
		// locked from caller
		delete nettracker;
	}

signals:
	void updated();
private:
	NetTrackerThread() {
		// locked from caller
		refs = 0;
		moveToThread(QCoreApplication::instance()->thread());
		startMutex = new QMutex();
		{
			QMutexLocker startLocker(startMutex);
			start();
			startCond.wait(startMutex); // wait for thread startup finished
		}
		delete startMutex;
		startMutex = 0;
	}

	void run() {
		{
			QMutexLocker locker(startMutex);

			nettracker = new NetTracker();
			connect(nettracker, SIGNAL(updated()), SIGNAL(updated()), Qt::DirectConnection);

			startCond.wakeOne(); // we're ready to serve.
		}
		exec();
	}

private:
	QWaitCondition startCond;
	QMutex *startMutex;
	// these are all protected by global nettracker_mutex.
	int refs;
	static NetTrackerThread *self;
	NetTracker *nettracker;
};

NetTrackerThread *NetTrackerThread::self = 0;

//----------------------------------------------------------------------------
// NetInterface
//----------------------------------------------------------------------------
class NetInterfacePrivate : public QObject
{
	Q_OBJECT
public:
	friend class NetInterfaceManagerPrivate;

	NetInterface *q;

	QPointer<NetInterfaceManager> man;
	bool valid;
	QString id, name;
	QList<QHostAddress> addrs;
	QHostAddress gw;

	NetInterfacePrivate(NetInterface *_q) : QObject(_q), q(_q)
	{
		valid = false;
	}

	void doUnavailable()
	{
		if (!valid) return;
		valid = false;
		if (man.isNull()) return;
		man->unreg(q);
		emit q->unavailable();
	}
};

NetInterface::NetInterface(const QString &id, NetInterfaceManager *manager)
				: QObject(manager)
{
	d = new NetInterfacePrivate(this);
	d->man = manager;

	NetInterfaceProvider::Info *info = (NetInterfaceProvider::Info *)d->man->reg(id, this);
	if (info) {
		d->valid = true;
		d->id = info->id;
		d->name = info->name;
		d->addrs = info->addresses;
		d->gw = info->gateway;
		delete info;
	}
}

NetInterface::~NetInterface()
{
	if (d->valid && !d->man.isNull()) d->man->unreg(this);
	delete d;
}

bool NetInterface::isValid() const
{
	return d->valid && !d->man.isNull();
}

QString NetInterface::id() const
{
	return d->id;
}

QString NetInterface::name() const
{
	return d->name;
}

QList<QHostAddress> NetInterface::addresses() const
{
	return d->addrs;
}

QHostAddress NetInterface::gateway() const
{
	return d->gw;
}

//----------------------------------------------------------------------------
// NetInterfaceManager
//----------------------------------------------------------------------------
class NetInterfaceManagerPrivate : public QObject
{
	Q_OBJECT
public:
	NetInterfaceManager *q;

	QList<NetInterfaceProvider::Info> info;
	QList<NetInterface*> listeners;
	NetTrackerThread *tracker;

	bool pending;

	NetInterfaceManagerPrivate(NetInterfaceManager *_q) : QObject(_q), q(_q)
	{
		tracker = NetTrackerThread::getRef();
		pending = false;
		connect(tracker, SIGNAL(updated()), SLOT(tracker_updated()));
	}

	~NetInterfaceManagerPrivate() {
		tracker->releaseRef();
		tracker = 0;
	}

	static int lookup(const QList<NetInterfaceProvider::Info> &list, const QString &id)
	{
		for(int n = 0; n < list.count(); ++n) {
			if(list[n].id == id) return n;
		}
		return -1;
	}

	static bool sameContent(const NetInterfaceProvider::Info &a, const NetInterfaceProvider::Info &b)
	{
		// assume ids are the same already
		return (a.name == b.name && a.isLoopback == b.isLoopback && a.addresses == b.addresses && a.gateway == b.gateway);
	}

	void do_update()
	{
		// grab the latest info
		QList<NetInterfaceProvider::Info> newinfo = tracker->getInterfaces();

		QStringList here_ids, gone_ids;

		// removed / changed
		for(int n = 0; n < info.count(); ++n)
		{
			int i = lookup(newinfo, info[n].id);
			// id is still here
			if(i != -1) {
				// content changed?
				if(!sameContent(info[n], newinfo[i])) {
					gone_ids += info[n].id;
					here_ids += info[n].id;
				}
			} else { // id is gone
				gone_ids += info[n].id;
			}
		}

		// added
		for(int n = 0; n < newinfo.count(); ++n) {
			int i = lookup(info, newinfo[n].id);
			if(i == -1)
				here_ids += newinfo[n].id;
		}
		info = newinfo;

		// announce gone
		for(int n = 0; n < gone_ids.count(); ++n) {
			// work on a copy, just in case the list changes.
			//   it is important to make the copy here, and not
			//   outside the outer loop, in case the items
			//   get deleted
			QList<NetInterface*> list = listeners;
			for(int i = 0; i < list.count(); ++i) {
				if(list[i]->d->id == gone_ids[n]) {
					list[i]->d->doUnavailable();
				}
			}
		}

		// announce here
		for(int n = 0; n < here_ids.count(); ++n)
			emit q->interfaceAvailable(here_ids[n]);
	}

public slots:
	void tracker_updated()
	{
		// collapse multiple updates by queuing up an update if there isn't any queued yet.
		if(!pending) {
			QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
			pending = true;
		}
	}

	void update()
	{
		pending = false;
		do_update();
	}
};

NetInterfaceManager::NetInterfaceManager(QObject *parent)
				:QObject(parent)
{
	d = new NetInterfaceManagerPrivate(this);
}

NetInterfaceManager::~NetInterfaceManager()
{
	delete d;
}

QStringList NetInterfaceManager::interfaces() const
{
	d->info = d->tracker->getInterfaces();
	QStringList out;
	for(int n = 0; n < d->info.count(); ++n) {
		out += d->info[n].id;
	}
	return out;
}

QString NetInterfaceManager::interfaceForAddress(const QHostAddress &a)
{
	NetInterfaceManager netman;
	QStringList list = netman.interfaces();
	for(int n = 0; n < list.count(); ++n) {
		NetInterface iface(list[n], &netman);
		if(iface.addresses().contains(a)) return list[n];
	}
	return QString();
}

void *NetInterfaceManager::reg(const QString &id, NetInterface *i)
{
	for(int n = 0; n < d->info.count(); ++n) {
		if(d->info[n].id == id) {
			d->listeners += i;
			return new NetInterfaceProvider::Info(d->info[n]);
		}
	}
	return 0;
}

void NetInterfaceManager::unreg(NetInterface *i)
{
	d->listeners.removeAll(i);
}

}

#include "netinterface.moc"
<commit_msg>netinterface.cpp: Fix assert in shutdown by calling the NetTracker constructor from the right thread<commit_after>/*
 * Copyright (C) 2006  Justin Karneges
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 *
 */

#include "netinterface.h"

#include "irisnetplugin.h"
#include "irisnetglobal_p.h"

#include <QWaitCondition>
#include <QPointer>
#include <QDebug>

namespace XMPP {

//----------------------------------------------------------------------------
// NetTracker
//----------------------------------------------------------------------------
class NetTracker : public QObject {
	Q_OBJECT
public:
	QList<NetInterfaceProvider::Info> getInterfaces() {
		QMutexLocker locker(&m);

		return info;
	}

	NetTracker() {
		QList<IrisNetProvider*> list = irisNetProviders();

		c = 0;
		foreach(IrisNetProvider* p, list) {
			c = p->createNetInterfaceProvider();
			if(c) break;
		}
		Q_ASSERT(c); // we have built-in support, so this should never fail
		connect(c, SIGNAL(updated()), SLOT(c_updated()));

		c->start();
		info = filterList(c->interfaces());
	}

	~NetTracker() {
		QMutexLocker locker(&m);
		delete c;
	}

signals:
	void updated();
private:

	static QList<NetInterfaceProvider::Info> filterList(const QList<NetInterfaceProvider::Info> &in) {
		QList<NetInterfaceProvider::Info> out;
		for(int n = 0; n < in.count(); ++n)
		{
			if(!in[n].isLoopback) out += in[n];
		}
		return out;
	}

private slots:
	void c_updated() {
		{
			QMutexLocker locker(&m);
			info = filterList(c->interfaces());
		}
		emit updated();
	}

private:
	// this are all protected by m
	NetInterfaceProvider *c;
	QMutex m;
	QList<NetInterfaceProvider::Info> info;

};

// Global because static getRef needs this too.
Q_GLOBAL_STATIC(QMutex, nettracker_mutex)

class NetTrackerThread : public QThread {
	Q_OBJECT
public:
	/** Get a reference to the NetTracker singleton.
	    Calls to getInterfaces will immediately give valid results
	 */
	static NetTrackerThread* getRef() {
		QMutexLocker locker(nettracker_mutex());

		if (!self) {
			self = new NetTrackerThread();
		}
		self->refs++;
		return self;
	}

	/** Release reference.
	 */
	void releaseRef() {
		QMutexLocker locker(nettracker_mutex());

		Q_ASSERT(refs > 0);
		refs--;
		if (refs <= 0) {
			exit(0);
			wait();
			delete this;
			self = 0;
		}
	}

	QList<NetInterfaceProvider::Info> getInterfaces() {
		return nettracker->getInterfaces();
	}

	~NetTrackerThread() {
		// locked from caller
	}

signals:
	void updated();
private:
	NetTrackerThread() {
		// locked from caller
		refs = 0;
		moveToThread(QCoreApplication::instance()->thread());
		startMutex = new QMutex();
		{
			QMutexLocker startLocker(startMutex);
			start();
			startCond.wait(startMutex); // wait for thread startup finished
		}
		delete startMutex;
		startMutex = 0;
	}

	void run() {
		{
			QMutexLocker locker(startMutex);

			nettracker = new NetTracker();
			connect(nettracker, SIGNAL(updated()), SIGNAL(updated()), Qt::DirectConnection);

			startCond.wakeOne(); // we're ready to serve.
		}
		exec();
		delete nettracker;
		nettracker = 0;
	}

private:
	QWaitCondition startCond;
	QMutex *startMutex;
	// these are all protected by global nettracker_mutex.
	int refs;
	static NetTrackerThread *self;
	NetTracker *nettracker;
};

NetTrackerThread *NetTrackerThread::self = 0;

//----------------------------------------------------------------------------
// NetInterface
//----------------------------------------------------------------------------
class NetInterfacePrivate : public QObject
{
	Q_OBJECT
public:
	friend class NetInterfaceManagerPrivate;

	NetInterface *q;

	QPointer<NetInterfaceManager> man;
	bool valid;
	QString id, name;
	QList<QHostAddress> addrs;
	QHostAddress gw;

	NetInterfacePrivate(NetInterface *_q) : QObject(_q), q(_q)
	{
		valid = false;
	}

	void doUnavailable()
	{
		if (!valid) return;
		valid = false;
		if (man.isNull()) return;
		man->unreg(q);
		emit q->unavailable();
	}
};

NetInterface::NetInterface(const QString &id, NetInterfaceManager *manager)
				: QObject(manager)
{
	d = new NetInterfacePrivate(this);
	d->man = manager;

	NetInterfaceProvider::Info *info = (NetInterfaceProvider::Info *)d->man->reg(id, this);
	if (info) {
		d->valid = true;
		d->id = info->id;
		d->name = info->name;
		d->addrs = info->addresses;
		d->gw = info->gateway;
		delete info;
	}
}

NetInterface::~NetInterface()
{
	if (d->valid && !d->man.isNull()) d->man->unreg(this);
	delete d;
}

bool NetInterface::isValid() const
{
	return d->valid && !d->man.isNull();
}

QString NetInterface::id() const
{
	return d->id;
}

QString NetInterface::name() const
{
	return d->name;
}

QList<QHostAddress> NetInterface::addresses() const
{
	return d->addrs;
}

QHostAddress NetInterface::gateway() const
{
	return d->gw;
}

//----------------------------------------------------------------------------
// NetInterfaceManager
//----------------------------------------------------------------------------
class NetInterfaceManagerPrivate : public QObject
{
	Q_OBJECT
public:
	NetInterfaceManager *q;

	QList<NetInterfaceProvider::Info> info;
	QList<NetInterface*> listeners;
	NetTrackerThread *tracker;

	bool pending;

	NetInterfaceManagerPrivate(NetInterfaceManager *_q) : QObject(_q), q(_q)
	{
		tracker = NetTrackerThread::getRef();
		pending = false;
		connect(tracker, SIGNAL(updated()), SLOT(tracker_updated()));
	}

	~NetInterfaceManagerPrivate() {
		tracker->releaseRef();
		tracker = 0;
	}

	static int lookup(const QList<NetInterfaceProvider::Info> &list, const QString &id)
	{
		for(int n = 0; n < list.count(); ++n) {
			if(list[n].id == id) return n;
		}
		return -1;
	}

	static bool sameContent(const NetInterfaceProvider::Info &a, const NetInterfaceProvider::Info &b)
	{
		// assume ids are the same already
		return (a.name == b.name && a.isLoopback == b.isLoopback && a.addresses == b.addresses && a.gateway == b.gateway);
	}

	void do_update()
	{
		// grab the latest info
		QList<NetInterfaceProvider::Info> newinfo = tracker->getInterfaces();

		QStringList here_ids, gone_ids;

		// removed / changed
		for(int n = 0; n < info.count(); ++n)
		{
			int i = lookup(newinfo, info[n].id);
			// id is still here
			if(i != -1) {
				// content changed?
				if(!sameContent(info[n], newinfo[i])) {
					gone_ids += info[n].id;
					here_ids += info[n].id;
				}
			} else { // id is gone
				gone_ids += info[n].id;
			}
		}

		// added
		for(int n = 0; n < newinfo.count(); ++n) {
			int i = lookup(info, newinfo[n].id);
			if(i == -1)
				here_ids += newinfo[n].id;
		}
		info = newinfo;

		// announce gone
		for(int n = 0; n < gone_ids.count(); ++n) {
			// work on a copy, just in case the list changes.
			//   it is important to make the copy here, and not
			//   outside the outer loop, in case the items
			//   get deleted
			QList<NetInterface*> list = listeners;
			for(int i = 0; i < list.count(); ++i) {
				if(list[i]->d->id == gone_ids[n]) {
					list[i]->d->doUnavailable();
				}
			}
		}

		// announce here
		for(int n = 0; n < here_ids.count(); ++n)
			emit q->interfaceAvailable(here_ids[n]);
	}

public slots:
	void tracker_updated()
	{
		// collapse multiple updates by queuing up an update if there isn't any queued yet.
		if(!pending) {
			QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
			pending = true;
		}
	}

	void update()
	{
		pending = false;
		do_update();
	}
};

NetInterfaceManager::NetInterfaceManager(QObject *parent)
				:QObject(parent)
{
	d = new NetInterfaceManagerPrivate(this);
}

NetInterfaceManager::~NetInterfaceManager()
{
	delete d;
}

QStringList NetInterfaceManager::interfaces() const
{
	d->info = d->tracker->getInterfaces();
	QStringList out;
	for(int n = 0; n < d->info.count(); ++n) {
		out += d->info[n].id;
	}
	return out;
}

QString NetInterfaceManager::interfaceForAddress(const QHostAddress &a)
{
	NetInterfaceManager netman;
	QStringList list = netman.interfaces();
	for(int n = 0; n < list.count(); ++n) {
		NetInterface iface(list[n], &netman);
		if(iface.addresses().contains(a)) return list[n];
	}
	return QString();
}

void *NetInterfaceManager::reg(const QString &id, NetInterface *i)
{
	for(int n = 0; n < d->info.count(); ++n) {
		if(d->info[n].id == id) {
			d->listeners += i;
			return new NetInterfaceProvider::Info(d->info[n]);
		}
	}
	return 0;
}

void NetInterfaceManager::unreg(NetInterface *i)
{
	d->listeners.removeAll(i);
}

}

#include "netinterface.moc"
<|endoftext|>


<commit_before>#ifndef matrix_hpp
#define matrix_hpp

#include <cmath>
#include <vector>
#include <algorithm>

// fwd declaration: in order to use enable_if
template<typename, typename = void> struct matrix;

// use matrices only for types that are arithmetic (i.e number types)
template<typename T>
struct matrix<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {

    matrix(unsigned N, unsigned M)
    : vec(N * M),
      rows(N),
      columns(M)
    {

    }

    matrix(unsigned N, unsigned M, T filler)
    : vec(N * M),
      rows(N),
      columns(M)
    {
        std::fill(vec.begin(), vec.end(), filler);
    }

    matrix(const matrix& other)
    : vec(other.vec),
      rows(other.rows),
      columns(other.columns)
    {

    }

    matrix(unsigned N, unsigned M, std::initializer_list<T> lst)
    : vec(N * M),
      rows(N),
      columns(M)
    {
        // only copy if they're the same size as the vector
        if (lst.size() == N * M) {
            std::copy(lst.begin(), lst.end(), vec.begin());
        }
    }

    T& operator()(unsigned int i, unsigned int j)
    {
        return vec[i * columns + j];
    }

    const T& operator()(unsigned int i, unsigned int j) const
    {
        return vec[i * columns + j];
    }

    unsigned int rowCount() const
    {
        return rows;
    }

    unsigned int colCount() const
    {
        return columns;
    }

    // data interface: return internal data
    std::vector<T> data() const
    {
        return vec;
    }

private:
    std::vector<T> vec;
    const unsigned int rows;
    const unsigned int columns;
};

// forward declaration
template<typename, typename = void> struct nvector;

// partial specialization of matrix in order to add some familiar operator[] syntax
// rather than using operator() for both matrices and vectors
template<typename T>
struct nvector<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {

    nvector(unsigned N)
    : vec(N, 1)
    {

    }

    nvector(unsigned N, T filler)
    : vec(N, 1, filler)
    {

    }

    nvector(const nvector& other)
    : vec(other.vec)
    {

    }

    nvector(unsigned N, std::initializer_list<T> lst)
    : vec(N, 1, lst)
    {

    }

    T& operator[](unsigned int i)
    {
        return vec(i, 0);
    }

    const T& operator[](unsigned int i) const
    {
        return vec(i, 0);
    }

    unsigned int size() const
    {
        return vec.rowCount();
    }

    typename std::vector<T>::const_iterator begin() const
    {
        return vec.data().cbegin();
    }

    typename std::vector<T>::const_iterator end() const
    {
        return vec.data().cend();
    }
private:
    matrix<T> vec;
};

// regular back-substitution without any regard for pivoting:
// this is to be used only when we do no pivoting: otherwise it's incorrect.
template<typename T>
nvector<T> backsub(const matrix<T>& U, const nvector<T>& b)
{
    nvector<T> solution(b.size());

    const auto n = U.rowCount();
    for (int i = n - 1; i >= 0; --i) {
        solution[i] = b[i];
        for (int j = i + 1; j < n; ++j) {
            solution[i] = solution[i] - U(i, j) * solution[j];
        }
        solution[i] /= U(i, i);
    }

    return solution;
}

// back-substitution overload that takes in a row pivot vector
// can be called if you don't use pivoting but you need to give it
// a vanilla row pivot vector (I think you can do this)
template<typename T>
nvector<T> backsub(const matrix<T>& U, const nvector<T>& b, std::vector<int> piv)
{
    nvector<T> solution(b.size());

    const auto n = U.rowCount();
    for (int i = n - 1; i >= 0; --i) {
        solution[piv[i]] = b[piv[i]];
        for (int j = i + 1; j < n; ++j) {
            solution[piv[i]] = solution[piv[i]] - U(piv[i], j) * solution[piv[j]];
        }
        solution[piv[i]] /= U(piv[i], i);
    }

    // translate the pivots back
    nvector<T> solution2(b.size());
    for (int i = 0; i < piv.size(); ++i)
    {
        solution2[i] = solution[piv[i]];
    }

    return solution2;
}

// overload of back-substitution for complete pivoting.
// When doing complete pivoting, one _must_ call this template function
// otherwise, nothing will look right (pretty sure)
template<typename T>
nvector<T> backsub(const matrix<T>& U, const nvector<T>& b, std::vector<int> piv, std::vector<int> cpiv)
{
    nvector<T> solution(b.size());

    const auto n = U.rowCount();
    for (int i = n - 1; i >= 0; --i) {
        solution[piv[i]] = b[piv[i]];
        for (int j = i + 1; j < n; ++j) {
            solution[piv[i]] = solution[piv[i]] - U(piv[i], cpiv[j]) * solution[piv[j]];
        }
        solution[piv[i]] /= U(piv[i], cpiv[i]);
    }

    // translate the pivots back
    nvector<T> solution2(b.size());
    for (int i = 0; i < piv.size(); ++i)
    {
        solution2[i] = solution[piv[cpiv[i]]];
    }

    return solution2;
}

template<typename T>
nvector<T> gaussian_no_pivoting(const matrix<T>& A, nvector<T> b)
{
    matrix<T> partial(A); // to row reduce to upper triangular

    // code here
    const auto nMinus1 = partial.colCount() - 1;
    const auto n = partial.rowCount();
    for (int i = 0; i < nMinus1; ++i) {
        for (int j = i + 1; j < n; ++j) {
            // calculate the ratio
            auto ratio = partial(j, i) / partial(i, i);
            for (int k = i; k < n; ++k) {
                // modify matrix entry
                partial(j, k) = partial(j, k) - ratio * partial(i, k);
            }

            // modify result vector
            b[j] = b[j] - ratio * b[i];
            auto rjAfter = b[j];
        }
    }

    return backsub(partial, b);
}

template<typename T>
nvector<T> gaussian_partial_pivoting(const matrix<T>& A, nvector<T> b)
{
    matrix<T> partial(A); // to row reduce to upper triangular form
    std::vector<int> piv(A.rowCount()); // row pivot vector

    // init the pivot vector with the default (no pivots)
    for (int i = 0; i < piv.size(); ++i) {
        piv[i] = i;
    }

    const auto nMinus1 = partial.colCount() - 1;
    const auto n = partial.rowCount();
    for (int i = 0; i < nMinus1; ++i) {

        // do the pivot first
        T magnitude = 0;
        int index = -1;
        for (int j = i; j <= nMinus1; ++j) {
            if (std::abs(partial(piv[j], i)) > magnitude) {
                magnitude = std::abs(partial(piv[j], i));
                index = j;
            }
        }

        if (index != -1) {
            std::swap(piv[i], piv[index]);
        }

        for (int j = i + 1; j < n; ++j) {
            // calculate the ratio
            auto ratio = partial(piv[j], i) / partial(piv[i], i);
            for (int k = i; k < n; ++k) {
                // modify matrix entry
                partial(piv[j], k) = partial(piv[j], k) - ratio * partial(piv[i], k);
            }

            // modify result vector
            b[piv[j]] = b[piv[j]] - ratio * b[piv[i]];
        }
    }

    // call overloaded backsub for partial pivoting
    return backsub(partial, b, piv);
}

template<typename T>
nvector<T> gaussian_complete_pivoting(const matrix<T>& A, nvector<T> b)
{
    matrix<T> partial(A); // to row reduce to upper triangular form
    std::vector<int> piv(A.rowCount()); // row pivot vector
    std::vector<int> cpiv(A.colCount()); // column pivot vector

    // init the pivot vectors with the default (no pivots)
    for (int i = 0; i < piv.size(); ++i) {
        piv[i] = i;
        cpiv[i] = i;
    }

    const auto nMinus1 = partial.colCount() - 1;
    const auto n = partial.rowCount();
    for (int i = 0; i < nMinus1; ++i) {

        // do the pivot first
        T magnitude = 0;
        int row_index = -1;
        int col_index = -1;
        for (int j = i; j <= nMinus1; ++j) {
            for (int k = i; k <= nMinus1; ++k)
            {
                if (std::abs(partial(piv[j], piv[k])) > magnitude) {
                    magnitude = std::abs(partial(piv[j], piv[k]));
                    row_index = j;
                    col_index = k;
                }
            }
        }

        if (row_index != -1) {
            std::swap(piv[i], piv[row_index]);
        }

        if (col_index != -1) {
            std::swap(cpiv[i], cpiv[col_index]);
        }

        for (int j = i + 1; j < n; ++j) {
            // calculate the ratio
            auto ratio = partial(piv[j], cpiv[i]) / partial(piv[i], cpiv[i]);
            for (int k = i; k < n; ++k) {
                // modify matrix entry
                partial(piv[j], cpiv[k]) = partial(piv[j], cpiv[k]) - ratio * partial(piv[i], cpiv[k]);
            }

            // modify result vector
            b[piv[j]] = b[piv[j]] - ratio * b[piv[i]];
        }
    }

    // call overloaded backsub for complete pivoting
    return backsub(partial, b, piv, cpiv);
}

#endif /* matrix_hpp */
<commit_msg>Subtle bug in the code but somehow the code works as it was previously. Not sure why to be honest.<commit_after>#ifndef matrix_hpp
#define matrix_hpp

#include <cmath>
#include <vector>
#include <algorithm>

// fwd declaration: in order to use enable_if
template<typename, typename = void> struct matrix;

// use matrices only for types that are arithmetic (i.e number types)
template<typename T>
struct matrix<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {

    matrix(unsigned N, unsigned M)
    : vec(N * M),
      rows(N),
      columns(M)
    {

    }

    matrix(unsigned N, unsigned M, T filler)
    : vec(N * M),
      rows(N),
      columns(M)
    {
        std::fill(vec.begin(), vec.end(), filler);
    }

    matrix(const matrix& other)
    : vec(other.vec),
      rows(other.rows),
      columns(other.columns)
    {

    }

    matrix(unsigned N, unsigned M, std::initializer_list<T> lst)
    : vec(N * M),
      rows(N),
      columns(M)
    {
        // only copy if they're the same size as the vector
        if (lst.size() == N * M) {
            std::copy(lst.begin(), lst.end(), vec.begin());
        }
    }

    T& operator()(unsigned int i, unsigned int j)
    {
        return vec[i * columns + j];
    }

    const T& operator()(unsigned int i, unsigned int j) const
    {
        return vec[i * columns + j];
    }

    unsigned int rowCount() const
    {
        return rows;
    }

    unsigned int colCount() const
    {
        return columns;
    }

    // data interface: return internal data
    std::vector<T> data() const
    {
        return vec;
    }

private:
    std::vector<T> vec;
    const unsigned int rows;
    const unsigned int columns;
};

// forward declaration
template<typename, typename = void> struct nvector;

// partial specialization of matrix in order to add some familiar operator[] syntax
// rather than using operator() for both matrices and vectors
template<typename T>
struct nvector<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {

    nvector(unsigned N)
    : vec(N, 1)
    {

    }

    nvector(unsigned N, T filler)
    : vec(N, 1, filler)
    {

    }

    nvector(const nvector& other)
    : vec(other.vec)
    {

    }

    nvector(unsigned N, std::initializer_list<T> lst)
    : vec(N, 1, lst)
    {

    }

    T& operator[](unsigned int i)
    {
        return vec(i, 0);
    }

    const T& operator[](unsigned int i) const
    {
        return vec(i, 0);
    }

    unsigned int size() const
    {
        return vec.rowCount();
    }

    typename std::vector<T>::const_iterator begin() const
    {
        return vec.data().cbegin();
    }

    typename std::vector<T>::const_iterator end() const
    {
        return vec.data().cend();
    }
private:
    matrix<T> vec;
};

// regular back-substitution without any regard for pivoting:
// this is to be used only when we do no pivoting: otherwise it's incorrect.
template<typename T>
nvector<T> backsub(const matrix<T>& U, const nvector<T>& b)
{
    nvector<T> solution(b.size());

    const auto n = U.rowCount();
    for (int i = n - 1; i >= 0; --i) {
        solution[i] = b[i];
        for (int j = i + 1; j < n; ++j) {
            solution[i] = solution[i] - U(i, j) * solution[j];
        }
        solution[i] /= U(i, i);
    }

    return solution;
}

// back-substitution overload that takes in a row pivot vector
// can be called if you don't use pivoting but you need to give it
// a vanilla row pivot vector (I think you can do this)
template<typename T>
nvector<T> backsub(const matrix<T>& U, const nvector<T>& b, std::vector<int> piv)
{
    nvector<T> solution(b.size());

    const auto n = U.rowCount();
    for (int i = n - 1; i >= 0; --i) {
        solution[piv[i]] = b[piv[i]];
        for (int j = i + 1; j < n; ++j) {
            solution[piv[i]] = solution[piv[i]] - U(piv[i], j) * solution[piv[j]];
        }
        solution[piv[i]] /= U(piv[i], i);
    }

    // translate the pivots back
    nvector<T> solution2(b.size());
    for (int i = 0; i < piv.size(); ++i)
    {
        solution2[i] = solution[piv[i]];
    }

    return solution2;
}

// overload of back-substitution for complete pivoting.
// When doing complete pivoting, one _must_ call this template function
// otherwise, nothing will look right (pretty sure)
template<typename T>
nvector<T> backsub(const matrix<T>& U, const nvector<T>& b, std::vector<int> piv, std::vector<int> cpiv)
{
    nvector<T> solution(b.size());

    const auto n = U.rowCount();
    for (int i = n - 1; i >= 0; --i) {
        solution[piv[i]] = b[piv[i]];
        for (int j = i + 1; j < n; ++j) {
            solution[piv[i]] = solution[piv[i]] - U(piv[i], cpiv[j]) * solution[piv[j]];
        }
        solution[piv[i]] /= U(piv[i], cpiv[i]);
    }

    // translate the pivots back
    nvector<T> solution2(b.size());
    for (int i = 0; i < piv.size(); ++i)
    {
        solution2[i] = solution[piv[cpiv[i]]];
    }

    return solution2;
}

template<typename T>
nvector<T> gaussian_no_pivoting(const matrix<T>& A, nvector<T> b)
{
    matrix<T> partial(A); // to row reduce to upper triangular

    // code here
    const auto nMinus1 = partial.colCount() - 1;
    const auto n = partial.rowCount();
    for (int i = 0; i < nMinus1; ++i) {
        for (int j = i + 1; j < n; ++j) {
            // calculate the ratio
            auto ratio = partial(j, i) / partial(i, i);
            for (int k = i; k < n; ++k) {
                // modify matrix entry
                partial(j, k) = partial(j, k) - ratio * partial(i, k);
            }

            // modify result vector
            b[j] = b[j] - ratio * b[i];
            auto rjAfter = b[j];
        }
    }

    return backsub(partial, b);
}

template<typename T>
nvector<T> gaussian_partial_pivoting(const matrix<T>& A, nvector<T> b)
{
    matrix<T> partial(A); // to row reduce to upper triangular form
    std::vector<int> piv(A.rowCount()); // row pivot vector

    // init the pivot vector with the default (no pivots)
    for (int i = 0; i < piv.size(); ++i) {
        piv[i] = i;
    }

    const auto nMinus1 = partial.colCount() - 1;
    const auto n = partial.rowCount();
    for (int i = 0; i < nMinus1; ++i) {

        // do the pivot first
        T magnitude = 0;
        int index = -1;
        for (int j = i; j <= nMinus1; ++j) {
            if (std::abs(partial(piv[j], i)) > magnitude) {
                magnitude = std::abs(partial(piv[j], i));
                index = j;
            }
        }

        if (index != -1) {
            std::swap(piv[i], piv[index]);
        }

        for (int j = i + 1; j < n; ++j) {
            // calculate the ratio
            auto ratio = partial(piv[j], i) / partial(piv[i], i);
            for (int k = i; k < n; ++k) {
                // modify matrix entry
                partial(piv[j], k) = partial(piv[j], k) - ratio * partial(piv[i], k);
            }

            // modify result vector
            b[piv[j]] = b[piv[j]] - ratio * b[piv[i]];
        }
    }

    // call overloaded backsub for partial pivoting
    return backsub(partial, b, piv);
}

template<typename T>
nvector<T> gaussian_complete_pivoting(const matrix<T>& A, nvector<T> b)
{
    matrix<T> partial(A); // to row reduce to upper triangular form
    std::vector<int> piv(A.rowCount()); // row pivot vector
    std::vector<int> cpiv(A.colCount()); // column pivot vector

    // init the pivot vectors with the default (no pivots)
    for (int i = 0; i < piv.size(); ++i) {
        piv[i] = i;
        cpiv[i] = i;
    }

    const auto nMinus1 = partial.colCount() - 1;
    const auto n = partial.rowCount();
    for (int i = 0; i < nMinus1; ++i) {

        // do the pivot first
        T magnitude = 0;
        int row_index = -1;
        int col_index = -1;
        for (int j = i; j <= nMinus1; ++j) {
            for (int k = i; k <= nMinus1; ++k)
            {
                if (std::abs(partial(piv[j], piv[k])) > magnitude) {
                    magnitude = std::abs(partial(piv[j], cpiv[k]));
                    row_index = j;
                    col_index = k;
                }
            }
        }

        if (row_index != -1) {
            std::swap(piv[i], piv[row_index]);
        }

        if (col_index != -1) {
            std::swap(cpiv[i], cpiv[col_index]);
        }

        for (int j = i + 1; j < n; ++j) {
            // calculate the ratio
            auto ratio = partial(piv[j], cpiv[i]) / partial(piv[i], cpiv[i]);
            for (int k = i; k < n; ++k) {
                // modify matrix entry
                partial(piv[j], cpiv[k]) = partial(piv[j], cpiv[k]) - ratio * partial(piv[i], cpiv[k]);
            }

            // modify result vector
            b[piv[j]] = b[piv[j]] - ratio * b[piv[i]];
        }
    }

    // call overloaded backsub for complete pivoting
    return backsub(partial, b, piv, cpiv);
}

#endif /* matrix_hpp */
<|endoftext|>


<commit_before>/*

  Copyright 2009 University of Helsinki
  Copyright 2015 Tino Didriksen <mail@tinodidriksen.com>
  Code adapted from https://github.com/TinoDidriksen/trie-tools

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

/*
	Tests up to 8 variations of each input token:
	- Verbatim
	- With leading non-alphanumerics removed
	- With trailing non-alphanumerics removed
	- With leading and trailing non-alphanumerics removed
	- Lower-case of all the above
*/

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
#include <map>
#include <sstream>
#include <stdexcept>
#include <cmath>
#include <cerrno>
#include <cctype>

#define U_CHARSET_IS_UTF8 1
#include <unicode/uclean.h>
#include <unicode/ucnv.h>
#include <unicode/uloc.h>
#include <unicode/uchar.h>
#include <unicode/unistr.h>

#include "ol-exceptions.h"
#include "ospell.h"
#include "ZHfstOspeller.h"

using hfst_ol::ZHfstOspeller;
using hfst_ol::Transducer;

typedef std::map<UnicodeString,bool> valid_words_t;
valid_words_t valid_words;

struct word_t {
	size_t start, count;
	UnicodeString buffer;
};
std::vector<word_t> words(8);
std::string buffer;
UnicodeString ubuffer, uc_buffer;
size_t cw;

bool verbatim = false;
bool uc_first = false;
bool uc_all = true;

bool find_alternatives(ZHfstOspeller& speller, size_t suggs) {
	for (size_t k=1 ; k <= cw ; ++k) {
		buffer.clear();
		words[cw-k].buffer.toUTF8String(buffer);
		hfst_ol::CorrectionQueue corrections = speller.suggest(buffer);

		if (corrections.size() == 0) {
			continue;
		}

		std::cout << "&";
		// Because speller.set_queue_limit() doesn't actually work, hard limit it here
		for (size_t i=0, e=corrections.size() ; i<e && i<suggs ; ++i) {
			std::cout << "\t";

			buffer.clear();
			if (cw - k != 0) {
				words[0].buffer.tempSubString(0, words[cw-k].start).toUTF8String(buffer);
			}
			if (uc_all) {
				UnicodeString::fromUTF8(corrections.top().first).toUpper().toUTF8String(buffer);
			}
			else if (uc_first) {
				uc_buffer.setTo(UnicodeString::fromUTF8(corrections.top().first));
				ubuffer.setTo(uc_buffer, 0, 1);
				ubuffer.toUpper();
				ubuffer.append(uc_buffer, 1, uc_buffer.length()-1);
				ubuffer.toUTF8String(buffer);
			}
			else {
				buffer.append(corrections.top().first);
			}
			if (cw - k != 0) {
				words[0].buffer.tempSubString(words[cw-k].start + words[cw-k].count).toUTF8String(buffer);
			}

			std::cout << buffer;
			corrections.pop();
		}
		std::cout << std::endl;
		return true;
	}
	return false;
}

bool is_valid_word(ZHfstOspeller& speller, const std::string& word, size_t suggs) {
	ubuffer.setTo(UnicodeString::fromUTF8(word));

	if (word.size() == 13 && word[5] == 'D' && word == "nuvviDspeller") {
		uc_first = false;
		uc_all = false;
		words[0].start = 0;
		words[0].count = ubuffer.length();
		words[0].buffer = ubuffer;
		cw = 1;
		return false;
	}

	uc_first = false;
	uc_all = true;
	bool has_letters = false;
	for (int32_t i=0 ; i<ubuffer.length() ; ++i) {
		if (u_isalpha(ubuffer[i])) {
			has_letters = true;
			if (u_isupper(ubuffer[i]) && uc_all) {
				uc_first = true;
			}
			else if (u_islower(ubuffer[i])) {
				uc_all = false;
				break;
			}
		}
	}

	// If there are no letters in this token, just ignore it
	if (has_letters == false) {
		return true;
	}

	size_t ichStart = 0, cchUse = ubuffer.length();
	const UChar *pwsz = ubuffer.getTerminatedBuffer();

	// Always test the full given input
	words[0].buffer.remove();
	words[0].start = ichStart;
	words[0].count = cchUse;
	words[0].buffer = ubuffer;
	cw = 1;

	if (cchUse > 1 && !verbatim) {
		size_t count = cchUse;
		while (count && !u_isalnum(pwsz[ichStart+count-1])) {
			--count;
		}
		if (count != cchUse) {
			// If the input ended with non-alphanumerics, test input with non-alphanumerics trimmed from the end
			words[cw].buffer.remove();
			words[cw].start = ichStart;
			words[cw].count = count;
			words[cw].buffer.append(pwsz, words[cw].start, words[cw].count);
			++cw;
		}

		size_t start = ichStart, count2 = cchUse;
		while (start < ichStart+cchUse && !u_isalnum(pwsz[start])) {
			++start;
			--count2;
		}
		if (start != ichStart) {
			// If the input started with non-alphanumerics, test input with non-alphanumerics trimmed from the start
			words[cw].buffer.remove();
			words[cw].start = start;
			words[cw].count = count2;
			words[cw].buffer.append(pwsz, words[cw].start, words[cw].count);
			++cw;
		}

		if (start != ichStart && count != cchUse) {
			// If the input both started and ended with non-alphanumerics, test input with non-alphanumerics trimmed from both sides
			words[cw].buffer.remove();
			words[cw].start = start;
			words[cw].count = count - (cchUse - count2);
			words[cw].buffer.append(pwsz, words[cw].start, words[cw].count);
			++cw;
		}
	}

	for (size_t i=0, e=cw ; i<e ; ++i) {
		// If we are looking for suggestions, don't use the cache
		valid_words_t::iterator it = suggs ? valid_words.end() : valid_words.find(words[i].buffer);

		if (it == valid_words.end()) {
			buffer.clear();
			words[i].buffer.toUTF8String(buffer);
			bool valid = speller.spell(buffer);
			it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;

			if (!valid && !verbatim) {
				// If the word was not valid, fold it to lower case and try again
				buffer.clear();
				ubuffer = words[i].buffer;
				ubuffer.toLower();
				ubuffer.toUTF8String(buffer);

				// Add the lower case variant to the list so that we get suggestions using that, if need be
				words[cw].start = words[i].start;
				words[cw].count = words[i].count;
				words[cw].buffer = ubuffer;
				++cw;

				// Don't try again if the lower cased variant has already been tried
				valid_words_t::iterator itl = suggs ? valid_words.end() : valid_words.find(ubuffer);
				if (itl != valid_words.end()) {
					it->second = itl->second;
					it = itl;
				}
				else {
					valid = speller.spell(buffer);
					it->second = valid; // Also mark the original mixed case variant as whatever the lower cased one was
					it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;
				}
			}
		}

		if (it->second == true) {
			return true;
		}
	}

	return false;
}

int zhfst_spell(const char* zhfst_filename) {
	ZHfstOspeller speller;
	try {
		speller.read_zhfst(zhfst_filename);
	}
	catch (hfst_ol::ZHfstMetaDataParsingError zhmdpe) {
		fprintf(stderr, "cannot finish reading zhfst archive %s:\n%s.\n", zhfst_filename, zhmdpe.what());
		return EXIT_FAILURE;
	}
	catch (hfst_ol::ZHfstZipReadingError zhzre) {
		fprintf(stderr, "cannot read zhfst archive %s:\n%s.\n", zhfst_filename, zhzre.what());
		return EXIT_FAILURE;
	}
	catch (hfst_ol::ZHfstXmlParsingError zhxpe) {
		fprintf(stderr, "Cannot finish reading index.xml from %s:\n%s.\n", zhfst_filename, zhxpe.what());
		return EXIT_FAILURE;
	}

	std::cout << "@@ hfst-ospell-office is alive" << std::endl;

	std::string line;
	std::string word;
	std::istringstream ss;
	while (std::getline(std::cin, line)) {
		while (!line.empty() && std::isspace(line[line.size()-1])) {
			line.resize(line.size()-1);
		}
		if (line.empty()) {
			continue;
		}
		// Just in case anyone decides to use the speller for a minor eternity
		if (valid_words.size() > 20480) {
			valid_words.clear();
		}

		ss.clear();
		ss.str(line);
		size_t suggs = 0;
		char c = 0;
		if (!(ss >> suggs) || !ss.get(c) || !std::getline(ss, line)) {
			std::cout << "!" << std::endl;
			continue;
		}

		if (is_valid_word(speller, line, suggs)) {
			std::cout << "*" << std::endl;
			continue;
		}

		if (!suggs || !find_alternatives(speller, suggs)) {
			std::cout << "#" << std::endl;
		}
	}
    return EXIT_SUCCESS;
}

int main(int argc, char **argv) {
	UErrorCode status = U_ZERO_ERROR;
	u_init(&status);
	if (U_FAILURE(status) && status != U_FILE_ACCESS_ERROR) {
		std::cerr << "Error: Cannot initialize ICU. Status = " << u_errorName(status) << std::endl;
		return -1;
	}

	ucnv_setDefaultName("UTF-8");
	uloc_setDefault("en_US_POSIX", &status);

	std::vector<std::string> args(argv, argv+argc);
	for (std::vector<std::string>::iterator it=args.begin() ; it != args.end() ; ) {
		if (*it == "--verbatim") {
			verbatim = true;
			it = args.erase(it);
		}
		else {
			++it;
		}
	}

	if (args.size() < 2) {
		throw std::invalid_argument("Must pass a zhfst as argument");
	}

	int rv = zhfst_spell(args[1].c_str());

	u_cleanup();
	return rv;
}
<commit_msg>If the input token had any upper case, also try a first-upper variant of it<commit_after>/*

  Copyright 2009 University of Helsinki
  Copyright 2015 Tino Didriksen <mail@tinodidriksen.com>
  Code adapted from https://github.com/TinoDidriksen/trie-tools

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

/*
	Tests up to 16 variations of each input token:
	- Verbatim
	- With leading non-alphanumerics removed
	- With trailing non-alphanumerics removed
	- With leading and trailing non-alphanumerics removed
	- Lower-case of all the above
	- First-upper of all the above
*/

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
#include <map>
#include <sstream>
#include <stdexcept>
#include <cmath>
#include <cerrno>
#include <cctype>

#define U_CHARSET_IS_UTF8 1
#include <unicode/uclean.h>
#include <unicode/ucnv.h>
#include <unicode/uloc.h>
#include <unicode/uchar.h>
#include <unicode/unistr.h>

#include "ol-exceptions.h"
#include "ospell.h"
#include "ZHfstOspeller.h"

using hfst_ol::ZHfstOspeller;
using hfst_ol::Transducer;

typedef std::map<UnicodeString,bool> valid_words_t;
valid_words_t valid_words;

struct word_t {
	size_t start, count;
	UnicodeString buffer;
};
std::vector<word_t> words(8);
std::string buffer;
UnicodeString ubuffer, uc_buffer;
size_t cw;

bool verbatim = false;
bool uc_first = false;
bool uc_all = true;

bool find_alternatives(ZHfstOspeller& speller, size_t suggs) {
	for (size_t k=1 ; k <= cw ; ++k) {
		buffer.clear();
		words[cw-k].buffer.toUTF8String(buffer);
		hfst_ol::CorrectionQueue corrections = speller.suggest(buffer);

		if (corrections.size() == 0) {
			continue;
		}

		std::cout << "&";
		// Because speller.set_queue_limit() doesn't actually work, hard limit it here
		for (size_t i=0, e=corrections.size() ; i<e && i<suggs ; ++i) {
			std::cout << "\t";

			buffer.clear();
			if (cw - k != 0) {
				words[0].buffer.tempSubString(0, words[cw-k].start).toUTF8String(buffer);
			}
			if (uc_all) {
				UnicodeString::fromUTF8(corrections.top().first).toUpper().toUTF8String(buffer);
			}
			else if (uc_first) {
				uc_buffer.setTo(UnicodeString::fromUTF8(corrections.top().first));
				ubuffer.setTo(uc_buffer, 0, 1);
				ubuffer.toUpper();
				ubuffer.append(uc_buffer, 1, uc_buffer.length()-1);
				ubuffer.toUTF8String(buffer);
			}
			else {
				buffer.append(corrections.top().first);
			}
			if (cw - k != 0) {
				words[0].buffer.tempSubString(words[cw-k].start + words[cw-k].count).toUTF8String(buffer);
			}

			std::cout << buffer;
			corrections.pop();
		}
		std::cout << std::endl;
		return true;
	}
	return false;
}

bool is_valid_word(ZHfstOspeller& speller, const std::string& word, size_t suggs) {
	ubuffer.setTo(UnicodeString::fromUTF8(word));

	if (word.size() == 13 && word[5] == 'D' && word == "nuvviDspeller") {
		uc_first = false;
		uc_all = false;
		words[0].start = 0;
		words[0].count = ubuffer.length();
		words[0].buffer = ubuffer;
		cw = 1;
		return false;
	}

	uc_first = false;
	uc_all = true;
	bool has_letters = false;
	for (int32_t i=0 ; i<ubuffer.length() ; ++i) {
		if (u_isalpha(ubuffer[i])) {
			has_letters = true;
			if (u_isupper(ubuffer[i]) && uc_all) {
				uc_first = true;
			}
			else if (u_islower(ubuffer[i])) {
				uc_all = false;
				break;
			}
		}
	}

	// If there are no letters in this token, just ignore it
	if (has_letters == false) {
		return true;
	}

	size_t ichStart = 0, cchUse = ubuffer.length();
	const UChar *pwsz = ubuffer.getTerminatedBuffer();

	// Always test the full given input
	words[0].buffer.remove();
	words[0].start = ichStart;
	words[0].count = cchUse;
	words[0].buffer = ubuffer;
	cw = 1;

	if (cchUse > 1 && !verbatim) {
		size_t count = cchUse;
		while (count && !u_isalnum(pwsz[ichStart+count-1])) {
			--count;
		}
		if (count != cchUse) {
			// If the input ended with non-alphanumerics, test input with non-alphanumerics trimmed from the end
			words[cw].buffer.remove();
			words[cw].start = ichStart;
			words[cw].count = count;
			words[cw].buffer.append(pwsz, words[cw].start, words[cw].count);
			++cw;
		}

		size_t start = ichStart, count2 = cchUse;
		while (start < ichStart+cchUse && !u_isalnum(pwsz[start])) {
			++start;
			--count2;
		}
		if (start != ichStart) {
			// If the input started with non-alphanumerics, test input with non-alphanumerics trimmed from the start
			words[cw].buffer.remove();
			words[cw].start = start;
			words[cw].count = count2;
			words[cw].buffer.append(pwsz, words[cw].start, words[cw].count);
			++cw;
		}

		if (start != ichStart && count != cchUse) {
			// If the input both started and ended with non-alphanumerics, test input with non-alphanumerics trimmed from both sides
			words[cw].buffer.remove();
			words[cw].start = start;
			words[cw].count = count - (cchUse - count2);
			words[cw].buffer.append(pwsz, words[cw].start, words[cw].count);
			++cw;
		}
	}

	for (size_t i=0, e=cw ; i<e ; ++i) {
		// If we are looking for suggestions, don't use the cache
		valid_words_t::iterator it = suggs ? valid_words.end() : valid_words.find(words[i].buffer);

		if (it == valid_words.end()) {
			buffer.clear();
			words[i].buffer.toUTF8String(buffer);
			bool valid = speller.spell(buffer);
			it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;

			if (!valid && !verbatim) {
				// If the word was not valid, fold it to lower case and try again
				buffer.clear();
				ubuffer = words[i].buffer;
				ubuffer.toLower();
				ubuffer.toUTF8String(buffer);

				// Add the lower case variant to the list so that we get suggestions using that, if need be
				words[cw].start = words[i].start;
				words[cw].count = words[i].count;
				words[cw].buffer = ubuffer;
				++cw;

				// Don't try again if the lower cased variant has already been tried
				valid_words_t::iterator itl = suggs ? valid_words.end() : valid_words.find(ubuffer);
				if (itl != valid_words.end()) {
					it->second = itl->second;
					it = itl;
				}
				else {
					valid = speller.spell(buffer);
					it->second = valid; // Also mark the original mixed case variant as whatever the lower cased one was
					it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;
				}
			}

			if (!valid && !verbatim && (uc_all || uc_first)) {
				// If the word was still not valid but had upper case, try a first-upper variant
				buffer.clear();
				ubuffer.setTo(words[i].buffer, 0, 1);
				ubuffer.toUpper();
				uc_buffer.setTo(words[i].buffer, 1);
				uc_buffer.toLower();
				ubuffer.append(uc_buffer);
				ubuffer.toUTF8String(buffer);

				// Add the first-upper variant to the list so that we get suggestions using that, if need be
				words[cw].start = words[i].start;
				words[cw].count = words[i].count;
				words[cw].buffer = ubuffer;
				++cw;

				// Don't try again if the first-upper variant has already been tried
				valid_words_t::iterator itl = suggs ? valid_words.end() : valid_words.find(ubuffer);
				if (itl != valid_words.end()) {
					it->second = itl->second;
					it = itl;
				}
				else {
					valid = speller.spell(buffer);
					it->second = valid; // Also mark the original mixed case variant as whatever the first-upper one was
					it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;
				}
			}
		}

		if (it->second == true) {
			return true;
		}
	}

	return false;
}

int zhfst_spell(const char* zhfst_filename) {
	ZHfstOspeller speller;
	try {
		speller.read_zhfst(zhfst_filename);
	}
	catch (hfst_ol::ZHfstMetaDataParsingError zhmdpe) {
		fprintf(stderr, "cannot finish reading zhfst archive %s:\n%s.\n", zhfst_filename, zhmdpe.what());
		return EXIT_FAILURE;
	}
	catch (hfst_ol::ZHfstZipReadingError zhzre) {
		fprintf(stderr, "cannot read zhfst archive %s:\n%s.\n", zhfst_filename, zhzre.what());
		return EXIT_FAILURE;
	}
	catch (hfst_ol::ZHfstXmlParsingError zhxpe) {
		fprintf(stderr, "Cannot finish reading index.xml from %s:\n%s.\n", zhfst_filename, zhxpe.what());
		return EXIT_FAILURE;
	}

	std::cout << "@@ hfst-ospell-office is alive" << std::endl;

	std::string line;
	std::string word;
	std::istringstream ss;
	while (std::getline(std::cin, line)) {
		while (!line.empty() && std::isspace(line[line.size()-1])) {
			line.resize(line.size()-1);
		}
		if (line.empty()) {
			continue;
		}
		// Just in case anyone decides to use the speller for a minor eternity
		if (valid_words.size() > 20480) {
			valid_words.clear();
		}

		ss.clear();
		ss.str(line);
		size_t suggs = 0;
		char c = 0;
		if (!(ss >> suggs) || !ss.get(c) || !std::getline(ss, line)) {
			std::cout << "!" << std::endl;
			continue;
		}

		if (is_valid_word(speller, line, suggs)) {
			std::cout << "*" << std::endl;
			continue;
		}

		if (!suggs || !find_alternatives(speller, suggs)) {
			std::cout << "#" << std::endl;
		}
	}
    return EXIT_SUCCESS;
}

int main(int argc, char **argv) {
	UErrorCode status = U_ZERO_ERROR;
	u_init(&status);
	if (U_FAILURE(status) && status != U_FILE_ACCESS_ERROR) {
		std::cerr << "Error: Cannot initialize ICU. Status = " << u_errorName(status) << std::endl;
		return -1;
	}

	ucnv_setDefaultName("UTF-8");
	uloc_setDefault("en_US_POSIX", &status);

	std::vector<std::string> args(argv, argv+argc);
	for (std::vector<std::string>::iterator it=args.begin() ; it != args.end() ; ) {
		if (*it == "--verbatim") {
			verbatim = true;
			it = args.erase(it);
		}
		else {
			++it;
		}
	}

	if (args.size() < 2) {
		throw std::invalid_argument("Must pass a zhfst as argument");
	}

	int rv = zhfst_spell(args[1].c_str());

	u_cleanup();
	return rv;
}
<|endoftext|>