#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>

#define MAX_IL_CYF 1000

typedef struct {
  int ilcyf;
  int cyf[MAX_IL_CYF];
}  dluga;

dluga  zero, jeden;

//===============================================
// Funkcje pomocnicze:

dluga  blad(char komunikat[]) {
  // sygnalizacja bledu:
  printf("\n!!! BLAD: %s\n\n", komunikat);
  exit(1);
  return zero;
}

dluga  konw_int_dluga(int n) {
  // konwersja krotkiej liczby  n  na dluga:
  dluga wyn;
  wyn.ilcyf = 0;
  while (n != 0) {
    wyn.cyf[wyn.ilcyf] = n%1000;
    n /= 1000;
    wyn.ilcyf++;
  }
  return wyn;
}

void  init(void) {
  // inicjalizacja ,,dlugich stalych'':
  zero = konw_int_dluga(0);
  jeden = konw_int_dluga(1);
}

//===============================================
// Dzialania:

dluga  dodaj (dluga skl1, dluga skl2) {
  // dodawanie dlugich liczb:
  dluga wyn;  int przen=0;
  wyn.ilcyf = 0;
  while (wyn.ilcyf<skl1.ilcyf && wyn.ilcyf<skl2.ilcyf) {
    przen += skl1.cyf[wyn.ilcyf] + skl2.cyf[wyn.ilcyf];
    wyn.cyf[wyn.ilcyf] = przen % 1000;  przen /= 1000;
    wyn.ilcyf++;
  }
  while (wyn.ilcyf<skl1.ilcyf) {
    przen += skl1.cyf[wyn.ilcyf];
    wyn.cyf[wyn.ilcyf] = przen % 1000;  przen /= 1000;
    wyn.ilcyf++;
  }
  while (wyn.ilcyf<skl2.ilcyf) {
    przen += skl2.cyf[wyn.ilcyf];
    wyn.cyf[wyn.ilcyf] = przen % 1000;  przen /= 1000;
    wyn.ilcyf++;
  }
  while (przen>0) {
    wyn.cyf[wyn.ilcyf] = przen;  przen /= 1000;
    wyn.ilcyf++;
  }
  if (wyn.ilcyf > MAX_IL_CYF)  blad("za duzy wynik dodawania");
  return wyn;
}

dluga  pomnoz_cyf(dluga arg, int n) {
  // mnozenie dlugiej liczby przez krotka:
  dluga wyn;  int przen=0;
  wyn.ilcyf = 0;
  while (wyn.ilcyf<arg.ilcyf) {
    przen += arg.cyf[wyn.ilcyf]*n;
    wyn.cyf[wyn.ilcyf] = przen % 1000;  przen /= 1000;
    wyn.ilcyf++;
  }
  while (przen>0) {
    wyn.cyf[wyn.ilcyf] = przen % 1000;  przen /= 1000;
    wyn.ilcyf++;
  }
  if (wyn.ilcyf > MAX_IL_CYF)  blad("za duzy wynik mnozenia przez cyfre");
  return wyn;
}

dluga  pomnoz(dluga cz1, dluga cz2) {
  // mnozenie dlugich liczb:
  dluga  wyn, ilcz;  int i;
  wyn = zero;
  for (i=0; i<cz2.ilcyf; i++) {
    ilcz = pomnoz_cyf(cz1,cz2.cyf[i]);
    if (i+1 < cz2.ilcyf)  cz1 = pomnoz_cyf(cz1,1000);
    wyn = dodaj(wyn,ilcz);
  }
  if (wyn.ilcyf > MAX_IL_CYF)  blad("za duzy wynik mnozenia");
  return wyn;
}

dluga  kwadrat(dluga arg) {
  dluga wyn;
  wyn = pomnoz(arg,arg);
  if (wyn.ilcyf > MAX_IL_CYF)  blad("za duzy kwadrat");
  return wyn;
}

dluga  potega(dluga podst, int wykl) {
  dluga wyn;
  if (wykl < 0)  blad("ujemny wykladnik potegi");
  if (wykl > 2000000000)  blad("za duzy wykladnik potegi");
  wyn = jeden;
  while (wykl > 0) {
    if (wykl%2 == 1)  wyn = pomnoz(wyn,podst);
    wykl /= 2;
    if (wykl > 0)  podst = kwadrat(podst);
  }
  if (wyn.ilcyf > MAX_IL_CYF)  blad("za duza potega");
  return wyn;
}

int  porownaj(dluga arg1, dluga arg2) {
  // porownanie dlugich liczb --
  // jesli  arg1<arg2  to wynik ujemny,
  // jesli  arg1>arg2  to wynik dodatni,
  // jesli  arg1=arg2  to wynik zerowy.
  if (arg1.ilcyf < arg2.ilcyf)  return -1;
  else  if (arg1.ilcyf > arg2.ilcyf)  return 1;
  else { // arg1.ilcyf == arg2.ilcyf
    int i=arg1.ilcyf-1;
    while (i>=0 && arg1.cyf[i] == arg2.cyf[i])  i--;
    if (i<0)  return 0;
    else  return arg1.cyf[i] - arg2.cyf[i];
  }
}

dluga  pol(dluga arg) {
  // (mniejsza) polowa dlugiej liczby:
  dluga wyn;  int i;
  arg = pomnoz_cyf(arg,500);
  for (i=1; i<arg.ilcyf; i++)
    wyn.cyf[i-1] = arg.cyf[i];
  wyn.ilcyf = arg.ilcyf-1;
  return wyn;
}

dluga  pierw_kwad(dluga podst) {
  dluga dol=zero, gora, srodek;
  gora = dodaj(podst, jeden);
  while (porownaj(dodaj(dol,jeden), gora) < 0) {
    srodek = pol(dodaj(dol,gora));
    if (porownaj(podst,kwadrat(srodek)) < 0)  gora = srodek;
    else  dol = srodek;
  }
  return dol;
}

dluga  silnia(int n) {
  dluga wyn = jeden;  int i;
  for (i=1; i<=n; i++)
    wyn = pomnoz(wyn, konw_int_dluga(i));
  if (wyn.ilcyf > MAX_IL_CYF)  blad("za duza silnia");
  return wyn;
}

//===============================================
// Wejscie/wyjscie:

char  znak_wid(void) {
  // wczytanie jednego znaku widocznego lub nowej linii:
  char ch;
  do { ch = getchar(); }
  while (isspace(ch) && ch!='\n');
  return ch;
}

void  czytaj(dluga* arg) {
  // wczytywanie dlugiej liczby:
  char ch;
  *arg = zero;
  do { ch = znak_wid(); }  while (ch == '\n');
  while (ch != '\n') {
    if ('0' <= ch && ch <= '9')
      *arg = dodaj(
	pomnoz_cyf(*arg, 10),
        konw_int_dluga((int)ch-(int)'0')
      );
    else  blad("niecyfra we wczytywanej liczbie");
    ch = znak_wid();
  }
}

void  drukuj(dluga arg) {
  // drukowanie dlugiej liczby:
  int i;
  if (arg.ilcyf == 0)  printf("0");
  else {
    for (i=arg.ilcyf-1; i>=0; i--)
      if (i == arg.ilcyf-1)  printf("%i", arg.cyf[i]);
      else {
	if (arg.cyf[i] == 0)  printf(" 000");
	else  if (arg.cyf[i] < 10)  printf(" 00%i", arg.cyf[i]);
	else  if (arg.cyf[i] < 100)  printf(" 0%i", arg.cyf[i]);
	else  printf(" %i", arg.cyf[i]);
      }
  }
}

void test(char a[], dluga dl) {
  printf("%s", a); drukuj(dl); printf("\n");
}

//===============================================

int  main () {
  dluga  arg1, arg2;  char dzial;  int koniec=0, n;
  init();
  printf("\nKALKULATOR DLUGICH LICZB\n");

  while (!koniec) {
    printf("WYBIERZ:\n");
    printf("  + (dodaj)  * (pomnoz)  ^ (potega)  ! (silnia)\n");
    printf("  p (polowa)  s (pierwiastek kwadratowy)  q (koniec)\n");
    do { dzial = znak_wid(); }  while(dzial == '\n');
  
    switch (dzial) {
      case '+':
        printf("\n  skl1 = ");  czytaj(&arg1);
        printf("  skl2 = ");  czytaj(&arg2);
        printf("  ");  drukuj(arg1);
        printf(" + ");  drukuj(arg2);
        printf(" == ");  drukuj(dodaj(arg1, arg2));
        printf("\n\n");  
        break;
      case '*':
        printf("\n  cz1 = ");  czytaj(&arg1);
        printf("  cz2 = ");  czytaj(&arg2);
        printf("  ");  drukuj(arg1);
        printf(" * ");  drukuj(arg2);
        printf(" == ");  drukuj(pomnoz(arg1, arg2));
        printf("\n\n");  
        break;
      case '^':
        printf("\n  podst = ");  czytaj(&arg1);
        printf("  wykl (nie dlugi) = ");  scanf("%i", &n);
        printf("  ");  drukuj(arg1);
        printf(" ^ ");  printf("%i", n);
        printf(" == ");  drukuj(potega(arg1, n));
        printf("\n\n");  
        break;
      case '!':
        printf("\n  podst (nie dluga) = ");  scanf("%i", &n);
        printf("  ");  printf("%i", n);
        printf(" !  == ");  drukuj(silnia(n));
        printf("\n\n");  
        break;
      case 'p': case 'P':
        printf("\n  arg = ");  czytaj(&arg1);
        printf("  ");  drukuj(arg1);
        printf(" /2  == ");  drukuj(pol(arg1));
        printf("\n\n");  
        break;
      case 'k': case 'K':
        printf("\n  arg = ");  czytaj(&arg1);
        printf("  ");  drukuj(arg1);
        printf(" ^2  == ");  drukuj(kwadrat(arg1));
        printf("\n\n");
        break;
      case 's': case 'S':
        printf("\n  arg = ");  czytaj(&arg1);
        printf("   _____\n  V ");  drukuj(arg1);
        printf("   == ");  drukuj(pierw_kwad(arg1));
        printf("\n\n");
        break;
      case 'q': case 'Q':
        printf("\nDZIEKUJE!\n\n");  koniec = 1;
        break;
      default:
        printf("\nNIEZNANE DZIALANIE\n\n");
    }
  }
  
  return 0;
}
