moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kbruch / src / task.cpp
blobf98bf84ec88b9080d955478ab7d803524b230ab6
1 /***************************************************************************
2 task.cpp - source code of class task
3 -------------------
4 begin : Tue Nov 27 16:40:42 CET 2001
5 copyright : (C) 2001 by Sebastian Stein
6 email : seb.kde@hpfsc.de
7 ***************************************************************************/
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
18 #include <math.h>
20 #include <kdebug.h>
22 #include <time.h>
24 #include "task.h"
26 /** constructor of class task */
27 task::task()
29 srand(time(NULL));
30 #ifdef DEBUG
31 kdDebug() << "constructor task" << endl;
32 #endif
35 /** destructor of class task */
36 task::~task()
38 #ifdef DEBUG
39 kdDebug() << "destructor task" << endl;
40 #endif
43 /** create a task with random ratios and operations; the generated task
44 * can be customized by the given parameters:
45 * pmax_md: maximum main denominator
46 * pnr_ratios: number of ratios -> pnr_ratios - 1 operations
47 * padd_sub: if TRUE + and - are allowed operations
48 * pmul_div: if TRUE * and / are allowed operations */
49 void task::create_task(unsigned int pmax_md, short pnr_ratios,
50 short padd_sub, short pmul_div)
52 unsigned short max_product_length = 0;
53 int main_denominator = 1;
55 /* we say that if add/sub and mul/div are not allowed we want a task
56 * for add/sub only */
57 if (padd_sub == NO && pmul_div == NO)
58 padd_sub = YES;
62 /* delete a maybe given task */
63 ratio_vector.clear();
65 /* generate the operations and count the max. mul/div in one block */
66 max_product_length = make_operation(padd_sub, pmul_div, pnr_ratios);
68 #ifdef DEBUG
69 kdDebug() << "1: max_product_length: " << max_product_length << endl;
70 #endif
71 /* later we must be able to find a main denominator;
72 * so 2 ^ max_product_length couldn't be bigger than the max. denominator */
74 while ((unsigned int) pow(2, max_product_length) > pmax_md);
76 #ifdef DEBUG
77 kdDebug() << "2: max_product_length: " << max_product_length << endl;
78 #endif
80 /* find a main denominator */
81 main_denominator = make_main_dn(pmax_md, max_product_length);
83 #ifdef DEBUG
84 kdDebug() << "after make_main_dn()" << endl;
85 #endif
87 /* create the ratios' numerators */
88 make_numerators(main_denominator, pnr_ratios);
90 #ifdef DEBUG
91 kdDebug() << "after make_numerators()" << endl;
92 #endif
94 /* create the ratios' denominators */
95 make_denominators(main_denominator, pmax_md, pmul_div);
97 #ifdef DEBUG
98 kdDebug() << "main deno: " << main_denominator << endl;
99 kdDebug() << "prim fakt: " << prim_fac_vector.size() << endl;
100 #endif
102 return;
105 /** set ratio n in the ratio_vector */
106 void task::set_ratio_n(unsigned short number, int numerator, int denominator)
108 /* do not set something outside our vector */
109 if (number > ratio_vector.size() - 1)
110 number = 0;
111 ratio_vector[number].setNumerator(numerator); // set numerator
112 ratio_vector[number].setDenominator(denominator); // set denominator
113 return;
116 /** set ratio n in the ratio_vector */
117 void task::set_ratio_n(unsigned short number, ratio fraction)
119 /* do not set something outside our vector */
120 if (number > ratio_vector.size() - 1)
121 number = 0;
122 ratio_vector[number].setNumerator(fraction.numerator()); // set numerator
123 ratio_vector[number].setDenominator(fraction.denominator()); // set denominator
124 return;
127 /** returns the ratio given by number from the ratio_vector */
128 ratio task::get_ratio_n(unsigned short number) const
130 /* do not set something outside our vector */
131 if (number > ratio_vector.size() - 1)
132 number = 0;
133 return ratio_vector[number];
136 /** set operation given by the number in the op_vector */
137 void task::set_op_n(unsigned short number, short operation)
139 /* do not set something outside our vector */
140 if (number > op_vector.size() - 1)
141 number = 0;
142 op_vector[number] = operation;
143 return;
146 /** returns the operation given by number from the op_vector */
147 short task::get_op_n(unsigned short number) const
149 /* do not set something outside our vector */
150 if (number > op_vector.size() - 1)
151 number = 0;
152 return op_vector[number];
155 /** add a new ratio at the end of the ratio vector */
156 void task::add_ratio(ratio new_ratio)
158 ratio_vector.push_back(new_ratio);
159 return;
162 /** add a new ratio at the end of the ratio vector */
163 void task::add_ratio(int numerator, int denominator)
165 ratio new_ratio(numerator, denominator);
166 ratio_vector.push_back(new_ratio);
167 return;
170 /** add a new operation at the end of the operation vector */
171 void task::add_operation(short operation)
173 op_vector.push_back(operation);
174 return;
177 /** just outputs the whole given task to stdout; for debugging */
178 QTextStream & task::display(QTextStream & str)
180 /* this is our pointer on the ratio_vector, set it to the beginning */
181 RatioArray::iterator ratio_pointer = ratio_vector.begin();
183 /* this is our pointer on the op_vector, set it to the beginning */
184 ShortArray::iterator op_pointer = op_vector.begin();
186 /* we need this array to look up the fitting chars for the operations */
187 const char a[] = "+-*/";
189 /* check, if a qSetW() was given to the stream */
190 int weite = str.width();
191 int pweite = weite;
192 str << qSetW(0);
194 /* check, if ratio number and operation number fit together */
195 if (ratio_vector.size() != op_vector.size() + 1)
197 kdDebug() << "Number of ratios and operations do not fit." << endl;
198 return str;
201 while (pweite-- > 0)
202 str << " ";
204 /* display all numerators */
205 for (ratio_pointer = ratio_vector.begin();
206 ratio_pointer != ratio_vector.end(); ratio_pointer++)
208 str << qSetW(5) << ratio_pointer->numerator() << " ";
210 str << endl;
212 pweite = weite;
213 while (pweite-- > 0)
214 str << " ";
216 /* display all operations */
217 for (op_pointer = op_vector.begin();
218 op_pointer != op_vector.end(); op_pointer++)
220 str << " ----- " << a[*op_pointer];
222 str << " ----- = " << endl;
224 pweite = weite;
225 while (pweite-- > 0)
226 str << " ";
228 /* display all denominators */
229 for (ratio_pointer = ratio_vector.begin();
230 ratio_pointer != ratio_vector.end(); ratio_pointer++)
232 if (ratio_pointer == ratio_vector.end() - 1)
233 return str << qSetW(5) << ratio_pointer->denominator() << " ";
234 str << qSetW(5) << ratio_pointer->denominator() << " ";
236 return str;
239 /** solves the given task and returns the result as a ratio */
240 ratio task::solve()
242 ratio ergebnis(0, 1); /* that is the starting point */
244 /* this is our pointer on the ratio_vector, set it to the beginning */
245 RatioArray::iterator ratio_pointer = ratio_vector.begin();
247 /* add a temp operation at the beginning */
248 op_vector.insert(op_vector.begin(), ADD);
250 /* this is our pointer on the op_vector, set it to the beginning */
251 ShortArray::iterator op_pointer = op_vector.begin() + 1;
253 /* check, if ratio number and operation number fit together */
254 if (ratio_vector.size() != op_vector.size())
256 kdDebug() << "Number of ratios and operations do not fit." << endl;
257 return ergebnis;
262 /* we have to decide our next action by the given operation */
263 switch (*op_pointer)
265 case ADD :
266 case SUB :
267 switch(*(op_pointer - 1))
269 /* we only have to add/sub the next ratio */
270 case ADD :
271 ergebnis = ergebnis + *ratio_pointer++;
272 break;
273 case SUB :
274 ergebnis = ergebnis - *ratio_pointer++;
275 break;
277 break;
278 case MUL :
279 case DIV :
280 switch (*(op_pointer - 1))
282 /* the next ratio is a product, so we have to
283 * compute this product first and than add/sub it */
284 case ADD :
285 ergebnis = ergebnis +
286 product(ratio_pointer, op_pointer);
287 break;
288 case SUB :
289 ergebnis = ergebnis -
290 product(ratio_pointer, op_pointer);
291 break;
293 break;
295 /* check if we reached the and of the task */
296 if (ratio_pointer == ratio_vector.end())
297 break;
299 #ifdef DEBUG
300 kdDebug() << "Schleifenende" << endl;
301 #endif
304 while (++op_pointer != op_vector.end());
306 #ifdef DEBUG
308 kdDebug() << "after do while in solve()" << endl;
309 #endif
311 /* if the last operation was an add/sub we haven't add/subed it until now */
312 --op_pointer;
313 switch (*op_pointer)
315 case ADD :
316 ergebnis = ergebnis + *ratio_pointer;
317 break;
318 case SUB :
319 ergebnis = ergebnis - *ratio_pointer;
320 break;
323 /* erase the temp operation */
324 op_vector.erase(op_vector.begin());
326 /* before we return the result we have to reduce it */
327 ergebnis.reduce();
329 return ergebnis; /* return the solution */
332 /* returns the number of ratios in the vector */
333 int task::getNumberOfRatios() const
335 return ratio_vector.count();
338 /* returns the number of operations in the vector */
339 int task::getNumberOfOperations() const
341 return op_vector.count();
344 /** this function is called by the solving function to compute a given
345 * product (or div) and return the solution */
346 ratio task::product(RatioArray::iterator & ratio_pointer,
347 ShortArray::iterator & op_pointer)
349 /* the function's parameters are pointing to the next ratio;
350 * to the starting point of the product */
351 ratio product(ratio_pointer->numerator(), ratio_pointer->denominator());
353 #ifdef DEBUG
355 kdDebug() << "in product()" << endl;
356 #endif
358 ++ratio_pointer;
361 switch (*op_pointer)
363 case ADD :
364 case SUB :
365 return product; /* finished */
367 /* compute the next step of the product (or div) */
368 case MUL :
369 product = product * *ratio_pointer++;
370 ++op_pointer;
371 break;
372 case DIV :
373 product = product / *ratio_pointer++;
374 ++op_pointer;
375 break;
378 while (op_pointer != op_vector.end());
380 /* we get here if the product consists of the whole given task starting
381 * at the point given by the function's parameters */
382 return product;
385 /** generate the operations randomly; return how many mul or div
386 * are in one block */
387 unsigned short task::make_operation(short padd_sub, short pmul_div,
388 short pnr_ratios)
390 unsigned short max_product_length = 0;
391 unsigned short operations = 0;
393 /* this is our pointer on the op_vector, set it to the beginning */
394 ShortArray::iterator op_pointer;
396 /* we need this to generate the fitting operations */
397 if (padd_sub == YES)
398 operations += 2;
399 if (pmul_div == YES)
400 operations += 2;
402 /* clear the old operations */
403 op_vector.clear();
405 /* generate the operations */
406 for (short counter = 0; counter < pnr_ratios - 1; counter++)
407 op_vector.push_back(short((double(rand()) / RAND_MAX) * operations));
409 /* if we only wanted mul/div, operations was 2; but we want values
410 * for the operations with 2 and 3 so we have to add 2 */
411 if (padd_sub == NO && pmul_div == YES)
413 /* loop through all operations and add 2, so that the operations
414 * are interpreted as mul/div and not add/sub */
415 for (op_pointer = op_vector.begin();
416 op_pointer != op_vector.end(); op_pointer++)
417 *op_pointer += 2;
420 if (pmul_div == YES)
422 short flag_counter = 0;
424 /* loop through all operations */
425 for (op_pointer = op_vector.begin();
426 op_pointer != op_vector.end(); op_pointer++)
428 /* look if we got a mul/div or add/sub */
429 if (*op_pointer == DIV || *op_pointer == MUL)
431 flag_counter++;
433 else
435 /* we have to decide, if this was the end of a mul/div block or
436 * just another add/sub */
437 if (flag_counter > 0)
439 /* it was the end of a mul/div block; lets look if it was
440 * longer than the blocks before and save it; than restart */
441 if (flag_counter > max_product_length)
442 max_product_length = flag_counter;
443 flag_counter = 0;
444 } /* if (flag_counter > 0) */
445 } /* if (*op_pointer == DIV || *op_pointer == MUL) */
446 } /* for (op_pointer = op_vector.begin(); ...) */
448 /* just to correct the things a little bit if the last operation was a
449 * mul/div as well */
450 if (flag_counter > max_product_length)
451 max_product_length = flag_counter;
452 max_product_length++;
454 else
455 { /* if (pmul_div == YES) */
456 /* a task is given only with add/sub ops; so we want a max.
457 * of pnr_ratios / 2 + 1 prime factors, but at least */
458 max_product_length = (unsigned short) (float(pnr_ratios) / 2) + 1;
459 if (max_product_length < 2)
460 max_product_length = 2;
461 } /* if (pmul_div == YES) */
463 return max_product_length;
466 /** find a denominator for the task */
467 int task::make_main_dn(unsigned int pmax_md, unsigned short max_product_length)
469 int denominator;
471 /* find a main denominator in the given limits by pmax_md and check
472 * if the main denominator has enough prime factors */
475 denominator = int(((double(rand()) / RAND_MAX) * pmax_md) + 1);
477 while ((pmax_md < 1) ||
478 (prim_factor_nr(denominator) < max_product_length));
480 return denominator;
483 /** returns the count number's prime factors and stores the prime factors
484 * in the prim_fac_vektor vektor */
485 unsigned short task::prim_factor_nr(int number)
487 unsigned int tmp_number = number;
488 primenumber primenumber;
489 Tprime_factor prim_fac_struct;
491 /* delete all the prime factors of the old main denominator */
492 prim_fac_vector.clear();
494 /* test if we can find prime factors */
495 for (primenumber.move_first(); primenumber.get_current() <= tmp_number; )
497 /* if the current selected prime number is a divisor */
498 if (tmp_number % primenumber.get_current() != 0)
500 primenumber.move_forward(); /* no, test next one */
502 else
504 /* yes, we found a new prime factor; so first we use the divisor */
505 tmp_number = int(tmp_number / primenumber.get_current());
507 /* now we add the prime factor to our prime factor vector */
508 prim_fac_struct.factor = primenumber.get_current();
509 prim_fac_struct.flag = UNUSED;
510 prim_fac_vector.push_back(prim_fac_struct);
513 #ifdef DEBUG
514 PrimeFactorArray::iterator prim_fac_pointer = prim_fac_vector.begin();
515 kdDebug() << "Primfaktoren von: " << number << endl;
516 for (prim_fac_pointer = prim_fac_vector.begin();
517 prim_fac_pointer != prim_fac_vector.end();
518 prim_fac_pointer++)
519 kdDebug() << (*prim_fac_pointer).factor << endl;
520 kdDebug() << "Anzahl: " << prim_fac_vector.size() << endl;
521 #endif
523 return prim_fac_vector.size();
526 /** set the numerators randomly */
527 void task::make_numerators(int main_denominator, short pnr_ratios)
529 /* I think it is to easy to deal with ratios like 1/1 or 4/4; so
530 * I limit the maximum of a numerator */
531 int max_numerator = int(main_denominator * float(0.7));
533 /* add a new ratio to the task and compute the numerator randomly */
534 for (short tmpcounter = 0; tmpcounter < pnr_ratios; tmpcounter++)
536 (*this).add_ratio(int((double(rand()) / RAND_MAX)
537 * max_numerator) + 1, 1);
539 return;
542 /** create the ratios' denominators */
543 void task::make_denominators(int main_denominator, short pmax_md,
544 short pmul_div)
546 /* this is our pointer on the ratio_vector, set it to the beginning */
547 RatioArray::iterator ratio_pointer = ratio_vector.begin();
549 /* this is our pointer on the op_vector, set it to the beginning */
550 ShortArray::iterator op_pointer = op_vector.begin() + 1;
552 /* this is a pointer on the array with the prime factors of the main
553 * denominator */
554 PrimeFactorArray::iterator prim_fac_pointer;
556 unsigned short unused_fac = prim_fac_vector.size();
557 unsigned short next_fac;
558 unsigned short tmp_counter;
559 int tmp_deno;
561 /* check, if ratio number and operation number fit together */
562 if (ratio_vector.size() != op_vector.size() + 1)
564 kdDebug() << "Number of ratios and operations do not fit." << endl;
565 return;
568 /* first make all denominators */
569 for (ratio_pointer = ratio_vector.begin();
570 ratio_pointer != ratio_vector.end(); ratio_pointer++)
574 tmp_deno = int((double(rand()) / RAND_MAX) * pmax_md) + 1;
576 while (main_denominator % tmp_deno != 0);
577 (*ratio_pointer).setDenominator(tmp_deno);
580 /* if the ratio is connected to a mul or div operation, we have to do some
581 * extra work and regenerate the denominators */
582 if (pmul_div == YES)
584 /* lets loop through all ratios and check, if there is a mul/div
585 * after the ratio */
586 ratio_pointer = ratio_vector.begin();
587 op_pointer = op_vector.begin();
590 if (*op_pointer == MUL || *op_pointer == DIV)
592 /* yes, there is a mul/div after the ratio;
593 * reset the prime number structure */
594 for (prim_fac_pointer = prim_fac_vector.begin();
595 prim_fac_pointer != prim_fac_vector.end();
596 prim_fac_pointer++)
597 (*prim_fac_pointer).flag = UNUSED;
599 /* how many prime factors are avaible? */
600 unused_fac = prim_fac_vector.size() - 1;
602 /* now loop through this mul/div section until we find a add/sub */
605 /* the prim_fac_vector is sorted, but we do not want the
606 * factors in this sorted way as our denominators;
607 * so we choose one randomly */
608 next_fac = (unsigned short)((double(rand()) / RAND_MAX)
609 * unused_fac);
610 tmp_counter = 0;
612 /* check the prime factors, if they are unused */
613 for (prim_fac_pointer = prim_fac_vector.begin();
614 prim_fac_pointer != prim_fac_vector.end();
615 prim_fac_pointer++)
617 if ((*prim_fac_pointer).flag == UNUSED)
619 tmp_counter++; /* we found a unused factor */
621 /* we found the factor, which we have chosen randomly */
622 if (tmp_counter > next_fac)
623 break;
625 /* mark the factor as used, so we can not use it again in
626 * this mul/div section */
627 (*prim_fac_pointer).flag = USED;
629 /* store the factor as our new denominator for this ratio */
630 (*ratio_pointer).setDenominator((*prim_fac_pointer).factor, false);
631 unused_fac--; /* now there is one factor less avaible */
633 /* move to the next ratio */
634 ratio_pointer++;
635 op_pointer++;
637 while ((op_pointer != op_vector.end()) &&
638 (*op_pointer == MUL || *op_pointer == DIV));
640 /* we always miss to set the last ratio in a mul/div section;
641 * so we have to fix this here */
642 if (ratio_pointer != ratio_vector.end())
644 /* the prim_fac_vector is sorted, but we do not want the
645 * factors in this sorted way as our denominators;
646 * so we choose one randomly */
647 next_fac = (unsigned short)((double(rand()) / RAND_MAX)
648 * unused_fac);
649 tmp_counter = 0;
651 /* check the prime factors, if they are unused */
652 for (prim_fac_pointer = prim_fac_vector.begin();
653 prim_fac_pointer != prim_fac_vector.end();
654 prim_fac_pointer++)
656 if ((*prim_fac_pointer).flag == UNUSED)
658 tmp_counter++; /* we found a unused factor */
660 /* we found the factor, which we have chosen randomly */
661 if (tmp_counter > next_fac)
662 break;
664 /* mark the factor as used, so we can not use it again in
665 * this mul/div section */
666 (*prim_fac_pointer).flag = USED;
668 /* store the factor as our new denominator for this ratio */
669 (*ratio_pointer).setDenominator((*prim_fac_pointer).factor, false);
670 unused_fac--; /* now there is one factor less avaible */
672 /* move to the next ratio */
673 ratio_pointer++;
674 op_pointer++;
677 else
678 { /* if (*op_pointer == MUL || ...) */
679 ratio_pointer++;
680 op_pointer++;
683 while (ratio_pointer != ratio_vector.end() &&
684 op_pointer != op_vector.end());
686 /* now we will swap all ratios, if there is a div in front of */
687 ratio_pointer = ratio_vector.begin();
688 ratio_pointer++;
690 for (op_pointer = op_vector.begin(); op_pointer != op_vector.end();
691 op_pointer++)
693 if (*op_pointer == DIV)
695 (*ratio_pointer).reziproc();
697 ratio_pointer++;
699 } /* if (pmul_div == YES) */
701 return;
705 /* ------ some prototyps of non class functions ------ */
707 /** it is possible to code: cout << task_object << endl; */
708 QTextStream & operator<<(QTextStream & str, task & ptask)
710 return ptask.display(str);