├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── example
├── misc
│ ├── drive.dart
│ ├── html_docs.dart
│ ├── menu.dart
│ ├── sheet.dart
│ └── src
│ │ └── data.dart
├── readme.md
└── tournament
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── analysis_options.yaml
│ ├── bin
│ └── main.dart
│ ├── lib
│ ├── menu.dart
│ └── src
│ │ ├── brackets.dart
│ │ ├── cache.dart
│ │ ├── config.dart
│ │ ├── next_round.dart
│ │ ├── reports.dart
│ │ ├── tournament.dart
│ │ └── utils.dart
│ └── pubspec.yaml
├── lib
├── cache.dart
├── document.dart
├── drive.dart
├── google_apps.dart
├── html.dart
├── lock.dart
├── properties.dart
├── spreadsheet.dart
├── tasks.dart
├── ui.dart
└── url_fetch.dart
└── pubspec.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Files and directories created by pub
2 | .packages
3 | .pub/
4 | build/
5 | # Remove the following pattern if you wish to check in your lock file
6 | pubspec.lock
7 |
8 | # Directory created by dartdoc
9 | doc/api/
10 |
11 | out
12 |
13 | .idea
14 | google-apps.iml
15 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 0.1.2
4 | - Run dartfmt.
5 |
6 | ## 0.1.1
7 | - Add link to medium article in README.
8 |
9 | ## 0.1.0
10 |
11 | - Add a big tournament example.
12 | - Add a tiny drive example.
13 | - Fix some linter complaints.
14 |
15 | ## 0.0.1+3
16 |
17 | - Fix the sheet example.
18 |
19 | ## 0.0.1+2
20 |
21 | - Update README (removing the example and adding the issue tracker).
22 |
23 | ## 0.0.1+1
24 |
25 | - Rename the example file so it's recognized by pub.dartlang.org.
26 |
27 | ## 0.0.1
28 |
29 | - Initial version.
30 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution,
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dart APIs for Google Apps Script
2 |
3 | This is not an official Google product. It is not supported by the Dart team.
4 |
5 | See [article] for a blog post explaining how to use this package.
6 |
7 | [article]: https://medium.com/@florian_32814/google-apps-scripts-with-dart-402c042fa606
8 |
9 | This package is still in an experimental state.
10 |
11 | A library to write Google Apps Script.
12 |
13 | This library has been written on a per-need basis. As such it is missing lots
14 | of useful functionality that I just hadn't needed yet. Until the API coverage
15 | is nearing completeness I recommend to checkout the GIT repository during
16 | development and to use this library with a `path` directive, adding the missing
17 | functions when they are encountered.
18 |
19 | Consider contributing your changes back to the original repository.
20 |
21 | ## Features and bugs
22 |
23 | Please file feature requests and bugs at the [issue tracker][tracker].
24 |
25 | [tracker]: https://github.com/google/dart_google_apps/issues
26 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https:#www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | analyzer:
16 | strong-mode: true
17 | # exclude:
18 | # - path/to/excluded/files/**
19 |
20 | # Lint rules and documentation, see http://dart-lang.github.io/linter/lints
21 | linter:
22 | rules:
23 | - hash_and_equals
24 | - iterable_contains_unrelated_type
25 | - list_remove_unrelated_type
26 | - test_types_in_equals
27 | - unrelated_type_equality_checks
28 | - valid_regexps
29 |
--------------------------------------------------------------------------------
/example/misc/drive.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /// Compile this example with
16 | /// `dart2js --csp -o drive.js example/drive.dart`.
17 | ///
18 | /// See [apps_script_tools](https://pub.dartlang.org/packages/apps_script_tools)
19 | /// for a description on how to execute the generated program.
20 |
21 | import 'package:google_apps/drive.dart';
22 |
23 | main() {
24 | DriveApp.createFile("hello.txt", "Hello from Dart");
25 | }
26 |
--------------------------------------------------------------------------------
/example/misc/html_docs.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /// Compile this example with
16 | /// `dart2js --csp -o html_docs.js example/html_docs.dart`.
17 | ///
18 | /// See [apps_script_tools](https://pub.dartlang.org/packages/apps_script_tools)
19 | /// for a description on how to execute the generated program.
20 |
21 | @JS()
22 | library html_docs;
23 |
24 | import 'package:js/js.dart';
25 | import 'package:google_apps/google_apps.dart';
26 |
27 | @JS()
28 | external set onOpen(value);
29 |
30 | @JS()
31 | external set demo(value);
32 |
33 | @JS()
34 | external set modal(value);
35 |
36 | void onOpenDart(e) {
37 | SpreadsheetApp
38 | .getUi()
39 | .createMenu("Dart")
40 | .addItem("demo", "demo")
41 | .addToUi();
42 | }
43 |
44 | void exportToJs() {
45 | onOpen = allowInterop(onOpenDart);
46 | demo = allowInterop(demoDart);
47 | modal = allowInterop(modalDart);
48 | }
49 |
50 | String sideBar = r"""
51 |
52 |
116 |
117 |
118 | """;
119 | HtmlOutput userInterface = HtmlService.createHtmlOutput(html.toString());
120 | SpreadsheetApp.getUi().showModalDialog(userInterface, "Document Ready");
121 | }
122 |
123 | main() {
124 | exportToJs();
125 | }
126 |
--------------------------------------------------------------------------------
/example/misc/menu.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /// Compile this example with
16 | /// `dart2js --csp -o hello.js example/hello_docs.dart`.
17 | ///
18 | /// See [apps_script_tools](https://pub.dartlang.org/packages/apps_script_tools)
19 | /// for a description on how to execute the generated program.
20 |
21 | @JS()
22 | library hello_docs;
23 |
24 | import 'package:js/js.dart';
25 | import 'package:google_apps/document.dart';
26 |
27 | @JS()
28 | external set sayHello(value);
29 |
30 | @JS()
31 | external set onOpen(value);
32 |
33 | void sayHelloDart() {
34 | DocumentApp.getUi().alert("Hello world");
35 | }
36 |
37 | void onOpenDart(e) {
38 | DocumentApp
39 | .getUi()
40 | .createMenu("from dart")
41 | .addItem("say hello", "sayHello")
42 | .addToUi();
43 | }
44 |
45 | main(List arguments) {
46 | onOpen = allowInterop(onOpenDart);
47 | sayHello = allowInterop(sayHelloDart);
48 | }
49 |
--------------------------------------------------------------------------------
/example/misc/sheet.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /// Compile this example with
16 | /// `dart2js --csp -o sheet.js example/sheet.dart`.
17 | ///
18 | /// See [apps_script_tools](https://pub.dartlang.org/packages/apps_script_tools)
19 | /// for a description on how to execute the generated program.
20 |
21 | @JS()
22 | library sheet;
23 |
24 | import 'package:js/js.dart';
25 | import 'package:google_apps/spreadsheet.dart';
26 | import 'src/data.dart';
27 |
28 | @JS()
29 | external set onOpen(value);
30 |
31 | @JS()
32 | external set demo(value);
33 |
34 | void onOpenDart(e) {
35 | SpreadsheetApp
36 | .getUi()
37 | .createMenu("Dart")
38 | .addItem("demo", "demo")
39 | .addToUi();
40 | }
41 |
42 | void exportToJs() {
43 | onOpen = allowInterop(onOpenDart);
44 | demo = allowInterop(demoDart);
45 | }
46 |
47 | void demoDart() {
48 | var sheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet();
49 | var imageHeight = imageData.length ~/ imageWidth;
50 | if (sheet.getMaxRows() < imageHeight + 1) {
51 | sheet.insertRows(1, imageHeight - sheet.getMaxRows());
52 | }
53 | if (sheet.getMaxColumns() < imageWidth + 1) {
54 | sheet.insertColumns(1, imageWidth - sheet.getMaxColumns());
55 | }
56 | for (int i = 0; i < imageWidth; i++) {
57 | sheet.setColumnWidth(i + 1, 2);
58 | }
59 | for (int i = 0; i < imageHeight; i++) {
60 | sheet.setRowHeight(i + 1, 3);
61 | }
62 | var colors = [];
63 | var index = 0;
64 | for (int i = 0; i < imageHeight; i++) {
65 | var row = [];
66 | colors.add(row);
67 | for (int j = 0; j < imageWidth; j++) {
68 | var color = imageData[index++];
69 | if (color == 0) {
70 | row.add(null);
71 | continue;
72 | }
73 | var r = (color & 0xFF000000) >> 32;
74 | var r2 = r.toRadixString(16);
75 | if (r2.length != 2) r2 = "0$r2";
76 | var g = (color & 0xFF00) >> 8;
77 | var g2 = g.toRadixString(16);
78 | if (g2.length != 2) g2 = "0$g2";
79 | var b = (color & 0xFF0000) >> 16;
80 | var b2 = b.toRadixString(16);
81 | if (b2.length != 2) b2 = "0$b2";
82 | row.add("#$r2$g2$b2");
83 | }
84 | }
85 | sheet.getRange(1, 1, imageHeight, imageWidth).setBackgrounds(colors);
86 | }
87 |
88 | main() {
89 | exportToJs();
90 | }
91 |
--------------------------------------------------------------------------------
/example/misc/src/data.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | const imageWidth = 128;
16 | const imageData = const [0,568894464,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2313199360,4226393344,3085082624,234127616,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,788736,3672744960,4293502208,4259947776,4025066496,1474010112,4276480,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85069056,4041843968,4293502208,4293502208,4293502208,4125729792,2950339072,323695616,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,466523392,4192838656,4293502208,4293502208,4293502208,4293502208,4226393344,3790185472,1473024256,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1523224320,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4025066752,2849544448,359419136,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2800526592,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4209616128,3605570816,1455523840,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52829184,3471352832,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3941115136,2748816128,327704320,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,220863744,3773342720,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4209616128,3454706944,1420983552,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,531989760,4025066752,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4276724992,3874071808,2632488704,262106880,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1035305216,4209616128,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4243170560,3337069568,1369732352,525824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1706196224,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3806962944,2460251136,295726592,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,177763328,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2398010368,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4226393344,3186205696,1318480384,19208192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,74073856,2664337920,4209616128,3135874304,681012224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37036800,2867373312,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3739854080,2292807680,361393408,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1082942976,3756631296,4293502208,4293502208,4293502208,4209616128,2800591360,479625216,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120989184,3186205696,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4192838912,3035211008,1250516992,21112832,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24990464,2480248576,4209616128,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4108887296,2461959680,273758208,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,271984128,3488195840,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3706299392,2142469888,394744320,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,914579968,3672744960,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3991512064,2109901312,123026432,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,456467712,3756565760,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4176061696,2900993280,1182488320,5658112,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2279513856,4226393344,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3857294336,1757908992,23413504,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,682389760,3974735104,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3689522176,1992460800,374618880,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,745755136,3588793344,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3655902464,1400132096,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,933918720,4142441728,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4176061696,2783618048,1080904960,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34014464,2045157888,4276724992,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3370755328,1080970496,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1252554240,4243170560,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3672745216,1809423104,308101376,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,628380416,3437798656,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4276724992,3035145472,795166720,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51646208,406267906,792209410,1194862337,1597515777,2021613314,2004835075,1602181891,1044853506,373568259,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1621718784,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4243170304,2666112256,979387136,197120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68226048,1843895808,4276724992,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4292319232,4290676481,4290807809,4292516352,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4259947776,2649335296,509822720,0,0,0,0,0,0,338896129,791946497,1244931329,1683046913,2155698947,2692700932,3229571844,3766442756,4219427588,4269759236,3732888324,2860407811,1766470914,541734401,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2057992192,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3655902464,1641916160,291061504,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1445376,812535296,3270026240,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4290347778,4286536452,4286536452,4286536452,4286601988,4290808065,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436416,4292911872,4291993088,2261554177,1513169665,2071878404,2759809795,3430898435,4101987076,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,3850328835,2927581699,1749956610,491665921,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3154944,2531828736,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4276724992,2565448704,879840768,34737408,0,0,0,0,0,0,0,0,0,0,85528832,1181961472,3184560896,4291922689,4275606272,4293436416,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4291990784,4286536452,4286536452,4286536452,4286536452,4286536452,4286667780,4292516608,4293502208,4293502208,4293305088,4292582400,4291728640,4291005185,4289691394,4288050178,4286931971,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4135541508,3380566788,2321827842,793918467,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52960512,2984879360,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3588859136,1509200384,290141440,0,0,0,0,0,0,306392320,1656788992,3822160641,4291922689,4291922689,4291922689,4291922689,4241920256,4293436416,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4288902146,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4288507650,4289099522,4287588867,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,3816774403,2961136643,1481981187,136254977,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,119806208,3421086720,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4243170560,2464720128,759443456,35394816,2631680,624765184,2328988160,4208036609,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4224945664,4293370624,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4288507650,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286800387,4286932483,4286734595,4286536708,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,3833551619,2843040003,945438466,986880,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,186520832,3672745216,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436416,3588529920,3151467008,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4224879616,4293304832,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4292779264,4289887746,4236467971,4286536452,4286536452,4286536452,4286536452,4286536452,4286536452,4289111809,4290366464,4290366464,4290300672,4289904640,4289442304,4288979969,4288517634,4288055298,4287592706,4287329283,4287196931,3666109955,1499481603,17631488,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,202969344,3840517376,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436416,4276329984,4258565889,3805515008,4293502208,3940391168,4241591041,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4275145472,4259750144,4293502208,4293502208,4293502208,4292779264,4289821954,4186334467,4003236865,4239837184,4287064835,4286536452,4286536452,4286536452,4286536452,4287461379,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4273589248,2730085632,17303808,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,186126848,3974735104,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293370624,4276198400,4275211521,4291922689,4291922689,3957102336,4293502208,4293502208,3991183360,4191390976,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4226064384,4292845056,4289756418,4203178243,3936525057,4290300672,4290366464,4290366464,4290168576,4287725058,4286932739,4286998531,4288055554,4290300416,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4256812032,2043008512,132096,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,198413568,4075398400,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293304832,4276001024,4275145473,4291922689,4291922689,4291922689,4291922689,3856768000,4293502208,4293502208,4293502208,4159218688,3990393344,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4136462851,4037717505,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4256812032,1905303040,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,214270720,4142507008,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293304832,4292317440,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4191259393,4192773120,4293502208,4293502208,4293502208,4293502208,4293502208,3823213568,4224813825,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3971792896,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4256812032,1906360320,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,298291200,4159284224,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436416,4293304832,4292120064,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3654783488,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4058489344,4208168192,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4206544384,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4273589248,2495007232,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,516460544,4192773376,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436416,4276461824,4275211521,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4108360448,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4243170304,3705378048,4275145473,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4189702400,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4273589248,3031877888,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,751341056,4226393344,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436416,4293107200,4275145473,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4275145473,3940917248,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3823476992,4241591041,4291922689,4291922689,4291922689,4291922689,4258302721,4256811776,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3602303232,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1036290560,4259947776,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436416,4292909824,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3771828992,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4159218688,4057173248,4291922689,4291922689,4291922689,3452867073,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3971533824,23249664,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1371572224,4276724992,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436416,4292515328,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3856439296,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4276724992,3353320192,4291922689,4291922689,3351489280,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,1015716096,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1740210944,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436160,4292251904,4291922688,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3940719872,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4276659200,4008158208,4275145473,4156278272,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,2696531200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2075295232,4293502208,4293502208,4293502208,4293502208,4293502208,4293304576,4292120064,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4241591041,4192773120,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4226327552,3334986044,3301168426,3619541011,4223257601,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3602500608,34015232,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1050624,2330894592,4293502208,4293502208,4292778496,4291988480,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3570831616,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4259882240,4107066186,4291550798,4291550798,4291550798,4274773582,4224376141,4190821708,4190821706,4241087560,4039695174,3519535679,3318011439,3502166298,4005219590,4290366464,4290366464,4206480384,528583680,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,714959616,3906639104,4291922688,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4125203456,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4259882240,3872184390,4274773326,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4274773582,4224376141,4174044492,2848710217,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1254460672,3133574144,4041712384,4276659200,4293436416,4292383488,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3991249152,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4276724992,3604075070,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,1904249905,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,262912,1052739584,2864678912,3773342720,4159284480,4293502208,4293502208,4293502208,4293502208,4293502208,4293436416,4292449280,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3805449217,4276659200,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3486894129,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,746687537,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2365440,867730432,2596111872,3639125248,4092175616,4276724992,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436416,4275803392,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3755841536,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3638409758,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4006338126,18488586,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,665878272,2343730432,3504907264,4025066752,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436416,4275803648,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4058226432,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3890328083,4241219150,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,3284852301,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,531396864,2125166848,3353912320,3941180672,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436416,4275869696,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4241591041,4175930112,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4058230542,4174110029,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,2530928438,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,413824512,1906603008,3169297408,3840517120,4276724992,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293501952,4275935232,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3520499712,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4091849739,4073512011,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,1584956206,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,293098240,1688039168,2983301888,3739788544,4259947776,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293436672,4275935488,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4058160640,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4091980038,4023244870,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,595100205,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,143483136,1486318080,2816187136,3622413568,4243170560,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4276066816,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4058423552,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4159153923,3956331584,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,3989560910,2307345,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,368088320,3135874304,4243170304,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4276132352,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3872558081,4259881984,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4243105024,3855798075,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,3251297614,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,476070912,1197425408,2027131904,3051988224,4058621184,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4276132608,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3621623808,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3755330099,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,2496189770,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22427904,610682880,1399212032,2246550528,3236537600,4125730048,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4276198400,4275145473,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4142243840,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3755655720,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,1601206827,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71313920,762137856,1600998656,2481562368,3404244224,4209616128,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4259421440,4275145473,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,4258368257,4175930112,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3873290781,4257996366,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,678525739,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125529600,863261184,1651790336,2448008448,3219760128,3991512064,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4259552768,4258368257,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3503656960,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,3957372182,4174044493,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,3838500430,20658967,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1380352,528111104,1367037440,2196950016,2951324672,3689456640,4276724992,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4259553024,4241591041,4291922689,4291922689,4291922689,4291922689,4291922689,3974340096,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4007833873,4073446476,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,3754614350,982955069,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175658240,1031953152,1904368640,2699601152,3404243968,4092175616,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4259553024,4241591041,4291922689,4291922689,4291922689,4291922689,4142309888,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4024806410,4006468168,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,2949438798,288766484,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,596140032,1435066624,2261492480,2917704960,3488195840,4075398400,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4259618816,4224813825,4291922689,4291922689,3956378369,4259881984,4293502208,4293502208,4293502208,4293502208,4293502208,4108822533,4006597954,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4140555854,1838786362,4276480,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5329152,546268928,1401906688,2276887552,2951324672,3488195584,4025066752,4293502208,4293502208,4293502208,4293502208,4293502208,4293502208,4259684608,4208036608,4291922689,3487340288,4293502208,4293502208,4293502208,4293502208,4293502208,4209550849,3956396349,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,3620461902,746490161,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3026432,479619328,1385654784,2292544000,3001591040,3504973056,4008289536,4293502208,4259684608,4208036608,4209418752,4293502208,4293502208,4293502208,4293502208,4276724992,3822243642,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,2781667403,153693711,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,525312,396194048,1352560640,2358930432,3940588800,4142375680,4293502208,4293502208,4293502208,3688220721,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4073446990,1520019258,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,142990848,4073428992,4258368257,3587740160,4041383168,3671768354,4274773582,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,3553287758,429431604,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1068350720,3703292928,4275079937,4291922689,4291922689,3771705886,4257996109,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4274773582,2630407755,51911176,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2091629824,4290366464,4174287104,4291922689,4291922689,4291922689,3889021234,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4023115342,1084206652,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3081551104,4290366464,3989284608,4291922689,4291922689,4291922689,4174416385,4073444421,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4274773582,3570130254,113822051,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3937783296,4290366464,4172925440,4275080193,4291922689,4291922689,4291922689,3704657933,4224441420,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4241219150,2359801408,65792,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4055419648,4290366464,4290366464,4207712000,4291922689,4291922689,4291922689,4291922689,3905860647,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,3956006222,664908093,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4105751296,4290366464,4290366464,3904878848,4291857153,4291922689,4291922689,4291922689,4291922689,3855403322,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4274773582,3368540749,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4172925952,4290366464,4290366464,4290366464,4258238464,4291922689,4291922689,4291922689,4291922689,3922824451,4190886474,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4291550798,4207664718,1907870019,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4239969280,4290366464,4290366464,4290366464,4274561024,4291922689,4291922689,4291922689,4291922689,4291922689,3838748443,4274773325,4291550798,4291550798,4291550798,4291550798,4274773582,3905674574,412263224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41809152,4273589248,4290366464,4290366464,4290366464,4105881600,4291857153,4291922689,4291922689,4291922689,4291922689,4291922689,3821975595,4291550798,4291550798,4291550798,4274773581,3049904973,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,682673920,4290366464,4290366464,4290366464,4290366464,4290366464,4207712000,4291922689,4291922689,4291922689,4291922689,4291922689,4208036609,3972781125,4291550798,4257930571,1524295767,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1353958400,4290366464,4290366464,4290366464,4290366464,4290366464,4257460480,4291922689,4291922689,4291922689,4291922689,4291922689,4291922689,3805254923,4173518121,4273589248,3786984448,59345152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2024981760,4290366464,4290366464,4290366464,4290366464,4290366464,4273588992,4291792641,4291922689,4291922689,4291922689,4291922689,4291922689,4275080449,4273589248,4290366464,4290366464,3820538880,125800448,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2528364032,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4257849088,4291922689,4291922689,4291922689,4291922689,4291922689,4291727361,4290366464,4290366464,4290366464,4290366464,4038642432,646024960,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2931017216,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4156408064,4291922689,4291922689,4291922689,4291922689,4291922689,4291728129,4290366464,4290366464,4290366464,4290366464,4290366464,4189702912,1839575808,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3333670144,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4258108928,4291922689,4291922689,4291922689,4291922689,4258043393,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4256812032,3333801728,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3736323328,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4274301952,4291922689,4291922689,4291922689,4291922689,4157315329,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3988376320,1385540352,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,256,4105620224,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4240099584,4291857665,4291922689,4291922689,4291922689,4090141952,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4273589248,3451374080,143565312,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65792,4156082944,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4224294656,4291922689,4291922689,4291922689,4056457216,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4156148736,2559548928,51121408,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65792,4156148736,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4223580672,4291922689,4291922689,4291922689,4073169664,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3954821888,2039389184,35002368,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65792,4156082944,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4273588992,4291662593,4291922689,4291922689,4123436544,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3753495296,1821219840,52437504,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65792,4156148480,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4257589760,4291922689,4291922689,4207323136,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3820604416,2173541376,153561856,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65792,4156083200,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4223386880,4291857153,4291922689,4274366720,4290366464,4290366464,4290366464,4290366464,4290366464,4256812032,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3971599360,2475662592,221197568,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65792,4156083200,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4207517184,4291922689,4291079168,4290366464,4290366464,4290366464,4290366464,4290366464,4089039872,2914634752,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,558452992,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4038444800,4290366464,4290366464,4290366464,4290366464,3636055040,4290366464,4290366464,4290366464,4290366464,4290366464,4223710720,4291922689,4291014656,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,2881014528,2797063168,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3485060096,2697472,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3585328384,4290366464,4290366464,4290366464,2981743616,2543890688,4290366464,4290366464,4290366464,4290366464,4290366464,4240034560,4274950401,4240552960,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4172925952,694447616,2763639808,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,1685488128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3115500800,4290366464,4290366464,4290366464,694579456,2123341312,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4173703680,4106270720,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3585723136,50924032,2511915776,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4072262656,255804160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2629027072,4290366464,4290366464,3954822144,85005056,1720687872,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4005348608,4022254592,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4273589248,1554233088,17172224,2092551680,4273589248,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,1890897408,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2125710592,4290366464,4290366464,1622854144,0,1318034944,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3652896768,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4072197120,38427904,197376,1622918400,4240034816,4290366464,4290366464,4290366464,4290366464,4290366464,3837381632,203366912,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1270072832,4290366464,4290366464,370875904,0,797875456,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,2881080320,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,2763179008,0,0,1063414784,3988376320,4290366464,4290366464,4290366464,4290366464,1132301312,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,295491584,4273589248,3787049984,17830144,0,193499648,4273589248,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,1958333440,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4223257600,457789440,0,0,207973120,3233140224,4273589248,4290366464,2662845440,2039552,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4223257600,1404553728,0,0,0,4139371520,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,1387050496,3770272512,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3770272512,65792,0,0,0,2293153792,3636055040,203103744,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4105751296,252974592,0,0,0,3921267456,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,1566862592,2360920576,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4273589248,1320930816,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3015363584,855808,0,0,0,3636054784,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,1734569216,1179538944,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,105071616,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3283667968,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,1902341376,188169216,4172925952,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,2629422080,263424,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2575865088,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,2070113536,0,2881014528,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4256812032,168759552,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1401394688,4273589248,4290366464,4290366464,4290366464,4290366464,4290366464,2237885696,0,1435999744,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4223257600,1083154176,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3518614272,4290366464,4290366464,4290366464,4290366464,4290366464,2394802432,0,457592320,4273589248,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,4156082944,680698368,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,241792000,3803761408,4290366464,4290366464,4290366464,4290366464,2545404416,0,0,3367619584,4290366464,4290366464,4290366464,4290366464,4290366464,4290366464,3971599360,395550976,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,291861248,3854158592,4290366464,4290366464,4290366464,2679688192,0,0,1638184704,4290366464,4290366464,4290366464,4290366464,4273589248,3702966272,23448064,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,342256896,3921201920,4290366464,4290366464,2830683136,0,0,524240384,4273589248,4290366464,4290366464,4256812032,3098920960,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,291596544,3870870272,4290366464,2981743616,0,0,0,3350842112,4290366464,4189637376,1756018432,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,190801152,3787049728,3115895552,0,0,0,1370669824,3568683008,159419904,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,123363840,2629356544,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
17 |
--------------------------------------------------------------------------------
/example/readme.md:
--------------------------------------------------------------------------------
1 | # Google Apps Libraries
2 |
3 | This library wraps the existing Google Apps APIs so they can be used
4 | in Dart.
5 |
6 | A simple example creates a new file with content "Hello from Dart":
7 |
8 | ``` dart
9 | import 'package:google_apps/drive.dart';
10 |
11 | main() {
12 | DriveApp.createFile("hello.txt", "Hello from Dart");
13 | }
14 | ```
15 |
16 | This example must be compiled with the `--csp` flag and uploaded with
17 | apps_script_tools (https://pub.dartlang.org/packages/apps_script_tools):
18 | ```
19 | `dart2js --csp -o drive.js example/drive.dart`.
20 | apps_script_watch /tmp/drive.js drive
21 | ```
22 |
23 | It can then be executed by opening the script and running the script. Since
24 | the example doesn't expose any entry point, simply running the `dartPrint` function
25 | is good enough.
26 |
27 | ## Other examples
28 | There are other small examples in the "misc" directory.
29 | A big example can be found in the "tournament" directory.
--------------------------------------------------------------------------------
/example/tournament/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | tournament.iml
3 | .idea
4 | .packages
5 | .pub
6 | .dart_tool
7 | build
8 |
--------------------------------------------------------------------------------
/example/tournament/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.0.0
4 |
5 | - Initial release.
6 |
--------------------------------------------------------------------------------
/example/tournament/README.md:
--------------------------------------------------------------------------------
1 | # Tournament Google Apps Script
2 |
3 | This is not an official Google product. It is not supported by the Dart team.
4 |
5 | This Example creates a Google Apps Script that can be used to run a
6 | tasting tournament.
7 |
8 | It adds menu items to the Spreadsheet to create brackets, run the event, and
9 | generate final reports.
10 |
11 | See https://docs.google.com/document/d/1d9pvdwD9OixXPOGs2PWC9J5M-YrP3qsJ29xkRg6xkBc
12 | for a detailed explanation on how to use this program.
13 |
14 | ## Features and bugs
15 |
16 | Please file feature requests and bugs at the [issue tracker][tracker].
17 |
18 | [tracker]: https://github.com/google/dart_google_apps/issues
19 |
--------------------------------------------------------------------------------
/example/tournament/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # Lint rules and documentation, see http://dart-lang.github.io/linter/lints
2 | linter:
3 | rules:
4 | - hash_and_equals
5 | - iterable_contains_unrelated_type
6 | - list_remove_unrelated_type
7 | - test_types_in_equals
8 | - unrelated_type_equality_checks
9 | - valid_regexps
10 |
--------------------------------------------------------------------------------
/example/tournament/bin/main.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | @JS()
16 | library main;
17 |
18 | import 'package:js/js.dart';
19 | import 'package:tournament/menu.dart' as tournament_menu;
20 | import 'package:google_apps/google_apps.dart';
21 |
22 | @JS()
23 | external set onOpen(value);
24 |
25 | void onOpenDart(e, [String prefix]) {
26 | String withPrefix(String funName) {
27 | if (prefix == null) return funName;
28 | return '$prefix.$funName';
29 | }
30 |
31 | var menu = SpreadsheetApp.getUi().createAddonMenu();
32 | var dartMenu = tournament_menu.createMenuEntries();
33 | dartMenu.forEach((caption, value) {
34 | if (caption == null) {
35 | menu = menu.addSeparator();
36 | } else if (value is Map) {
37 | var subMenu = SpreadsheetApp.getUi().createMenu(caption);
38 | value.forEach((caption, funName) {
39 | subMenu = subMenu.addItem(caption, withPrefix(funName));
40 | });
41 | menu = menu.addSubMenu(subMenu);
42 | } else {
43 | assert(value is String);
44 | menu = menu.addItem(caption, withPrefix(value));
45 | }
46 | });
47 | menu.addToUi();
48 | }
49 |
50 | void exportToJs() {
51 | onOpen = allowInterop(onOpenDart);
52 | }
53 |
54 | void main() {
55 | exportToJs();
56 | tournament_menu.exportToJs();
57 | }
58 |
--------------------------------------------------------------------------------
/example/tournament/lib/menu.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | @JS()
16 | library brackets;
17 |
18 | import 'src/brackets.dart';
19 | import 'src/cache.dart';
20 | import 'src/next_round.dart';
21 | import 'src/reports.dart';
22 |
23 | import 'package:js/js.dart';
24 |
25 | @JS()
26 | external set createBracketForSelected(value);
27 |
28 | @JS()
29 | external set createBracketForAll(value);
30 |
31 | @JS()
32 | external set deleteAllBracketSheets(value);
33 |
34 | @JS()
35 | external set prepareNextRound(value);
36 |
37 | @JS()
38 | external set competitorRanking(value);
39 |
40 | @JS()
41 | external set generateFinalReports(value);
42 |
43 | @JS()
44 | external set generateCompetitorReports(value);
45 |
46 | @JS()
47 | external set rebuildCache(value);
48 |
49 | @JS()
50 | external set test(value);
51 |
52 | void exportToJs() {
53 | createBracketForSelected = allowInterop(createBracketForSelectedDart);
54 | createBracketForAll = allowInterop(createBracketForAllDart);
55 | deleteAllBracketSheets = allowInterop(deleteAllBracketSheetsDart);
56 | prepareNextRound = allowInterop(prepareNextRoundDart);
57 | competitorRanking = allowInterop(competitorRankingDart);
58 | rebuildCache = allowInterop(rebuildCacheDart);
59 | generateFinalReports = allowInterop(generateFinalReportsDart);
60 | generateCompetitorReports = allowInterop(generateCompetitorReportsDart);
61 | test = allowInterop(testDart);
62 | }
63 |
64 | Map createMenuEntries() {
65 | return {
66 | 'Setup': {
67 | 'Create Bracket for Selected': 'createBracketForSelected',
68 | 'Create Bracket for All': 'createBracketForAll',
69 | },
70 | 'Next Round': 'prepareNextRound',
71 | 'Reports': {
72 | 'Ranking': 'competitorRanking',
73 | 'Participants': 'generateFinalReports',
74 | 'Competitors': 'generateCompetitorReports',
75 | },
76 | 'Maintenance': {
77 | 'Rebuild Cache': 'rebuildCache',
78 | 'Delete all Bracket-sheets': 'deleteAllBracketSheets',
79 | },
80 | // 'Debug': 'test',
81 | };
82 | }
83 |
84 | void testDart() {}
85 |
--------------------------------------------------------------------------------
/example/tournament/lib/src/brackets.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import 'dart:math' as math;
16 |
17 | import 'config.dart';
18 | import 'cache.dart';
19 | import 'utils.dart';
20 |
21 | import 'package:google_apps/google_apps.dart';
22 |
23 | /// Creates (or replaces) a sheet with brackets for the selected participant.
24 | ///
25 | /// This function is primarily used when new participants join at a later moment.
26 | void createBracketForSelectedDart() {
27 | var ss = SpreadsheetApp.getActiveSpreadsheet();
28 | var sheet = ss.getSheetByName(configSheetName);
29 | // Returns the active cell
30 | var cell = sheet.getActiveCell();
31 | var participant = cell.getValue();
32 | createBracketFor(participant);
33 | rebuildCacheDart();
34 | }
35 |
36 | /// Creates (or replaces) a sheet with brackets for all participants.
37 | void createBracketForAllDart() {
38 | allParticipants.forEach(createBracketFor);
39 | rebuildCacheDart();
40 | }
41 |
42 | /// Deletes all bracket sheets.
43 | ///
44 | /// This function uses the participant names to find the sheets. As a
45 | /// consequence, removing participants from the list before invoking this
46 | /// function would not remove their bracket sheets.
47 | ///
48 | /// This function is mostly useful for development.
49 | void deleteAllBracketSheetsDart() {
50 | var participants = allParticipants;
51 | var ss = SpreadsheetApp.getActiveSpreadsheet();
52 |
53 | for (var participant in participants) {
54 | var name = sheetNameFromParticipant(participant);
55 | var sheet = ss.getSheetByName(name);
56 | if (sheet != null) ss.deleteSheet(sheet);
57 | }
58 | var cacheSheet = ss.getSheetByName(cacheSheetName);
59 | if (cacheSheet != null) ss.deleteSheet(cacheSheet);
60 | }
61 |
62 | void createBracketFor(String participant) {
63 | var participants = allParticipants;
64 | if (!participants.contains(participant)) {
65 | error('Not a valid participant: $participant');
66 | }
67 |
68 | var sheet = _createParticipantSheet(participant);
69 | _fillWithBrackets(sheet, participant);
70 | }
71 |
72 | /// The location of the name of the participant, used to find the corresponding
73 | /// battle-results in the results-sheet.
74 | const String participantLocation = r'$B$1';
75 |
76 | /// Creates a fresh sheet for the given participant.
77 | ///
78 | /// If a sheet already exists overwrites it.
79 | Sheet _createParticipantSheet(String participant) {
80 | var sheetName = sheetNameFromParticipant(participant);
81 | var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
82 | var sheet = activeSpreadsheet.getSheetByName(sheetName);
83 |
84 | // Remove it first, if it already exists.
85 | if (sheet != null) activeSpreadsheet.deleteSheet(sheet);
86 |
87 | sheet = activeSpreadsheet.insertSheet();
88 | sheet.setName(sheetName);
89 | sheet.getRange(participantLocation).offset(0, -1).setValue('Participant:');
90 | sheet.getRange(participantLocation).setValue(participant);
91 | return sheet;
92 | }
93 |
94 | /// Fills the given sheet with a new bracket.
95 | ///
96 | /// Depending on the [isDoubleElimination] the generated sheets will be
97 | /// double elimination or not.
98 | ///
99 | /// If only some participants want to do double-elimination, then it is safe
100 | /// to delete the double-elimination entries from the sheet *and* then to
101 | /// rebuild the cache.
102 | ///
103 | /// This code was inspired by
104 | /// [https://developers.google.com/apps-script/articles/bracket_maker]
105 | void _fillWithBrackets(Sheet sheet, String participant) {
106 | var competitorNames = allCompetitors;
107 | var competitorCount = competitorNames.length;
108 | var competitorsRange = competitorsConfig.range;
109 | var competitorsColumnANotation =
110 | String.fromCharCode('A'.codeUnitAt(0) + competitorsRange.getColumn() - 1);
111 | var competitorsRow = competitorsRange.getRow();
112 |
113 | if (competitorCount < 3) {
114 | error('You must have at least 3 competitors.');
115 | }
116 |
117 | var competitorIndices =
118 | List.generate(competitorCount, (x) => x).toList();
119 |
120 | var upperPower = (math.log(competitorCount) / math.ln2).ceil();
121 |
122 | // Find out what is the number that is a power of 2 and lower than
123 | // numCompetitors.
124 | var withByesCount = math.pow(2, upperPower);
125 |
126 | competitorIndices.shuffle();
127 |
128 | if (competitorCount < withByesCount) {
129 | // Fill with Byes.
130 | var missing = withByesCount - competitorCount;
131 | var newCompetitorIndices = [];
132 |
133 | var remainingCompetitors = competitorCount;
134 |
135 | // We are avoiding having two pairings containing a bye next to each
136 | // other, as the byes would meet in the loser-bracket.
137 | var lastContainedBye = false;
138 | for (var i = 0; i < withByesCount ~/ 2; i++) {
139 | if (!lastContainedBye && missing > 0 || missing == remainingCompetitors) {
140 | newCompetitorIndices.add(-1);
141 | newCompetitorIndices.add(competitorIndices[--remainingCompetitors]);
142 | missing--;
143 | lastContainedBye = true;
144 | } else {
145 | newCompetitorIndices.add(competitorIndices[--remainingCompetitors]);
146 | newCompetitorIndices.add(competitorIndices[--remainingCompetitors]);
147 | lastContainedBye = false;
148 | }
149 | }
150 | competitorIndices = newCompetitorIndices.reversed.toList();
151 | }
152 |
153 | // Enter the competitors for the 1st round
154 | var cells = <_SheetCell>[];
155 | for (var i = 0; i < competitorIndices.length; i++) {
156 | var competitorIndex = competitorIndices[i];
157 | String formula;
158 | if (competitorIndex != -1) {
159 | formula = '=$configSheetName!'
160 | '\$$competitorsColumnANotation'
161 | '\$${competitorsRow + competitorIndex}';
162 | }
163 | var row = 3 + i * 2;
164 | var cell = _SheetCell(sheet, row, 1,
165 | isBye: competitorIndex == -1, formula: formula);
166 | cells.add(cell);
167 | }
168 |
169 | // Connects the given list of cells and connects them pairwise.
170 | //
171 | // Returns two lists: one with the cells of all winners, and one
172 | // for all the losers. The loser cells don't have any position yet, and
173 | // can be discarded. For example, the loser-bracket doesn't care for
174 | // any losers anymore. Similarly, losers are always discarded, if there is
175 | // no double-elimination.
176 | List> connectAll(List<_SheetCell> cells,
177 | {bool isWinnerBracket}) {
178 | var winners = <_SheetCell>[];
179 | var losers = <_SheetCell>[];
180 | for (var i = 0; i < cells.length; i += 2) {
181 | var cellA = cells[i];
182 | var cellB = cells[i + 1];
183 | var winnerLoser =
184 | cellA.connectTo(cellB, isWinnerBracket: isWinnerBracket);
185 | winners.add(winnerLoser[0]);
186 | losers.add(winnerLoser[1]);
187 | }
188 | return [winners, losers];
189 | }
190 |
191 | var winners = cells;
192 | var allLosers = >[];
193 | while (winners.length > 1) {
194 | var winnersLosers = connectAll(winners, isWinnerBracket: true);
195 | winners = winnersLosers[0];
196 | allLosers.add(winnersLosers[1]);
197 | }
198 |
199 | if (!isDoubleElimination) return;
200 |
201 | // 2 for the header, and 3 to have some space.
202 | var loserPos = competitorIndices.length * 2 + 6;
203 |
204 | var losersIndex = 0;
205 | var firstRoundLosers = allLosers[losersIndex++];
206 | // Sets the positions of the losers.
207 | for (var i = 0; i < firstRoundLosers.length; i += 2) {
208 | // Add 3 for the incoming loser from phase 2.
209 | loserPos += 3;
210 |
211 | firstRoundLosers[i] = firstRoundLosers[i].withPos(loserPos, 1);
212 | loserPos += 2;
213 |
214 | firstRoundLosers[i + 1] = firstRoundLosers[i + 1].withPos(loserPos, 1);
215 | loserPos += 2;
216 | }
217 |
218 | var shouldDoubleUp = false;
219 | var shouldInvertIncoming = true;
220 | var losers = firstRoundLosers;
221 | var previousLosers;
222 | while (losers.length > 1 || shouldDoubleUp) {
223 | if (shouldDoubleUp) {
224 | var newLosers = <_SheetCell>[];
225 | var incoming = allLosers[losersIndex++];
226 | if (shouldInvertIncoming) {
227 | incoming = incoming.reversed.toList();
228 | }
229 | shouldInvertIncoming = !shouldInvertIncoming;
230 | for (var i = 0; i < losers.length; i++) {
231 | // We use the positions of the previous phase to compute the
232 | // position of the incoming losers.
233 | var previousLoser = previousLosers[i * 2];
234 | // Introduce a new competitor (coming from the winner's bracket).
235 | var incomingCell = incoming[i]
236 | .withPos(previousLoser.row - 2, previousLoser.column + 2);
237 | newLosers.add(incomingCell);
238 | newLosers.add(losers[i]);
239 | }
240 | losers = newLosers;
241 | }
242 | shouldDoubleUp = !shouldDoubleUp;
243 | // Keep track of the previous losers to make it easier to compute the
244 | // position of incoming losers.
245 | previousLosers = losers;
246 | var winnersLosers = connectAll(losers, isWinnerBracket: false);
247 | // The winners advance. The losers are out now.
248 | losers = winnersLosers[0];
249 | }
250 |
251 | // Create a final pairing between the winner's and loser's bracket winners.
252 | var winnersWinner = winners[0];
253 | var losersWinner = losers[0];
254 | var column = winnersWinner.column + 2;
255 | var winnersRef = winnersWinner._a1Notation;
256 | var losersRef = losersWinner._a1Notation;
257 | var cellA = _SheetCell(sheet, 3, column, formula: '=$winnersRef');
258 | var cellB = _SheetCell(sheet, 5, column, formula: '=$losersRef');
259 | cellA.connectTo(cellB, isWinnerBracket: true);
260 | }
261 |
262 | /// A cell in the participant's sheet.
263 | ///
264 | /// When building the bracket, we use this class to refer to already
265 | /// generated cells.
266 | class _SheetCell {
267 | final Sheet sheet;
268 | final int row;
269 | final int column;
270 |
271 | /// Whether this cell is a bye.
272 | final bool isBye;
273 |
274 | /// A position-independent formula (which means that we can write at any
275 | /// place in the sheet).
276 | final String formula;
277 |
278 | _SheetCell(this.sheet, this.row, this.column,
279 | {this.formula, this.isBye = false});
280 |
281 | _SheetCell withPos(int row, int column) {
282 | return _SheetCell(sheet, row, column, formula: formula, isBye: isBye);
283 | }
284 |
285 | Range get _range => sheet.getRange(row, column);
286 |
287 | String get _a1Notation => _range.getA1Notation();
288 |
289 | /// Writes the cells data into the sheet.
290 | void markActive() {
291 | _range.setFormula(formula);
292 | _range.setBackground(competitorColor);
293 | }
294 |
295 | /// Connects this cell to [cell2].
296 | ///
297 | /// Returns two cells.
298 | /// The first entry is the winner-cell. The second, the loser cell *without*
299 | /// the correct location.
300 | ///
301 | /// The loser cell is only relevant for double-elimination tournaments.
302 | List<_SheetCell> connectTo(_SheetCell cell2, {bool isWinnerBracket}) {
303 | assert(column == cell2.column);
304 | var newRow = (row + cell2.row) ~/ 2;
305 | if (isBye && cell2.isBye) {
306 | var winner = _SheetCell(sheet, newRow, column + 2, isBye: true);
307 | var loser = _SheetCell(sheet, -1, -1, isBye: true);
308 | return [winner, loser];
309 | } else if (isBye || cell2.isBye) {
310 | var dataCell = isBye ? cell2 : this;
311 | var winner =
312 | _SheetCell(sheet, newRow, column + 2, formula: dataCell.formula);
313 | var loser = _SheetCell(sheet, -1, -1, isBye: true);
314 | return [winner, loser];
315 | } else {
316 | markActive();
317 | cell2.markActive();
318 | var fromRow = row;
319 | var toRow = cell2.row;
320 | var connector =
321 | sheet.getRange(fromRow, column + 1, toRow - fromRow + 1, 1);
322 | sheet.setColumnWidth(connector.getColumn(), connectorWidth);
323 | connector.setBackground(connectorColor);
324 | var outcomeCell = sheet.getRange(newRow, column + 1);
325 | outcomeCell.setFormula(_outcomeFormula);
326 | var roundCell = sheet.getRange(newRow, column);
327 | roundCell
328 | .setBackground(isWinnerBracket ? winnerRoundColor : loserRoundColor);
329 | var winnerFormula =
330 | computeNextRoundCompetitorFormula(this, cell2, outcomeCell);
331 | var winner =
332 | _SheetCell(sheet, newRow, column + 2, formula: winnerFormula);
333 | winner.markActive();
334 | var loserFormula = computeNextRoundCompetitorFormula(
335 | this, cell2, outcomeCell,
336 | invert: true);
337 | var loser = _SheetCell(sheet, newRow, column + 2, formula: loserFormula);
338 | return [winner, loser];
339 | }
340 | }
341 |
342 | /// Creates the formula that queries the results sheet for the result
343 | /// of this battle.
344 | ///
345 | /// If a result is available writes "A" or "B" into the cell.
346 | String get _outcomeFormula {
347 | var participant = participantLocation;
348 | var query = '"select $resultsWinColumn '
349 | '''where $resultsParticipantColumn = '"&$participant&"' and '''
350 | '$resultsRoundColumn = "&INDIRECT("R[0]C[-1]", false)';
351 | return '=IFERROR(QUERY($resultsQualifiedRange, $query))';
352 | }
353 |
354 | /// Computes the formula that finds the competitor for the next round, using
355 | /// the found outcome (see [_outcomeFormula]).
356 | String computeNextRoundCompetitorFormula(
357 | _SheetCell cellA, _SheetCell cellB, Range outcomeCell,
358 | {bool invert = false}) {
359 | var outcome = outcomeCell.getA1Notation();
360 | var a = cellA._a1Notation;
361 | var b = cellB._a1Notation;
362 |
363 | if (invert) {
364 | return '=IFS($outcome = "", "", $outcome = "A", $b, $outcome = "B", $a)';
365 | }
366 | return '=IFS($outcome = "", "", $outcome = "A", $a, $outcome = "B", $b)';
367 | }
368 | }
369 |
--------------------------------------------------------------------------------
/example/tournament/lib/src/cache.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import 'brackets.dart' show participantLocation;
16 | import 'config.dart';
17 | import 'tournament.dart';
18 | import 'utils.dart';
19 |
20 | import 'package:google_apps/google_apps.dart';
21 |
22 | /// (Re)Builds the cache.
23 | ///
24 | /// Runtime calls to the Google Apps service are very expensive, and it is
25 | /// hence advisable to limit the number of API calls.
26 | ///
27 | /// The cache sheet groups all interesting properties of the individual
28 | /// tournament sheets (of each participant) into one sheet. This way, many
29 | /// operation scan be performed without even looking at the participant sheets.
30 | ///
31 | /// The cache is built based on the *colors* of the tournament sheets.
32 | /// Conceptually it runs through the tournament sheet and finds two
33 | /// competitors by detecting their colors. The corresponding round-number (and
34 | /// results) are similarly found by detecting cells with the correct colors.
35 | void rebuildCacheDart() {
36 | var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
37 |
38 | var cacheSheet = spreadsheet.getSheetByName(cacheSheetName);
39 | if (cacheSheet != null) spreadsheet.deleteSheet(cacheSheet);
40 | cacheSheet = spreadsheet.insertSheet();
41 | cacheSheet.setName(cacheSheetName);
42 | cacheSheet.hideSheet();
43 |
44 | var sheets = allParticipants.map((participant) {
45 | var name = sheetNameFromParticipant(participant);
46 | return spreadsheet.getSheetByName(name);
47 | }).toList();
48 |
49 | var formulas = >[];
50 |
51 | for (var sheet in sheets) {
52 | if (sheet == null) continue; // Not all sheets have been built yet.
53 | var range = sheet.getDataRange();
54 | var rowCount = range.getNumRows();
55 | var columnCount = range.getNumColumns();
56 |
57 | var colors = range.getBackgrounds();
58 |
59 | var sheetName = sheet.getName();
60 |
61 | String ref(int row, int column) {
62 | var alphaColumn = String.fromCharCode('A'.codeUnitAt(0) + column);
63 | return '=$sheetName!\$$alphaColumn\$${row + 1}';
64 | }
65 |
66 | var participantNameReference = '=$sheetName!$participantLocation';
67 |
68 | for (var j = 0; j < columnCount; j += 2) {
69 | String competitorARef;
70 | String roundRef;
71 | int roundRow;
72 | int roundColumn;
73 | BattleType type;
74 | String resultRef;
75 | for (var i = 0; i < rowCount; i++) {
76 | var color = colors[i][j];
77 | switch (color) {
78 | case competitorColor:
79 | if (roundRef == null) {
80 | // This might overwrite the competitorA, if it was referring to the
81 | // tournament winner cell. That's fine, since the winner isn't
82 | // paired anymore.
83 | competitorARef = ref(i, j);
84 | } else {
85 | var competitorBRef = ref(i, j);
86 | formulas.add([
87 | participantNameReference,
88 | competitorARef,
89 | competitorBRef,
90 | '"${battleTypeToString[type]}"',
91 | resultRef,
92 | roundRef,
93 | '$roundRow',
94 | '$roundColumn'
95 | ]);
96 | roundRef = null;
97 | }
98 | break;
99 | case winnerRoundColor:
100 | case loserRoundColor:
101 | case bonusRoundColor:
102 | if (color == winnerRoundColor) type = BattleType.winner;
103 | if (color == loserRoundColor) type = BattleType.loser;
104 | if (color == bonusRoundColor) type = BattleType.bonus;
105 |
106 | roundRef = ref(i, j);
107 | roundRow = i + 1;
108 | roundColumn = j + 1;
109 | resultRef = ref(i, j + 1);
110 | break;
111 | }
112 | }
113 | }
114 | }
115 | var maxRows = cacheSheet.getMaxRows();
116 | if (maxRows < formulas.length) {
117 | cacheSheet.insertRows(maxRows - 1, (formulas.length - maxRows));
118 | }
119 | var range = cacheSheet.getRange(1, 1, formulas.length, formulas.first.length);
120 | range.setFormulas(formulas);
121 | }
122 |
--------------------------------------------------------------------------------
/example/tournament/lib/src/config.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import 'utils.dart';
16 | import 'package:google_apps/google_apps.dart';
17 |
18 | /// A list of all participants.
19 | final List allParticipants = participantsConfig.read();
20 |
21 | /// A list of boolean flags whether participants are active.
22 | ///
23 | /// This returns the boolean flags and must be matched up with
24 | /// [allParticipants]. Use [activeParticipants] to get a list of filtered
25 | /// participants.
26 | ///
27 | /// No checks are done to ensure that this list has the same length as the
28 | /// number of [allParticipants].
29 | final List allActives = activesConfig.read();
30 |
31 | /// A list of active participants.
32 | ///
33 | /// This combines [allParticipants] and [allActives] to filter out participants
34 | /// that are not active.
35 | final List activeParticipants = () {
36 | var participants = allParticipants;
37 | var activeFlags = allActives;
38 | if (participants.length != activeFlags.length) {
39 | error('Participants and Actives have different lengths.');
40 | }
41 | var activeParticipants = [];
42 | for (var i = 0; i < participants.length; i++) {
43 | if (boolify(activeFlags[i])) {
44 | activeParticipants.add(participants[i]);
45 | }
46 | }
47 | return activeParticipants;
48 | }();
49 |
50 | final Map urlMapping = () {
51 | var participants = allParticipants;
52 | var urls = urlConfig.read();
53 | var result = {};
54 | for (var i = 0; i < participants.length; i++) {
55 | result[participants[i]] = urls[i];
56 | }
57 | return result;
58 | }();
59 |
60 | /// A list of all competitors.
61 | final List allCompetitors = competitorsConfig.read();
62 |
63 | /// A list of boolean flags whether competitors are in stock.
64 | ///
65 | /// This returns the boolean flags and must be matched up with [allCompetitors].
66 | /// Use [inStockCompetitors] to get a filtered set of the competitors.
67 | final List allInStocks = inStockConfig.read();
68 |
69 | /// A set of competitors that are in stock.
70 | ///
71 | /// This combines [allCompetitors] and [allInStock].
72 | final Set inStockCompetitors = () {
73 | var competitors = allCompetitors;
74 | var inStock = allInStocks;
75 | if (competitors.length != inStock.length) {
76 | error('Competitors and In-stock have different lengths.');
77 | }
78 |
79 | var result = {};
80 | for (var i = 0; i < competitors.length; i++) {
81 | if (boolify(inStock[i])) result.add(competitors[i]);
82 | }
83 | return result;
84 | }();
85 |
86 | /// Whether two next-round pages are printed on one paper sheet.
87 | ///
88 | /// See [isPrinting2PagesPerSheetConfig].
89 | final bool isPrinting2PagesPerSheet =
90 | boolify(isPrinting2PagesPerSheetConfig.read());
91 |
92 | /// Whether the tournament for each participant is double-elimination.
93 | final bool isDoubleElimination = boolify(isDoubleEliminationConfig.read());
94 |
95 | /// The name of the sheet that contains all configurations.
96 | const String configSheetName = 'Config';
97 |
98 | /// The name of the sheet that contains all results.
99 | ///
100 | /// Usually, the results are filled in through a form, but they can also be
101 | /// filled in by hand. See below ([resultsParticipantColumn], ...) to see how
102 | /// the results sheet must be filled.
103 | const String resultsSheetName = 'Results';
104 |
105 | /// The name of the sheet that contains the cache.
106 | ///
107 | /// When there are many participants and competitors, running the script can
108 | /// take a significant time. The cache-sheet speeds up the computations
109 | /// significantly because it reduces the number of API calls.
110 | const String cacheSheetName = 'Cache';
111 |
112 | /// An list-entry in the config sheet.
113 | ///
114 | /// Config entries are found by their header.
115 | ///
116 | /// The library searches the header in the first column of the config sheet and
117 | /// returns all entries that follow the header.
118 | class ListConfigEntry {
119 | final String header;
120 | Range _range;
121 | List _values;
122 |
123 | ListConfigEntry(this.header);
124 |
125 | /// Returns the range for this config.
126 | Range get range {
127 | if (_range == null) read();
128 | return _range;
129 | }
130 |
131 | /// Returns the entries for this config.
132 | List read() {
133 | if (_values != null) return _values;
134 |
135 | var configSheet = _configSheet;
136 | var lastRow = configSheet.getLastRow();
137 | var lastColumn = configSheet.getLastColumn();
138 | var configValues =
139 | configSheet.getRange(1, 1, lastRow, lastColumn).getDisplayValues();
140 |
141 | var nextIsHeader = true;
142 | for (var i = 0; i < lastRow; i++) {
143 | if (nextIsHeader && configValues[i][0] == header) {
144 | var result = [];
145 | for (var j = i + 1; j < lastRow; j++) {
146 | if (configValues[j][0] == '') break;
147 | result.add(configValues[j][0]);
148 | }
149 | _values = result;
150 | _range = configSheet.getRange(i + 2, 1, result.length, 1);
151 | return _values;
152 | }
153 | nextIsHeader = (configValues[i][0] == '');
154 | }
155 | error("Couldn't find ${header}");
156 | return [];
157 | }
158 | }
159 |
160 | /// An secondary entry in the config sheet.
161 | ///
162 | /// Secondary entries are in a column next to the primary configuration.
163 | /// They are identified by a primary configuration, and a secondary header.
164 | ///
165 | /// The library searches the primary configuration, and then finds the header in
166 | /// the same row.
167 | class SecondaryConfigEntry {
168 | final ListConfigEntry primary;
169 | final String header;
170 |
171 | List _values;
172 |
173 | SecondaryConfigEntry(this.primary, this.header);
174 |
175 | /// Returns the range for this config.
176 | List read() {
177 | if (_values != null) return _values;
178 |
179 | var primaryRange = primary.range;
180 | // We search at most 10 columns.
181 | var headerRow = primaryRange.offset(-1, 0, 1, 10);
182 | var headerValuesRow = headerRow.getDisplayValues()[0];
183 | var secondaryOffset = -1;
184 | for (var i = 0; i < headerValuesRow.length; i++) {
185 | if (headerValuesRow[i] == header) {
186 | secondaryOffset = i;
187 | break;
188 | }
189 | }
190 | if (secondaryOffset == -1) error("Couldn't find ${header}");
191 |
192 | var secondaryRange = primaryRange.offset(0, secondaryOffset);
193 | var result = [];
194 | var displayValues = secondaryRange.getDisplayValues();
195 | for (var i = 0; i < displayValues.length; i++) {
196 | result.add(displayValues[i][0]);
197 | }
198 | _values = result;
199 | return result;
200 | }
201 | }
202 |
203 | /// A single config entry in the config sheet.
204 | ///
205 | /// Single entries are in a section and have just one value.
206 | /// They are identified by a list configuration (the section), and a header
207 | /// (in the section).
208 | ///
209 | /// The library searches the primary configuration, and then finds the header in
210 | /// the same column. The returned value is the entry next to the found header.
211 | class SingleConfigEntry {
212 | final ListConfigEntry section;
213 | final String header;
214 | String _value;
215 |
216 | SingleConfigEntry(this.section, this.header);
217 |
218 | String read() {
219 | if (_value != null) return _value;
220 | var sectionValues = section.read();
221 | var sectionRange = section.range;
222 | for (var i = 0; i < sectionValues.length; i++) {
223 | if (sectionValues[i] == header) {
224 | _value = sectionRange.offset(i, 1, 1, 1).getDisplayValue();
225 | return _value;
226 | }
227 | }
228 | error("Couldn't find ${header}");
229 | return '';
230 | }
231 | }
232 |
233 | /// The configuration for all participating "judges".
234 | ///
235 | /// These are the humans deciding which of the competitors win each sampling.
236 | final participantsConfig = ListConfigEntry('Participants');
237 |
238 | /// The configuration, whether a given participant is active.
239 | ///
240 | /// When participants are (temporarily) out, then this flag can be set to false.
241 | final activesConfig = SecondaryConfigEntry(participantsConfig, 'Active');
242 |
243 | /// The URL that should be printed on the battle sheet.
244 | ///
245 | /// Each participant may have a different one. For example, when the URL points
246 | /// to a prefilled form sheet.
247 | final urlConfig = SecondaryConfigEntry(participantsConfig, 'URL');
248 |
249 | /// The configuration for the competitors.
250 | ///
251 | /// These are the samples (chocolate, wine, ...) that are judged.
252 | final competitorsConfig = ListConfigEntry('Competitors');
253 |
254 | /// The configuration, whether a competitor is in stock.
255 | ///
256 | /// Since the amount of samples is dependent on the choices of the participants
257 | /// it can happen that not enough competitors have been bought/stocked.
258 | ///
259 | /// When this configuration is set to false, the script will not create
260 | /// pairings that require the out-of-stock competitor.
261 | final inStockConfig = SecondaryConfigEntry(competitorsConfig, 'In Stock');
262 |
263 | final miscSection = ListConfigEntry('Misc');
264 |
265 | /// The configuration whether two next-round pages are printed on one sheet.
266 | ///
267 | /// This reorders the order in which the pages are printed.
268 | ///
269 | /// Print-outs are done in the order of the sheets. This allows the game-master
270 | /// to organize the samples in such a way that the distribution is most
271 | /// efficient.
272 | ///
273 | /// Often, it's not necessary to have a full A4 (or letter) paper used for the
274 | /// print-out, but half of it is enough. When printing two pages per paper-sheet
275 | /// one can then cut all papers in the middle. When this flag is set to true,
276 | /// it is then possible to put the left side (of the cut pages) on top of the
277 | /// right side to get back the original order.
278 | final isPrinting2PagesPerSheetConfig =
279 | SingleConfigEntry(miscSection, '2 Sheets per Page');
280 |
281 | /// The configuration whether two next-round pages are printed on one sheet.
282 | ///
283 | /// This reorders the order in which the pages are printed.
284 | ///
285 | /// Print-outs are done in the order of the sheets. This allows the game-master
286 | /// to organize the samples in such a way that the distribution is most
287 | /// efficient.
288 | ///
289 | /// Often, it's not necessary to have a full A4 (or letter) paper used for the
290 | /// print-out, but half of it is enough. When printing two pages per paper-sheet
291 | /// one can then cut all papers in the middle. When this flag is set to true,
292 | /// it is then possible to put the left side (of the cut pages) on top of the
293 | /// right side to get back the original order.
294 | final isDoubleEliminationConfig =
295 | SingleConfigEntry(miscSection, 'Double Elimination');
296 |
297 | /// The column of the results sheet that contains the participant.
298 | const resultsParticipantColumn = 'B';
299 |
300 | /// The column of the results sheet that contains the round number.
301 | const resultsRoundColumn = 'C';
302 |
303 | /// The column of the results sheet that contains the winner of the battle.
304 | const resultsWinColumn = 'D';
305 |
306 | /// The column of the results sheet that contains the comment for competitor A.
307 | const resultsCommentsAColumn = 'E';
308 |
309 | /// The column of the results sheet that contains the comment for competitor B.
310 | const resultsCommentsBColumn = 'F';
311 |
312 | /// The range of results.
313 | ///
314 | /// Just skips the first row, since it contains the headers.
315 | const resultsRange = r'$A$2:$D$9999';
316 |
317 | /// Fully qualified range that includes the sheet-name.
318 | const resultsQualifiedRange = '$resultsSheetName!$resultsRange';
319 |
320 | // We are using "#ffffff' notation for colors, because this is, what
321 | // 'getBackground` returns.
322 |
323 | /// The color for competitors
324 | const competitorColor = '#ffff00'; // yellow
325 | /// The color of connectors.
326 | const connectorColor = '#40C040'; // green
327 | /// The color of the round/battle in the winner bracket.
328 | const winnerRoundColor = '#e8f8f5';
329 |
330 | /// The color of the round/battle in the loser bracket.
331 | const loserRoundColor = '#ebf5fb';
332 |
333 | /// The color of the round/battle for bonus rounds.
334 | const bonusRoundColor = '#fce5cd';
335 |
336 | /// Width of the connector column.
337 | const connectorWidth = 15;
338 |
339 | /// The config sheet.
340 | final Sheet _configSheet =
341 | SpreadsheetApp.getActiveSpreadsheet().getSheetByName(configSheetName);
342 |
343 | bool boolify(dynamic value) {
344 | if (value == null) return false;
345 | if (value is bool) return value;
346 | if (value == 'TRUE') return true;
347 | return false;
348 | }
349 |
--------------------------------------------------------------------------------
/example/tournament/lib/src/next_round.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import 'dart:convert';
16 |
17 | import 'config.dart';
18 | import 'tournament.dart';
19 | import 'utils.dart';
20 |
21 | import 'package:google_apps/google_apps.dart';
22 |
23 | /// Computes the percentage of how often 'A' was picked for the given pairings.
24 | ///
25 | /// Returns -1, if there is no completed battle.
26 | ///
27 | /// This stat is only useful if participants taste the competitors in a
28 | /// specific order ("A" first).
29 | int computeAPercentage(List pairings) {
30 | var selectedACount = 0;
31 | var completedPairingsCount = 0;
32 | for (var pairing in pairings) {
33 | var result = pairing.result;
34 | if (result != null) {
35 | completedPairingsCount++;
36 | if (result == 'A') selectedACount++;
37 | }
38 | }
39 | if (completedPairingsCount == 0) return -1;
40 | return selectedACount * 100 ~/ completedPairingsCount;
41 | }
42 |
43 | /// Computes the percentage of how often 'A' was picked for all pairings.
44 | ///
45 | /// Returns -1, if there is no completed battle.
46 | ///
47 | /// This stat is only useful if participants taste the competitors in a
48 | /// specific order ("A" first).
49 | int computeTotalAPercentage(List> allPairings) {
50 | var selectedACount = 0;
51 | var completedPairingsCount = 0;
52 | for (var pairings in allPairings) {
53 | for (var pairing in pairings) {
54 | var result = pairing.result;
55 | if (result != null) {
56 | completedPairingsCount++;
57 | if (result == 'A') selectedACount++;
58 | }
59 | }
60 | }
61 | if (completedPairingsCount == 0) return -1;
62 | return selectedACount * 100 ~/ completedPairingsCount;
63 | }
64 |
65 | /// Computes the ids of participants for the selection-buttons.
66 | ///
67 | /// When preparing a new round, we allow the administrator to select
68 | /// participants that are behind, or that do double-elimination.
69 | ///
70 | /// We compute these ids here and return them in a list:
71 | /// - the first entry of the returned list contains the participants that are
72 | /// behind. That is, participants that have played fewer rounds than other
73 | /// participants.
74 | /// - the second entry contains the participants that play double-elimination.
75 | /// When not all participants do the same
76 | List> computeSelections(List states) {
77 | var behindParticipants = {};
78 | var doubleEliminationParticipants = [];
79 |
80 | var maxWinnerRounds = 0;
81 | var maxLoserRounds = 0;
82 | for (var i = 0; i < states.length; i++) {
83 | var state = states[i];
84 | if (state.playedWinnersCount < maxWinnerRounds ||
85 | state.playedLosersCount < maxLoserRounds) {
86 | behindParticipants.add(i);
87 | }
88 |
89 | if (state.playedWinnersCount > maxWinnerRounds) {
90 | maxWinnerRounds = state.playedWinnersCount;
91 | // All participants so far are behind.
92 | for (var j = 0; j < i; j++) {
93 | behindParticipants.add(j);
94 | }
95 | if (maxLoserRounds < state.playedLosersCount) {
96 | maxLoserRounds = state.playedLosersCount;
97 | }
98 | }
99 | if (state.playedLosersCount > maxLoserRounds) {
100 | maxLoserRounds = state.playedLosersCount;
101 | // Insert all existing doubleEliminationParticipants in the behind list.
102 | behindParticipants.addAll(doubleEliminationParticipants);
103 | }
104 | if (state.hasLoserBracket) {
105 | doubleEliminationParticipants.add(i);
106 | }
107 | }
108 | return [behindParticipants.toList()..sort(), doubleEliminationParticipants];
109 | }
110 |
111 | /// Prepares the next round.
112 | ///
113 | /// This function is called in two different ways:
114 | /// 1. from the menu, which opens a side-bar.
115 | /// 2. as a callback from the side-bar.
116 | void prepareNextRoundDart([List userData]) {
117 | if (userData == null) {
118 | // Menu click.
119 | openNextRoundWindow();
120 | } else {
121 | // Callback from the opened window.
122 | createNextRoundDocument(userData);
123 | }
124 | }
125 |
126 | /// Opens a side-bar with the participants.
127 | ///
128 | /// The administrator can then select the active participants. The callback
129 | /// of the window then invokes [prepareNextRoundDart] again, which then
130 | /// creates the documents for the next round.
131 | void openNextRoundWindow() {
132 | var participants = activeParticipants;
133 | var urls = urlMapping;
134 |
135 | var allPairings = computeAllPairings();
136 |
137 | var states = participants.map((participant) {
138 | var pairings = allPairings[participant];
139 | var isActive = true;
140 | return State(participant, isActive, urls[participant], pairings);
141 | }).toList();
142 |
143 | var totalPlayed = 0;
144 | var totalASelected = 0;
145 | for (var state in states) {
146 | totalPlayed += state.finishedCount;
147 | totalASelected += state.aSelectionCount;
148 | }
149 | var totalAPercentage = -1;
150 | if (totalPlayed != 0) {
151 | totalAPercentage = 100 * totalASelected ~/ totalPlayed;
152 | }
153 |
154 | var selections = computeSelections(states);
155 |
156 | var behindSelection = selections[0];
157 | var doubleEliminationSelection = selections[1];
158 |
159 | var html = StringBuffer();
160 | html.write('');
161 | html.write(""
164 | ' ');
165 | if (doubleEliminationSelection.isNotEmpty) {
166 | html.write(""
169 | ' ');
170 | }
171 | html.write(""
174 | ' ');
175 | html.write(""
178 | ' ');
179 |
180 | for (var i = 0; i < states.length; i++) {
181 | var state = states[i];
182 | var isEnabled = state.isActive &&
183 | (state.availableWinnerPairings.isNotEmpty ||
184 | state.availableLoserPairings.isNotEmpty);
185 | var participant = state.participant;
186 | var participantName = state.participantName;
187 | var checkBoxValue = json.encode(participant);
188 | var playedWinnersCount = state.playedWinnersCount;
189 | var playedLosersCount = state.playedLosersCount;
190 | var playedCount = playedWinnersCount + playedLosersCount;
191 | var hasLoserBracket = state.hasLoserBracket;
192 |
193 | var countSuffix = '$playedCount';
194 | if (hasLoserBracket) {
195 | countSuffix += ' ($playedWinnersCount | $playedLosersCount)';
196 | }
197 | var missing = state.missingResults;
198 | var missingSuffix = missing.isEmpty ? '' : ' - [${missing.join(', ')}]';
199 | var suffix = '$countSuffix$missingSuffix';
200 | var escapedParticipantName = htmlEscape.convert(participantName);
201 | var escapedCheckBoxValue = htmlEscape.convert(checkBoxValue);
202 | html.write('
"
207 | ''
208 | '
');
209 | }
210 | html.write(
211 | "");
212 | html.write("""""");
257 |
258 | html.write('');
259 | var userInterface = HtmlService.createHtmlOutput(html.toString());
260 | userInterface.setTitle('Next Round');
261 | SpreadsheetApp.getUi().showSidebar(userInterface);
262 | }
263 |
264 | /// Finds the next pairing.
265 | ///
266 | /// The pairings in a state are sorted lower rounds first, top-to-bottom.
267 | ///
268 | /// This method only needs to decide whether to pick a winner or loser bracket
269 | /// pairing. We pick loser bracket, unless there are winner brackets that feed
270 | /// into the unplayed loser-bracket column.
271 | Pairing findBestNextPairing(State state) {
272 | var winnerPairings = state.availableWinnerPairings;
273 | var loserPairings = state.availableLoserPairings;
274 |
275 | if (winnerPairings.isEmpty) return loserPairings.first;
276 | if (loserPairings.isEmpty) return winnerPairings.first;
277 |
278 | var winnerColumn = winnerPairings.first.column;
279 | var loserColumn = loserPairings.first.column;
280 | if (winnerColumn == 0) return winnerPairings.first;
281 | if (loserColumn == 0) return loserPairings.first;
282 | if ((loserColumn - 1) < (winnerColumn - 2) * 2) return loserPairings.first;
283 | return winnerPairings.first;
284 | }
285 |
286 | void createNextRoundDocument(List userData) {
287 | // Might require more permissions: https://developers.google.com/apps-script/concepts/scopes
288 | var document = DocumentApp.create('round - ${DateTime.now()}');
289 | var body = document.getBody();
290 |
291 | var isFirst = true;
292 | var totalAPercentage = userData.last;
293 | var checkBoxValues = userData.sublist(0, userData.length - 1);
294 | if (checkBoxValues.isEmpty) return;
295 |
296 | var selectedParticipants =
297 | checkBoxValues.map((x) => x as String).map(json.decode).toList();
298 | var urls = urlMapping;
299 | var allPairings = computeAllPairings();
300 | var states = selectedParticipants.map((participant) {
301 | var isActive = true;
302 | return State(
303 | participant, isActive, urls[participant], allPairings[participant]);
304 | }).toList();
305 |
306 | var leftIndex = 0;
307 | var rightIndex = (states.length + 1) ~/ 2;
308 | var lastPageTable = List>(states.length);
309 |
310 | for (var j = 0; j < states.length; j++) {
311 | var i = j;
312 | if (isPrinting2PagesPerSheet) {
313 | // We reorder the pages so that we can cut in the middle and then
314 | // put the two piles of papers on top of each other while still having
315 | // the order we want.
316 | if (j.isEven) {
317 | i = leftIndex++;
318 | } else {
319 | i = rightIndex++;
320 | }
321 | }
322 | var state = states[i];
323 | var participant = state.participantName;
324 | var selectedPairing = findBestNextPairing(state);
325 | var row = selectedPairing.row;
326 | var column = selectedPairing.column;
327 | var competitorA = selectedPairing.competitorA;
328 | var competitorB = selectedPairing.competitorB;
329 | var roundNumber = state.nextRoundNumber;
330 | var missingResults = state.missingResults;
331 | var selectedAPercentage = state.finishedCount == 0
332 | ? -1
333 | : 100 * state.aSelectionCount ~/ state.finishedCount;
334 |
335 | var inTestMode = false;
336 | if (inTestMode) {
337 | body.appendParagraph('IN TEST MODE. NOT SETTING THE SHEET');
338 | } else {
339 | var sheetName = sheetNameFromParticipant(participant);
340 | SpreadsheetApp.getActiveSpreadsheet()
341 | .getSheetByName(sheetName)
342 | .getRange(row, column)
343 | .setValue(roundNumber);
344 | }
345 |
346 | Paragraph paragraph;
347 | if (isFirst) {
348 | isFirst = false;
349 | // Reuse already existing paragraph.
350 | paragraph = body.getChild(0);
351 | } else {
352 | paragraph = body.appendParagraph('');
353 | }
354 | paragraph.setText('$participant - $roundNumber');
355 | paragraph
356 | .setAlignment(DocumentApp.HorizontalAlignment.CENTER)
357 | .editAsText()
358 | .setFontSize(32);
359 | body.appendParagraph(state.url).editAsText().setFontSize(20);
360 | if (missingResults.isNotEmpty) {
361 | body.appendParagraph('Missing results for: ${missingResults.join(', ')}');
362 | }
363 | if (selectedAPercentage != -1) {
364 | body
365 | .appendParagraph('Your A-percentage: $selectedAPercentage%')
366 | .editAsText()
367 | .setFontSize(12);
368 | }
369 | if (totalAPercentage != -1) {
370 | body
371 | .appendParagraph('Total A-percentage: $totalAPercentage%')
372 | .editAsText()
373 | .setFontSize(12);
374 | }
375 | var table = body.appendTable([
376 | ['A', 'B']
377 | ]);
378 | table.setBorderColor('#ffffff');
379 | Paragraph cellA = table.getCell(0, 0).getChild(0);
380 | Paragraph cellB = table.getCell(0, 1).getChild(0);
381 | cellA
382 | .setAlignment(DocumentApp.HorizontalAlignment.LEFT)
383 | .editAsText()
384 | .setFontSize(27);
385 | cellB
386 | .setAlignment(DocumentApp.HorizontalAlignment.RIGHT)
387 | .editAsText()
388 | .setFontSize(27);
389 | body.appendPageBreak();
390 | lastPageTable[i] = [participant, competitorA, competitorB];
391 | }
392 | if (states.length.isEven && isPrinting2PagesPerSheet) {
393 | // Add an empty page to move the table to the right side.
394 | body.appendPageBreak();
395 | }
396 | // Add a small paragraph, to reset the default font size.
397 | body.appendParagraph('').editAsText().setFontSize(12);
398 | body.appendTable(lastPageTable);
399 |
400 | var id = document.getId();
401 | var html = """
402 |
403 |
404 |
405 |
406 | """;
407 | var userInterface = HtmlService.createHtmlOutput(html.toString());
408 | SpreadsheetApp.getUi().showModalDialog(userInterface, 'Next Round Ready');
409 | }
410 |
--------------------------------------------------------------------------------
/example/tournament/lib/src/reports.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import 'dart:convert';
16 |
17 | import 'config.dart';
18 | import 'tournament.dart';
19 | import 'utils.dart';
20 |
21 | import 'package:google_apps/google_apps.dart';
22 | import 'package:ranking/ranking.dart';
23 |
24 | // Used both as target for the menu and the callback from the selection sidebar.
25 | void generateFinalReportsDart([List selectedParticipants]) {
26 | if (selectedParticipants == null) {
27 | // Menu click
28 | var html = StringBuffer();
29 | html.write('');
30 | html.write(
31 | "");
32 | html.write(
33 | "");
34 |
35 | for (var participant in allParticipants) {
36 | var participantName = normalizeName(participant);
37 | var escapedParticipant = htmlEscape.convert(participant);
38 | var escapedParticipantName = htmlEscape.convert(participantName);
39 | html.write('
"
43 | ''
44 | '
');
45 | }
46 | html.write(
47 | "");
48 | html.write("""""");
74 |
75 | html.write('');
76 | var userInterface = HtmlService.createHtmlOutput(html.toString());
77 | userInterface.setTitle('Final Reports');
78 | SpreadsheetApp.getUi().showSidebar(userInterface);
79 | return;
80 | }
81 | // Callback from sidebar.
82 | var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
83 |
84 | var activeSet = Set.from(activeParticipants);
85 | var allPairings = computeAllPairings();
86 | var urls = urlMapping;
87 | var participantStates = {};
88 | for (var participant in selectedParticipants) {
89 | var pairings = allPairings[participant];
90 | var isActive = activeSet.contains(participant);
91 | participantStates[participant] =
92 | State(participant, isActive, urls[participant], pairings);
93 | }
94 |
95 | var responses = readResponses();
96 |
97 | combineStatesAndResponses(participantStates.values.toList(), responses);
98 |
99 | // Might require more permissions: https://developers.google.com/apps-script/concepts/scopes
100 | var folder =
101 | DriveApp.getRootFolder().createFolder('Final Report - ${DateTime.now()}');
102 |
103 | for (var participant in selectedParticipants) {
104 | var participantName = normalizeName(participant);
105 | var name = sheetNameFromParticipant(participant);
106 | var sheet = spreadsheet.getSheetByName(name);
107 | var data = sheet.getDataRange().getValues();
108 |
109 | var individualReportSpreadsheet = SpreadsheetApp.create(participantName);
110 | moveFileToFolder(individualReportSpreadsheet, folder);
111 | var tmpSheet = individualReportSpreadsheet.getSheets()[0];
112 | var individualSheet = sheet.copyTo(individualReportSpreadsheet);
113 | individualSheet.setName(sheet.getName());
114 | individualSheet.getRange(1, 1, data.length, data[0].length).setValues(data);
115 | individualReportSpreadsheet.deleteSheet(tmpSheet);
116 |
117 | var state = participantStates[participant];
118 | var pairings = state.allPairings.toList();
119 | pairings.sort((a, b) => a.round.compareTo(b.round));
120 |
121 | for (var pairing in pairings) {
122 | var response = pairing.response;
123 | if (response == null) continue;
124 | if (response.aComment == '' && response.bComment == '') {
125 | continue;
126 | }
127 |
128 | var text = '';
129 | if (response.aComment != '') {
130 | text += '\nA: ${response.aComment}';
131 | }
132 | if (response.bComment != '') {
133 | text += '\nB: ${response.bComment}';
134 | }
135 |
136 | individualSheet
137 | .getRange(pairing.row, pairing.column, 1, 1)
138 | .setNote(text.trim());
139 | }
140 | }
141 |
142 | var id = folder.getId();
143 | var html = """
144 |
145 |