///
/// Utility classes
/// 14 Mar 2009 - D.Bozarth - Sonoma State Engineering Science
///
#region Using directives
using System;
using System.Collections.Generic;
using System.IO;
#endregion
namespace Utility
{
/*
* T, U should be Comparable
*/
public class Pair
{
#region Methods of class Pair
//
public Pair() { }
public Pair( T first, U second, string delim ) {
this.First = first;
this.Second = second;
this.DelimString = delim;
}
public Pair( T first, U second ) {
this.First = first;
this.Second = second;
this.DelimString = ", ";
}
public bool Equals( Pair other ) {
return other.First.Equals( this.First )
&& other.Second.Equals(this.Second);
}
public string ToString( string delimiter ) {
return First.ToString() + DelimString + Second.ToString();
}
//
#endregion
#region Properties of class Pair
//
public T First { get {return first;} set {first = value;} }
public U Second { get {return second;} set {second = value;} }
public string DelimString { get {return delimString;} set {delimString = value;} }
//
#endregion
#region Data of class Pair
//
private T first;
private U second;
private string delimString;
//
#endregion
} // class Pair
public class Io
{
// The following 3 class members must be maintained in sync:
public enum Result
{ Exception = -4, BadFormat, BadIndex, Underflow, OK = 0, Overflow, Mismatch, NoAction };
private static string[] ResultMsg
= { "Exception", "Bad Format", "Bad Index", "Underflow", "OK", "Overflow", "Mismatch", "No Action" };
const int ResultOffset = 4; // Translates Result to ResultMsg.
// --------------------------------------------------------
public static string GetResultTag( Io.Result ior ) {
return ResultMsg[ (int) ior + ResultOffset ];
}
} // class Io
public static class StreamProcessor
{
public const int BufSizeDflt = 0x1000;
/*
* Post: No problem -> return 0; otherwise return nonzero
* parameters may have been modified.
*/
public delegate Io.Result Transform( Stream inStream, Stream outStream, int[] parameters );
/*
* If sizes of inStream and h * w don't match, the method will still perform
* .. a transpose, but the result may not be useful.
*/
public static Io.Result TransformTranspose( Stream inStream, Stream outStream, int[] hw ) {
int h = hw[0];
int w = hw[1];
byte[,] buf = new byte[h, w];
Io.Result rtn = StreamToArray2D( inStream, buf );
for ( int i = 0; i < w; ++i ) {
for ( int j = 0; j < h; ++j ) {
outStream.WriteByte( buf[j, i] ); // transposed
}
}
return rtn;
} // method TransformTranspose
public static Io.Result TransformCopyStream( Stream inStream, Stream outStream, int[] bufSzOrEmpty ) {
int bufSize;
if ( 0 < bufSzOrEmpty.Length ) {
bufSize = bufSzOrEmpty[0];
} else {
bufSize = BufSizeDflt;
}
CopyStream( inStream, outStream, bufSize );
return Io.Result.OK;
}
/*
* Post: outStream has the indicated prefix stripped off, otherwise identical to inStream.
*/
public static Io.Result CopyStreamMinusPrefix( Stream inStream, Stream outStream, int preSize, int bufSize ) {
byte[] nothing = new byte[ preSize ];
Io.Result rtn = StreamProcessor.ReadByteStream( nothing, 0, preSize, inStream );
if ( rtn != Io.Result.BadIndex ) {
CopyStream( inStream, outStream, bufSize );
}
return rtn;
}
/*
* Adapted from http://stackoverflow.com/questions/129305/
* .. how-to-write-the-content-of-one-stream-into-another-stream-in-net
*/
public static void CopyStream( Stream inStream, Stream outStream, int bufSize ) {
byte[] buffer = new byte[ bufSize ];
int nRead;
while ( 0 < (nRead = inStream.Read( buffer, 0, bufSize )) ) {
outStream.Write(buffer, 0, nRead);
}
}
public static void MergeStreams( Stream outStream, Stream[] inStreams, int[] counts ) {
bool goFlg = true;
while ( goFlg ) {
goFlg = false;
for ( int i = 0; i < counts.Length; ++i ) {
Stream source = inStreams[i];
for ( int j = 0; j < counts[i]; ++j ) {
if ( source.Position < source.Length ) {
int next = source.ReadByte();
outStream.WriteByte( (byte) next );
}
}
if ( source.Position < source.Length ) goFlg = true;
}
}
}
public static Io.Result StreamToArray2D( Stream inStream, byte[,] mat ) {
int h = mat.GetLength( 0 );
int w = mat.GetLength( 1 );
long len = inStream.Length;
Io.Result rtn = Io.Result.OK;
inStream.Position = 0;
for ( int i = 0; i < h; ++i ) {
for ( int j = 0; j < w; ++j ) {
if ( inStream.Position < len ) {
mat[i, j] = (byte) inStream.ReadByte();
} else {
// Stream has overflown the array.
rtn = Io.Result.Overflow;
break;
}
}
}
if ( inStream.Position + 1 < len ) {
// Stream couldn't fill the array.
rtn = Io.Result.Underflow;
}
return rtn;
} // method StreamToArray2D
/*
* outStream will be populated starting at position outStream.Length
*/
public static void Array2DToStream( Stream outStream, byte[,] mat ) {
int h = mat.GetLength( 0 );
int w = mat.GetLength( 1 );
for ( int i = 0; i < h; ++i ) {
for ( int j = 0; j < w; ++j ) {
outStream.WriteByte( mat[i, j] );
}
}
}
/*
* Limitation: Works only with integer-sized arrays and streams.
*/
public static Io.Result ReadByteStream( byte[] buf, int start, int count, Stream inStream ) {
int sum = start + count;
if ( start < 0 || count < 0 || buf.Length < sum || (int) inStream.Length < sum ) {
return Io.Result.BadIndex;
}
for ( int n = start, m = 0; n < count; n += m ) {
m = inStream.Read( buf, n, count - n );
}
return Io.Result.OK;
}
} // class StreamProcessor
public static class FileProcessor
{
public static void Catenate( string outFile, string[] inFiles, bool addByteCount ) {
File.Delete( outFile );
FileStream outStream = File.OpenWrite( outFile );
try {
foreach ( string file in inFiles ) {
byte[] next = File.ReadAllBytes( file );
if ( addByteCount ) {
byte[] nextBytes = BitConverter.GetBytes( next.Length );
outStream.Write( nextBytes, 0, nextBytes.Length );
}
outStream.Write( next, 0, next.Length );
}
} catch ( Exception ex ) {
outStream.Close();
throw ex;
}
outStream.Close();
} // method Catenate
/*
* Pre: The content of inFile is partitioned by a set of leading counts,
* .. each of which gives the length in bytes of the following subset.
* Each count is an Int32.
* Post: Each file pointed by outFiles, has the number of bytes indicated
* .. by its corresponding count in inFile.
* Each count has been discarded.
*/
public static Io.Result Decatenate( string inFile, string[] outFiles ) {
FileStream inStream = File.OpenRead( inFile );
Io.Result rtn = Io.Result.OK;
try {
foreach ( string file in outFiles ) {
int sz = sizeof( Int32 );
byte[] countBytes = new byte[ sz ];
rtn = StreamProcessor.ReadByteStream( countBytes, 0, sz, inStream );
if ( rtn == Io.Result.BadIndex ) return rtn;
Int32 count = BitConverter.ToInt32( countBytes, 0 );
byte[] next = new byte[count];
rtn = StreamProcessor.ReadByteStream( next, 0, count, inStream );
if ( rtn == Io.Result.BadIndex ) return rtn;
File.Delete( file );
FileStream outStream = File.OpenWrite( file );
try {
outStream.Write( next, 0, count );
} catch ( Exception ex ) {
outStream.Close();
throw ex;
}
outStream.Close();
}
} catch ( Exception ex ) {
inStream.Close();
throw ex;
}
inStream.Close();
return rtn;
} // method Decatenate
/*
* Post: outFile has the indicated prefix stripped off, otherwise identical to inFile.
*/
public static Io.Result CopyFileMinusPrefix( string inFile, string outFile, int preSize, int bufSize ) {
Io.Result rtn = Io.Result.OK;
FileStream inStream = File.OpenRead( inFile );
FileStream outStream = File.OpenWrite( outFile );
try {
rtn = StreamProcessor.CopyStreamMinusPrefix( inStream, outStream, preSize, bufSize );
} catch ( Exception ex ) {
string msg = "Caught in FileProcessor.CopyFileMinusPrefix(string,string,int,int)";
throw new Exception( msg, ex );
} finally {
outStream.Close();
inStream.Close();
}
return rtn;
} // method CopyFileMinusPrefix
} // class FileProcessor
public static class ArrayQuery
{
public static bool Contains( T[] ary, T val ) {
foreach ( T test in ary ) {
if ( test.Equals(val) ) return true;
}
return false;
}
public static void JustifyLimits(
ref int tstLo,
ref int tstHi,
int limLo,
int limHi
){
tstLo = limHi < tstLo ? limHi : tstLo;
tstHi = limHi < tstHi ? limHi : tstHi;
tstLo = tstLo < limLo ? limLo : tstLo;
tstHi = tstHi < limLo ? limLo : tstHi;
if ( tstHi < tstLo ) {
int tmp = tstHi; tstHi = tstLo; tstLo = tmp;
}
} // method JustifyLimits
} // class ArrayQuery
public static class ByteArrayManipulator
{
public static byte[,] Transpose( byte[,] mat ) {
int h = mat.GetLength(0);
int w = mat.GetLength(1);
byte[,] x = new byte[w, h];
for ( int i = 0; i < h; ++i ) {
for ( int j = 0; j < w; ++j ) {
x[j, i] = mat[i, j];
}
}
return x;
} // method Transpose
/*
* PickPeriodicValues:
* Return the sub-matrix composed of all chosen cells.
* X must be a rectangular array.
* Periodic specification: m[], n specify the rows; p[], q specify the columns.
* If n == 0, then all rows are specified.
* If q == 0, then all cols are specified.
* If m is empty or p is empty, then an empty array is returned.
* The intersection of the row spec & col spec, is chosen.
* Example: m=[2,3], n=4 => The 3rd & 4th rows in each group of 4 rows is specified.
* p=[1], q=6 => The 2nd col in each group of 6 cols is specified.
* The returned matrix contains values with the following indices:
* (2, 3), (3, 1), (2, 7), (3, 7)...
* (6, 1), (7, 1), (6, 7), (7, 7)... etc.
* 7 Feb 2009 - Ported from Matlab. D.Bozarth
*/
public static byte[,] PickPeriodicValues
( byte[,] X,
int[] m, int n,
int[] p, int q
){
List block = new List();
int h = 0;
int w = 0;
if ( 0 < m.Length && 0 < p.Length ) {
h = X.GetLength(0);
w = X.GetLength(1);
}
for ( int i = 0; i < h; ++i ) {
if ( n == 0 || ArrayQuery.Contains(m, (i+n) % n) ) {
// The current row is specified for output.
if ( q == 0 ) {
// All columns are specified for output.
byte[] row = new byte[w];
for ( int k = 0; k < w; ++k ) row[k] = X[i, k];
block.Add( row );
} else {
List list = new List();
for ( int j = 0; j < w; ++j ) {
if ( ArrayQuery.Contains(p, (j+q) % q) ) {
// The current column is specified for output.
list.Add( X[i, j] );
}
}
byte[] row = new byte[list.Count];
list.CopyTo( row );
block.Add( row );
}
}
}
int a = block.Count;
if ( a == 0 ) {
return new byte[0, 0];
}
int b = block[0].Length;
byte[,] Y = new byte[a, b];
for ( int i = 0; i < a; ++i ) {
for ( int j = 0; j < b; ++j ) {
Y[i, j] = block[i][j];
}
}
return Y;
} // method PickPeriodicValues
/*
* Pre: Dimensions of dest array must be able to accomodate the addition
* .. of src at the indicated starting indices.
* Post: dest contains all values from src, in addition to its prior contents.
*/
public static void CopyArrayByColumn(
byte[,] dest, int ndxV, int ndxH, byte[,] src
){
int dimV = src.GetLength(0);
int dimH = src.GetLength(1);
for ( int i = 0; i < dimV; ++i ) {
int row = ndxV + i;
for ( int j = 0; j < dimH; ++j ) {
dest[row, ndxH + j] = src[i, j];
}
}
} // method CopyArrayByColumn
/*
* Pre: (1) src1 and src2 must have the same dimensions.
* (2) Dimensions of dest array must be able to accomodate the addition
* .. of src1 & src2 interleaved at the indicated starting indices.
* Post: dest contains interleaved data from src1 & src2, in addition to
* .. its prior contents.
*/
public static void InterleaveTwoArraysByColumn(
byte[,] dest, int ndxV, int ndxH,
byte[,] src1, byte[,] src2
){
int dimV = src1.GetLength(0);
int dimH = src1.GetLength(1);
for ( int i = 0; i < dimV; ++i ) {
int row = ndxV + i;
for ( int j = 0; j < dimH; ++j ) {
dest[row, ndxH + 2*j] = src1[i, j];
dest[row, ndxH + 2*j + 1] = src2[i, j];
}
}
} // method InterleaveTwoArraysByColumn
/*
* Pre: All 4 arrays must have the same dimensions.
* The width of each array must divide nBytesPerValue.
* Each nBytesPerComponent-byte set of array positions represents
* .. a single-precision floating-point number.
* Post: Using the first pair of arrays as input, the conversion indicated by
* ... toPolFlg (0 => polar-to-rectangular, 1 => rectangular-to-polar)
* ... appears in the second pair of arrays.
*/
public static void ConvertCoordinates(
byte[,] reOrMa, byte[,] imOrAn,
byte[,] maOrRe, byte[,] anOrIm,
int nBytesPerValue, bool toPolFlg
){
int h = reOrMa.GetLength(0);
int w = reOrMa.GetLength(1) / nBytesPerValue;
for ( int i = 0; i < h; ++i ) {
for ( int j = 0; j < w; ++j ) {
int pos = j * nBytesPerValue;
byte[] a = new byte[nBytesPerValue];
byte[] b = new byte[nBytesPerValue];
for ( int k = 0; k < nBytesPerValue; ++k ) {
a[k] = reOrMa[i, pos + k];
b[k] = imOrAn[i, pos + k];
}
float af = BitConverter.ToSingle( a, 0 );
float bf = BitConverter.ToSingle( b, 0 );
double ad = (double) af, xd;
double bd = (double) bf, yd;
if ( toPolFlg ) {
xd = Math.Sqrt( ad*ad + bd*bd ); // Magnitude
yd = Math.Atan( Math.Abs(bd) / Math.Abs(ad) ); // Angle
if ( bd < 0 && ad < 0 ) {
yd += Math.PI;
} else {
if ( bd < 0 ) yd = 0 - yd;
if ( ad < 0 ) yd = Math.PI - yd;
}
} else {
xd = ad * Math.Cos( bd ); // Real
yd = ad * Math.Sin( bd ); // Imaginary
}
float xf = (float) xd;
float yf = (float) yd;
a = BitConverter.GetBytes( xf );
b = BitConverter.GetBytes( yf );
for ( int k = 0; k < nBytesPerValue; ++k ) {
maOrRe[i, pos + k] = a[k];
anOrIm[i, pos + k] = b[k];
}
}
}
} // method ConvertCoordinates
} // class ByteArrayManipulator
//
// BinaryReader, BinaryWriter, BitConverter were the way to go instead of this!!
// .. next time ...
//
public static class ByteArrayConverter
{
/*
* Pre: (1) inStream: Each comma-separated group of bytes represents a numeric char array.
* (2) Each numeric char array represents a single byte value.
* (3) inStream may also contain newline separators.
* Post: return -1 => inStream is empty, content of myByte is not valid.
* return 0 => myByte contains a valid byte converted from inStream.
*/
public static int ReadByteFromCsvStream( Stream inStream, ref byte myByte ) {
Object obj = (Object) myByte;
int result = ReadValueFromCsvStream( inStream, ref obj );
myByte = (byte) obj;
return result;
} // method ReadByteFromCsvStream
/*
* Pre: (1) inStream: Each comma-separated group of bytes represents a numeric char array.
* (2) Each numeric char array represents a single float value.
* (3) inStream may also contain newline separators.
* Post: return -1 => inStream is empty, content of myFloat is not valid.
* return 0 => myFloat contains a valid float converted from inStream.
*/
public static int ReadFloatFromCsvStream( Stream inStream, ref float myFloat ) {
Object obj = (Object) myFloat;
int result = ReadValueFromCsvStream( inStream, ref obj );
myFloat = (float) obj;
return result;
} // method ReadFloatFromCsvStream
/*
* Pre: (1) inStream: Each comma-separated group of bytes represents a numeric char array.
* (2) Each numeric char array represents a single object value.
* (3) inStream may also contain newline separators.
* Post: return -1 => inStream is empty, content of obj is not valid.
* return 0 => obj wraps a valid numeric value converted from inStream.
*/
private static int ReadValueFromCsvStream( Stream inStream, ref Object obj ) {
List scratch = new List();
bool eolFlg = false;
int next = 1;
while ( next == 1 ) {
next = inStream.ReadByte();
if ( next == -1 ) break;
char c = Convert.ToChar( next );
if ( c == '\r' ) {
eolFlg = true;
next = 1;
continue;
}
if ( c == ',' || c == '\n' ) {
Type t = obj.GetType();
if ( 0 < scratch.Count ) {
byte[] bar = scratch.ToArray();
byte[] buf = new byte[2 * bar.Length];
for ( int i = 0; i < bar.Length; ++i ) {
buf[2 * i] = bar[i];
buf[2*i+1] = 0;
}
int ndx = 0;
string str = Utility.ByteArrayConverter.GetString( buf, ref ndx, bar.Length );
if ( t.Name == "Single" ) {
float myFloat = Convert.ToSingle( str ); obj = (Object) myFloat;
double myDubb = Convert.ToDouble( str );
// The float will be as close as it can, to what you're telling it to be.
//byte[] test = new byte[4];
//int zro = 0;
//SetFloat( test, ref zro, myFloat );
//zro = 0;
} else {
if ( t.Name == "Byte" ) {
byte myByte = Convert.ToByte( str ); obj = (Object) myByte;
} else {
next = -1;
break;
}
}
scratch.Clear();
} else {
if ( eolFlg == false ) {
if ( t.Name == "Single" ) {
float myFloat = 0; obj = (Object) myFloat;
} else {
if ( t.Name == "Byte" ) {
byte myByte = 0; obj = (Object) myByte;
} else {
next = -1;
break;
}
}
} else { // End of line
eolFlg = false;
continue;
}
} // if 0
next = 0;
} else {
scratch.Add( Convert.ToByte(next) );
next = 1;
} // if c
} // while
return next;
} // method ReadValueFromCsvStream
// Post: ndx has been incremented by the size of one short.
public static void SetShort( byte[] buf, ref int ndx, short toSet ) {
byte[] bytes = BitConverter.GetBytes( toSet );
Array.Copy( bytes, 0, buf, ndx, bytes.Length );
ndx += bytes.Length;
}
// Post: ndx has been incremented by the size of one integer.
public static void SetInt( byte[] buf, ref int ndx, int toSet ) {
byte[] bytes = BitConverter.GetBytes( toSet );
Array.Copy( bytes, 0, buf, ndx, bytes.Length );
ndx += bytes.Length;
}
// Post: ndx has been incremented by the size of one long.
public static void SetLong( byte[] buf, ref int ndx, long toSet ) {
byte[] bytes = BitConverter.GetBytes( toSet );
Array.Copy( bytes, 0, buf, ndx, bytes.Length );
ndx += bytes.Length;
}
// Post: ndx has been incremented by the size of one float.
public static void SetFloat( byte[] buf, ref int ndx, float toSet ) {
byte[] bytes = BitConverter.GetBytes( toSet );
Array.Copy( bytes, 0, buf, ndx, bytes.Length );
ndx += bytes.Length;
}
// Post: ndx has been incremented by the size of one Pair.
public static void SetPairFloat( byte[] buf, ref int ndx, Pair toSet ) {
SetFloat( buf, ref ndx, toSet.First );
SetFloat( buf, ref ndx, toSet.Second );
}
// Post: ndx has been incremented by the size of one Pair.
public static void SetPairInt( byte[] buf, ref int ndx, Pair toSet ) {
SetInt( buf, ref ndx, toSet.First );
SetInt( buf, ref ndx, toSet.Second );
}
// Post: ndx has been incremented by the size of one char.
public static void SetChar( byte[] buf, ref int ndx, char toSet ) {
byte[] bytes = BitConverter.GetBytes( toSet );
Array.Copy( bytes, 0, buf, ndx, bytes.Length );
ndx += bytes.Length;
}
// Post: ndx has been incremented by toSet.Length * sizeof(char)
public static void SetString( byte[] buf, ref int ndx, string toSet ) {
for ( int i = 0; i < toSet.Length; ++i ) {
SetChar( buf, ref ndx, toSet[i] );
}
}
// Post: ndx has been incremented by the size of one short.
public static short GetShort( byte[] buf, ref int ndx ) {
byte[] bytes = new byte[ sizeof(short) ];
Array.Copy( buf, ndx, bytes, 0, bytes.Length );
ndx += ( bytes.Length );
return BitConverter.ToInt16( bytes, 0 );
}
// Post: ndx has been incremented by the size of one integer.
public static int GetInt( byte[] buf, ref int ndx ) {
byte[] bytes = new byte[ sizeof(int) ];
Array.Copy( buf, ndx, bytes, 0, bytes.Length );
ndx += ( bytes.Length );
return BitConverter.ToInt32( bytes, 0 );
}
// Post: ndx has been incremented by the size of one long.
public static long GetLong( byte[] buf, ref int ndx ) {
byte[] bytes = new byte[ sizeof(long) ];
Array.Copy( buf, ndx, bytes, 0, bytes.Length );
ndx += ( bytes.Length );
return BitConverter.ToInt64( bytes, 0 );
}
// Post: ndx has been incremented by the size of one float. ** Why not use BitConverter.ToSingle ...**
public static float GetFloat( byte[] buf, ref int ndx ) {
byte[] bytes = new byte[ sizeof(float) ];
Array.Copy( buf, ndx, bytes, 0, bytes.Length );
ndx += ( bytes.Length );
return BitConverter.ToSingle( bytes, 0 );
}
// Post: ndx has been incremented by the size of one Pair.
public static Pair GetPairFloat( byte[] buf, ref int ndx ) {
float real = GetFloat( buf, ref ndx );
float imag = GetFloat( buf, ref ndx );
Pair complex = new Pair();
complex.First = real;
complex.Second = imag;
return complex;
}
// Post: ndx has been incremented by the size of one Pair.
public static Pair GetPairInt( byte[] buf, ref int ndx ) {
int real = GetInt( buf, ref ndx );
int imag = GetInt( buf, ref ndx );
Pair complex = new Pair();
complex.First = real;
complex.Second = imag;
return complex;
}
// Pre: buf represents a Unicode character array (2 bytes per char).
// Post: ndx has been incremented by sizeof(char).
public static char GetChar( byte[] buf, ref int ndx ) {
byte[] bytes = new byte[ sizeof(char) ];
Array.Copy( buf, ndx, bytes, 0, bytes.Length );
ndx += ( bytes.Length );
return BitConverter.ToChar( bytes, 0 );
}
// Pre: buf represents a Unicode character array (2 bytes per char).
// Post: ndx has been incremented by strLen * sizeof(char)
public static string GetString( byte[] buf, ref int ndx, int strLen ) {
string s = "";
for ( int i = 0; i < strLen; ++i ) {
s += GetChar( buf, ref ndx );
}
return s;
}
} // class ByteArrayConverter
} // namespace Utility