Math 102

Programming Project #2

A Visual C++ Calculator for Galois Fields

   You will create a Visual C++ program that lets you add, subtract. multiply. or divide two elements of a Galois field with fewer than 50 elements. Again, for the user-interface parts of the program, you need only follow instructions, but you will have to implement the "real mathematics" of arithmetic in finite fields yourself.

The program used in class that does this, "GField.exe", can be downloaded from the course Web site.  You can also download a copy of this document, from which you can paste code into your project as you are creating it. 

1. Click the "Microsoft Visual C++ 6.0" icon on the desktop, or go to Start...Programs...Microsoft Visual C++ 6.0...Microsoft Visual C++ 6.0 if there is no icon on the desktop.  From the menu select File...New.  Click on the Projects tab, then click on MFC AppWizard(exe).  In the location box, choose the folder in which you keep your Math S15 projects. As the project name, type in GField..  Now click OK to start   the AppWizard.

2. In Step 1, select Dialog Based, then click Next.
       In Step 2, uncheck About Box and ActiveX Controls. Change  the title to "Galois Field Calculator."  Click Finish.

3. Go to "Resource View" and  click on the + next to Dialog, then double-click on IDD_GFIELD_DIALOG.   Click on the text that starts "TODO:...", then press the Delete key to get  rid of it.  Do the same for the Cancel button.  With the OK button  highlighted, press the Enter key.  Under the General tab, edit the caption  to change it to "Done."  Click the little "x" to finish, then drag the  button down to the lower right-hand corner of the dialog.

4. Now we will change some properties of the entire dialog.  Click anywhere in empty space on the dialog, then press Enter.
    Under the General tab, click Font...and select Courier New, Size 10.
    Under the Styles tab, check Minimize box.
    Click the little x to close Dialog Properties.

5. In order to be able to switch among different Galois fields, we are going to embed a property sheet with two tabbed property pages in our dialog.  Later we can add more pages to support additional Galois fields.  This is not supported by the graphical dialog editor, but it is not hard to do.  We begin by making the two pages.  While still in Resource View, right-click on Dialog and select Insert... from the menu that pops up.  Under Insert Resource, click the + next to Dialog, then select IDD_OLE_PROPPAGE_MEDIUM and click New.  Delete the TODO... item, then press Enter to get to the Dialog Properties.  Under the General tab, change the ID to IDD_ORDER3 and the caption to Order 3.  Since there will be only a few buttons, click Font... and change the Font to Courier New, size 10. (Note: if you decide later to do the 49-element field, leave the font size as 8 on that property page).
On the dialog place three buttons:
ID IDC_3_0    Caption [0]
ID IDC_3_1    Caption [1]
ID IDC_3_2    Caption [2]
Now press Ctrl-W to bring up ClassWizard.  You will be invited to "Create a new class." Press OK to do so.  For the name choose COrder3, and for the Base Class change CDialog to CPropertyPage.  With the Class Name now showing COrder3, add three member variables
For IDC_3_0,    CButton m_3_0
For IDC_3_1,    CButton m_3_1
For IDC_3_2,    CButton m_3_2
Under the Message Maps tab, add a BN_CLICKED handler for each of the three buttons.
Finally, with the class name COrder3 highlighted under Object IDs, scroll down under Messages to OnSetActive and click Add Function to override the virtual function OnSetActive(), which gets called whenever this page is selected.  Click OK to exit from Class View.
Right-click on the class COrder3 and add two member variables:
   CGFieldDlg* m_pDlg;
   CFiniteField* m_pField;
Just above the comment
//  COrder 3 dialog, add the declarations
class CGFieldDlg;
class CFiniteField;
These will keep the compiler happy when it sees the pointer-type variables that you just declared.  Use Ctrl-X to delete the line
#include "GFieldDlg.h"    //Added by Class View
and then paste this line at the top of Order3.cpp

6. Repeat the operations of step 5 to add a property page IDD_ORDER5 with an associated class COrder5 derived from CPropertyPage. This should have with 5 buttons, each with its own member variable, starting with
IDC_5_0     Caption [0]      CButton m_5_0
and ending with
IDC_5_4     Caption [4]      CButton m_5_4
(In general, there is no required order for the buttons, but it is convenient to have their IDs end in 0, 1, 2, ...)
Again add BN_CLICKED handlers for the buttons and override OnSetActive.
Right-click on the class COrder5 and add two member variables:
   CGFieldDlg* m_pDlg;
   CFiniteField* m_pField;
Just above the comment
  //  COrder 5 dialog,
add the declarations
class CGFieldDlg;
class CFiniteField;
Near the top of Order5.cpp, insert the line
#include "GFieldDlg.h"

7. Go into ClassView. Right-click on CGFieldDlg and add a member variable
CPropertySheet   m_propSheet
Also add a member variable
COrder3 m_page3
and a member variable
COrder5 m_page5
At the top of  the file GFieldDlg.h will be the line
#include "Order3.h"  // Added by ClassView
Under it, add the line
#include "Order5.h"

Also add a member function
void InitPropertySheet
Type in (or paste in from this document) this code:
   m_page3.m_pDlg = this;
   m_page3.m_pField = &m_field;
   m_page5.m_pDlg = this;
   m_page5.m_pField = &m_field;
   m_propSheet.AddPage(&m_page3);
   m_propSheet.AddPage(&m_page5);
   m_propSheet.Create(this, WS_CHILD | WS_VISIBLE, 0);
   m_propSheet.SetWindowPos(NULL,0,0,0,0,SWP_NOSIZE | SWP_NOZORDER);

In the function CGFieldDlg::OnInitDialog add, just before the last line,
InitPropertySheet();

8. Finally, right-click on GFieldClasses in ClassView and select New Class... from the menu.
Choose Generic Class as the Class type and CFiniteField as the name.
This class will contain nothing but mathematics, and once it is finished, you will be able to use it in other projects.
In CGFieldDlg add a member variable
CFiniteField m_field;
Now press Ctrl-F5 to build and run the project.  If you have followed instructions very carefully, you will see two tabs with captions Order 3 and Order 5 at the upper left of the dialog, and clicking these tabs will let you switch between the two property pages.  Later, when you want to add and display property pages for other Galois fields, refer to steps 5 through 7 to remind yourself about how to add them.  The tabs always appear, from left to right, in the order specified by the calls to AddPage() in InitPropertySheet().

At this point you have completed the hard work of defining all the C++ classes in the project and making it possible for them to communicate with one another.  It is prudent now to close the GField workspace and reopen it in order to make sure that the changes you have made to the project workspace are safely stored in files.

9.  Now go back to ResourceView and open up IDD_GFIELD_DIALOG.  To the right of where the property pages will display, add a column of five buttons as follows:

IDC_PLUS    Caption +
IDC_MINUS  Caption -
IDC_TIMES   Caption *
IDC_SLASH   Caption /
IDC_EQUALS  Caption =

Across the bottom of the dialog , add a row of read-only edit boxes, as follows:
IDC_LEFT
IDC_OP
IDC_RIGHT
IDC_RESULT
For each of these, go to the Styles tab to check Read-only, and also change Align text to Centered.
Now bring up Class Wizard (with CGFieldDlg as the Class name).  Add BN_CLICKED handlers for the five buttons.  For the four edit boxes, add CString member variables m_left, m_right, m_op, and m_result.  Build and run the project to make sure that the property pages do not overlap with any of the controls that you just added.

10. Now the dialog class must be made aware of the buttons on the active property page.  At the top of the file CGFieldDlg.h, above the class declaration, add three lines

#define MAX_ORDER 50
enum stateEnum  {STATE_LEFT, STATE_OP, STATE_RIGHT};
enum opEnum  {OP_ADD, OP_SUBTRACT, OP_MULTIPLY, OP_DIVIDE};

Then add the member variables
    opEnum m_enumOp;
   CButton* m_pButtons[MAX_ORDER];
   stateEnum m_state;

and the member functions

   void ExecuteButton(int n);
   void Reinitialize();

11. In the class CFiniteField add member variables that specify the elements of the finite field:

   int m_prime;  //the size of the field from which the coefficient are taken
   int m_power;  //the maximum number of terms in a polynomial
   int m_nElements;  //the number of  elements ("prime" raised to "power")
   CStringArray m_strElements;  //the array of polynomials that specify the elements

and the member function
   void Initialize(int prime, int power);
 

For COrder3, m_prime is 3 and m_power is 1.
The polynomials have just 1 term; they are [0] through [2]
COrder5 is similar.
But when you do COrder4, m_prime is 2 and m_power is 2
The polynomials have 2 terms: they are [0], [1], [x], and [x+1]
If you do COrder27 (ambitious), m_prime = 3 and m_power = 3.
You will then have polynomials like [2x2 + x + 2]

12. Whenever a property page becomes active, CGFieldDlg must get pointers to all the buttons on the page, while CFiniteField must be informed about the captions on the buttons, which specify the elements of the field.  This is done in OnSetActive.  For COrder3 you need

BOOL COrder3::OnSetActive()
{
   m_pField->Initialize(3,1);
   m_pDlg->m_pButtons[0] = &m_3_0;
   m_pDlg->m_pButtons[1] = &m_3_1;
   m_pDlg->m_pButtons[2] = &m_3_2;
   m_pDlg->Reinitialize();
   return CPropertyPage::OnSetActive();
}

while for COrder5 you will want
BOOL COrder5::OnSetActive()
{
   m_pField->Initialize(5,1);
   m_pDlg->m_pButtons[0] = &m_5_0;
   m_pDlg->m_pButtons[1] = &m_5_1;
   m_pDlg->m_pButtons[2] = &m_5_2;
   m_pDlg->m_pButtons[3] = &m_5_3;
   m_pDlg->m_pButtons[3] = &m_5_4;
   m_pDlg->Reinitialize();
   return CPropertyPage::OnSetActive();
}

  As you add other Galois fields, it is easy to modify a copy of one of these functions to do the right thing in the class for the new field.

13. Now add the code for Initialize, which calculates the number of elements as follows:
void CFiniteField::Initialize(int prime, int power)
{
   m_prime = prime;
   m_power = power;
   m_nElements = prime;
   while (--power) {
       m_nElements *= prime;
   }
}

In class CGFieldDlg, add the code for Reinitialize, which sets up the array of polynomial strings for the field elements by reading the captions of the buttons on the property page.

void CGFieldDlg::Reinitialize()
{
   CString text;
   m_field.m_strElements.RemoveAll();
   for (int i = 0; i < m_field.m_nElements; i++) {
       m_pButtons[i]->GetWindowText(text);
       m_field.m_strElements.Add(text);
   }
   m_left.Empty();
   m_right.Empty();
   m_op.Empty();
   m_result.Empty();
   m_state = STATE_LEFT;
}

14. The function ExecuteButton does different things depending on the state of the calculation: sometime the element is the left operand and sometime it is the right operand.

void CGFieldDlg::ExecuteButton(int n)
{
   switch (m_state) {
   case STATE_LEFT:
       m_left = m_field.m_strElements[n];
       m_right = "";
       m_result = "";
       m_op = "";
       m_state = STATE_OP;
       break;
   case STATE_OP:
       m_left = m_field.m_strElements[n];
       m_result = m_field.m_strElements[n];
       break;
   case STATE_RIGHT:
       m_right = m_field.m_strElements[n];
       break;
   default:
       break;
   }
   UpdateData(FALSE);
}

15. Each button on each property page simply calls ExecuteButton with the correct argument:
void COrder3::On30()
{
   m_pDlg->ExecuteButton(0);
}

void COrder3::On31()
{
   m_pDlg->ExecuteButton(1);
}

void COrder3::On32()
{
   m_pDlg->ExecuteButton(2);
}

You must also add the similar functions for COrder5

16. The buttons for the arithmetic operations just record what operation is desired and change the state:

void CGFieldDlg::OnMinus()
{
   if (m_state == STATE_OP) {
       m_op = "-";
       m_enumOp = OP_SUBTRACT;
       m_state = STATE_RIGHT;
       UpdateData(FALSE);
   }
}

void CGFieldDlg::OnPlus()
{
   if (m_state == STATE_OP) {
       m_op = "+";
       m_enumOp = OP_ADD;
       m_state = STATE_RIGHT;
       UpdateData(FALSE);
   }
}

void CGFieldDlg::OnSlash()
{
   if (m_state == STATE_OP) {
       m_op = "/";
       m_enumOp = OP_DIVIDE;
       m_state = STATE_RIGHT;
       UpdateData(FALSE);
   }
}

void CGFieldDlg::OnTimes()
{
   if (m_state == STATE_OP) {
       m_op = "*";
       m_enumOp = OP_MULTIPLY;
       m_state = STATE_RIGHT;
       UpdateData(FALSE);
   }
}
17. At this point everything should be working except the equals button.  When you run the program, you can push a field element button, then an operation, then another field element button, and the "arithmetic problem" should appear at the bottom of the dialog.
 

18. When the equals button is pressed the CFiniteField class should take the strings for the two operands, perform the correct arithmetic, and display the result, as follows:

void CGFieldDlg::OnEquals()
{
   switch(m_enumOp) {
   case OP_ADD:
       m_result = m_field.Add(m_left, m_right);
       break;
   case OP_SUBTRACT:
       m_result = m_field.Subtract(m_left, m_right);
       break;
   case OP_MULTIPLY:
       m_result = m_field.Multiply(m_left, m_right);
       break;
   case OP_DIVIDE:
       m_result = m_field.Divide(m_left, m_right);
       break;
   default:
       break;
   }
   m_state = STATE_LEFT;
   UpdateData(FALSE);
}

To finish the job, you need to add four functions to CFiniteField, and in fact it is useful to define six functions. Use AddMemberFunction to add these:
   CString Invert(CString op);
  CString Negate(CString op);
   CString Add(CString op1, CString op2);
   CString Subtract(CString op1, CString op2);
   CString Multiply(CString op1, CString op2);
   CString Divide(CString op1, CString op2);
 

As long as m_power is 1, the arithmetic is easy.  Here are some functions that do the job.

CString CFiniteField::Add(CString op1, CString op2)
{
   int n1 = op1[1] - '0';
   int n2 = op2[1] - '0';
   int n3 = (n1 + n2) % m_prime;
   return CString("[") + char(n3+'0') + "]";
}

CString CFiniteField::Subtract(CString op1, CString op2)
{
   return Add(op1, Negate(op2));
}

CString CFiniteField::Multiply(CString op1, CString op2)
{
   int n1 = op1[1] - '0';
   int n2 = op2[1] - '0';
   int n3 = (n1 * n2) % m_prime;
   return CString("[") + char(n3+'0') + "]";
}

CString CFiniteField::Divide(CString op1, CString op2)
{
   return Multiply(op1, Invert(op2));
}

CString CFiniteField::Negate(CString op)
{
   int n1 = op[1] - '0';
   int n2 = (- n1) % m_prime;
   return CString("[") + char(n2+'0') + "]";
}

CString CFiniteField::Invert(CString op)
{
   for (int i = 0; i < m_nElements; i++) {
       if (Multiply(op, m_strElements[i]) == "[1]")
           return m_strElements[i];
   }
//If we got here it was probably division by zero
   ASSERT(FALSE);
   return ("[0]");
}

19. Your job is to make this application work for the more challenging cases where the number of elements in the field is a power of a prime number.  With a limit of 49 elements (only because you can't get more buttons onto a property page of the size that we have used), some interesting possibilities are
4, 9, 25, 49 for m_power = 2
8, 27  for m_power = 3
16 for m_power = 4
32 for m_power = 5.
When m_power is greater than 1, you need to "simplify" the result of polynomial multiplication by taking the result modulo an irreducible polynomial.  Here are some good choices.
According to the book in which I found these polynomials (Lidl and Pilz, Applied Abstract Algebra), for these choices x is a "primitive element:" that is, every nonzero element of the field is a power of x.

If m_prime = 2
m_power = 2:   x2 + x + 1 = 0    so replace x2 by -x -1(which is the same as x + 1)
m_power = 3:   x3 + x + 1 = 0    so replace x3 by -x -1(which is the same as x + 1)
m_power = 4:   x4 + x + 1 = 0    so replace x4 by -x -1(which is the same as x + 1)
m_power = 5:   x5 + x2 + 1 = 0    so replace x5 by -x2 -1(which is the same as x2 + 1)

If m_prime = 3
m_power = 2:   x2 + 2x + 2 = 0    so replace x2 by -2x - 2 (which is the same as x + 1)
m_power = 3:   x3 + 2x + 1 = 0    so replace x3 by -2x -1(which is the same as x + 2)

If m_prime = 5
m_power = 2:   x2 + 3x + 3 = 0    so replace x2 by -3x -3 (which is the same as 2x + 2)

If m_prime = 7
m_power = 2:   x2 + x + 3 = 0    so replace x2 by -x -3 (which is the same as 6x + 4)

A good general strategy is to write a function that turns a CString into an array of coefficients, functions to add and multiply the polynomials that these arrays represent, and a function to convert an array of coefficients back into a button caption.

Grading standards:
Getting GF[3] and GF[5] to work (just following the instructions) 5 points
Also getting GF[7]  to work (m_power = 1) : 6 points
Also getting GF[4]  to work (m_power = 2, but you can hard-code each case) : 7 points
Also GF[9]  to work (m_power = 2)  : 8 points
For the next two the polynomial muliplication starts to get hard
Also getting GF[8]  to work (m_power = 3)  : 9 points
At this point you have done all the cases that are posted on the course Web site
Also getting GF[16] (m_power = 4) to work  10 points
The following don't really look worth the effort, but if you're having fun, keep going:
Also getting GF[25](m_prime = 5)  to work 11 points
Also getting GF[27](m_prime = 3, m_power = 3)  to work 12 points
Also getting GF[49](m_prime = 7)  to work 13 points
Also getting GF[32](m_power = 5)  to work 14 points
(There will be a limit of 40 points for all 4 programming exercises in the course, however)