diff options
Diffstat (limited to 'kbruch/src/task.cpp')
-rw-r--r-- | kbruch/src/task.cpp | 712 |
1 files changed, 712 insertions, 0 deletions
diff --git a/kbruch/src/task.cpp b/kbruch/src/task.cpp new file mode 100644 index 00000000..4c3748a3 --- /dev/null +++ b/kbruch/src/task.cpp @@ -0,0 +1,712 @@ +/*************************************************************************** + task.cpp - source code of class task + ------------------- + begin : Tue Nov 27 16:40:42 CET 2001 + copyright : (C) 2001 by Sebastian Stein + email : seb.kde@hpfsc.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <math.h> +#include <stdlib.h> + +#include <kdebug.h> + +#include <time.h> + +#include "task.h" + +/** constructor of class task */ +task::task() +{ + srand(time(NULL)); +#ifdef DEBUG + kdDebug() << "constructor task" << endl; +#endif +} + +/** destructor of class task */ +task::~task() +{ +#ifdef DEBUG + kdDebug() << "destructor task" << endl; +#endif +} + +/** create a task with random ratios and operations; the generated task + * can be customized by the given parameters: + * pmax_md: maximum main denominator + * pnr_ratios: number of ratios -> pnr_ratios - 1 operations + * padd_sub: if TRUE + and - are allowed operations + * pmul_div: if TRUE * and / are allowed operations */ +void task::create_task(unsigned int pmax_md, short pnr_ratios, + short padd_sub, short pmul_div) +{ + unsigned short max_product_length = 0; + int main_denominator = 1; + + /* we say that if add/sub and mul/div are not allowed we want a task + * for add/sub only */ + if (padd_sub == NO && pmul_div == NO) + padd_sub = YES; + + do + { + /* delete a maybe given task */ + ratio_vector.clear(); + + /* generate the operations and count the max. mul/div in one block */ + max_product_length = make_operation(padd_sub, pmul_div, pnr_ratios); + +#ifdef DEBUG + kdDebug() << "1: max_product_length: " << max_product_length << endl; +#endif + /* later we must be able to find a main denominator; + * so 2 ^ max_product_length couldn't be bigger than the max. denominator */ + } + while ((unsigned int) pow(2, max_product_length) > pmax_md); + +#ifdef DEBUG + kdDebug() << "2: max_product_length: " << max_product_length << endl; +#endif + + /* find a main denominator */ + main_denominator = make_main_dn(pmax_md, max_product_length); + +#ifdef DEBUG + kdDebug() << "after make_main_dn()" << endl; +#endif + + /* create the ratios' numerators */ + make_numerators(main_denominator, pnr_ratios); + +#ifdef DEBUG + kdDebug() << "after make_numerators()" << endl; +#endif + + /* create the ratios' denominators */ + make_denominators(main_denominator, pmax_md, pmul_div); + +#ifdef DEBUG + kdDebug() << "main deno: " << main_denominator << endl; + kdDebug() << "prim fakt: " << prim_fac_vector.size() << endl; +#endif + + return; +} + +/** set ratio n in the ratio_vector */ +void task::set_ratio_n(unsigned short number, int numerator, int denominator) +{ + /* do not set something outside our vector */ + if (number > ratio_vector.size() - 1) + number = 0; + ratio_vector[number].setNumerator(numerator); // set numerator + ratio_vector[number].setDenominator(denominator); // set denominator + return; +} + +/** set ratio n in the ratio_vector */ +void task::set_ratio_n(unsigned short number, ratio fraction) +{ + /* do not set something outside our vector */ + if (number > ratio_vector.size() - 1) + number = 0; + ratio_vector[number].setNumerator(fraction.numerator()); // set numerator + ratio_vector[number].setDenominator(fraction.denominator()); // set denominator + return; +} + +/** returns the ratio given by number from the ratio_vector */ +ratio task::get_ratio_n(unsigned short number) const +{ + /* do not set something outside our vector */ + if (number > ratio_vector.size() - 1) + number = 0; + return ratio_vector[number]; +} + +/** set operation given by the number in the op_vector */ +void task::set_op_n(unsigned short number, short operation) +{ + /* do not set something outside our vector */ + if (number > op_vector.size() - 1) + number = 0; + op_vector[number] = operation; + return; +} + +/** returns the operation given by number from the op_vector */ +short task::get_op_n(unsigned short number) const +{ + /* do not set something outside our vector */ + if (number > op_vector.size() - 1) + number = 0; + return op_vector[number]; +} + +/** add a new ratio at the end of the ratio vector */ +void task::add_ratio(ratio new_ratio) +{ + ratio_vector.push_back(new_ratio); + return; +} + +/** add a new ratio at the end of the ratio vector */ +void task::add_ratio(int numerator, int denominator) +{ + ratio new_ratio(numerator, denominator); + ratio_vector.push_back(new_ratio); + return; +} + +/** add a new operation at the end of the operation vector */ +void task::add_operation(short operation) +{ + op_vector.push_back(operation); + return; +} + +/** just outputs the whole given task to stdout; for debugging */ +QTextStream & task::display(QTextStream & str) +{ + /* this is our pointer on the ratio_vector, set it to the beginning */ + RatioArray::iterator ratio_pointer = ratio_vector.begin(); + + /* this is our pointer on the op_vector, set it to the beginning */ + ShortArray::iterator op_pointer = op_vector.begin(); + + /* we need this array to look up the fitting chars for the operations */ + const char a[] = "+-*/"; + + /* check, if a qSetW() was given to the stream */ + int weite = str.width(); + int pweite = weite; + str << qSetW(0); + + /* check, if ratio number and operation number fit together */ + if (ratio_vector.size() != op_vector.size() + 1) + { + kdDebug() << "Number of ratios and operations do not fit." << endl; + return str; + } + + while (pweite-- > 0) + str << " "; + + /* display all numerators */ + for (ratio_pointer = ratio_vector.begin(); + ratio_pointer != ratio_vector.end(); ratio_pointer++) + { + str << qSetW(5) << ratio_pointer->numerator() << " "; + } + str << endl; + + pweite = weite; + while (pweite-- > 0) + str << " "; + + /* display all operations */ + for (op_pointer = op_vector.begin(); + op_pointer != op_vector.end(); op_pointer++) + { + str << " ----- " << a[*op_pointer]; + } + str << " ----- = " << endl; + + pweite = weite; + while (pweite-- > 0) + str << " "; + + /* display all denominators */ + for (ratio_pointer = ratio_vector.begin(); + ratio_pointer != ratio_vector.end(); ratio_pointer++) + { + if (ratio_pointer == ratio_vector.end() - 1) + return str << qSetW(5) << ratio_pointer->denominator() << " "; + str << qSetW(5) << ratio_pointer->denominator() << " "; + } + return str; +} + +/** solves the given task and returns the result as a ratio */ +ratio task::solve() +{ + ratio ergebnis(0, 1); /* that is the starting point */ + + /* this is our pointer on the ratio_vector, set it to the beginning */ + RatioArray::iterator ratio_pointer = ratio_vector.begin(); + + /* add a temp operation at the beginning */ + op_vector.insert(op_vector.begin(), ADD); + + /* this is our pointer on the op_vector, set it to the beginning */ + ShortArray::iterator op_pointer = op_vector.begin() + 1; + + /* check, if ratio number and operation number fit together */ + if (ratio_vector.size() != op_vector.size()) + { + kdDebug() << "Number of ratios and operations do not fit." << endl; + return ergebnis; + } + + do + { + /* we have to decide our next action by the given operation */ + switch (*op_pointer) + { + case ADD : + case SUB : + switch(*(op_pointer - 1)) + { + /* we only have to add/sub the next ratio */ + case ADD : + ergebnis = ergebnis + *ratio_pointer++; + break; + case SUB : + ergebnis = ergebnis - *ratio_pointer++; + break; + } + break; + case MUL : + case DIV : + switch (*(op_pointer - 1)) + { + /* the next ratio is a product, so we have to + * compute this product first and than add/sub it */ + case ADD : + ergebnis = ergebnis + + product(ratio_pointer, op_pointer); + break; + case SUB : + ergebnis = ergebnis - + product(ratio_pointer, op_pointer); + break; + } + break; + } + /* check if we reached the and of the task */ + if (ratio_pointer == ratio_vector.end()) + break; + +#ifdef DEBUG + kdDebug() << "Schleifenende" << endl; +#endif + + } + while (++op_pointer != op_vector.end()); + +#ifdef DEBUG + + kdDebug() << "after do while in solve()" << endl; +#endif + + /* if the last operation was an add/sub we haven't add/subed it until now */ + --op_pointer; + switch (*op_pointer) + { + case ADD : + ergebnis = ergebnis + *ratio_pointer; + break; + case SUB : + ergebnis = ergebnis - *ratio_pointer; + break; + } + + /* erase the temp operation */ + op_vector.erase(op_vector.begin()); + + /* before we return the result we have to reduce it */ + ergebnis.reduce(); + + return ergebnis; /* return the solution */ +} + +/* returns the number of ratios in the vector */ +int task::getNumberOfRatios() const +{ + return ratio_vector.count(); +} + +/* returns the number of operations in the vector */ +int task::getNumberOfOperations() const +{ + return op_vector.count(); +} + +/** this function is called by the solving function to compute a given + * product (or div) and return the solution */ +ratio task::product(RatioArray::iterator & ratio_pointer, + ShortArray::iterator & op_pointer) +{ + /* the function's parameters are pointing to the next ratio; + * to the starting point of the product */ + ratio product(ratio_pointer->numerator(), ratio_pointer->denominator()); + +#ifdef DEBUG + + kdDebug() << "in product()" << endl; +#endif + + ++ratio_pointer; + do + { + switch (*op_pointer) + { + case ADD : + case SUB : + return product; /* finished */ + + /* compute the next step of the product (or div) */ + case MUL : + product = product * *ratio_pointer++; + ++op_pointer; + break; + case DIV : + product = product / *ratio_pointer++; + ++op_pointer; + break; + } + } + while (op_pointer != op_vector.end()); + + /* we get here if the product consists of the whole given task starting + * at the point given by the function's parameters */ + return product; +} + +/** generate the operations randomly; return how many mul or div + * are in one block */ +unsigned short task::make_operation(short padd_sub, short pmul_div, + short pnr_ratios) +{ + unsigned short max_product_length = 0; + unsigned short operations = 0; + + /* this is our pointer on the op_vector, set it to the beginning */ + ShortArray::iterator op_pointer; + + /* we need this to generate the fitting operations */ + if (padd_sub == YES) + operations += 2; + if (pmul_div == YES) + operations += 2; + + /* clear the old operations */ + op_vector.clear(); + + /* generate the operations */ + for (short counter = 0; counter < pnr_ratios - 1; counter++) + op_vector.push_back(short((double(rand()) / RAND_MAX) * operations)); + + /* if we only wanted mul/div, operations was 2; but we want values + * for the operations with 2 and 3 so we have to add 2 */ + if (padd_sub == NO && pmul_div == YES) + { + /* loop through all operations and add 2, so that the operations + * are interpreted as mul/div and not add/sub */ + for (op_pointer = op_vector.begin(); + op_pointer != op_vector.end(); op_pointer++) + *op_pointer += 2; + } + + if (pmul_div == YES) + { + short flag_counter = 0; + + /* loop through all operations */ + for (op_pointer = op_vector.begin(); + op_pointer != op_vector.end(); op_pointer++) + { + /* look if we got a mul/div or add/sub */ + if (*op_pointer == DIV || *op_pointer == MUL) + { + flag_counter++; + } + else + { + /* we have to decide, if this was the end of a mul/div block or + * just another add/sub */ + if (flag_counter > 0) + { + /* it was the end of a mul/div block; lets look if it was + * longer than the blocks before and save it; than restart */ + if (flag_counter > max_product_length) + max_product_length = flag_counter; + flag_counter = 0; + } /* if (flag_counter > 0) */ + } /* if (*op_pointer == DIV || *op_pointer == MUL) */ + } /* for (op_pointer = op_vector.begin(); ...) */ + + /* just to correct the things a little bit if the last operation was a + * mul/div as well */ + if (flag_counter > max_product_length) + max_product_length = flag_counter; + max_product_length++; + } + else + { /* if (pmul_div == YES) */ + /* a task is given only with add/sub ops; so we want a max. + * of pnr_ratios / 2 + 1 prime factors, but at least */ + max_product_length = (unsigned short) (float(pnr_ratios) / 2) + 1; + if (max_product_length < 2) + max_product_length = 2; + } /* if (pmul_div == YES) */ + + return max_product_length; +} + +/** find a denominator for the task */ +int task::make_main_dn(unsigned int pmax_md, unsigned short max_product_length) +{ + int denominator; + + /* find a main denominator in the given limits by pmax_md and check + * if the main denominator has enough prime factors */ + do + { + denominator = int(((double(rand()) / RAND_MAX) * pmax_md) + 1); + } + while ((pmax_md < 1) || + (prim_factor_nr(denominator) < max_product_length)); + + return denominator; +} + +/** returns the count number's prime factors and stores the prime factors + * in the prim_fac_vektor vektor */ +unsigned short task::prim_factor_nr(int number) +{ + unsigned int tmp_number = number; + primenumber primenumber; + Tprime_factor prim_fac_struct; + + /* delete all the prime factors of the old main denominator */ + prim_fac_vector.clear(); + + /* test if we can find prime factors */ + for (primenumber.move_first(); primenumber.get_current() <= tmp_number; ) + { + /* if the current selected prime number is a divisor */ + if (tmp_number % primenumber.get_current() != 0) + { + primenumber.move_forward(); /* no, test next one */ + } + else + { + /* yes, we found a new prime factor; so first we use the divisor */ + tmp_number = int(tmp_number / primenumber.get_current()); + + /* now we add the prime factor to our prime factor vector */ + prim_fac_struct.factor = primenumber.get_current(); + prim_fac_struct.flag = UNUSED; + prim_fac_vector.push_back(prim_fac_struct); + } + } +#ifdef DEBUG + PrimeFactorArray::iterator prim_fac_pointer = prim_fac_vector.begin(); + kdDebug() << "Primfaktoren von: " << number << endl; + for (prim_fac_pointer = prim_fac_vector.begin(); + prim_fac_pointer != prim_fac_vector.end(); + prim_fac_pointer++) + kdDebug() << (*prim_fac_pointer).factor << endl; + kdDebug() << "Anzahl: " << prim_fac_vector.size() << endl; +#endif + + return prim_fac_vector.size(); +} + +/** set the numerators randomly */ +void task::make_numerators(int main_denominator, short pnr_ratios) +{ + /* I think it is to easy to deal with ratios like 1/1 or 4/4; so + * I limit the maximum of a numerator */ + int max_numerator = int(main_denominator * float(0.7)); + + /* add a new ratio to the task and compute the numerator randomly */ + for (short tmpcounter = 0; tmpcounter < pnr_ratios; tmpcounter++) + { + (*this).add_ratio(int((double(rand()) / RAND_MAX) + * max_numerator) + 1, 1); + } + return; +} + +/** create the ratios' denominators */ +void task::make_denominators(int main_denominator, short pmax_md, + short pmul_div) +{ + /* this is our pointer on the ratio_vector, set it to the beginning */ + RatioArray::iterator ratio_pointer = ratio_vector.begin(); + + /* this is our pointer on the op_vector, set it to the beginning */ + ShortArray::iterator op_pointer = op_vector.begin() + 1; + + /* this is a pointer on the array with the prime factors of the main + * denominator */ + PrimeFactorArray::iterator prim_fac_pointer; + + unsigned short unused_fac = prim_fac_vector.size(); + unsigned short next_fac; + unsigned short tmp_counter; + int tmp_deno; + + /* check, if ratio number and operation number fit together */ + if (ratio_vector.size() != op_vector.size() + 1) + { + kdDebug() << "Number of ratios and operations do not fit." << endl; + return; + } + + /* first make all denominators */ + for (ratio_pointer = ratio_vector.begin(); + ratio_pointer != ratio_vector.end(); ratio_pointer++) + { + do + { + tmp_deno = int((double(rand()) / RAND_MAX) * pmax_md) + 1; + } + while (main_denominator % tmp_deno != 0); + (*ratio_pointer).setDenominator(tmp_deno); + } + + /* if the ratio is connected to a mul or div operation, we have to do some + * extra work and regenerate the denominators */ + if (pmul_div == YES) + { + /* lets loop through all ratios and check, if there is a mul/div + * after the ratio */ + ratio_pointer = ratio_vector.begin(); + op_pointer = op_vector.begin(); + do + { + if (*op_pointer == MUL || *op_pointer == DIV) + { + /* yes, there is a mul/div after the ratio; + * reset the prime number structure */ + for (prim_fac_pointer = prim_fac_vector.begin(); + prim_fac_pointer != prim_fac_vector.end(); + prim_fac_pointer++) + (*prim_fac_pointer).flag = UNUSED; + + /* how many prime factors are avaible? */ + unused_fac = prim_fac_vector.size() - 1; + + /* now loop through this mul/div section until we find a add/sub */ + do + { + /* the prim_fac_vector is sorted, but we do not want the + * factors in this sorted way as our denominators; + * so we choose one randomly */ + next_fac = (unsigned short)((double(rand()) / RAND_MAX) + * unused_fac); + tmp_counter = 0; + + /* check the prime factors, if they are unused */ + for (prim_fac_pointer = prim_fac_vector.begin(); + prim_fac_pointer != prim_fac_vector.end(); + prim_fac_pointer++) + { + if ((*prim_fac_pointer).flag == UNUSED) + { + tmp_counter++; /* we found a unused factor */ + } + /* we found the factor, which we have chosen randomly */ + if (tmp_counter > next_fac) + break; + } + /* mark the factor as used, so we can not use it again in + * this mul/div section */ + (*prim_fac_pointer).flag = USED; + + /* store the factor as our new denominator for this ratio */ + (*ratio_pointer).setDenominator((*prim_fac_pointer).factor, false); + unused_fac--; /* now there is one factor less avaible */ + + /* move to the next ratio */ + ratio_pointer++; + op_pointer++; + } + while ((op_pointer != op_vector.end()) && + (*op_pointer == MUL || *op_pointer == DIV)); + + /* we always miss to set the last ratio in a mul/div section; + * so we have to fix this here */ + if (ratio_pointer != ratio_vector.end()) + { + /* the prim_fac_vector is sorted, but we do not want the + * factors in this sorted way as our denominators; + * so we choose one randomly */ + next_fac = (unsigned short)((double(rand()) / RAND_MAX) + * unused_fac); + tmp_counter = 0; + + /* check the prime factors, if they are unused */ + for (prim_fac_pointer = prim_fac_vector.begin(); + prim_fac_pointer != prim_fac_vector.end(); + prim_fac_pointer++) + { + if ((*prim_fac_pointer).flag == UNUSED) + { + tmp_counter++; /* we found a unused factor */ + } + /* we found the factor, which we have chosen randomly */ + if (tmp_counter > next_fac) + break; + } + /* mark the factor as used, so we can not use it again in + * this mul/div section */ + (*prim_fac_pointer).flag = USED; + + /* store the factor as our new denominator for this ratio */ + (*ratio_pointer).setDenominator((*prim_fac_pointer).factor, false); + unused_fac--; /* now there is one factor less avaible */ + + /* move to the next ratio */ + ratio_pointer++; + op_pointer++; + } + } + else + { /* if (*op_pointer == MUL || ...) */ + ratio_pointer++; + op_pointer++; + } + } + while (ratio_pointer != ratio_vector.end() && + op_pointer != op_vector.end()); + + /* now we will swap all ratios, if there is a div in front of */ + ratio_pointer = ratio_vector.begin(); + ratio_pointer++; + + for (op_pointer = op_vector.begin(); op_pointer != op_vector.end(); + op_pointer++) + { + if (*op_pointer == DIV) + { + (*ratio_pointer).reziproc(); + } + ratio_pointer++; + } + } /* if (pmul_div == YES) */ + + return; +} + + +/* ------ some prototyps of non class functions ------ */ + +/** it is possible to code: cout << task_object << endl; */ +QTextStream & operator<<(QTextStream & str, task & ptask) +{ + return ptask.display(str); +} |