1 /*
2     KmPlot - a math. function plotter for the KDE-Desktop
3 
4     SPDX-FileCopyrightText: 1998, 1999, 2000, 2002 Klaus-Dieter Möller <kd.moeller@t-online.de>
5     SPDX-FileCopyrightText: 2006, 2007 David Saxton <david@bluehaze.org>
6 
7     This file is part of the KDE Project.
8     KmPlot is part of the KDE-EDU Project.
9 
10     SPDX-License-Identifier: GPL-2.0-or-later
11 
12 */
13 
14 #include "xparser.h"
15 
16 #include <kmplot/config-kmplot.h>
17 
18 // local includes
19 #include "parseradaptor.h"
20 #include "maindlg.h"
21 
22 // KDE includes
23 #include <KLocalizedString>
24 #include <KMessageBox>
25 
26 #include <QList>
27 
28 #include <assert.h>
29 #include <cmath>
30 #ifdef HAVE_IEEEFP_H
31 #include <ieeefp.h>
32 #endif
33 
34 
35 XParser * XParser::m_self = 0;
36 
self()37 XParser * XParser::self()
38 {
39 	if ( !m_self )
40 		m_self = new XParser();
41 
42 	return m_self;
43 }
44 
45 
XParser()46 XParser::XParser()
47 {
48 	differentialFinite = true;
49 	differentialDiverge = 0;
50 
51 	new ParserAdaptor(this);
52 	QDBusConnection::sessionBus().registerObject(QStringLiteral("/parser"), this);
53 }
54 
~XParser()55 XParser::~XParser()
56 {
57 }
58 
getext(Function * item,const QString & fstr)59 bool XParser::getext( Function *item, const QString &fstr )
60 {
61   	bool errflg = false;
62    	int p1, p2, p3, pe;
63 	QString tstr;
64 	pe = fstr.length();
65 	if ( fstr.indexOf( 'N' ) != -1 )
66 		item->plotAppearance( Function::Derivative0 ).visible = false;
67 	else
68 	{
69 		if ( fstr.indexOf( QLatin1String("A1") ) != -1 )
70 			item->plotAppearance( Function::Derivative1 ).visible = true;
71 		if ( fstr.indexOf( QLatin1String("A2") ) != -1 )
72 			item->plotAppearance( Function::Derivative2 ).visible = true;
73 	}
74 	switch ( fstr[0].unicode() )
75 	{
76 		case 'x':
77 		case 'y':
78 		case 'r':
79 			item->plotAppearance( Function::Derivative1 ).visible = item->plotAppearance( Function::Derivative2 ).visible = false;
80 	}
81 
82 	p1 = fstr.indexOf( QLatin1String("D[") );
83 	if ( p1 != -1 )
84 	{
85 		p1 += 2;
86 		const QString str = fstr.mid( p1, pe - p1);
87 		p2 = str.indexOf(',');
88 		p3 = str.indexOf(']');
89 		if ( p2 > 0 && p2 < p3 )
90 		{
91 			tstr = str.left( p2 );
92 			errflg |= !item->dmin.updateExpression( tstr );
93 			tstr = str.mid( p2 + 1, p3 - p2 - 1 );
94 			errflg |= !item->dmax.updateExpression( tstr );
95 			if ( item->dmin.value() > item->dmax.value() )
96 				errflg = true;
97 		}
98 		else
99 			errflg = true;
100 	}
101 	p1 = fstr.indexOf( QLatin1String("P[") );
102 	if ( p1 != -1 )
103 	{
104 		int i = 0;
105 		p1 += 2;
106 		QString str = fstr.mid( p1, 1000);
107 		p3 = str.indexOf( ']' );
108 		do
109 		{
110 			p2 = str.indexOf( ',' );
111 			if ( p2 == -1 || p2 > p3 )
112 				p2 = p3;
113 			tstr = str.left( p2++ );
114 			str = str.mid( p2, 1000 );
115 			Value value;
116 			if ( !value.updateExpression( tstr ) )
117 			{
118 				errflg = true;
119 				break;
120 			}
121 			item->m_parameters.list.append( value );
122 			p3 -= p2;
123 		}
124 		while ( p3 > 0 && i < 10 );
125 	}
126 
127 	if ( errflg )
128 	{
129 		KMessageBox::error( 0, i18n( "Error in extension." ) );
130 		return false;
131 	}
132 	else
133 		return true;
134 }
135 
136 
derivative(int n,Equation * eq,DifferentialState * state,double x,double h)137 double XParser::derivative( int n, Equation * eq, DifferentialState * state, double x, double h )
138 {
139 	if ( n < -1 )
140 	{
141 		qCritical() << "Can't handle derivative < -1\n";
142 		return 0.0;
143 	}
144 
145 	switch ( n )
146 	{
147 		case -1:
148 			return differential( eq, & eq->differentialStates[0], x, h );
149 
150 		case 0:
151 			if ( state )
152 				return differential( eq, state, x, h );
153 			else
154 				return fkt( eq, x );
155 
156 		case 1:
157 			if ( state )
158 				return ( differential(eq, state, x + (h/2), h ) - differential( eq, state, x - (h/2), h ) ) / h;
159 			else
160 				return ( fkt(eq, x + (h/2) ) - fkt( eq, x - (h/2) ) ) / h;
161 
162 		default:
163 			return ( derivative( n-1, eq, state, x+(h/2), (h/4) ) - derivative( n-1, eq, state, x-(h/2), (h/4) ) ) / h;
164 	}
165 }
166 
167 
partialDerivative(int n1,int n2,Equation * eq,DifferentialState * state,double x,double y,double h1,double h2)168 double XParser::partialDerivative( int n1, int n2, Equation * eq, DifferentialState * state, double x, double y, double h1, double h2 )
169 {
170 	if ( n1 < 0 || n2 < 0 )
171 	{
172 		qCritical() << "Can't handle derivative < 0\n";
173 		return 0.0;
174 	}
175 
176 	if ( n1 > 0 )
177 		return ( partialDerivative( n1-1, n2, eq, state, x+(h1/2), y, (h1/4), h2 ) - partialDerivative( n1-1, n2, eq, state, x-(h1/2), y, (h1/4), h2 ) ) / h1;
178 
179 	Function * f = eq->parent();
180 	f->m_implicitMode = Function::FixedX;
181 	f->x = x;
182 
183 	return derivative( n2, eq, state, y, h2 );
184 }
185 
186 
findFunctionName(const QString & preferredName,int id,const QStringList & neededPatterns)187 QString XParser::findFunctionName( const QString & preferredName, int id, const QStringList& neededPatterns )
188 {
189 	// The position of the character attempting to replace
190 	int pos = preferredName.length()-1;
191 
192 	QString name = preferredName;
193 
194 	for ( ; ; ++pos)
195 	{
196 		for ( QChar lastChar = 'f'; lastChar<'x'; ++lastChar.unicode() )
197 		{
198 			bool ok = true;
199 			name[pos] = lastChar;
200 
201 			for ( Function * it : qAsConst(m_ufkt) )
202 			{
203 				if ( int(it->id()) == id )
204 					continue;
205 
206 				for ( Equation * eq : qAsConst(it->eq) )
207 				{
208 					for ( const QString& pattern : neededPatterns) {
209 						if ( eq->name() == pattern.arg(name) )
210 							ok = false;
211 					}
212 				}
213 
214 				if (!ok)
215 					break;
216 			}
217 			if ( !ok )
218 				continue;
219 
220 			// Found a free name :)
221 			return name;
222 		}
223 		name[pos]='f';
224 		name.append('f');
225 	}
226 }
227 
228 
fixFunctionName(QString & str,Equation::Type const type,int const id)229 void XParser::fixFunctionName( QString &str, Equation::Type const type, int const id)
230 {
231 	int p1 = str.indexOf('(');
232 	int p2 = str.indexOf(')');
233 	int p3 = str.indexOf('=');
234 
235 	if ( p1 < 0 )
236 		return;
237 
238 	for ( int i = p2+1; i < p3; ++i )
239 	{
240 		if ( !str.at(i).isSpace() )
241 			return;
242 	}
243 
244 	QString const fname = str.left(p1);
245 	for ( Function * it : qAsConst(m_ufkt) )
246 	{
247 		if ( int(it->id()) == id )
248 			continue;
249 
250 		for ( Equation * eq : qAsConst(it->eq) )
251 		{
252 			if ( eq->name() != fname )
253 				continue;
254 
255 			str = str.mid(p1,str.length()-1);
256 			QString function_name;
257 			if ( type == Equation::ParametricX )
258 				function_name = 'x';
259 			else if ( type == Equation::ParametricY )
260 				function_name = 'y';
261 			else
262 				function_name = 'f';
263 			function_name = findFunctionName( function_name, id );
264 			str.prepend( function_name );
265 			return;
266 		}
267 	}
268 }
269 
270 
rk4_f(int order,Equation * eq,double x,const Vector & y)271 Vector XParser::rk4_f( int order, Equation * eq, double x, const Vector & y )
272 {
273 	bool useParameter = eq->usesParameter();
274 
275 	m_result.resize( order );
276 	m_arg.resize( order+1 + (useParameter ? 1 : 0) );
277 
278 	m_arg[0] = x;
279 
280 	if ( useParameter )
281 		m_arg[1] = eq->parent()->k;
282 
283 	memcpy( m_arg.data() + 1 + (useParameter ? 1 : 0), y.data(), order*sizeof(double) );
284 	memcpy( m_result.data(), y.data() + 1, (order-1)*sizeof(double) );
285 
286 	m_result[order-1] = XParser::fkt( eq, m_arg );
287 
288 	return m_result;
289 }
290 
291 
differential(Equation * eq,DifferentialState * state,double x_target,double max_dx)292 double XParser::differential( Equation * eq, DifferentialState * state, double x_target, double max_dx )
293 {
294 	differentialFinite = true;
295 
296 	if ( eq->order() < 1 )
297 	{
298 		qWarning() << "Zero order!\n";
299 		return 0;
300 	}
301 
302 	max_dx = qAbs(max_dx);
303 	assert( max_dx > 0 ); // in case anyone tries to pass us a zero h
304 
305 	// the difference between h and dx is that h is only used as a hint for the
306 	// stepwidth; dx is made similar to h in size, yet tiles the gap between x
307 	// and the previous x perfectly
308 
309 	// see if the initial integral point in the function is closer to our
310 	// required x value than the last one (or the last point is invalid)
311 	if ( qAbs( state->x0.value() - x_target ) < qAbs( state->x - x_target ) )
312 		state->resetToInitial();
313 
314 	int order = eq->order();
315 
316 	m_k1.resize( order );
317 	m_k2.resize( order );
318 	m_k3.resize( order );
319 	m_k4.resize( order );
320 	m_y_temp.resize( order );
321 
322 	double x = state->x;
323 	m_y = state->y;
324 	if ( x_target == x )
325 		return m_y[0];
326 
327 	int intervals = int( qAbs(x_target-x)/max_dx + 1 );
328 	double dx = (x_target-x) / double(intervals);
329 
330 	for ( int i = 0; i < intervals; ++i )
331 	{
332 		// Update differentialDiverge before y possible becomes infinite
333 		differentialDiverge = x;
334 
335 		x = state->x + i*dx;
336 
337 
338 		m_k1 = rk4_f( order, eq, x, m_y );
339 
340 		m_y_temp.combine( m_y, dx/2, m_k1 );
341 		m_k2 = rk4_f( order, eq, x + dx/2, m_y_temp);
342 
343 		m_y_temp.combine( m_y, dx/2, m_k2 );
344 		m_k3 = rk4_f( order, eq, x + dx/2, m_y_temp );
345 
346 		m_y_temp.combine( m_y, dx, m_k3 );
347 		m_k4 = rk4_f( order, eq, x + dx, m_y_temp );
348 
349 		m_y.addRK4( dx, m_k1, m_k2, m_k3, m_k4 );
350 
351 		// The condition on the total accumulated error (O(dx^5)) should not be violated for rapidly increasing functions, e.g. e^x^2
352 		if ( !std::isfinite(m_y[0]) || qAbs((state->y[0]-m_y[0])*dx*dx) > 1)
353 		{
354 			differentialFinite = false;
355 			state->resetToInitial();
356 			return 1e200*((m_y[0] > 0) - (m_y[0] < 0));
357 		}
358 	}
359 
360 	state->x = x + dx;
361 	state->y = m_y;
362 
363 	return m_y[0];
364 }
365 
366 
defaultColor(int function)367 QColor XParser::defaultColor(int function)
368 {
369 	switch ( function % 10 )
370 	{
371 		case 0:
372 			return Settings::color0();
373 		case 1:
374 			return Settings::color1();
375 		case 2:
376 			return Settings::color2();
377 		case 3:
378 			return Settings::color3();
379 		case 4:
380 			return Settings::color4();
381 		case 5:
382 			return Settings::color5();
383 		case 6:
384 			return Settings::color6();
385 		case 7:
386 			return Settings::color7();
387 		case 8:
388 			return Settings::color8();
389 		case 9:
390 			return Settings::color9();
391 	}
392 
393 	assert( !"Should not happen - XParser::defaultColor" );
394 	return QColor();
395 }
396 
listFunctionNames()397 QStringList XParser::listFunctionNames()
398 {
399 	return userFunctions();
400 }
401 
functionFVisible(uint id)402 bool XParser::functionFVisible(uint id)
403 {
404 	return m_ufkt.contains(id) ? m_ufkt[id]->plotAppearance( Function::Derivative0 ).visible : false;
405 }
functionF1Visible(uint id)406 bool XParser::functionF1Visible(uint id)
407 {
408 	return m_ufkt.contains(id) ? m_ufkt[id]->plotAppearance( Function::Derivative1 ).visible : false;
409 }
functionF2Visible(uint id)410 bool XParser::functionF2Visible(uint id)
411 {
412 	return m_ufkt.contains(id) ? m_ufkt[id]->plotAppearance( Function::Derivative2 ).visible : false;
413 }
functionIntVisible(uint id)414 bool XParser::functionIntVisible(uint id)
415 {
416 	return m_ufkt.contains(id) ? m_ufkt[id]->plotAppearance( Function::Integral ).visible : false;
417 }
418 
setFunctionFVisible(uint id,bool visible)419 bool XParser::setFunctionFVisible(uint id, bool visible)
420 {
421 	if ( !m_ufkt.contains( id ) )
422 		return false;
423 	m_ufkt[id]->plotAppearance( Function::Derivative0 ).visible = visible;
424 	MainDlg::self()->requestSaveCurrentState();
425 	return true;
426 }
setFunctionF1Visible(uint id,bool visible)427 bool XParser::setFunctionF1Visible(uint id, bool visible)
428 {
429 	if ( !m_ufkt.contains( id ) )
430 		return false;
431 	m_ufkt[id]->plotAppearance( Function::Derivative1 ).visible = visible;
432 	MainDlg::self()->requestSaveCurrentState();
433 	return true;
434 }
setFunctionF2Visible(uint id,bool visible)435 bool XParser::setFunctionF2Visible(uint id, bool visible)
436 {
437 	if ( !m_ufkt.contains( id ) )
438 		return false;
439 	m_ufkt[id]->plotAppearance( Function::Derivative2 ).visible = visible;
440 	MainDlg::self()->requestSaveCurrentState();
441 	return true;
442 }
setFunctionIntVisible(uint id,bool visible)443 bool XParser::setFunctionIntVisible(uint id, bool visible)
444 {
445 	if ( !m_ufkt.contains( id ) )
446 		return false;
447 	m_ufkt[id]->plotAppearance( Function::Integral ).visible = visible;
448 	MainDlg::self()->requestSaveCurrentState();
449 	return true;
450 }
451 
functionStr(uint id,uint eq)452 QString XParser::functionStr(uint id, uint eq)
453 {
454 	if ( !m_ufkt.contains( id ) || (eq>=2) )
455 		return QLatin1String("");
456 	return m_ufkt[id]->eq[eq]->fstr();
457 }
458 
functionFColor(uint id)459 QColor XParser::functionFColor(uint id)
460 {
461 	if ( !m_ufkt.contains( id ) )
462 		return QColor();
463 	return QColor(m_ufkt[id]->plotAppearance( Function::Derivative0 ).color);
464 }
functionF1Color(uint id)465 QColor XParser::functionF1Color(uint id)
466 {
467 	if ( !m_ufkt.contains( id ) )
468 		return QColor();
469 	return QColor(m_ufkt[id]->plotAppearance( Function::Derivative1 ).color);
470 }
functionF2Color(uint id)471 QColor XParser::functionF2Color(uint id)
472 {
473 	if ( !m_ufkt.contains( id ) )
474 		return QColor();
475 	return QColor(m_ufkt[id]->plotAppearance( Function::Derivative2 ).color);
476 }
functionIntColor(uint id)477 QColor XParser::functionIntColor(uint id)
478 {
479 	if ( !m_ufkt.contains( id ) )
480 		return QColor();
481 	return QColor(m_ufkt[id]->plotAppearance( Function::Integral ).color);
482 }
setFunctionFColor(uint id,const QColor & color)483 bool XParser::setFunctionFColor(uint id, const QColor &color)
484 {
485 	if ( !m_ufkt.contains( id ) )
486 		return false;
487 	m_ufkt[id]->plotAppearance( Function::Derivative0 ).color = color;
488 	MainDlg::self()->requestSaveCurrentState();
489 	return true;
490 }
setFunctionF1Color(uint id,const QColor & color)491 bool XParser::setFunctionF1Color(uint id, const QColor &color)
492 {
493 	if ( !m_ufkt.contains( id ) )
494 		return false;
495 	m_ufkt[id]->plotAppearance( Function::Derivative1 ).color = color;
496 	MainDlg::self()->requestSaveCurrentState();
497 	return true;
498 }
setFunctionF2Color(uint id,const QColor & color)499 bool XParser::setFunctionF2Color(uint id, const QColor &color)
500 {
501 	if ( !m_ufkt.contains( id ) )
502 		return false;
503 	m_ufkt[id]->plotAppearance( Function::Derivative2 ).color = color;
504 	MainDlg::self()->requestSaveCurrentState();
505 	return true;
506 }
setFunctionIntColor(uint id,const QColor & color)507 bool XParser::setFunctionIntColor(uint id, const QColor &color)
508 {
509 	if ( !m_ufkt.contains( id ) )
510 		return false;
511 	m_ufkt[id]->plotAppearance( Function::Integral ).color = color;
512 	MainDlg::self()->requestSaveCurrentState();
513 	return true;
514 }
515 
functionFLineWidth(uint id)516 double XParser::functionFLineWidth(uint id)
517 {
518 	if ( !m_ufkt.contains( id ) )
519 		return 0;
520 	return m_ufkt[id]->plotAppearance( Function::Derivative0 ).lineWidth;
521 }
functionF1LineWidth(uint id)522 double XParser::functionF1LineWidth(uint id)
523 {
524 	if ( !m_ufkt.contains( id ) )
525 		return 0;
526 	return m_ufkt[id]->plotAppearance( Function::Derivative1 ).lineWidth;
527 }
functionF2LineWidth(uint id)528 double XParser::functionF2LineWidth(uint id)
529 {
530 	if ( !m_ufkt.contains( id ) )
531 		return 0;
532 	return m_ufkt[id]->plotAppearance( Function::Derivative2 ).lineWidth;
533 }
functionIntLineWidth(uint id)534 double XParser::functionIntLineWidth(uint id)
535 {
536 	if ( !m_ufkt.contains( id ) )
537 		return 0;
538 	return m_ufkt[id]->plotAppearance( Function::Integral ).lineWidth;
539 }
setFunctionFLineWidth(uint id,double linewidth)540 bool XParser::setFunctionFLineWidth(uint id, double linewidth)
541 {
542 	if ( !m_ufkt.contains( id ) )
543 		return false;
544 	m_ufkt[id]->plotAppearance( Function::Derivative0 ).lineWidth = linewidth;
545 	MainDlg::self()->requestSaveCurrentState();
546 	return true;
547 }
setFunctionF1LineWidth(uint id,double linewidth)548 bool XParser::setFunctionF1LineWidth(uint id, double linewidth)
549 {
550 	if ( !m_ufkt.contains( id ) )
551 		return false;
552 	m_ufkt[id]->plotAppearance( Function::Derivative1 ).lineWidth = linewidth;
553 	MainDlg::self()->requestSaveCurrentState();
554 	return true;
555 }
setFunctionF2LineWidth(uint id,double linewidth)556 bool XParser::setFunctionF2LineWidth(uint id, double linewidth)
557 {
558 	if ( !m_ufkt.contains( id ) )
559 		return false;
560 	m_ufkt[id]->plotAppearance( Function::Derivative2 ).lineWidth = linewidth;
561 	MainDlg::self()->requestSaveCurrentState();
562 	return true;
563 }
setFunctionIntLineWidth(uint id,double linewidth)564 bool XParser::setFunctionIntLineWidth(uint id, double linewidth)
565 {
566 	if ( !m_ufkt.contains( id ) )
567 		return false;
568 	m_ufkt[id]->plotAppearance( Function::Integral ).lineWidth = linewidth;
569 	MainDlg::self()->requestSaveCurrentState();
570 	return true;
571 }
572 
functionMinValue(uint id)573 QString XParser::functionMinValue(uint id)
574 {
575 	if ( !m_ufkt.contains( id ) )
576 		return 0;
577   return m_ufkt[id]->dmin.expression();
578 }
579 
setFunctionMinValue(uint id,const QString & min)580 bool XParser::setFunctionMinValue(uint id, const QString &min)
581 {
582 	if ( !m_ufkt.contains( id ) )
583 		return false;
584 	m_ufkt[id]->dmin.expression() = min;
585 	MainDlg::self()->requestSaveCurrentState();
586   return true;
587 }
588 
functionMaxValue(uint id)589 QString XParser::functionMaxValue(uint id)
590 {
591 	if ( !m_ufkt.contains( id ) )
592 		return 0;
593   return m_ufkt[id]->dmax.expression();
594 }
595 
setFunctionMaxValue(uint id,const QString & max)596 bool XParser::setFunctionMaxValue(uint id, const QString &max)
597 {
598 	if ( !m_ufkt.contains( id ) )
599 		return false;
600 	m_ufkt[id]->dmax.expression() = max;
601 	MainDlg::self()->requestSaveCurrentState();
602   return true;
603 }
604 
setFunctionStartValue(uint id,const QString & x,const QString & y)605 bool XParser::setFunctionStartValue(uint id, const QString &x, const QString &y)
606 {
607 	if ( !m_ufkt.contains( id ) )
608 		return false;
609 	DifferentialState * state = & m_ufkt[id]->eq[0]->differentialStates[0];
610 	state->x0.updateExpression( x );
611 	state->y0[0].updateExpression( y );
612 	MainDlg::self()->requestSaveCurrentState();
613 	return true;
614 }
615 
functionStartXValue(uint id)616 QString XParser::functionStartXValue(uint id)
617 {
618 	if ( !m_ufkt.contains( id ) )
619 		return 0;
620 	DifferentialState * state = & m_ufkt[id]->eq[0]->differentialStates[0];
621 	return state->x0.expression();
622 }
623 
624 
functionStartYValue(uint id)625 QString XParser::functionStartYValue(uint id)
626 {
627 	if ( !m_ufkt.contains( id ) )
628 		return 0;
629 	DifferentialState * state = & m_ufkt[id]->eq[0]->differentialStates[0];
630 	return state->y0[0].expression();
631 }
632 
functionParameterList(uint id)633 QStringList XParser::functionParameterList(uint id)
634 {
635 	if ( !m_ufkt.contains( id ) )
636 		return QStringList();
637 	Function *item = m_ufkt[id];
638 	QStringList str_parameter;
639 	for ( const Value &it : qAsConst(item->m_parameters.list) )
640 		str_parameter << it.expression();
641 	return str_parameter;
642 }
functionAddParameter(uint id,const QString & new_parameter)643 bool XParser::functionAddParameter(uint id, const QString &new_parameter)
644 {
645 	if ( !m_ufkt.contains( id ) )
646 		return false;
647 	Function *tmp_ufkt = m_ufkt[id];
648 
649 	//check if the parameter already exists
650 	for ( const Value &it : qAsConst(tmp_ufkt->m_parameters.list) )
651 	{
652 		if ( it.expression() == new_parameter )
653 			return false;
654 	}
655 
656 	Value value;
657 	if ( !value.updateExpression( new_parameter ) )
658 		return false;
659 	tmp_ufkt->m_parameters.list.append( value );
660 	MainDlg::self()->requestSaveCurrentState();
661 	return true;
662 }
functionRemoveParameter(uint id,const QString & remove_parameter)663 bool XParser::functionRemoveParameter(uint id, const QString &remove_parameter)
664 {
665 	if ( !m_ufkt.contains( id ) )
666 		return false;
667 	Function *tmp_ufkt = m_ufkt[id];
668 
669 	bool found = false;
670 	QList<Value>::iterator it;
671 	for ( it = tmp_ufkt->m_parameters.list.begin(); it != tmp_ufkt->m_parameters.list.end(); ++it)
672 	{
673 		if ( (*it).expression() == remove_parameter) //check if the parameter already exists
674 		{
675 			found = true;
676 			break;
677 		}
678 	}
679 	if (!found)
680 		return false;
681 	tmp_ufkt->m_parameters.list.erase(it);
682 	MainDlg::self()->requestSaveCurrentState();
683 	return true;
684 }
addFunction(const QString & f_str0,const QString & _f_str1)685 int XParser::addFunction(const QString &f_str0, const QString &_f_str1)
686 {
687 	QString added_function(f_str0);
688 	QString f_str1(_f_str1);
689 	int const pos = added_function.indexOf(';');
690 	if (pos!=-1)
691 	  added_function = added_function.left(pos);
692 
693 	fixFunctionName(added_function);
694 	if ( !f_str1.isEmpty() )
695 		fixFunctionName( f_str1 );
696 
697 	Function::Type type;
698 
699 	if ( !f_str1.isEmpty() )
700 		type = Function::Parametric;
701 	else if ( f_str0.count( '=' ) > 1 )
702 		type = Function::Implicit;
703 	else
704 		type = (added_function[0] == 'r') ? Function::Polar : Function::Cartesian;
705 
706 	int const id = Parser::addFunction( added_function, f_str1, type );
707 	if (id==-1)
708 		return -1;
709 	Function *tmp_ufkt = m_ufkt[id];
710 	if ( pos!=-1 && !getext( tmp_ufkt, f_str0 ) )
711 	{
712 		Parser::removeFunction( tmp_ufkt );
713 		return -1;
714 	}
715 	MainDlg::self()->requestSaveCurrentState();
716 	return id;
717 }
718 
addFunction(const QString & fstr_const0,const QString & fstr_const1,bool f_mode,bool f1_mode,bool f2_mode,bool integral_mode,double linewidth,double f1_linewidth,double f2_linewidth,double integral_linewidth,const QString & str_dmin,const QString & str_dmax,const QString & str_startx,const QString & str_starty,double integral_precision,const QColor & color,const QColor & f1_color,const QColor & f2_color,const QColor & integral_color,const QStringList & str_parameter,int use_slider)719 bool XParser::addFunction(const QString &fstr_const0, const QString &fstr_const1, bool f_mode, bool f1_mode, bool f2_mode, bool integral_mode, double linewidth, double f1_linewidth, double f2_linewidth, double integral_linewidth, const QString &str_dmin, const QString &str_dmax, const QString &str_startx, const QString &str_starty, double integral_precision, const QColor &color, const QColor &f1_color, const QColor &f2_color, const QColor &integral_color, const QStringList & str_parameter, int use_slider)
720 {
721 	QString fstr[2] = { fstr_const0, fstr_const1 };
722 	Function::Type type = Function::Cartesian;
723 	for ( unsigned i = 0; i < 2; ++i )
724 	{
725 		if ( fstr[i].isEmpty() )
726 			continue;
727 
728 		switch ( fstr[i][0].unicode() )
729 		{
730 			case 'r':
731 			{
732 				fixFunctionName(fstr[i], Equation::Polar);
733 				type = Function::Polar;
734 				break;
735 			}
736 			case 'x':
737 				fixFunctionName(fstr[i], Equation::ParametricX);
738 				type = Function::Parametric;
739 				break;
740 			case 'y':
741 				fixFunctionName(fstr[i], Equation::ParametricY);
742 				type = Function::Parametric;
743 				break;
744 			default:
745 				fixFunctionName(fstr[i], Equation::Cartesian );
746 				type = Function::Cartesian;
747 				break;
748 		}
749 	}
750 
751 	int const id = Parser::addFunction( fstr[0], fstr[1], type );
752 	if ( id==-1 )
753 		return false;
754 	Function *added_function = m_ufkt[id];
755 
756 	PlotAppearance appearance;
757 
758 	// f0
759 	appearance.visible = f_mode;
760 	appearance.color = color;
761 	appearance.lineWidth = linewidth;
762 	added_function->plotAppearance( Function::Derivative0 ) = appearance;
763 
764 	// f1
765 	appearance.visible = f1_mode;
766 	appearance.color = f1_color;
767 	appearance.lineWidth = f1_linewidth;
768 	added_function->plotAppearance( Function::Derivative1 ) = appearance;
769 
770 	// f2
771 	appearance.visible = f2_mode;
772 	appearance.color = f2_color;
773 	appearance.lineWidth = f2_linewidth;
774 	added_function->plotAppearance( Function::Derivative2 ) = appearance;
775 
776 	// integral
777 	appearance.visible = integral_mode;
778 	appearance.color = integral_color;
779 	appearance.lineWidth = integral_linewidth;
780 	added_function->plotAppearance( Function::Integral ) = appearance;
781 
782 	added_function->dmin.updateExpression( str_dmin );
783 	added_function->usecustomxmin = !str_dmin.isEmpty();
784 
785 	added_function->dmax.updateExpression( str_dmax );
786 	added_function->usecustomxmax = !str_dmax.isEmpty();
787 
788 	DifferentialState * state = & added_function->eq[0]->differentialStates[0];
789 	state->x0.updateExpression( str_startx );
790 	state->y0[0].updateExpression( str_starty );
791 
792 	added_function->eq[0]->differentialStates.setStep( Value( integral_precision ) );
793 
794 	added_function->m_parameters.sliderID = use_slider;
795 	for( QStringList::ConstIterator it = str_parameter.begin(); it != str_parameter.end(); ++it )
796 	{
797 		added_function->m_parameters.list.append( *it );
798 	}
799 	MainDlg::self()->requestSaveCurrentState();
800 	return true;
801 }
802 
setFunctionExpression(uint id,uint eq,const QString & f_str)803 bool XParser::setFunctionExpression(uint id, uint eq, const QString &f_str)
804 {
805 	Function * tmp_ufkt = functionWithID( id );
806 	if ( !tmp_ufkt )
807 		return false;
808 	QString const old_fstr = tmp_ufkt->eq[eq]->fstr();
809 	QString const fstr_begin = tmp_ufkt->eq[eq]->fstr().left(tmp_ufkt->eq[eq]->fstr().indexOf('=')+1);
810 
811 	return tmp_ufkt->eq[eq]->setFstr( fstr_begin+f_str );
812 }
813 
814