/*
* Soubor: proj3.c
* Datum: 8.12.2004
* Autor: Kamil Dudka, xdudka00@stud.fit.vutbr.cz
* Projekt: IZP c. 3 - Maticove oprace
* Popis: viz. Dokumentace
*
* Prosím, neodevzdávejte zdrojové kódy, které jsou zde k dispozici,
* jako Vaše školní projekty. Děkuji :-)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Umoznovane oprace s maticemi - poradi je dulezite
typedef enum
{
MOPER_NULL,
MOPER_ADD,
MOPER_MULT,
MOPER_MONO,
MOPER_SPIRAL,
MOPER_DB,
MOPER_WORM
} TypOperace;
// Charakterizuje monotonost
typedef enum
{
MONO_KLESA = -1,
MONO_NULL,
MONO_ROSTE
} TypMono, *PTypMono;
// Struktura charakterizujici matici
typedef struct
{
unsigned int rows, cols; // Rozmery matice
int *matrix; // Ukazatel na prvni prvek matice
} TMatrix, *PTMatrix;
// Vypise se na stdout pokud operace nema legalni vysledek
const char MatrixIllegal[] = "#\n";
// Vypise se na stdout, je-li matice monotoni
const char MatrixMono[] = "1\n";
// Funkce jsou popsany nize (a take v dokumentaci)
int ctiMatici (PTMatrix, const char *);
int tiskniMatici (const PTMatrix);
int prictiMatici (const PTMatrix, const PTMatrix);
int nasobMatice (PTMatrix, const PTMatrix, const PTMatrix);
int monoMatice (const PTMatrix);
inline int monoCheck (PTypMono, const int *, const int *);
int vypisSpiralu (const PTMatrix);
int vypisTerc (const PTMatrix);
int wormTrans (const PTMatrix);
void wormTransOne (const PTMatrix, int);
// Vyctovy typ errNum - konstanty predstavuji cislo chyby
typedef enum errN
{
ERR_NULL,
ERR_NOARG,
ERR_BADARG1,
ERR_MISSARG,
ERR_MATRIX1,
ERR_MATRIX2
} errNum;
// Vlastni chybove hlasky
const char *err_msg[] =
{
"",
"Nebyly zadany parametry. Pro vypis napovedy zadejte parametr -h\n",
"Neznama operace\n",
"Nebyl zadan povinny parametr\n",
"Nelze nacist matici 1\n",
"Nelze nacist matici 2\n"
};
// Vypise chybovou hlasku na stderr
inline void printError (errNum n)
{
fprintf (stderr,err_msg[n]);
}
// Vypise help na standartni vystup
inline void printHelp()
{
printf (
"Program Maticove operace\n"
"Autor: Kamil Dudka\n"
"Tento program provadi ruzne operace s maticemi. Podrobny popis techto opreaci je\n"
"uveden v prilozene dokumentaci ve formatu pdf.\n"
"Pouziti: proj3 -h\n"
" proj3 -add|-mult M1 M2\n"
" proj3 -mono|-spiral|-dartboard|-worm M1\n"
"Popis parametru:\n"
" -h Vypise tuto obrazovku s napovedou.\n"
" -add Secte matice M1 a M2 a vysledek vypise na stdout.\n"
" -mult Vynasobi matice M1 a M2 a vysledek vypise na stdout.\n"
" -mono Pokud je matice M1 monotoni, vypise 1, jinak vypise #.\n"
" -spiral Vypise prvky matice M1 ve tvaru spiraly.\n"
" -dartboard Spocita aritmeticky prumer prvku na kruznicich terce matice M1\n"
" -worm Provede transformaci typu \"zizala\" matice M1 a vypise na stdout.\n"
" M1 Jmeno souboru pro nacteni matice 1\n"
" M2 Jmeno souboru pro nacteni matice 2\n"
);
}
// Hlavni program
int main (int argc, char *argv[])
{
// Provadena operace zadana prvnim parametrem programu
// (vyctovy typ)
TypOperace operace;
// Defnice struktur typu matice
// Pametovy prostor pro vlastni prvky matic
// se zatim nealokuje !!!
TMatrix m1, m2, m3;
// Inicializace ukazatelu zabrani
// volani free() na konci programu
// pro matice, ktere nebyly alokovany
m1.matrix = NULL;
m2.matrix = NULL;
m3.matrix = NULL;
if (argc<2)
{ // Program spusten bez parametru
printError(ERR_NOARG);
return EXIT_FAILURE;
}
if (strcmp(argv[1], "-h")==0)
{ // Vypis napovedy
printHelp();
return EXIT_SUCCESS;
}
// Rozpoznani typu operace z prvniho parametru
if (strcmp(argv[1], "-add")==0) operace=MOPER_ADD;
else if (strcmp(argv[1], "-mult")==0) operace=MOPER_MULT;
else if (strcmp(argv[1], "-mono")==0) operace=MOPER_MONO;
else if (strcmp(argv[1], "-spiral")==0) operace=MOPER_SPIRAL;
else if (strcmp(argv[1], "-dartboard")==0) operace=MOPER_DB;
else if (strcmp(argv[1], "-worm")==0) operace=MOPER_WORM;
else
{ // Parametr 1 nerozpoznan
printError(ERR_BADARG1);
return EXIT_FAILURE;
}
// Kontrola zadani nazvu matic (pritomnost 2. a 3. parametru)
if ((argc<3)||((operace<=MOPER_MULT)&&(argc<4)))
{
printError(ERR_MISSARG);
return EXIT_FAILURE;
}
// Nacteni prvni matice ze souboru
if (EXIT_SUCCESS!=ctiMatici(&m1,argv[2]))
{ // Chyba pri nacitani 1. matice
printError(ERR_MATRIX1);
return EXIT_FAILURE;
}
if (operace<=MOPER_MULT)
// Nacteni druhe matice ze souboru
if (EXIT_SUCCESS!=ctiMatici(&m2,argv[3]))
{ // Chyba pri nacitani 2. matice
printError(ERR_MATRIX2);
free(m1.matrix);
return EXIT_FAILURE;
}
// Provede se pozadovana operace volanim prislusne funkce
switch (operace)
{
case MOPER_ADD:
if (EXIT_SUCCESS==prictiMatici (&m1, &m2))
tiskniMatici(&m1);
else
printf(MatrixIllegal);
break;
case MOPER_MULT:
if (EXIT_SUCCESS==nasobMatice (&m3, &m1, &m2))
tiskniMatici(&m3);
else
printf(MatrixIllegal);
break;
case MOPER_MONO:
if (monoMatice(&m1))
printf(MatrixMono);
else
printf(MatrixIllegal);
break;
case MOPER_SPIRAL:
vypisSpiralu (&m1);
break;
case MOPER_DB:
vypisTerc (&m1);
break;
case MOPER_WORM:
wormTrans (&m1);
tiskniMatici (&m1);
default: break;
}
// Uvolneni pameti alokovane pro matice
if (NULL!=m1.matrix)
free (m1.matrix);
if (NULL!=m2.matrix)
free (m2.matrix);
if (NULL!=m3.matrix)
free (m3.matrix);
// Pokud nedojde k zadne chybe, program skonci normalne
return EXIT_SUCCESS;
}
// Funkce nacte ze souboru rozmer matice
// Dynamicky alokuje potrebny pametovy prostor
// A naplni prvky matice hodnotami ze souboru
// Pokud dojde k chybe neni alokovana zadna pamet !!!
int ctiMatici (PTMatrix matice, const char* fileName)
{
FILE *mFile; // Ukazatel na otevreny soubor
unsigned long pocetPrvku;
register unsigned long prvek; // Promenna cyklu
register int *pPrvek; // Ukazatel na aktualne nacitany prvek matice
// Otevreni souboru s matici (v rezimu pouze pro cteni)
if (NULL==(mFile=fopen(fileName,"r")))
return EXIT_FAILURE;
// Nacteni rozmeru matice
if (2!=fscanf (mFile,"%u%u",&matice->rows,&matice->cols))
{
fclose (mFile);
return EXIT_FAILURE;
}
// Vypocet celkoveho poctu prvku matice
if (0==(pocetPrvku=matice->rows*matice->cols))
{
fclose (mFile);
return EXIT_FAILURE;
}
// Alokace prostoru pro vlastni matici
if (NULL==(matice->matrix = malloc (pocetPrvku*sizeof(int)) ))
{
fclose (mFile);
return EXIT_FAILURE;
}
// Vlastni cyklus nacitajici prvky matice
// Soubor otvira jako textovy a vyuziva io funkci vyssi urovne
// Nerozlisuje rozlozeni prvku na jednotlivych radcich
for (prvek=0,pPrvek=matice->matrix; prvek<pocetPrvku; prvek++,pPrvek++)
if (1!=fscanf (mFile,"%i",pPrvek) )
{ // Chyba behem vstupu
free (matice->matrix);
fclose (mFile);
return (EXIT_FAILURE);
}
// Pokud je vse v poradku zavru otevreny soubor a vratim EXIT_SUCCESS
fclose (mFile);
return EXIT_SUCCESS;
}
// Tiskne prvky matice na stdout
int tiskniMatici (const PTMatrix matice)
{
// Ze struktury nacte pocet prvku a ukazatel na prvni prvek
const unsigned long pocetPrvku = matice->rows * matice->cols;
register int *pPrvek = matice->matrix;
// Kontrola vstupnich hodnot
if ((NULL==pPrvek)||(0==pocetPrvku))
return EXIT_FAILURE;
// Vytiskne radek s rozmery matice na stdout
printf ("%u %u\n",matice->rows,matice->cols);
// Cyklus vypisuje prvky sekvencne
for (register unsigned long prvek=0; prvek<pocetPrvku; prvek++,pPrvek++)
{
printf ("%i", *pPrvek);
// Pokud byl vypsan posledni prvek na radku
// odradkuje se; jinak se vytiskne mezera
if ((prvek+1)%(matice->cols))
printf (" ");
else
printf ("\n");
}
return EXIT_SUCCESS;
}
// Ke kazdemu prvku z matice "dest"
// pricte odpovidajici prvek z matice "src"
// Matice musi mit stejny rozmer jinak vraci EXIT_FAILURE
int prictiMatici (const PTMatrix dest, const PTMatrix src)
{
// Ze struktury nacte pocet prvku a ukazatele na prvni prvek
const unsigned long pocetPrvku = dest->rows * dest->cols;
int *pDest = dest-> matrix;
int *pSrc = src-> matrix;
// Kontrola vstupnich hodnot
if (
(0==pocetPrvku) ||
(dest->rows!=src->rows) ||
(dest->cols!=src->cols) )
return EXIT_FAILURE;
// Vlastni cyklus pricitajici matici src k matici dest
for (unsigned long prvek=0; prvek<pocetPrvku; prvek++, pDest++, pSrc++)
*pDest += *pSrc;
return EXIT_SUCCESS;
}
// Funkce nasobi matice "m1" a "m2" (v tomto poradi)
// Automaticky si alokuje pametovy prostor pro vyslednou matici
// a ulozi do nej vysledek
// Pokud dojde k chybe, neni alokovana zadna pamet !!!
int nasobMatice (PTMatrix dest, const PTMatrix m1, const PTMatrix m2)
{
// Vypocet poctu prvku vysledne matice
const unsigned long pocetPrvku = m1->rows * m2->cols;
// Pracovni promenne jednotlivych cyklu
// tj. radkovy, sloupcovy a skalarni soucin
unsigned int row, col, i;
// Ukazatele na aktualni prvky jednotlivych matic
int *pDest, *pM1, *pM2;
// Osetreni vstupnich hodnot
if (
(0==pocetPrvku) ||
(0==m1->cols) ||
(m1->cols != m2->rows) )
return EXIT_FAILURE;
// Alokace pametoveho prostoru pro vyslednou matici
if (NULL==(dest->matrix = malloc(pocetPrvku*sizeof(int)) ))
return EXIT_FAILURE;
// Inicializace rozmeru nove matice
dest->rows = m1->rows;
dest->cols = m2->cols;
// Nasledujici dva vnorene cykly prochazi
// jednotlive prvky matice "dest"
for (row=0, pDest= dest->matrix; row< dest->rows; row++)
for (col=0; col< dest->cols; col++, pDest++)
{
// Nastaveni ukazatelu do zdrojovych matic
// na zaklade aktualniho prvku
// pro vypocet skalarniho soucinu
pM1 = m1->matrix + (row * m1->cols);
pM2 = m2->matrix + col;
// Vypocet skalarniho soucinu
*pDest = 0;
for (i=0; i< m1->cols; i++, pM1++, pM2 += m2->cols)
*pDest += (*pM1) * (*pM2);
}
return EXIT_SUCCESS;
}
// Funkce vraci nenulovou hodnotu pokud je matice monotoni
int monoMatice (const PTMatrix m)
{
unsigned int row,col; // pracovni promenne cyklu
register int *pM; // ukazatel na aktualni prvek
TypMono mono; // promenna charakterizujici monotonost
// Scanovani po radcich - viz. funkce monoCheck()
pM = m->matrix;
mono = MONO_NULL;
for (row=0; row< m->rows; row++)
{
pM++;
for (col=1; col< m->cols; col++, pM++)
if (monoCheck (&mono, pM, pM-1))
// Matice neni monotoni
return 0;
}
// Scanovani po sloupcich - viz. funkce monoCheck()
mono = MONO_NULL;
for (col=0; col< m->cols; col++)
{
pM = m->matrix + m->cols + col;
for (row=1; row< m->rows; row++, pM += m->cols)
if (monoCheck (&mono, pM, pM - m->cols))
// Matice neni monotoni
return 0;
}
// Pokud nebyla nalezena zadna odchylka tak je matice monotoni
return 1;
}
// Funkce testuje monotonost dvou prvku
// a srovnava ji s monotonosti predchozich prvku
// Vraci 0 pokud se monotonost shoduje
// nebo 1 pokud se monotonost neshoduje
// pouziva vyctovy typ TypMono
inline int monoCheck (PTypMono mono, const int *aktualni, const int *predchozi)
{
switch (*mono)
{
case MONO_ROSTE:
// Monotonost dvou prvku
// musi byt take rostouci
return (*predchozi > *aktualni);
case MONO_KLESA:
// Monotonost dvou prvku
// musi byt take klesajici
return (*predchozi < *aktualni);
case MONO_NULL:
// Predchozi prvky byly stejne
// nebo se jedna o prvni dvojici
// Hodnota "mono" se inicializuje
// pro dalsi krok
if (*predchozi < *aktualni)
*mono = MONO_ROSTE;
else if (*aktualni < *predchozi)
*mono = MONO_KLESA;
default:return 0;
}
}
// Funkce vypise vsechny prvky matice
// v poradi danem spiralou - viz. zadani
int vypisSpiralu (const PTMatrix m)
{
// Tato tabulka charakterizuje periodickou zmenu smeru pohybu
// pri cteni prvku matice ve tvaru spiraly
// Levy sloupec udava radkovy prirustek
// Pravy sloupec udava sloupcovy prirustek
const int spiralPeriod[4][2] =
{
{0,1},
{-1,0},
{0,-1},
{1,0}
};
// Promenne cyklu
register long int row, col;
long int i=0, dir=0, rowLimit=0, colLimit=0;
// Kontrola vstupnich hodnot
if ( (0==m->rows) || (0==m->cols) )
return EXIT_FAILURE;
// Inicializace nastavi promenne tak
// aby ukazatel v prvni iteraci ukazoval
// na levy dolni roh matice - viz. zadani
row = m->rows - 1;
col = 0;
// Vlastni cyklus vypisujici prvky matice
do {
// Vypis aktualniho prvku daneho
// souradnicemi (row,col)
printf ("%i ", *(m->matrix + row*m->cols + col));
// Podminka pro zmenu smeru pohybu
if (
(row+spiralPeriod[dir][0] >= (signed)m->rows-rowLimit)||
(row+spiralPeriod[dir][0] < rowLimit)||
(col+spiralPeriod[dir][1] >= (signed)m->cols-colLimit)||
(col+spiralPeriod[dir][1] < colLimit)
)
{
// Inkrementace citace - zmena smeru pohybu
i++;
// Promenna pro indexaci tabulky spiralPeriod
dir = i&3;
// Orezavani radku a sloupcu matice,
// ktere uz byly vypsany
rowLimit = (i+1) >> 2;
colLimit = i >> 2;
}
// Pricteni radkoveho a sloupcoveho prirustku
// (vypocet souradnic nasledujiciho prvku)
row += spiralPeriod[dir][0];
col += spiralPeriod[dir][1];
} while (!(
// Podminka kontroluje zdali je mozne
// v nasledujici iteraci vypsat prvek
// ktery jeste vypsan nebyl
(row >= (signed) m->rows-rowLimit)||
(row < rowLimit)||
(col >= (signed) m->cols-colLimit)||
(col < colLimit) ));
// Po vypsani vsech prvku odradkuje
printf ("\n");
return EXIT_SUCCESS;
}
// Funkce vypise posloupnost aritmetickych prumeru
// soustrednych kruznic prvku smerem od okraje matice
// viz. Dokumentace
int vypisTerc (const PTMatrix m)
{
// Tato tabulka charakterizuje periodickou zmenu smeru pohybu
// pri cteni prvku matice ve tvaru terce
// Levy sloupec udava radkovy prirustek
// Pravy sloupec udava sloupcovy prirustek
const int dartPeriod [4][2] =
{
{1,0},
{0,1},
{-1,0},
{0,-1}
};
// Kontrola vstupnich hodnot
if ( (0==m->rows) || (0==m->cols) )
return EXIT_FAILURE;
// Promenne cyklu
register long int row=0, col=0;
long int i=0, dir=0, limit=0;
double suma=0.0;
// Cyklus prochazi postupne vsechny prvky matice
do
{
// Pricteni aktualniho prvku a inkrementace pocitadla
// Tyto dve promenne vyuziju pozdeji k vypoctu prumeru
suma += *(m->matrix + row*m->cols + col);
i++;
// Podmika pro zmenu smeru pohybu
if (
(row+dartPeriod[dir][0] >= (signed)m->rows-limit)||
(row+dartPeriod[dir][0] < limit)||
(col+dartPeriod[dir][1] >= (signed)m->cols-limit)||
(col+dartPeriod[dir][1] < limit)
)
{
// Zmena smeru pohybu
dir ++;
if (dir>3)
{
// Ted odectu posledni prvek
// abych ho nepocital dvakrat
// (na zacatku a na konci cyklu)
suma -= *(m->matrix + row*m->cols + col);
i --;
// Vypise prumer nactenych prvku
printf ("%.5lf ", suma/i);
// Inicializace promennych pro vypocet
// prumeru dalsich prvku
suma = 0.0;
i = dir = 0;
// Orezani jiz nactenych radku a sloupcu
row = limit;
limit ++;
col = limit;
}
}
// Pricteni radkoveho a sloupcoveho prirustku
// (vypocet souradnic nasledujiciho prvku)
row += dartPeriod[dir][0];
col += dartPeriod[dir][1];
} while (!( // Podminka kontroluje zdali je mozne
// v nasledujici iteraci nacist prvek
// ktery jeste nacten nebyl
(row >= (signed)m->rows-limit)||
(row < limit)||
(col >= (signed)m->cols-limit)||
(col < limit)
));
// Pokud matice neni ctvercova, nebyl jeste vypsan prumer
// poslednich prvku - je na case to udelat..
if (i)
printf ("%.5lf",suma/i);
// Po vypsani vsech prvku odradkuje
printf ("\n");
return EXIT_SUCCESS;
}
// Funkce na zaklade prvniho prvku matice opakovane
// vola funkci wormTransOne, ktera provadi vlastni
// zizali transformaci matice - viz. Dokumentace
int wormTrans (const PTMatrix m)
{
// Vypocet poctu prvku a nacteni hodnoty, o kterou se bude posouvat
const long int pocetPrvku = (signed) (m->rows * m->cols);
register long int posunKolik = (long) *(m->matrix);
// Udava smer transformace, ktery je pozdeji predan
// funkci wormTransOne jako druhy parametr
const int WormForward = 0;
const int WormBack = 1;
// Kontrola, jestli matice neni prazdna
if (pocetPrvku==0)
return EXIT_FAILURE;
// Vypocet nejmensiho mozneho poctu transformaci
// aby byl vysledek ekvivalentni
posunKolik %= pocetPrvku;
// Zjisteni smeru posuvu, volani funkce s paramtrem udavajicim smer
if (posunKolik>0)
for (;posunKolik; posunKolik--)
wormTransOne (m,WormForward);
else
for (;posunKolik; posunKolik++)
wormTransOne (m,WormBack);
return EXIT_SUCCESS;
}
// Funcke posune prvky matice ve tvaru zizaly
// dopredu nebo dozadu; vzdy pouze o jeden prvek
void wormTransOne (const PTMatrix m, int reverse)
{
// Vypocet poctu prvku dane matice
const unsigned long pocetPrvku = m->rows * m->cols;
// Promenne cyklu
int tmp, *pNow;
unsigned char dir;
// Aktualni pozice pri pruchodu matici
struct prvekXY
{
int row;
int col;
} now;
// Tabulka charakterizuje zmenu smeru pohybu
// ve tvaru zizaly - viz. zadani ulohy
// (v teto podobe jde o pohyb dopredu)
struct prvekXY wormPeriod[]=
{
{0,1},
{1,0},
{0,-1},
{1,0}
};
// Zjisti jestli je pocet radku sudy nebo lichy
// podle toho lze stanovit jestli cerv skonci vlevo nebo vparvo
dir = m->rows&1;
// Nastaveni pozice na posledni prvek zizaly
now.row = m->rows-1;
now.col = (dir) ? m->cols-1 : 0;
// Nasledujici kod je rozlisen na dve casti
// pohyb ve smeru zizaly dopredu a dozadu
if (reverse)
{
// Nastavi smer pruchodu matici zespodu nahoru
wormPeriod[1].row = -1;
wormPeriod[3].row = -1;
// Inicializace promennych cyklu pro prevraceny smer
// do tmp se ulozi prvni prvek zizaly
tmp = *(m->matrix);
dir <<= 1;
}
else
{
// Inicializace promennych cyklu pro smer dopredu
// do tmp se ulozi posledni prvek zizaly
tmp = *(m->matrix + now.row*m->cols + now.col);
now.row = now.col = dir = 0;
}
// Cyklus provadejici transformaci
for (register unsigned int i=pocetPrvku; i; i--)
{
// Vymena aktualniho prvku s tmp
pNow = (m->matrix + now.row*m->cols + now.col);
*pNow ^= tmp;
tmp ^= *pNow;
*pNow ^= tmp;
// Podminka pro zmenu smeru pohybu
if (
(dir&1) ||
( (dir==0) && (now.col>=(signed)m->cols-1) ) ||
( (dir==2) && (now.col==0) )
)
dir ++;
// Smery jsou jen ctyri takze mi budou stacit
// posledni dva bity promenne dir
dir &=3;
// Pricteni smeroveho prirustku k souradnicim prvku
now.row += wormPeriod [dir].row;
now.col += wormPeriod [dir].col;
}
}