Sindbad~EG File Manager
/*
12.14.24 file created by M. Beeson for Web MathXpert
12.16.24 problemNumber+1 instead of problemNumber
12.25.24 moved lim_trig
1.14.25 added ordinarygraph_topics
1.18.25 added default parameter range for parametric_graph.
It was already there for polar_graph
1.18.25 modified randomProblem not to find the same problem twice in a row.
Added code to add a random fifth 'style' argument to a Riemann-sums problem.
1.19.25, corrected 1.20.25 added code to graphTopics to treat ODE, ODE2, HDE separately.
2.20.25 added mainchoiceToProblemtype
*/
#include <assert.h>
#include <string.h> // strcat
#include <stdlib.h>
#include <stdio.h> // printf, for debugging only
#include "randomProblem.h"
#include "tdefn.h"
#include "probtype.h"
#include "errbuf.h" // COMMENTARYBUFLENGTH
#include "ProblemLibrary.h"
#include "mainchoi.h"
static int complex_simplify_topics[] =
{
_complex_arithmetic,
_polar_form,
_de_moivre,
_complex_trig
};
static int simplify_topics[] =
{ _evaluate_numerically,
_simplify,
_simple_commondenom,
_advanced_commondenom,
_compound_fractions,
_simplify_polynomials,
_simple_exponents,
_simplify_rational_functions,
_negative_exponents,
_eliminate_negative_exponents,
_radicals,
_fractional_exponents,
_eliminate_fractional_exponents,
_simplify_roots_and_powers,
_simplify_roots_and_fractions,
_simplify_any_function,
_sigma_notation,
_binomial_theorem,
_multiply_polynomials,
_numerical_exponents,
_alg1_exponents,
_alg1_simplify,
_add_numerical_fractions,
_simplify_fractions,
_numerical_compound_fractions,
_alg1_compound_fractions,
_numerical_radicals,
_alg1_radicals,
_evaluate_trig,
_trig_product,
_trig_simplify,
_inverse_trig_functions,
_logarithms,
_change_log_base,
_exponentials,
_simplify_logpower,
_sigma_notation1
};
static int factor_topics[] =
{ _factor_quadratics, // 85
_simple_factoring, // 51
_advanced_factoring, // 88
_alg1_factor_quadratic, // 52
_factor_by_grouping // 53
};
static int complex_solve_equation_topics[] =
{
_complex_quadratics,
_complex_roots
// _complex_cubics // not yet ready for prime time
};
static int solve_equation_topics[] =
{ _solve_linear_equation,
_equations_containing_fractions,
_quadratic_formula,
_complete_the_square,
_solve_quadratic_equation,
_solve_higher_degree_equation,
_solve_rational_equation,
_solve_root_equation,
_solve_fractions_and_roots,
_cubic_one_root,
_solve_equation,
_simple_trig_eqn,
_trig_eqn,
_log_eqn,
_exp_eqn
};
static int inequalities_topics[] =
{ _alg1_linear_inequality,
_solve_linear_inequality,
_solve_polynomial_inequality,
_solve_rational_inequality,
_solve_root_inequality,
_absolute_value,
_alg1_absolute_value
};
static int linear_equations_topics[] =
{ _alg1_linear_systems,
_eqns_by_substitution,
_eqns_by_adding_eqns,
_eqns_in_matrix_form,
_gauss_jordan,
_eqns_by_matrix_inverse,
_cramers_rule
};
static int trig_identity_topics[] = // not only trig, but any identity to be verified
{ _verify_algebraic_identity,
_verify_alg2_identity,
_basic_trig,
_trig_addition,
_double_angle,
_half_angle,
_trig_identities,
_trig_factor,
_hyperfunctions,
_hyperfunctions2
};
static int limit_topics[] =
{
_polynomial_limits,
_simple_limits,
_rational_limits,
_limits_at_infinity,
_infinite_limits,
_lim_trig,
_lim_exp,
_lhopitals_rule,
_limleadingterm,
_limits_any_function
};
static int differentiate_topics[] =
{
_diff_polynomial,
_diff_basics,
_diff_trig,
_chain_rule,
_difreview,
_higher_order_diff,
_diff_exp,
_diff_logs,
// _logarithmic_differentiation,
// That topic has problems whose derivative MathXpert can't graph, so
// graphButton specifically doesn't try to do so. Therefore we omit them
// from the samples produced by arrowButton.
_diff_arctrig,
_diff_hyperbolic,
_diff_any_function,
_fundamental_theorem
};
static int differentiate_from_defn_topics[] =
{
_diff_from_def,
_diff_exp_from_defn
};
static int integration_topics[] =
{
_int_poly,
_simple_int,
// _fundamental_theorem, diff(integral(... is not accepted
_int_by_substitution,
_int_by_parts1,
_intreview,
_int_by_parts2,
_int_logs,
_trigpower_integrals,
_trig_substitution,
_integrate_rational_functions,
_rationalizing_substitutions,
_integrate_any_function
};
static int improper_integration_topics[] =
{
_improper_integrals
};
static int implicit_diff_topics[] =
{ _implicit_diff
};
static int related_rates_topics[] =
{
_related_rates
};
static int minmax_topics[] =
{
_minmax
};
static int test_convergence_topics[] =
{
_sum_series,
_integral_test,
_comparison_test,
_root_ratio_tests
};
static int powerseries_topics[] =
{
_power_series1,
_power_series2
};
static int ordinarygraph_topics[] =
{
_ordinary_graph,
_ordinary_graph_quadratic,
_ordinary_graph_polynomial,
_ordinary_graph_linear,
_ordinary_graph_rational,
_ordinary_graph_fractexp,
_ordinary_graph_exponential,
_ordinary_graph_log,
_ordinary_graph_algvariety,
_ordinary_graph_sincos,
_ordinary_graph_trig,
_ordinary_graph_arctrig,
_ordinary_graph_htrig,
_ordinary_graph_trigvariety,
_ordinary_graph_bessel
};
static int compare_same_topics[] =
{ _compare_same,
_graph_taylor,
_graph_fourier
};
static int compare_different_topics[] =
{ _compare_different
};
static int mc_ineq_topics[] =
{ _graph_ineq,
};
static int mc_set_topics[] =
{ _graph_set
};
static int relation_topics[] =
{ _graph_relation,
_graph_circle,
_graph_ellipse,
_graph_hyperbola
};
static int parametric_topics[] =
{ _parametric_graph
};
static int polar_topics[] =
{ _polar_graph
};
static int polyroot_topics[] =
{ _polyroots
};
static int comparederiv_topics[]=
{ _comparefandfprime
};
static int comparederivs_topics[]=
{ _comparefandfprime
};
static int ode_topics[] =
{ _solve_ode,
};
static int ode2_topics[] =
{ _solve_two_odes,
};
static int hde_topics[] =
{
_high_order_ode
};
static int riemannsums_topics[] =
{ _riemann_sums,
_trapezoid_rule,
_simpsons_rule
};
static int *symbolTopics(int problemtype, int *ntopics)
// return an array of topic numbers, and its dimension in ntopics
// These should be the topics associated with the given problemtype.
// problemtype should be one of the problemtypes used in the EnterXXX.php files.
// For symbolic problems these are problemtypes from probtype.h.
// For graphs, they are values of 'mainchoice', from mainchoi.h.
{
switch (problemtype)
{ case SIMPLIFY:
*ntopics = sizeof(simplify_topics)/sizeof(int);
return simplify_topics;
case FACTOR:
*ntopics = sizeof(factor_topics)/sizeof(int);
return factor_topics;
case SOLVE_EQUATION:
*ntopics = sizeof(solve_equation_topics)/sizeof(int);
return solve_equation_topics;
case INEQUALITIES:
*ntopics = sizeof(inequalities_topics)/sizeof(int);
return inequalities_topics;
case LINEAR_EQUATIONS:
*ntopics = sizeof(linear_equations_topics)/sizeof(int);
return linear_equations_topics;
case TRIG_IDENTITY:
*ntopics = sizeof(trig_identity_topics)/sizeof(int);
return trig_identity_topics;
case LIMITS:
*ntopics = sizeof(limit_topics)/sizeof(int);
return limit_topics;
case DIFFERENTIATE:
*ntopics = sizeof(differentiate_topics)/sizeof(int);
return differentiate_topics;
case DIFFERENTIATE_FROM_DEFN:
*ntopics = sizeof(differentiate_from_defn_topics)/sizeof(int);
return differentiate_from_defn_topics;
case INTEGRATION:
*ntopics = sizeof(integration_topics)/sizeof(int);
return integration_topics;
case IMPROPER_INTEGRATION:
*ntopics = sizeof(improper_integration_topics)/sizeof(int);
return improper_integration_topics;
case IMPLICIT_DIFF:
*ntopics = sizeof(implicit_diff_topics)/sizeof(int);
return implicit_diff_topics;
case RELATED_RATES:
*ntopics = sizeof(related_rates_topics)/sizeof(int);
return related_rates_topics;
case MINMAX:
*ntopics = sizeof(minmax_topics)/sizeof(int);
return minmax_topics;
case ADDSERIES: // fall through
case TESTCONVERGENCE:
*ntopics = sizeof(test_convergence_topics)/sizeof(int);
return test_convergence_topics;
case POWERSERIES:
*ntopics = sizeof(powerseries_topics)/sizeof(int);
return powerseries_topics;
default:
assert(0);
}
}
/*____________________________________________________________*/
static int *graphTopics(int mainchoice, int *ntopics)
{ switch(mainchoice)
{
case ORDINARY: // ORDINARY is a mainchoice, ORDINARY_GRAPH is a problemtype
*ntopics = sizeof(ordinarygraph_topics)/sizeof(int);
return ordinarygraph_topics;
case COMPARE_SAME: /* two graphs on the same axes */
*ntopics = sizeof(compare_same_topics)/sizeof(int);
return compare_same_topics;
case COMPARE_DIFFERENT: /* two graphs on different axes */
*ntopics = sizeof(compare_different_topics)/sizeof(int);
return compare_different_topics;
case MC_INEQ: /* y < f(x) or f(x) < y < g(x) */
*ntopics = sizeof(mc_ineq_topics)/sizeof(int);
return mc_ineq_topics;
case MC_SET: /* arbitrary relation with shaded interior */
*ntopics = sizeof(mc_set_topics)/sizeof(int);
return mc_set_topics;
case PARAMETRIC:
*ntopics = sizeof(parametric_topics)/sizeof(int);
return parametric_topics;
case POLAR_CIRCULAR: /* axes drawn as circles and radial lines */
*ntopics = sizeof(polar_topics)/sizeof(int);
return polar_topics;
case POLYROOT: /* graph complex roots of a polynomial */
*ntopics = sizeof(polyroot_topics)/sizeof(int);
return polyroot_topics;
case ODE:
*ntopics = sizeof(ode_topics)/sizeof(int);
return ode_topics;
case ODE2:
*ntopics = sizeof(ode2_topics)/sizeof(int);
return ode2_topics;
case HDE:
*ntopics = sizeof(hde_topics)/sizeof(int);
return hde_topics;
case RELATION: /* with equality rather than < */
*ntopics = sizeof(relation_topics)/sizeof(int);
return relation_topics;
case COMPAREDERIV:
*ntopics = sizeof(comparederiv_topics)/sizeof(int);
return comparederiv_topics;
case COMPAREDERIVS:
*ntopics = sizeof(comparederivs_topics)/sizeof(int);
return comparederivs_topics;
case RIEMANNSUMS: // fall through
case TRAPEZOIDRULE:
case SIMPSONSRULE:
*ntopics = sizeof(riemannsums_topics)/sizeof(int);
return riemannsums_topics;
default: assert(0);
};
}
static int mainchoiceToProblemtype(int mainchoice)
// mainchoice is for graphs, from mainchoi.h
// problemtype is from probtype.h
// return the problemtype that applies for the given mainchoice
{ switch(mainchoice)
{ case ORDINARY: return ORDINARY_GRAPH;
case COMPARE_SAME: return COMPARE_GRAPHS;
case COMPARE_DIFFERENT: return COMPARE_GRAPHS;
case MC_INEQ: return GRAPH_INEQUALITY;
case MC_SET: return GRAPH_RELATION;
case PARAMETRIC: return COMPARE_GRAPHS;
case POLAR_CIRCULAR: return COMPARE_GRAPHS;
case POLAR: return COMPARE_GRAPHS;
case POLYROOT: return POLYROOTS;
case ODE: return COMPARE_GRAPHS;
case ODE2: return COMPARE_GRAPHS;
case ODESYSTEM: return COMPARE_GRAPHS;
case HDE: return COMPARE_GRAPHS;
case RELATION: return GRAPH_RELATION;
case COMPAREDERIV: return COMPARE_GRAPHS;
case COMPAREDERIVS: return COMPARE_GRAPHS;
case RIEMANNSUMS: return COMPARE_GRAPHS;
case TRAPEZOIDRULE: return COMPARE_GRAPHS;
case SIMPSONSRULE: return COMPARE_GRAPHS;
default: assert(0);
# if 0
case CONTOUR 14 /* contour_plot */
case COMPLEX_CONTOUR 15
case SPACECURVE 21
case NONPARAMETRICSURFACE 22
case POLARNONPARAMETRICSURFACE 23
case PARAMETRICSURFACE 24
#endif
}
}
void randomProblem(int problemtype,int topicIn,int *newTopic,char *problemText,char *commentary)
/* problemText has 2048 bytes available when this is called in ProcessMessage
Given a problemtype and a topicIn that is one of the topics associated with
that problemtype. TopicIn is only of interest to determine whether complex numbers
are allowed. Pick a random newTopic suitable for this problemtype, and a
random problemText from the MathXpert Problem Library for that topic, and
return them in the last two variables. There is no possibility of failure,
so no return value is needed.
Input problemtype is expected to be from mainchoi.h on graph problemtypes,
and from probtype.h on symbolic problemtypes.
*/
{ int complex_allowed = 0;
static int lastTopic=0;
static int lastProblem=0;
start: // return here if we accidentally generate the same problem twice in a row.
if(COMPLEXTOPIC(topicIn))
complex_allowed = 1;
int nrealtopics;
char ranges[500];
int ncomplextopics=0;
int ntopics;
int *realtopics;
int *graphtopics;
int *complextopics = NULL;
if(topicIn == 0)
assert(0); // 0 is not a legal topic number!
if(GRAPH_TOPIC((topicIn)))
{ int mainchoice = problemtype;
graphtopics = graphTopics(mainchoice,&ntopics);
// see specs of this function
// now we have to get the problemtype that goes with this mainchoice
problemtype = mainchoiceToProblemtype(mainchoice);
}
else
{
realtopics = symbolTopics(problemtype,&nrealtopics);
if(complex_allowed && problemtype == SIMPLIFY)
{ complextopics = complex_simplify_topics;
ncomplextopics = sizeof(complex_simplify_topics)/sizeof(int);
}
if(complex_allowed && problemtype == SOLVE_EQUATION)
{ complextopics = complex_solve_equation_topics;
ncomplextopics = sizeof(complex_solve_equation_topics)/sizeof(int);
}
ntopics = nrealtopics + ncomplextopics;
}
// Now pick a random topic
// The random number generator is seeded in main(), so it reseeds
// whenever the Engine is restarted.
int index = random() % ntopics;
if(GRAPH_TOPIC((topicIn)))
*newTopic = graphtopics[index];
else
{ if (index < nrealtopics)
{ *newTopic = realtopics[index];
}
else
{ *newTopic = complextopics[index-nrealtopics];
}
}
// OK, now we have *newTopic. Next pick a random problem
int nProblems = getnProblems(*newTopic);
assert(nProblems >= 1); // newTopic shouldn't have zero problems
// now pick a random problem number
int problemnumber = random() % nProblems;
int err = getStoredProblem(*newTopic, problemnumber+1, problemText, ranges, commentary);
if(err)
{ // assert(0); problem numbers start with 1 and there are nProblems of them,
// so you can't fail, unless possibly getNProblems is mistaken.
problemnumber = random() % nProblems; // just try it again
}
if(ranges[0] == '\0' && *newTopic == _polar_graph) // 29
{ strcpy(ranges, "0 <= theta <= 2 pi");
// the stored problems actually have no ranges given, but they might have
// but the php page requires a range to be entered.
// All the stored problems have variable theta.
}
if(ranges[0] == '\0' && *newTopic == _parametric_graph) // 28
{ // A few of these problems do have stored ranges, hence the check of ranges[0]
// They all have independent variable t or theta
if(strstr(problemText,"theta"))
strcpy(ranges, "0 <= theta <= 2 pi");
else
strcpy(ranges, "0 <= t <= 2 pi");
}
if(ranges[0] != '\0')
{ // there were graph ranges
strcat(problemText, " , ");
/* | can occur for ABS or for OR.
semicolon occurs in IF. So we just use comma */
strncat(problemText, ranges, 500);
}
if(*newTopic == _riemann_sums)
{ // the stored problems do not have an entry for left, right, or center sums.
// we need to tack on a random choice.
// Namely, 0,1,2 for left, centered, or right
int style = random() % 3;
char buffer[32];
sprintf(buffer,",%d",style);
strcat(problemText,buffer);
}
if(*newTopic == _fundamental_theorem)
{ // strip off the outer diff, otherwise the second derivative is calculated
char *marker = strchr(problemText,'(') + 1;
if(!marker)
{ return; // assert(0);
}
memmove(problemText, marker, strlen(marker)); // Move memory safely
marker = problemText + strlen(problemText)-1; // points to last character
while(*marker != ',')
--marker;
*marker = '\0'; // replace comma with null char
}
printf("\nrandomProblem finds topic %d and problemnumber %d and text %s\n",*newTopic, problemnumber+1, problemText); // DEBUG
if(*newTopic == _alg1_linear_systems)
*newTopic = _eqns_by_adding_eqns;
// there's no selector with value _alg1_linear_systems.
// this enables us to still use those problems.
if(problemnumber+1 == lastProblem && *newTopic == lastTopic)
goto start; // prevent finding the same "random" problem twice in a row
lastProblem = problemnumber+1;
lastTopic = *newTopic;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists