(Polynomial Class) (c++, must work on visual studios 2017, and must contain a fu
ID: 3750836 • Letter: #
Question
(Polynomial Class) (c++, must work on visual studios 2017, and must contain a full working program using said class)
Develop class Polynomial. The internal representation of a Polynomial is an array of terms. Each term contains a coefficient and an exponent, e.g., the term
2x4
has the coefficient 2 and the exponent 4. Develop a complete class containing proper constructor and destructor functions as well as set and get functions. The class should also provide the following overloaded operator capabilities:
a) Overload the addition operator (+) to add two Polynomials.
b) Overload the subtraction operator (-) to subtract two Polynomials.
c) Overload the assignment operator to assign one Polynomial to another.
d) Overload the multiplication operator (*) to multiply two Polynomials.
e) Overload the addition assignment operator (+=), subtraction assignment operator (-=), and multiplication assignment operator (*=).
Explanation / Answer
main.cpp
#ifdef NDEBUG
#include <iostream> // cout/endl
#include "polynomial.h" // Our polynomial class.
using std::cout;
using std::endl;
// Perform basic demonstration of Polynomial class.
int main()
{
// Demo.
cout << "Polynomial Class Demonstration Program ";
// Create polynomial, 7x^4 - x^2 + 3.
Polynomial a;
a.setTerm(4, 7.);
a.setTerm(2, -1.);
a.setTerm(0, 3.);
// Create polynomial, -3x^2 + 4 with list initalizer.
Polynomial b({ { 2, -3. }, { 0, 4. } });
// Output some information about polynomials.
cout << " degree of (" << a << ") is: " << a.getDegree() << endl;
// Get a term's coefficient.
double c;
if (b.getTerm(2, c))
{
cout << " Coefficient of 2nd term of (" << b << ") is: " << c << endl;
//cout << " Coefficient of 2nd term of (" << b << ") is: " << b[2] << endl;
}
// These two methods not fully tested.
//cout << " (" << a << ") evaluated at 2 = " << a.evaluate(2) << endl;
//cout << " Differentiate (" << a << ") = " << a.differentiate() << endl;
// Assignment operator.
cout << "Assignment: ";
cout << " a = " << a << endl;
b = a;
cout << " Set b = a, now b = " << b << endl;
// Add, subtract and multiply binary operators.
cout << "Add, subtract & multiply: ";
cout << " (" << a << ") + (" << b << ") = " << (a + b) << endl;
cout << " (" << a << ") - (" << b << ") = " << (a - b) << endl;
cout << " (" << a << ") * (" << b << ") = " << (a * b) << endl;
// Unary add, subtract and multiply.
cout << "Unary add, subtract & multiply: ";
a += b;
cout << " Set a += b, now a = " << a << endl;
a -= b;
cout << " Set a -= b, now a = " << a << endl;
a *= b;
cout << " Set a *= b, now a = " << a << endl;
// Unary negate.
cout << "Unary negation: ";
cout << " a = " << a << endl;
b = -a;
cout << " Set b = -a, now b = " << b << endl;
// Equality.
cout << "Equality: ";
cout << " a = " << a << endl;
cout << " b = " << b << endl;
cout << " Is a == b? " << std::boolalpha << (a == b) << endl;
cout << " b = " << -b << endl;
cout << " Is a == -b now? " << std::boolalpha << (a == -b) << endl;
// Divison and modulus (with remainder).
cout << "Division & modulus (with remainder): ";
Polynomial e({ { 3, 1. },{ 2, -2. },{ 0, -4. } });
Polynomial f({ { 1, 1. },{ 0, -3. } });
cout << " (" << e << ") / (" << f << ") = " << (e / f) << endl;
cout << " (" << e << ") % (" << f << ") = " << (e % f) << endl;
// Divison and modulus (with remainder).
//Polynomial g({ { 4, 5. },{ 3, 2. },{ 2, -3. },{ 1, 2. },{ 0, 2. } });
//Polynomial h({ { 2, 2. },{ 1, 3. },{ 0, 1. } });
//cout << "(" << g << ") / (" << h << ") = " << (g / h) << endl;
//cout << "(" << g << ") % (" << h << ") = " << (g % h) << endl;
// Divison and modulus (without remainder).
cout << "Division & modulus (without remainder): ";
Polynomial i({ { 2, 1. },{ 1, 2. },{ 0, 1. } });
Polynomial j({ { 1, 1. },{ 0, 1. } });
cout << " (" << i << ") / (" << j << ") = " << (i / j) << endl;
cout << " (" << i << ") % (" << j << ") = " << (i % j) << endl;
try
{
Polynomial e;
a = a / e;
}
catch (std::overflow_error ex)
{
cout << "Correctly caught attempted division by zero. ";
}
return 0;
}
#endif
polynomial.h
#include <iostream> // cout/endl
#include <string> // string
#include <iomanip> // setprecision
#include <sstream> // stringstream
#include <map> // map for poly terms.
#include <numeric> // accumulate
#include <algorithm> // for_each
#include <cmath> // fabs
#include <initializer_list>
#include "range_for_reverse_iterator.h"
// Used to calculate poly evaluation for comparison operators.
static const double EVALUATION_CONSTANT{ 1.1 };
class Polynomial
{
private:
// Polynomial terms contained in map in format of map<exponent, coefficient>.
std::map<unsigned, double> terms;
// Determines if a term exists for given exponent.
bool exists(const unsigned& exponent) const;
public:
// Default constructor.
Polynomial();
// List initaializer constructor.
Polynomial(std::initializer_list<std::pair<const unsigned, double>> init);
// Default destructor.
~Polynomial() = default;
// Setter function for term.
void setTerm(const unsigned exponent, const double coefficient);
// Getter function for term coefficient.
bool const getTerm(const unsigned exponent, double& coefficient);
// Getter function for polynomial degree.
unsigned const getDegree();
// Evaluate polynomial at x (used by comparison operations).
double evaluate(double x);
// Differentiate polynomial and return result.
Polynomial differentiate();
// Return polynomial coefficient at exponent index.
double operator[] (const int exponent) const
{
if (exponent < 0)
throw std::out_of_range("Index < 0");
return terms.at(exponent);
}
// Set polynomial coefficient at exponent index.
double& operator[] (const int exponent)
{
if (exponent < 0)
throw std::out_of_range("Index < 0");
return terms[exponent];
}
// Overload assignment operator.
const Polynomial& operator= (const Polynomial rhs)
{
// Check for self-assignment.
if (this == &rhs)
// Skip assignment, and just return *this.
return *this;
// Zeroize poly, and reassign this to rhs terms.
terms.clear();
for (auto t : rhs.terms)
terms[t.first] = t.second;
return *this;
}
// Add polynomials via overloaded binary plus operator.
const Polynomial operator+ (const Polynomial rhs)
{
// Copy this's terms to result poly.
Polynomial result = *this;
// Use accumulator to add rhs to lhs terms.
result.terms = accumulate(rhs.terms.cbegin(), rhs.terms.cend(), result.terms,
[](std::map<unsigned, double>& t, const std::pair<const unsigned, double>& p)
{
return (t[p.first] += p.second, t);
}
);
// Normalize polynomial.
result.getDegree();
return result;
}
// Subtract polynomials via overloaded binary minus operator.
const Polynomial operator- (const Polynomial rhs)
{
// Copy this's terms to result poly.
Polynomial result = *this;
// Use accumulator to subtract rhs from lhs terms.
result.terms = accumulate(rhs.terms.cbegin(), rhs.terms.cend(), result.terms,
[](std::map<unsigned, double> &t, const std::pair<const unsigned, double> &p)
{
return (t[p.first] -= p.second, t);
}
);
// Normalize polynomial.
result.getDegree();
return result;
}
// Multiply Polynomials via overloaded binary multiplication operator.
const Polynomial operator* (const Polynomial rhs)
{
Polynomial result;
// Multiply all lhs (or this) terms by all rhs terms.
for_each(this->terms.cbegin(), this->terms.cend(), [&result, rhs](auto lhsTerm)
{
for_each(rhs.terms.cbegin(), rhs.terms.cend(), [&result, lhsTerm](auto rhsTerm)
{
result.terms[lhsTerm.first + rhsTerm.first] += (lhsTerm.second * rhsTerm.second);
} );
} );
// Normalize polynomial.
result.getDegree();
return result;
}
// Overloaded unary += operator, passes our to "overloaded +".
const Polynomial& operator+= (const Polynomial rhs)
{
*this = *this + rhs;
return *this;
}
// Overloaded unary -= operator, passes our to "overloaded -".
const Polynomial& operator-= (const Polynomial rhs)
{
*this = *this - rhs;
return *this;
}
// Overloaded unary *= operator, passes to our "overloaded *".
const Polynomial& operator*= (const Polynomial rhs)
{
*this = *this * rhs;
return *this;
}
// Overloaded unary minus operator.
const Polynomial operator- ()
{
// Iterate through all terms negating them.
for (auto& t : (*this).terms)
t.second = -t.second;
return *this;
}
// Overload equality operator.
const bool operator== (Polynomial rhs)
{
if ((*this).getDegree() != rhs.getDegree())
return false;
// Iterate through all terms, check equal.
for (auto& t : (*this).terms)
// If coefficients don't match then fail.
if (rhs[t.first] != t.second)
return false;
return true;
}
// Overload inequality operator.
const bool operator!= (Polynomial rhs)
{
return !(*this == rhs);
}
// Overload greater than operator.
const bool operator> (Polynomial rhs)
{
if (fabs(evaluate(EVALUATION_CONSTANT)) > fabs(rhs.evaluate(EVALUATION_CONSTANT)))
return true;
return false;
}
// Overload greater than or equal operator.
const bool operator>= (Polynomial rhs)
{
if (fabs(evaluate(EVALUATION_CONSTANT)) >= fabs(rhs.evaluate(EVALUATION_CONSTANT)))
return true;
return false;
}
// Overload less than operator.
const bool operator< (Polynomial rhs)
{
if (fabs(evaluate(EVALUATION_CONSTANT)) < fabs(rhs.evaluate(EVALUATION_CONSTANT)))
return true;
return false;
}
// Overload less than or equal operator.
const bool operator<= (Polynomial rhs)
{
if (fabs(evaluate(EVALUATION_CONSTANT)) <= fabs(rhs.evaluate(EVALUATION_CONSTANT)))
return true;
return false;
}
// Divide polynomials via overloaded binary modulus operator.
const Polynomial operator% (Polynomial& divisor)
{
// Check for division by zero.
if (divisor.getDegree() == 0 && divisor[0] == 0)
throw std::overflow_error("Divide by zero");
// Preserve this.
Polynomial dividend = *this;
// Is divisor larger than dividend?
if (divisor > dividend)
// Return zero.
return Polynomial();
Polynomial quotient;
// Iterate through all dividend terms.
do {
// Divide coefficients of highest terms, subtract exponents, insert as new quotient term.
quotient[dividend.getDegree() - divisor.getDegree()] = dividend[dividend.getDegree()] / divisor[divisor.getDegree()];
// Multiply divisor by quotient and subtract from dividend.
dividend = *this - (divisor * quotient);
// Repeat until reaching final term or divison is complete.
} while ((dividend.getDegree() != 0) && (dividend.getDegree() >= divisor.getDegree()));
// Normalize polynomial.
dividend.getDegree();
return dividend;
}
// Polynomial long division via overloaded binary divide operator.
const Polynomial operator/ (Polynomial& divisor)
{
// Check for division by zero.
if (divisor.getDegree() == 0 && divisor[0] == 0)
throw std::overflow_error("Divide by zero");
// Preserve this.
Polynomial dividend = *this;
// Is divisor larger than dividend?
if (divisor > dividend)
// Return zero.
return Polynomial();
Polynomial quotient;
// Iterate through all dividend terms.
do {
// Divide coefficients of highest terms, subtract exponents, insert as new quotient term.
quotient[dividend.getDegree() - divisor.getDegree()] = dividend[dividend.getDegree()] / divisor[divisor.getDegree()];
// Multiply divisor by quotient and subtract from dividend.
dividend = *this - (divisor * quotient);
// Repeat until reaching final term or divison is complete.
} while ((dividend.getDegree() != 0) && (dividend.getDegree() >= divisor.getDegree()));
// Normalize polynomial.
quotient.getDegree();
return quotient;
}
// Stream polynomial.
friend std::ostream& operator<< (std::ostream& os, const Polynomial& p)
{
// Loop, filling string with poly terms.
std::string s{ "" };
// Check for zero polynomial first.
if (p.terms.empty())
s = "0";
else
{
// Iterate backwards through all terms.
for (auto& t : reverse(p.terms))
{
// Only terms with coefficients are printed.
if (t.second)
{
std::stringstream stream;
// Print/format properly leading sign.
s += (t == *p.terms.rbegin()) ? (t.second < 0) ? "-" : "" : (t.second > 0) ? " - " : " + ";
stream << std::fixed << std::setprecision(1) << abs(t.second);
// Skip display of superfluous 0 exponent.
if (t.first)
stream << "x^" << t.first;
// Add to string.
s += stream.str();
}
}
}
return os << s;
}
};
// Default constructor.
Polynomial::Polynomial()
{
// Empty polynomial.
terms.clear();
}
// List initaializer constructor.
Polynomial::Polynomial(std::initializer_list<std::pair<const unsigned, double>> init) : terms(init) { }
// Determines if a term exists for exponent.
bool Polynomial::exists(const unsigned& exponent) const
{
return terms.count(exponent);
}
// Setter function for term.
void Polynomial::setTerm(const unsigned exponent, const double coefficient)
{
// Set or update an existing polynomial term.
if (coefficient != 0.)
terms[exponent] += coefficient;
}
// Getter function for term coefficient.
bool const Polynomial::getTerm(const unsigned exponent, double& coefficient)
{
// Get polynomial term if exists.
if (exists(exponent))
{
coefficient = terms[exponent];
return true;
}
return false;
}
// Getter function for polynomial degree.
unsigned const Polynomial::getDegree()
{
// Check for and remove any null terms.
std::map<unsigned, double>::iterator it = terms.begin();
while (it != terms.end())
{
if ((*it).second == 0.)
it = terms.erase(it);
else
++it;
}
// Return highest degree or zero.
if (!terms.empty())
return terms.rbegin()->first;
else
return 0;
}
// Evaluate polynomial at x.
double Polynomial::evaluate(double x)
{
double p{ 0. };
for (auto t : terms)
p += (pow(x, t.first) * t.second);
return p;
}
// Differentiate polynomial and return result.
Polynomial Polynomial::differentiate()
{
Polynomial derivative;
unsigned degree = getDegree();
if (degree == 0)
return derivative;
// derivative.degree = degree - 1;
// Calculate looping through all terms.
for (unsigned i = 0; i < degree; i++)
if (exists(i + 1))
derivative.terms[i] = (i + 1) * terms[i + 1];
return derivative;
}
polynomial.cpp
#include <iostream> // cout/endl
#include <string> // string
#include <iomanip> // setprecision
#include <sstream> // stringstream
#include <map> // map for poly terms.
#include <numeric> // accumulate
#include <algorithm> // for_each
#include <cmath> // fabs
#include <initializer_list>
#include "range_for_reverse_iterator.h"
// Used to calculate poly evaluation for comparison operators.
static const double EVALUATION_CONSTANT{ 1.1 };
class Polynomial
{
private:
// Polynomial terms contained in map in format of map<exponent, coefficient>.
std::map<unsigned, double> terms;
// Determines if a term exists for given exponent.
bool exists(const unsigned& exponent) const;
public:
// Default constructor.
Polynomial();
// List initaializer constructor.
Polynomial(std::initializer_list<std::pair<const unsigned, double>> init);
// Default destructor.
~Polynomial() = default;
// Setter function for term.
void setTerm(const unsigned exponent, const double coefficient);
// Getter function for term coefficient.
bool const getTerm(const unsigned exponent, double& coefficient);
// Getter function for polynomial degree.
unsigned const getDegree();
// Evaluate polynomial at x (used by comparison operations).
double evaluate(double x);
// Differentiate polynomial and return result.
Polynomial differentiate();
// Return polynomial coefficient at exponent index.
double operator[] (const int exponent) const
{
if (exponent < 0)
throw std::out_of_range("Index < 0");
return terms.at(exponent);
}
// Set polynomial coefficient at exponent index.
double& operator[] (const int exponent)
{
if (exponent < 0)
throw std::out_of_range("Index < 0");
return terms[exponent];
}
// Overload assignment operator.
const Polynomial& operator= (const Polynomial rhs)
{
// Check for self-assignment.
if (this == &rhs)
// Skip assignment, and just return *this.
return *this;
// Zeroize poly, and reassign this to rhs terms.
terms.clear();
for (auto t : rhs.terms)
terms[t.first] = t.second;
return *this;
}
// Add polynomials via overloaded binary plus operator.
const Polynomial operator+ (const Polynomial rhs)
{
// Copy this's terms to result poly.
Polynomial result = *this;
// Use accumulator to add rhs to lhs terms.
result.terms = accumulate(rhs.terms.cbegin(), rhs.terms.cend(), result.terms,
[](std::map<unsigned, double>& t, const std::pair<const unsigned, double>& p)
{
return (t[p.first] += p.second, t);
}
);
// Normalize polynomial.
result.getDegree();
return result;
}
// Subtract polynomials via overloaded binary minus operator.
const Polynomial operator- (const Polynomial rhs)
{
// Copy this's terms to result poly.
Polynomial result = *this;
// Use accumulator to subtract rhs from lhs terms.
result.terms = accumulate(rhs.terms.cbegin(), rhs.terms.cend(), result.terms,
[](std::map<unsigned, double> &t, const std::pair<const unsigned, double> &p)
{
return (t[p.first] -= p.second, t);
}
);
// Normalize polynomial.
result.getDegree();
return result;
}
// Multiply Polynomials via overloaded binary multiplication operator.
const Polynomial operator* (const Polynomial rhs)
{
Polynomial result;
// Multiply all lhs (or this) terms by all rhs terms.
for_each(this->terms.cbegin(), this->terms.cend(), [&result, rhs](auto lhsTerm)
{
for_each(rhs.terms.cbegin(), rhs.terms.cend(), [&result, lhsTerm](auto rhsTerm)
{
result.terms[lhsTerm.first + rhsTerm.first] += (lhsTerm.second * rhsTerm.second);
} );
} );
// Normalize polynomial.
result.getDegree();
return result;
}
// Overloaded unary += operator, passes our to "overloaded +".
const Polynomial& operator+= (const Polynomial rhs)
{
*this = *this + rhs;
return *this;
}
// Overloaded unary -= operator, passes our to "overloaded -".
const Polynomial& operator-= (const Polynomial rhs)
{
*this = *this - rhs;
return *this;
}
// Overloaded unary *= operator, passes to our "overloaded *".
const Polynomial& operator*= (const Polynomial rhs)
{
*this = *this * rhs;
return *this;
}
// Overloaded unary minus operator.
const Polynomial operator- ()
{
// Iterate through all terms negating them.
for (auto& t : (*this).terms)
t.second = -t.second;
return *this;
}
// Overload equality operator.
const bool operator== (Polynomial rhs)
{
if ((*this).getDegree() != rhs.getDegree())
return false;
// Iterate through all terms, check equal.
for (auto& t : (*this).terms)
// If coefficients don't match then fail.
if (rhs[t.first] != t.second)
return false;
return true;
}
// Overload inequality operator.
const bool operator!= (Polynomial rhs)
{
return !(*this == rhs);
}
// Overload greater than operator.
const bool operator> (Polynomial rhs)
{
if (fabs(evaluate(EVALUATION_CONSTANT)) > fabs(rhs.evaluate(EVALUATION_CONSTANT)))
return true;
return false;
}
// Overload greater than or equal operator.
const bool operator>= (Polynomial rhs)
{
if (fabs(evaluate(EVALUATION_CONSTANT)) >= fabs(rhs.evaluate(EVALUATION_CONSTANT)))
return true;
return false;
}
// Overload less than operator.
const bool operator< (Polynomial rhs)
{
if (fabs(evaluate(EVALUATION_CONSTANT)) < fabs(rhs.evaluate(EVALUATION_CONSTANT)))
return true;
return false;
}
// Overload less than or equal operator.
const bool operator<= (Polynomial rhs)
{
if (fabs(evaluate(EVALUATION_CONSTANT)) <= fabs(rhs.evaluate(EVALUATION_CONSTANT)))
return true;
return false;
}
// Divide polynomials via overloaded binary modulus operator.
const Polynomial operator% (Polynomial& divisor)
{
// Check for division by zero.
if (divisor.getDegree() == 0 && divisor[0] == 0)
throw std::overflow_error("Divide by zero");
// Preserve this.
Polynomial dividend = *this;
// Is divisor larger than dividend?
if (divisor > dividend)
// Return zero.
return Polynomial();
Polynomial quotient;
// Iterate through all dividend terms.
do {
// Divide coefficients of highest terms, subtract exponents, insert as new quotient term.
quotient[dividend.getDegree() - divisor.getDegree()] = dividend[dividend.getDegree()] / divisor[divisor.getDegree()];
// Multiply divisor by quotient and subtract from dividend.
dividend = *this - (divisor * quotient);
// Repeat until reaching final term or divison is complete.
} while ((dividend.getDegree() != 0) && (dividend.getDegree() >= divisor.getDegree()));
// Normalize polynomial.
dividend.getDegree();
return dividend;
}
// Polynomial long division via overloaded binary divide operator.
const Polynomial operator/ (Polynomial& divisor)
{
// Check for division by zero.
if (divisor.getDegree() == 0 && divisor[0] == 0)
throw std::overflow_error("Divide by zero");
// Preserve this.
Polynomial dividend = *this;
// Is divisor larger than dividend?
if (divisor > dividend)
// Return zero.
return Polynomial();
Polynomial quotient;
// Iterate through all dividend terms.
do {
// Divide coefficients of highest terms, subtract exponents, insert as new quotient term.
quotient[dividend.getDegree() - divisor.getDegree()] = dividend[dividend.getDegree()] / divisor[divisor.getDegree()];
// Multiply divisor by quotient and subtract from dividend.
dividend = *this - (divisor * quotient);
// Repeat until reaching final term or divison is complete.
} while ((dividend.getDegree() != 0) && (dividend.getDegree() >= divisor.getDegree()));
// Normalize polynomial.
quotient.getDegree();
return quotient;
}
// Stream polynomial.
friend std::ostream& operator<< (std::ostream& os, const Polynomial& p)
{
// Loop, filling string with poly terms.
std::string s{ "" };
// Check for zero polynomial first.
if (p.terms.empty())
s = "0";
else
{
// Iterate backwards through all terms.
for (auto& t : reverse(p.terms))
{
// Only terms with coefficients are printed.
if (t.second)
{
std::stringstream stream;
// Print/format properly leading sign.
s += (t == *p.terms.rbegin()) ? (t.second < 0) ? "-" : "" : (t.second > 0) ? " - " : " + ";
stream << std::fixed << std::setprecision(1) << abs(t.second);
// Skip display of superfluous 0 exponent.
if (t.first)
stream << "x^" << t.first;
// Add to string.
s += stream.str();
}
}
}
return os << s;
}
};
// Default constructor.
Polynomial::Polynomial()
{
// Empty polynomial.
terms.clear();
}
// List initaializer constructor.
Polynomial::Polynomial(std::initializer_list<std::pair<const unsigned, double>> init) : terms(init) { }
// Determines if a term exists for exponent.
bool Polynomial::exists(const unsigned& exponent) const
{
return terms.count(exponent);
}
// Setter function for term.
void Polynomial::setTerm(const unsigned exponent, const double coefficient)
{
// Set or update an existing polynomial term.
if (coefficient != 0.)
terms[exponent] += coefficient;
}
// Getter function for term coefficient.
bool const Polynomial::getTerm(const unsigned exponent, double& coefficient)
{
// Get polynomial term if exists.
if (exists(exponent))
{
coefficient = terms[exponent];
return true;
}
return false;
}
// Getter function for polynomial degree.
unsigned const Polynomial::getDegree()
{
// Check for and remove any null terms.
std::map<unsigned, double>::iterator it = terms.begin();
while (it != terms.end())
{
if ((*it).second == 0.)
it = terms.erase(it);
else
++it;
}
// Return highest degree or zero.
if (!terms.empty())
return terms.rbegin()->first;
else
return 0;
}
// Evaluate polynomial at x.
double Polynomial::evaluate(double x)
{
double p{ 0. };
for (auto t : terms)
p += (pow(x, t.first) * t.second);
return p;
}
// Differentiate polynomial and return result.
Polynomial Polynomial::differentiate()
{
Polynomial derivative;
unsigned degree = getDegree();
if (degree == 0)
return derivative;
// derivative.degree = degree - 1;
// Calculate looping through all terms.
for (unsigned i = 0; i < degree; i++)
if (exists(i + 1))
derivative.terms[i] = (i + 1) * terms[i + 1];
return derivative;
}
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.