15.98 - Write a program that reads a file (e15.98_name_data.txt in the Student F
ID: 3682520 • Letter: 1
Question
15.98 - Write a program that reads a file (e15.98_name_data.txt in the Student Files folder copied to your project's folder) containing data about the changing popularity of various baby names over time and displays the data about a user selected name. Use JFileChooser (Figures 15.12 and 15.13 in the textbook) to prompt for the name of the input file. Each line of the file contains a name followed by integers representing the name's popularity in each decade: 1900, 1910, 1920, and so on. 1900 is first decade. Other decades must not be "hard coded". The rankings range from 1 (most popular) to 1000 (least popular). Two parallel arrays are recommended. A 1-dimensional string array to hold the names and a 2-dimensional integer array to hold the popularity values.
Added 3/16/2016:
Since your program will need to compare Strings for equality, it is very important that you read 14.3.3 before attempting to compare String variables. The == equality operator does not work in the way you think when used with Strings and you will need to use the .equals() String method to compare two Strings for equality.
Your program should list the names in the name string array, prompt the user to enter a name, search the name string array for the name, provide an error message if an invalid name is selected, determine the decade of the greatest popularity of the selected name, and output the following to a JTextArea window (Figure 15.13 in the textbook, code example in Figure 15.12):
Statistics on name "Sam":
1900: 58
1910: 69
1920: 145
1930: 88
.
.
.
Average Popularity: 89.75
Decade of Greatest Popularity: 1900
Set the Font of the output to 12 point Courier. See Figure 13.11 for an example.
Your program(s) must include the following:
1. Tell the user how to terminate prompted input of unspecified length.
2. Include description of what user is being prompted for each input.
3. Include description of units when appropriate, i.e., .05 or 5 for 5%.
4. Provide appropriate try/catch/throw exception processing where appropriate.
FILE CONTENTS:
Sam 409 228 149 169 977 779 87 479 449 630 744
Sally 780 790 955 132 852 214 359 901 73 631 932
Sean 67 894 125 475 955 214 662 475 693 186 340
Greg 79 621 979 622 891 88 710 748 794 433 384
Steve 743 909 930 551 974 965 692 808 252 725 261
Jerri 229 777 261 840 419 672 43 148 867 659 513
Ned 42 469 861 427 799 420 334 203 369 134 508
Viper 721 722 537 991 718 794 362 584 760 979 92
Goofy 942 601 691 52 243 215 151 90 414 543 512
Hazel 661 921 383 226 565 26 930 84 237 834 34
Janet 682 634 979 696 227 766 964 406 329 378 307
(Book used: Java How To Program Deitel 10th Edition)
Explanation / Answer
import java.awt.Color;
import java.awt.Graphics2D;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class Project4 {
public static void main(String[] args) {
try {
Scanner user = new Scanner(System.in);
Scanner input = new Scanner(new File("project4.txt"));
System.out.println("This program allows you to search through the "
+ "datat from the Social Security Administration "
+ "to see how popular a particular name has been ");
System.out.print("Name: ");
String name = user.next();
// Create the window
DrawingPanel panel = new DrawingPanel(500, 500);
panel.setBackground(Color.white);
Graphics2D g = panel.getGraphics();
g.setColor(Color.black);
String dataName = null;
int prevPopularity = 0;
int prevYear = 80;
int curYear = 0;
int curPopularity = 0;
while (input.hasNext()) {
dataName = input.next();
if (dataName.equalsIgnoreCase(name)) {
System.out.println(" Statistics on name "" + dataName + """);
for (int year = 1900, popularity = 0; input.hasNextInt(); year += 10) {
popularity = input.nextInt();
curPopularity = (1000 - popularity) / 4;
curYear = 3 * (year - 1900) + 80;
System.out.printf(" %d: %d ", year, popularity);
if (prevPopularity == 0) {
prevYear = curYear;
prevPopularity = curPopularity;
}
g.drawLine(prevYear, prevPopularity, curYear, curPopularity);
g.fillOval((int) (curYear - 2.5), (int) (curPopularity - 2.5), 5, 5);
panel.copyGraphicsToScreen();
prevPopularity = curPopularity;
prevYear = curYear;
}
}
}
} catch (IOException error) {
System.out.println("Error in processing the file project4.txt" + error);
}
}
}
project4.txt
Sally 0 0 0 0 0 0 0 0 0 0 886
Sam 58 69 99 131 168 236 278 380 467 408 466
Samantha 0 0 0 0 0 0 272 107 26 5 7
Samir 0 0 0 0 0 0 0 0 920 0 798
DrawingPanel.java
import java.awt.*;
import java.awt.color.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import java.awt.EventQueue;
import javax.imageio.*;
import javax.swing.*;
import javax.swing.event.*;
//=====================================================================
public class DrawingPanel extends Thread implements MouseInputListener, KeyListener
{
public static final int LEFT_BUTTON = MouseEvent.BUTTON1; // 1
public static final int MIDDLE_BUTTON = MouseEvent.BUTTON2; // 2
public static final int RIGHT_BUTTON = MouseEvent.BUTTON3; // 3
private static final boolean PRETTY = true; // true to anti-alias
private static final int STATUS_BAR_HEIGHT = 30;
private static final int MAXIMUM_ACTIVE_KEYS = 20;
public static final int NO_KEY_PRESSED = 0;
public static final int ANY_KEY = 1;
public static final int F1_KEY = KeyEvent.VK_F1;
public static final int F2_KEY = KeyEvent.VK_F2;
public static final int F3_KEY = KeyEvent.VK_F3;
public static final int F4_KEY = KeyEvent.VK_F4;
public static final int F5_KEY = KeyEvent.VK_F5;
public static final int F6_KEY = KeyEvent.VK_F6;
public static final int F7_KEY = KeyEvent.VK_F7;
public static final int F8_KEY = KeyEvent.VK_F8;
public static final int F9_KEY = KeyEvent.VK_F9;
public static final int F10_KEY = KeyEvent.VK_F10;
public static final int F11_KEY = KeyEvent.VK_F11;
public static final int F12_KEY = KeyEvent.VK_F12;
public static final int LEFT_ARROW_KEY = KeyEvent.VK_LEFT;
public static final int RIGHT_ARROW_KEY = KeyEvent.VK_RIGHT;
public static final int UP_ARROW_KEY = KeyEvent.VK_UP;
public static final int DOWN_ARROW_KEY = KeyEvent.VK_DOWN;
public static final int INSERT_KEY = KeyEvent.VK_INSERT;
public static final int HOME_KEY = KeyEvent.VK_HOME;
public static final int DELETE_KEY = KeyEvent.VK_DELETE;
public static final int PAGE_UP_KEY = KeyEvent.VK_PAGE_UP;
public static final int PAGE_DOWN_KEY = KeyEvent.VK_PAGE_DOWN;
public static final int ESC_KEY = KeyEvent.VK_ESCAPE;
public static final int TAB_KEY = KeyEvent.VK_TAB;
public static final int SHIFT_KEY = KeyEvent.VK_SHIFT;
public static final int ENTER_KEY = KeyEvent.VK_ENTER;
private int width, height; // dimensions of window frame
private JFrame frame; // overall window frame
private MyCanvas canvas; // drawing canvas for window (inside panel)
private BufferedImage image; // remembers drawing commands
private Graphics2D offscreenGraphics; // buffered graphics context for painting
private JLabel statusBar; // status bar showing mouse position
private long createTime;
private Thread application;
private boolean [] mouseClicked;
private int [] mouseClickedX;
private int [] mouseClickedY;
private int mostRecentMouseButton;
private int currentMouseX;
private int currentMouseY;
private int waitingForButton;
private boolean [] buttonDown; // uses indexes of 1, 2, 3 for buttons
private int [] activeKeys;
private boolean [] keyHasBeenRetreivedByApplication;
private boolean [] keyIsDown;
private int numberActiveKeys;
private int indexOfKeyToReturn;
private boolean waitingForKeyHit;
private boolean waitingForKeyPressed;
//----------------------------------------------------------------------------
/**
* Construct a drawing panel of a given width and height enclosed in a window.
*/
public DrawingPanel(int desiredWidth, int desiredHeight)
{
// Keep a reference to the user application so that it can be suspended to
// wait for mouse and keyboard events.
width = desiredWidth;
height = desiredHeight;
application = Thread.currentThread();
// Start the drawing panel in its own thread
this.run();
}
public void run()
{
// Construct a buffered image (an offscreen image that is stored in RAM)
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
offscreenGraphics = image.createGraphics();
offscreenGraphics.setColor( Color.BLACK );
// Create a AWT canvas object that can be drawn on - the offscreen image will
// be drawn onto this canvas
canvas = new MyCanvas(this);
canvas.setPreferredSize(new Dimension(width, height));
canvas.setBounds(0, 0, width, height);
canvas.setBackground( Color.WHITE );
canvas.addMouseListener(this);
canvas.addMouseMotionListener(this);
canvas.addKeyListener(this);
// Create a swing label to display the location of the cursor
statusBar = new JLabel(" ");
statusBar.setBorder(BorderFactory.createLineBorder(Color.BLACK));
statusBar.setBackground(Color.WHITE);
statusBar.setForeground(Color.BLACK);
statusBar.setBounds(0, STATUS_BAR_HEIGHT, width, 20);
// Create the window
frame = new JFrame();
frame.setTitle("Drawing Panel");
frame.setResizable(false);
frame.setLayout( new BorderLayout() );
frame.getContentPane().add(canvas, "North");
frame.getContentPane().add(statusBar, "South");
frame.pack();
frame.setFocusable(true);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.toFront();
// Initialize the mouse states
// Store information about mouse clicks for each button separately
mouseClicked = new boolean[4];
mouseClickedX = new int[4];
mouseClickedY = new int[4];
buttonDown = new boolean[4];
for (int j=0; j<4; j++)
{
mouseClicked[j] = false;
mouseClickedX[j] = 0;
mouseClickedY[j] = 0;
buttonDown[j] = false;
}
// Store general information about the state of the mouse
mostRecentMouseButton = -1;
currentMouseX = -1;
currentMouseY = -1;
waitingForButton = -1;
// Initialize the key (keyboard) states
activeKeys = new int[MAXIMUM_ACTIVE_KEYS];
keyHasBeenRetreivedByApplication = new boolean[MAXIMUM_ACTIVE_KEYS];
keyIsDown = new boolean[MAXIMUM_ACTIVE_KEYS];
numberActiveKeys = 0;
indexOfKeyToReturn = -1;
waitingForKeyHit = false;
waitingForKeyPressed = false;
sleep(1000);
// Make the canvas have the focus so that events are immediately sent to it.
canvas.requestFocus();
frame.setAlwaysOnTop(false);
}
//----------------------------------------------------------------------------
/**
* Set the windows name -- which appears in the window's header.
*/
public void setWindowTitle(String name)
{
frame.setTitle(name);
}
//----------------------------------------------------------------------------
/**
* Close the DrawingPanel (actually closes the JFrame)
*/
public void closeWindow()
{
frame.dispose();
}
public void mouseMoved(MouseEvent e)
{
currentMouseX = e.getX();
currentMouseY = e.getY();
statusBar.setText("(" + currentMouseX + ", " + currentMouseY + ")");
}
public void mouseEntered(MouseEvent e)
{
mouseMoved(e);
}
public void mouseExited(MouseEvent e)
{
statusBar.setText(" ");
}
//----------------------------------------------------------------------------
/*
* MouseInputListener Callback (application never calls this method)
*/
public void mousePressed(MouseEvent e)
{
mostRecentMouseButton = e.getButton();
buttonDown[mostRecentMouseButton] = true;
}
//----------------------------------------------------------------------------
/*
* MouseInputListener Callback (application never calls this method)
*/
public void mouseReleased(MouseEvent e)
{
mostRecentMouseButton = e.getButton();
buttonDown[mostRecentMouseButton] = false;
mouseClicked[mostRecentMouseButton] = true;
mouseClickedX[mostRecentMouseButton] = e.getX();
mouseClickedY[mostRecentMouseButton] = e.getY();
}
public void mouseDragged(MouseEvent e)
{
mouseMoved(e);
}
public void mouseClicked(MouseEvent e)
{
int whichButton = e.getButton();
// get the mouse click information and save it for retreival later
mouseClicked[whichButton] = true;
mouseClickedX[whichButton] = e.getX();
mouseClickedY[whichButton] = e.getY();
mostRecentMouseButton = whichButton;
//System.out.println("Recognized click button " + whichButton);
if ( waitingForButton == whichButton )
{
// The application is waiting for this button
waitingForButton = -1;
//System.out.println("mouse click = " + mouseButton + " x = " + mouseX + " y = " + mouseY);
// Resume the application
synchronized(application)
{
application.notify();
}
}
}
private int findKeyCodeIndexInList(int keyCode)
{
for (int j=0; j<numberActiveKeys; j++)
{
if (activeKeys[j] == keyCode)
return (j);
}
return (-1);
}
private void printActiveKeys(String description)
{
// Debugging
System.out.print(description + numberActiveKeys + " keys in buffer: ");
for (int j=0; j<numberActiveKeys; j++)
System.out.printf("%d %c %b %b ", activeKeys[j], (char) activeKeys[j],
keyHasBeenRetreivedByApplication[j] ,keyIsDown[j] );
System.out.println();
}
private void removeActiveKey(int index)
{
for (int j=index; j<numberActiveKeys-1; j++)
{
activeKeys[j] = activeKeys[j+1];
keyHasBeenRetreivedByApplication[j] = keyHasBeenRetreivedByApplication[j+1];
keyIsDown[j] = keyIsDown[j+1];
}
numberActiveKeys--;
if (numberActiveKeys < 0) numberActiveKeys = 0;
}
public void keyPressed(KeyEvent e)
{
//System.out.printf("KEY PRESSED EVENT keycode = %d %c ", e.getKeyCode(), e.getKeyCode());
// Add this key code to the list of keys that are currently down
if (numberActiveKeys < MAXIMUM_ACTIVE_KEYS)
{
int keyCode = e.getKeyCode();
int index = findKeyCodeIndexInList(keyCode);
if (index == -1)
{
activeKeys[numberActiveKeys]= keyCode;
keyHasBeenRetreivedByApplication[numberActiveKeys] = false;
keyIsDown[numberActiveKeys] = true;
numberActiveKeys++;
}
else
{
keyHasBeenRetreivedByApplication[index] = false;
keyIsDown[index] = true;
}
}
// If the application is waiting for a key press, wake the application up
if (waitingForKeyPressed)
{
waitingForKeyPressed = false;
synchronized(application)
{
application.notify(); // stop waiting
}
}
}
public void keyReleased(KeyEvent e)
{
int keyCode = e.getKeyCode();
// find the key in the list of active keys.
int index = findKeyCodeIndexInList(keyCode);
// Remove the key if it has been consumed at least once by the application.
if (index >= 0)
{
keyIsDown[index] = false;
if ( keyHasBeenRetreivedByApplication[index] ) removeActiveKey(index);
}
if (waitingForKeyHit)
{
waitingForKeyHit = false;
synchronized(application)
{
application.notify(); // stop waiting
}
}
}
//----------------------------------------------------------------------------
/**
* Callback method for "key typed" events (never used - never call explicitly).
*/
public void keyTyped(KeyEvent e)
{
}
//============================================================================
// Implement the "DrawingPanel's" methods
//============================================================================
//----------------------------------------------------------------------------
/**
* Obtain the Graphics object needed to draw on the DrawingPanel's offscreen
* graphics buffer. Make sure you call copyGraphicsToScreen() after all your drawing
* methods have been called to copy the offscreen graphics to the screen.
*/
//
public Graphics2D getGraphics()
{
return offscreenGraphics;
}
public void setBackground(Color c)
{
// remember the current color so it can be restored
Color currentColor = offscreenGraphics.getColor();
offscreenGraphics.setColor(c);
offscreenGraphics.fillRect(0,0,width,height);
// restore color
offscreenGraphics.setColor(currentColor);
}
public void copyGraphicsToScreen()
{
Graphics2D myG = (Graphics2D) canvas.getGraphics();
myG.drawImage(image, 0, 0, width, height, null );
}
public void sleep(int millis)
{
synchronized(application)
{
try {
application.sleep(millis);
} catch (InterruptedException e) {}
}
}
public void waitForMouseClick(int whichButton)
{
if (whichButton >= LEFT_BUTTON && whichButton <= RIGHT_BUTTON)
{
waitingForButton = whichButton;
//System.out.println("Waiting for button " + whichButton + ". Application waiting.");
synchronized(application)
{
try {
application.wait();
} catch ( InterruptedException e) {};
}
}
}
public boolean mouseClickHasOccurred(int whichButton)
{
if (whichButton >= LEFT_BUTTON && whichButton <= RIGHT_BUTTON)
{
boolean status = mouseClicked[whichButton];
mouseClicked[whichButton] = false;
return status;
}
return false;
}
//----------------------------------------------------------------------------
/**
* Determine if a specific mouse button is down at the time of the method call.
*
* @param whichButton which mouse button you want to check the status of
* (DrawingPanel.LEFT_BUTTON, DrawingPanel.MIDDLE_BUTTON, or DrawingPanel.RIGHT_BUTTON).
*
* @return true if a mouse button is down, false otherwise.
*/
public boolean isMouseButtonDown(int whichButton)
{
if (whichButton >= LEFT_BUTTON && whichButton <= RIGHT_BUTTON)
return buttonDown[whichButton];
else
return false;
}
//----------------------------------------------------------------------------
/**
* Get which mouse button was most recently clicked.
*
* @return button code (DrawingPanel.LEFT_BUTTON, DrawingPanel.MIDDLE_BUTTON, or DrawingPanel.RIGHT_BUTTON)
*/
public int getMouseButton()
{
return mostRecentMouseButton;
}
//----------------------------------------------------------------------------
/**
* Get the current x coordinate of the mouse.
*
* @return x coordinate of the mouse in pixels (left side of window is 0, right
* side is the (DrawingPanel's width - 1).
*/
public int getMouseX()
{
return currentMouseX;
}
//----------------------------------------------------------------------------
/**
* Get the current y coordinate of the mouse.
*
* @return y coordinate of the mouse in pixels (top of window is 0, bottom
* is the (DrawingPanel's height - 1).
*/
public int getMouseY()
{
return currentMouseY;
}
//----------------------------------------------------------------------------
/**
* Get the x coordinate of the mouse's location when the most recent mouse click
* of the specified button occurred. Call the mouseClickHasOccurred(whichButton)
* method to determine if a mouse click has occurred. If no mouse click has occurred,
* this method returns a bogus x coordinate.
*
* @return x coordinate of the mouse (in pixels) when the most recent mouse click for the specified button occurred.
*/
public int getMouseClickX(int whichButton)
{
if (whichButton >= LEFT_BUTTON && whichButton <= RIGHT_BUTTON)
{
return mouseClickedX[whichButton];
}
else
return -1;
}
//----------------------------------------------------------------------------
/**
* Get the y coordinate of the mouse's location when the most recent mouse click
* of the specified button occurred. Call the mouseClickHasOccurred(whichButton)
* method to determine if a mouse click has occurred. If no mouse click has occurred,
* this method returns a bogus y coordinate.
*
* @return y coordinate of the mouse (in pixels) when the most recent mouse click for the specified button occurred.
*/
public int getMouseClickY(int whichButton)
{
if (whichButton >= LEFT_BUTTON && whichButton <= RIGHT_BUTTON)
{
return mouseClickedY[whichButton];
}
else
return -1;
}
//----------------------------------------------------------------------------
/**
* Wait for the user to hit a key on the keyboard -- your application goes to
* sleep until a key is pressed and released on the keyboard.
*/
//
public void waitForKeyHit()
{
waitingForKeyHit = true;
synchronized(application)
{
try {
application.wait();
} catch ( InterruptedException e) {};
}
}
//----------------------------------------------------------------------------
/**
* Is the specified key down at this moment in time?
*
* @param whichKeyCode which key to check
*
* @return true if the specified key is down, false otherwise.
*/
public boolean keyIsDown(int whichKeyCode)
{
if (whichKeyCode == ANY_KEY)
{
// if any key is in the active buffer and down, then return true
for (int j=0; j<numberActiveKeys; j++)
if ( keyIsDown[j] )
return true;
}
else
{
int index = findKeyCodeIndexInList(whichKeyCode);
if (index >= 0) return keyIsDown[index];
}
return false;
}
//----------------------------------------------------------------------------
/**
* Has the specified key been hit (pressed and released)?
*
* @param whichKeyCode which key to check
*
* @return true if the specified key has been hit, false otherwise.
*/
public boolean keyHasBeenHit(int whichKeyCode)
{
if (whichKeyCode == ANY_KEY)
{
// if any key is in the active buffer and not down, then return true
for (int j=0; j<numberActiveKeys; j++)
if ( ! keyIsDown[j] )
{
removeActiveKey( j );
return true;
}
}
else // there is a specific key the user wants to query
{
int index = findKeyCodeIndexInList(whichKeyCode);
if (index >= 0)
{
if ( ! keyIsDown[index] &&
! keyHasBeenRetreivedByApplication[index] )
{
removeActiveKey( index );
return true;
}
}
}
return false;
}
//----------------------------------------------------------------------------
/**
* Wait for the user to press a key on the keyboard -- your application goes to
* sleep until a key is pressed.
*/
//
public void waitForKeyPressed()
{
waitingForKeyPressed = true;
synchronized(application)
{
try {
application.wait();
} catch ( InterruptedException e) {};
}
}
public int getKeyHitCode()
{
int returnKeyCode = NO_KEY_PRESSED; // assume there are no active keys
if (numberActiveKeys > 0)
{
// Debugging
//printActiveKeys("Start of getKeyHitCode: " );
//System.out.println("indexOfKeyToReturn = " + indexOfKeyToReturn);
// Make sure there is a valid index into the keys hit buffer
if (indexOfKeyToReturn < 0 ||
indexOfKeyToReturn >= numberActiveKeys)
indexOfKeyToReturn = 0;
// Only return the active key if it has been released and it has not been
// returned previously.
if (! keyIsDown[indexOfKeyToReturn] &&
! keyHasBeenRetreivedByApplication[indexOfKeyToReturn] )
{
returnKeyCode = activeKeys[indexOfKeyToReturn];
removeActiveKey( indexOfKeyToReturn );
if (indexOfKeyToReturn >= numberActiveKeys)
indexOfKeyToReturn = 0;
}
else
indexOfKeyToReturn = (indexOfKeyToReturn + 1) % numberActiveKeys;
}
return returnKeyCode;
}
//----------------------------------------------------------------------------
/**
* Return the number of keys that are currently being pressed on the keyboard.
* <p>If you call the getKeyCode() the number of times returned by this method,
* you will get the key codes for all keys currently being held down.
*/
public int numberOfKeysActive()
{
return numberActiveKeys;
}
//----------------------------------------------------------------------------
/**
* Return an integer representation of the color at a specified pixel location.
*/
public int getRGB(int x, int y)
{
try {
return image.getRGB(x,y);
}
catch(Exception e) {return 0;}
}
//----------------------------------------------------------------------------
/**
* Return an integer representation of the color at a specified pixel location.
*/
public void setRGB(int x, int y, int RGB)
{
try {
image.setRGB(x, y, RGB);
}
catch(Exception e) {}
}
//----------------------------------------------------------------------------
/*
* Save the current graphics (in the offscreen buffer) to a file. The file name
* should have an appropriate image file extension, such as ".bmp" or ".jpg"
*/
public void saveGraphics(String filename)
{
String extension = filename.substring(filename.lastIndexOf(".") + 1);
// write file
try
{
ImageIO.write(image, extension, new java.io.File(filename));
}
catch (java.io.IOException e)
{
System.err.println("Unable to save image: " + e);
}
}
//----------------------------------------------------------------------------
/*
* Load a bitmap image into memory.
*
* @param filename the name of the bitmap image file (including the .bmp extension)
*
* @return a BufferedImage object
*/
public BufferedImage loadBitmap(String filename)
{
// read file
try
{
BufferedImage image = ImageIO.read( new java.io.File(filename) );
return image;
}
catch (java.io.IOException e)
{
System.err.println("Unable to read bitmap image: " + e);
return null;
}
}
}
class MyCanvas extends Canvas
{
//-------------------------------------------------------------------
private DrawingPanel panel;
//-------------------------------------------------------------------
public MyCanvas(DrawingPanel thisPanel)
{
super();
panel = thisPanel;
}
//-------------------------------------------------------------------
public void paint(Graphics g)
{
panel.copyGraphicsToScreen();
}
}
sample output
This program allows you to search through the
datat from the Social Security Administration
to see how popular a particular name has been
Name:
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.