// Lösungen zu "Richard Kaiser: C++ mit dem Borland C++Builder 2007", ISBN: 978-3-540-69575-2
// Copyright Richard Kaiser, 2007. http://www.rkaiser.de. All rights reserved.
// Diese Quelltexte dürfen in eigenen Programmen verwendet werden, wenn sie einen Hinweis auf die Quelle (die ersten beiden Zeilen) enthalten. In keinem Fall wird eine Haftung für direkte, indirekte, zufällige oder Folgeschäden übernommen, die sich aus der Nutzung ergeben.
// It is permitted to use this source text in programs of your own, provided that it contains a reference to the source (the first two lines above). All liabilities for use of the code are disclaimed.

#include <vcl\vcl.h>
#pragma hdrstop
#include "FuncU.h"
#pragma resource "*.dfm"
TForm1 *Form1;


__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
#ifdef _DEBUG
bool test_3_4();      // Prototyp
void initTestUtils(); // Prototyp
void testUtilsSummary(TMemo* Memo, bool result); // Prototyp

initTestUtils();
testUtilsSummary(Memo1, test_3_4());
#endif

// Damit es keine Konversionsfehler gibt, wenn man vergisst, einen
// ganzzahligen Wert in das Eingabefenster einzugeben.
Edit1->Text="50";
Edit2->Text="1990";
Edit3->Text="2010";
}


// --------------- Aufgaben 3.4.1 --------------------------

// --------------- Aufgabe 1 -------------------------------

void __fastcall TForm1::Button_3_4_1_1Click(TObject *Sender)
{
int x, Punkte, i, j;
bool b;

// a)
if (x=17) Memo1->Lines->Add("Volltreffer");
// "x=17" ist eine Zuweisung und keine Prüfungen auf Gleichheit

// b)
if (i>=1 && i<=10) Edit1->Text = "Volltreffer";
// Syntaktisch und semantisch korrekt. Verknüpfte Bedingungen müssen nicht
// geklammert werden.

// c)
// if b && (i=j*x) Edit1->Text = "Volltreffer";
// Syntaxfehler: Die Bedingung in einer if-Anweisung muss in einer Klammer
// stehen. "=" ist ein Zuweisung und keine Prüfung auf Gleichheit.

// d)
if (Punkte >= 0) Edit1->Text="Extremes Pech";
else if (Punkte >= 20) Edit1->Text="Ziemliches Pech";
else if (Punkte >= 40) Edit1->Text="Ein wenig Glück gehabt";
// Syntaktisch korrekt, aber der zweite und dritte else-Zweig werden nie
// erreicht. Vermutlich „<" mit „>" verwechselt.
}


// --------------- Aufgabe 2 -------------------------------

void __fastcall TForm1::Button_3_4_1_2Click(TObject *Sender)
{
/*
Für diese Aufgabe werden meist zuerst die folgenden beiden Lösungen
vorgeschlagen:

  if (Bewegungsart=='+') Kontostand = Kontostand+Betrag;
  else Kontostand = Kontostand-Betrag;

oder

  if (Bewegungsart=='+') Kontostand = Kontostand+Betrag;
  if (Bewegungsart=='-') Kontostand = Kontostand-Betrag;

Beide Lösungen haben aber den Nachteil, dass eine unzulässige Bewegungsart
zu einer falschen Verbuchung des Betrages führt: Im ersten Fall kommt es
zu einer Abbuchung (so würde ich das machen, wenn die Bank mir gehören
würde), und im zweiten Fall fällt jede falsche Kontobewegung unter den
Tisch. Bei einer größeren Anzahl von Datensätzen bliebe ein solcher Fehler
vermutlich sogar unentdeckt.

Fügt man diesen Anweisungen eine weitere hinzu, um eine falsche
Bewegungsart abzufangen, muss man schon etwas nachdenken, um die richtige
Bedingung zu finden (hier werden relativ oft && und || verwechselt): Ist

  if ((Bewegungsart != '+') && (Bewegungsart != '-'))
   Fehlermeldung();

oder || anstelle von && richtig? Falls das Programm dann später auf noch
andere Bewegungsarten erweitert wird, erhält man zunächst eine falsche
Fehlermeldung, weil meist vergessen wurde, die Bedingung für die
Fehlermeldung zu ändern.

Das Problem bei Aufgaben wie dieser liegt darin, dass die Aufgabenstellung
zwar die Auswahl einer von zwei Bedingungen nahe legt. Tatsächlich ist aber
eine Bedingung mehr zu berücksichtigen, nämlich die Fehlerbehandlung.
Deshalb ist in diesem Beispiel eine von drei Anweisungen auszuwählen.

Die nächste Variante hat die oben genannten Nachteile nicht:

  if (Bewegungsart=='+') Kontostand = Kontostand + Betrag;
  else if (Bewegungsart=='-') Kontostand = Kontostand - Betrag;
  else Fehlermeldung();
*/
}


// --------------- Aufgabe 3 -------------------------------

int LA_Summe,LB_Summe,MA_Summe,MB_Summe,MC_Summe,Summe;

void LagMat(char Lagergruppe, char Materialgruppe)
{
if (Lagergruppe == 'A')
  {
    if (Materialgruppe == 'A')
      {
        LA_Summe = LA_Summe + Summe;
        MA_Summe = MA_Summe + Summe;
      }
    else if (Materialgruppe == 'B')
      {
        LA_Summe = LA_Summe + Summe;
        MB_Summe = MB_Summe + Summe;
      }
    else if (Materialgruppe == 'C')
      {
        LA_Summe = LA_Summe + Summe;
        MC_Summe = MC_Summe + Summe;
      }
    else ShowMessage("Unzulässige Materialgruppe in Lager A");
  }
else if (Lagergruppe == 'B')
  {
    if (Materialgruppe == 'A')
      {
        LB_Summe = LB_Summe + Summe;
        MA_Summe = MA_Summe + Summe;
      }
    else if (Materialgruppe == 'B')
      {
        LB_Summe = LB_Summe + Summe;
        MB_Summe = MB_Summe + Summe;
      }
    else ShowMessage("Unzulässige Materialgruppe in Lager B");
  }
else ShowMessage("Unzulässige Lagergruppe");
}

void __fastcall TForm1::Button_3_4_1_3Click(TObject *Sender)
{
char Lagergruppe, Materialgruppe;
LagMat(Lagergruppe, Materialgruppe);
}

// --------------- Aufgabe 4 -------------------------------

bool vorher(int t1, int m1, int j1, int t2, int m2, int j2)
{
if (j1!=j2) return j1<j2;
else // j1==j2
     if (m1!=m2) return m1<m2;
else // (j1==j2) and (m1==m2)
     return t1<t2;

// Falls die boolesche Variable vorher_oder_gleich genau dann den
// Wert true erhalten soll, wenn das erste Datum zeitlich vor dem
// zweiten liegt oder gleich dem zweiten ist, muss lediglich die
// letzte Zeile auf <= geändert werden zu:
//
//   return t1<=t2;
}

void __fastcall TForm1::Button_3_4_1_4Click(TObject *Sender)
{
int t1=1,m1=2,j1=3; // Datum 1
int t2=1,m2=2,j2=3; // Datum 2
vorher(t1,m1,j1, t2,m2,j2);
}


// --------------- Aufgaben 3.4.6 --------------------------


// --------------- Aufgabe 1 -------------------------------

int Quersumme(int n)
{
if (n<0) n=-n; // berücksichtige negative n
// % ist für negative Operanden compilerabhängig
int s=0;
while (n>0)
  {
    s = s+n%10;
    n = n/10;
  }
return s;
}

void zeigeQuerSummen(int a, int b)
{
Form1->Memo1->Lines->Add("Quersummen");
for (int i=a; i<=b; i++)
  Form1->Memo1->Lines->Add(IntToStr(i)+": "+
                           IntToStr(Quersumme(i)));
}

void __fastcall TForm1::Button_3_4_6_1Click(TObject *Sender)
{
zeigeQuerSummen(1,StrToInt(Edit1->Text));
}


// --------------- Aufgabe 2 -------------------------------

int Fibonacci(int n)
{
int f=0,x=0,y=1;
for (int i=0; i<n; i++)
  {
    x=y;
    y=f;
    f=x+y;
  }
return f;
}

void zeigeFibonacci(int n)
{
Form1->Memo1->Lines->Add("Fibonacci-Zahlen:");
for (int i=0; i<n; i++)
  Form1->Memo1->Lines->Add(IntToStr(i)+":  "+IntToStr(Fibonacci(i)) );
Form1->Memo1->Lines->Add("Falsche Ergebnisse wegen Überlauf für n>=47");
}

void __fastcall TForm1::Button_3_4_6_2Click(TObject *Sender)
{
zeigeFibonacci(StrToInt(Edit1->Text));
}


// --------------- Aufgabe 3 -------------------------------

void __fastcall TForm1::Button_3_4_6_3Click(TObject *Sender)
{
int n=StrToInt(Edit1->Text);
for (unsigned int u=0; u<=n-1; u++) // Warnung: Vergleich von signed- und
  Memo1->Lines->Add(IntToStr(u));   //          unsigned-Werten
// a), b)
// Da u den Datentyp unsigned int hat, wird der Datentyp von n-1 im
// Ausdruck u<=n-1 ebenfalls in unsigned int konvertiert. Für n=0 führt
// das zu einer Endlosschleife. Für Werte n>=1 erhält man das erwartete
// Ergebnis.

// c) Mit der Bedingung u<n gibt es auch mit n=0 keine Endlosschleife.
for (unsigned int u=0; u<n; u++) // Warnung: Vergleich von signed- und
  Memo1->Lines->Add(IntToStr(u));//          unsigned-Werten
}


// --------------- Aufgabe 4 -------------------------------

bool istPrim(int n)
{
if (n<2) return false;
else if (n==2) return true; // dieser Zweig ist überflüssig
else
  {
  for (int i=2; i*i<=n; i++)
    if (n%i==0) return false;
  return true;
  }
}

bool istPrim_(int n) // gleichwertige Alternative
{
if (n<2) return false;
else if (n==2) return true;
else
  {
    int i=2;
    while (i*i<=n)
      {
        if (n%i==0) return false;
        i++;
      }
    return true;
  }
}

void zeigePrimzahlen(int n)
{
Form1->Memo1->Lines->Add("Primzahlen: ");
for (int i=1;i<n; i++)
  if (istPrim(i))
    Form1->Memo1->Lines->Add(IntToStr(i));
}


void __fastcall TForm1::Button_3_4_6_4Click(TObject *Sender)
{
zeigePrimzahlen(StrToInt(Edit1->Text));
}

int Goldbach(int n)
{
if ((n<4) || (n%2==1)) return -1;
else
  {
    int k=0;
    for (int i=2; i<=n/2; i++)
      if (istPrim(i) && istPrim(n-i)) k++;
    return k;
  }
}

void zeigeGoldbachPaare(int n)
{
Form1->Memo1->Lines->Add("Goldbach'sche Vermutung:");
for (int i=6; i<100; i=i+2)
  Form1->Memo1->Lines->Add(IntToStr(i)+": g="+IntToStr(Goldbach(i)));
}


void __fastcall TForm1::Button_3_4_6_5Click(TObject *Sender)
{
zeigeGoldbachPaare(StrToInt(Edit1->Text));
}


// --------------- Aufgabe 5 -------------------------------

int zeigePythagTripel(int n)
{
int nt=0; // Anzahl der gefundenen Tripel
for (int a=1; a<n; a++)
  for (int b=a; b<n; b++)
     for (int c=b; c*c<=a*a+b*b; c++) // "for (int c=1; ..." geht auch
       if (a*a+b*b==c*c)
         {
         Form1->Memo1->Lines->Add("a="+IntToStr(a)+" b="+
                                   IntToStr(b)+" c= "+ IntToStr(c));
         nt++;
         }
return nt;
}

void __fastcall TForm1::Button_3_4_6_6Click(TObject *Sender)
{
int n=zeigePythagTripel(StrToInt(Edit1->Text));
Form1->Memo1->Lines->Add("n="+IntToStr(n));
}


// --------------- Aufgabe 6 -------------------------------

int f3nplus1(int n, bool show)
{
if (n<=0) return -1;
int m=0;
while (n!=1)
  {
    m++;
    if (n%2==1) n=3*n+1;
    else n=n/2;
    if (show) Form1->Memo1->Lines->Add(" n="+IntToStr(n));
  }
return m;
}

void zeige3nplus1(int n)
{
for (int i=0; i<n; i++)
  {
    int k=f3nplus1(i,false);
    Form1->Memo1->Lines->Add("i="+IntToStr(i)+" k="+IntToStr(k));
  }
}

void __fastcall TForm1::Button_3_4_6_7Click(TObject *Sender)
{
zeige3nplus1(StrToInt(Edit1->Text));
}


// --------------- Aufgabe 7 -------------------------------

void zeigeZufallszahlen(int n)
{
Form1->Memo1->Lines->Add("rand Zufallszahlen: ");
for (int i=0; i<n; i++)
    Form1->Memo1->Lines->Add(IntToStr(1+rand()%49));
Form1->Memo1->Lines->Add("random Zufallszahlen: ");
for (int i=0; i<n; i++)
    Form1->Memo1->Lines->Add(IntToStr(1+random(48)));
}

void __fastcall TForm1::Button_3_4_6_8Click(TObject *Sender)
{
zeigeZufallszahlen(10);
}

void __fastcall TForm1::RandomizeClick(TObject *Sender)
{
randomize();
}


// --------------- Aufgabe 8 -------------------------------
AnsiString Osterdatum(int J)
{
int A,B,C,D,E,M,N,OTag,PTag;
AnsiString s;
if (J < 1583) s = " Formel gilt nicht ";

else if (J < 1700) { M = 22; N = 2; }
else if (J < 1800) { M = 23; N = 3; }
else if (J < 1900) { M = 23; N = 4; }
else if (J < 2100) { M = 24; N = 5; }
else if (J < 2200) { M = 24; N = 6; }
else if (J < 2300) { M = 25; N = 0; }
else s = " Formel gilt nicht ";

A = J % 19;
B = J % 4;
C = J % 7;
D = (19*A + M) % 30;
E = (2*B + 4*C + 6*D + N) % 7;
if ((22 + D + E >= 1) && (22 + D + E <= 31)) // in [1..31])
  {
    OTag =22 + D + E;
    PTag = OTag+49;
    s = s+IntToStr(J)+" Ostern: "+IntToStr(OTag)+ ". März Pfingsten: ";
    if (PTag>31+30+31) //  Tage im März + April + Mai
      s = s+IntToStr(PTag-92) + ". Juni";
    else if (PTag > 30+31) // Tage im April + Mai
      s = s+IntToStr(PTag-61) + ". Mai";
    else s = s + " unmöglich"; // Wegen OTag >= 22 ist PTag >= 71
  }
else
  {
    OTag = D + E - 9;
    PTag = OTag+49;
    if (OTag == 26) OTag = 19;
    else if ((OTag==25) && (D==28) && (E==6) && (A>10)) OTag = 18;
    s= s+IntToStr(J)+" Ostern: "+IntToStr(OTag)+ ". April Pfingsten: ";
    if (PTag > 30+31) // Tage im April + Mai
      s = s+IntToStr(PTag-61) + ". Juni";
    else if (PTag > 30) // Tage im April
      s = s+IntToStr(PTag-30) + ". Mai";
    else s = s + " unmöglich"; // wegen OTag >= -9 ist PTag >= 40
  }
return s;
}

void __fastcall TForm1::Button_3_4_6_9Click(TObject *Sender)
{
int von=StrToInt(Edit2->Text);
int bis=StrToInt(Edit3->Text);
for (int J=von; J<=bis; J++)
  Memo1->Lines->Add(Osterdatum(J));
}

// --------------- Aufgabe 9 -------------------------------

/*
1. Quersumme:

   Es wird immer (unabhängig vom Argument n) der Wert von s
   zurückgegeben. s wird für jeden Wert von n mit 0 initialisiert und
   eventuell in der Schleife verändert.

   Wenn man die Funktion dagegen so formuliert hätte

      int Quersumme(int n)
      {
      if (n>=0)
        {
          int s=0;
          while (n>0)
            {
              s = s+n%10;
              n = n/10;
            }
          return s;
        }
      }

   wäre der Funktionswert für Argumente <0 nicht definiert.

2. Fibonacci

   Es wird immer (unabhängig vom Argument n) der Wert von f
   zurückgegeben. f wird für jeden Wert von n mit 0 initialisiert und
   eventuell in der Schleife verändert.

4. bool prim(int n)
   int Goldbach(int n)

   Da in jedem Zweig der if-Anweisung ein Wert zurückgegeben wird,
   ist der Funktionswert immer definiert.

5. int zeige_PythagZahlentripel(int n)

   wie 1.

6. int f3nplus1(int n, bool show)

   Die Funktion gibt entweder -1 oder m zurück.

7. void Lottozahlen()

   Diese Funktion gibt keinen Wert zurück.

8. AnsiString Osterdatum(int J)

   wie 1.
*/


// --------------- Aufgaben 3.5.2 --------------------------

#include "..\3.5\Tests.cpp"

void __fastcall TForm1::Test_1Click(TObject *Sender)
{
initTestUtils();
testUtilsSummary(Memo1, test_3_4());
}

