1/* gtkconstraintexpression.c: Constraint expressions and variables
2 * Copyright 2019 GNOME Foundation
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Emmanuele Bassi
20 */
21
22#include "config.h"
23
24#include "gtkconstraintexpressionprivate.h"
25#include "gtkconstraintsolverprivate.h"
26
27/* {{{ Variables */
28
29typedef enum {
30 GTK_CONSTRAINT_SYMBOL_DUMMY = 'd',
31 GTK_CONSTRAINT_SYMBOL_OBJECTIVE = 'o',
32 GTK_CONSTRAINT_SYMBOL_SLACK = 'S',
33 GTK_CONSTRAINT_SYMBOL_REGULAR = 'v'
34} GtkConstraintSymbolType;
35
36struct _GtkConstraintVariable
37{
38 guint64 _id;
39
40 GtkConstraintSymbolType _type;
41
42 /* Interned strings */
43 const char *name;
44 const char *prefix;
45
46 double value;
47
48 guint is_external : 1;
49 guint is_pivotable : 1;
50 guint is_restricted : 1;
51};
52
53/* Variables are sorted by a monotonic id */
54static guint64 gtk_constraint_variable_next_id;
55
56static void
57gtk_constraint_variable_init (GtkConstraintVariable *variable,
58 const char *prefix,
59 const char *name)
60{
61 variable->_id = gtk_constraint_variable_next_id++;
62
63 variable->prefix = g_intern_string (string: prefix);
64 variable->name = g_intern_string (string: name);
65 variable->prefix = NULL;
66 variable->value = 0.0;
67}
68
69/*< private >
70 * gtk_constraint_variable_new_dummy:
71 * @name: the name of the variable
72 *
73 * Allocates and initializes a new `GtkConstraintVariable` for a "dummy"
74 * symbol. Dummy symbols are typically used as markers inside a solver,
75 * and will not be factored in the solution when pivoting the tableau
76 * of the constraint equations.
77 *
78 * Only `GtkConstraintSolver` should use this function.
79 *
80 * Returns: a newly allocated `GtkConstraintVariable`
81 */
82GtkConstraintVariable *
83gtk_constraint_variable_new_dummy (const char *name)
84{
85 GtkConstraintVariable *res = g_rc_box_new (GtkConstraintVariable);
86
87 gtk_constraint_variable_init (variable: res, NULL, name);
88
89 res->_type = GTK_CONSTRAINT_SYMBOL_DUMMY;
90 res->is_external = FALSE;
91 res->is_pivotable = FALSE;
92 res->is_restricted = TRUE;
93
94 return res;
95}
96
97/*< private >
98 * gtk_constraint_variable_new_objective:
99 * @name: the name of the variable
100 *
101 * Allocates and initializes a new `GtkConstraintVariable` for an objective
102 * symbol. This is the constant value we wish to find as the result of the
103 * simplex optimization.
104 *
105 * Only `GtkConstraintSolver` should use this function.
106 *
107 * Returns: a newly allocated `GtkConstraintVariable`
108 */
109GtkConstraintVariable *
110gtk_constraint_variable_new_objective (const char *name)
111{
112 GtkConstraintVariable *res = g_rc_box_new (GtkConstraintVariable);
113
114 gtk_constraint_variable_init (variable: res, NULL, name);
115
116 res->_type = GTK_CONSTRAINT_SYMBOL_OBJECTIVE;
117 res->is_external = FALSE;
118 res->is_pivotable = FALSE;
119 res->is_restricted = FALSE;
120
121 return res;
122}
123
124/*< private >
125 * gtk_constraint_variable_new_slack:
126 * @name: the name of the variable
127 *
128 * Allocates and initializes a new `GtkConstraintVariable` for a "slack"
129 * symbol. Slack variables are introduced inside the tableau to turn
130 * inequalities, like:
131 *
132 * |[
133 * expr ≥ 0
134 * ]|
135 *
136 * Into equalities, like:
137 *
138 * |[
139 * expr - slack = 0
140 * ]|
141 *
142 * Only `GtkConstraintSolver` should use this function.
143 *
144 * Returns: a newly allocated `GtkConstraintVariable`
145 */
146GtkConstraintVariable *
147gtk_constraint_variable_new_slack (const char *name)
148{
149 GtkConstraintVariable *res = g_rc_box_new (GtkConstraintVariable);
150
151 gtk_constraint_variable_init (variable: res, NULL, name);
152
153 res->_type = GTK_CONSTRAINT_SYMBOL_SLACK;
154 res->is_external = FALSE;
155 res->is_pivotable = TRUE;
156 res->is_restricted = TRUE;
157
158 return res;
159}
160
161/*< private >
162 * gtk_constraint_variable_new:
163 * @prefix: (nullable): an optional prefix string for @name
164 * @name: (nullable): an optional name for the variable
165 *
166 * Allocates and initializes a new `GtkConstraintVariable` for a regular
167 * symbol. All variables introduced by constraints are regular variables.
168 *
169 * Only `GtkConstraintSolver` should use this function; a constraint layout
170 * manager should ask the `GtkConstraintSolver` to create a variable, using
171 * gtk_constraint_solver_create_variable(), which will insert the variable
172 * in the solver's tableau.
173 *
174 * Returns: a newly allocated `GtkConstraintVariable`
175 */
176GtkConstraintVariable *
177gtk_constraint_variable_new (const char *prefix,
178 const char *name)
179{
180 GtkConstraintVariable *res = g_rc_box_new (GtkConstraintVariable);
181
182 gtk_constraint_variable_init (variable: res, prefix, name);
183
184 res->_type = GTK_CONSTRAINT_SYMBOL_REGULAR;
185 res->is_external = TRUE;
186 res->is_pivotable = FALSE;
187 res->is_restricted = FALSE;
188
189 return res;
190}
191
192/*< private >
193 * gtk_constraint_variable_ref:
194 * @variable: a `GtkConstraintVariable`
195 *
196 * Acquires a reference to @variable.
197 *
198 * Returns: (transfer full): the given `GtkConstraintVariable`, with its reference
199 * count increased
200 */
201GtkConstraintVariable *
202gtk_constraint_variable_ref (GtkConstraintVariable *variable)
203{
204 g_return_val_if_fail (variable != NULL, NULL);
205
206 return g_rc_box_acquire (variable);
207}
208
209/*< private >
210 * gtk_constraint_variable_unref:
211 * @variable: (transfer full): a `GtkConstraintVariable`
212 *
213 * Releases a reference to @variable.
214 */
215void
216gtk_constraint_variable_unref (GtkConstraintVariable *variable)
217{
218 g_return_if_fail (variable != NULL);
219
220 g_rc_box_release (mem_block: variable);
221}
222
223/*< private >
224 * gtk_constraint_variable_set_value:
225 * @variable: a `GtkConstraintVariable`
226 *
227 * Sets the current value of a `GtkConstraintVariable`.
228 */
229void
230gtk_constraint_variable_set_value (GtkConstraintVariable *variable,
231 double value)
232{
233 variable->value = value;
234}
235
236/*< private >
237 * gtk_constraint_variable_get_value:
238 * @variable: a `GtkConstraintVariable`
239 *
240 * Retrieves the current value of a `GtkConstraintVariable`
241 *
242 * Returns: the value of the variable
243 */
244double
245gtk_constraint_variable_get_value (const GtkConstraintVariable *variable)
246{
247 return variable->value;
248}
249
250/*< private >
251 * gtk_constraint_variable_to_string:
252 * @variable: a `GtkConstraintVariable`
253 *
254 * Turns @variable into a string, for debugging purposes.
255 *
256 * Returns: (transfer full): a string with the contents of @variable
257 */
258char *
259gtk_constraint_variable_to_string (const GtkConstraintVariable *variable)
260{
261 GString *buf = g_string_new (NULL);
262
263 if (variable == NULL)
264 g_string_append (string: buf, val: "<null>");
265 else
266 {
267 switch (variable->_type)
268 {
269 case GTK_CONSTRAINT_SYMBOL_DUMMY:
270 g_string_append (string: buf, val: "(d)");
271 break;
272 case GTK_CONSTRAINT_SYMBOL_OBJECTIVE:
273 g_string_append (string: buf, val: "(O)");
274 break;
275 case GTK_CONSTRAINT_SYMBOL_SLACK:
276 g_string_append (string: buf, val: "(S)");
277 break;
278 case GTK_CONSTRAINT_SYMBOL_REGULAR:
279 break;
280
281 default:
282 g_assert_not_reached ();
283 }
284
285 g_string_append_c (buf, '[');
286
287 if (variable->prefix != NULL)
288 {
289 g_string_append (string: buf, val: variable->prefix);
290 g_string_append_c (buf, '.');
291 }
292
293 if (variable->name != NULL)
294 g_string_append (string: buf, val: variable->name);
295
296 if (variable->_type == GTK_CONSTRAINT_SYMBOL_REGULAR)
297 {
298 char dbl_buf[G_ASCII_DTOSTR_BUF_SIZE];
299
300 g_ascii_dtostr (buffer: dbl_buf, G_ASCII_DTOSTR_BUF_SIZE, d: variable->value);
301
302 g_string_append_c (buf, ':');
303 g_string_append (string: buf, val: dbl_buf);
304 }
305
306 g_string_append_c (buf, ']');
307 }
308
309 return g_string_free (string: buf, FALSE);
310}
311
312/*< private >
313 * gtk_constraint_variable_is_external:
314 * @variable: a `GtkConstraintVariable`
315 *
316 * Checks whether the @variable was introduced from outside the solver.
317 *
318 * Returns: %TRUE if the variable is external
319 */
320gboolean
321gtk_constraint_variable_is_external (const GtkConstraintVariable *variable)
322{
323 return variable->is_external;
324}
325
326/*< private >
327 * gtk_constraint_variable_is_pivotable:
328 * @variable: a `GtkConstraintVariable`
329 *
330 * Checks whether the @variable can be used as a pivot.
331 *
332 * Returns: %TRUE if the variable is pivotable
333 */
334gboolean
335gtk_constraint_variable_is_pivotable (const GtkConstraintVariable *variable)
336{
337 return variable->is_pivotable;
338}
339
340/*< private >
341 * gtk_constraint_variable_is_restricted:
342 * @variable: a `GtkConstraintVariable`
343 *
344 * Checks whether the @variable's use is restricted.
345 *
346 * Returns: %TRUE if the variable is restricted
347 */
348gboolean
349gtk_constraint_variable_is_restricted (const GtkConstraintVariable *variable)
350{
351 return variable->is_restricted;
352}
353
354/*< private >
355 * gtk_constraint_variable_is_dummy:
356 * @variable: a `GtkConstraintVariable`
357 *
358 * Checks whether the @variable is a dummy symbol.
359 *
360 * Returns: %TRUE if the variable is a dummy symbol
361 */
362gboolean
363gtk_constraint_variable_is_dummy (const GtkConstraintVariable *variable)
364{
365 return variable->_type == GTK_CONSTRAINT_SYMBOL_DUMMY;
366}
367
368/*< private >
369 * GtkConstraintVariableSet:
370 *
371 * A set of variables.
372 */
373struct _GtkConstraintVariableSet {
374 /* List<Variable>, owns a reference */
375 GSequence *set;
376
377 /* Age of the set, to guard against mutations while iterating */
378 gint64 age;
379};
380
381/*< private >
382 * gtk_constraint_variable_set_free:
383 * @set: a `GtkConstraintVariable`Set
384 *
385 * Frees the resources associated to a `GtkConstraintVariable`Set/
386 */
387void
388gtk_constraint_variable_set_free (GtkConstraintVariableSet *set)
389{
390 g_return_if_fail (set != NULL);
391
392 g_sequence_free (seq: set->set);
393
394 g_free (mem: set);
395}
396
397/*< private >
398 * gtk_constraint_variable_set_new:
399 *
400 * Creates a new `GtkConstraintVariable`Set.
401 *
402 * Returns: the newly created variable set
403 */
404GtkConstraintVariableSet *
405gtk_constraint_variable_set_new (void)
406{
407 GtkConstraintVariableSet *res = g_new (GtkConstraintVariableSet, 1);
408
409 res->set = g_sequence_new (data_destroy: (GDestroyNotify) gtk_constraint_variable_unref);
410
411 res->age = 0;
412
413 return res;
414}
415
416static int
417sort_by_variable_id (gconstpointer a,
418 gconstpointer b,
419 gpointer data)
420{
421 const GtkConstraintVariable *va = a, *vb = b;
422
423 if (va == vb)
424 return 0;
425
426 return va->_id - vb->_id;
427}
428
429/*< private >
430 * gtk_constraint_variable_set_add:
431 * @set: a `GtkConstraintVariable`Set
432 * @variable: a `GtkConstraintVariable`
433 *
434 * Adds @variable to the given @set, if the @variable is not already
435 * in it.
436 *
437 * The @set will acquire a reference on the @variable, and will release
438 * it after calling gtk_constraint_variable_set_remove(), or when the @set
439 * is freed.
440 *
441 * Returns: %TRUE if the variable was added to the set, and %FALSE otherwise
442 */
443gboolean
444gtk_constraint_variable_set_add (GtkConstraintVariableSet *set,
445 GtkConstraintVariable *variable)
446{
447 GSequenceIter *iter;
448
449 iter = g_sequence_search (seq: set->set, data: variable, cmp_func: sort_by_variable_id, NULL);
450 if (!g_sequence_iter_is_end (iter))
451 {
452 GtkConstraintVariable *v = g_sequence_get (iter);
453 if (v->_id == variable->_id)
454 return FALSE;
455 }
456
457 g_sequence_insert_before (iter, data: gtk_constraint_variable_ref (variable));
458
459 set->age += 1;
460
461 return TRUE;
462}
463
464/*< private >
465 * gtk_constraint_variable_set_remove:
466 * @set: a `GtkConstraintVariable`Set
467 * @variable: a `GtkConstraintVariable`
468 *
469 * Removes @variable from the @set.
470 *
471 * This function will release the reference on @variable held by the @set.
472 *
473 * Returns: %TRUE if the variable was removed from the set, and %FALSE
474 * otherwise
475 */
476gboolean
477gtk_constraint_variable_set_remove (GtkConstraintVariableSet *set,
478 GtkConstraintVariable *variable)
479{
480 GSequenceIter *iter;
481
482 iter = g_sequence_lookup (seq: set->set, data: variable, cmp_func: sort_by_variable_id, NULL);
483 if (iter != NULL)
484 {
485 g_sequence_remove (iter);
486 set->age += 1;
487
488 return TRUE;
489 }
490
491 return FALSE;
492}
493
494/*< private >
495 * gtk_constraint_variable_set_size:
496 * @set: a `GtkConstraintVariable`Set
497 *
498 * Retrieves the size of the @set.
499 *
500 * Returns: the number of variables in the set
501 */
502int
503gtk_constraint_variable_set_size (GtkConstraintVariableSet *set)
504{
505 return g_sequence_get_length (seq: set->set);
506}
507
508gboolean
509gtk_constraint_variable_set_is_empty (GtkConstraintVariableSet *set)
510{
511 return g_sequence_is_empty (seq: set->set);
512}
513
514gboolean
515gtk_constraint_variable_set_is_singleton (GtkConstraintVariableSet *set)
516{
517 return g_sequence_iter_next (iter: g_sequence_get_begin_iter (seq: set->set)) == g_sequence_get_end_iter (seq: set->set);
518}
519
520/*< private >
521 * GtkConstraintVariableSetIter:
522 *
523 * An iterator type for `GtkConstraintVariable`Set.
524 */
525/* Keep in sync with GtkConstraintVariableSetIter */
526typedef struct {
527 GtkConstraintVariableSet *set;
528 GSequenceIter *iter;
529 gint64 age;
530} RealVariableSetIter;
531
532#define REAL_VARIABLE_SET_ITER(i) ((RealVariableSetIter *) (i))
533
534/*< private >
535 * gtk_constraint_variable_set_iter_init:
536 * @iter: a `GtkConstraintVariable`SetIter
537 * @set: the `GtkConstraintVariable`Set to iterate
538 *
539 * Initializes @iter for iterating over @set.
540 */
541void
542gtk_constraint_variable_set_iter_init (GtkConstraintVariableSetIter *iter,
543 GtkConstraintVariableSet *set)
544{
545 RealVariableSetIter *riter = REAL_VARIABLE_SET_ITER (iter);
546
547 g_return_if_fail (iter != NULL);
548 g_return_if_fail (set != NULL);
549
550 riter->set = set;
551 riter->iter = g_sequence_get_begin_iter (seq: set->set);
552 riter->age = set->age;
553}
554
555/*< private >
556 * gtk_constraint_variable_set_iter_next:
557 * @iter: a `GtkConstraintVariable`SetIter
558 * @variable_p: (out): the next variable in the set
559 *
560 * Advances the @iter to the next variable in the `GtkConstraintVariable`Set.
561 *
562 * Returns: %TRUE if the iterator was advanced, and %FALSE otherwise
563 */
564gboolean
565gtk_constraint_variable_set_iter_next (GtkConstraintVariableSetIter *iter,
566 GtkConstraintVariable **variable_p)
567{
568 RealVariableSetIter *riter = REAL_VARIABLE_SET_ITER (iter);
569
570 g_return_val_if_fail (iter != NULL, FALSE);
571 g_return_val_if_fail (variable_p != NULL, FALSE);
572
573 g_assert (riter->age == riter->set->age);
574
575 if (g_sequence_iter_is_end (iter: riter->iter))
576 return FALSE;
577
578 *variable_p = g_sequence_get (iter: riter->iter);
579 riter->iter = g_sequence_iter_next (iter: riter->iter);
580
581 return TRUE;
582}
583
584/*< private >
585 * gtk_constraint_variable_pair_new:
586 * @first: a `GtkConstraintVariable`
587 * @second: a `GtkConstraintVariable`
588 *
589 * Creates a new `GtkConstraintVariable`Pair, containing @first and @second.
590 *
591 * The `GtkConstraintVariable`Pair acquires a reference over the two
592 * given `GtkConstraintVariable`s.
593 *
594 * Returns: a new `GtkConstraintVariable`Pair
595 */
596GtkConstraintVariablePair *
597gtk_constraint_variable_pair_new (GtkConstraintVariable *first,
598 GtkConstraintVariable *second)
599{
600 GtkConstraintVariablePair *res = g_new (GtkConstraintVariablePair, 1);
601
602 res->first = gtk_constraint_variable_ref (variable: first);
603 res->second = gtk_constraint_variable_ref (variable: second);
604
605 return res;
606}
607
608/*< private >
609 * gtk_constraint_variable_pair_free:
610 * @pair: a `GtkConstraintVariable`Pair
611 *
612 * Frees the resources associated by @pair.
613 */
614void
615gtk_constraint_variable_pair_free (GtkConstraintVariablePair *pair)
616{
617 g_clear_pointer (&pair->first, gtk_constraint_variable_unref);
618 g_clear_pointer (&pair->second, gtk_constraint_variable_unref);
619
620 g_free (mem: pair);
621}
622
623/* }}} */
624
625/* {{{ Expressions */
626
627/*< private >
628 * Term:
629 * @variable: a `GtkConstraintVariable`
630 * @coefficient: the coefficient applied to the @variable
631 * @next: the next term in the expression
632 * @prev: the previous term in the expression;
633 *
634 * A tuple of (@variable, @coefficient) in an equation.
635 *
636 * The term acquires a reference on the variable.
637 */
638typedef struct _Term Term;
639
640struct _Term {
641 GtkConstraintVariable *variable;
642 double coefficient;
643
644 Term *next;
645 Term *prev;
646};
647
648static Term *
649term_new (GtkConstraintVariable *variable,
650 double coefficient)
651{
652 Term *res = g_new (Term, 1);
653
654 res->variable = gtk_constraint_variable_ref (variable);
655 res->coefficient = coefficient;
656 res->next = res->prev = NULL;
657
658 return res;
659}
660
661static void
662term_free (gpointer data)
663{
664 Term *term = data;
665
666 if (term == NULL)
667 return;
668
669 gtk_constraint_variable_unref (variable: term->variable);
670
671 g_free (mem: term);
672}
673
674struct _GtkConstraintExpression
675{
676 double constant;
677
678 /* HashTable<Variable, Term>; the key is the term's variable,
679 * and the value is owned by the hash table
680 */
681 GHashTable *terms;
682
683 /* List of terms, in insertion order */
684 Term *first_term;
685 Term *last_term;
686
687 /* Used by GtkConstraintExpressionIter to guard against changes
688 * in the expression while iterating
689 */
690 gint64 age;
691};
692
693/*< private >
694 * gtk_constraint_expression_add_term:
695 * @self: a `GtkConstraintExpression`
696 * @variable: a `GtkConstraintVariable`
697 * @coefficient: a coefficient for @variable
698 *
699 * Adds a new term formed by (@variable, @coefficient) into a
700 * `GtkConstraintExpression`.
701 *
702 * The @expression acquires a reference on @variable.
703 */
704static void
705gtk_constraint_expression_add_term (GtkConstraintExpression *self,
706 GtkConstraintVariable *variable,
707 double coefficient)
708{
709 Term *term;
710
711 if (self->terms == NULL)
712 {
713 g_assert (self->first_term == NULL && self->last_term == NULL);
714 self->terms = g_hash_table_new_full (NULL, NULL,
715 NULL,
716 value_destroy_func: term_free);
717 }
718
719 term = term_new (variable, coefficient);
720
721 g_hash_table_insert (hash_table: self->terms, key: term->variable, value: term);
722
723 if (self->first_term == NULL)
724 self->first_term = term;
725
726 term->prev = self->last_term;
727
728 if (self->last_term != NULL)
729 self->last_term->next = term;
730
731 self->last_term = term;
732
733 /* Increase the age of the expression, so that we can catch
734 * mutations from within an iteration over the terms
735 */
736 self->age += 1;
737}
738
739static void
740gtk_constraint_expression_remove_term (GtkConstraintExpression *self,
741 GtkConstraintVariable *variable)
742{
743 Term *term, *iter;
744
745 if (self->terms == NULL)
746 return;
747
748 term = g_hash_table_lookup (hash_table: self->terms, key: variable);
749 if (term == NULL)
750 return;
751
752 /* Keep the variable alive for the duration of the function */
753 gtk_constraint_variable_ref (variable);
754
755 iter = self->first_term;
756 while (iter != NULL)
757 {
758 Term *next = iter->next;
759 Term *prev = iter->prev;
760
761 if (iter == term)
762 {
763 if (prev != NULL)
764 prev->next = next;
765 if (next != NULL)
766 next->prev = prev;
767
768 if (iter == self->first_term)
769 self->first_term = next;
770 if (iter == self->last_term)
771 self->last_term = prev;
772
773 iter->next = NULL;
774 iter->prev = NULL;
775
776 break;
777 }
778
779 iter = next;
780 }
781
782 g_hash_table_remove (hash_table: self->terms, key: variable);
783
784 gtk_constraint_variable_unref (variable);
785
786 self->age += 1;
787}
788
789/*< private >
790 * gtk_constraint_expression_new:
791 * @constant: a constant for the expression
792 *
793 * Creates a new `GtkConstraintExpression` with the given @constant.
794 *
795 * Returns: (transfer full): the newly created expression
796 */
797GtkConstraintExpression *
798gtk_constraint_expression_new (double constant)
799{
800 GtkConstraintExpression *res = g_rc_box_new (GtkConstraintExpression);
801
802 res->age = 0;
803 res->terms = NULL;
804 res->first_term = NULL;
805 res->last_term = NULL;
806 res->constant = constant;
807
808 return res;
809}
810
811/*< private >
812 * gtk_constraint_expression_new_from_variable:
813 * @variable: a `GtkConstraintVariable`
814 *
815 * Creates a new `GtkConstraintExpression` with the given @variable.
816 *
817 * Returns: (transfer full): the newly created expression
818 */
819GtkConstraintExpression *
820gtk_constraint_expression_new_from_variable (GtkConstraintVariable *variable)
821{
822 GtkConstraintExpression *res = gtk_constraint_expression_new (constant: 0.0);
823
824 gtk_constraint_expression_add_term (self: res, variable, coefficient: 1.0);
825
826 return res;
827}
828
829/*< private >
830 * gtk_constraint_expression_ref:
831 * @expression: a `GtkConstraintExpression`
832 *
833 * Acquires a reference on @expression.
834 *
835 * Returns: (transfer full): the @expression, with its reference
836 * count increased
837 */
838GtkConstraintExpression *
839gtk_constraint_expression_ref (GtkConstraintExpression *expression)
840{
841 g_return_val_if_fail (expression != NULL, NULL);
842
843 return g_rc_box_acquire (expression);
844}
845
846static void
847gtk_constraint_expression_clear (gpointer data)
848{
849 GtkConstraintExpression *self = data;
850
851 g_clear_pointer (&self->terms, g_hash_table_unref);
852
853 self->age = 0;
854 self->constant = 0.0;
855 self->first_term = NULL;
856 self->last_term = NULL;
857}
858
859/*< private >
860 * gtk_constraint_expression_unref:
861 * @expression: (transfer full): a `GtkConstraintExpression`
862 *
863 * Releases a reference on @expression.
864 */
865void
866gtk_constraint_expression_unref (GtkConstraintExpression *expression)
867{
868 g_rc_box_release_full (mem_block: expression, clear_func: gtk_constraint_expression_clear);
869}
870
871/*< private >
872 * gtk_constraint_expression_is_constant:
873 * @expression: a `GtkConstraintExpression`
874 *
875 * Checks whether @expression is a constant value, with no variable terms.
876 *
877 * Returns: %TRUE if the @expression is a constant
878 */
879gboolean
880gtk_constraint_expression_is_constant (const GtkConstraintExpression *expression)
881{
882 return expression->terms == NULL;
883}
884
885/*< private >
886 * gtk_constraint_expression_set_constant:
887 * @expression: a `GtkConstraintExpression`
888 * @constant: the value of the constant
889 *
890 * Sets the value of the constant part of @expression.
891 */
892void
893gtk_constraint_expression_set_constant (GtkConstraintExpression *expression,
894 double constant)
895{
896 g_return_if_fail (expression != NULL);
897
898 expression->constant = constant;
899}
900
901/*< private >
902 * gtk_constraint_expression_get_constant:
903 * @expression: a `GtkConstraintExpression`
904 *
905 * Retrieves the constant value of @expression.
906 *
907 * Returns: the constant of @expression
908 */
909double
910gtk_constraint_expression_get_constant (const GtkConstraintExpression *expression)
911{
912 g_return_val_if_fail (expression != NULL, 0.0);
913
914 return expression->constant;
915}
916
917GtkConstraintExpression *
918gtk_constraint_expression_clone (GtkConstraintExpression *expression)
919{
920 GtkConstraintExpression *res;
921 Term *iter;
922
923 res = gtk_constraint_expression_new (constant: expression->constant);
924
925 iter = expression->first_term;
926 while (iter != NULL)
927 {
928 gtk_constraint_expression_add_term (self: res, variable: iter->variable, coefficient: iter->coefficient);
929
930 iter = iter->next;
931 }
932
933 return res;
934}
935
936/*< private >
937 * gtk_constraint_expression_add_variable:
938 * @expression: a `GtkConstraintExpression`
939 * @variable: a `GtkConstraintVariable` to add to @expression
940 * @coefficient: the coefficient of @variable
941 * @subject: (nullable): a `GtkConstraintVariable`
942 * @solver: (nullable): a `GtkConstraintSolver`
943 *
944 * Adds a `(@coefficient × @variable)` term to @expression.
945 *
946 * If @expression already contains a term for @variable, this function will
947 * update its coefficient.
948 *
949 * If @coefficient is 0 and @expression already contains a term for @variable,
950 * the term for @variable will be removed.
951 *
952 * This function will notify @solver if @variable is added or removed from
953 * the @expression.
954 */
955void
956gtk_constraint_expression_add_variable (GtkConstraintExpression *expression,
957 GtkConstraintVariable *variable,
958 double coefficient,
959 GtkConstraintVariable *subject,
960 GtkConstraintSolver *solver)
961{
962 /* If the expression already contains the variable, update the coefficient */
963 if (expression->terms != NULL)
964 {
965 Term *t = g_hash_table_lookup (hash_table: expression->terms, key: variable);
966
967 if (t != NULL)
968 {
969 double new_coefficient = t->coefficient + coefficient;
970
971 /* Setting the coefficient to 0 will remove the variable */
972 if (G_APPROX_VALUE (new_coefficient, 0.0, 0.001))
973 {
974 /* Update the tableau if needed */
975 if (solver != NULL)
976 gtk_constraint_solver_note_removed_variable (self: solver, variable, subject);
977
978 gtk_constraint_expression_remove_term (self: expression, variable);
979 }
980 else
981 {
982 t->coefficient = new_coefficient;
983 }
984
985 return;
986 }
987 }
988
989 /* Otherwise, add the variable if the coefficient is non-zero */
990 if (!G_APPROX_VALUE (coefficient, 0.0, 0.001))
991 {
992 gtk_constraint_expression_add_term (self: expression, variable, coefficient);
993
994 if (solver != NULL)
995 gtk_constraint_solver_note_added_variable (self: solver, variable, subject);
996 }
997}
998
999/*< private >
1000 * gtk_constraint_expression_remove_variable:
1001 * @expression: a `GtkConstraintExpression`
1002 * @variable: a `GtkConstraintVariable`
1003 *
1004 * Removes @variable from @expression.
1005 */
1006void
1007gtk_constraint_expression_remove_variable (GtkConstraintExpression *expression,
1008 GtkConstraintVariable *variable)
1009{
1010 g_return_if_fail (expression != NULL);
1011 g_return_if_fail (variable != NULL);
1012
1013 gtk_constraint_expression_remove_term (self: expression, variable);
1014}
1015
1016/*< private >
1017 * gtk_constraint_expression_set_variable:
1018 * @expression: a `GtkConstraintExpression`
1019 * @variable: a `GtkConstraintVariable`
1020 * @coefficient: a coefficient for @variable
1021 *
1022 * Sets the @coefficient for @variable inside an @expression.
1023 *
1024 * If the @expression does not contain a term for @variable, a new
1025 * one will be added.
1026 */
1027void
1028gtk_constraint_expression_set_variable (GtkConstraintExpression *expression,
1029 GtkConstraintVariable *variable,
1030 double coefficient)
1031{
1032 if (expression->terms != NULL)
1033 {
1034 Term *t = g_hash_table_lookup (hash_table: expression->terms, key: variable);
1035
1036 if (t != NULL)
1037 {
1038 t->coefficient = coefficient;
1039 return;
1040 }
1041 }
1042
1043 gtk_constraint_expression_add_term (self: expression, variable, coefficient);
1044}
1045
1046/*< private >
1047 * gtk_constraint_expression_add_expression:
1048 * @a_expr: first operand
1049 * @b_expr: second operand
1050 * @n: the multiplication factor for @b_expr
1051 * @subject: (nullable): a `GtkConstraintVariable`
1052 * @solver: (nullable): a `GtkConstraintSolver`
1053 *
1054 * Adds `(@n × @b_expr)` to @a_expr.
1055 *
1056 * Typically, this function is used to turn two expressions in the
1057 * form:
1058 *
1059 * |[
1060 * a.x + a.width = b.x + b.width
1061 * ]|
1062 *
1063 * into a single expression:
1064 *
1065 * |[
1066 * a.x + a.width - b.x - b.width = 0
1067 * ]|
1068 *
1069 * If @solver is not %NULL, this function will notify a `GtkConstraintSolver`
1070 * of every variable that was added or removed from @a_expr.
1071 */
1072void
1073gtk_constraint_expression_add_expression (GtkConstraintExpression *a_expr,
1074 GtkConstraintExpression *b_expr,
1075 double n,
1076 GtkConstraintVariable *subject,
1077 GtkConstraintSolver *solver)
1078{
1079 Term *iter;
1080
1081 a_expr->constant += (n * b_expr->constant);
1082
1083 iter = b_expr->last_term;
1084 while (iter != NULL)
1085 {
1086 Term *next = iter->prev;
1087
1088 gtk_constraint_expression_add_variable (expression: a_expr,
1089 variable: iter->variable, coefficient: n * iter->coefficient,
1090 subject,
1091 solver);
1092
1093 iter = next;
1094 }
1095}
1096
1097/*< private >
1098 * gtk_constraint_expression_plus_constant:
1099 * @expression: a `GtkConstraintExpression`
1100 * @constant: a constant value
1101 *
1102 * Adds a @constant value to the @expression.
1103 *
1104 * This is the equivalent of creating a new `GtkConstraintExpression` for
1105 * the @constant and calling gtk_constraint_expression_add_expression().
1106 *
1107 * Returns: the @expression
1108 */
1109GtkConstraintExpression *
1110gtk_constraint_expression_plus_constant (GtkConstraintExpression *expression,
1111 double constant)
1112{
1113 GtkConstraintExpression *e;
1114
1115 e = gtk_constraint_expression_new (constant);
1116 gtk_constraint_expression_add_expression (a_expr: expression, b_expr: e, n: 1.0, NULL, NULL);
1117 gtk_constraint_expression_unref (expression: e);
1118
1119 return expression;
1120}
1121
1122/*< private >
1123 * gtk_constraint_expression_minus_constant:
1124 * @expression: a `GtkConstraintExpression`
1125 * @constant: a constant value
1126 *
1127 * Removes a @constant value from the @expression.
1128 *
1129 * This is the equivalent of creating a new `GtkConstraintExpression` for
1130 * the inverse of @constant and calling gtk_constraint_expression_add_expression().
1131 *
1132 * Returns: the @expression
1133 */
1134GtkConstraintExpression *
1135gtk_constraint_expression_minus_constant (GtkConstraintExpression *expression,
1136 double constant)
1137{
1138 return gtk_constraint_expression_plus_constant (expression, constant: constant * -1.0);
1139}
1140
1141/*< private >
1142 * gtk_constraint_expression_plus_variable:
1143 * @expression: a `GtkConstraintExpression`
1144 * @variable: a `GtkConstraintVariable`
1145 *
1146 * Adds a @variable to the @expression.
1147 *
1148 * Returns: the @expression
1149 */
1150GtkConstraintExpression *
1151gtk_constraint_expression_plus_variable (GtkConstraintExpression *expression,
1152 GtkConstraintVariable *variable)
1153{
1154 GtkConstraintExpression *e;
1155
1156 e = gtk_constraint_expression_new_from_variable (variable);
1157 gtk_constraint_expression_add_expression (a_expr: expression, b_expr: e, n: 1.0, NULL, NULL);
1158 gtk_constraint_expression_unref (expression: e);
1159
1160 return expression;
1161}
1162
1163/*< private >
1164 * gtk_constraint_expression_minus_variable:
1165 * @expression: a `GtkConstraintExpression`
1166 * @variable: a `GtkConstraintVariable`
1167 *
1168 * Subtracts a @variable from the @expression.
1169 *
1170 * Returns: the @expression
1171 */
1172GtkConstraintExpression *
1173gtk_constraint_expression_minus_variable (GtkConstraintExpression *expression,
1174 GtkConstraintVariable *variable)
1175{
1176 GtkConstraintExpression *e;
1177
1178 e = gtk_constraint_expression_new_from_variable (variable);
1179 gtk_constraint_expression_add_expression (a_expr: expression, b_expr: e, n: -1.0, NULL, NULL);
1180 gtk_constraint_expression_unref (expression: e);
1181
1182 return expression;
1183}
1184
1185/*< private >
1186 * gtk_constraint_expression_multiply_by:
1187 * @expression: a `GtkConstraintExpression`
1188 * @factor: the multiplication factor
1189 *
1190 * Multiplies the constant part and the coefficient of all terms
1191 * in @expression with the given @factor.
1192 *
1193 * Returns: the @expression
1194 */
1195GtkConstraintExpression *
1196gtk_constraint_expression_multiply_by (GtkConstraintExpression *expression,
1197 double factor)
1198{
1199 GHashTableIter iter;
1200 gpointer value_p;
1201
1202 expression->constant *= factor;
1203
1204 if (expression->terms == NULL)
1205 return expression;
1206
1207 g_hash_table_iter_init (iter: &iter, hash_table: expression->terms);
1208 while (g_hash_table_iter_next (iter: &iter, NULL, value: &value_p))
1209 {
1210 Term *t = value_p;
1211
1212 t->coefficient *= factor;
1213 }
1214
1215 return expression;
1216}
1217
1218/*< private >
1219 * gtk_constraint_expression_divide_by:
1220 * @expression: a `GtkConstraintExpression`
1221 * @factor: the division factor
1222 *
1223 * Divides the constant part and the coefficient of all terms
1224 * in @expression by the given @factor.
1225 *
1226 * Returns: the @expression
1227 */
1228GtkConstraintExpression *
1229gtk_constraint_expression_divide_by (GtkConstraintExpression *expression,
1230 double factor)
1231{
1232 if (G_APPROX_VALUE (factor, 0.0, 0.001))
1233 return expression;
1234
1235 return gtk_constraint_expression_multiply_by (expression, factor: 1.0 / factor);
1236}
1237
1238/*< private >
1239 * gtk_constraint_expression_new_subject:
1240 * @expression: a `GtkConstraintExpression`
1241 * @subject: a `GtkConstraintVariable` part of @expression
1242 *
1243 * Modifies @expression to have a new @subject.
1244 *
1245 * A `GtkConstraintExpression` is a linear expression in the form of
1246 * `@expression = 0`. If @expression contains @subject, for instance:
1247 *
1248 * |[
1249 * c + (a × @subject) + (a1 × v1) + … + (an × vn) = 0
1250 * ]|
1251 *
1252 * this function will make @subject the new subject of the expression:
1253 *
1254 * |[
1255 * subject = - (c / a) - ((a1 / a) × v1) - … - ((an / a) × vn) = 0
1256 * ]|
1257 *
1258 * The term @subject is removed from the @expression.
1259 *
1260 * Returns: the reciprocal of the coefficient of @subject, so we
1261 * can use this function in gtk_constraint_expression_change_subject()
1262 */
1263double
1264gtk_constraint_expression_new_subject (GtkConstraintExpression *expression,
1265 GtkConstraintVariable *subject)
1266{
1267 double reciprocal = 1.0;
1268 Term *term;
1269
1270 g_assert (!gtk_constraint_expression_is_constant (expression));
1271
1272 term = g_hash_table_lookup (hash_table: expression->terms, key: subject);
1273 g_assert (term != NULL);
1274 g_assert (!G_APPROX_VALUE (term->coefficient, 0.0, 0.001));
1275
1276 reciprocal = 1.0 / term->coefficient;
1277
1278 gtk_constraint_expression_remove_term (self: expression, variable: subject);
1279 gtk_constraint_expression_multiply_by (expression, factor: -reciprocal);
1280
1281 return reciprocal;
1282}
1283
1284/*< private >
1285 * gtk_constraint_expression_change_subject:
1286 * @expression: a `GtkConstraintExpression`
1287 * @old_subject: the old subject `GtkConstraintVariable` of @expression
1288 * @new_subject: the new subject `GtkConstraintVariable` of @expression
1289 *
1290 * Turns an @expression in the form of:
1291 *
1292 * |[
1293 * old_subject = c + (a × new_subject) + (a1 × v1) + … + (an × vn)
1294 * ]|
1295 *
1296 * into the form of:
1297 *
1298 * |[
1299 * new_subject = -c / a + old_subject / a - ((a1 / a) × v1) - … - ((an / a) × vn)
1300 * ]|
1301 *
1302 * Which means resolving @expression for @new_subject.
1303 */
1304void
1305gtk_constraint_expression_change_subject (GtkConstraintExpression *expression,
1306 GtkConstraintVariable *old_subject,
1307 GtkConstraintVariable *new_subject)
1308{
1309 double reciprocal;
1310
1311 g_return_if_fail (expression != NULL);
1312 g_return_if_fail (old_subject != NULL);
1313 g_return_if_fail (new_subject != NULL);
1314
1315 reciprocal = gtk_constraint_expression_new_subject (expression, subject: new_subject);
1316 gtk_constraint_expression_set_variable (expression, variable: old_subject, coefficient: reciprocal);
1317}
1318
1319/*< private >
1320 * gtk_constraint_expression_get_coefficient:
1321 * @expression: a `GtkConstraintExpression`
1322 * @variable: a `GtkConstraintVariable`
1323 *
1324 * Retrieves the coefficient of the term for @variable inside @expression.
1325 *
1326 * Returns: the coefficient of @variable
1327 */
1328double
1329gtk_constraint_expression_get_coefficient (GtkConstraintExpression *expression,
1330 GtkConstraintVariable *variable)
1331{
1332 const Term *term;
1333
1334 g_return_val_if_fail (expression != NULL, 0.0);
1335 g_return_val_if_fail (variable != NULL, 0.0);
1336
1337 if (expression->terms == NULL)
1338 return 0.0;
1339
1340 term = g_hash_table_lookup (hash_table: expression->terms, key: variable);
1341 if (term == NULL)
1342 return 0.0;
1343
1344 return term->coefficient;
1345}
1346
1347/*< private >
1348 * gtk_constraint_expression_substitute_out:
1349 * @expression: a `GtkConstraintExpression`
1350 * @out_var: the variable to replace
1351 * @expr: the expression used to replace @out_var
1352 * @subject: (nullable): a `GtkConstraintVariable`
1353 * @solver: (nullable): a `GtkConstraintSolver`
1354 *
1355 * Replaces every term containing @out_var inside @expression with @expr.
1356 *
1357 * If @solver is not %NULL, this function will notify the `GtkConstraintSolver`
1358 * for every variable added to or removed from @expression.
1359 */
1360void
1361gtk_constraint_expression_substitute_out (GtkConstraintExpression *expression,
1362 GtkConstraintVariable *out_var,
1363 GtkConstraintExpression *expr,
1364 GtkConstraintVariable *subject,
1365 GtkConstraintSolver *solver)
1366{
1367 double multiplier;
1368 Term *iter;
1369
1370 if (expression->terms == NULL)
1371 return;
1372
1373 multiplier = gtk_constraint_expression_get_coefficient (expression, variable: out_var);
1374 gtk_constraint_expression_remove_term (self: expression, variable: out_var);
1375
1376 expression->constant = expression->constant + multiplier * expr->constant;
1377
1378 iter = expr->first_term;
1379 while (iter != NULL)
1380 {
1381 GtkConstraintVariable *clv = iter->variable;
1382 double coeff = iter->coefficient;
1383 Term *next = iter->next;
1384
1385 if (expression->terms != NULL &&
1386 g_hash_table_contains (hash_table: expression->terms, key: clv))
1387 {
1388 double old_coefficient = gtk_constraint_expression_get_coefficient (expression, variable: clv);
1389 double new_coefficient = old_coefficient + multiplier * coeff;
1390
1391 if (G_APPROX_VALUE (new_coefficient, 0.0, 0.001))
1392 {
1393 if (solver != NULL)
1394 gtk_constraint_solver_note_removed_variable (self: solver, variable: clv, subject);
1395
1396 gtk_constraint_expression_remove_term (self: expression, variable: clv);
1397 }
1398 else
1399 gtk_constraint_expression_set_variable (expression, variable: clv, coefficient: new_coefficient);
1400 }
1401 else
1402 {
1403 gtk_constraint_expression_set_variable (expression, variable: clv, coefficient: multiplier * coeff);
1404
1405 if (solver != NULL)
1406 gtk_constraint_solver_note_added_variable (self: solver, variable: clv, subject);
1407 }
1408
1409 iter = next;
1410 }
1411}
1412
1413/*< private >
1414 * gtk_constraint_expression_get_pivotable_variable:
1415 * @expression: a `GtkConstraintExpression`
1416 *
1417 * Retrieves the first `GtkConstraintVariable` in @expression that
1418 * is marked as pivotable.
1419 *
1420 * Returns: (transfer none) (nullable): a `GtkConstraintVariable`
1421 */
1422GtkConstraintVariable *
1423gtk_constraint_expression_get_pivotable_variable (GtkConstraintExpression *expression)
1424{
1425 Term *iter;
1426
1427 if (expression->terms == NULL)
1428 {
1429 g_critical ("Expression %p is a constant", expression);
1430 return NULL;
1431 }
1432
1433 iter = expression->first_term;
1434 while (iter != NULL)
1435 {
1436 Term *next = iter->next;
1437
1438 if (gtk_constraint_variable_is_pivotable (variable: iter->variable))
1439 return iter->variable;
1440
1441 iter = next;
1442 }
1443
1444 return NULL;
1445}
1446
1447/*< private >
1448 * gtk_constraint_expression_to_string:
1449 * @expression: a `GtkConstraintExpression`
1450 *
1451 * Creates a string containing @expression.
1452 *
1453 * This function is only useful for debugging.
1454 *
1455 * Returns: (transfer full): a string containing the given expression
1456 */
1457char *
1458gtk_constraint_expression_to_string (const GtkConstraintExpression *expression)
1459{
1460 gboolean needs_plus = FALSE;
1461 GString *buf;
1462 Term *iter;
1463
1464 if (expression == NULL)
1465 return g_strdup (str: "<null>");
1466
1467 buf = g_string_new (NULL);
1468
1469 if (!G_APPROX_VALUE (expression->constant, 0.0, 0.001))
1470 {
1471 g_string_append_printf (string: buf, format: "%g", expression->constant);
1472
1473 if (expression->terms != NULL)
1474 needs_plus = TRUE;
1475 }
1476
1477 if (expression->terms == NULL)
1478 return g_string_free (string: buf, FALSE);
1479
1480 iter = expression->first_term;
1481 while (iter != NULL)
1482 {
1483 char *str = gtk_constraint_variable_to_string (variable: iter->variable);
1484 Term *next = iter->next;
1485
1486 if (needs_plus)
1487 g_string_append (string: buf, val: " + ");
1488
1489 if (G_APPROX_VALUE (iter->coefficient, 1.0, 0.001))
1490 g_string_append_printf (string: buf, format: "%s", str);
1491 else
1492 g_string_append_printf (string: buf, format: "(%g * %s)", iter->coefficient, str);
1493
1494 g_free (mem: str);
1495
1496 if (!needs_plus)
1497 needs_plus = TRUE;
1498
1499 iter = next;
1500 }
1501
1502 return g_string_free (string: buf, FALSE);
1503}
1504
1505/* Keep in sync with GtkConstraintExpressionIter */
1506typedef struct {
1507 GtkConstraintExpression *expression;
1508 Term *current;
1509 gint64 age;
1510} RealExpressionIter;
1511
1512#define REAL_EXPRESSION_ITER(i) ((RealExpressionIter *) (i))
1513
1514/*< private >
1515 * gtk_constraint_expression_iter_init:
1516 * @iter: a `GtkConstraintExpression`Iter
1517 * @expression: a `GtkConstraintExpression`
1518 *
1519 * Initializes an iterator over @expression.
1520 */
1521void
1522gtk_constraint_expression_iter_init (GtkConstraintExpressionIter *iter,
1523 GtkConstraintExpression *expression)
1524{
1525 RealExpressionIter *riter = REAL_EXPRESSION_ITER (iter);
1526
1527 riter->expression = expression;
1528 riter->current = NULL;
1529 riter->age = expression->age;
1530}
1531
1532/*< private >
1533 * gtk_constraint_expression_iter_next:
1534 * @iter: a valid `GtkConstraintExpression`Iter
1535 * @variable: (out): the variable of the next term
1536 * @coefficient: (out): the coefficient of the next term
1537 *
1538 * Moves the given `GtkConstraintExpression`Iter forwards to the next
1539 * term in the expression, starting from the first term.
1540 *
1541 * Returns: %TRUE if the iterator was moved, and %FALSE if the iterator
1542 * has reached the end of the terms of the expression
1543 */
1544gboolean
1545gtk_constraint_expression_iter_next (GtkConstraintExpressionIter *iter,
1546 GtkConstraintVariable **variable,
1547 double *coefficient)
1548{
1549 RealExpressionIter *riter = REAL_EXPRESSION_ITER (iter);
1550
1551 g_assert (riter->age == riter->expression->age);
1552
1553 if (riter->current == NULL)
1554 riter->current = riter->expression->first_term;
1555 else
1556 riter->current = riter->current->next;
1557
1558 if (riter->current != NULL)
1559 {
1560 *coefficient = riter->current->coefficient;
1561 *variable = riter->current->variable;
1562 }
1563
1564 return riter->current != NULL;
1565}
1566
1567/*< private >
1568 * gtk_constraint_expression_iter_prev:
1569 * @iter: a valid `GtkConstraintExpression`Iter
1570 * @variable: (out): the variable of the previous term
1571 * @coefficient: (out): the coefficient of the previous term
1572 *
1573 * Moves the given `GtkConstraintExpression`Iter backwards to the previous
1574 * term in the expression, starting from the last term.
1575 *
1576 * Returns: %TRUE if the iterator was moved, and %FALSE if the iterator
1577 * has reached the beginning of the terms of the expression
1578 */
1579gboolean
1580gtk_constraint_expression_iter_prev (GtkConstraintExpressionIter *iter,
1581 GtkConstraintVariable **variable,
1582 double *coefficient)
1583{
1584 RealExpressionIter *riter = REAL_EXPRESSION_ITER (iter);
1585
1586 g_assert (riter->age == riter->expression->age);
1587
1588 if (riter->current == NULL)
1589 riter->current = riter->expression->last_term;
1590 else
1591 riter->current = riter->current->prev;
1592
1593 if (riter->current != NULL)
1594 {
1595 *coefficient = riter->current->coefficient;
1596 *variable = riter->current->variable;
1597 }
1598
1599 return riter->current != NULL;
1600}
1601
1602typedef enum {
1603 BUILDER_OP_NONE,
1604 BUILDER_OP_PLUS,
1605 BUILDER_OP_MINUS,
1606 BUILDER_OP_MULTIPLY,
1607 BUILDER_OP_DIVIDE
1608} BuilderOpType;
1609
1610typedef struct
1611{
1612 GtkConstraintExpression *expression;
1613 GtkConstraintSolver *solver;
1614 int op;
1615} RealExpressionBuilder;
1616
1617#define REAL_EXPRESSION_BUILDER(b) ((RealExpressionBuilder *) (b))
1618
1619/*< private >
1620 * gtk_constraint_expression_builder_init:
1621 * @builder: a `GtkConstraintExpression`Builder
1622 * @solver: a `GtkConstraintSolver`
1623 *
1624 * Initializes the given `GtkConstraintExpression`Builder for the
1625 * given `GtkConstraintSolver`.
1626 *
1627 * You can use the @builder to construct expressions to be added to the
1628 * @solver, in the form of constraints.
1629 *
1630 * A typical use is:
1631 *
1632 * |[<!-- language="C" -->
1633 * GtkConstraintExpressionBuilder builder;
1634 *
1635 * // "solver" is set in another part of the code
1636 * gtk_constraint_expression_builder_init (&builder, solver);
1637 *
1638 * // "width" is set in another part of the code
1639 * gtk_constraint_expression_builder_term (&builder, width);
1640 * gtk_constraint_expression_builder_divide_by (&builder);
1641 * gtk_constraint_expression_builder_constant (&builder, 2.0);
1642 *
1643 * // "left" is set in another part of the code
1644 * gtk_constraint_expression_builder_plus (&builder);
1645 * gtk_constraint_expression_builder_term (&builder, left);
1646 *
1647 * // "expr" now contains the following expression:
1648 * // width / 2.0 + left
1649 * GtkConstraintExpression *expr =
1650 * gtk_constraint_expression_builder_finish (&builder);
1651 *
1652 * // The builder is inert, and can be re-used by calling
1653 * // gtk_constraint_expression_builder_init() again.
1654 * ]|
1655 */
1656void
1657gtk_constraint_expression_builder_init (GtkConstraintExpressionBuilder *builder,
1658 GtkConstraintSolver *solver)
1659{
1660 RealExpressionBuilder *rbuilder = REAL_EXPRESSION_BUILDER (builder);
1661
1662 rbuilder->solver = solver;
1663 rbuilder->expression = gtk_constraint_expression_new (constant: 0);
1664 rbuilder->op = BUILDER_OP_NONE;
1665}
1666
1667/*< private >
1668 * gtk_constraint_expression_builder_term:
1669 * @builder: a `GtkConstraintExpression`Builder
1670 * @term: a `GtkConstraintVariable`
1671 *
1672 * Adds a variable @term to the @builder.
1673 */
1674void
1675gtk_constraint_expression_builder_term (GtkConstraintExpressionBuilder *builder,
1676 GtkConstraintVariable *term)
1677{
1678 RealExpressionBuilder *rbuilder = REAL_EXPRESSION_BUILDER (builder);
1679 GtkConstraintExpression *expr;
1680
1681 expr = gtk_constraint_expression_new_from_variable (variable: term);
1682
1683 switch (rbuilder->op)
1684 {
1685 case BUILDER_OP_NONE:
1686 g_clear_pointer (&rbuilder->expression, gtk_constraint_expression_unref);
1687 rbuilder->expression = g_steal_pointer (&expr);
1688 break;
1689
1690 case BUILDER_OP_PLUS:
1691 gtk_constraint_expression_add_expression (a_expr: rbuilder->expression,
1692 b_expr: expr, n: 1.0,
1693 NULL,
1694 NULL);
1695 gtk_constraint_expression_unref (expression: expr);
1696 break;
1697
1698 case BUILDER_OP_MINUS:
1699 gtk_constraint_expression_add_expression (a_expr: rbuilder->expression,
1700 b_expr: expr, n: -1.0,
1701 NULL,
1702 NULL);
1703 gtk_constraint_expression_unref (expression: expr);
1704 break;
1705
1706 default:
1707 break;
1708 }
1709
1710 rbuilder->op = BUILDER_OP_NONE;
1711}
1712
1713/*< private >
1714 * gtk_constraint_expression_builder_plus:
1715 * @builder: a `GtkConstraintExpression`Builder
1716 *
1717 * Adds a plus operator to the @builder.
1718 */
1719void
1720gtk_constraint_expression_builder_plus (GtkConstraintExpressionBuilder *builder)
1721{
1722 RealExpressionBuilder *rbuilder = REAL_EXPRESSION_BUILDER (builder);
1723
1724 rbuilder->op = BUILDER_OP_PLUS;
1725}
1726
1727/*< private >
1728 * gtk_constraint_expression_builder_minus:
1729 * @builder: a `GtkConstraintExpression`Builder
1730 *
1731 * Adds a minus operator to the @builder.
1732 */
1733void
1734gtk_constraint_expression_builder_minus (GtkConstraintExpressionBuilder *builder)
1735{
1736 RealExpressionBuilder *rbuilder = REAL_EXPRESSION_BUILDER (builder);
1737
1738 rbuilder->op = BUILDER_OP_MINUS;
1739}
1740
1741/*< private >
1742 * gtk_constraint_expression_builder_divide_by:
1743 * @builder: a `GtkConstraintExpression`Builder
1744 *
1745 * Adds a division operator to the @builder.
1746 */
1747void
1748gtk_constraint_expression_builder_divide_by (GtkConstraintExpressionBuilder *builder)
1749{
1750 RealExpressionBuilder *rbuilder = REAL_EXPRESSION_BUILDER (builder);
1751
1752 rbuilder->op = BUILDER_OP_DIVIDE;
1753}
1754
1755/*< private >
1756 * gtk_constraint_expression_builder_multiply_by:
1757 * @builder: a `GtkConstraintExpression`Builder
1758 *
1759 * Adds a multiplication operator to the @builder.
1760 */
1761void
1762gtk_constraint_expression_builder_multiply_by (GtkConstraintExpressionBuilder *builder)
1763
1764{
1765 RealExpressionBuilder *rbuilder = REAL_EXPRESSION_BUILDER (builder);
1766
1767 rbuilder->op = BUILDER_OP_MULTIPLY;
1768}
1769
1770/*< private >
1771 * gtk_constraint_expression_builder_constant:
1772 * @builder: a `GtkConstraintExpression`Builder
1773 * @value: a constant value
1774 *
1775 * Adds a constant value to the @builder.
1776 */
1777void
1778gtk_constraint_expression_builder_constant (GtkConstraintExpressionBuilder *builder,
1779 double value)
1780{
1781 RealExpressionBuilder *rbuilder = REAL_EXPRESSION_BUILDER (builder);
1782
1783 switch (rbuilder->op)
1784 {
1785 case BUILDER_OP_NONE:
1786 gtk_constraint_expression_set_constant (expression: rbuilder->expression, constant: value);
1787 break;
1788
1789 case BUILDER_OP_PLUS:
1790 gtk_constraint_expression_plus_constant (expression: rbuilder->expression, constant: value);
1791 break;
1792
1793 case BUILDER_OP_MINUS:
1794 gtk_constraint_expression_minus_constant (expression: rbuilder->expression, constant: value);
1795 break;
1796
1797 case BUILDER_OP_MULTIPLY:
1798 gtk_constraint_expression_multiply_by (expression: rbuilder->expression, factor: value);
1799 break;
1800
1801 case BUILDER_OP_DIVIDE:
1802 gtk_constraint_expression_divide_by (expression: rbuilder->expression, factor: value);
1803 break;
1804
1805 default:
1806 break;
1807 }
1808
1809 rbuilder->op = BUILDER_OP_NONE;
1810}
1811
1812/*< private >
1813 * gtk_constraint_expression_builder_finish:
1814 * @builder: a `GtkConstraintExpression`Builder
1815 *
1816 * Closes the given expression builder, and returns the expression.
1817 *
1818 * You can only call this function once.
1819 *
1820 * Returns: (transfer full): the built expression
1821 */
1822GtkConstraintExpression *
1823gtk_constraint_expression_builder_finish (GtkConstraintExpressionBuilder *builder)
1824{
1825 RealExpressionBuilder *rbuilder = REAL_EXPRESSION_BUILDER (builder);
1826
1827 rbuilder->solver = NULL;
1828 rbuilder->op = BUILDER_OP_NONE;
1829
1830 return g_steal_pointer (&rbuilder->expression);
1831}
1832
1833/* }}} */
1834

source code of gtk/gtk/gtkconstraintexpression.c