CMSC216 Project 1: C Programming
- Due: 11:59pm Mon 29-Sep-2025
- Approximately 4.0% of total grade
- Submit to Gradescope Submission will open soon
- Projects are individual work: no sharing of code between students is allowed. Seek help from course staff if you get stuck for too long.
CODE DISTRIBUTION: p1-code.zip
VIDEO OVERVIEW: https://umd.instructure.com/courses/1388320/pages/week03-videos
CHANGELOG:
- Sun Sep 21 03:38:18 PM EDT 2025
- Post 119 asked whether students
interested in the Makeup Credit should implement both the O(N2)
in
stock_set_best()
and the O(N) version instock_set_best_linear()
or something else. This is indeed the intent though the instructions were previously a bit unclear on this. Instructions in the project spec and documentation comments have been adjusted to make this more clear: for full credit implement both functions with their respective time complexities.
1 Introduction
Basic application programming in C is an essential step downward form higher-level environments (Java / Python / OCaml) towards the lower levels of computing. This project explores fundamental aspects of getting work done in C:
- Dynamic memory management with
malloc()/free()
- Reading data from files in text format
- Displaying information to the screen
- Reading commands from users in interactive programs
- Building data structures with C
structs
The assignment is divided into several problems utilizing many of the above techniques.
- Problem 1 implements a few simple function surrounding a
struct
representing stock prices. - Problem 2 builds on the previous routines with more complex functionality related to the stock data.
- Problem 3 completes the application by coding a
main()
function to employ the stock functions to plot from the command line. - Problem 4 adds several new functions and a separate
main()
which plots multiple stocks in the same space to aid with analysis.
Problems build on each other and should be done in order to complete the project.
Each problem contributes to an application that will read stock price data in a simple text format and display that information in a text terminal as a primitive graph. The application is divided between a set of service functions and two main functions which will call those functions to achieve the overall aims of the applications.
2 Download Code and Setup
Download the code pack linked at the top of the page. Unzip this which will create a project folder. Create new files in this folder. Ultimately you will re-zip this folder to submit it.
File | State | Notes |
---|---|---|
Makefile |
Provided | Build file to compile all programs |
gradescope-submit |
Provided | Allows submission to Gradescope from the command line |
stock_funcs.c |
CREATE | Problem 1/2/4 functions to write |
stock_main.c |
CREATE | Problem 3 main function to complete |
stock_multimain.c |
CREATE | Problem 4 main function to complete |
stock_demo.c |
Provided | Problem 1/2 demo code to show some function invocations |
stock.h |
Provided | Problem 1/2 header file |
TESTING | ||
testy |
Testing | Test running script |
test-results/ |
Testing | Directory in which temporary testing files are written |
test_stock_funcs.c |
Testing | Testing file for Problems 1 & 2 |
test_stock1.org |
Testing | Problem 1 tests |
… | ||
test_stock4.org |
Testing | Problem 4 tests |
data/stock-ascending.txt |
Data | Data files testing |
data/stock-valley.txt |
Data | |
data/stock-jagged.txt |
Data | |
… | Data |
2.1 Makefile
A Makefile
is provided as part of this project. Building programs in
C is a bit tedious and most folks use build systems of which make
is the oldest. The instructions and dependencies to create programs
are written in a Makefile
which is then interpreted by the make
program which will run gcc
and other commands to create programs.
Use this Makefile
by issuing commands like make prob1
>> make help Typical usage is: > make # build all programs > make clean # remove all compiled items > make zip # create a zip file for submission > make prob1 # built targets associated with problem 1 > make test # run all tests > make test-prob2 # run test for problem 2 > make test-prob2 testnum=5 # run problem 2 test #5 only > make submit # upload submission to Gradescope >> make prob2 # build problem 2 demo program gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c stock_funcs.c gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c stock_demo.c gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o stock_demo stock_demo.o stock_funcs.o >> make clean # remove all programs/binary object files rm -f stock_main stock_demo test_stock_funcs *.o >> make prob3 # build problem 3 main program gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c stock_main.c gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c stock_funcs.c gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o stock_main stock_main.o stock_funcs.o >> make clean # remove all programs/binary object files rm -f stock_main stock_demo test_stock_funcs *.o >> make # build all programs/objects for the assignment gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c stock_main.c gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c stock_funcs.c gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o stock_main stock_main.o stock_funcs.o gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c stock_demo.c gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o stock_demo stock_demo.o stock_funcs.o gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o test_stock_funcs test_stock_funcs.c stock_funcs.o
You are not required to understand all that is in the Makefile
(yet)
but it is a very useful tool well worth your time to learn.
2.2 Automated Tests
Automated tests are included with the code distribution. These tests are known to work on grace.umd.edu only but in most cases they should run identically in Linux environments. They may work on the Windows Subsystem for Linux but no guarantees are made. They very unlikely to run on MacOS natively as Linux-specific tools are used.
The provided Makefile
allows automated tests to be run via calls
like make test-prob1
to test Problem 1 and make test-prob2
to test
Problem 2. See the transcript below.
>> make test-prob1 # run tests for problem 1, compiles required code first gcc -Wall -Wno-comment -Werror -g -c stock_funcs.c gcc -Wall -Wno-comment -Werror -g -o test_stock_funcs test_stock_funcs.c stock_funcs.o ./testy test_stock1.org ============================================================ == test_stock1.org : Problem 1 First 3 Functions in stock_funcs.c == Running 15 / 15 tests 1) stock_new : ok 2) stock_free1 : ok 3) stock_free2 : ok 4) stock_free3 : ok 5) stock_free4 : ok 6) stock_print1 : ok 7) stock_print2 : ok 8) stock_print3 : ok 9) stock_print4 : ok 10) stock_print5 : ok 11) stock_print_prices_0 : ok 12) stock_print_prices_1 : ok 13) stock_print_prices_2 : ok 14) stock_print_prices_3 : ok 15) stock_print_final : ok ============================================================ RESULTS: 15 / 15 tests passed >> make test-prob2 # run tests for problem 2 gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c stock_funcs.c gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o test_stock_funcs test_stock_funcs.c stock_funcs.o gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c stock_demo.c gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o stock_demo stock_demo.o stock_funcs.o ./testy test_stock2.org ============================================================ == test_stock2.org : Problem 2 Remaining Functions in stock_funcs.c == Running 20 / 20 tests 1) stock_set_hilo1 : ok 2) stock_set_hilo2 : ok 3) stock_set_hilo3 : ok 4) stock_set_best1 : ok 5) stock_set_best2 : ok 6) stock_set_best3 : ok 7) stock_set_best4 : ok 8) count_lines : ok 9) stock_load1 : ok 10) stock_load2 : ok 11) stock_load3 : ok 12) stock_load pathological : ok 13) stock_plot1 : ok 14) stock_plot2 : ok 15) stock_plot3 : ok 16) stock_plot4 : ok 17) stock_plot5 : ok 18) stock_plot6 : ok 19) stock_plot7 : ok 20) stock_demo : ok ============================================================ RESULTS: 20 / 20 tests passed >> make test # run tests for all problems ...
Each problem describes specifically how tests can be run and how credit will be assigned.
Note that one can run a single test with the following make
invocation which sets testnum
.
>> make test-prob2 testnum=5
This is useful when debugging to limit the output and time it takes to check program results.
3 Outline of Code
The file stock_funcs.c
will contain most of the support functions
for the plotting program. An outline of these functions are presented
below. Note that each function has the Problem # to which it
belongs. The final main()
function should be written in the
stock_main.c
file.
You may add additional functions to those indicated ("helper" or "utility" functions) but these will not graded and the design of the project is oriented towards not needing additional functions.
// stock_funcs.c: support functions for the stock_main program. #include "stock.h" stock_t *stock_new(); // PROBLEM 1: Allocate a new stock struct and initialize its fields. // Integer fields like 'count' and 'lo_index' should be initialied to // -1. Pointer fields like 'prices' should be initialized to // NULL. The stock should be heap-allocated using malloc() and // returned. Since this is an allocation function, no use of 'free()' // should appear. void stock_free(stock_t *stock); // PROBLEM 1: Free a stock. Check the 'data_file' and 'prices' fields: // if they are non-NULL, then free them. Then free the pointer to // 'stock' itself. void stock_print(stock_t *stock); // PROBLEM 1: Prints data about a stock that is passed in via a // pointer. Uses the syntax ptr->field to access fields of the struct // pointed by 'stock'. Output follows the general convention: // // ==STOCK DATA== // data_file: data/stock-jagged.txt // count: 15 // prices: [103.00, 250.00, 133.00, ...] // lo_index: 8 // hi_index: 11 // best_buy: 8 // best_sell: 11 // profit: 232.00 // // Each line prints a field of the stock_t struct. In all cases, // floating point numbers are printed with 2 decimal digits of // accuracy. // // NULLS FOR FIELDS // The fields 'data_file' and 'prices' may be NULL in which case they // will be printed specially as in // // data_file: NULL // prices: NULL // // This requires a manual if/else check for NULL values for these // pointers. // // PRICES FIELD // When printing the 'prices' field, if the 'count' field is 0 to 3, // print the entire array as in // // prices: [] # count == 0 // prices: [70.00] # count == 1 // prices: [50.00, 90.00] # count == 2 // prices: [59.00, 45.00, 103.00] # count == 3 // // Otherwise, print the first 3 elements with a ... at the end as in // // prices: [10.00, 20.00, 30.00, ...] # count > 3 // // PROFIT // There is no 'profit' field in the struct. Instead, calculate the // profit as the difference between the price at the 'best_sell' index // and 'best_buy' index. If these indices are -1 indicating the best // buy/sell time is not known or not viable, print a proit of 0.0 void stock_set_hilo(stock_t *stock); // PROBLEM 2: Sets the index of 'lo_index' and 'hi_index' fields of // the stock to be the positions in 'prices' of the lowest and highest // values present in it. Uses a simple loop over the array 'prices' // which is 'count' elements long to examine each for high/low. If // 'count' is zero, makes no changes to 'lo_index' and 'hi_index'. int stock_set_best(stock_t *stock); // PROBLEM 2: Sets the 'best_buy' and 'best_sell' fields of 'stock'. // This corresponds to the pair which produces the best profit. On // determining the best buy/sell indices which produce a positive // profit, sets these fields in 'stock' and returns 0. If there is no // buy/sell point which would result in a positive profit, sets the // 'best_buy' and 'best_sell' indices to -1 and returns -1. Always // calculates the earliest buy/sell pair of indices that would get the // best profit: if 5,8 and 5,9 and 7,10 all give the same, maximal // profit, the best buy/sell indices are set to 5,8. // // ALGORITHM NOTES // One intuitive algorithm to compute this is to try every possible // buy index (outer loop) and every possible sell index after it // (inner loop) looking for the maximum profit produced in it. This is // a O(N^2) algorithm with N=count. Using this algorithm is a good // start. // // Some MAKEUP CREDIT will be awarded for implementing a more // efficient, O(N) algorithm but THIS FUNCTION MUST IMPLEMENT THE // O(N^2) ALGORITHM to earn credit. Implement the O(N) version // elsewhere for Makeup Credit. int count_lines(char *filename); // PROBLEM 2: Opens file named 'filename' and counts how many times // the '\n' newline character appears in it which corresponds to how // many lines of text are in it. Makes use of either fscanf() with // the %c format to read single characters or alternative I/O // functions like fgetc(). Closes the file before returning a count of // how many lines are it it. If for any reason the file cannot be // opened, prints a message like // // Could not open file '<FILENAME>', cannot count lines // // with <FILENAME> substituted for the name of the file. Returns -1 to // indicate failure in this case. int stock_load(stock_t *stock, char *filename); // PROBLEM 2: Loads a stock from file 'filename' into 'stock' filling // its 'prices' and 'count' fields in. Makes use of the count_lines() // function to determine how many lines are in the file which will // dictate 'count' and the length of 'prices'. Allocates space in the // heap for the stock's 'prices' array, opens the 'filename' and // iterates through reading prices into the array. The data format for // prices files is // // time_03 133.00 // time_04 143.00 // time_05 168.00 // time_06 91.00 // // where each line has a time as as single string and a price which is // floating point number. The times can be ignored which can be // accomplished with a fscanf() call with format "%*s" to read a // string but ignore/discard it. // // Assigns the 'datafile' field to be a duplicated string of // 'filename' for which 'strdup()' is extremely useful. This string // must be free()'d later likely in 'stock_free()' // // On successfully loading the stock, returns 0. // // If 'filename' cannot be opened, prints the message // // Unable to open stock file '<FILENAME>', cannot load stock data // // with '<FILENAME>' substituted in for the name of the stock and // returns -1. void stock_plot(stock_t *stock, int max_height, int start, int stop); // PROBLEM 2: Plots a graphical representation of stock // information. First calculates and prints plot which is in the // following format: // // ==PLOT DATA== // start/stop: 0 15 // max_height: 14 // price range: 309.00 // plot step: 22.07 // +--B=S----------+ // 300.93 | H * | // 278.86 | * H * | // 256.79 | * H * | // 234.71 | * H * | // 212.64 |** H * | // 190.57 |** H * * | // 168.50 |** H** * * *| // 146.43 |** H** * ****| // 124.36 |** H**** ****| // 102.29 |** H**** ****| // 80.21 |** *H***** ****| // 58.14 |** *H***** ****| // 36.07 |** *H***** ****| // 14.00 |****H*****L****| // +^----^----^----+ // 0 5 10 // // Here brief notes on the format with more detail in the project // specification. // - Parameters start, stop, and max_height are printed first along with // calculated information like the price_range (uses hi_index and // lo_index) // - The main body of the plot is exactly max_height characters high. The // lowest line is the price at lo_index; the highest is hi_index minus // plot_step // - The vertical axis prices can be printed with the format specifier // %10.2f to give the correct leading whitespace with 2 digits of // accuracy // - If the hi and lo prices appear in the range, their bar is printed // with an H or an L while all other stocks are printed with a * // - The top axis may include a B and an S at the positions of the // best_buy and best_sell index with a = between these to show the // period of ownership. // - The bottom axis shows tic marks as ^ and below them index labels at // values divisible by 5. For the numeric index labels, it is easiest // to print spaces to a value divisible by 5 then use the format // specifier %-5d to print integers: this aligns left and prints // whitespace padding on the right to width 5. In a loop, one can // advance by +5 each time a label is printed. int main(int argc, char *argv[]); // PROBLEM 3: main() in stock_main.c // // The program must support three command line forms. // 1. ./stock_main <stockfile> <max_height> // 2. ./stock_main <stockfile> <max_height> <start> // 3. ./stock_main <stockfile> <max_height> <start> <stop> // // All forms have a the stock file as the first command line argument // and the maximum plotting height as the 2nd argument. Forms 2 and 3 // optionally specify a starting time index (inclusive) and stopping // time index (exclusive) to plot a time range for the stock prices. // // NOTES // - It is a good idea to check that the number of command line // arguments is either 3 (Form 1) or 4 (Form 2) or 5 (form 3) if // not, bail out from the program. There may or may not be an // automated test that checks that no action is taken when an // incorrect number of command line arguments appear but there will // definitely be tests for each of the forms indicated so a logical // and clean structure that attends to them is a good idea. // - All command line arguments come into C programs as strings // (char*). That means the numbers on the command line like // `max_height` will also be a string of characters and need to be // converted to an int to be used in the program. The atoi() // function is useful for this: search for documentation on it and // use it for the conversion. // - Make sure to check that loading stock data from a file // succeeds. If not, print the following error message: // // Failed to load stock file, exiting // // This message is in addition to the error message that is printed // by the stock_load() function. Return 1 from main() to indicate // that the program was not successful. // - If stock data is successfully loaded, perform the requested // plotting using appropriate functions. Don't forget to free // heap-allocated memory before ending the program. // // NOTE: a second main() function is required for Problem 4 in a // different file. stock_t **stock_load_all(char *filenames[], int stock_count); // PROBLEM 4: Loads all files named in `filenames[]` as stocks and // returns an array of pointers to those stocks. The length of the // `filenames[]` array is given in `stock_count`. Uses `stock_new() / // stock_load()` to create and load individual stocks. If any stock // fails to load, prints an error message // // Failed to load stock file <I> // // with <I> substituted for the index of the problematic file, then // de-allocates all memory that has so far been allocated and returns // NULL to indicate failures. // max stocks supported for mult-stock printing in Problem 4 #define STOCKS_MAX_COUNT 26 // characters to use for plotting stocks char stock_chars[STOCKS_MAX_COUNT+1] ="abcdefghijklmnopqrstuvwxyz"; void stock_multiplot(stock_t **stocks, int stocks_count, int max_height); // // // PROBLEM 4: Plot all of the stocks in the `stocks` array which is // `stocks_count` elements long. // // EXAMPLE PLOT: // // ==MULTIPLOT DATA== // max_height: 10 // stop_count: 15 // min price: 38.00 // max price: 270.00 // price range: 232.00 // plot step: 23.20 // ==LEGEND== // a : data/stock-valley.txt // b : data/stock-jagged.txt // // +------------------------------+ // 246.80 | b b B | // 223.60 | b b b B | // 200.40 | b b b B | // 177.20 | b b b B | // 154.00 | b b b b B | // 130.80 | b b b b b b B | // 107.60 | b b b b b b B | // 84.40 |abab b b b b b a abAB b| // 61.20 |abababab b b ba a a abAB b b| // 38.00 |abababababAbababaBababAB b b b| // +^---------^---------^---------+ // 0 5 10 // // DETAILS // // Plots start with the ==MULTIPLOT DATA== shown above which shows // basic plotting parameters. The stop index, minimum price, and // maximum price are set by finding the maximum count and min/max // price among all stocks in the array. // // The ==LEGEND== shows the character which will be printed associated // with each stock file. These are drawn from the provided // `stock_chars[]` array with the 0th stock printed using the 0th // character, 1th stock the 1th character, and so on. For each stock, // the columns of the low and high prices for the stock are printed as // upper case characters using the library `toupper()` conversion // function to make it easier to identify the extrema for stocks. // // When plotting, each stock is printed in its own column. If there // are 2 stocks, then each "time" index will have two columns as shown // above. If there are 8 stocks, then each time index will have 8 // columns. The plotting area will be stocks_count*stop_count // characters wide. If a stock has a lower count (shorter prices // array) than stop_count, then blanks are printed in the columns // beyond its count (true for the `a` stock above in the last few // columns). // // If `stocks_count` is larger than `STOCKS_MAX_COUNT`, prints message // ERROR: stocks array length <COUNT> exceeds max <MAX>\n // with <COUNT> and <MAX> substituted and immediately returns. // // NOTES // - The `stocks` variable is an array of pointers; that means the // syntax `stocks[i]->field` accesses some field for the ith // stock. Adapt this syntax to the various cases of accessing data // for an individual stock. // - Unlike the single plotting, plot parameters like the stop_count and // min/max price must be determined by analyzing all stocks. Use a // loop to iterate through to find the extreme values among stocks. // - The plotting procedure itself will be similar to `stock_plot()` // except for an additional layer of looping: for time index <i>, // loop through all stocks and print their characters. Copying in // code from `stock_plot()` is a good idea and then making // adjustments is likely to yield good results. // - Multiplots always start at time index 0 and go to the time index // of the stock with the largest prices[] count. // - When plotting a stock, check that it has a count that is above // the current time index so that it will have data in its prices[] // array. Not all stocks in a multiplot will have the same count of // prices so it is possible to go out of bounds in arrays if one is // not careful. // - The axis are wider: stop_count*stock_count characters wide. Use // this in some loops to print boundaries. // - The axis label ticks are tricky. TRY USING PRINTF() by printing // the numeric label in a field of 5 characters wide, then print // additional 5-wide characters based stock_count to line up the // next location that needs a numeric label. // - The function assumes that the high and low price indices have // been set for all stocks prior to them being passed in. A main() // is a good spot to do this. Since the best buy/sell points do not // show in this function, these fields can remain uninitialized not // affect the function's behavior. int main(int argc, char *argv[]); // PROBLEM 4: main() function in stock_multimain.c: Load multiple // stock files and plot them together. // // The program must support an arbitrary number of command line // arguments according to the pattern: // 1. ./stock_multimain <max_height> <stockfile1> // 2. ./stock_multimain <max_height> <stockfile1> <stockfile2> // 3. ./stock_multimain <max_height> <stockfile1> <stockfile2> <stockfile3> // 4. etc. // The first parameter is the maximum height to plot which remaining // arguments as filenames containing the stock data to use in the // multiplot. // // `stock_load_all()` is used to load the stock files. If something goes // wrong, prints the error message: // Problems loading all stock files, exiting // and exit with return code 1. // // The high/low prices are set for each stock using an appropriate // service function and the stocks are plotted using // `stock_multiplot()` // // SAMPLE OUTPUT OF STOCK_MULTIMAIN // >> ./stock_multimain 10 data/s01.txt data/s02.txt data/s03.txt // ==MULTIPLOT DATA== // max_height: 10 // stop_count: 10 // min price: 1.00 // max price: 99.00 // price range: 98.00 // plot step: 9.80 // ==LEGEND== // a : data/s01.txt // b : data/s02.txt // c : data/s03.txt // // +------------------------------+ // 89.20 | B | // 79.40 | B bC Abc c| // 69.60 | B bC Abc c| // 59.80 | c B bC a c Abc bc| // 50.00 | c B bC a c b Abc bc| // 40.20 |a c B bCa ca c b Abc bc| // 30.40 |a caB abCabca c b Abc bc| // 20.60 |a caB abCabcabca b b Abc bc| // 10.80 |abcaB abCabcabcaBc b ab Abc bc| // 1.00 |abcaBcabCabcabcaBcAbCabcAbcabc| // +^--------------^--------------+ // 0 5 // // NOTES // - If fewer than 1 stock file is provided, print a usage // message. This case won't be tested but is instructive. // - It is handy to use pointer arithmetic to locate where in argv[] // the names of stock files begin so that this portion of the array // can be passed directly to `stock_load_all()` and spare one the // need of copying strings. // - Typically the function will load all stock files required, then // loop through and initialize their high/low price indices with an // appropriate function and then call the multi-plot function to // show them. There is no need to set the best buy/sell indices in // this application. // - The normal `stock_set_best()` function MUST be present with // Quadratic complexity (or worse) // - The linear time version must also be present // - The linear time version MUST be used in `main()` and pass test cases // associated with running the whole program. int stock_set_best_linear(stock_t *stock); // // OPTIONAL MAKEUP CREDIT: Identical to stock_set_best() except uses a // linear time algorithm to determine the best buy/sell times
3.1 Getting Started
Create the file stock_funcs.c
. Copy the code outline above into
the C file to serve as a starting point for the project. Filling in
dummy bodies (e.g. return 0
or return NULL
etc.) will allow the
Automated Tests to run after which one can begin filling in
definitions for the functions according to the provided specification.
For the most part, try to solve functions in the order that they appear in the code outline as later functions will use earlier functions. If you do get stuck on a function, don't be afraid to move on momentarily to keep your momentum up.
3.2 The Dev Cycle
To complete the project
- Read documentation on a required function
- Write part of the implementation
- Run test cases associated with function
- Analyze the results
- Consult docs on the function for clarification
- Add
printf()
statements to show debug information - Repeat the above until all tests for the function pass
- Consult the MANUAL INSPECTION criteria to ensure your style and functionality meet their requirements
- Repeat for the next function
When stuck on a bug for an Extended period (1+ hour)
- Search for related examples from Labs / HWs / Lectures
- Ask for help on Piazza or visit office hours
- Take a break and come back after recovering some mental energy
- Stay Determined!
4 Problem 1: Stock Plotting
4.1 Overview and Demo
Problems 1 and 2 create a small plotting application that is focused on stock prices. In stock trading, the idea is to buy a stock when it is priced low and sell it at a later time point when the price is high which will net the profit of the difference between each. Of course, this must be done by predicting when prices are at their highest and lowest points and some insight can be garnered from examining historical data for stock prices. The application you will build allows for easy analysis of a simple data file containing times/prices for stocks and display of their information in simple text plots. At the end of problems 1 and 2, you will have an application which produces the following kind of output.
>> make stock_main # compile the stock_main program (after completing problems 1&2) gcc -Wall -Wno-comment -Werror -g -c stock_main.c gcc -Wall -Wno-comment -Werror -g -c stock_funcs.c gcc -Wall -Wno-comment -Werror -g -o stock_main stock_main.o stock_funcs.o >> ./stock_main data/stock-ascending.txt 10 # plot the data in a stock-ascending data file ==STOCK DATA== data_file: data/stock-ascending.txt count: 10 prices: [10.00, 20.00, 30.00, ...] # shows the first few stock prices lo_index: 0 # calculates index of low/high price hi_index: 9 best_buy: 0 # calculates optimal buy/sell time best_sell: 9 profit: 90.00 ==PLOT DATA== start/stop: 0 10 max_height: 10 # 10 specifed on command line for bar height price range: 90.00 # highest_price - lowest_price plot step: 9.00 # range / max_height +B========S+ # top axis shows Buy/Sell range 91.00 | H| # main plot body shows bars for stock price 82.00 | *H| # at each time step 73.00 | **H| 64.00 | ***H| # normal prices plotted with a * 55.00 | ****H| # Lowest and Highest prices plotted with 46.00 | *****H| # an L and H respectively 37.00 | ******H| 28.00 | *******H| # bars are between 1 and max_height chars 19.00 | ********H| # tall 10.00 |L********H| +^----^----+ # bottom axis has tick marks and labels 0 5 # at time indexes divisible by 5 >> ./stock_main data/stock-ascending.txt 5 3 10 ==STOCK DATA== # same file but make bars at most 5 high data_file: data/stock-ascending.txt # and limit output to time index 3 to 9 count: 10 prices: [10.00, 20.00, 30.00, ...] # same info on stock file lo_index: 0 hi_index: 9 best_buy: 0 best_sell: 9 profit: 90.00 ==PLOT DATA== start/stop: 3 10 # plot data has adjusted start/stop index max_height: 5 # different max_height price range: 90.00 plot step: 18.00 # plot step as seen on left axis labels is +======S+ # different due to the different max_height 82.00 | *H| 64.00 | ***H| 46.00 | *****H| 28.00 |******H| 10.00 |******H| +--^----+ # bottom axis indicates starting time of 3 5 # rather than 0, tics and labels adjusted >> ./stock_main data/stock-valley.txt 12 # plot a "valley" shaped stock pricing ==STOCK DATA== data_file: data/stock-valley.txt count: 12 prices: [100.00, 90.00, 80.00, ...] lo_index: 5 hi_index: 11 best_buy: 5 # best to buy in the middle at lowest point best_sell: 11 # and sell at the end at highest price profit: 55.00 ==PLOT DATA== start/stop: 0 12 max_height: 12 price range: 55.00 plot step: 4.58 +-----B=====S+ # top axis shows - when stock is not 100.42 | H| # purchased, B at the best_buy index 95.83 |* H| # and S at the best_sell index with 91.25 |* *H| # = symbols between B and S 86.67 |** *H| 82.08 |** **H| 77.50 |*** **H| 72.92 |*** ***H| 68.33 |**** ***H| 63.75 |**** ****H| 59.17 |***** ****H| 54.58 |***** *****H| 50.00 |*****L*****H| +^----^----^-+ 0 5 10 >> ./stock_main data/stock-jagged.txt 15 # plot a jagged stock price file ==STOCK DATA== # where prices bound around a lot data_file: data/stock-jagged.txt count: 15 prices: [103.00, 250.00, 133.00, ...] lo_index: 8 hi_index: 11 best_buy: 8 best_sell: 11 profit: 232.00 ==PLOT DATA== start/stop: 0 15 max_height: 15 price range: 232.00 plot step: 15.47 +--------B==S---+ # Buy to Sell range is shorter in 254.53 | H | # this case and does not align with 239.07 | * *H | # either the first or last time index 223.60 | * * *H | 208.13 | * * *H | 192.67 | * * *H | 177.20 | * * *H | 161.73 | * * * *H | 146.27 | * * * *H | 130.80 | **** * *H | 115.33 | **** * *H | 99.87 |***** * *H *| 84.40 |******* *H *| 68.93 |******* *H **| 53.47 |******** *H***| 38.00 |********L**H***| +^----^----^----+ 0 5 10 >> ./stock_main data/stock-min-after-max.txt 10 ==STOCK DATA== # file with buy/sell different than data_file: data/stock-min-after-max.txt # hi/lo index count: 15 prices: [223.00, 292.00, 27.00, ...] lo_index: 10 # minimum price appear after hi_index: 4 # the maximum price best_buy: 2 # finding the optimal buy/sell best_sell: 4 # point is harder in this case profit: 296.00 ==PLOT DATA== start/stop: 0 15 max_height: 10 price range: 309.00 plot step: 30.90 +--B=S----------+ # best_buy index is not at the 292.10 | H * | # low price because the high 261.20 | * H * | # price occurs before the low 230.30 | * H * | # price 199.40 |** H * * | 168.50 |** H** * * *| 137.60 |** H**** ****| 106.70 |** H**** ****| 75.80 |** *H***** ****| 44.90 |** *H***** ****| 14.00 |****H*****L****| +^----^----^----+ 0 5 10 >> ./stock_main data/stock-descending.txt 9 # prices descend No viable buy/sell point # printed when trying to find a ==STOCK DATA== # buy/sell point data_file: data/stock-descending.txt count: 10 prices: [100.00, 90.00, 80.00, ...] lo_index: 9 hi_index: 0 best_buy: -1 # left as -1 to indicate no best_sell: -1 # good time to buy/sell was found profit: 0.00 ==PLOT DATA== start/stop: 0 10 max_height: 9 price range: 90.00 plot step: 10.00 +----------+ 90.00 |H* | # prices descend over the 80.00 |H** | # entire period meaning NOT 70.00 |H*** | # investing is the best option 60.00 |H**** | 50.00 |H***** | 40.00 |H****** | 30.00 |H******* | 20.00 |H******** | 10.00 |H********L| +^----^----+ 0 5 >> ./stock_main data/stock-FB-08-02-216.txt 15 87 142 ==STOCK DATA== # plot values for Facebook's stock data_file: data/stock-FB-08-02-216.txt # on a day when prices were gradually count: 543 # falling. Shows a slice of time from prices: [358.94, 358.50, 358.50, ...] # this large file which includes the lo_index: 470 # best Buy/Sell time indices hi_index: 15 best_buy: 109 best_sell: 129 profit: 2.38 ==PLOT DATA== start/stop: 87 142 max_height: 15 price range: 8.00 plot step: 0.53 +----------------------B===================S------------+ 358.46 | | 357.92 |** | 357.39 |**** | 356.86 |****** | 356.32 |******* *** * | 355.79 |************* | 355.25 |***************** | 354.72 |****************** ****** ** | 354.19 |****************** * * ***** *************** | 353.65 |******************** ************ ******************| 353.12 |********************** ********************************| 352.59 |*******************************************************| 352.05 |*******************************************************| 351.52 |*******************************************************| 350.99 |*******************************************************| +---^----^----^----^----^----^----^----^----^----^----^-+ 90 95 100 105 110 115 120 125 130 135 140
4.2 Editing and Testing
The project code contains a skeleton version of stock_funcs.c
which
you should fill in with definitions. With this skeleton version, you
can immediately start testing your code by typing make test-prob1
.
Without changes, you will get failures for all tests as in
>> make test-prob1 gcc -Wall -Wno-comment -Werror -g -o test_stock_funcs test_stock_funcs.c stock_funcs.o ./testy test_stock1.org ============================================================ == test_stock1.org : Problem 1 First 3 Functions in stock_funcs.c == Running 15 / 15 tests 1) stock_new : FAIL -> results in file 'test-results/prob1-01-result.tmp' 2) stock_free1 : FAIL -> results in file 'test-results/prob1-02-result.tmp' 3) stock_free2 : FAIL -> results in file 'test-results/prob1-03-result.tmp' ... ============================================================ RESULTS: 0 / 15 tests passed
However, the ability to run tests on your code an see progress is extremely important. Your first goal when starting a new project should be to see some results or running the program which is much easier if some benevolent dictator has provided a bunch of tests.
You can also use the invocation
> make test-prob1 testnum=5
to run a single test and see its results in the terminal.
Failed tests generate results files which can be viewed in any text
editor or in the terminal. The cat
command can show such results in
the terminal via cat test-results/some-file.txt
.
Below the results for the first test are shown and from the comparison
and Valgrind report, it appears that there is some sort of Memory
problem with the stock_new()
function.
>> cat test-results/prob1-01-result.tmp * (TEST 1) stock_new COMMENTS: ** program: ./test_stock_funcs stock_new ** --- Failure messages --- - FAILURE(139) due to SIGSEGV (segmentation fault) from OS - FAILURE: Output Mismatch at lines marked ** --- Side by Side Differences --- - Expect output in: test-results/raw/prob1-01-expect.tmp - Actual output in: test-results/raw/prob1-01-actual.tmp - Differing lines have a character like '|' '>' or '<' in the middle #+BEGIN_SRC sbs-diff ==== EXPECT ==== ==== ACTUAL ==== { { // Tests stock_new() function and whether it initializes fields // Tests stock_new() function and whether it initializes fields // correctly before returning a stock. // correctly before returning a stock. stock_t *stock = stock_new(); // call function to allocate/init stock_t *stock = stock_new(); // call function to allocate/init printf("stock->data_file: %p\n" , stock->data_file); printf("stock->data_file: %p\n" , stock->data_file); printf("stock->count: %d\n" , stock->count); printf("stock->count: %d\n" , stock->count); printf("stock->prices: %p\n" , stock->prices); printf("stock->prices: %p\n" , stock->prices); printf("stock->lo_index: %d\n" , stock->lo_index); printf("stock->lo_index: %d\n" , stock->lo_index); printf("stock->hi_index: %d\n" , stock->hi_index); printf("stock->hi_index: %d\n" , stock->hi_index); printf("stock->best_buy: %d\n" , stock->best_buy); printf("stock->best_buy: %d\n" , stock->best_buy); printf("stock->best_sell: %d\n" , stock->best_sell); printf("stock->best_sell: %d\n" , stock->best_sell); free(stock); // de-allocate manually free(stock); // de-allocate manually } } stock->data_file: (nil) | Segmentation Fault stock->count: -1 < stock->prices: (nil) < stock->lo_index: -1 < stock->hi_index: -1 < stock->best_buy: -1 < stock->best_sell: -1 < #+END_SRC ** --- Line Differences --- EXPECT: 17) stock->data_file: (nil) EXPECT: 18) stock->count: -1 EXPECT: 19) stock->prices: (nil) EXPECT: 20) stock->lo_index: -1 EXPECT: 21) stock->hi_index: -1 EXPECT: 22) stock->best_buy: -1 EXPECT: 23) stock->best_sell: -1 ACTUAL: 17) Segmentation Fault --- Valgrind Log from: test-results/raw/prob1-01-valgrd.tmp --- ==150464== Memcheck, a memory error detector ==150464== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==150464== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info ==150464== Command: ./test_stock_funcs stock_new ==150464== Parent PID: 150463 ==150464== ==150464== Invalid read of size 8 ==150464== at 0x109354: main (test_stock_funcs.c:33) ==150464== Address 0x0 is not stack'd, malloc'd or (recently) free'd ==150464== ==150464== ==150464== Process terminating with default action of signal 11 (SIGSEGV): dumping core ==150464== Access not within mapped region at address 0x0 ==150464== at 0x109354: main (test_stock_funcs.c:33) ==150464== If you believe this happened as a result of a stack ==150464== overflow in your program's main thread (unlikely but ==150464== possible), you can try to increase the size of the ==150464== main thread stack using the --main-stacksize= flag. ==150464== The main thread stack size used in this run was 10022912. ==150464== ==150464== HEAP SUMMARY: ==150464== in use at exit: 0 bytes in 0 blocks ==150464== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==150464== ==150464== All heap blocks were freed -- no leaks are possible ==150464== ==150464== For lists of detected and suppressed errors, rerun with: -s ==150464== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
4.3 Implementation Notes
Use the Arrow
The first 3 functions to implement in stock_funcs.c
are utility
functions for stocks to allocate, de-allocate and print them. As will
be covered in lecture, C often deals with pointers to a struct
and
that is the case in these functions. For this, the sp->fld
syntax is
used to first dereference the struct
pointer and access a field of
the struct
. For example, in stock_print()
one could use the
following code to print the count
field of the parameter struct
:
void print_stock(stock_t *stock){ // ... printf("count: %d\n", stock->count); // ^^^^^^^^^^^^
Throughout most of this problem and the next, the Arrow syntax will be used in most functions.
Allocating Stocks in Heap Memory
In order to get dynamically allocated memory in the Heap, the
malloc()
function should be used. This function takes a number of
bytes and returns a void *
pointer for that number of bytes if
available. As will be discussed in lecture, C provides the sizeof()
operator to calculate the size of various intrinsic and user-defined
types. To allocate enough space for a stock_t
, use
malloc(sizeof(stock_t))
.
Though malloc()
returns a void *
, it is typical to simply assign
it to a pointer of some other kind. For example, the following is a
typical way to allocate space for a single stock_t
struct
and save
the reference for later use.
stock_t *stock = malloc(sizeof(stock_t));
Once allocated, the Arrow syntax mentioned above, stock->lo_index
can be used to adjust fields.
To understand which fields are present in the stock_t
struct, open
up the header file stock.h
which declares the struct. There will be
a list of fields like prices
and hi_index
: these will all need to
receive initial values like -1
or NULL
. Don't alter anything in
the header as this may break tests that you'll run later.
Avoid Freeing NULL
While the free()
function is used to de-allocate memory that has
been obtained via malloc()
, it should never be called with a NULL
pointer. In the stock_free()
function, one should check the
data_file
and prices
fields and free them only if they are not
NULL
. The following code demonstrates how this can be done for one
of those fields.
void stock_free(stock_t *stock){ // ... if(stock->data_file != NULL){ free(stock->data_file); } // ...
Printing Prices Array
During stock_print()
, some special code will be needed to print the
prices[]
array if it has 3 or fewer elements. As per the function
documentation, the line associated with prices will like one of the
following depending on the value for count
:
prices: [] # count == 0 prices: [70.00] # count == 1 prices: [50.00, 90.00] # count == 2 prices: [59.00, 45.00, 103.00] # count == 3 prices: [10.00, 20.00, 30.00, ...] # count > 3
There are a couple tricks for producing this type of output and it may take some care to get the output formatted correctly for all situations. You might try either of the following:
- Brute force: establish 5 cases for each of the formats above with
separate
printf()
call for each. Since the number of cases is relatively small, this should not be unmanageable. - Use the
count==0
as a special case and then write a carefully crafted loop with conditionals to handle the remaining cases. This can get tricky but is more flexible if for later one wants to see the first 5 numbers instead.
Either approach is acceptable in this case.
Keep in mind that though prices
is declared as a double *prices
(pointer to double
data), it is fine to use array syntax like square
brace indexing with it. Syntax like stock->prices[2]
will follow a
pointer to a stock_t struct
then find the prices
field and index
into it as an array.
4.4 Grading Criteria for Problem 1 grading 25
The following criteria will be checked.
Weight | Criteria |
---|---|
AUTOMATED TESTS via make test-prob1 |
|
15 | Runs tests in test_stock1.org on the first 3 functions in stock_funcs.c |
Runs all code under Valgrind to ensure that no memory errors are present. | |
1 point per test passed | |
MANUAL INSPECTION | |
5 | Code Style: Functions adhere to CMSC 216 C Coding Style Guide which dictates |
reasonable indentation, commenting, consistency of curly usage, etc. | |
2 | stock_new() and stock_free() |
Appropriate use of malloc() and sizeof() to get heap memory |
|
Correct initialization of all fields to -1 or NULL |
|
Appropriate use of the Arrow syntax for field access | |
Checks for data_file and prices fields to avoid calling free() on a NULL pointer |
|
Appropriate use of the Arrow syntax for field access | |
3 | stock_print() |
Appropriate use of the Arrow syntax for field access | |
Clear logic which deals with printing prices[] array with 3 or fewer elements specially |
|
Clear section to calculate Profit from best_buy/best_sell or print 0.0 if these are -1 |
5 Problem 2: Complete Stock Plot Functions
5.1 Overview
This problem implements further functions required by the Stock
Plotting application. These functions are somewhat more complex and
utilize functions and techniques that are part of Problem 1. When this
problem is complete, all functions in stock_funcs.c
will be working
which will allow the provided stock_demo.c
program to be compiled
and run on the command line. These functions will then be used in the
next problem to complete stock_main.c
.
The remainder of this section gives some notes and implementation hints on how to handle the various functions.
5.2 Setting Low/High Index
void stock_set_hilo(stock_t *stock)
This function should be implemented as a simple "scan" across the
prices[]
array. Each step will check an element of prices[]
and
adjust a running min/max. Make use of similar syntax here as you used
in stock_print()
in order to access the elements of prices
and
subsequently set the lo_index
and hi_index
fields.
5.3 Best Buy/Sell Index
int stock_set_best(stock_t *stock);
In many cases it is best to buy at a minimum price and sell at a
maximum price. This approach fails if the maximum price occurs
before the minimum price. The job of stock_set_best()
is to run an
algorithm to locate the best possible buy/sell pair and set the
best_buy / best_sell
fields of a stock. As the documentation for the
function indicates, one can use a simple "brute force" approach to
this by simply trying every buy index paired with every sell index
(which must occur later in the array than the buy index). This
approach leads to the following pattern of iteration through the
prices
array which uses a doubly nested pair of loops.
set best_buy and best_sell to -1 set best_profit to 0.0 for every buy index I in prices for every sell index J later than I in prices if prices[J] - prices[I] > best_profit set best_buy,best_sell, to I,J set best_profit to prices[J] - prices[I] done done
Start by adapting this approach in the function and make sure it works. Then move on to solve other problems here.
For those that want a bit more fun, revisit this function later to implement a more efficient version of it as described in the Optional MAKEUP CREDIT Section.
5.4 Stock Files
Data on stock prices over time are stored in several example files in
the data/
directory such as data/stock-ascending.txt
and
data/stock-jagged.txt
.
>> ls data/stock-* # list all the sample stock data data/stock-1only.txt data/stock-empty.txt data/stock-TSLA-08-02-216.txt data/stock-2only.txt data/stock-FB-08-02-216.txt data/stock-TSLA-08-12-216.txt data/stock-3only.txt data/stock-GOOG-08-02-216.txt data/stock-valley.txt data/stock-ascending.txt data/stock-jagged.txt data/stock-descending.txt data/stock-min-after-max.txt
Each of has a simple data format which looks like the following.
>> cat data/stock-jagged.txt time_01 103.00 time_02 250.00 time_03 133.00 time_04 143.00 time_05 168.00 time_06 91.00 time_07 234.00 time_08 59.00 time_09 38.00 time_10 45.00 time_11 254.00 time_12 270.00 time_13 59.00 time_14 72.00 time_15 107.00
- Each time/price pair appears on its own line so that the number of lines in the file is the number of time/price pairs
- The first item on each line is a time at which a price appears: this will be ignored.
- The second item that appears on each line is a floating point number which is the stock price at that time.
5.5 Counting Lines
Since the number of lines in stock files corresponded to the number of prices, it is useful to have a utility function to count lines in a file which is required.
int count_lines(char *filename)
As the documentation for the function indicates, this function will
open the named file and count the lines in it. This can be done most
easily via treating the file as a long array of characters and looking
for the special \n
newline character which designates a line break.
Character comparisons are done in C like other equality comparisons
via syntax like
if(somevar == '\n'){ ... } if(somevar == 'X'){ ... }
To read a single character from a file, a common approach is to use
the fscanf()
function with the %c
format specifier as in
char c; int ret = fscanf(fin, "%c", &c);
Keep in mind that the fscanf()
function will fail to read a
character when it reaches the end of a file. In these cases it will
return the special return value EOF
which should be checked. A
common structure is
int ret = fscanf(fin, ..., ...); if(ret == EOF){ // take action for end of file, like breaking out of a loop // or returning } else{ // fscanf() completed normally so variables should contain // new values }
Experiment with these elements to complete the count_lines()
function.
Keep in mind that count_lines()
should fail gracefully if a file
cannot be opened. In such cases, the fopen()
function returns
NULL
. Make sure to check for this, print an error message as
indicated in the function documentation, and return -1 from the
function to indicate a failure has occurred.
5.6 Loading Stock Files
int stock_load(stock_t *stock, char *filename)
This function takes an existing stock
and fills in its price
and
count
fields based on the contents of the given file. An example:
stock_t *stock = stock_new(); // all fields of stock have default values like -1 and NULL int ret = stock_load(stock, "data/stock-ascending.txt"); // Specified data file as 10 lines so // stock->count is now 10 // stock->prices is an array of 10 values read from the file // If the load failed, -1 would be returned
Make use of the count_lines()
function to determine how many lines
are in the stock file. This will allow you to immediately allocate
enough space for a double
array for all of the prices in the file.
You will need to skip the first string on each line in prices files:
time_03 133.00 time_04 143.00 | | | +---> Read these into prices +---> Skip these times
An easy way to accomplish this is to use the C format modifier *
which reads a field but does not place it in memory anywhere. For
example:
fscanf(fin, "%*s");
would read a string but ignore it thus one does not have to provide a memory address to store the string.
One requirement of stock_load()
is to set the data_file
field to
be a copy of the filename
parameter. While this can be done
manually with malloc()
and a loop, the strdup()
function makes
this easy and is encouraged here. Look up documentation for it either
in the Unix man pages (type man strdup
in a terminal) or via an
internet search.
Ensure that at the end of stock_load()
the data_file
, prices
,
and count
fields have been set according to the data from the
file. Do not modify any other fields in the stock.
5.7 Plotting Stocks
By far the trickiest function is the stock plot function which
presents a dense set of information in a compact region. Its output
will follow the below pattern which shows output for
stock-min-after-max.txt
.
==PLOT DATA== start/stop: 0 15 max_height: 10 price range: 309.00 plot step: 30.90 +--B=S----------+ 292.10 | H * | 261.20 | * H * | 230.30 | * H * | 199.40 |** H * * | 168.50 |** H** * * *| 137.60 |** H**** ****| 106.70 |** H**** ****| 75.80 |** *H***** ****| 44.90 |** *H***** ****| 14.00 |****H*****L****| +^----^----^----+ 0 5 10
This section outlines some techniques to make implementing this function more tractable.
Plot Data
Initially, some plot data should be printed to verify that correct values are being used for the remainder of the plotting routine.
==PLOT DATA== start/stop: 0 15 max_height: 10 price range: 309.00 plot step: 30.90
The first 3 numbers are parameters to stock_plot()
. The price range
can be computed from looking at the stock's price
array at
hi_index
and lo_index
and the plot step
by dividing the price
range by max_height
.
Vertical Printing
Character terminals tend to print things well horizontally so printing the vertical bars that appear in the main body of the plot will take some planning and effort. The general approach is encoded in the following pseudocode.
for h=max_height-1 down to 0 do thresh = h*plot_step + min_price # make sure to use a double for thresh for i=start index to stop index # note start/stop are parameters to stock_plot() if stock price i >= thresh print "*" else print " " done print "\n" done
This approach gives the general idea, that one iterates top down and prints a symbol for stocks with a price larger than a threshold and a blank space for those less than. After printing a "row" of such symbols, advance to the next line down and repeat the process with a lower threshold.
Follow this pseudocode approach closely but note that it misses several details.
- The syntax needs to be converted to C code.
- It does not print any of the left axis number labels or left axis bar.
- It does not print the Highest priced stock or Lowest priced stock
(at stock index
hi_index
andlo_index
with the special symbolsH
andL
that should be used.
After getting the basic body of the algorithm working you can add these elements in.
NOTE: Floating point computations are a bit fragile so make sure
that you follow the above code well and use a double
for
thresholds. Past students who have worked on projects like this one
have sometimes failed tests if they use a float
instead due to
imprecision and rounding errors.
Left-side Axis Values and Left/Right Bars
The left side axis labels look something like
292.10 | 261.20 | 230.30 | 199.40 | .......... 10-char wide numeric field
Note that the whitespace leading each of the numeric label values on
the left axis. It is painful to try to manually print such numbers
with appropriate spacing and unnecessary as the printf()
function
already has such facilities built in. Use an invocation like the
following
printf("%10.2f |", ...)
which will print a field that is 10 characters wide and place a
floating point value aligned to right in it. The number will be
printed with 2 decimal places and be trailed by the string constant
" |"
to set up the left axis bar.
The value to be printed is the thresh
value described in the
previous section, the price at which a symbol is printed on the row
for prices at or above that level. After printing the left axis bar,
print out the main plot line at that threshold.
At the end of each line in the main plot body, print a "|\n"
to get
the right axis bar and carry down to the next line.
Top Axis Buy/Sell Markings
The top axis will look like one of the following:
...........11 spaces +--B====S------+ Best Buy/Sell is being plotted +----------B===+ Best Buy part of plot range but Sell is not in range +----------B===+ Best Buy part of plot range but Sell is not in range +====S---------+ Best Sell is being plotted but Buy is out of range +--------------+ Best Buy/Sell is not in range or not set
This can be done with a loop that traverses from the start
to stop
index and checks if the index is at the best_buy
or best_sell
index, in between these two, or outside the range. In each case,
prints an appropriate character, one of B S = -
.
Note also the leading whitespace which is 10 spaces plus 1 space and a
+
symbol. This can be easily printed with a printf()
invocation
like
printf("%10s +","");
which prints a string in a space 10 characters wide but substitutes an empty string in that space. The 10 spaces matches the width of the left side numeric labels.
Bottom Axis Tic Marks and Numeric Indices
The bottom axis and axis labels will look something like one of the following.
...........11 spaces +^----^----^----^--+ Starting at 0, axis marks at indices divisible by 5 0 5 10 15 Axis labels aligned with each tic +---^----^----^----+ Starting at 2, axis marks at indices divisible by 5 5 10 15 Axis labels aligned with each tic
The axis tic marks can be printed easily by starting the line with
spaces and a +
, then iterating from the start
to stop
parameters
and printing a -
except when the index is divisible by 5 in which
case a ^
is printed.
The numeric labels are a bit trickier. The following approach is suggested.
- Print 11 spaces to line up with the first bottom axis mark.
- Create a loop variable outside of any loop as this loop variable will be used in 2 consecutive loops.
- Start a loop with your loop variable initialized to
start
and increment the variable each iteration. Terminate this loop when the variable is evenly divisible by five (e.g.var % 5 == 0
). Each iteration print a space. This loop will "line up" further printing at a positions divisible by 5. Start a second loop based on the current value of your loop variable. Each iteration increase the variable by 5 and terminate it after surpassing
stop
. Suggested syntax isfor(; var < stop; var += 5){ ... }
with the first
;
indicating there is no loop initialization so the previous value ofvar
will be used as the staring point.Each iteration print out a numeric index using a 5-char width but left aligned. This can be done with
printf("%-5d",...)
with the negative sign yielding left-alignment.
It may take a bit of work to get the formatting correct but this will be good practice to acquaint you with formatted output.
Don't forget you can run stockplot_main
to observe its output
while working to get the right format: many forget this while
attempting to only use the test cases.
5.8 Grading Criteria for Problem 2 grading 35
Weight | Criteria |
---|---|
AUTOMATED TESTS via make test-prob2 |
|
20 | Runs the tests provided in test_stock2.org for last functions in stock_funcs.c, stock_demo.c |
Runs all code under Valgrind to ensure that no memory errors are present. | |
1 point per test passed | |
MANUAL INSPECTION | |
5 | Code Style: Functions adhere to CMSC 216 C Coding Style Guide which dictates |
reasonable indentation, commenting, consistency of curly usage, etc. | |
2 | stock_set_hilo() and stock_set_best() |
Clear loop to scan through the prices array to find the low/high and best buy/sell |
|
Proper changes to the fields lo_index / hi_index / best_buy / best_sell |
|
3 | stock_load() and count_lines() |
Use of fopen() and fscanf() to read in data from the file |
|
Code to gracefully handle failure to open a file and print appropriate messages. | |
Proper use of count_lines() to determine the number of lines in the data file |
|
Use of malloc() to allocate memory for the prices array |
|
Use of the "%*s" format specifier to skip leading "time" string in files |
|
Duplication of the filename to the data_file field likely using the strdup() function |
|
5 | stock_plot() |
Clear section that calculates the "plot step" with appropriate variables like range | |
Code to print out the initial plot information and loop to print the upper axis "bar" | |
Clear loop that prints out bars with height proportional to the price of the stock | |
Section of conditional code which prints if stock is low/high and best buy/sell | |
Clear section dealing with lower axis ticks and labels. |
6 Problem 3: Main Stock Application
6.1 main() Command Line Args
The final steps to complete the application are to create a main()
function that combines the previously completed functions to load
stock data and plot it. There are examples of how this main
application should run that appear earlier in the Overview Section
which are worth inspecting. Briefly, the syntax to run the main
function is as follows which features 2, 3, or 4 command line
arguments.
# 2 required command line arguments, otherwise print a usage message >> ./stock_main usage: ./stock_main <stockfile> <max_height> [start] [stop] # arg1: stock file, arg2: maxiumum height >> ./stock_main data/stock-jagged.txt 20 ... # arg3 preset: start index to print (inclusive) # arg4 missing: no stop index given # print from prices[6] to the end of the array >> ./stock_main data/stock-jagged.txt 20 6 ... # arg3 preset: start index to print (inclusive) # arg4 present: stop index (exclusive) # print from prices[3] to prices[7] >> ./stock_main data/stock-jagged.txt 20 3 8 ...
Most C program main()
functions have the following prototype
(return value and argument types):
int main(int argc, char *argv[])
argc
is the number of items that appear on a command lineargv[]
is an array of strings which are those command line arguments
Using a condition and/or loop like the following, you can print out all command line arguments
int main(int argc, char *argv[]){ if(argc > 2){ printf("passed at least 2 arguments\n"); } for(int i=0; i<argc; i++){ printf("argv[%d]: %s\n",argv[i]); } ...
Running a program with that loop at the top would print something like the following:
>> ./a.out argv[0]: a.out >> ./a.out stuff.txt argv[0]: a.out argv[1]: stuff.txt >> ./a.out one 2 three passed at least 2 arguments argv[0]: a.out argv[1]: one argv[2]: 2 argv[3]: three
Note that all of the arguments are strings so if you want binary number versions of them, you'll need to use a conversion function.
6.2 Implementation of main()
Create the file stock_main.c
and write your main()
function in
it. Do not write a main()
in stock_funcs.c
as this will cause
testing files to fail to compile. C programs can have only one
main()
function and the testing setup already has a main()
that is
used to interrogate the service functions. It is customary to have a
main()
in a separate file so that multiple programs can use the same
set of service functions.
Here are few notes on how to complete main()
function in
stock_main.c
.
- Use the functions you have completed previously for most of the
functionality that
main()
should perform. - Examine how to convert a string to a binary integer using the
atoi()
function. There is an example of this given in a PROVIDED section ofmain()
. - It may be helpful to review a
main()
function that is similar to what you wish to accomplish. Good options are themain()
provided instock_demo.c
OR themain()
in a recent lab like the "treasure maps" exercise. - Avoid repeating common code in conditionals. Load the stock file in only one line of code, not in multiple conditional cases. While it can be easy to handle the cases of 3, 4, 5, etc. command line arguments in an if/else if and copy paste code between them, this runs against the "DRY" principle ("Don't Repeat Yourself") that should guide most programming. Make your code compact with the smallest amount of copy/pasted lines you can muster.
Below is a quick demo of how the behavior of main()
should work on
the command line.
# omit command line arguements to show usage >> ./stock_main usage: ./stock_main <stockfile> <max_height> [start] [stop] # plot all prices at height 10 >> ./stock_main data/stock-jagged.txt 10 ==STOCK DATA== data_file: data/stock-jagged.txt count: 15 prices: [103.00, 250.00, 133.00, ...] lo_index: 8 hi_index: 11 best_buy: 8 best_sell: 11 profit: 232.00 ==PLOT DATA== start/stop: 0 15 max_height: 10 price range: 232.00 plot step: 23.20 +--------B==S---+ 246.80 | * *H | 223.60 | * * *H | 200.40 | * * *H | 177.20 | * * *H | 154.00 | * * * *H | 130.80 | **** * *H | 107.60 | **** * *H | 84.40 |******* *H *| 61.20 |******* *H **| 38.00 |********L**H***| +^----^----^----+ 0 5 10 # plot all prices at height 5 >> ./stock_main data/stock-jagged.txt 5 ==STOCK DATA== data_file: data/stock-jagged.txt count: 15 prices: [103.00, 250.00, 133.00, ...] lo_index: 8 hi_index: 11 best_buy: 8 best_sell: 11 profit: 232.00 ==PLOT DATA== start/stop: 0 15 max_height: 5 price range: 232.00 plot step: 46.40 +--------B==S---+ 223.60 | * * *H | 177.20 | * * *H | 130.80 | **** * *H | 84.40 |******* *H *| 38.00 |********L**H***| +^----^----^----+ 0 5 10 # plot a specific range of prices, times 20 to 50 >> ./stock_main data/stock-GOOG-08-02-2021.txt 10 20 50 ==STOCK DATA== data_file: data/stock-GOOG-08-02-2021.txt count: 345 prices: [2715.00, 2715.00, 2711.00, ...] lo_index: 24 hi_index: 337 best_buy: 24 best_sell: 337 profit: 25.75 ==PLOT DATA== start/stop: 20 50 max_height: 10 price range: 25.75 plot step: 2.58 +----B=========================+ 2717.22 | | 2714.64 | | 2712.07 | | 2709.49 | | 2706.91 | **| 2704.34 | ***| 2701.76 | * *******| 2699.19 | * ****** ********| 2696.61 |**** ************ **********| 2694.04 |****L*************************| +^----^----^----^----^----^----+ 20 25 30 35 40 45 # plot from a large index to the end of the stock prices >> ./stock_main data/stock-GOOG-08-02-2021.txt 10 290 ==STOCK DATA== data_file: data/stock-GOOG-08-02-2021.txt count: 345 prices: [2715.00, 2715.00, 2711.00, ...] lo_index: 24 hi_index: 337 best_buy: 24 best_sell: 337 profit: 25.75 ==PLOT DATA== start/stop: 290 345 max_height: 10 price range: 25.75 plot step: 2.58 +===============================================S-------+ 2717.22 | **H*******| 2714.64 | ****H*******| 2712.07 |** ******** * * * *****H*******| 2709.49 |***************************************** *****H*******| 2706.91 |***********************************************H*******| 2704.34 |***********************************************H*******| 2701.76 |***********************************************H*******| 2699.19 |***********************************************H*******| 2696.61 |***********************************************H*******| 2694.04 |***********************************************H*******| +^----^----^----^----^----^----^----^----^----^----^----+ 290 295 300 305 310 315 320 325 330 335 340 # show tricky case where the low/high stock points are not the best # buy/sell times >> ./stock_main data/stock-min-after-max.txt 10 ==STOCK DATA== data_file: data/stock-min-after-max.txt count: 15 prices: [223.00, 292.00, 27.00, ...] lo_index: 10 hi_index: 4 best_buy: 2 best_sell: 4 profit: 296.00 ==PLOT DATA== start/stop: 0 15 max_height: 10 price range: 309.00 plot step: 30.90 +--B=S----------+ 292.10 | H * | 261.20 | * H * | 230.30 | * H * | 199.40 |** H * * | 168.50 |** H** * * *| 137.60 |** H**** ****| 106.70 |** H**** ****| 75.80 |** *H***** ****| 44.90 |** *H***** ****| 14.00 |****H*****L****| +^----^----^----+ 0 5 10 # attempt to load a missing file >> ./stock_main data/stock-no-such-file.txt 15 Could not open file 'data/stock-no-such-file.txt', cannot count lines Unable to open stock file 'data/stock-no-such-file.txt', cannot load stock data Failed to load stock file, exiting # too few command line args >> ./stock_main data/stock-jagged.txt usage: ./stock_main <stockfile> <max_height> [start] [stop] # too many command line args >> ./stock_main data/stock-jagged.txt 15 100 200 500 900 usage: ./stock_main <stockfile> <max_height> [start] [stop]
Once you have completed the main function, you can test it via make test-prob3
.
>> make gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c stock_main.c gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c stock_funcs.c gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o stock_main stock_main.o stock_funcs.o gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c stock_demo.c gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o stock_demo stock_demo.o stock_funcs.o gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o test_stock_funcs test_stock_funcs.c stock_funcs.o >> make test-prob3 ./testy test_stock3.org ============================================================ == test_stock3.org : Problem 3 stock_main == Running 10 / 10 tests 1) stock_main data/stock-ascending.txt all : ok 2) stock_main fail to open : ok 3) stock_main data/stock-ascending.txt 5 to 10 : ok 4) stock_main data/stock-ascending.txt 5 to End : ok 5) stock_main data/stock-min-after-max.txt : ok 6) stock_main Facebook Stock 5 to 43 : ok 7) stock_main Facebook Stock 100 to 140 : ok 8) stock_main Facebook Stock 152 to 203 : ok 9) stock_main Facebook Stock 500 to End : ok 10) stock_main Google Stock : ok ============================================================ RESULTS: 10 / 10 tests passed
6.3 Grading Criteria for Problem 3 grading 20
Weight | Criteria |
---|---|
AUTOMATED TESTS via make test-prob3 |
|
10 | Runs the tests provided in test_stock3.org for stock_main.c |
Runs all code under Valgrind to ensure that no memory errors are present. | |
1 point per test passed | |
MANUAL INSPECTION | |
5 | Code Style: Functions adhere to CMSC 216 C Coding Style Guide which dictates |
reasonable indentation, commenting, consistency of curly usage, etc. | |
2 | Correct use of a previously written functions for memory allocation, loading, setting stock properties, and plotting |
3 | Logically handles command line arguments including appropriate behavior for different invocations |
Avoids too much duplicate code while handling command line arguments | |
Avoids repeated code for common actions like loading the stock file; loads only appear in one spot, not several conditions | |
Uses of an appropriate library function to convert from strings to integers for numeric arguments |
7 Problem 4: Multi-stock Plotting
7.1 Rationale
It is useful when analyzing data (including stocks) to plot relevant
data together in the same chart for purpose of comparison. This
problem extends the plot application to enable this. It develops
several additional service functions that enable loading of multiple
stock files and an alternate main()
application that plots multiple
stocks.
NOTE: Unlike with previous problems, there are no "unit tests" for
the individual functions. Only tests for the resulting main()
function are present. This means implementers will need to build most
of the functionality together and check the overall results of tests
OR alternatively develop their own tests. While debugging, it will be
essential to practice isolating bugs to individual functions through
inspection of results and possibly use of a debugger.
Below is example output for how the multi-plotting application looks.
>> stock_multimain 20 data/stock-daily12-googl_us_d.txt data/stock-daily8-aapl_us_d.txt data/stock-daily10-nvda_us_d.txt data/stock-daily4-adbe_us_d.txt data/stock-daily10-tsla_us_d.txt ==MULTIPLOT DATA== max_height: 20 stop_count: 12 min price: 174.98 max price: 354.85 price range: 179.87 plot step: 8.99 ==LEGEND== a : data/stock-daily12-googl_us_d.txt b : data/stock-daily8-aapl_us_d.txt c : data/stock-daily10-nvda_us_d.txt d : data/stock-daily4-adbe_us_d.txt e : data/stock-daily10-tsla_us_d.txt +------------------------------------------------------------+ 345.86 | d d D E | 336.86 | De de d D e E | 327.87 | De de de De e e e E | 318.88 | De de de De e e e E e E | 309.88 | De de de De e e e E e E | 300.89 | De de de De e e e E e E | 291.90 | De de de De e e e E e E | 282.90 | De de de De e e e E e E | 273.91 | De de de De e e e E e E | 264.91 | De de de De e e e E e E | 255.92 | De de de De e e e E e E | 246.93 | De de de De e e e E e E | 237.93 | De de de De e e e E e E | 228.94 | b De B de b de b De b e b e e E e E | 219.95 | b De B de b de b De b e b e b e B E e E | 210.95 | b De B de b de b De b e b e b e B E e E | 201.96 |ab De B deab deab Deab e b e b e B Ea eA Ea a | 192.97 |ab DeaB deab deab Deab eab eAb eaB Ea eA Ea a | 183.97 |ab DeaB deab deab Deab eab eAb eaB Ea eA Ea a | 174.98 |abCDeaBcdeabcdeabcDeabc eabc eAbc eaBC Ea c eA c Ea a | +^------------------------^------------------------^---------+ 0 5 10
7.2 Additional Required Service Functions
There are two additional functions the are required to support plotting multiple stocks. These are described in the preceding outline of all required functions and are repeated briefly here.
stock_t **stock_load_all(char *filenames[], int stock_count) // PROBLEM 4: Loads all files named in `filenames[]` as stocks and // returns an array of pointers to those stocks. void stock_multiplot(stock_t **stocks, int stocks_count, int max_height); // PROBLEM 4: Plot all of the stocks in the `stocks` array which is // `stocks_count` elements long.
Both these functions deal with the C data type stock **
. This is a
double indirection (two-layered pointers) of stocks but is best
thought of as "an array of stock pointers". Syntax like stocks[i]
will peel off one layer of pointers giving a single pointer stock_t
*
to a stock. The syntax stocks[i]->count
accesses one field of the
stocks referenced in this function. The array of stock pointers must
be allocated in stock_load_all()
along with the stocks pointed at by
constituent pointers. stock_multiplot()
uses this array of stocks in
its plotting. Later in a main()
function the array of stocks must be
de-allocated.
stock_load_all()
should make use of previously written functions
allocate and load stock data from files. This should lead to a short
function (around 20 lines of code).
It is recommended that implementations of stock_multiplot()
start
by copying the implementation of stock_plot()
and then make
adjustments to adapt to the multiple stock setting.
Examples of how stock_multiplot()
shows its output are below.
7.3 Additional Main Application
The file stock_multimain.c
will contain another main()
function
that prints multiple stocks from the command line. The command line
formats supported are different form stock_main.c
and support an
arbitrary number of stock files that may be displayed but no
start/stop indices. Some Examples are below.
>> ./stock_multimain usage: ./stock_multimain <max_height> <stockfile1> [stockfile2] [...] >> ./stock_multimain 10 data/stock-jagged.txt ==MULTIPLOT DATA== max_height: 10 stop_count: 15 min price: 38.00 max price: 270.00 price range: 232.00 plot step: 23.20 ==LEGEND== a : data/stock-jagged.txt +---------------+ 246.80 | a aA | 223.60 | a a aA | 200.40 | a a aA | 177.20 | a a aA | 154.00 | a a a aA | 130.80 | aaaa a aA | 107.60 | aaaa a aA | 84.40 |aaaaaaa aA a| 61.20 |aaaaaaa aA aa| 38.00 |aaaaaaaaAaaAaaa| +^----^----^----+ 0 5 10 >> ./stock_multimain 10 data/stock-jagged.txt data/stock-valley.txt ==MULTIPLOT DATA== max_height: 10 stop_count: 15 min price: 38.00 max price: 270.00 price range: 232.00 plot step: 23.20 ==LEGEND== a : data/stock-jagged.txt b : data/stock-valley.txt +------------------------------+ 246.80 | a a A | 223.60 | a a a A | 200.40 | a a a A | 177.20 | a a a A | 154.00 | a a a a A | 130.80 | a a a a a a A | 107.60 | a a a a a a A | 84.40 |ababa a a a a babAB a | 61.20 |ababababa a a b b babAB a a | 38.00 |abababababaBababAbababABa a a | +^---------^---------^---------+ 0 5 10 >> ./stock_multimain 5 data/stock-3only.txt data/stock-2only.txt ==MULTIPLOT DATA== max_height: 5 stop_count: 3 min price: 45.26 max price: 103.07 price range: 57.81 plot step: 11.56 ==LEGEND== a : data/stock-3only.txt b : data/stock-2only.txt +------+ 91.51 |A | 79.95 |A B | 68.38 |A B | 56.82 |A Ba | 45.26 |ABABa | +^-----+ 0 >> ./stock_multimain 10 data/stock-jagged.txt data/stock-valley.txt data/stock-min-after-max.txt ==MULTIPLOT DATA== max_height: 10 stop_count: 15 min price: 14.00 max price: 323.00 price range: 309.00 plot step: 30.90 ==LEGEND== a : data/stock-jagged.txt b : data/stock-valley.txt c : data/stock-min-after-max.txt +---------------------------------------------+ 292.10 | C c | 261.20 | c C c A | 230.30 | a c C a c a A | 199.40 | ca c C a c c a A | 168.50 | ca c C ca c c a A c c| 137.60 | ca c a a C ca c c c a A c c c c| 106.70 | ca ca a a C ca c c c a A c c ca c| 75.80 |abcabcab a ca Ca ca c c c bcab ABc c ca c| 44.90 |abcabcab abcabCaBcabcabc bcabcab ABca ca ca c| 14.00 |abcabcabcabcabCaBcabcabcAbcabcabCABca ca ca c| +^--------------^--------------^--------------+ 0 5 10 >> ./stock_multimain 20 data/stock-daily12-aapl_us_d.txt data/stock-daily10-nvda_us_d.txt data/stock-daily6-tsla_us_d.txt data/stock-daily8-googl_us_d.txt ==MULTIPLOT DATA== max_height: 20 stop_count: 12 min price: 174.98 max price: 340.84 price range: 165.86 plot step: 8.29 ==LEGEND== a : data/stock-daily12-aapl_us_d.txt b : data/stock-daily10-nvda_us_d.txt c : data/stock-daily6-tsla_us_d.txt d : data/stock-daily8-googl_us_d.txt +------------------------------------------------+ 332.55 | C c c c | 324.25 | C c c c c C | 315.96 | C c c c c C | 307.67 | C c c c c C | 299.38 | C c c c c C | 291.08 | C c c c c C | 282.79 | C c c c c C | 274.50 | C c c c c C | 266.20 | C c c c c C | 257.91 | C c c c c C | 249.62 | C c c c c C | 241.32 | C c c c c C | 233.03 | C A c c c c C | 224.74 |a C A c a c a c a c a C a A a a a a | 216.44 |a C A c a c a c a c a C a A a a a a | 208.15 |a C A c a c a c a c a C a A a a a a | 199.86 |a CdA cda cda cDa cda Cda A a a a a | 191.57 |a CdA cda cda cDa cda Cda DA da a a a | 183.27 |a CdA cda cda cDa cda Cda DA da a a a | 174.98 |aBCdAbcdabcdabcDabcdabCdab DAB dab ab a a | +^-------------------^-------------------^-------+ 0 5 10
7.4 Grading Criteria for Problem 4 grading 20
Weight | Criteria |
---|---|
AUTOMATED TESTS via make test-prob4 |
|
10 | Runs the tests provided in test_stock4.org for stock_multimain.c |
Runs all code under Valgrind to ensure that no memory errors are present. | |
1 point per test passed | |
MANUAL INSPECTION | |
5 | Code Style: Functions adhere to CMSC 216 C Coding Style Guide which dictates |
reasonable indentation, commenting, consistency of curly usage, etc. | |
1 | stock_load_all() in stock_funcs.c |
Correct memory allocation for an array of pointers to stocks | |
Use of previously defined functions to create and load data for stock files | |
Clear error checking so that if any load fails, all memory is de-allocated before returning. | |
3 | stock_multiplot() in stock_funcs.c |
Code structure similar to the previous stock_plot() function |
|
Clear loops ahead near the beginning to determine data ranges for prices | |
COMMENTS indicating the intent of loops and conditions amid this complex function | |
Concise code for printing axis marks and labels | |
1 | main() in stock_multimain.c |
Use of previously defined functions for loading stocks, finding their price ranges, and plotting | |
Clear memory de-allocation of data allocated in other functions and passed to main() |
8 OPTIONAL Makeup Credit
The "brute force" approach initially suggested to find best_buy /
best_sell
indices compares all possible buy/sell pairs to determine
the best profit possible. This is a Quadratic algorithm with
complexity O(N^2)
with N
being the length of the prices[]
array.
One can improve upon this algorithm to get an O(N)
linear
algorithm. This takes some cleverness or some research. For example,
the problem is directly related to the Maximum Subarray Problem and
its various solutions among which the Linear time algorithm is
sometimes referred to as Kadane's approach. Some possible references
are here:
- https://en.wikipedia.org/wiki/Maximum_subarray_problem
- https://cs.gmu.edu/~kauffman/cs310/01-intro.pdf
- https://www.techiedelight.com/maximum-subarray-problem-kadanes-algorithm/
Note that adapting these approaches to the current setting will
require some care: the Maximum Subarray problem assumes elements in
the array are added together whereas in our Stock setting, it is the
difference between elements that matter. However, by absorbing the
tricks used the Linear time Maximum Subarray algorithm, one can get
the same effect for the Stocks, an O(N)
algorithm to set best_buy /
best_sell
.
For up to 10 points of PROJECT MAKEUP CREDIT implement the following alternate function.
int stock_set_best_linear(stock_t *stock); // OPTIONAL MAKEUP CREDIT: Identical to stock_set_best() except uses a // linear time algorithm to determine the best buy/sell times
- The normal
stock_set_best()
function MUST be present with Quadratic complexity (or worse) - The linear time version must also be present
- The linear time version MUST be used in
main()
and pass test cases associated with running the whole program.
MAKEUP CREDIT will go into the general pool of points earned for projects to make up for points missed on projects elsewhere. If a student scores 110/100 on Project 1 and 90 / 100 on Project 2, the 10 points from Project 1 will make up for the credit lost on Project 2. Your total score on All Projects cannot exceed 100% so any points beyond the overall total will simply be dropped.
Grading Criteria for MAKEUP CREDIT grading 10
Weight | Criteria |
---|---|
10 | TOTAL OPTIONAL MAKEUP CREDIT |
Implements the normal stock_set_best() which uses a quadratic or slower algorithm |
|
ALSO Implements a stock_set_best_linear() which uses a linear time algorithm. |
|
5 | Manual verification by graders that the linear algorithm looks correct runs in linear time on the size of the stock prices. |
5 | Uses stock_set_best_linear() in stock_main() and passes all associated the main function test cases. |
9 Project Submission
9.1 Submit to Gradescope
Submission is identical to lab work. There are two options to submit to Gradescope.
In a terminal, run the command
>> make submit
and punch in your email/password on Gradescope to use the provided
gradescope-submit
script to upload your work. This is more convenient so is recommended.- Run the command
make zip
to createp1-complete.zip
, download this file and upload it to Gradescope through a web browser. This is a good fallback if you are having trouble with command line submission.
Refer to Lab01 Submission Instructions if you need a refresher on submitting.
9.2 Late Policies
You may wish to review the policy on late project submission which will cost 1 Engagement Point per day late. No projects will be accepted more than 48 hours after the deadline.