Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
hungarian.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cmath>
18#include <cstdint>
19#include <cstdio>
20#include <limits>
21#include <vector>
22
23#include "absl/strings/str_format.h"
24#include "absl/types/span.h"
26
27namespace operations_research {
28
30 static constexpr int kHungarianOptimizerRowNotFound = -1;
31 static constexpr int kHungarianOptimizerColNotFound = -2;
32
33 public:
34 // Setup the initial conditions for the algorithm.
35
36 // Parameters: costs is a matrix of the cost of assigning each agent to
37 // each task. costs[i][j] is the cost of assigning agent i to task j.
38 // All the costs must be non-negative. This matrix does not have to
39 // be square (i.e. we can have different numbers of agents and tasks), but it
40 // must be regular (i.e. there must be the same number of entries in each row
41 // of the matrix).
42 explicit HungarianOptimizer(absl::Span<const std::vector<double>> costs);
43
44 // Find an assignment which maximizes the total cost.
45 // Returns the assignment in the two vectors passed as argument.
46 // preimage[i] is assigned to image[i].
47 void Maximize(std::vector<int>* preimage, std::vector<int>* image);
48
49 // Like Maximize(), but minimizing the cost instead.
50 void Minimize(std::vector<int>* preimage, std::vector<int>* image);
51
52 private:
53 typedef void (HungarianOptimizer::*Step)();
54
55 typedef enum : uint8_t { NONE, PRIME, STAR } Mark;
56
57 // Convert the final cost matrix into a set of assignments of preimage->image.
58 // Returns the assignment in the two vectors passed as argument, the same as
59 // Minimize and Maximize
60 void FindAssignments(std::vector<int>* preimage, std::vector<int>* image);
61
62 // Is the cell (row, col) starred?
63 bool IsStarred(int row, int col) const { return marks_[row][col] == STAR; }
64
65 // Mark cell (row, col) with a star
66 void Star(int row, int col) {
67 marks_[row][col] = STAR;
68 stars_in_col_[col]++;
69 }
70
71 // Remove a star from cell (row, col)
72 void UnStar(int row, int col) {
73 marks_[row][col] = NONE;
74 stars_in_col_[col]--;
75 }
76
77 // Find a column in row 'row' containing a star, or return
78 // kHungarianOptimizerColNotFound if no such column exists.
79 int FindStarInRow(int row) const;
80
81 // Find a row in column 'col' containing a star, or return
82 // kHungarianOptimizerRowNotFound if no such row exists.
83 int FindStarInCol(int col) const;
84
85 // Is cell (row, col) marked with a prime?
86 bool IsPrimed(int row, int col) const { return marks_[row][col] == PRIME; }
87
88 // Mark cell (row, col) with a prime.
89 void Prime(int row, int col) { marks_[row][col] = PRIME; }
90
91 // Find a column in row containing a prime, or return
92 // kHungarianOptimizerColNotFound if no such column exists.
93 int FindPrimeInRow(int row) const;
94
95 // Remove the prime marks_ from every cell in the matrix.
96 void ClearPrimes();
97
98 // Does column col contain a star?
99 bool ColContainsStar(int col) const { return stars_in_col_[col] > 0; }
100
101 // Is row 'row' covered?
102 bool RowCovered(int row) const { return rows_covered_[row]; }
103
104 // Cover row 'row'.
105 void CoverRow(int row) { rows_covered_[row] = true; }
106
107 // Uncover row 'row'.
108 void UncoverRow(int row) { rows_covered_[row] = false; }
109
110 // Is column col covered?
111 bool ColCovered(int col) const { return cols_covered_[col]; }
112
113 // Cover column col.
114 void CoverCol(int col) { cols_covered_[col] = true; }
115
116 // Uncover column col.
117 void UncoverCol(int col) { cols_covered_[col] = false; }
118
119 // Uncover ever row and column in the matrix.
120 void ClearCovers();
121
122 // Find the smallest uncovered cell in the matrix.
123 double FindSmallestUncovered() const;
124
125 // Find an uncovered zero and store its coordinates in (zeroRow_, zeroCol_)
126 // and return true, or return false if no such cell exists.
127 bool FindZero(int* zero_row, int* zero_col) const;
128
129 // Print the matrix to stdout (for debugging.)
130 void PrintMatrix();
131
132 // Run the Munkres algorithm!
133 void DoMunkres();
134
135 // Step 1.
136 // For each row of the matrix, find the smallest element and subtract it
137 // from every element in its row. Go to Step 2.
138 void ReduceRows();
139
140 // Step 2.
141 // Find a zero (Z) in the matrix. If there is no starred zero in its row
142 // or column, star Z. Repeat for every element in the matrix. Go to step 3.
143 // Note: profiling shows this method to use 9.2% of the CPU - the next
144 // slowest step takes 0.6%. I can't think of a way of speeding it up though.
145 void StarZeroes();
146
147 // Step 3.
148 // Cover each column containing a starred zero. If all columns are
149 // covered, the starred zeros describe a complete set of unique assignments.
150 // In this case, terminate the algorithm. Otherwise, go to step 4.
151 void CoverStarredZeroes();
152
153 // Step 4.
154 // Find a noncovered zero and prime it. If there is no starred zero in the
155 // row containing this primed zero, Go to Step 5. Otherwise, cover this row
156 // and uncover the column containing the starred zero. Continue in this manner
157 // until there are no uncovered zeros left, then go to Step 6.
158 void PrimeZeroes();
159
160 // Step 5.
161 // Construct a series of alternating primed and starred zeros as follows.
162 // Let Z0 represent the uncovered primed zero found in Step 4. Let Z1 denote
163 // the starred zero in the column of Z0 (if any). Let Z2 denote the primed
164 // zero in the row of Z1 (there will always be one). Continue until the
165 // series terminates at a primed zero that has no starred zero in its column.
166 // Unstar each starred zero of the series, star each primed zero of the
167 // series, erase all primes and uncover every line in the matrix. Return to
168 // Step 3.
169 void MakeAugmentingPath();
170
171 // Step 6.
172 // Add the smallest uncovered value in the matrix to every element of each
173 // covered row, and subtract it from every element of each uncovered column.
174 // Return to Step 4 without altering any stars, primes, or covered lines.
175 void AugmentPath();
176
177 // The size of the problem, i.e. max(#agents, #tasks).
178 int matrix_size_;
179
180 // The expanded cost matrix.
181 std::vector<std::vector<double>> costs_;
182
183 // The greatest cost in the initial cost matrix.
184 double max_cost_;
185
186 // Which rows and columns are currently covered.
187 std::vector<bool> rows_covered_;
188 std::vector<bool> cols_covered_;
189
190 // The marks_ (star/prime/none) on each element of the cost matrix.
191 std::vector<std::vector<Mark>> marks_;
192
193 // The number of stars in each column - used to speed up coverStarredZeroes.
194 std::vector<int> stars_in_col_;
195
196 // Representation of a path_ through the matrix - used in step 5.
197 std::vector<int> preimage_; // i.e. the agents
198 std::vector<int> image_; // i.e. the tasks
199
200 // The width_ and height_ of the initial (non-expanded) cost matrix.
201 int width_;
202 int height_;
203
204 // The current state of the algorithm
205 HungarianOptimizer::Step state_;
206};
207
209 absl::Span<const std::vector<double>> costs)
210 : matrix_size_(0),
211 costs_(),
212 max_cost_(0),
213 rows_covered_(),
214 cols_covered_(),
215 marks_(),
216 stars_in_col_(),
217 preimage_(),
218 image_(),
219 width_(0),
220 height_(0),
221 state_(nullptr) {
222 width_ = costs.size();
223
224 if (width_ > 0) {
225 height_ = costs[0].size();
226 } else {
227 height_ = 0;
228 }
229
230 matrix_size_ = std::max(width_, height_);
231 max_cost_ = 0;
232
233 // Generate the expanded cost matrix by adding extra 0-valued elements in
234 // order to make a square matrix. At the same time, find the greatest cost
235 // in the matrix (used later if we want to maximize rather than minimize the
236 // overall cost.)
237 costs_.resize(matrix_size_);
238 for (int row = 0; row < matrix_size_; ++row) {
239 costs_[row].resize(matrix_size_);
240 }
241
242 for (int row = 0; row < matrix_size_; ++row) {
243 for (int col = 0; col < matrix_size_; ++col) {
244 if ((row >= width_) || (col >= height_)) {
245 costs_[row][col] = 0;
246 } else {
247 costs_[row][col] = costs[row][col];
248 max_cost_ = std::max(max_cost_, costs_[row][col]);
249 }
250 }
251 }
252
253 // Initially, none of the cells of the matrix are marked.
254 marks_.resize(matrix_size_);
255 for (int row = 0; row < matrix_size_; ++row) {
256 marks_[row].resize(matrix_size_);
257 for (int col = 0; col < matrix_size_; ++col) {
258 marks_[row][col] = NONE;
259 }
260 }
261
262 stars_in_col_.resize(matrix_size_);
263
264 rows_covered_.resize(matrix_size_);
265 cols_covered_.resize(matrix_size_);
266
267 preimage_.resize(matrix_size_ * 2);
268 image_.resize(matrix_size_ * 2);
269}
270
271// Find an assignment which maximizes the total cost.
272// Return an array of pairs of integers. Each pair (i, j) corresponds to
273// assigning agent i to task j.
274void HungarianOptimizer::Maximize(std::vector<int>* preimage,
275 std::vector<int>* image) {
276 // Find a maximal assignment by subtracting each of the
277 // original costs from max_cost_ and then minimizing.
278 for (int row = 0; row < width_; ++row) {
279 for (int col = 0; col < height_; ++col) {
280 costs_[row][col] = max_cost_ - costs_[row][col];
281 }
282 }
283 Minimize(preimage, image);
284}
285
286// Find an assignment which minimizes the total cost.
287// Return an array of pairs of integers. Each pair (i, j) corresponds to
288// assigning agent i to task j.
289void HungarianOptimizer::Minimize(std::vector<int>* preimage,
290 std::vector<int>* image) {
291 DoMunkres();
292 FindAssignments(preimage, image);
293}
294
295// Convert the final cost matrix into a set of assignments of agents -> tasks.
296// Return an array of pairs of integers, the same as the return values of
297// Minimize() and Maximize()
298void HungarianOptimizer::FindAssignments(std::vector<int>* preimage,
299 std::vector<int>* image) {
300 preimage->clear();
301 image->clear();
302 for (int row = 0; row < width_; ++row) {
303 for (int col = 0; col < height_; ++col) {
304 if (IsStarred(row, col)) {
305 preimage->push_back(row);
306 image->push_back(col);
307 break;
308 }
309 }
310 }
311 // TODO(user)
312 // result_size = min(width_, height_);
313 // CHECK image.size() == result_size
314 // CHECK preimage.size() == result_size
315}
316
317// Find a column in row 'row' containing a star, or return
318// kHungarianOptimizerColNotFound if no such column exists.
319int HungarianOptimizer::FindStarInRow(int row) const {
320 for (int col = 0; col < matrix_size_; ++col) {
321 if (IsStarred(row, col)) {
322 return col;
323 }
324 }
325
326 return kHungarianOptimizerColNotFound;
327}
328
329// Find a row in column 'col' containing a star, or return
330// kHungarianOptimizerRowNotFound if no such row exists.
331int HungarianOptimizer::FindStarInCol(int col) const {
332 if (!ColContainsStar(col)) {
333 return kHungarianOptimizerRowNotFound;
334 }
335
336 for (int row = 0; row < matrix_size_; ++row) {
337 if (IsStarred(row, col)) {
338 return row;
339 }
340 }
341
342 // NOTREACHED
343 return kHungarianOptimizerRowNotFound;
344}
345
346// Find a column in row containing a prime, or return
347// kHungarianOptimizerColNotFound if no such column exists.
348int HungarianOptimizer::FindPrimeInRow(int row) const {
349 for (int col = 0; col < matrix_size_; ++col) {
350 if (IsPrimed(row, col)) {
351 return col;
352 }
353 }
354
355 return kHungarianOptimizerColNotFound;
356}
357
358// Remove the prime marks from every cell in the matrix.
359void HungarianOptimizer::ClearPrimes() {
360 for (int row = 0; row < matrix_size_; ++row) {
361 for (int col = 0; col < matrix_size_; ++col) {
362 if (IsPrimed(row, col)) {
363 marks_[row][col] = NONE;
364 }
365 }
366 }
367}
368
369// Uncovery ever row and column in the matrix.
370void HungarianOptimizer::ClearCovers() {
371 for (int x = 0; x < matrix_size_; x++) {
372 UncoverRow(x);
373 UncoverCol(x);
374 }
375}
376
377// Find the smallest uncovered cell in the matrix.
378double HungarianOptimizer::FindSmallestUncovered() const {
379 double minval = std::numeric_limits<double>::max();
380
381 for (int row = 0; row < matrix_size_; ++row) {
382 if (RowCovered(row)) {
383 continue;
384 }
385
386 for (int col = 0; col < matrix_size_; ++col) {
387 if (ColCovered(col)) {
388 continue;
389 }
390
391 minval = std::min(minval, costs_[row][col]);
392 }
393 }
394
395 return minval;
396}
397
398// Find an uncovered zero and store its co-ordinates in (zeroRow, zeroCol)
399// and return true, or return false if no such cell exists.
400bool HungarianOptimizer::FindZero(int* zero_row, int* zero_col) const {
401 for (int row = 0; row < matrix_size_; ++row) {
402 if (RowCovered(row)) {
403 continue;
404 }
405
406 for (int col = 0; col < matrix_size_; ++col) {
407 if (ColCovered(col)) {
408 continue;
409 }
410
411 if (costs_[row][col] == 0) {
412 *zero_row = row;
413 *zero_col = col;
414 return true;
415 }
416 }
417 }
418
419 return false;
420}
421
422// Print the matrix to stdout (for debugging.)
423void HungarianOptimizer::PrintMatrix() {
424 for (int row = 0; row < matrix_size_; ++row) {
425 for (int col = 0; col < matrix_size_; ++col) {
426 absl::PrintF("%g ", costs_[row][col]);
427
428 if (IsStarred(row, col)) {
429 absl::PrintF("*");
430 }
431
432 if (IsPrimed(row, col)) {
433 absl::PrintF("'");
434 }
435 }
436 absl::PrintF("\n");
437 }
438}
439
440// Run the Munkres algorithm!
441void HungarianOptimizer::DoMunkres() {
442 state_ = &HungarianOptimizer::ReduceRows;
443 while (state_ != nullptr) {
444 (this->*state_)();
445 }
446}
447
448// Step 1.
449// For each row of the matrix, find the smallest element and subtract it
450// from every element in its row. Go to Step 2.
451void HungarianOptimizer::ReduceRows() {
452 for (int row = 0; row < matrix_size_; ++row) {
453 double min_cost = costs_[row][0];
454 for (int col = 1; col < matrix_size_; ++col) {
455 min_cost = std::min(min_cost, costs_[row][col]);
456 }
457 for (int col = 0; col < matrix_size_; ++col) {
458 costs_[row][col] -= min_cost;
459 }
460 }
461 state_ = &HungarianOptimizer::StarZeroes;
462}
463
464// Step 2.
465// Find a zero (Z) in the matrix. If there is no starred zero in its row
466// or column, star Z. Repeat for every element in the matrix. Go to step 3.
467void HungarianOptimizer::StarZeroes() {
468 // Since no rows or columns are covered on entry to this step, we use the
469 // covers as a quick way of marking which rows & columns have stars in them.
470 for (int row = 0; row < matrix_size_; ++row) {
471 if (RowCovered(row)) {
472 continue;
473 }
474
475 for (int col = 0; col < matrix_size_; ++col) {
476 if (ColCovered(col)) {
477 continue;
478 }
479
480 if (costs_[row][col] == 0) {
481 Star(row, col);
482 CoverRow(row);
483 CoverCol(col);
484 break;
485 }
486 }
487 }
488
489 ClearCovers();
490 state_ = &HungarianOptimizer::CoverStarredZeroes;
491}
492
493// Step 3.
494// Cover each column containing a starred zero. If all columns are
495// covered, the starred zeros describe a complete set of unique assignments.
496// In this case, terminate the algorithm. Otherwise, go to step 4.
497void HungarianOptimizer::CoverStarredZeroes() {
498 int num_covered = 0;
499
500 for (int col = 0; col < matrix_size_; ++col) {
501 if (ColContainsStar(col)) {
502 CoverCol(col);
503 num_covered++;
504 }
505 }
506
507 if (num_covered >= matrix_size_) {
508 state_ = nullptr;
509 return;
510 }
511 state_ = &HungarianOptimizer::PrimeZeroes;
512}
513
514// Step 4.
515// Find a noncovered zero and prime it. If there is no starred zero in the
516// row containing this primed zero, Go to Step 5. Otherwise, cover this row
517// and uncover the column containing the starred zero. Continue in this manner
518// until there are no uncovered zeros left, then go to Step 6.
519
520void HungarianOptimizer::PrimeZeroes() {
521 // This loop is guaranteed to terminate in at most matrix_size_ iterations,
522 // as findZero() returns a location only if there is at least one uncovered
523 // zero in the matrix. Each iteration, either one row is covered or the
524 // loop terminates. Since there are matrix_size_ rows, after that many
525 // iterations there are no uncovered cells and hence no uncovered zeroes,
526 // so the loop terminates.
527 for (;;) {
528 int zero_row, zero_col;
529 if (!FindZero(&zero_row, &zero_col)) {
530 // No uncovered zeroes.
531 state_ = &HungarianOptimizer::AugmentPath;
532 return;
533 }
534
535 Prime(zero_row, zero_col);
536 int star_col = FindStarInRow(zero_row);
537
538 if (star_col != kHungarianOptimizerColNotFound) {
539 CoverRow(zero_row);
540 UncoverCol(star_col);
541 } else {
542 preimage_[0] = zero_row;
543 image_[0] = zero_col;
544 state_ = &HungarianOptimizer::MakeAugmentingPath;
545 return;
546 }
547 }
548}
549
550// Step 5.
551// Construct a series of alternating primed and starred zeros as follows.
552// Let Z0 represent the uncovered primed zero found in Step 4. Let Z1 denote
553// the starred zero in the column of Z0 (if any). Let Z2 denote the primed
554// zero in the row of Z1 (there will always be one). Continue until the
555// series terminates at a primed zero that has no starred zero in its column.
556// Unstar each starred zero of the series, star each primed zero of the
557// series, erase all primes and uncover every line in the matrix. Return to
558// Step 3.
559void HungarianOptimizer::MakeAugmentingPath() {
560 bool done = false;
561 int count = 0;
562
563 // Note: this loop is guaranteed to terminate within matrix_size_ iterations
564 // because:
565 // 1) on entry to this step, there is at least 1 column with no starred zero
566 // (otherwise we would have terminated the algorithm already.)
567 // 2) each row containing a star also contains exactly one primed zero.
568 // 4) each column contains at most one starred zero.
569 //
570 // Since the path_ we construct visits primed and starred zeroes alternately,
571 // and terminates if we reach a primed zero in a column with no star, our
572 // path_ must either contain matrix_size_ or fewer stars (in which case the
573 // loop iterates fewer than matrix_size_ times), or it contains more. In
574 // that case, because (1) implies that there are fewer than
575 // matrix_size_ stars, we must have visited at least one star more than once.
576 // Consider the first such star that we visit more than once; it must have
577 // been reached immediately after visiting a prime in the same row. By (2),
578 // this prime is unique and so must have also been visited more than once.
579 // Therefore, that prime must be in the same column as a star that has been
580 // visited more than once, contradicting the assumption that we chose the
581 // first multiply visited star, or it must be in the same column as more
582 // than one star, contradicting (3). Therefore, we never visit any star
583 // more than once and the loop terminates within matrix_size_ iterations.
584
585 while (!done) {
586 // First construct the alternating path...
587 int row = FindStarInCol(image_[count]);
588
589 if (row != kHungarianOptimizerRowNotFound) {
590 count++;
591 preimage_[count] = row;
592 image_[count] = image_[count - 1];
593 } else {
594 done = true;
595 }
596
597 if (!done) {
598 int col = FindPrimeInRow(preimage_[count]);
599 count++;
600 preimage_[count] = preimage_[count - 1];
601 image_[count] = col;
602 }
603 }
604
605 // Then modify it.
606 for (int i = 0; i <= count; ++i) {
607 int row = preimage_[i];
608 int col = image_[i];
609
610 if (IsStarred(row, col)) {
611 UnStar(row, col);
612 } else {
613 Star(row, col);
614 }
615 }
616
617 ClearCovers();
618 ClearPrimes();
619 state_ = &HungarianOptimizer::CoverStarredZeroes;
620}
621
622// Step 6
623// Add the smallest uncovered value in the matrix to every element of each
624// covered row, and subtract it from every element of each uncovered column.
625// Return to Step 4 without altering any stars, primes, or covered lines.
626void HungarianOptimizer::AugmentPath() {
627 double minval = FindSmallestUncovered();
628
629 for (int row = 0; row < matrix_size_; ++row) {
630 for (int col = 0; col < matrix_size_; ++col) {
631 if (RowCovered(row)) {
632 costs_[row][col] += minval;
633 }
634
635 if (!ColCovered(col)) {
636 costs_[row][col] -= minval;
637 }
638 }
639 }
640
641 state_ = &HungarianOptimizer::PrimeZeroes;
642}
643
644bool InputContainsNan(absl::Span<const std::vector<double>> input) {
645 for (const auto& subvector : input) {
646 for (const auto& num : subvector) {
647 if (std::isnan(num)) {
648 LOG(ERROR) << "The provided input contains " << num << ".";
649 return true;
650 }
651 }
652 }
653 return false;
654}
655
657 absl::Span<const std::vector<double>> cost,
658 absl::flat_hash_map<int, int>* direct_assignment,
659 absl::flat_hash_map<int, int>* reverse_assignment) {
660 if (InputContainsNan(cost)) {
661 LOG(ERROR) << "Returning before invoking the Hungarian optimizer.";
662 return;
663 }
664 std::vector<int> agent;
665 std::vector<int> task;
666 HungarianOptimizer hungarian_optimizer(cost);
667 hungarian_optimizer.Minimize(&agent, &task);
668 for (int i = 0; i < agent.size(); ++i) {
669 (*direct_assignment)[agent[i]] = task[i];
670 (*reverse_assignment)[task[i]] = agent[i];
671 }
672}
673
675 absl::Span<const std::vector<double>> cost,
676 absl::flat_hash_map<int, int>* direct_assignment,
677 absl::flat_hash_map<int, int>* reverse_assignment) {
678 if (InputContainsNan(cost)) {
679 LOG(ERROR) << "Returning before invoking the Hungarian optimizer.";
680 return;
681 }
682 std::vector<int> agent;
683 std::vector<int> task;
684 HungarianOptimizer hungarian_optimizer(cost);
685 hungarian_optimizer.Maximize(&agent, &task);
686 for (int i = 0; i < agent.size(); ++i) {
687 (*direct_assignment)[agent[i]] = task[i];
688 (*reverse_assignment)[task[i]] = agent[i];
689 }
690}
691
692} // namespace operations_research
void Minimize(std::vector< int > *preimage, std::vector< int > *image)
Like Maximize(), but minimizing the cost instead.
Definition hungarian.cc:289
HungarianOptimizer(absl::Span< const std::vector< double > > costs)
Setup the initial conditions for the algorithm.
Definition hungarian.cc:208
void Maximize(std::vector< int > *preimage, std::vector< int > *image)
Definition hungarian.cc:274
In SWIG mode, we don't want anything besides these top-level includes.
bool InputContainsNan(absl::Span< const std::vector< double > > input)
Definition hungarian.cc:644
void MinimizeLinearAssignment(absl::Span< const std::vector< double > > cost, absl::flat_hash_map< int, int > *direct_assignment, absl::flat_hash_map< int, int > *reverse_assignment)
See IMPORTANT NOTE at the top of the file.
Definition hungarian.cc:656
void MaximizeLinearAssignment(absl::Span< const std::vector< double > > cost, absl::flat_hash_map< int, int > *direct_assignment, absl::flat_hash_map< int, int > *reverse_assignment)
See IMPORTANT NOTE at the top of the file.
Definition hungarian.cc:674
static int input(yyscan_t yyscanner)