Question: PLEASE HELP ME CODE THIS PROGRAM. ALL I ASK FOR ARE SOME EXAMPLES OF HOW TO GO ABOUT DOING THIS AS YOU WILL SEE IN

PLEASE HELP ME CODE THIS PROGRAM. ALL I ASK FOR ARE SOME EXAMPLES OF HOW TO GO ABOUT DOING THIS AS YOU WILL SEE IN MY BOLD & ITALIC MESSAGES THROUGHOUT THIS PROBLEM. I HAVE INCLUDED CODE FOR YOU TO AKE A LOOK AT AND HELP ME THROUGH THIS.

Overview

The purpose of this part of the project is to make sure that you know how to write a program that will leverage linked lists, creates separate compilation files, and also utilizes external linkage.

PROGRAM SPECIFICATION

The universities really liked the ability to bundle the information for the labs, but now there is some consideration given to increasing the performance of the system that have been accompanied with recommendations. Our task will be to implement these suggested changes.

Recap of current system

The computer lab is a system that hosts users of several universities computer labs, and allows users of the labs to log into and log off from the available machines. The computer lab system also keeps track of the machines that are available and those that are in use, allowing users to locate an existing computer that is free as well as free the computers (remove users) upon the users logout.

Specifications for the program

You have 8 universities under contract. These labs contain computer stations that are hold the work stations numbered as shown in the table below:

Lab Number

Computer station numbers

1

1-19

2

1-15

3

1-24

4

1-33

5

1-61

6

1-17

7

1-55

8

1-37

The universities would like to change the structure of the dynamic array of objects to that of a linked list. This linked list should continue to hold the same information as before, and also continue to store the data dynamically (on the heap). We will be creating linked lists to replace our member arrays, and the pointer array too! Each node will be dynamically allocate for this Linked List. We also will be leveraging one of the major benefits that come with the linked list by allocating only as many nodes as there are active users. What this means is that for each universitys lab, we can grow and shrink the linked list accordingly. We will continue to utilize the parallel arrays from prior, and we will use the maximum size for each linked list from the corresponding values found in the LABSIZES array. Whenever a linked lists size reaches the threshold, issue a message that states the lab is full at this time and to try again later. So, for example, when I have allocated the 19th active node on linked list 1, the next request for a new user will exceed the limit and generate a message (assuming no users had been removed between requests).

Another requirement for the linked list pertains to requests for the removal of a user from a work station. You will have the choice of two mutually exclusive options here to perform this task, and you should explicitly state in your comments which of the two options that you are using. The first option is to remove the node, and rewire the pointers. The second option is to create what is called a tombstone. With this option you leave the node intact, but mark the node as being not in use (use a sentinel?). These tombstones can then be reused for new users by searching through the list and locating one sequentially as you go. Consider how to manage this. Creating tombstones only when the threshold was reached? Allocate the entire linked list and initially fill it with tombstones? Make a choice based on solid reasoning, and weigh the tradeoffs. Overall, the first option of no tombstones allows us to save memory, by only allocating when someone uses a station, and by deallocating a node when the user logs off. Youll have to accommodate the index somehow, and likely that will involve adding another member item to your class to hold it. The overhead to that will be carrying a small int around for each node. The second option yields faster processing because we dont do any individual Input/Output operations (allocations/deallocations) after we do the initial allocation. Notice, with this change you might consider the existing capacity checking procedure and modify that process as you need to with efficiency in mind.

Next, the recommendation has been put forth to make use of iterators for the linked list. An iterator is a generalization of a pointer. Iterators are used to move through the elements in some range of a container. The operations ++ , -- , and re-referencing * are usually defined for an iterator. The basic idea, and in fact the prototypical model for iterators, can easily be seen in the context of linked lists. A linked list is one of the prototypical data structures, and a pointer is a prototypical example of an iterator. Where applicable, use these iterators exclusively for all operations. Construct your iterator as constant iterator where it should be too.

Next, you will create a file for capturing login information. This file will log all of the information and activities in chronological order as new users login, and only for login information and logout. The design of this file is up to you, but this file must contain the following:

Timestamp, a c-string of 20 characters that looks like: Mon Jun 12 15:22:30

Action, a char that is in/out, that is: I (for login), and O (for logout)

UserID, this is the integer from the nodes element, can pad with leading zeroes

User time, this is our integer from the nodes element.

User name, this is our string (putting this last to make it easier to read back).

Each and every time that your program runs, this file will be appended to. The name of the log file is totally up to you, but should be a .txt extension. Design a separate function(s) to handle all of this. Be sure to write only when the user has successfully completed a transaction. Dont forget to think through what happens when a request has to wait because the lab is full.

Consider the following code example for the time (must #include ctime, which you may already have from randomizing):

// current date/time based on current system

time_t now = time(0);

// convert now to c-string form

char* timeOf = ctime(&now);

// we want a way to limit the size to be just 20 in length

timeIn[20] = '\0'; // this effectively truncates the c-string

This code works and can be used directly in your program. Placing spaces between each item/field in your file output will probably be a good idea, in case we need to read this log later (and we will). Make use of manipulators for the output.

Finally, we will now use separate compilation files, and we will try to externalize our CONSTANTS (see below).

Design of the structures

We will continue to use the following code that creates a constant fixed array of length 8 for the labs. This is our array that holds the number of possible work stations. NUMLABS is also created to enable us to add or subtract a lab. Using constants here allows our program to be much more dynamic in terms of modification efforts and control. This array is also used for the allocation of the linked list (the sizes).

Separate files by placing your code into implementations files (.cpp), and also all of your headers (.hpp). A header contains class and functions definitions. The implementation file holds the implementation of the class. By doing this, if our class implementation doesnt change then it wont need to be recompiled. Dont forget your include guards. You will at least have main, a file that has your application (cpp), and your link list class (hpp).

Next, modify this part of your existing code to declare these constants using either an unnamed Namespace or a Namespace named myConstants.

// Global Constants

// Number of computer labs

const int NUMLABS = 8;

// Number of computers in each lab

const int LABSIZES[NUMLABS] = {19, 15, 24, 33, 61, 17, 55, 37};

// Names of university of each lab

const std::string UNIVERSITYNAMES[NUMUNIVERSITIES] = {"The University of Michigan", "The University of Pittsburgh", "Stanford University", "Arizona State University", "North Texas State University", "The University of Alabama, Huntsville", "Princeton University", "Duquesne University"};

You are going to figure out how to share the constants with the other functions. Consider extern to explicitly make the constants NUMLABS, LABSIZES, and UNIVERSITYNAMES external. We want these constant identifiers to be defined in one translation unit (hpp) and shared with other translation units. Dont make extra copies (see the lecture notes).

Consider your login logic and insert the function(s) to add your log file. Make sure you successfully logged in first before adding the record to your file.

Design of the application

Everything else in your system should remain intact, and the functionality should be the same. This means all off the displays (menu, etc) look and behave exactly as they did before. Carefully consider your redesign effort strategy to maintain your functionality, and to change code as minimally as possible. Test thoroughly, and test incrementally.

Thats a lot to change, and at a high level heres what we are doing: 1) convert arrays of objects to linked lists (holding the same objects); 2) distribute code into separate compilation files, 3) convert to iterators for searching, reading, and writing to the linked lists; 4) create a log file for each and every login and logout, and; 5) employ external linkage for the constants listed above.

You may continue to maintain your data as classes in the linked list or convert them to structs, your choice. Your list class may encapsulate a separate node class too (see the demo), but whatever you decide you will want to have all of the supporting methods in your design. Do not forget to manage (and free ~destructor) the LL memory since we are on the heap. Do not use #pragma directives in your files in place of include guards. #Pragma is not approved by the C++ Standards Committee. Be careful not to use the linked lists from the STL either or you will receive no credit L. Yikes!

So this is a modification of the code that I will provide below. I DO NOT need you to code the whole thing. I really just need help or an example as to how I would go about dynamically allocating an array of linked lists. I also would like to see an example of how I would use an iterator to access the linked lists. The constants, the output file I can figure out. I'm just stuck on creating and accessing the linked lists. Please see attached code below, THE FOLLOWING IS WHERE TO START (THIS IS A WORKING PROGRAM THAT FOLLOWS PRIOR INSTRUCTIONS), THIS BUILDS ON THIS CODE:

#include #include #include #include #include //library for sleep

using std::cin; using std::cout; using std::endl; using std::setw; using std::setfill;//used to make 5-digit ID's using std::string;

class Student//class required to store the user's ID, name and time spent on the computer. { private: //needed variables..kept private int time; int ID; string Name; public: //setters & getters void setName(string); string getName(); void setID(int); int getID(); void setTime(int); int getTime(); Student();//constructor ~Student();//deconstructor };

//deconstructor for the class Student, empty braces Student::~Student(){}

Student::Student()//just gives initial values to the three private variables of the class { ID = -1;//will store the random ID Name = "";//stores the valid string from user time = 0;//stores the time desired by user }

//Pre: just needs to be called with a string type parameter //Post: nothing returned but Name gets the parameter x void Student::setName(string x) { Name = x; }

//Pre: needs to be called with dot operator //Post: returns the value in Name (string) string Student::getName() { return Name; }

//Pre: needs to be called and have an int parameter //Post: nothing returned but ID gets the parameter y void Student::setID(int y) { ID = y; }

//Pre: needs called by . operator //Post: returns the value in ID as an int int Student::getID() { return ID; }

//Pre: needs to be called and have an int parameter //Post: nothing returned but time gets the parameter z void Student::setTime(int z) { time = z; }

//Pre: needs to be called by . operator //Post: returns the value in time as an int int Student::getTime() { return time; }

//Function prototypes void beginArray(Student* labs[], const int labsizes[]); void clearArray(Student* labs[]); void displayStatus(Student* labs[], const int labsizes[]); void login(Student* labs[], const int labsizes[]); void logoff(Student* labs[], const int labsizes[]); void searchUser(Student* labs[], const int labsizes[]);

// Global Constants // Number of computer labs const int NUMLABS = 8; const int NUMUNIVERSITIES = 8; // Number of computers in each lab const int labsizes[NUMLABS] = {19, 15, 24, 33, 61, 17, 55, 37};

//list of university names to be printed when display lab is called const string UNIVERSITYNAMES[NUMUNIVERSITIES] = { "The University of Michigan", "The University of Pittsburgh", "Stanford University", "Arizona State University", "North Texas State University", "The University of Alabama, Huntsville", "Princeton University", "Duquesne University" };//given list of colleges for parallel array

//MAIN int main() {

//Array to reference each lab of Student type Student* labs[NUMLABS]; int choice = 1;

//create array structure beginArray(labs, labsizes);

//Initial Lab Selection cout << " WELCOME -- SEE THE FOLLOWING FOR A LIST OF AVAILABLE LABS: "; cout << "lab # 1 The University of Michigan "; cout << "lab # 2 The University of Pittsburgh "; cout << "lab # 3 Stanford University "; cout << "lab # 4 Arizona State University "; cout << "lab # 5 North Texas State University "; cout << "lab # 6 The University of Alabama, Huntsville "; cout << "lab # 7 Princeton University "; cout << "lab # 8 Duquesne University ";

// Repeating Menu**************************************************************************************** while (choice != 5) { //call the displayStatus function cout << endl;

//displays the main menu for user to choose what to do next(labels are pretty self explanatory) cout << " __________________________________________________________________" << endl; cout << "|------------------------------------------------------------------|" << endl; cout << "| Student Incorporated |" << endl; cout << "| Computer Lab System |" << endl; cout << "|__________________________________________________________________|" << endl; cout << "| MAIN MENU |" << endl; cout << "| 1) Simulate Login |" << endl; cout << "| 2) Simulate Logoff |" << endl; cout << "| 3) Search |" << endl; cout << "| 4) Display a lab |" << endl; cout << "| 5) Quit |" << endl; cout << "|__________________________________________________________________|" << endl;

cin >> choice; if(choice == 1) { cout << " Your Choice: 1 " << endl << endl;//displays choice back to user login(labs, labsizes);//Notice the functions match the choices offered to the user (makes it simple)!! } else if (choice == 2) { cout << " Your Choice: 2 " << endl << endl; logoff(labs, labsizes); } else if (choice == 3) { cout << " Your Choice: 3 " << endl << endl; searchUser(labs, labsizes); } else if (choice == 4) { cout << " Your Choice: 4 " << endl << endl; displayStatus(labs, labsizes); } } //*************************************************************************************************************

//free the allocated memory clearArray(labs); return 0; }

//PRE-Condition: Variables for the lab array and labsize array must be created, function is prompted at beginning of program //POST-Condition: Array has been dynamically allocated for use with the program, and ready to accept data void beginArray(Student* labs[], const int labsizes[]) { int i, j; //loop dynamically allocates array for the program for (i = 0; i < NUMLABS; i++) { labs[i] = new Student[labsizes[i]]; for (j = 0; j < labsizes[i]; j++) { //-1 means unused computer labs[i][j].setID(-1); } } return;//Nothing returned here, however we are ready for the user to login or out! } //____________________________________________________________________________________________________________________________________________________________________________

//PRE-Condition: Menu option 5 is selected, the array is no longer needed and the user wishes to end program //POST-Condition: The array is now cleared and the space/memory is now free of data from the program void clearArray(Student* labs[]) { for (int i = 0; i < NUMLABS; i++) { delete[] labs[i];//Uses the delete function to delete every element in the array } return; } //____________________________________________________________________________________________________________________________________________________________________________

//PRE-Condition: Menu option 4 is selected and the program has stored user info(ID, lab no, comp no) and will be passed to this function //POST-Condition: The program has now displayed all information about labs array requested by user to standard output void displayStatus(Student* labs[], const int labsizes[]) { int labNum = 1, counter = 0; //counter is used simply for output, when it reaches 5, then it breaks into a new line to make it easier to read cout << "Please a enter a lab to display: "; cin >> labNum; while (labNum <= NUMLABS && labNum >= 1) { cout << "LAB STATUS Lab # " << labNum << " for " << UNIVERSITYNAMES[labNum - 1] << " Computer Stations "; for(int i = 0; i < labsizes[labNum - 1]; i++) { cout << setw(2) << std::left << i + 1 << ":"; if (labs[labNum-1][i].getID() == -1) { cout << " empty ";//if empty simply empty is printed counter++;//display purposes } else { cout << " " << setfill('0') << setw(5) << std::right << labs[labNum-1][i].getID() << " " << setfill(' ');//if not empty the user will be printed counter++;//display purposes } if(counter == 5)//when five stations are printed it goes to a new line to ease reading { cout << ' '; counter = 0;//resets back to 1 to start again } } return; } return;//nothing returned, but the user can clearly see what stations are open! } //____________________________________________________________________________________________________________________________________________________________________________

//PRE-Condition: the newly allocated array is sent to this function, and the user must have chosen menu option 1. //POST-Condition: the function is returned to menu with the new user saved into the database. void login(Student* labs[], const int labsizes[]) { int id, lab = -1, num = -1, t = 0;//declare and initialize needed variables (neg numbers eliminate the possibility of infinite loops) string name;

//Get input then validate // THIS BLOCK VALIDATES THE CHOSEN LAB NUMBER & CHECKS FOR AVAILABILITY--------------------------------------------------------------------------------------- while ((lab < 1) || (lab > NUMLABS)) { cout << "Enter the lab number(1 - " << NUMLABS << ") :" << endl; cin >> lab;//reads in lab number //the following loop checks availability for a user to log into that lab for(int k = 0; k < labsizes[lab - 1]; k++)//because of the strategic order, the lab can be filled to the max before the third loop will catch { if(labs[lab - 1][k].getID() == -1) break;//if there is an empty spot we break and continue getting info from user if(k == labsizes[lab - 1] - 1)//this condition is satisfied when the k variable equals the max number of k, of course after the last spot is already filled. { { cout << " Lab " << lab << ", " << UNIVERSITYNAMES[lab - 1] << " ,is at full capacity! "; cout << "SYSTEM RESTARTING... 3 "; sleep(1); // cout << "2 "; // sleep(1); //unecessary just me having fun, enjoy! It counts down before returning so the user sees how many seconds. cout << "1 "; // sleep(1); // return;//returns back to main menu for user to do something else. } } } } //---------------------------------------------------------------------------------------------------------------------------------------------------------- // THIS BLOCK VALIDATES THE CHOSEN LAB STATION +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ while ((num < 1) || (num > labsizes[lab - 1])) { cout << "Enter computer station desired " <<"(1 - " << labsizes[lab-1] << ") : "<< endl; cin >> num;//reads in desired station }

//Check to see if this station is free, prints an error message if not available if (labs[lab - 1][num - 1].getID() != -1) { cout << "ERROR, user " << std:: right << setfill('0') << setw(5) << labs[lab - 1][num - 1].getID() << setfill(' ') << " is already logged into that station. Please try again" << endl; return; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

id = rand () % 10000;//make the random user id

cout << " User ID: " << std::right << setfill('0') << setw(5) << id << setfill(' ') << endl;//print the user ID with 5 digits

// THE FOLLOWING VALIDATES THE USER'S NAME _________________________________________________________________________________________________________________ cout << "Please enter your name: ";//prompt user for name cin.ignore();//clear the input for getline below

do { getline(cin, name);//read in name

if((name.length() > 36) || (name.length() == 0)) cout << " Error name is invalid choose one less than 35 characters & at least one letter. ";

}while((name.length() > 36) || (name.length() == 0));//this loop only breaks when a valid name is provided //__________________________________________________________________________________________________________________________________________________________

//Assign this station to the user labs[lab - 1][num - 1].setID(id); //Assign name to corresponding user labs[lab - 1][num - 1].setName(name);

// THE FOLLOWING VALIDATES THE AMOUNT OF TIME DESIRED ON THE COMPUTER ************************************************************************************** do { cout << " Please enter the desired minutes for this workstation (15/30/45/60): "; cin >> t; }while((t != 15) && (t != 30) && (t != 45) && (t != 60)); //Assign validated time to class variable labs[lab - 1][num - 1].setTime(t); return;//Nothing is returned but the user is now into a lab and computer station!

} //____________________________________________________________________________________________________________________________________________________________________________

//PRE-Condition: The user must have selescted menu option 2, and this assumes a user has been previously logged in. //POST-Condition: The function returns back to main menu with either an updated array or updated array/error message. void logoff(Student* labs[], const int labsizes[]) { int id = -1, i, j; //get the input from the keyboard, validating data ranges

while ((id < 0) || (id > 99999)) { cout << "Enter the 5 digit ID number of the user to logoff:" << endl; cin >> id;//get the id of the user wanting to log off } for (i = 0; i < NUMLABS; i++) { for(j = 0; j < labsizes[i]; j++) { if(labs[i][j].getID() == id) { //Log the user off by seting the entry to -1 labs[i][j].setID(-1); cout << "User " << setfill('0') << setw(5) << std::right << id << setfill(' ') << " successfully logged off." << endl;//prints status return; } } } cout << "That user is not loggd in." << endl;//takes care of nonexisting users return;//Nothing returned but the specified user is now logged off } //____________________________________________________________________________________________________________________________________________________________________________

//PRE-Condition: Menu option 3 must have been selected, and the function assumes a user has been previously logged in. //POST-Condition: A message is displayed to user with either an error or info about the user wanted(comp no, lab no) void searchUser(Student* labs[], const int labsizes[]) { int id = -1, i, j; //get input from the keyboard, validating data ranges while ((id < 0) || (id > 99999)) { cout << "Enter the five digit ID number of the user to find:" << endl; cin >> id; } for (i = 0; i < NUMLABS; i++)//executes loop checking every element of the 2D array { for (j = 0; j < labsizes[i]; j++) { if(labs[i][j].getID() == id)//requests the ID from the class each iteration { cout << "User " << id << " is in lab " << i + 1 << " at computer " << j + 1 << endl;//prints status return; } } } cout << "That user is not logged in." << endl;//takes care of non existing users return;//Nothing returned, just the information about a particular user's location }

Please notice that this code does compile and is the code that needs to be modified per the instructions above. I will also include an example of how a linked list is typically used, so you can help me figure out how to apply it here. The following is NOT part of the assignment just to give you some ideas, it's basically what I have to work with.

Header FIle:

#ifndef CHARLIST_HPP_INCLUDED #define CHARLIST_HPP_INCLUDED

//My class class CharList { private: //declare a structure for the list struct ListNode { char value; //value stored here struct ListNode *next;//points to next node }; ListNode *head; //list head pointer public: //constructor CharList() {head = nullptr;}

//destructor ~CharList();

//Linked list operations void appendNode(char); void insertNode(char); void deleteNode(char); void displayList() const; };

#endif // CHARLIST_HPP_INCLUDED

Program that uses linked lists.

#include #include "charList.hpp"

//Implementation of the insert node function

//PRE: called from main with a single char sent as the parameter //POST: adds the char character to the linked list by making a new node and storing it void CharList::insertNode(char c) { ListNode *newNode; //new node ListNode *nodePtr; //traverse the linked list ListNode *previousNode = nullptr; //previous trailing node

//Allocate a new node and store c there newNode = new ListNode; newNode->value = c;

//If there are no nodes in the list make newNode the first node if (!head) { head = newNode; newNode->next = nullptr; } else //Otherwise, insert the node { //position nodePtr at the head of the list nodePtr = head;

//Initialize prevNode to nullptr. previousNode = nullptr;

//Skip all nodes whose value is greater than c. while (nodePtr != nullptr && nodePtr->value > c) { previousNode = nodePtr; nodePtr = nodePtr->next; }

//If the new node is to be the 1st in the list, insert it before all other nodes. if (previousNode == nullptr) { head = newNode; newNode->next = nodePtr; } else //otherwise insert after the previous node { previousNode->next = newNode; newNode->next = nodePtr; } } }

//************************************************** // displayList shows the value * // stored in each node of the linked list * // pointed to by head. * // pre: an empty parameter list * // post: standard output of the linked list * //**************************************************

void CharList::displayList() const { ListNode *nodePtr; // To move through the list

// Position nodePtr at the head of the list. nodePtr = head; short count = 0;

// While nodePtr points to a node, traverse // the list. while (nodePtr) { // Display the value in this node. std::cout << "[" << nodePtr->value << "] -> "; ++count; // Move to the next node. nodePtr = nodePtr->next; if (count % 10 == 0) { std::cout << std::endl; count = 0; } } std::cout << std::endl; }

//Pre: called from main and sent a single char //Post: the character sent to the function will add that character to the end void CharList::appendNode(char c) { ListNode *newNode; //points to new node ListNode *nodePtr; //moves throgh linked list

//make room for a new node and then store it newNode = new ListNode; newNode->value = c; newNode->next = nullptr;

//if no nodes exist, make the new node the first if(!head) head = newNode; else //Otherwise, insert newNode at the end { //Initialize nodePtr to head of linked list nodePtr = head;

//Find the last node in the list while (nodePtr->next) nodePtr = nodePtr->next;

//Insert the new node as the last node nodePtr->next = newNode; } }

//************************************************** // Destructor * // This function deletes every node in the list. * // pre: n/a * // post: destroyed object * //**************************************************

CharList::~CharList() { ListNode *nodePtr; // To traverse the list ListNode *nextNode; // To point to the next node

// Position nodePtr at the head of the list. nodePtr = head;

// While nodePtr is not at the end of the list... while (nodePtr != nullptr) { // Save a pointer to the next node. nextNode = nodePtr->next;

// Delete the current node. delete nodePtr;

// Position nodePtr at the next node. nodePtr = nextNode; } }

//Pre: called from main sending a single char variable as the parameter //Post: the charachter sent is deleted from the linked list. void CharList::deleteNode(char c) { ListNode *nodePtr; //moves through the list ListNode *previousNode; //points to prior node

//if the list is empty, return...do nothing. if (!head) return;

//determine if the first node is what we want to delete if (head->value == c) { nodePtr = head->next; delete head; head = nodePtr; } else { //initialize nodePtr to head of list nodePtr = head;

//skip all nodes != c while (nodePtr != nullptr && nodePtr->value != c) { previousNode = nodePtr; nodePtr = nodePtr->next; }

//if nodePtr is not at the end, the previous node is linked to node after, then delete nodePtr. if(nodePtr) { previousNode->next = nodePtr->next; delete nodePtr; } } }

PLEASE ANY HELP IS APPRECIATED, YOU CAN ASK QUESTIONS BY COMMENTING BEFORE YOU ANSWER, I WILL RESPOND QUICKLY.

Step by Step Solution

There are 3 Steps involved in it

1 Expert Approved Answer
Step: 1 Unlock blur-text-image
Question Has Been Solved by an Expert!

Get step-by-step solutions from verified subject matter experts

Step: 2 Unlock
Step: 3 Unlock

Students Have Also Explored These Related Databases Questions!