[
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our community include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others’ private information, such as a physical or email address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [xuri.me](https://xuri.me). All complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the reporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\nCommunity Impact: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.\n\nConsequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\nCommunity Impact: A violation through a single incident or series of actions.\n\nConsequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.\n\n### 3. Temporary Ban\n\nCommunity Impact: A serious violation of community standards, including sustained inappropriate behavior.\n\nConsequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\nCommunity Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.\n\nConsequence: A permanent ban from any sort of public interaction within the community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).\n\nCommunity Impact Guidelines were inspired by Mozilla’s code of conduct enforcement ladder.\n\nFor answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are available at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations).\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing to excelize\n\nWant to hack on excelize? Awesome! This page contains information about reporting issues as well as some tips and\nguidelines useful to experienced open source contributors. Finally, make sure\nyou read our [community guidelines](#community-guidelines) before you\nstart participating.\n\n## Topics\n\n* [Reporting Security Issues](#reporting-security-issues)\n* [Design and Cleanup Proposals](#design-and-cleanup-proposals)\n* [Reporting Issues](#reporting-other-issues)\n* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)\n* [Community Guidelines](#community-guidelines)\n\n## Reporting security issues\n\nThe excelize maintainers take security seriously. If you discover a security\nissue, please bring it to their attention right away!\n\nPlease **DO NOT** file a public issue, instead send your report privately to\n[xuri.me](https://xuri.me).\n\nSecurity reports are greatly appreciated and we will publicly thank you for them.\nWe currently do not offer a paid security bounty program, but are not\nruling it out in the future.\n\n## Reporting other issues\n\nA great way to contribute to the project is to send a detailed report when you\nencounter an issue. We always appreciate a well-written, thorough bug report,\nand will thank you for it!\n\nCheck that [our issue database](https://github.com/xuri/excelize/issues)\ndoesn't already include that problem or suggestion before submitting an issue.\nIf you find a match, you can use the \"subscribe\" button to get notified on\nupdates. Do *not* leave random \"+1\" or \"I have this too\" comments, as they\nonly clutter the discussion, and don't help resolving it. However, if you\nhave ways to reproduce the issue or have additional information that may help\nresolving the issue, please leave a comment.\n\nWhen reporting issues, always include the output of `go env`.\n\nAlso include the steps required to reproduce the problem if possible and\napplicable. This information will help us review and fix your issue faster.\nWhen sending lengthy log-files, consider posting them as a gist [https://gist.github.com](https://gist.github.com).\nDon't forget to remove sensitive data from your logfiles before posting (you can\nreplace those parts with \"REDACTED\").\n\n## Quick contribution tips and guidelines\n\nThis section gives the experienced contributor some tips and guidelines.\n\n### Pull requests are always welcome\n\nNot sure if that typo is worth a pull request? Found a bug and know how to fix\nit? Do it! We will appreciate it. Any significant improvement should be\ndocumented as [a GitHub issue](https://github.com/xuri/excelize/issues) before\nanybody starts working on it.\n\nWe are always thrilled to receive pull requests. We do our best to process them\nquickly. If your pull request is not accepted on the first try,\ndon't get discouraged!\n\n### Design and cleanup proposals\n\nYou can propose new designs for existing excelize features. You can also design\nentirely new features. We really appreciate contributors who want to refactor or\notherwise cleanup our project.\n\nWe try hard to keep excelize lean and focused. Excelize can't do everything for\neverybody. This means that we might decide against incorporating a new feature.\nHowever, there might be a way to implement that feature *on top of* excelize.\n\n### Conventions\n\nFork the repository and make changes on your fork in a feature branch:\n\n* If it's a bug fix branch, name it XXXX-something where XXXX is the number of\n    the issue.\n* If it's a feature branch, create an enhancement issue to announce\n    your intentions, and name it XXXX-something where XXXX is the number of the\n    issue.\n\nSubmit unit tests for your changes. Go has a great test framework built in; use\nit! Take a look at existing tests for inspiration. Run the full test on your branch before\nsubmitting a pull request.\n\nUpdate the documentation when creating or modifying features. Test your\ndocumentation changes for clarity, concision, and correctness, as well as a\nclean documentation build.\n\nWrite clean code. Universally formatted code promotes ease of writing, reading,\nand maintenance. Always run `gofmt -s -w file.go` on each changed file before\ncommitting your changes. Most editors have plug-ins that do this automatically.\n\nPull request descriptions should be as clear as possible and include a reference\nto all the issues that they address.\n\n### Successful Changes\n\nBefore contributing large or high impact changes, make the effort to coordinate\nwith the maintainers of the project before submitting a pull request. This\nprevents you from doing extra work that may or may not be merged.\n\nLarge PRs that are just submitted without any prior communication is unlikely\nto be successful.\n\nWhile pull requests are the methodology for submitting changes to code, changes\nare much more likely to be accepted if they are accompanied by additional\nengineering work. While we don't define this explicitly, most of these goals\nare accomplished through the communication of the design goals and subsequent\nsolutions. Oftentimes, it helps to first state the problem before presenting\nsolutions.\n\nTypically, the best methods of accomplishing this are to submit an issue,\nstating the problem. This issue can include a problem statement and a\nchecklist with requirements. If solutions are proposed, alternatives should be\nlisted and eliminated. Even if the criteria for elimination of a solution is\nfrivolous, say so.\n\nLarger changes typically work best with design documents. These are focused on\nproviding context to the design at the time the feature was conceived and can\ninform future documentation contributions.\n\n### Commit Messages\n\nCommit messages must start with a capitalized and short summary\nwritten in the imperative, followed by an optional, more detailed explanatory\ntext which is separated from the summary by an empty line.\n\nCommit messages should follow best practices, including explaining the context\nof the problem and how it was solved, including in caveats or follow-up changes\nrequired. They should tell the story of the change and provide readers\nunderstanding of what led to it.\n\nIn practice, the best approach to maintaining a nice commit message is to\nleverage a `git add -p` and `git commit --amend` to formulate a solid\nchangeset. This allows one to piece together a change, as information becomes\navailable.\n\nIf you squash a series of commits, don't just submit that. Re-write the commit\nmessage, as if the series of commits was a single stroke of brilliance.\n\nThat said, there is no requirement to have a single commit for a PR, as long as\neach commit tells the story. For example, if there is a feature that requires a\npackage, it might make sense to have the package in a separate commit then have\na subsequent commit that uses it.\n\nRemember, you're telling part of the story with the commit message. Don't make\nyour chapter weird.\n\n### Review\n\nCode review comments may be added to your pull request. Discuss, then make the\nsuggested modifications and push additional commits to your feature branch. Post\na comment after pushing. New commits show up in the pull request automatically,\nbut the reviewers are notified only when you comment.\n\nPull requests must be cleanly rebased on top of master without multiple branches\nmixed into the PR.\n\n**Git tip**: If your PR no longer merges cleanly, use `rebase master` in your\nfeature branch to update your pull request rather than `merge master`.\n\nBefore you make a pull request, squash your commits into logical units of work\nusing `git rebase -i` and `git push -f`. A logical unit of work is a consistent\nset of patches that should be reviewed together: for example, upgrading the\nversion of a vendored dependency and taking advantage of its now available new\nfeature constitute two separate units of work. Implementing a new function and\ncalling it in another file constitute a single logical unit of work. The very\nhigh majority of submissions should have a single commit, so if in doubt: squash\ndown to one.\n\nAfter every commit, make sure the test passes. Include documentation\nchanges in the same pull request so that a revert would remove all traces of\nthe feature or fix.\n\nInclude an issue reference like `Closes #XXXX` or `Fixes #XXXX` in commits that\nclose an issue. Including references automatically closes the issue on a merge.\n\nPlease see the [Coding Style](#coding-style) for further guidelines.\n\n### Merge approval\n\nThe excelize maintainers use LGTM (Looks Good To Me) in comments on the code review to\nindicate acceptance.\n\n### Sign your work\n\nThe sign-off is a simple line at the end of the explanation for the patch. Your\nsignature certifies that you wrote the patch or otherwise have the right to pass\nit on as an open-source patch. The rules are pretty simple: if you can certify\nthe below (from [developercertificate.org](https://developercertificate.org)):\n\n```text\nDeveloper Certificate of Origin\nVersion 1.1\n\nCopyright (C) 2004, 2006 The Linux Foundation and its contributors.\n\nEveryone is permitted to copy and distribute verbatim copies of this\nlicense document, but changing it is not allowed.\n\n\nDeveloper's Certificate of Origin 1.1\n\nBy making a contribution to this project, I certify that:\n\n(a) The contribution was created in whole or in part by me and I\n    have the right to submit it under the open source license\n    indicated in the file; or\n\n(b) The contribution is based upon previous work that, to the best\n    of my knowledge, is covered under an appropriate open source\n    license and I have the right under that license to submit that\n    work with modifications, whether created in whole or in part\n    by me, under the same open source license (unless I am\n    permitted to submit under a different license), as indicated\n    in the file; or\n\n(c) The contribution was provided directly to me by some other\n    person who certified (a), (b) or (c) and I have not modified\n    it.\n\n(d) I understand and agree that this project and the contribution\n    are public and that a record of the contribution (including all\n    personal information I submit with it, including my sign-off) is\n    maintained indefinitely and may be redistributed consistent with\n    this project or the open source license(s) involved.\n```\n\nThen you just add a line to every git commit message:\n\n```text\nSigned-off-by: Ri Xu https://xuri.me\n```\n\nUse your real name (sorry, no pseudonyms or anonymous contributions.)\n\nIf you set your `user.name` and `user.email` git configs, you can sign your\ncommit automatically with `git commit -s`.\n\n### How can I become a maintainer\n\nFirst, all maintainers have 3 things\n\n* They share responsibility in the project's success.\n* They have made a long-term, recurring time investment to improve the project.\n* They spend that time doing whatever needs to be done, not necessarily what\n is the most interesting or fun.\n\nMaintainers are often under-appreciated, because their work is harder to appreciate.\nIt's easy to appreciate a really cool and technically advanced feature. It's harder\nto appreciate the absence of bugs, the slow but steady improvement in stability,\nor the reliability of a release process. But those things distinguish a good\nproject from a great one.\n\nDon't forget: being a maintainer is a time investment. Make sure you\nwill have time to make yourself available. You don't have to be a\nmaintainer to make a difference on the project!\n\nIf you want to become a maintainer, contact [xuri.me](https://xuri.me) and given an introduction of you.\n\n## Community guidelines\n\nWe want to keep the community awesome, growing and collaborative. We need\nyour help to keep it that way. To help with this we've come up with some general\nguidelines for the community as a whole:\n\n* Be nice: Be courteous, respectful and polite to fellow community members:\n  no regional, racial, gender, or other abuse will be tolerated. We like\n  nice people way better than mean ones!\n\n* Encourage diversity and participation: Make everyone in our community feel\n  welcome, regardless of their background and the extent of their\n  contributions, and do everything possible to encourage participation in\n  our community.\n\n* Keep it legal: Basically, don't get us in trouble. Share only content that\n  you own, do not share private or sensitive information, and don't break\n  the law.\n\n* Stay on topic: Make sure that you are posting to the correct channel and\n  avoid off-topic discussions. Remember when you update an issue or respond\n  to an email you are potentially sending to a large number of people. Please\n  consider this before you update. Also remember that nobody likes spam.\n\n* Don't send email to the maintainers: There's no need to send email to the\n  maintainers to ask them to investigate an issue or to take a look at a\n  pull request. Instead of sending an email, GitHub mentions should be\n  used to ping maintainers to review a pull request, a proposal or an\n  issue.\n\n### Guideline violations — 3 strikes method\n\nThe point of this section is not to find opportunities to punish people, but we\ndo need a fair way to deal with people who are making our community suck.\n\n1. First occurrence: We'll give you a friendly, but public reminder that the\n   behavior is inappropriate according to our guidelines.\n\n2. Second occurrence: We will send you a private message with a warning that\n   any additional violations will result in removal from the community.\n\n3. Third occurrence: Depending on the violation, we may need to delete or ban\n   your account.\n\n**Notes:**\n\n* Obvious spammers are banned on first occurrence. If we don't do this, we'll\n  have spam all over the place.\n\n* Violations are forgiven after 6 months of good behavior, and we won't hold a\n  grudge.\n\n* People who commit minor infractions will get some education, rather than\n  hammering them in the 3 strikes process.\n\n* The rules apply equally to everyone in the community, no matter how much\n    you've contributed.\n\n* Extreme violations of a threatening, abusive, destructive or illegal nature\n    will be addressed immediately and are not subject to 3 strikes or forgiveness.\n\n* Contact [xuri.me](https://xuri.me) to report abuse or appeal violations. In the case of\n    appeals, we know that mistakes happen, and we'll work with you to come up with a\n    fair solution if there has been a misunderstanding.\n\n## Coding Style\n\nUnless explicitly stated, we follow all coding guidelines from the Go\ncommunity. While some of these standards may seem arbitrary, they somehow seem\nto result in a solid, consistent codebase.\n\nIt is possible that the code base does not currently comply with these\nguidelines. We are not looking for a massive PR that fixes this, since that\ngoes against the spirit of the guidelines. All new contributions should make a\nbest effort to clean up and make the code base better than they left it.\nObviously, apply your best judgement. Remember, the goal here is to make the\ncode base easier for humans to navigate and understand. Always keep that in\nmind when nudging others to comply.\n\nThe rules:\n\n1. All code should be formatted with `gofmt -s`.\n2. All code should pass the default levels of\n   [`go vet`](https://pkg.go.dev/cmd/vet).\n3. All code should follow the guidelines covered in [Effective\n   Go](https://go.dev/doc/effective_go) and [Go Code Review\n   Comments](https://github.com/golang/go/wiki/CodeReviewComments).\n4. Comment the code. Tell us the why, the history and the context.\n5. Document _all_ declarations and methods, even private ones. Declare\n   expectations, caveats and anything else that may be important. If a type\n   gets exported, having the comments already there will ensure it's ready.\n6. Variable name length should be proportional to its context and no longer.\n   `noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`.\n   In practice, short methods will have short variable names and globals will\n   have longer names.\n7. No underscores in package names. If you need a compound name, step back,\n   and re-examine why you need a compound name. If you still think you need a\n   compound name, lose the underscore.\n8. No utils or helpers packages. If a function is not general enough to\n   warrant its own package, it has not been written generally enough to be a\n   part of a util package. Just leave it unexported and well-documented.\n9. All tests should run with `go test` and outside tooling should not be\n   required. No, we don't need another unit testing framework. Assertion\n   packages are acceptable if they provide _real_ incremental value.\n10. Even though we call these \"rules\" above, they are actually just\n    guidelines. Since you've read all the rules, you now know that.\n\nIf you are having trouble getting into the mood of idiomatic Go, we recommend\nreading through [Effective Go](https://go.dev/doc/effective_go). The\n[Go Blog](https://go.dev/blog/) is also a great resource. Drinking the\nkool-aid is a lot easier than going thirsty.\n\n## Code Review Comments and Effective Go Guidelines\n\n[CodeLingo](https://www.codelingo.io) automatically checks every pull request against the following guidelines from [Effective Go](https://go.dev/doc/effective_go) and [Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).\n\n### Package Comment\n\nEvery package should have a package comment, a block comment preceding the package clause.\nFor multi-file packages, the package comment only needs to be present in one file, and any one will do.\nThe package comment should introduce the package and provide information relevant to the package as a\nwhole. It will appear first on the godoc page and should set up the detailed documentation that follows.\n\n### Single Method Interface Name\n\nBy convention, one-method interfaces are named by the method name plus an -er suffix\nor similar modification to construct an agent noun: Reader, Writer, Formatter, CloseNotifier etc.\n\nThere are a number of such names and it's productive to honor them and the function names they capture.\nRead, Write, Close, Flush, String and so on have canonical signatures and meanings. To avoid confusion,\ndon't give your method one of those names unless it has the same signature and meaning. Conversely,\nif your type implements a method with the same meaning as a method on a well-known type, give it the\nsame name and signature; call your string-converter method String not ToString.\n\n### Avoid Annotations in Comments\n\nComments do not need extra formatting such as banners of stars. The generated output\nmay not even be presented in a fixed-width font, so don't depend on spacing for alignment—godoc,\nlike gofmt, takes care of that. The comments are uninterpreted plain text, so HTML and other\nannotations such as _this_ will reproduce verbatim and should not be used. One adjustment godoc\ndoes do is to display indented text in a fixed-width font, suitable for program snippets.\nThe package comment for the fmt package uses this to good effect.\n\n### Comment First Word as Subject\n\nDoc comments work best as complete sentences, which allow a wide variety of automated presentations.\nThe first sentence should be a one-sentence summary that starts with the name being declared.\n\n### Good Package Name\n\nIt's helpful if everyone using the package can use the same name\nto refer to its contents, which implies that the package name should\nbe good: short, concise, and evocative. By convention, packages are\ngiven lower case, single-word names; there should be no need for\nunderscores or mixedCaps. Err on the side of brevity, since everyone\nusing your package will be typing that name. And don't worry about\ncollisions a priori. The package name is only the default name for\nimports; it need not be unique across all source code, and in the\nrare case of a collision the importing package can choose a different\nname to use locally. In any case, confusion is rare because the file\nname in the import determines just which package is being used.\n\n### Avoid Renaming Imports\n\nAvoid renaming imports except to avoid a name collision; good package names\nshould not require renaming. In the event of collision, prefer to rename the\nmost local or project-specific import.\n\n### Context as First Argument\n\nValues of the context.Context type carry security credentials, tracing information,\ndeadlines, and cancellation signals across API and process boundaries. Go programs\npass Contexts explicitly along the entire function call chain from incoming RPCs\nand HTTP requests to outgoing requests.\n\nMost functions that use a Context should accept it as their first parameter.\n\n### Do Not Discard Errors\n\nDo not discard errors using _ variables. If a function returns an error,\ncheck it to make sure the function succeeded. Handle the error, return it, or,\nin truly exceptional situations, panic.\n\n### Go Error Format\n\nError strings should not be capitalized (unless beginning with proper nouns\nor acronyms) or end with punctuation, since they are usually printed following\nother context. That is, use fmt.Errorf(\"something bad\") not fmt.Errorf(\"Something bad\"),\nso that log.Printf(\"Reading %s: %v\", filename, err) formats without a spurious\ncapital letter mid-message. This does not apply to logging, which is implicitly\nline-oriented and not combined inside other messages.\n\n### Use Crypto Rand\n\nDo not use package math/rand to generate keys, even\nthrowaway ones. Unseeded, the generator is completely predictable.\nSeeded with time.Nanoseconds(), there are just a few bits of entropy.\nInstead, use crypto/rand's Reader, and if you need text, print to\nhexadecimal or base64.\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: xuri\nopen_collective: excelize\npatreon: xuri\nko_fi: xurime\nliberapay: xuri\nissuehunt: xuri\ncustom: https://www.paypal.com/paypalme/xuri"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug report\ndescription: Create a report to help us improve\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        If you are reporting a new issue, make sure that we do not have any duplicates already open. You can ensure this by searching the issue list for this repository. If there is a duplicate, please close your issue and add a comment to the existing issue instead.\n\n  - type: textarea\n    id: description\n    attributes:\n      label: Description\n      description: Briefly describe the problem you are having in a few paragraphs.\n    validations:\n      required: true\n\n  - type: textarea\n    id: reproduction-steps\n    attributes:\n      label: Steps to reproduce the issue\n      description: Explain how to cause the issue in the provided reproduction.\n      placeholder: |\n        1.\n        2.\n        3.\n    validations:\n      required: true\n\n  - type: textarea\n    id: received\n    attributes:\n      label: Describe the results you received\n    validations:\n      required: true\n\n  - type: textarea\n    id: expected\n    attributes:\n      label: Describe the results you expected\n    validations:\n      required: true\n\n  - type: input\n    id: go-version\n    attributes:\n      label: Go version\n      description: |\n        Output of `go version`:\n      placeholder: e.g. 1.23.4\n    validations:\n      required: true\n\n  - type: input\n    id: excelize-version\n    attributes:\n      label: Excelize version or commit ID\n      description: |\n        Which version of Excelize are you using?\n      placeholder: e.g. 2.9.0\n    validations:\n      required: true\n\n  - type: textarea\n    id: env\n    attributes:\n      label: Environment\n      description: Environment details (OS, Microsoft Excel&trade; version, physical, etc.)\n      render: shell\n    validations:\n      required: true\n\n  - type: checkboxes\n    id: checkboxes\n    attributes:\n      label: Validations\n      description: Before submitting the issue, please make sure you do the following\n      options:\n        - label: Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.\n          required: true\n        - label: The provided reproduction is a minimal reproducible example of the bug.\n          required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: Feature request\ndescription: Suggest an idea for this project\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        If you are reporting a new issue, make sure that we do not have any duplicates already open. You can ensure this by searching the issue list for this repository. If there is a duplicate, please close your issue and add a comment to the existing issue instead.\n\n  - type: textarea\n    id: description\n    attributes:\n      label: Description\n      description: Describe the feature that you would like added\n    validations:\n      required: true\n\n  - type: textarea\n    id: additional-context\n    attributes:\n      label: Additional context\n      description: Any other context or screenshots about the feature request here?\n\n  - type: checkboxes\n    id: checkboxes\n    attributes:\n      label: Validations\n      description: Before submitting the issue, please make sure you do the following\n      options:\n        - label: Check that there isn't already an issue that requests the same feature to avoid creating a duplicate.\n          required: true\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "# PR Details\n\n<!--- Provide a general summary of your changes in the Title above -->\n\n## Description\n\n<!--- Describe your changes in detail -->\n\n## Related Issue\n\n<!--- This project only accepts pull requests related to open issues -->\n<!--- If suggesting a new feature or change, please discuss it in an issue first -->\n<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->\n<!--- Please link to the issue here: -->\n\n## Motivation and Context\n\n<!--- Why is this change required? What problem does it solve? -->\n\n## How Has This Been Tested\n\n<!--- Please describe in detail how you tested your changes. -->\n<!--- Include details of your testing environment, and the tests you ran to -->\n<!--- See how your change affects other areas of the code, etc. -->\n\n## Types of changes\n\n<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->\n\n- [ ] Docs change / refactoring / dependency upgrade\n- [ ] Bug fix (non-breaking change which fixes an issue)\n- [ ] New feature (non-breaking change which adds functionality)\n- [ ] Breaking change (fix or feature that would cause existing functionality to change)\n\n## Checklist\n\n<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->\n<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->\n\n- [ ] My code follows the code style of this project.\n- [ ] My change requires a change to the documentation.\n- [ ] I have updated the documentation accordingly.\n- [ ] I have read the **CONTRIBUTING** document.\n- [ ] I have added tests to cover my changes.\n- [ ] All new and existing tests passed.\n"
  },
  {
    "path": ".github/SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nWe will dive into any security-related issue as long as your Excelize version is still supported by us. When reporting an issue, include as much information as possible, but no need to fill fancy forms or answer tedious questions. Just tell us what you found, how to reproduce it, and any concerns you have about it. We will respond as soon as possible and follow up with any missing information.\n\n## Reporting a Vulnerability\n\nPlease e-mail us directly at `xuri.me@gmail.com` or use the security issue template on GitHub. In general, public disclosure is made after the issue has been fully identified and a patch is ready to be released. A security issue gets the highest priority assigned and a reply regarding the vulnerability is given within a typical 24 hours. Thank you!\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [master]\n  pull_request:\n    branches: [master]\n  schedule:\n    - cron: '0 6 * * 3'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-24.04\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: ['go']\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v6\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v4\n      with:\n        languages: ${{ matrix.language }}\n\n    - name: Autobuild\n      uses: github/codeql-action/autobuild@v4\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v4\n"
  },
  {
    "path": ".github/workflows/go.yml",
    "content": "on: [push, pull_request]\nname: build\njobs:\n\n  test:\n    strategy:\n      matrix:\n        go-version: [1.25.x, 1.26.x]\n        os: [ubuntu-24.04, macos-15-intel, macos-26, windows-latest]\n        targetplatform: [x86, x64]\n\n    runs-on: ${{ matrix.os }}\n\n    steps:\n\n    - name: Install Go\n      uses: actions/setup-go@v6\n      with:\n        go-version: ${{ matrix.go-version }}\n        cache: false\n\n    - name: Checkout code\n      uses: actions/checkout@v6\n\n    - name: Get dependencies\n      run: |\n        env GO111MODULE=on go vet ./...\n    - name: Build\n      run: go build -v .\n\n    - name: Build on ARM\n      if: runner.os == 'Linux'\n      run: |\n        GOARCH=arm GOARM=5 go build .\n        GOARCH=arm GOARM=6 go build .\n        GOARCH=arm GOARM=7 go build .\n        GOARCH=arm64 go build .\n        GOARCH=arm64 GOOS=android go build .\n\n    - name: Test\n      run: env GO111MODULE=on go test -v -timeout 60m -race ./... -coverprofile='coverage.txt' -covermode=atomic\n\n    - name: Codecov\n      uses: codecov/codecov-action@v5\n      env:\n        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n      with:\n        files: coverage.txt\n        flags: unittests\n        name: codecov-umbrella\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.idea\n*.json\n*.out\n*.test\n~$*.xlsx\ntest/*.png\ntest/BadWorkbook.SaveAsEmptyStruct.xlsx\ntest/Encryption*.xlsx\ntest/excelize-*\ntest/Test*.xlam\ntest/Test*.xlsm\ntest/Test*.xlsx\ntest/Test*.xltm\ntest/Test*.xltx\n"
  },
  {
    "path": "LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2016-2026 The excelize Authors.\nCopyright (c) 2011-2017 Geoffrey J. Teale\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><img width=\"650\" src=\"./excelize.svg\" alt=\"Excelize logo\"></p>\n\n<p align=\"center\">\n    <a href=\"https://github.com/xuri/excelize/actions/workflows/go.yml\"><img src=\"https://github.com/xuri/excelize/actions/workflows/go.yml/badge.svg\" alt=\"Build Status\"></a>\n    <a href=\"https://codecov.io/gh/qax-os/excelize\"><img src=\"https://codecov.io/gh/qax-os/excelize/branch/master/graph/badge.svg\" alt=\"Code Coverage\"></a>\n    <a href=\"https://goreportcard.com/report/github.com/xuri/excelize/v2\"><img src=\"https://goreportcard.com/badge/github.com/xuri/excelize/v2\" alt=\"Go Report Card\"></a>\n    <a href=\"https://pkg.go.dev/github.com/xuri/excelize/v2\"><img src=\"https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white\" alt=\"go.dev\"></a>\n    <a href=\"https://opensource.org/licenses/BSD-3-Clause\"><img src=\"https://img.shields.io/badge/license-bsd-orange.svg\" alt=\"Licenses\"></a>\n    <a href=\"https://www.paypal.com/paypalme/xuri\"><img src=\"https://img.shields.io/badge/Donate-PayPal-green.svg\" alt=\"Donate\"></a>\n</p>\n\n# Excelize\n\n## Introduction\n\nExcelize is a library written in pure Go providing a set of functions that allow you to write to and read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and writing spreadsheet documents generated by Microsoft Excel&trade; 2007 and later. Supports complex components by high compatibility, and provided streaming API for generating or reading data from a worksheet with huge amounts of data. This library needs Go version 1.25.0 or later. The full docs can be seen using go's built-in documentation tool, or online at [go.dev](https://pkg.go.dev/github.com/xuri/excelize/v2) and [docs reference](https://xuri.me/excelize/).\n\n## Basic Usage\n\n### Installation\n\n```bash\ngo get github.com/xuri/excelize\n```\n\n- If your packages are managed using [Go Modules](https://go.dev/blog/using-go-modules), please install with following command.\n\n```bash\ngo get github.com/xuri/excelize/v2\n```\n\n### Create spreadsheet\n\nHere is a minimal example usage that will create spreadsheet file.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/xuri/excelize/v2\"\n)\n\nfunc main() {\n    f := excelize.NewFile()\n    defer func() {\n        if err := f.Close(); err != nil {\n            fmt.Println(err)\n        }\n    }()\n    // Create a new sheet.\n    index, err := f.NewSheet(\"Sheet2\")\n    if err != nil {\n        fmt.Println(err)\n        return\n    }\n    // Set value of a cell.\n    f.SetCellValue(\"Sheet2\", \"A2\", \"Hello world.\")\n    f.SetCellValue(\"Sheet1\", \"B2\", 100)\n    // Set active sheet of the workbook.\n    f.SetActiveSheet(index)\n    // Save spreadsheet by the given path.\n    if err := f.SaveAs(\"Book1.xlsx\"); err != nil {\n        fmt.Println(err)\n    }\n}\n```\n\n### Reading spreadsheet\n\nThe following constitutes the bare to read a spreadsheet document.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/xuri/excelize/v2\"\n)\n\nfunc main() {\n    f, err := excelize.OpenFile(\"Book1.xlsx\")\n    if err != nil {\n        fmt.Println(err)\n        return\n    }\n    defer func() {\n        // Close the spreadsheet.\n        if err := f.Close(); err != nil {\n            fmt.Println(err)\n        }\n    }()\n    // Get value from cell by given worksheet name and cell reference.\n    cell, err := f.GetCellValue(\"Sheet1\", \"B2\")\n    if err != nil {\n        fmt.Println(err)\n        return\n    }\n    fmt.Println(cell)\n    // Get all the rows in the Sheet1.\n    rows, err := f.GetRows(\"Sheet1\")\n    if err != nil {\n        fmt.Println(err)\n        return\n    }\n    for _, row := range rows {\n        for _, colCell := range row {\n            fmt.Print(colCell, \"\\t\")\n        }\n        fmt.Println()\n    }\n}\n```\n\n### Add chart to spreadsheet file\n\nWith Excelize chart generation and management is as easy as a few lines of code. You can build charts based on data in your worksheet or generate charts without any data in your worksheet at all.\n\n<p align=\"center\"><img width=\"650\" src=\"./test/images/chart.png\" alt=\"Excelize\"></p>\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/xuri/excelize/v2\"\n)\n\nfunc main() {\n    f := excelize.NewFile()\n    defer func() {\n        if err := f.Close(); err != nil {\n            fmt.Println(err)\n        }\n    }()\n    for idx, row := range [][]interface{}{\n        {nil, \"Apple\", \"Orange\", \"Pear\"}, {\"Small\", 2, 3, 3},\n        {\"Normal\", 5, 2, 4}, {\"Large\", 6, 7, 8},\n    } {\n        cell, err := excelize.CoordinatesToCellName(1, idx+1)\n        if err != nil {\n            fmt.Println(err)\n            return\n        }\n        f.SetSheetRow(\"Sheet1\", cell, &row)\n    }\n    if err := f.AddChart(\"Sheet1\", \"E1\", &excelize.Chart{\n        Type: excelize.Col3DClustered,\n        Series: []excelize.ChartSeries{\n            {\n                Name:       \"Sheet1!$A$2\",\n                Categories: \"Sheet1!$B$1:$D$1\",\n                Values:     \"Sheet1!$B$2:$D$2\",\n            },\n            {\n                Name:       \"Sheet1!$A$3\",\n                Categories: \"Sheet1!$B$1:$D$1\",\n                Values:     \"Sheet1!$B$3:$D$3\",\n            },\n            {\n                Name:       \"Sheet1!$A$4\",\n                Categories: \"Sheet1!$B$1:$D$1\",\n                Values:     \"Sheet1!$B$4:$D$4\",\n            }},\n        Title: []excelize.RichTextRun{\n            {\n                Text: \"Fruit 3D Clustered Column Chart\",\n            },\n        },\n    }); err != nil {\n        fmt.Println(err)\n        return\n    }\n    // Save spreadsheet by the given path.\n    if err := f.SaveAs(\"Book1.xlsx\"); err != nil {\n        fmt.Println(err)\n    }\n}\n```\n\n### Add picture to spreadsheet file\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    _ \"image/gif\"\n    _ \"image/jpeg\"\n    _ \"image/png\"\n\n    \"github.com/xuri/excelize/v2\"\n)\n\nfunc main() {\n    f, err := excelize.OpenFile(\"Book1.xlsx\")\n    if err != nil {\n        fmt.Println(err)\n        return\n    }\n    defer func() {\n        // Close the spreadsheet.\n        if err := f.Close(); err != nil {\n            fmt.Println(err)\n        }\n    }()\n    // Insert a picture.\n    if err := f.AddPicture(\"Sheet1\", \"A2\", \"image.png\", nil); err != nil {\n        fmt.Println(err)\n    }\n    // Insert a picture to worksheet with scaling.\n    if err := f.AddPicture(\"Sheet1\", \"D2\", \"image.jpg\",\n        &excelize.GraphicOptions{ScaleX: 0.5, ScaleY: 0.5}); err != nil {\n        fmt.Println(err)\n    }\n    // Insert a picture offset in the cell with printing support.\n    enable, disable := true, false\n    if err := f.AddPicture(\"Sheet1\", \"H2\", \"image.gif\",\n        &excelize.GraphicOptions{\n            PrintObject:     &enable,\n            LockAspectRatio: false,\n            OffsetX:         15,\n            OffsetY:         10,\n            Locked:          &disable,\n        }); err != nil {\n        fmt.Println(err)\n    }\n    // Save the spreadsheet with the origin path.\n    if err = f.Save(); err != nil {\n        fmt.Println(err)\n    }\n}\n```\n\n## Contributing\n\nContributions are welcome! Open a pull request to fix a bug, or open an issue to discuss a new feature or change. XML is compliant with [part 1 of the 5th edition of the ECMA-376 Standard for Office Open XML](https://www.ecma-international.org/publications-and-standards/standards/ecma-376/).\n\n## Licenses\n\nThis program is under the terms of the BSD 3-Clause License. See [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause).\n\nThe Excel logo is a trademark of [Microsoft Corporation](https://aka.ms/trademarks-usage). This artwork is an adaptation.\n\nThe Go gopher was created by [Renee French](https://go.dev/doc/gopher/README). Licensed under the [Creative Commons 4.0 Attributions license](http://creativecommons.org/licenses/by/4.0/).\n\n## Gold Sponsors\n\n<a href=\"https://www.gravityclimate.com\" alt=\"Gravity\"><img width=\"120\" src=\"https://xuri.me/excelize/images/vendor/gravityclimate.com.svg\" alt=\"Gravity\"></a>\n"
  },
  {
    "path": "README_zh.md",
    "content": "<p align=\"center\"><img width=\"650\" src=\"./excelize.svg\" alt=\"Excelize logo\"></p>\n\n<p align=\"center\">\n    <a href=\"https://github.com/xuri/excelize/actions/workflows/go.yml\"><img src=\"https://github.com/xuri/excelize/actions/workflows/go.yml/badge.svg\" alt=\"Build Status\"></a>\n    <a href=\"https://codecov.io/gh/qax-os/excelize\"><img src=\"https://codecov.io/gh/qax-os/excelize/branch/master/graph/badge.svg\" alt=\"Code Coverage\"></a>\n    <a href=\"https://goreportcard.com/report/github.com/xuri/excelize/v2\"><img src=\"https://goreportcard.com/badge/github.com/xuri/excelize/v2\" alt=\"Go Report Card\"></a>\n    <a href=\"https://pkg.go.dev/github.com/xuri/excelize/v2\"><img src=\"https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white\" alt=\"go.dev\"></a>\n    <a href=\"https://opensource.org/licenses/BSD-3-Clause\"><img src=\"https://img.shields.io/badge/license-bsd-orange.svg\" alt=\"Licenses\"></a>\n    <a href=\"https://www.paypal.com/paypalme/xuri\"><img src=\"https://img.shields.io/badge/Donate-PayPal-green.svg\" alt=\"Donate\"></a>\n</p>\n\n# Excelize\n\n## 简介\n\nExcelize 是 Go 语言编写的用于操作 Office Excel 文档基础库，基于 ECMA-376，ISO/IEC 29500 国际标准。可以使用它来读取、写入由 Microsoft Excel&trade; 2007 及以上版本创建的电子表格文档。支持 XLAM / XLSM / XLSX / XLTM / XLTX 等多种文档格式，高度兼容带有样式、图片(表)、透视表、切片器等复杂组件的文档，并提供流式读写函数，用于处理包含大规模数据的工作簿。可应用于各类报表平台、云计算、边缘计算等系统。使用本类库要求使用的 Go 语言为 1.25.0 或更高版本。完整的使用文档请访问 [go.dev](https://pkg.go.dev/github.com/xuri/excelize/v2) 或查看 [参考文档](https://xuri.me/excelize/)。\n\n## 快速上手\n\n### 安装\n\n```bash\ngo get github.com/xuri/excelize\n```\n\n- 如果您使用 [Go Modules](https://go.dev/blog/using-go-modules) 管理软件包，请使用下面的命令来安装最新版本。\n\n```bash\ngo get github.com/xuri/excelize/v2\n```\n\n### 创建 Excel 文档\n\n下面是一个创建 Excel 文档的简单例子：\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/xuri/excelize/v2\"\n)\n\nfunc main() {\n    f := excelize.NewFile()\n    defer func() {\n        if err := f.Close(); err != nil {\n            fmt.Println(err)\n        }\n    }()\n    // 创建一个工作表\n    index, err := f.NewSheet(\"Sheet2\")\n    if err != nil {\n        fmt.Println(err)\n        return\n    }\n    // 设置单元格的值\n    f.SetCellValue(\"Sheet2\", \"A2\", \"Hello world.\")\n    f.SetCellValue(\"Sheet1\", \"B2\", 100)\n    // 设置工作簿的默认工作表\n    f.SetActiveSheet(index)\n    // 根据指定路径保存文件\n    if err := f.SaveAs(\"Book1.xlsx\"); err != nil {\n        fmt.Println(err)\n    }\n}\n```\n\n### 读取 Excel 文档\n\n下面是读取 Excel 文档的例子：\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/xuri/excelize/v2\"\n)\n\nfunc main() {\n    f, err := excelize.OpenFile(\"Book1.xlsx\")\n    if err != nil {\n        fmt.Println(err)\n        return\n    }\n    defer func() {\n        // 关闭工作簿\n        if err := f.Close(); err != nil {\n            fmt.Println(err)\n        }\n    }()\n    // 获取工作表中指定单元格的值\n    cell, err := f.GetCellValue(\"Sheet1\", \"B2\")\n    if err != nil {\n        fmt.Println(err)\n        return\n    }\n    fmt.Println(cell)\n    // 获取 Sheet1 上所有单元格\n    rows, err := f.GetRows(\"Sheet1\")\n    if err != nil {\n        fmt.Println(err)\n        return\n    }\n    for _, row := range rows {\n        for _, colCell := range row {\n            fmt.Print(colCell, \"\\t\")\n        }\n        fmt.Println()\n    }\n}\n```\n\n### 在 Excel 文档中创建图表\n\n使用 Excelize 生成图表十分简单，仅需几行代码。您可以根据工作表中的已有数据构建图表，或向工作表中添加数据并创建图表。\n\n<p align=\"center\"><img width=\"650\" src=\"./test/images/chart.png\" alt=\"使用 Excelize 在 Excel 电子表格文档中创建图表\"></p>\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/xuri/excelize/v2\"\n)\n\nfunc main() {\n    f := excelize.NewFile()\n    defer func() {\n        if err := f.Close(); err != nil {\n            fmt.Println(err)\n        }\n    }()\n    for idx, row := range [][]interface{}{\n        {nil, \"Apple\", \"Orange\", \"Pear\"}, {\"Small\", 2, 3, 3},\n        {\"Normal\", 5, 2, 4}, {\"Large\", 6, 7, 8},\n    } {\n        cell, err := excelize.CoordinatesToCellName(1, idx+1)\n        if err != nil {\n            fmt.Println(err)\n            return\n        }\n        f.SetSheetRow(\"Sheet1\", cell, &row)\n    }\n    if err := f.AddChart(\"Sheet1\", \"E1\", &excelize.Chart{\n        Type: excelize.Col3DClustered,\n        Series: []excelize.ChartSeries{\n            {\n                Name:       \"Sheet1!$A$2\",\n                Categories: \"Sheet1!$B$1:$D$1\",\n                Values:     \"Sheet1!$B$2:$D$2\",\n            },\n            {\n                Name:       \"Sheet1!$A$3\",\n                Categories: \"Sheet1!$B$1:$D$1\",\n                Values:     \"Sheet1!$B$3:$D$3\",\n            },\n            {\n                Name:       \"Sheet1!$A$4\",\n                Categories: \"Sheet1!$B$1:$D$1\",\n                Values:     \"Sheet1!$B$4:$D$4\",\n            }},\n        Title: []excelize.RichTextRun{\n            {\n                Text: \"Fruit 3D Clustered Column Chart\",\n            },\n        },\n    }); err != nil {\n        fmt.Println(err)\n        return\n    }\n    // 根据指定路径保存文件\n    if err := f.SaveAs(\"Book1.xlsx\"); err != nil {\n        fmt.Println(err)\n    }\n}\n```\n\n### 向 Excel 文档中插入图片\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    _ \"image/gif\"\n    _ \"image/jpeg\"\n    _ \"image/png\"\n\n    \"github.com/xuri/excelize/v2\"\n)\n\nfunc main() {\n    f, err := excelize.OpenFile(\"Book1.xlsx\")\n    if err != nil {\n        fmt.Println(err)\n        return\n    }\n    defer func() {\n        // 关闭工作簿\n        if err := f.Close(); err != nil {\n            fmt.Println(err)\n        }\n    }()\n    // 插入图片\n    if err := f.AddPicture(\"Sheet1\", \"A2\", \"image.png\", nil); err != nil {\n        fmt.Println(err)\n    }\n    // 在工作表中插入图片，并设置图片的缩放比例\n    if err := f.AddPicture(\"Sheet1\", \"D2\", \"image.jpg\",\n        &excelize.GraphicOptions{ScaleX: 0.5, ScaleY: 0.5}); err != nil {\n        fmt.Println(err)\n    }\n    // 在工作表中插入图片，并设置图片的打印属性\n    enable, disable := true, false\n    if err := f.AddPicture(\"Sheet1\", \"H2\", \"image.gif\",\n        &excelize.GraphicOptions{\n            PrintObject:     &enable,\n            LockAspectRatio: false,\n            OffsetX:         15,\n            OffsetY:         10,\n            Locked:          &disable,\n        }); err != nil {\n        fmt.Println(err)\n    }\n    // 保存工作簿\n    if err = f.Save(); err != nil {\n        fmt.Println(err)\n    }\n}\n```\n\n## 社区合作\n\n欢迎您为此项目贡献代码，提出建议或问题、修复 Bug 以及参与讨论对新功能的想法。 XML 符合标准： [part 1 of the 5th edition of the ECMA-376 Standard for Office Open XML](https://www.ecma-international.org/publications-and-standards/standards/ecma-376/)。\n\n## 开源许可\n\n本项目遵循 BSD 3-Clause 开源许可协议，访问 [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 查看许可协议文件。\n\nExcel 徽标是 [Microsoft Corporation](https://aka.ms/trademarks-usage) 的商标，项目的图片是一种改编。\n\nGo gopher 由 [Renee French](https://go.dev/doc/gopher/README) 创作，遵循 [Creative Commons 4.0 Attributions license](http://creativecommons.org/licenses/by/4.0/) 创作共用授权条款。\n\n## 金牌赞助商\n\n<a href=\"https://www.gravityclimate.com\" alt=\"Gravity\"><img width=\"120\" src=\"https://xuri.me/excelize/images/vendor/gravityclimate.com.svg\" alt=\"Gravity\"></a>\n"
  },
  {
    "path": "adjust.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/xuri/efp\"\n)\n\ntype adjustDirection bool\n\nconst (\n\tcolumns adjustDirection = false\n\trows    adjustDirection = true\n)\n\n// adjustHelperFunc defines functions to adjust helper.\nvar adjustHelperFunc = [9]func(*File, *xlsxWorksheet, string, adjustDirection, int, int, int) error{\n\tfunc(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\t\treturn f.adjustConditionalFormats(ws, sheet, dir, num, offset, sheetID)\n\t},\n\tfunc(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\t\treturn f.adjustDataValidations(ws, sheet, dir, num, offset, sheetID)\n\t},\n\tfunc(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\t\treturn f.adjustDefinedNames(sheet, dir, num, offset)\n\t},\n\tfunc(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\t\treturn f.adjustDrawings(ws, sheet, dir, num, offset)\n\t},\n\tfunc(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\t\treturn f.adjustMergeCells(ws, sheet, dir, num, offset, sheetID)\n\t},\n\tfunc(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\t\treturn f.adjustAutoFilter(ws, sheet, dir, num, offset, sheetID)\n\t},\n\tfunc(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\t\treturn f.adjustCalcChain(ws, sheet, dir, num, offset, sheetID)\n\t},\n\tfunc(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\t\treturn f.adjustTable(ws, sheet, dir, num, offset, sheetID)\n\t},\n\tfunc(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\t\treturn f.adjustVolatileDeps(ws, sheet, dir, num, offset, sheetID)\n\t},\n}\n\n// adjustHelper provides a function to adjust rows and columns dimensions,\n// hyperlinks, merged cells and auto filter when inserting or deleting rows or\n// columns.\n//\n// sheet: Worksheet name that we're editing\n// column: Index number of the column we're inserting/deleting before\n// row: Index number of the row we're inserting/deleting before\n// offset: Number of rows/column to insert/delete negative values indicate deletion\n//\n// TODO: adjustComments, adjustPageBreaks, adjustProtectedCells\nfunc (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.clearCalcCache()\n\tsheetID := f.getSheetID(sheet)\n\tif dir == rows {\n\t\terr = f.adjustRowDimensions(sheet, ws, num, offset)\n\t} else {\n\t\terr = f.adjustColDimensions(sheet, ws, num, offset)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.adjustHyperlinks(ws, sheet, dir, num, offset)\n\tws.checkSheet()\n\t_ = ws.checkRow()\n\tfor _, fn := range adjustHelperFunc {\n\t\tif err := fn(f, ws, sheet, dir, num, offset, sheetID); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif ws.MergeCells != nil && len(ws.MergeCells.Cells) == 0 {\n\t\tws.MergeCells = nil\n\t}\n\treturn nil\n}\n\n// adjustCols provides a function to update column style when inserting or\n// deleting columns.\nfunc (f *File) adjustCols(ws *xlsxWorksheet, col, offset int) error {\n\tif ws.Cols == nil {\n\t\treturn nil\n\t}\n\tfor i := 0; i < len(ws.Cols.Col); i++ {\n\t\tif offset > 0 {\n\t\t\tif ws.Cols.Col[i].Min >= col {\n\t\t\t\tif ws.Cols.Col[i].Min += offset; ws.Cols.Col[i].Min > MaxColumns {\n\t\t\t\t\tws.Cols.Col = append(ws.Cols.Col[:i], ws.Cols.Col[i+1:]...)\n\t\t\t\t\ti--\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ws.Cols.Col[i].Max >= col || ws.Cols.Col[i].Max+1 == col {\n\t\t\t\tif ws.Cols.Col[i].Max += offset; ws.Cols.Col[i].Max > MaxColumns {\n\t\t\t\t\tws.Cols.Col[i].Max = MaxColumns\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif ws.Cols.Col[i].Min == col && ws.Cols.Col[i].Max == col {\n\t\t\tws.Cols.Col = append(ws.Cols.Col[:i], ws.Cols.Col[i+1:]...)\n\t\t\ti--\n\t\t\tcontinue\n\t\t}\n\t\tif ws.Cols.Col[i].Min > col {\n\t\t\tws.Cols.Col[i].Min += offset\n\t\t}\n\t\tif ws.Cols.Col[i].Max >= col {\n\t\t\tws.Cols.Col[i].Max += offset\n\t\t}\n\t}\n\tif len(ws.Cols.Col) == 0 {\n\t\tws.Cols = nil\n\t}\n\treturn nil\n}\n\n// adjustColDimensions provides a function to update column dimensions when\n// inserting or deleting rows or columns.\nfunc (f *File) adjustColDimensions(sheet string, ws *xlsxWorksheet, col, offset int) error {\n\tfor rowIdx := range ws.SheetData.Row {\n\t\tfor _, v := range ws.SheetData.Row[rowIdx].C {\n\t\t\tif cellCol, _, _ := CellNameToCoordinates(v.R); col <= cellCol {\n\t\t\t\tif newCol := cellCol + offset; newCol > 0 && newCol > MaxColumns {\n\t\t\t\t\treturn ErrColumnNumber\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfor _, sheetN := range f.GetSheetList() {\n\t\tworksheet, err := f.workSheetReader(sheetN)\n\t\tif err != nil {\n\t\t\tif err.Error() == newNotWorksheetError(sheetN).Error() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tfor rowIdx := range worksheet.SheetData.Row {\n\t\t\tfor colIdx, v := range worksheet.SheetData.Row[rowIdx].C {\n\t\t\t\tif cellCol, cellRow, _ := CellNameToCoordinates(v.R); sheetN == sheet && col <= cellCol {\n\t\t\t\t\tif newCol := cellCol + offset; newCol > 0 {\n\t\t\t\t\t\tworksheet.SheetData.Row[rowIdx].C[colIdx].R, _ = CoordinatesToCellName(newCol, cellRow)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif err := f.adjustFormula(sheet, sheetN, &worksheet.SheetData.Row[rowIdx].C[colIdx], columns, col, offset, false); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn f.adjustCols(ws, col, offset)\n}\n\n// adjustRowDimensions provides a function to update row dimensions when\n// inserting or deleting rows or columns.\nfunc (f *File) adjustRowDimensions(sheet string, ws *xlsxWorksheet, row, offset int) error {\n\tfor _, sheetN := range f.GetSheetList() {\n\t\tif sheetN == sheet {\n\t\t\tcontinue\n\t\t}\n\t\tworksheet, err := f.workSheetReader(sheetN)\n\t\tif err != nil {\n\t\t\tif err.Error() == newNotWorksheetError(sheetN).Error() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tnumOfRows := len(worksheet.SheetData.Row)\n\t\tfor i := 0; i < numOfRows; i++ {\n\t\t\tr := &worksheet.SheetData.Row[i]\n\t\t\tif err = f.adjustSingleRowFormulas(sheet, sheetN, r, row, offset, false); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\ttotalRows := len(ws.SheetData.Row)\n\tif totalRows == 0 {\n\t\treturn nil\n\t}\n\tlastRow := &ws.SheetData.Row[totalRows-1]\n\tif newRow := lastRow.R + offset; lastRow.R >= row && newRow > 0 && newRow > TotalRows {\n\t\treturn ErrMaxRows\n\t}\n\tnumOfRows := len(ws.SheetData.Row)\n\tfor i := 0; i < numOfRows; i++ {\n\t\tr := &ws.SheetData.Row[i]\n\t\tif newRow := r.R + offset; r.R >= row && newRow > 0 {\n\t\t\tr.adjustSingleRowDimensions(offset)\n\t\t}\n\t\tif err := f.adjustSingleRowFormulas(sheet, sheet, r, row, offset, false); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// adjustSingleRowDimensions provides a function to adjust single row dimensions.\nfunc (r *xlsxRow) adjustSingleRowDimensions(offset int) {\n\tr.R += offset\n\tfor i, col := range r.C {\n\t\tcolName, _, _ := SplitCellName(col.R)\n\t\tr.C[i].R, _ = JoinCellName(colName, r.R)\n\t}\n}\n\n// adjustSingleRowFormulas provides a function to adjust single row formulas.\nfunc (f *File) adjustSingleRowFormulas(sheet, sheetN string, r *xlsxRow, num, offset int, si bool) error {\n\tfor i := 0; i < len(r.C); i++ {\n\t\tif err := f.adjustFormula(sheet, sheetN, &r.C[i], rows, num, offset, si); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// adjustCellRef provides a function to adjust cell reference.\nfunc (f *File) adjustCellRef(cellRef string, dir adjustDirection, num, offset int) (string, error) {\n\tvar SQRef []string\n\tapplyOffset := func(coordinates []int, idx1, idx2, maxVal int) []int {\n\t\tif coordinates[idx1] >= num {\n\t\t\tcoordinates[idx1] += offset\n\t\t}\n\t\tif coordinates[idx2] >= num {\n\t\t\tif coordinates[idx2] += offset; coordinates[idx2] > maxVal {\n\t\t\t\tcoordinates[idx2] = maxVal\n\t\t\t}\n\t\t}\n\t\treturn coordinates\n\t}\n\tfor _, ref := range strings.Split(cellRef, \" \") {\n\t\tif !strings.Contains(ref, \":\") {\n\t\t\tref += \":\" + ref\n\t\t}\n\t\tcoordinates, err := rangeRefToCoordinates(ref)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif dir == columns {\n\t\t\tif offset < 0 && coordinates[0] == coordinates[2] && num == coordinates[0] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcoordinates = applyOffset(coordinates, 0, 2, MaxColumns)\n\t\t} else {\n\t\t\tif offset < 0 && coordinates[1] == coordinates[3] && num == coordinates[1] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcoordinates = applyOffset(coordinates, 1, 3, TotalRows)\n\t\t}\n\t\tif ref, err = coordinatesToRangeRef(coordinates); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tSQRef = append(SQRef, ref)\n\t}\n\treturn strings.Join(SQRef, \" \"), nil\n}\n\n// adjustFormula provides a function to adjust formula reference and shared\n// formula reference.\nfunc (f *File) adjustFormula(sheet, sheetN string, cell *xlsxC, dir adjustDirection, num, offset int, si bool) error {\n\tvar err error\n\tif cell.f != \"\" {\n\t\tif cell.f, err = f.adjustFormulaRef(sheet, sheetN, cell.f, false, dir, num, offset); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif cell.F == nil {\n\t\treturn nil\n\t}\n\tif cell.F.Ref != \"\" && sheet == sheetN {\n\t\tif cell.F.Ref, err = f.adjustCellRef(cell.F.Ref, dir, num, offset); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif si && cell.F.Si != nil {\n\t\t\tcell.F.Si = intPtr(*cell.F.Si + 1)\n\t\t}\n\t}\n\tif cell.F.Content != \"\" {\n\t\tif cell.F.Content, err = f.adjustFormulaRef(sheet, sheetN, cell.F.Content, false, dir, num, offset); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// escapeSheetName enclose sheet name in single quotation marks if the giving\n// worksheet name includes spaces or non-alphabetical characters.\nfunc escapeSheetName(name string) string {\n\tif strings.IndexFunc(name, func(r rune) bool {\n\t\treturn !unicode.IsLetter(r) && !unicode.IsNumber(r)\n\t}) != -1 {\n\t\treturn \"'\" + strings.ReplaceAll(name, \"'\", \"''\") + \"'\"\n\t}\n\treturn name\n}\n\n// adjustFormulaColumnName adjust column name in the formula reference.\nfunc adjustFormulaColumnName(name, operand string, abs, keepRelative bool, dir adjustDirection, num, offset int) (string, string, bool, error) {\n\tif name == \"\" || (!abs && keepRelative) {\n\t\treturn \"\", operand + name, abs, nil\n\t}\n\tcol, err := ColumnNameToNumber(name)\n\tif err != nil {\n\t\treturn \"\", operand, false, err\n\t}\n\tif dir == columns && col >= num {\n\t\tif col += offset; col < 1 {\n\t\t\tcol = 1\n\t\t}\n\t\tcolName, err := ColumnNumberToName(col)\n\t\treturn \"\", operand + colName, false, err\n\t}\n\treturn \"\", operand + name, false, nil\n}\n\n// adjustFormulaRowNumber adjust row number in the formula reference.\nfunc adjustFormulaRowNumber(name, operand string, abs, keepRelative bool, dir adjustDirection, num, offset int) (string, string, bool, error) {\n\tif name == \"\" || (!abs && keepRelative) {\n\t\treturn \"\", operand + name, abs, nil\n\t}\n\trow, _ := strconv.Atoi(name)\n\tif dir == rows && row >= num {\n\t\tif row += offset; row < 1 {\n\t\t\trow = 1\n\t\t}\n\t\tif row > TotalRows {\n\t\t\treturn \"\", operand + name, false, ErrMaxRows\n\t\t}\n\t\treturn \"\", operand + strconv.Itoa(row), false, nil\n\t}\n\treturn \"\", operand + name, false, nil\n}\n\n// adjustFormulaOperandRef adjust cell reference in the operand tokens for the formula.\nfunc adjustFormulaOperandRef(row, col, operand string, abs, keepRelative bool, dir adjustDirection, num int, offset int) (string, string, string, bool, error) {\n\tvar err error\n\tcol, operand, abs, err = adjustFormulaColumnName(col, operand, abs, keepRelative, dir, num, offset)\n\tif err != nil {\n\t\treturn row, col, operand, abs, err\n\t}\n\trow, operand, abs, err = adjustFormulaRowNumber(row, operand, abs, keepRelative, dir, num, offset)\n\treturn row, col, operand, abs, err\n}\n\n// adjustFormulaOperand adjust range operand tokens for the formula.\nfunc (f *File) adjustFormulaOperand(sheet, sheetN string, keepRelative bool, token efp.Token, dir adjustDirection, num int, offset int) (string, error) {\n\tvar (\n\t\terr                          error\n\t\tabs                          bool\n\t\tsheetName, col, row, operand string\n\t\tcell                         = token.TValue\n\t\ttokens                       = strings.Split(token.TValue, \"!\")\n\t)\n\tif len(tokens) == 2 { // have a worksheet\n\t\tsheetName, cell = tokens[0], tokens[1]\n\t\toperand = escapeSheetName(sheetName) + \"!\"\n\t}\n\tif sheetName == \"\" {\n\t\tsheetName = sheetN\n\t}\n\tif sheet != sheetName {\n\t\treturn operand + cell, err\n\t}\n\tfor _, r := range cell {\n\t\tif r == '$' {\n\t\t\tif col, operand, _, err = adjustFormulaColumnName(col, operand, abs, keepRelative, dir, num, offset); err != nil {\n\t\t\t\treturn operand, err\n\t\t\t}\n\t\t\tabs = true\n\t\t\toperand += string(r)\n\t\t\tcontinue\n\t\t}\n\t\tif ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') {\n\t\t\tcol += string(r)\n\t\t\tcontinue\n\t\t}\n\t\tif '0' <= r && r <= '9' {\n\t\t\trow += string(r)\n\t\t\tcol, operand, abs, err = adjustFormulaColumnName(col, operand, abs, keepRelative, dir, num, offset)\n\t\t\tif err != nil {\n\t\t\t\treturn operand, err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif row, col, operand, abs, err = adjustFormulaOperandRef(row, col, operand, abs, keepRelative, dir, num, offset); err != nil {\n\t\t\treturn operand, err\n\t\t}\n\t\toperand += string(r)\n\t}\n\t_, _, operand, _, err = adjustFormulaOperandRef(row, col, operand, abs, keepRelative, dir, num, offset)\n\treturn operand, err\n}\n\n// adjustFormulaRef returns adjusted formula by giving adjusting direction and\n// the base number of column or row, and offset.\nfunc (f *File) adjustFormulaRef(sheet, sheetN, formula string, keepRelative bool, dir adjustDirection, num, offset int) (string, error) {\n\tvar (\n\t\tval          string\n\t\tdefinedNames []string\n\t\tps           = efp.ExcelParser()\n\t)\n\tfor _, definedName := range f.GetDefinedName() {\n\t\tif definedName.Scope == \"Workbook\" || definedName.Scope == sheet {\n\t\t\tdefinedNames = append(definedNames, definedName.Name)\n\t\t}\n\t}\n\tfor _, token := range ps.Parse(formula) {\n\t\tif token.TType == efp.TokenTypeUnknown {\n\t\t\tval = formula\n\t\t\tbreak\n\t\t}\n\t\tif token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeRange {\n\t\t\tif inStrSlice(definedNames, token.TValue, true) != -1 {\n\t\t\t\tval += token.TValue\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif strings.ContainsAny(token.TValue, \"[]\") {\n\t\t\t\tval += token.TValue\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\toperand, err := f.adjustFormulaOperand(sheet, sheetN, keepRelative, token, dir, num, offset)\n\t\t\tif err != nil {\n\t\t\t\treturn val, err\n\t\t\t}\n\t\t\tval += operand\n\t\t\tcontinue\n\t\t}\n\t\tif paren := transformParenthesesToken(token); paren != \"\" {\n\t\t\tval += transformParenthesesToken(token)\n\t\t\tcontinue\n\t\t}\n\t\tif token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeText {\n\t\t\tval += string(efp.QuoteDouble) + strings.ReplaceAll(token.TValue, \"\\\"\", \"\\\"\\\"\") + string(efp.QuoteDouble)\n\t\t\tcontinue\n\t\t}\n\t\tval += token.TValue\n\t}\n\treturn val, nil\n}\n\n// transformParenthesesToken returns formula part with parentheses by given\n// token.\nfunc transformParenthesesToken(token efp.Token) string {\n\tif isFunctionStartToken(token) || isBeginParenthesesToken(token) {\n\t\treturn token.TValue + string(efp.ParenOpen)\n\t}\n\tif isFunctionStopToken(token) || isEndParenthesesToken(token) {\n\t\treturn token.TValue + string(efp.ParenClose)\n\t}\n\treturn \"\"\n}\n\n// adjustRangeSheetName returns replaced range reference by given source and\n// target sheet name.\nfunc adjustRangeSheetName(rng, source, target string) string {\n\tsource = escapeSheetName(source)\n\tcellRefs := strings.Split(rng, \",\")\n\tfor i, cellRef := range cellRefs {\n\t\trangeRefs := strings.Split(cellRef, \":\")\n\t\tfor j, rangeRef := range rangeRefs {\n\t\t\tparts := strings.Split(rangeRef, \"!\")\n\t\t\tfor k, part := range parts {\n\t\t\t\tif strings.TrimPrefix(strings.TrimSuffix(part, \"'\"), \"'\") == source {\n\t\t\t\t\tpart = escapeSheetName(target)\n\t\t\t\t}\n\t\t\t\tparts[k] = part\n\t\t\t}\n\t\t\trangeRefs[j] = strings.Join(parts, \"!\")\n\t\t}\n\t\tcellRefs[i] = strings.Join(rangeRefs, \":\")\n\t}\n\treturn strings.Join(cellRefs, \",\")\n}\n\n// arrayFormulaOperandToken defines meta fields for transforming the array\n// formula to the normal formula.\ntype arrayFormulaOperandToken struct {\n\toperandTokenIndex, topLeftCol, topLeftRow, bottomRightCol, bottomRightRow int\n\tsheetName, sourceCellRef, targetCellRef                                   string\n}\n\n// setCoordinates convert each corner cell reference in the array formula cell\n// range to the coordinate number.\nfunc (af *arrayFormulaOperandToken) setCoordinates() error {\n\tfor i, ref := range strings.Split(af.sourceCellRef, \":\") {\n\t\tcellRef, col, row, err := parseRef(ref)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar c, r int\n\t\tif col {\n\t\t\tif cellRef.Row = TotalRows; i == 0 {\n\t\t\t\tcellRef.Row = 1\n\t\t\t}\n\t\t}\n\t\tif row {\n\t\t\tif cellRef.Col = MaxColumns; i == 0 {\n\t\t\t\tcellRef.Col = 1\n\t\t\t}\n\t\t}\n\t\tif c, r = cellRef.Col, cellRef.Row; cellRef.Sheet != \"\" {\n\t\t\taf.sheetName = cellRef.Sheet + \"!\"\n\t\t}\n\t\tif af.topLeftCol == 0 || c < af.topLeftCol {\n\t\t\taf.topLeftCol = c\n\t\t}\n\t\tif af.topLeftRow == 0 || r < af.topLeftRow {\n\t\t\taf.topLeftRow = r\n\t\t}\n\t\tif c > af.bottomRightCol {\n\t\t\taf.bottomRightCol = c\n\t\t}\n\t\tif r > af.bottomRightRow {\n\t\t\taf.bottomRightRow = r\n\t\t}\n\t}\n\treturn nil\n}\n\n// transformArrayFormula transforms an array formula to the normal formula by\n// giving a formula tokens list and formula operand tokens list.\nfunc transformArrayFormula(tokens []efp.Token, afs []arrayFormulaOperandToken) string {\n\tvar val string\n\tfor i, token := range tokens {\n\t\tvar skip bool\n\t\tfor _, af := range afs {\n\t\t\tif af.operandTokenIndex == i {\n\t\t\t\tval += af.sheetName + af.targetCellRef\n\t\t\t\tskip = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif skip {\n\t\t\tcontinue\n\t\t}\n\t\tif paren := transformParenthesesToken(token); paren != \"\" {\n\t\t\tval += transformParenthesesToken(token)\n\t\t\tcontinue\n\t\t}\n\t\tif token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeText {\n\t\t\tval += string(efp.QuoteDouble) + strings.ReplaceAll(token.TValue, \"\\\"\", \"\\\"\\\"\") + string(efp.QuoteDouble)\n\t\t\tcontinue\n\t\t}\n\t\tval += token.TValue\n\t}\n\treturn val\n}\n\n// getArrayFormulaTokens returns parsed formula token and operand related token\n// list for in array formula.\nfunc getArrayFormulaTokens(sheet, formula string, definedNames []DefinedName) ([]efp.Token, []arrayFormulaOperandToken, error) {\n\tvar (\n\t\tps                        = efp.ExcelParser()\n\t\ttokens                    = ps.Parse(formula)\n\t\tarrayFormulaOperandTokens []arrayFormulaOperandToken\n\t)\n\tfor i, token := range tokens {\n\t\tif token.TSubType == efp.TokenSubTypeRange && token.TType == efp.TokenTypeOperand {\n\t\t\ttokenVal := token.TValue\n\t\t\tfor _, definedName := range definedNames {\n\t\t\t\tif (definedName.Scope == \"Workbook\" || definedName.Scope == sheet) && definedName.Name == tokenVal {\n\t\t\t\t\ttokenVal = definedName.RefersTo\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(strings.Split(tokenVal, \":\")) > 1 {\n\t\t\t\tarrayFormulaOperandToken := arrayFormulaOperandToken{\n\t\t\t\t\toperandTokenIndex: i,\n\t\t\t\t\tsourceCellRef:     tokenVal,\n\t\t\t\t}\n\t\t\t\tif err := arrayFormulaOperandToken.setCoordinates(); err != nil {\n\t\t\t\t\treturn tokens, arrayFormulaOperandTokens, err\n\t\t\t\t}\n\t\t\t\tarrayFormulaOperandTokens = append(arrayFormulaOperandTokens, arrayFormulaOperandToken)\n\t\t\t}\n\t\t}\n\t}\n\treturn tokens, arrayFormulaOperandTokens, nil\n}\n\n// adjustHyperlinks provides a function to update hyperlinks when inserting or\n// deleting rows or columns.\nfunc (f *File) adjustHyperlinks(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) {\n\t// short path\n\tif ws.Hyperlinks == nil || len(ws.Hyperlinks.Hyperlink) == 0 {\n\t\treturn\n\t}\n\n\t// order is important\n\tif offset < 0 {\n\t\tfor i := len(ws.Hyperlinks.Hyperlink) - 1; i >= 0; i-- {\n\t\t\tlinkData := ws.Hyperlinks.Hyperlink[i]\n\t\t\tcolNum, rowNum, _ := CellNameToCoordinates(linkData.Ref)\n\n\t\t\tif (dir == rows && num == rowNum) || (dir == columns && num == colNum) {\n\t\t\t\tf.deleteSheetRelationships(sheet, linkData.RID)\n\t\t\t\tif len(ws.Hyperlinks.Hyperlink) > 1 {\n\t\t\t\t\tws.Hyperlinks.Hyperlink = append(ws.Hyperlinks.Hyperlink[:i],\n\t\t\t\t\t\tws.Hyperlinks.Hyperlink[i+1:]...)\n\t\t\t\t} else {\n\t\t\t\t\tws.Hyperlinks = nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif ws.Hyperlinks == nil {\n\t\treturn\n\t}\n\tfor i := range ws.Hyperlinks.Hyperlink {\n\t\tlink := &ws.Hyperlinks.Hyperlink[i] // get reference\n\t\tlink.Ref, _ = f.adjustFormulaRef(sheet, sheet, link.Ref, false, dir, num, offset)\n\t}\n}\n\n// adjustTable provides a function to update the table when inserting or\n// deleting rows or columns.\nfunc (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\tif ws.TableParts == nil || len(ws.TableParts.TableParts) == 0 {\n\t\treturn nil\n\t}\n\tfor idx := 0; idx < len(ws.TableParts.TableParts); idx++ {\n\t\ttbl := ws.TableParts.TableParts[idx]\n\t\ttarget := f.getSheetRelationshipsTargetByID(sheet, tbl.RID)\n\t\ttableXML := strings.ReplaceAll(target, \"..\", \"xl\")\n\t\tcontent, ok := f.Pkg.Load(tableXML)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tt := xlsxTable{}\n\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).\n\t\t\tDecode(&t); err != nil && err != io.EOF {\n\t\t\treturn err\n\t\t}\n\t\tcoordinates, err := rangeRefToCoordinates(t.Ref)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// Remove the table when deleting the header row of the table\n\t\tif dir == rows && num == coordinates[0] && offset == -1 {\n\t\t\tws.TableParts.TableParts = append(ws.TableParts.TableParts[:idx], ws.TableParts.TableParts[idx+1:]...)\n\t\t\tws.TableParts.Count = len(ws.TableParts.TableParts)\n\t\t\tidx--\n\t\t\tcontinue\n\t\t}\n\t\tcoordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset)\n\t\tx1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]\n\t\tif y2-y1 < 1 || x2-x1 < 0 {\n\t\t\tws.TableParts.TableParts = append(ws.TableParts.TableParts[:idx], ws.TableParts.TableParts[idx+1:]...)\n\t\t\tws.TableParts.Count = len(ws.TableParts.TableParts)\n\t\t\tidx--\n\t\t\tcontinue\n\t\t}\n\t\tt.Ref, _ = coordinatesToRangeRef([]int{x1, y1, x2, y2})\n\t\tif t.AutoFilter != nil {\n\t\t\tt.AutoFilter.Ref = t.Ref\n\t\t}\n\t\t_ = f.setTableColumns(sheet, true, x1, y1, x2, &t)\n\t\t// Currently doesn't support query table\n\t\tt.TableType, t.TotalsRowCount, t.ConnectionID = \"\", 0, 0\n\t\ttable, _ := xml.Marshal(t)\n\t\tf.saveFileList(tableXML, table)\n\t}\n\treturn nil\n}\n\n// adjustAutoFilter provides a function to update the auto filter when\n// inserting or deleting rows or columns.\nfunc (f *File) adjustAutoFilter(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\tif ws.AutoFilter == nil {\n\t\treturn nil\n\t}\n\n\tcoordinates, err := rangeRefToCoordinates(ws.AutoFilter.Ref)\n\tif err != nil {\n\t\treturn err\n\t}\n\tx1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]\n\n\tif (dir == rows && y1 == num && offset < 0) || (dir == columns && x1 == num && x2 == num) {\n\t\tws.AutoFilter = nil\n\t\tfor rowIdx := range ws.SheetData.Row {\n\t\t\trowData := &ws.SheetData.Row[rowIdx]\n\t\t\tif rowData.R > y1 && rowData.R <= y2 {\n\t\t\t\trowData.Hidden = false\n\t\t\t}\n\t\t}\n\t\treturn err\n\t}\n\n\tcoordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset)\n\tx1, y1, x2, y2 = coordinates[0], coordinates[1], coordinates[2], coordinates[3]\n\n\tws.AutoFilter.Ref, err = coordinatesToRangeRef([]int{x1, y1, x2, y2})\n\treturn err\n}\n\n// adjustAutoFilterHelper provides a function for adjusting auto filter to\n// compare and calculate cell reference by the giving adjusting direction,\n// operation reference and offset.\nfunc (f *File) adjustAutoFilterHelper(dir adjustDirection, coordinates []int, num, offset int) []int {\n\tif dir == rows {\n\t\tif coordinates[1] >= num {\n\t\t\tcoordinates[1] += offset\n\t\t}\n\t\tif coordinates[3] >= num {\n\t\t\tcoordinates[3] += offset\n\t\t}\n\t\treturn coordinates\n\t}\n\tif coordinates[0] >= num {\n\t\tcoordinates[0] += offset\n\t}\n\tif coordinates[2] >= num {\n\t\tcoordinates[2] += offset\n\t}\n\treturn coordinates\n}\n\n// adjustMergeCells provides a function to update merged cells when inserting\n// or deleting rows or columns.\nfunc (f *File) adjustMergeCells(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\tif ws.MergeCells == nil {\n\t\treturn nil\n\t}\n\n\tfor i := 0; i < len(ws.MergeCells.Cells); i++ {\n\t\tmergedCells := ws.MergeCells.Cells[i]\n\t\tmergedCellsRef := mergedCells.Ref\n\t\tif !strings.Contains(mergedCellsRef, \":\") {\n\t\t\tmergedCellsRef += \":\" + mergedCellsRef\n\t\t}\n\t\tcoordinates, err := rangeRefToCoordinates(mergedCellsRef)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tx1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]\n\t\tif dir == rows {\n\t\t\tif y1 == num && y2 == num && offset < 0 {\n\t\t\t\tf.deleteMergeCell(ws, i)\n\t\t\t\ti--\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\ty1, y2 = f.adjustMergeCellsHelper(y1, y2, num, offset)\n\t\t} else {\n\t\t\tif x1 == num && x2 == num && offset < 0 {\n\t\t\t\tf.deleteMergeCell(ws, i)\n\t\t\t\ti--\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tx1, x2 = f.adjustMergeCellsHelper(x1, x2, num, offset)\n\t\t}\n\t\tif x1 == x2 && y1 == y2 {\n\t\t\tf.deleteMergeCell(ws, i)\n\t\t\ti--\n\t\t\tcontinue\n\t\t}\n\t\tmergedCells.rect = []int{x1, y1, x2, y2}\n\t\tif mergedCells.Ref, err = coordinatesToRangeRef([]int{x1, y1, x2, y2}); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// adjustMergeCellsHelper provides a function for adjusting merge cells to\n// compare and calculate cell reference by the given pivot, operation reference\n// and offset.\nfunc (f *File) adjustMergeCellsHelper(p1, p2, num, offset int) (int, int) {\n\tif p2 < p1 {\n\t\tp1, p2 = p2, p1\n\t}\n\n\tif offset >= 0 {\n\t\tif num <= p1 {\n\t\t\tp1 += offset\n\t\t\tp2 += offset\n\t\t} else if num <= p2 {\n\t\t\tp2 += offset\n\t\t}\n\t\treturn p1, p2\n\t}\n\tif num < p1 || (num == p1 && num == p2) {\n\t\tp1 += offset\n\t\tp2 += offset\n\t} else if num <= p2 {\n\t\tp2 += offset\n\t}\n\treturn p1, p2\n}\n\n// deleteMergeCell provides a function to delete merged cell by given index.\nfunc (f *File) deleteMergeCell(ws *xlsxWorksheet, idx int) {\n\tif idx < 0 {\n\t\treturn\n\t}\n\tif len(ws.MergeCells.Cells) > idx {\n\t\tws.MergeCells.Cells = append(ws.MergeCells.Cells[:idx], ws.MergeCells.Cells[idx+1:]...)\n\t\tws.MergeCells.Count = len(ws.MergeCells.Cells)\n\t}\n}\n\n// adjustCellName returns updated cell name by giving column/row number and\n// offset on inserting or deleting rows or columns.\nfunc adjustCellName(cell string, dir adjustDirection, c, r, offset int) (string, error) {\n\tif dir == rows {\n\t\tif rn := r + offset; rn > 0 {\n\t\t\treturn CoordinatesToCellName(c, rn)\n\t\t}\n\t}\n\treturn CoordinatesToCellName(c+offset, r)\n}\n\n// adjustCalcChain provides a function to update the calculation chain when\n// inserting or deleting rows or columns.\nfunc (f *File) adjustCalcChain(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\tif f.CalcChain == nil {\n\t\treturn nil\n\t}\n\t// If sheet ID is omitted, it is assumed to be the same as the i value of\n\t// the previous cell.\n\tvar prevSheetID int\n\tfor i := 0; f.CalcChain != nil && i < len(f.CalcChain.C); i++ {\n\t\tc := f.CalcChain.C[i]\n\t\tif c.I == 0 {\n\t\t\tc.I = prevSheetID\n\t\t}\n\t\tprevSheetID = c.I\n\t\tif c.I != sheetID {\n\t\t\tcontinue\n\t\t}\n\t\tcolNum, rowNum, err := CellNameToCoordinates(c.R)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif dir == rows && num <= rowNum {\n\t\t\tif num == rowNum && offset == -1 {\n\t\t\t\t_ = f.deleteCalcChain(c.I, c.R)\n\t\t\t\ti--\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tf.CalcChain.C[i].R, _ = adjustCellName(c.R, dir, colNum, rowNum, offset)\n\t\t}\n\t\tif dir == columns && num <= colNum {\n\t\t\tif num == colNum && offset == -1 {\n\t\t\t\t_ = f.deleteCalcChain(c.I, c.R)\n\t\t\t\ti--\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tf.CalcChain.C[i].R, _ = adjustCellName(c.R, dir, colNum, rowNum, offset)\n\t\t}\n\t}\n\treturn nil\n}\n\n// adjustVolatileDepsTopic updates the volatile dependencies topic when\n// inserting or deleting rows or columns.\nfunc (vt *xlsxVolTypes) adjustVolatileDepsTopic(cell string, dir adjustDirection, indexes []int) (int, error) {\n\tnum, offset, i1, i2, i3, i4 := indexes[0], indexes[1], indexes[2], indexes[3], indexes[4], indexes[5]\n\tcolNum, rowNum, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn i4, err\n\t}\n\tif dir == rows && num <= rowNum {\n\t\tif num == rowNum && offset == -1 {\n\t\t\tvt.deleteVolTopicRef(i1, i2, i3, i4)\n\t\t\ti4--\n\t\t\treturn i4, err\n\t\t}\n\t\tvt.VolType[i1].Main[i2].Tp[i3].Tr[i4].R, _ = adjustCellName(cell, dir, colNum, rowNum, offset)\n\t}\n\tif dir == columns && num <= colNum {\n\t\tif num == colNum && offset == -1 {\n\t\t\tvt.deleteVolTopicRef(i1, i2, i3, i4)\n\t\t\ti4--\n\t\t\treturn i4, err\n\t\t}\n\t\tif name, _ := adjustCellName(cell, dir, colNum, rowNum, offset); name != \"\" {\n\t\t\tvt.VolType[i1].Main[i2].Tp[i3].Tr[i4].R, _ = adjustCellName(cell, dir, colNum, rowNum, offset)\n\t\t}\n\t}\n\treturn i4, err\n}\n\n// adjustVolatileDeps updates the volatile dependencies when inserting or\n// deleting rows or columns.\nfunc (f *File) adjustVolatileDeps(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\tvolTypes, err := f.volatileDepsReader()\n\tif err != nil || volTypes == nil {\n\t\treturn err\n\t}\n\tfor i1 := 0; i1 < len(volTypes.VolType); i1++ {\n\t\tfor i2 := 0; i2 < len(volTypes.VolType[i1].Main); i2++ {\n\t\t\tfor i3 := 0; i3 < len(volTypes.VolType[i1].Main[i2].Tp); i3++ {\n\t\t\t\tfor i4 := 0; i4 < len(volTypes.VolType[i1].Main[i2].Tp[i3].Tr); i4++ {\n\t\t\t\t\tref := volTypes.VolType[i1].Main[i2].Tp[i3].Tr[i4]\n\t\t\t\t\tif ref.S != sheetID {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif i4, err = volTypes.adjustVolatileDepsTopic(ref.R, dir, []int{num, offset, i1, i2, i3, i4}); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// adjustConditionalFormats updates the cell reference of the worksheet\n// conditional formatting when inserting or deleting rows or columns.\nfunc (f *File) adjustConditionalFormats(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\tfor i := 0; i < len(ws.ConditionalFormatting); i++ {\n\t\tcf := ws.ConditionalFormatting[i]\n\t\tif cf == nil {\n\t\t\tcontinue\n\t\t}\n\t\tref, err := f.adjustCellRef(cf.SQRef, dir, num, offset)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif ref == \"\" {\n\t\t\tws.ConditionalFormatting = append(ws.ConditionalFormatting[:i],\n\t\t\t\tws.ConditionalFormatting[i+1:]...)\n\t\t\ti--\n\t\t\tcontinue\n\t\t}\n\t\tws.ConditionalFormatting[i].SQRef = ref\n\t}\n\treturn nil\n}\n\n// adjustDataValidations updates the range of data validations for the worksheet\n// when inserting or deleting rows or columns.\nfunc (f *File) adjustDataValidations(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {\n\tfor _, sheetN := range f.GetSheetList() {\n\t\tworksheet, err := f.workSheetReader(sheetN)\n\t\tif err != nil {\n\t\t\tif err.Error() == newNotWorksheetError(sheetN).Error() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif worksheet.DataValidations == nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor i := 0; i < len(worksheet.DataValidations.DataValidation); i++ {\n\t\t\tdv := worksheet.DataValidations.DataValidation[i]\n\t\t\tif dv == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif sheet == sheetN {\n\t\t\t\tref, err := f.adjustCellRef(dv.Sqref, dir, num, offset)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif ref == \"\" {\n\t\t\t\t\tworksheet.DataValidations.DataValidation = append(worksheet.DataValidations.DataValidation[:i],\n\t\t\t\t\t\tworksheet.DataValidations.DataValidation[i+1:]...)\n\t\t\t\t\ti--\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tworksheet.DataValidations.DataValidation[i].Sqref = ref\n\t\t\t}\n\t\t\tif worksheet.DataValidations.DataValidation[i].Formula1.isFormula() {\n\t\t\t\tformula := formulaUnescaper.Replace(worksheet.DataValidations.DataValidation[i].Formula1.Content)\n\t\t\t\tif formula, err = f.adjustFormulaRef(sheet, sheetN, formula, false, dir, num, offset); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tworksheet.DataValidations.DataValidation[i].Formula1 = &xlsxInnerXML{Content: formulaEscaper.Replace(formula)}\n\t\t\t}\n\t\t\tif worksheet.DataValidations.DataValidation[i].Formula2.isFormula() {\n\t\t\t\tformula := formulaUnescaper.Replace(worksheet.DataValidations.DataValidation[i].Formula2.Content)\n\t\t\t\tif formula, err = f.adjustFormulaRef(sheet, sheetN, formula, false, dir, num, offset); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tworksheet.DataValidations.DataValidation[i].Formula2 = &xlsxInnerXML{Content: formulaEscaper.Replace(formula)}\n\t\t\t}\n\t\t}\n\t\tif worksheet.DataValidations.Count = len(worksheet.DataValidations.DataValidation); worksheet.DataValidations.Count == 0 {\n\t\t\tworksheet.DataValidations = nil\n\t\t}\n\t}\n\treturn nil\n}\n\n// adjustDrawings updates the starting anchor of the two cell anchor pictures\n// and charts object when inserting or deleting rows or columns.\nfunc (from *xlsxFrom) adjustDrawings(dir adjustDirection, num, offset int, editAs string) (bool, error) {\n\tvar ok bool\n\tif dir == columns && from.Col+1 >= num && from.Col+offset >= 0 {\n\t\tif from.Col+offset >= MaxColumns {\n\t\t\treturn false, ErrColumnNumber\n\t\t}\n\t\tfrom.Col += offset\n\t\tok = editAs == \"oneCell\"\n\t}\n\tif dir == rows && from.Row+1 >= num && from.Row+offset >= 0 {\n\t\tif from.Row+offset >= TotalRows {\n\t\t\treturn false, ErrMaxRows\n\t\t}\n\t\tfrom.Row += offset\n\t\tok = editAs == \"oneCell\"\n\t}\n\treturn ok, nil\n}\n\n// adjustDrawings updates the ending anchor of the two cell anchor pictures\n// and charts object when inserting or deleting rows or columns.\nfunc (to *xlsxTo) adjustDrawings(dir adjustDirection, num, offset int, ok bool) error {\n\tif dir == columns && to.Col+1 >= num && to.Col+offset >= 0 && ok {\n\t\tif to.Col+offset >= MaxColumns {\n\t\t\treturn ErrColumnNumber\n\t\t}\n\t\tto.Col += offset\n\t}\n\tif dir == rows && to.Row+1 >= num && to.Row+offset >= 0 && ok {\n\t\tif to.Row+offset >= TotalRows {\n\t\t\treturn ErrMaxRows\n\t\t}\n\t\tto.Row += offset\n\t}\n\treturn nil\n}\n\n// adjustDrawings updates the two cell anchor pictures and charts object when\n// inserting or deleting rows or columns.\nfunc (a *xdrCellAnchor) adjustDrawings(dir adjustDirection, num, offset int) error {\n\teditAs := a.EditAs\n\tif (a.From == nil && (a.To == nil || a.Ext == nil)) || editAs == \"absolute\" {\n\t\treturn nil\n\t}\n\tok, err := a.From.adjustDrawings(dir, num, offset, editAs)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif a.To != nil {\n\t\treturn a.To.adjustDrawings(dir, num, offset, ok || editAs == \"\")\n\t}\n\treturn err\n}\n\n// adjustDrawings updates the existing two cell anchor pictures and charts\n// object when inserting or deleting rows or columns.\nfunc (a *xlsxCellAnchorPos) adjustDrawings(dir adjustDirection, num, offset int, editAs string) error {\n\tif (a.From == nil && (a.To == nil || a.Ext == nil)) || editAs == \"absolute\" {\n\t\treturn nil\n\t}\n\tok, err := a.From.adjustDrawings(dir, num, offset, editAs)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif a.To != nil {\n\t\treturn a.To.adjustDrawings(dir, num, offset, ok || editAs == \"\")\n\t}\n\treturn err\n}\n\n// adjustDrawings updates the pictures and charts object when inserting or\n// deleting rows or columns.\nfunc (f *File) adjustDrawings(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) error {\n\tif ws.Drawing == nil {\n\t\treturn nil\n\t}\n\ttarget := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)\n\tdrawingXML := strings.TrimPrefix(strings.ReplaceAll(target, \"..\", \"xl\"), \"/\")\n\tvar (\n\t\terr  error\n\t\twsDr *xlsxWsDr\n\t)\n\tif wsDr, _, err = f.drawingParser(drawingXML); err != nil {\n\t\treturn err\n\t}\n\tanchorCb := func(a *xdrCellAnchor) error {\n\t\tif a.GraphicFrame == \"\" {\n\t\t\treturn a.adjustDrawings(dir, num, offset)\n\t\t}\n\t\tdeCellAnchor := decodeCellAnchor{}\n\t\tdeCellAnchorPos := decodeCellAnchorPos{}\n\t\t_ = f.xmlNewDecoder(strings.NewReader(\"<decodeCellAnchor>\" + a.GraphicFrame + \"</decodeCellAnchor>\")).Decode(&deCellAnchor)\n\t\t_ = f.xmlNewDecoder(strings.NewReader(\"<decodeCellAnchorPos>\" + a.GraphicFrame + \"</decodeCellAnchorPos>\")).Decode(&deCellAnchorPos)\n\t\txlsxCellAnchorPos := xlsxCellAnchorPos(deCellAnchorPos)\n\t\tfor i := 0; i < len(xlsxCellAnchorPos.AlternateContent); i++ {\n\t\t\txlsxCellAnchorPos.AlternateContent[i].XMLNSMC = SourceRelationshipCompatibility.Value\n\t\t}\n\t\tif deCellAnchor.From != nil {\n\t\t\txlsxCellAnchorPos.From = &xlsxFrom{\n\t\t\t\tCol: deCellAnchor.From.Col, ColOff: deCellAnchor.From.ColOff,\n\t\t\t\tRow: deCellAnchor.From.Row, RowOff: deCellAnchor.From.RowOff,\n\t\t\t}\n\t\t}\n\t\tif deCellAnchor.To != nil {\n\t\t\txlsxCellAnchorPos.To = &xlsxTo{\n\t\t\t\tCol: deCellAnchor.To.Col, ColOff: deCellAnchor.To.ColOff,\n\t\t\t\tRow: deCellAnchor.To.Row, RowOff: deCellAnchor.To.RowOff,\n\t\t\t}\n\t\t}\n\t\tif err = xlsxCellAnchorPos.adjustDrawings(dir, num, offset, a.EditAs); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcellAnchor, _ := xml.Marshal(xlsxCellAnchorPos)\n\t\ta.GraphicFrame = strings.TrimSuffix(strings.TrimPrefix(string(cellAnchor), \"<xlsxCellAnchorPos>\"), \"</xlsxCellAnchorPos>\")\n\t\treturn err\n\t}\n\tfor _, anchor := range wsDr.TwoCellAnchor {\n\t\tif err = anchorCb(anchor); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tfor _, anchor := range wsDr.OneCellAnchor {\n\t\tif err = anchorCb(anchor); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// adjustDefinedNames updates the cell reference of the defined names when\n// inserting or deleting rows or columns.\nfunc (f *File) adjustDefinedNames(sheet string, dir adjustDirection, num, offset int) error {\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif wb.DefinedNames != nil {\n\t\tfor i := 0; i < len(wb.DefinedNames.DefinedName); i++ {\n\t\t\tdata := wb.DefinedNames.DefinedName[i].Data\n\t\t\tif data, err = f.adjustFormulaRef(sheet, \"\", data, true, dir, num, offset); err == nil {\n\t\t\t\twb.DefinedNames.DefinedName[i].Data = data\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "adjust_test.go",
    "content": "package excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t_ \"image/jpeg\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestAdjustMergeCells(t *testing.T) {\n\tf := NewFile()\n\t// Test adjustAutoFilter with illegal cell reference\n\tassert.Equal(t, f.adjustMergeCells(&xlsxWorksheet{\n\t\tMergeCells: &xlsxMergeCells{\n\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t{\n\t\t\t\t\tRef: \"A:B1\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}, \"Sheet1\", rows, 0, 0, 1), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")))\n\tassert.Equal(t, f.adjustMergeCells(&xlsxWorksheet{\n\t\tMergeCells: &xlsxMergeCells{\n\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t{\n\t\t\t\t\tRef: \"A1:B\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}, \"Sheet1\", rows, 0, 0, 1), newCellNameToCoordinatesError(\"B\", newInvalidCellNameError(\"B\")))\n\tassert.NoError(t, f.adjustMergeCells(&xlsxWorksheet{\n\t\tMergeCells: &xlsxMergeCells{\n\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t{\n\t\t\t\t\tRef: \"A1:B1\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}, \"Sheet1\", rows, 1, -1, 1))\n\tassert.NoError(t, f.adjustMergeCells(&xlsxWorksheet{\n\t\tMergeCells: &xlsxMergeCells{\n\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t{\n\t\t\t\t\tRef: \"A1:A2\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}, \"Sheet1\", columns, 1, -1, 1))\n\tassert.NoError(t, f.adjustMergeCells(&xlsxWorksheet{\n\t\tMergeCells: &xlsxMergeCells{\n\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t{\n\t\t\t\t\tRef: \"A2\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}, \"Sheet1\", columns, 1, -1, 1))\n\n\t// Test adjust merge cells\n\tvar cases []struct {\n\t\tlabel      string\n\t\tws         *xlsxWorksheet\n\t\tdir        adjustDirection\n\t\tnum        int\n\t\toffset     int\n\t\texpect     string\n\t\texpectRect []int\n\t}\n\n\t// Test adjust merged cell when insert rows and columns\n\tcases = []struct {\n\t\tlabel      string\n\t\tws         *xlsxWorksheet\n\t\tdir        adjustDirection\n\t\tnum        int\n\t\toffset     int\n\t\texpect     string\n\t\texpectRect []int\n\t}{\n\t\t{\n\t\t\tlabel: \"insert row on ref\",\n\t\t\tws: &xlsxWorksheet{\n\t\t\t\tMergeCells: &xlsxMergeCells{\n\t\t\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRef:  \"A2:B3\",\n\t\t\t\t\t\t\trect: []int{1, 2, 2, 3},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdir:        rows,\n\t\t\tnum:        2,\n\t\t\toffset:     1,\n\t\t\texpect:     \"A3:B4\",\n\t\t\texpectRect: []int{1, 3, 2, 4},\n\t\t},\n\t\t{\n\t\t\tlabel: \"insert row on bottom of ref\",\n\t\t\tws: &xlsxWorksheet{\n\t\t\t\tMergeCells: &xlsxMergeCells{\n\t\t\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRef:  \"A2:B3\",\n\t\t\t\t\t\t\trect: []int{1, 2, 2, 3},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdir:        rows,\n\t\t\tnum:        3,\n\t\t\toffset:     1,\n\t\t\texpect:     \"A2:B4\",\n\t\t\texpectRect: []int{1, 2, 2, 4},\n\t\t},\n\t\t{\n\t\t\tlabel: \"insert column on the left\",\n\t\t\tws: &xlsxWorksheet{\n\t\t\t\tMergeCells: &xlsxMergeCells{\n\t\t\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRef:  \"A2:B3\",\n\t\t\t\t\t\t\trect: []int{1, 2, 2, 3},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdir:        columns,\n\t\t\tnum:        1,\n\t\t\toffset:     1,\n\t\t\texpect:     \"B2:C3\",\n\t\t\texpectRect: []int{2, 2, 3, 3},\n\t\t},\n\t}\n\tfor _, c := range cases {\n\t\tassert.NoError(t, f.adjustMergeCells(c.ws, \"Sheet1\", c.dir, c.num, 1, 1))\n\t\tassert.Equal(t, c.expect, c.ws.MergeCells.Cells[0].Ref, c.label)\n\t\tassert.Equal(t, c.expectRect, c.ws.MergeCells.Cells[0].rect, c.label)\n\t}\n\n\t// Test adjust merged cells when delete rows and columns\n\tcases = []struct {\n\t\tlabel      string\n\t\tws         *xlsxWorksheet\n\t\tdir        adjustDirection\n\t\tnum        int\n\t\toffset     int\n\t\texpect     string\n\t\texpectRect []int\n\t}{\n\t\t{\n\t\t\tlabel: \"delete row on top of ref\",\n\t\t\tws: &xlsxWorksheet{\n\t\t\t\tMergeCells: &xlsxMergeCells{\n\t\t\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRef:  \"A2:B3\",\n\t\t\t\t\t\t\trect: []int{1, 2, 2, 3},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdir:        rows,\n\t\t\tnum:        2,\n\t\t\toffset:     -1,\n\t\t\texpect:     \"A2:B2\",\n\t\t\texpectRect: []int{1, 2, 2, 2},\n\t\t},\n\t\t{\n\t\t\tlabel: \"delete row on bottom of ref\",\n\t\t\tws: &xlsxWorksheet{\n\t\t\t\tMergeCells: &xlsxMergeCells{\n\t\t\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRef:  \"A2:B3\",\n\t\t\t\t\t\t\trect: []int{1, 2, 2, 3},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdir:        rows,\n\t\t\tnum:        3,\n\t\t\toffset:     -1,\n\t\t\texpect:     \"A2:B2\",\n\t\t\texpectRect: []int{1, 2, 2, 2},\n\t\t},\n\t\t{\n\t\t\tlabel: \"delete column on the ref left\",\n\t\t\tws: &xlsxWorksheet{\n\t\t\t\tMergeCells: &xlsxMergeCells{\n\t\t\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRef:  \"A2:B3\",\n\t\t\t\t\t\t\trect: []int{1, 2, 2, 3},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdir:        columns,\n\t\t\tnum:        1,\n\t\t\toffset:     -1,\n\t\t\texpect:     \"A2:A3\",\n\t\t\texpectRect: []int{1, 2, 1, 3},\n\t\t},\n\t\t{\n\t\t\tlabel: \"delete column on the ref right\",\n\t\t\tws: &xlsxWorksheet{\n\t\t\t\tMergeCells: &xlsxMergeCells{\n\t\t\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRef:  \"A2:B3\",\n\t\t\t\t\t\t\trect: []int{1, 2, 2, 3},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdir:        columns,\n\t\t\tnum:        2,\n\t\t\toffset:     -1,\n\t\t\texpect:     \"A2:A3\",\n\t\t\texpectRect: []int{1, 2, 1, 3},\n\t\t},\n\t}\n\tfor _, c := range cases {\n\t\tassert.NoError(t, f.adjustMergeCells(c.ws, \"Sheet1\", c.dir, c.num, -1, 1))\n\t\tassert.Equal(t, c.expect, c.ws.MergeCells.Cells[0].Ref, c.label)\n\t}\n\n\t// Test delete one row or column\n\tcases = []struct {\n\t\tlabel      string\n\t\tws         *xlsxWorksheet\n\t\tdir        adjustDirection\n\t\tnum        int\n\t\toffset     int\n\t\texpect     string\n\t\texpectRect []int\n\t}{\n\t\t{\n\t\t\tlabel: \"delete one row ref\",\n\t\t\tws: &xlsxWorksheet{\n\t\t\t\tMergeCells: &xlsxMergeCells{\n\t\t\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRef:  \"A1:B1\",\n\t\t\t\t\t\t\trect: []int{1, 1, 2, 1},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdir:    rows,\n\t\t\tnum:    1,\n\t\t\toffset: -1,\n\t\t},\n\t\t{\n\t\t\tlabel: \"delete one column ref\",\n\t\t\tws: &xlsxWorksheet{\n\t\t\t\tMergeCells: &xlsxMergeCells{\n\t\t\t\t\tCells: []*xlsxMergeCell{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRef:  \"A1:A2\",\n\t\t\t\t\t\t\trect: []int{1, 1, 1, 2},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdir:    columns,\n\t\t\tnum:    1,\n\t\t\toffset: -1,\n\t\t},\n\t}\n\tfor _, c := range cases {\n\t\tassert.NoError(t, f.adjustMergeCells(c.ws, \"Sheet1\", c.dir, c.num, -1, 1))\n\t\tassert.Len(t, c.ws.MergeCells.Cells, 0, c.label)\n\t}\n\n\tf = NewFile()\n\tp1, p2 := f.adjustMergeCellsHelper(2, 1, 0, 0)\n\tassert.Equal(t, 1, p1)\n\tassert.Equal(t, 2, p2)\n\tf.deleteMergeCell(nil, -1)\n}\n\nfunc TestAdjustAutoFilter(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.adjustAutoFilter(&xlsxWorksheet{\n\t\tSheetData: xlsxSheetData{\n\t\t\tRow: []xlsxRow{{Hidden: true, R: 2}},\n\t\t},\n\t\tAutoFilter: &xlsxAutoFilter{\n\t\t\tRef: \"A1:A3\",\n\t\t},\n\t}, \"Sheet1\", rows, 1, -1, 1))\n\t// Test adjustAutoFilter with illegal cell reference\n\tassert.Equal(t, f.adjustAutoFilter(&xlsxWorksheet{\n\t\tAutoFilter: &xlsxAutoFilter{\n\t\t\tRef: \"A:B1\",\n\t\t},\n\t}, \"Sheet1\", rows, 0, 0, 1), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")))\n\tassert.Equal(t, f.adjustAutoFilter(&xlsxWorksheet{\n\t\tAutoFilter: &xlsxAutoFilter{\n\t\t\tRef: \"A1:B\",\n\t\t},\n\t}, \"Sheet1\", rows, 0, 0, 1), newCellNameToCoordinatesError(\"B\", newInvalidCellNameError(\"B\")))\n}\n\nfunc TestAdjustTable(t *testing.T) {\n\tf, sheetName := NewFile(), \"Sheet1\"\n\tfor idx, reference := range []string{\"B2:C3\", \"E3:F5\", \"H5:H8\", \"J5:K9\"} {\n\t\tassert.NoError(t, f.AddTable(sheetName, &Table{\n\t\t\tRange:             reference,\n\t\t\tName:              fmt.Sprintf(\"table%d\", idx),\n\t\t\tStyleName:         \"TableStyleMedium2\",\n\t\t\tShowFirstColumn:   true,\n\t\t\tShowLastColumn:    true,\n\t\t\tShowRowStripes:    boolPtr(false),\n\t\t\tShowColumnStripes: true,\n\t\t}))\n\t}\n\tassert.NoError(t, f.RemoveRow(sheetName, 2))\n\tassert.NoError(t, f.RemoveRow(sheetName, 3))\n\tassert.NoError(t, f.RemoveRow(sheetName, 3))\n\tassert.NoError(t, f.RemoveCol(sheetName, \"H\"))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAdjustTable.xlsx\")))\n\n\tf = NewFile()\n\tassert.NoError(t, f.AddTable(sheetName, &Table{Range: \"A1:D5\"}))\n\t// Test adjust table with non-table part\n\tf.Pkg.Delete(\"xl/tables/table1.xml\")\n\tassert.NoError(t, f.RemoveRow(sheetName, 1))\n\t// Test adjust table with unsupported charset\n\tf.Pkg.Store(\"xl/tables/table1.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.RemoveRow(sheetName, 1), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test adjust table with invalid table range reference\n\tf.Pkg.Store(\"xl/tables/table1.xml\", []byte(`<table ref=\"-\" />`))\n\tassert.Equal(t, ErrParameterInvalid, f.RemoveRow(sheetName, 1))\n}\n\nfunc TestAdjustHelper(t *testing.T) {\n\tf := NewFile()\n\t_, err := f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tf.Sheet.Store(\"xl/worksheets/sheet1.xml\", &xlsxWorksheet{\n\t\tMergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: \"A:B1\"}}},\n\t})\n\tf.Sheet.Store(\"xl/worksheets/sheet2.xml\", &xlsxWorksheet{\n\t\tAutoFilter: &xlsxAutoFilter{Ref: \"A1:B\"},\n\t})\n\t// Test adjustHelper with illegal cell reference\n\tassert.Equal(t, f.adjustHelper(\"Sheet1\", rows, 0, 0), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")))\n\tassert.Equal(t, f.adjustHelper(\"Sheet2\", rows, 0, 0), newCellNameToCoordinatesError(\"B\", newInvalidCellNameError(\"B\")))\n\t// Test adjustHelper on not exists worksheet\n\tassert.EqualError(t, f.adjustHelper(\"SheetN\", rows, 0, 0), \"sheet SheetN does not exist\")\n}\n\nfunc TestAdjustCalcChain(t *testing.T) {\n\tf := NewFile()\n\tf.CalcChain = &xlsxCalcChain{\n\t\tC: []xlsxCalcChainC{{R: \"B2\", I: 2}, {R: \"B2\", I: 1}, {R: \"A1\", I: 1}},\n\t}\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"A\", 1))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 1, 1))\n\n\tf.CalcChain = &xlsxCalcChain{\n\t\tC: []xlsxCalcChainC{{R: \"B2\", I: 1}, {R: \"B3\"}, {R: \"A1\"}},\n\t}\n\tassert.NoError(t, f.RemoveRow(\"Sheet1\", 3))\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"B\"))\n\n\tf.CalcChain = &xlsxCalcChain{C: []xlsxCalcChainC{{R: \"B2\", I: 2}, {R: \"B2\", I: 1}}}\n\tf.CalcChain.C[1].R = \"invalid coordinates\"\n\tassert.Equal(t, f.InsertCols(\"Sheet1\", \"A\", 1), newCellNameToCoordinatesError(\"invalid coordinates\", newInvalidCellNameError(\"invalid coordinates\")))\n\tf.CalcChain = nil\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"A\", 1))\n}\n\nfunc TestAdjustCols(t *testing.T) {\n\tsheetName := \"Sheet1\"\n\tpreset := func() (*File, error) {\n\t\tf := NewFile()\n\t\tif err := f.SetColWidth(sheetName, \"J\", \"T\", 5); err != nil {\n\t\t\treturn f, err\n\t\t}\n\t\tif err := f.SetSheetRow(sheetName, \"J1\", &[]string{\"J1\", \"K1\", \"L1\", \"M1\", \"N1\", \"O1\", \"P1\", \"Q1\", \"R1\", \"S1\", \"T1\"}); err != nil {\n\t\t\treturn f, err\n\t\t}\n\t\treturn f, nil\n\t}\n\tbaseTbl := []string{\"B\", \"J\", \"O\", \"O\", \"O\", \"U\", \"V\"}\n\tinsertTbl := []int{2, 2, 2, 5, 6, 2, 2}\n\texpectedTbl := []map[string]float64{\n\t\t{\"J\": defaultColWidth, \"K\": defaultColWidth, \"U\": 5, \"V\": 5, \"W\": defaultColWidth},\n\t\t{\"J\": defaultColWidth, \"K\": defaultColWidth, \"U\": 5, \"V\": 5, \"W\": defaultColWidth},\n\t\t{\"O\": 5, \"P\": 5, \"U\": 5, \"V\": 5, \"W\": defaultColWidth},\n\t\t{\"O\": 5, \"S\": 5, \"X\": 5, \"Y\": 5, \"Z\": defaultColWidth},\n\t\t{\"O\": 5, \"S\": 5, \"Y\": 5, \"X\": 5, \"AA\": defaultColWidth},\n\t\t{\"U\": 5, \"V\": 5, \"W\": defaultColWidth},\n\t\t{\"U\": defaultColWidth, \"V\": defaultColWidth, \"W\": defaultColWidth},\n\t}\n\tfor idx, columnName := range baseTbl {\n\t\tf, err := preset()\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.InsertCols(sheetName, columnName, insertTbl[idx]))\n\t\tfor column, expected := range expectedTbl[idx] {\n\t\t\twidth, err := f.GetColWidth(sheetName, column)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, expected, width, column)\n\t\t}\n\t\tassert.NoError(t, f.Close())\n\t}\n\n\tbaseTbl = []string{\"B\", \"J\", \"O\", \"T\"}\n\texpectedTbl = []map[string]float64{\n\t\t{\"H\": defaultColWidth, \"I\": 5, \"S\": 5, \"T\": defaultColWidth},\n\t\t{\"I\": defaultColWidth, \"J\": 5, \"S\": 5, \"T\": defaultColWidth},\n\t\t{\"I\": defaultColWidth, \"O\": 5, \"S\": 5, \"T\": defaultColWidth},\n\t\t{\"R\": 5, \"S\": 5, \"T\": defaultColWidth, \"U\": defaultColWidth},\n\t}\n\tfor idx, columnName := range baseTbl {\n\t\tf, err := preset()\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.RemoveCol(sheetName, columnName))\n\t\tfor column, expected := range expectedTbl[idx] {\n\t\t\twidth, err := f.GetColWidth(sheetName, column)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, expected, width, column)\n\t\t}\n\t\tassert.NoError(t, f.Close())\n\t}\n\n\tf, err := preset()\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetColWidth(sheetName, \"I\", \"I\", 8))\n\tfor i := 0; i <= 12; i++ {\n\t\tassert.NoError(t, f.RemoveCol(sheetName, \"I\"))\n\t}\n\tfor c := 9; c <= 21; c++ {\n\t\tcolumnName, err := ColumnNumberToName(c)\n\t\tassert.NoError(t, err)\n\t\twidth, err := f.GetColWidth(sheetName, columnName)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, defaultColWidth, width, columnName)\n\t}\n\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).Cols = nil\n\tassert.NoError(t, f.RemoveCol(sheetName, \"A\"))\n\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tassert.NoError(t, f.SetColWidth(\"Sheet1\", \"XFB\", \"XFC\", 12))\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"A\", 2))\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tassert.Equal(t, MaxColumns, ws.(*xlsxWorksheet).Cols.Col[0].Min)\n\tassert.Equal(t, MaxColumns, ws.(*xlsxWorksheet).Cols.Col[0].Max)\n\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"A\", 2))\n\tassert.Nil(t, ws.(*xlsxWorksheet).Cols)\n\n\tf = NewFile()\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A2\", \"(1-0.5)/2\"))\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"A\", 1))\n\tformula, err := f.GetCellFormula(\"Sheet1\", \"B2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"(1-0.5)/2\", formula)\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestAdjustColDimensions(t *testing.T) {\n\tf := NewFile()\n\tws, err := f.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C3\", \"A1+B1\"))\n\tassert.Equal(t, ErrColumnNumber, f.adjustColDimensions(\"Sheet1\", ws, 1, MaxColumns))\n\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tf.Sheet.Delete(\"xl/worksheets/sheet2.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet2.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.adjustColDimensions(\"Sheet2\", ws, 2, 1), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestAdjustRowDimensions(t *testing.T) {\n\tf := NewFile()\n\tws, err := f.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C3\", \"A1+B1\"))\n\tassert.Equal(t, ErrMaxRows, f.adjustRowDimensions(\"Sheet1\", ws, 1, TotalRows))\n\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tf.Sheet.Delete(\"xl/worksheets/sheet2.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet2.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.adjustRowDimensions(\"Sheet1\", ws, 2, 1), \"XML syntax error on line 1: invalid UTF-8\")\n\n\tf = NewFile()\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tws, err = f.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B2\", fmt.Sprintf(\"Sheet2!A%d\", TotalRows)))\n\tassert.Equal(t, ErrMaxRows, f.adjustRowDimensions(\"Sheet2\", ws, 1, TotalRows))\n}\n\nfunc TestAdjustHyperlinks(t *testing.T) {\n\tf := NewFile()\n\tws, err := f.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C3\", \"A1+B1\"))\n\tf.adjustHyperlinks(ws, \"Sheet1\", rows, 3, -1)\n\n\t// Test adjust hyperlinks location with positive offset\n\tassert.NoError(t, f.SetCellHyperLink(\"Sheet1\", \"F5\", \"Sheet1!A1\", \"Location\"))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 1, 1))\n\tlink, target, err := f.GetCellHyperLink(\"Sheet1\", \"F6\")\n\tassert.NoError(t, err)\n\tassert.True(t, link)\n\tassert.Equal(t, target, \"Sheet1!A1\")\n\n\t// Test adjust hyperlinks location with negative offset\n\tassert.NoError(t, f.RemoveRow(\"Sheet1\", 1))\n\tlink, target, err = f.GetCellHyperLink(\"Sheet1\", \"F5\")\n\tassert.NoError(t, err)\n\tassert.True(t, link)\n\tassert.Equal(t, target, \"Sheet1!A1\")\n\n\t// Test adjust hyperlinks location on remove row\n\tassert.NoError(t, f.RemoveRow(\"Sheet1\", 5))\n\tlink, target, err = f.GetCellHyperLink(\"Sheet1\", \"F5\")\n\tassert.NoError(t, err)\n\tassert.False(t, link)\n\tassert.Empty(t, target)\n\n\t// Test adjust hyperlinks location on remove column\n\tassert.NoError(t, f.SetCellHyperLink(\"Sheet1\", \"F5\", \"Sheet1!A1\", \"Location\"))\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"F\"))\n\tlink, target, err = f.GetCellHyperLink(\"Sheet1\", \"F5\")\n\tassert.NoError(t, err)\n\tassert.False(t, link)\n\tassert.Empty(t, target)\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAdjustHyperlinks.xlsx\")))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestAdjustFormula(t *testing.T) {\n\tf := NewFile()\n\tformulaType, ref := STCellFormulaTypeShared, \"C1:C5\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"A1+B1\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\tassert.NoError(t, f.DuplicateRowTo(\"Sheet1\", 1, 10))\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"B\", 1))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 1, 1))\n\tfor cell, expected := range map[string]string{\"D2\": \"A2+C2\", \"D3\": \"A3+C3\", \"D11\": \"A11+C11\"} {\n\t\tformula, err := f.GetCellFormula(\"Sheet1\", cell)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, formula)\n\t}\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAdjustFormula.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\tassert.NoError(t, f.adjustFormula(\"Sheet1\", \"Sheet1\", &xlsxC{}, rows, 0, 0, false))\n\tassert.Equal(t, newCellNameToCoordinatesError(\"-\", newInvalidCellNameError(\"-\")), f.adjustFormula(\"Sheet1\", \"Sheet1\", &xlsxC{F: &xlsxF{Ref: \"-\"}}, rows, 0, 0, false))\n\tassert.Equal(t, ErrColumnNumber, f.adjustFormula(\"Sheet1\", \"Sheet1\", &xlsxC{F: &xlsxF{Ref: \"XFD1:XFD1\"}}, columns, 0, 1, false))\n\n\t_, err := f.adjustFormulaRef(\"Sheet1\", \"Sheet1\", \"XFE1\", false, columns, 0, 1)\n\tassert.Equal(t, ErrColumnNumber, err)\n\t_, err = f.adjustFormulaRef(\"Sheet1\", \"Sheet1\", \"XFD1\", false, columns, 0, 1)\n\tassert.Equal(t, ErrColumnNumber, err)\n\n\tf = NewFile()\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"XFD1\"))\n\tassert.Equal(t, ErrColumnNumber, f.InsertCols(\"Sheet1\", \"A\", 1))\n\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B2\", fmt.Sprintf(\"A%d\", TotalRows)))\n\tassert.Equal(t, ErrMaxRows, f.InsertRows(\"Sheet1\", 1, 1))\n\n\tf = NewFile()\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B3\", \"SUM(1048576:1:2)\"))\n\tassert.Equal(t, ErrMaxRows, f.InsertRows(\"Sheet1\", 1, 1))\n\n\tf = NewFile()\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B3\", \"SUM(XFD:A:B)\"))\n\tassert.Equal(t, ErrColumnNumber, f.InsertCols(\"Sheet1\", \"A\", 1))\n\n\tf = NewFile()\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B3\", \"SUM(A:B:XFD)\"))\n\tassert.Equal(t, ErrColumnNumber, f.InsertCols(\"Sheet1\", \"A\", 1))\n\n\t// Test adjust formula with defined name in formula text\n\tf = NewFile()\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     \"Amount\",\n\t\tRefersTo: \"Sheet1!$B$2\",\n\t}))\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B2\", \"Amount+B3\"))\n\tassert.NoError(t, f.RemoveRow(\"Sheet1\", 1))\n\tformula, err := f.GetCellFormula(\"Sheet1\", \"B1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Amount+B2\", formula)\n\n\t// Test adjust formula with array formula\n\tf = NewFile()\n\tformulaType, reference := STCellFormulaTypeArray, \"A3:A3\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A3\", \"A1:A2\", FormulaOpts{Ref: &reference, Type: &formulaType}))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 1, 1))\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"A4\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A2:A3\", formula)\n\n\t// Test adjust formula on duplicate row with array formula\n\tf = NewFile()\n\tformulaType, reference = STCellFormulaTypeArray, \"A3\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A3\", \"A1:A2\", FormulaOpts{Ref: &reference, Type: &formulaType}))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 1, 1))\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"A4\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A2:A3\", formula)\n\n\t// Test adjust formula on duplicate row with relative and absolute cell references\n\tf = NewFile()\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B10\", \"A$10+$A11&\\\" \\\"\"))\n\tassert.NoError(t, f.DuplicateRowTo(\"Sheet1\", 10, 2))\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"B2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A$2+$A3&\\\" \\\"\", formula)\n\n\tt.Run(\"for_cells_affected_directly\", func(t *testing.T) {\n\t\t// Test insert row in middle of range with relative and absolute cell references\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"$A1+A$2\"))\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 2, 1))\n\t\tformula, err := f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"$A1+A$3\", formula)\n\t\tassert.NoError(t, f.RemoveRow(\"Sheet1\", 2))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"$A1+A$2\", formula)\n\n\t\t// Test insert column in middle of range\n\t\tf = NewFile()\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", \"B1+C1\"))\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"C\", 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"B1+D1\", formula)\n\t\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"C\"))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"B1+C1\", formula)\n\n\t\t// Test insert row and column in a rectangular range\n\t\tf = NewFile()\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", \"D4+D5+E4+E5\"))\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"E\", 1))\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 5, 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"D4+D6+F4+F6\", formula)\n\n\t\t// Test insert row in middle of range\n\t\tf = NewFile()\n\t\tformulaType, reference := STCellFormulaTypeArray, \"B1:B1\"\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"A1:A2\", FormulaOpts{Ref: &reference, Type: &formulaType}))\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 2, 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"A1:A3\", formula)\n\t\tassert.NoError(t, f.RemoveRow(\"Sheet1\", 2))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"A1:A2\", formula)\n\n\t\t// Test insert column in middle of range\n\t\tf = NewFile()\n\t\tformulaType, reference = STCellFormulaTypeArray, \"A1:A1\"\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", \"B1:C1\", FormulaOpts{Ref: &reference, Type: &formulaType}))\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"C\", 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"B1:D1\", formula)\n\t\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"C\"))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"B1:C1\", formula)\n\n\t\t// Test insert row and column in a rectangular range\n\t\tf = NewFile()\n\t\tformulaType, reference = STCellFormulaTypeArray, \"A1:A1\"\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", \"D4:E5\", FormulaOpts{Ref: &reference, Type: &formulaType}))\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"E\", 1))\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 5, 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"D4:F6\", formula)\n\t})\n\tt.Run(\"for_cells_affected_indirectly\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"A3+A4\"))\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 2, 1))\n\t\tformula, err := f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"A4+A5\", formula)\n\t\tassert.NoError(t, f.RemoveRow(\"Sheet1\", 2))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"A3+A4\", formula)\n\n\t\tf = NewFile()\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"D3+D4\"))\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"C\", 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"E3+E4\", formula)\n\t\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"C\"))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"D3+D4\", formula)\n\t})\n\tt.Run(\"for_entire_cols_rows_reference\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\t// Test adjust formula on insert row in the middle of the range\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"SUM(A2:A3:A4,,Table1[])\"))\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 3, 1))\n\t\tformula, err := f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"SUM(A2:A4:A5,,Table1[])\", formula)\n\n\t\t// Test adjust formula on insert at the top of the range\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 2, 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"SUM(A3:A5:A6,,Table1[])\", formula)\n\n\t\tf = NewFile()\n\t\t// Test adjust formula on insert row in the middle of the range\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"SUM('Sheet 1'!A2,A3)\"))\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 3, 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"SUM('Sheet 1'!A2,A4)\", formula)\n\n\t\t// Test adjust formula on insert row at the top of the range\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 2, 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"SUM('Sheet 1'!A2,A5)\", formula)\n\n\t\tf = NewFile()\n\t\t// Test adjust formula on insert col in the middle of the range\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"SUM(C3:D3)\"))\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"D\", 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"SUM(C3:E3)\", formula)\n\n\t\t// Test adjust formula on insert at the top of the range\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"C\", 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"SUM(D3:F3)\", formula)\n\n\t\tf = NewFile()\n\t\t// Test adjust formula on insert column in the middle of the range\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"SUM(C3,D3)\"))\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"D\", 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"SUM(C3,E3)\", formula)\n\n\t\t// Test adjust formula on insert column at the top of the range\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"C\", 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"SUM(D3,F3)\", formula)\n\n\t\tf = NewFile()\n\t\t// Test adjust formula on insert row in the middle of the range (range of whole row)\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"SUM(2:3)\"))\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 3, 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"SUM(2:4)\", formula)\n\n\t\t// Test adjust formula on insert row at the top of the range (range of whole row)\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 2, 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"SUM(3:5)\", formula)\n\n\t\tf = NewFile()\n\t\t// Test adjust formula on insert row in the middle of the range (range of whole column)\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"SUM(C:D)\"))\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"D\", 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"SUM(C:E)\", formula)\n\n\t\t// Test adjust formula on insert row at the top of the range (range of whole column)\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"C\", 1))\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"SUM(D:F)\", formula)\n\t})\n\tt.Run(\"for_all_worksheet_cells_with_rows_insert\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\t_, err := f.NewSheet(\"Sheet2\")\n\t\tassert.NoError(t, err)\n\t\t// Tests formulas referencing Sheet2 should update but those referencing the original sheet should not\n\t\ttbl := [][]string{\n\t\t\t{\"B1\", \"Sheet2!A1+Sheet2!A2\", \"Sheet2!A1+Sheet2!A3\", \"Sheet2!A2+Sheet2!A4\"},\n\t\t\t{\"C1\", \"A1+A2\", \"A1+A2\", \"A1+A2\"},\n\t\t\t{\"D1\", \"Sheet2!B1:B2\", \"Sheet2!B1:B3\", \"Sheet2!B2:B4\"},\n\t\t\t{\"E1\", \"B1:B2\", \"B1:B2\", \"B1:B2\"},\n\t\t\t{\"F1\", \"SUM(Sheet2!C1:C2)\", \"SUM(Sheet2!C1:C3)\", \"SUM(Sheet2!C2:C4)\"},\n\t\t\t{\"G1\", \"SUM(C1:C2)\", \"SUM(C1:C2)\", \"SUM(C1:C2)\"},\n\t\t\t{\"H1\", \"SUM(Sheet2!D1,Sheet2!D2)\", \"SUM(Sheet2!D1,Sheet2!D3)\", \"SUM(Sheet2!D2,Sheet2!D4)\"},\n\t\t\t{\"I1\", \"SUM(D1,D2)\", \"SUM(D1,D2)\", \"SUM(D1,D2)\"},\n\t\t}\n\t\tfor _, preset := range tbl {\n\t\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", preset[0], preset[1]))\n\t\t}\n\t\t// Test adjust formula on insert row in the middle of the range\n\t\tassert.NoError(t, f.InsertRows(\"Sheet2\", 2, 1))\n\t\tfor _, preset := range tbl {\n\t\t\tformula, err := f.GetCellFormula(\"Sheet1\", preset[0])\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, preset[2], formula)\n\t\t}\n\n\t\t// Test adjust formula on insert row in the top of the range\n\t\tassert.NoError(t, f.InsertRows(\"Sheet2\", 1, 1))\n\t\tfor _, preset := range tbl {\n\t\t\tformula, err := f.GetCellFormula(\"Sheet1\", preset[0])\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, preset[3], formula)\n\t\t}\n\t})\n\tt.Run(\"for_all_worksheet_cells_with_cols_insert\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\t_, err := f.NewSheet(\"Sheet2\")\n\t\tassert.NoError(t, err)\n\t\ttbl := [][]string{\n\t\t\t{\"A1\", \"Sheet2!A1+Sheet2!B1\", \"Sheet2!A1+Sheet2!C1\", \"Sheet2!B1+Sheet2!D1\"},\n\t\t\t{\"A2\", \"A1+B1\", \"A1+B1\", \"A1+B1\"},\n\t\t\t{\"A3\", \"Sheet2!A2:B2\", \"Sheet2!A2:C2\", \"Sheet2!B2:D2\"},\n\t\t\t{\"A4\", \"A2:B2\", \"A2:B2\", \"A2:B2\"},\n\t\t\t{\"A5\", \"SUM(Sheet2!A3:B3)\", \"SUM(Sheet2!A3:C3)\", \"SUM(Sheet2!B3:D3)\"},\n\t\t\t{\"A6\", \"SUM(A3:B3)\", \"SUM(A3:B3)\", \"SUM(A3:B3)\"},\n\t\t\t{\"A7\", \"SUM(Sheet2!A4,Sheet2!B4)\", \"SUM(Sheet2!A4,Sheet2!C4)\", \"SUM(Sheet2!B4,Sheet2!D4)\"},\n\t\t\t{\"A8\", \"SUM(A4,B4)\", \"SUM(A4,B4)\", \"SUM(A4,B4)\"},\n\t\t}\n\t\tfor _, preset := range tbl {\n\t\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", preset[0], preset[1]))\n\t\t}\n\t\t// Test adjust formula on insert column in the middle of the range\n\t\tassert.NoError(t, f.InsertCols(\"Sheet2\", \"B\", 1))\n\t\tfor _, preset := range tbl {\n\t\t\tformula, err := f.GetCellFormula(\"Sheet1\", preset[0])\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, preset[2], formula)\n\t\t}\n\t\t// Test adjust formula on insert column in the top of the range\n\t\tassert.NoError(t, f.InsertCols(\"Sheet2\", \"A\", 1))\n\t\tfor _, preset := range tbl {\n\t\t\tformula, err := f.GetCellFormula(\"Sheet1\", preset[0])\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, preset[3], formula)\n\t\t}\n\t})\n\tt.Run(\"for_cross_sheet_ref_with_rows_insert)\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\t_, err := f.NewSheet(\"Sheet2\")\n\t\tassert.NoError(t, err)\n\t\t_, err = f.NewSheet(\"Sheet3\")\n\t\tassert.NoError(t, err)\n\t\t// Tests formulas referencing Sheet2 should update but those referencing\n\t\t// the original sheet or Sheet 3 should not update\n\t\ttbl := [][]string{\n\t\t\t{\"B1\", \"Sheet2!A1+Sheet2!A2+Sheet1!A3+Sheet1!A4\", \"Sheet2!A1+Sheet2!A3+Sheet1!A3+Sheet1!A4\", \"Sheet2!A2+Sheet2!A4+Sheet1!A3+Sheet1!A4\"},\n\t\t\t{\"C1\", \"Sheet2!B1+Sheet2!B2+B3+B4\", \"Sheet2!B1+Sheet2!B3+B3+B4\", \"Sheet2!B2+Sheet2!B4+B3+B4\"},\n\t\t\t{\"D1\", \"Sheet2!C1+Sheet2!C2+Sheet3!A3+Sheet3!A4\", \"Sheet2!C1+Sheet2!C3+Sheet3!A3+Sheet3!A4\", \"Sheet2!C2+Sheet2!C4+Sheet3!A3+Sheet3!A4\"},\n\t\t\t{\"E1\", \"SUM(Sheet2!D1:D2,Sheet1!A3:A4)\", \"SUM(Sheet2!D1:D3,Sheet1!A3:A4)\", \"SUM(Sheet2!D2:D4,Sheet1!A3:A4)\"},\n\t\t\t{\"F1\", \"SUM(Sheet2!E1:E2,A3:A4)\", \"SUM(Sheet2!E1:E3,A3:A4)\", \"SUM(Sheet2!E2:E4,A3:A4)\"},\n\t\t\t{\"G1\", \"SUM(Sheet2!F1:F2,Sheet3!A3:A4)\", \"SUM(Sheet2!F1:F3,Sheet3!A3:A4)\", \"SUM(Sheet2!F2:F4,Sheet3!A3:A4)\"},\n\t\t}\n\t\tfor _, preset := range tbl {\n\t\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", preset[0], preset[1]))\n\t\t}\n\t\t// Test adjust formula on insert row in the middle of the range\n\t\tassert.NoError(t, f.InsertRows(\"Sheet2\", 2, 1))\n\t\tfor _, preset := range tbl {\n\t\t\tformula, err := f.GetCellFormula(\"Sheet1\", preset[0])\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, preset[2], formula)\n\t\t}\n\t\t// Test adjust formula on insert row in the top of the range\n\t\tassert.NoError(t, f.InsertRows(\"Sheet2\", 1, 1))\n\t\tfor _, preset := range tbl {\n\t\t\tformula, err := f.GetCellFormula(\"Sheet1\", preset[0])\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, preset[3], formula)\n\t\t}\n\t})\n\tt.Run(\"for_cross_sheet_ref_with_cols_insert)\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\t_, err := f.NewSheet(\"Sheet2\")\n\t\tassert.NoError(t, err)\n\t\t_, err = f.NewSheet(\"Sheet3\")\n\t\tassert.NoError(t, err)\n\t\t// Tests formulas referencing Sheet2 should update but those referencing\n\t\t// the original sheet or Sheet 3 should not update\n\t\ttbl := [][]string{\n\t\t\t{\"A1\", \"Sheet2!A1+Sheet2!B1+Sheet1!C1+Sheet1!D1\", \"Sheet2!A1+Sheet2!C1+Sheet1!C1+Sheet1!D1\", \"Sheet2!B1+Sheet2!D1+Sheet1!C1+Sheet1!D1\"},\n\t\t\t{\"A2\", \"Sheet2!A2+Sheet2!B2+C2+D2\", \"Sheet2!A2+Sheet2!C2+C2+D2\", \"Sheet2!B2+Sheet2!D2+C2+D2\"},\n\t\t\t{\"A3\", \"Sheet2!A3+Sheet2!B3+Sheet3!C3+Sheet3!D3\", \"Sheet2!A3+Sheet2!C3+Sheet3!C3+Sheet3!D3\", \"Sheet2!B3+Sheet2!D3+Sheet3!C3+Sheet3!D3\"},\n\t\t\t{\"A4\", \"SUM(Sheet2!A4:B4,Sheet1!C4:D4)\", \"SUM(Sheet2!A4:C4,Sheet1!C4:D4)\", \"SUM(Sheet2!B4:D4,Sheet1!C4:D4)\"},\n\t\t\t{\"A5\", \"SUM(Sheet2!A5:B5,C5:D5)\", \"SUM(Sheet2!A5:C5,C5:D5)\", \"SUM(Sheet2!B5:D5,C5:D5)\"},\n\t\t\t{\"A6\", \"SUM(Sheet2!A6:B6,Sheet3!C6:D6)\", \"SUM(Sheet2!A6:C6,Sheet3!C6:D6)\", \"SUM(Sheet2!B6:D6,Sheet3!C6:D6)\"},\n\t\t}\n\t\tfor _, preset := range tbl {\n\t\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", preset[0], preset[1]))\n\t\t}\n\t\t// Test adjust formula on insert row in the middle of the range\n\t\tassert.NoError(t, f.InsertCols(\"Sheet2\", \"B\", 1))\n\t\tfor _, preset := range tbl {\n\t\t\tformula, err := f.GetCellFormula(\"Sheet1\", preset[0])\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, preset[2], formula)\n\t\t}\n\t\t// Test adjust formula on insert row in the top of the range\n\t\tassert.NoError(t, f.InsertCols(\"Sheet2\", \"A\", 1))\n\t\tfor _, preset := range tbl {\n\t\t\tformula, err := f.GetCellFormula(\"Sheet1\", preset[0])\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, preset[3], formula)\n\t\t}\n\t})\n\tt.Run(\"for_cross_sheet_ref_with_chart_sheet)\", func(t *testing.T) {\n\t\tassert.NoError(t, f.AddChartSheet(\"Chart1\", &Chart{Type: Line}))\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 2, 1))\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"A\", 1))\n\t})\n\tt.Run(\"for_array_formula_cell\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A1\", &[]int{1, 2}))\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A2\", &[]int{3, 4}))\n\t\tformulaType, ref := STCellFormulaTypeArray, \"C1:C2\"\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"A1:A2*B1:B2\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\t\tassert.NoError(t, f.InsertRows(\"Sheet1\", 1, 1))\n\t\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"A\", 1))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"D2\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"2\", result)\n\t\tresult, err = f.CalcCellValue(\"Sheet1\", \"D3\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"12\", result)\n\n\t\t// Test adjust array formula with invalid range reference\n\t\tformulaType, ref = STCellFormulaTypeArray, \"E1:E2\"\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"E1\", \"XFD1:XFD1\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\t\tassert.Equal(t, ErrColumnNumber, f.InsertCols(\"Sheet1\", \"A\", 1))\n\t})\n}\n\nfunc TestAdjustVolatileDeps(t *testing.T) {\n\tf := NewFile()\n\tf.Pkg.Store(defaultXMLPathVolatileDeps, []byte(fmt.Sprintf(`<volTypes xmlns=\"%s\"><volType><main><tp><tr r=\"C2\" s=\"2\"/><tr r=\"C2\" s=\"1\"/><tr r=\"D3\" s=\"1\"/></tp></main></volType></volTypes>`, NameSpaceSpreadSheet.Value)))\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"A\", 1))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 2, 1))\n\tassert.Equal(t, \"D3\", f.VolatileDeps.VolType[0].Main[0].Tp[0].Tr[1].R)\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"D\"))\n\tassert.NoError(t, f.RemoveRow(\"Sheet1\", 4))\n\tassert.Len(t, f.VolatileDeps.VolType[0].Main[0].Tp[0].Tr, 1)\n\n\tf = NewFile()\n\tf.Pkg.Store(defaultXMLPathVolatileDeps, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.InsertRows(\"Sheet1\", 2, 1), \"XML syntax error on line 1: invalid UTF-8\")\n\n\tf = NewFile()\n\tf.Pkg.Store(defaultXMLPathVolatileDeps, []byte(fmt.Sprintf(`<volTypes xmlns=\"%s\"><volType><main><tp><tr r=\"A\" s=\"1\"/></tp></main></volType></volTypes>`, NameSpaceSpreadSheet.Value)))\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.InsertCols(\"Sheet1\", \"A\", 1))\n\tf.volatileDepsWriter()\n}\n\nfunc TestAdjustConditionalFormats(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"B1\", &[]interface{}{1, nil, 1, 1}))\n\tformatID, err := f.NewConditionalStyle(&Style{Font: &Font{Color: \"09600B\"}, Fill: Fill{Type: \"pattern\", Color: []string{\"C7EECF\"}, Pattern: 1}})\n\tassert.NoError(t, err)\n\tformat := []ConditionalFormatOptions{\n\t\t{\n\t\t\tType:     \"cell\",\n\t\t\tCriteria: \"greater than\",\n\t\t\tFormat:   &formatID,\n\t\t\tValue:    \"0\",\n\t\t},\n\t}\n\tfor _, ref := range []string{\"B1\", \"D1:E1\"} {\n\t\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", ref, format))\n\t}\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"B\"))\n\topts, err := f.GetConditionalFormats(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, format, 1)\n\tassert.Equal(t, format, opts[\"C1:D1\"])\n\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).ConditionalFormatting[0].SQRef = \"-\"\n\tassert.Equal(t, newCellNameToCoordinatesError(\"-\", newInvalidCellNameError(\"-\")), f.RemoveCol(\"Sheet1\", \"B\"))\n\n\tws.(*xlsxWorksheet).ConditionalFormatting[0] = nil\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"B\"))\n\n\tt.Run(\"for_remove_conditional_formats_column\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tformat := []ConditionalFormatOptions{{\n\t\t\tType:     \"data_bar\",\n\t\t\tCriteria: \"=\",\n\t\t\tMinType:  \"min\",\n\t\t\tMaxType:  \"max\",\n\t\t\tBarColor: \"#638EC6\",\n\t\t}}\n\t\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"D2:D3\", format))\n\t\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"D5\", format))\n\t\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"D\"))\n\t\topts, err := f.GetConditionalFormats(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, opts, 0)\n\t})\n\tt.Run(\"for_remove_conditional_formats_row\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tformat := []ConditionalFormatOptions{{\n\t\t\tType:     \"data_bar\",\n\t\t\tCriteria: \"=\",\n\t\t\tMinType:  \"min\",\n\t\t\tMaxType:  \"max\",\n\t\t\tBarColor: \"#638EC6\",\n\t\t}}\n\t\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"D2:E2\", format))\n\t\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"F2\", format))\n\t\tassert.NoError(t, f.RemoveRow(\"Sheet1\", 2))\n\t\topts, err := f.GetConditionalFormats(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, opts, 0)\n\t})\n\tt.Run(\"for_adjust_conditional_formats_row\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tformat := []ConditionalFormatOptions{{\n\t\t\tType:     \"data_bar\",\n\t\t\tCriteria: \"=\",\n\t\t\tMinType:  \"min\",\n\t\t\tMaxType:  \"max\",\n\t\t\tBarColor: \"#638EC6\",\n\t\t}}\n\t\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"D2:D3\", format))\n\t\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"D5\", format))\n\t\tassert.NoError(t, f.RemoveRow(\"Sheet1\", 1))\n\t\topts, err := f.GetConditionalFormats(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, opts, 2)\n\t\tassert.Equal(t, format, opts[\"D1:D2\"])\n\t\tassert.Equal(t, format, opts[\"D4:D4\"])\n\t})\n}\n\nfunc TestAdjustDataValidations(t *testing.T) {\n\tf := NewFile()\n\tdv := NewDataValidation(true)\n\tdv.Sqref = \"B1\"\n\tassert.NoError(t, dv.SetDropList([]string{\"1\", \"2\", \"3\"}))\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"B\"))\n\tdvs, err := f.GetDataValidations(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, dvs, 0)\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"F2\", 1))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"F3\", 2))\n\tdv = NewDataValidation(true)\n\tdv.Sqref = \"C2:D3\"\n\tdv.SetSqrefDropList(\"$F$2:$F$3\")\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\n\tassert.NoError(t, f.AddChartSheet(\"Chart1\", &Chart{Type: Line}))\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetSheetRow(\"Sheet2\", \"C1\", &[]interface{}{1, 10}))\n\tdv = NewDataValidation(true)\n\tdv.Sqref = \"C5:D6\"\n\tassert.NoError(t, dv.SetRange(\"Sheet2!C1\", \"Sheet2!D1\", DataValidationTypeWhole, DataValidationOperatorBetween))\n\tdv.SetError(DataValidationErrorStyleStop, \"error title\", \"error body\")\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"B\"))\n\tassert.NoError(t, f.RemoveCol(\"Sheet2\", \"B\"))\n\tdvs, err = f.GetDataValidations(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"B2:C3\", dvs[0].Sqref)\n\tassert.Equal(t, \"$E$2:$E$3\", dvs[0].Formula1)\n\tassert.Equal(t, \"B5:C6\", dvs[1].Sqref)\n\tassert.Equal(t, \"Sheet2!B1\", dvs[1].Formula1)\n\tassert.Equal(t, \"Sheet2!C1\", dvs[1].Formula2)\n\n\tdv = NewDataValidation(true)\n\tdv.Sqref = \"C8:D10\"\n\tassert.NoError(t, dv.SetDropList([]string{`A<`, `B>`, `C\"`, \"D\\t\", `E'`, `F`}))\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"B\"))\n\tdvs, err = f.GetDataValidations(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"\\\"A<,B>,C\\\",D\\t,E',F\\\"\", dvs[2].Formula1)\n\n\t// Test adjust data validation with multiple cell range\n\tdv = NewDataValidation(true)\n\tdv.Sqref = \"G1:G3 H1:H3 A3:A1048576\"\n\tassert.NoError(t, dv.SetDropList([]string{\"1\", \"2\", \"3\"}))\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 2, 1))\n\tdvs, err = f.GetDataValidations(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"G1:G4 H1:H4 A4:A1048576\", dvs[3].Sqref)\n\n\tdv = NewDataValidation(true)\n\tdv.Sqref = \"C5:D6\"\n\tassert.NoError(t, dv.SetRange(\"Sheet1!A1048576\", \"Sheet1!XFD1\", DataValidationTypeWhole, DataValidationOperatorBetween))\n\tdv.SetError(DataValidationErrorStyleStop, \"error title\", \"error body\")\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\tassert.Equal(t, ErrColumnNumber, f.InsertCols(\"Sheet1\", \"A\", 1))\n\tassert.Equal(t, ErrMaxRows, f.InsertRows(\"Sheet1\", 1, 1))\n\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).DataValidations.DataValidation[0].Sqref = \"-\"\n\tassert.Equal(t, newCellNameToCoordinatesError(\"-\", newInvalidCellNameError(\"-\")), f.RemoveCol(\"Sheet1\", \"B\"))\n\n\tws.(*xlsxWorksheet).DataValidations.DataValidation[0] = nil\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"B\"))\n\n\tws.(*xlsxWorksheet).DataValidations = nil\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"B\"))\n\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.adjustDataValidations(nil, \"Sheet1\", columns, 0, 0, 1), \"XML syntax error on line 1: invalid UTF-8\")\n\n\tt.Run(\"for_escaped_data_validation_rules_formula\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\t_, err := f.NewSheet(\"Sheet2\")\n\t\tassert.NoError(t, err)\n\t\tdv := NewDataValidation(true)\n\t\tdv.Sqref = \"A1\"\n\t\tassert.NoError(t, dv.SetDropList([]string{\"option1\", strings.Repeat(\"\\\"\", 4)}))\n\t\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\t\tassert.True(t, ok)\n\t\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\t\t// The double quote symbol in none formula data validation rules will be escaped in the Kingsoft WPS Office\n\t\tformula := strings.ReplaceAll(fmt.Sprintf(\"\\\"option1, %s\", strings.Repeat(\"\\\"\", 9)), \"\\\"\", \"&quot;\")\n\t\tws.(*xlsxWorksheet).DataValidations.DataValidation[0].Formula1.Content = formula\n\t\tassert.NoError(t, f.RemoveCol(\"Sheet2\", \"A\"))\n\t\tdvs, err := f.GetDataValidations(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, formula, dvs[0].Formula1)\n\t})\n\n\tt.Run(\"no_data_validations_on_first_sheet\", func(t *testing.T) {\n\t\tf := NewFile()\n\n\t\t// Add Sheet2 and set a data validation\n\t\t_, err = f.NewSheet(\"Sheet2\")\n\t\tassert.NoError(t, err)\n\t\tdv := NewDataValidation(true)\n\t\tdv.Sqref = \"C5:D6\"\n\t\tassert.NoError(t, f.AddDataValidation(\"Sheet2\", dv))\n\n\t\t// Adjust Sheet2 by removing a column\n\t\tassert.NoError(t, f.RemoveCol(\"Sheet2\", \"A\"))\n\n\t\t// Verify that data validations on Sheet2 are adjusted correctly\n\t\tdvs, err = f.GetDataValidations(\"Sheet2\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"B5:C6\", dvs[0].Sqref) // Adjusted range\n\t})\n}\n\nfunc TestAdjustDrawings(t *testing.T) {\n\tf := NewFile()\n\t// Test add pictures to sheet with positioning\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"B2\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), nil))\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"B11\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), &GraphicOptions{Positioning: \"oneCell\"}))\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"B21\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), &GraphicOptions{Positioning: \"absolute\"}))\n\n\t// Test adjust pictures on inserting columns and rows\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"A\", 1))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 1, 1))\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"C\", 1))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 5, 1))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 15, 1))\n\tcells, err := f.GetPictureCells(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"D3\", \"B21\", \"D13\"}, cells)\n\twb := filepath.Join(\"test\", \"TestAdjustDrawings.xlsx\")\n\tassert.NoError(t, f.SaveAs(wb))\n\n\t// Test adjust pictures on deleting columns and rows\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"A\"))\n\tassert.NoError(t, f.RemoveRow(\"Sheet1\", 1))\n\tcells, err = f.GetPictureCells(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"C2\", \"B21\", \"C12\"}, cells)\n\n\t// Test adjust existing pictures on inserting columns and rows\n\tf, err = OpenFile(wb)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"A\", 1))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 1, 1))\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"D\", 1))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 5, 1))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 16, 1))\n\tcells, err = f.GetPictureCells(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"F4\", \"B21\", \"F15\"}, cells)\n\n\t// Test adjust drawings with unsupported charset\n\tf, err = OpenFile(wb)\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/drawings/drawing1.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.InsertCols(\"Sheet1\", \"A\", 1), \"XML syntax error on line 1: invalid UTF-8\")\n\n\terrors := []error{ErrColumnNumber, ErrColumnNumber, ErrMaxRows, ErrMaxRows}\n\tcells = []string{\"XFD1\", \"XFB1\"}\n\tfor i, cell := range cells {\n\t\tf = NewFile()\n\t\tassert.NoError(t, f.AddPicture(\"Sheet1\", cell, filepath.Join(\"test\", \"images\", \"excel.jpg\"), nil))\n\t\tassert.Equal(t, errors[i], f.InsertCols(\"Sheet1\", \"A\", 1))\n\t\tassert.NoError(t, f.SaveAs(wb))\n\t\tf, err = OpenFile(wb)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, errors[i], f.InsertCols(\"Sheet1\", \"A\", 1))\n\t}\n\terrors = []error{ErrMaxRows, ErrMaxRows}\n\tcells = []string{\"A1048576\", \"A1048570\"}\n\tfor i, cell := range cells {\n\t\tf = NewFile()\n\t\tassert.NoError(t, f.AddPicture(\"Sheet1\", cell, filepath.Join(\"test\", \"images\", \"excel.jpg\"), nil))\n\t\tassert.Equal(t, errors[i], f.InsertRows(\"Sheet1\", 1, 1))\n\t\tassert.NoError(t, f.SaveAs(wb))\n\t\tf, err = OpenFile(wb)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, errors[i], f.InsertRows(\"Sheet1\", 1, 1))\n\t}\n\n\ta := xdrCellAnchor{}\n\tassert.NoError(t, a.adjustDrawings(columns, 0, 0))\n\tp := xlsxCellAnchorPos{}\n\tassert.NoError(t, p.adjustDrawings(columns, 0, 0, \"\"))\n\n\tf, err = OpenFile(wb)\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/drawings/drawing1.xml\", []byte(xml.Header+`<wsDr xmlns=\"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing\"><twoCellAnchor><from><col>0</col><colOff>0</colOff><row>0</row><rowOff>0</rowOff></from><to><col>1</col><colOff>0</colOff><row>1</row><rowOff>0</rowOff></to><mc:AlternateContent xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"></mc:AlternateContent><clientData/></twoCellAnchor></wsDr>`))\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"A\", 1))\n\n\tf, err = OpenFile(wb)\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/drawings/drawing1.xml\", []byte(xml.Header+fmt.Sprintf(`<wsDr xmlns=\"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing\"><oneCellAnchor><from><col>%d</col><row>0</row></from><mc:AlternateContent xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"></mc:AlternateContent><clientData/></oneCellAnchor></wsDr>`, MaxColumns)))\n\tassert.Equal(t, ErrColumnNumber, f.InsertCols(\"Sheet1\", \"A\", 1))\n}\n\nfunc TestAdjustDefinedNames(t *testing.T) {\n\tf := NewFile()\n\t_, err := f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tfor _, dn := range []*DefinedName{\n\t\t{Name: \"Name1\", RefersTo: \"Sheet1!$XFD$1\"},\n\t\t{Name: \"Name2\", RefersTo: \"Sheet2!$C$1\", Scope: \"Sheet1\"},\n\t\t{Name: \"Name3\", RefersTo: \"Sheet2!$C$1:$D$2\", Scope: \"Sheet1\"},\n\t\t{Name: \"Name4\", RefersTo: \"Sheet2!$C1:D$2\"},\n\t\t{Name: \"Name5\", RefersTo: \"Sheet2!C$1:$D2\"},\n\t\t{Name: \"Name6\", RefersTo: \"Sheet2!C:$D\"},\n\t\t{Name: \"Name7\", RefersTo: \"Sheet2!$C:D\"},\n\t\t{Name: \"Name8\", RefersTo: \"Sheet2!C:D\"},\n\t\t{Name: \"Name9\", RefersTo: \"Sheet2!$C:$D\"},\n\t\t{Name: \"Name10\", RefersTo: \"Sheet2!1:2\"},\n\t} {\n\t\tassert.NoError(t, f.SetDefinedName(dn))\n\t}\n\tassert.NoError(t, f.InsertCols(\"Sheet1\", \"A\", 1))\n\tassert.NoError(t, f.InsertRows(\"Sheet1\", 1, 1))\n\tassert.NoError(t, f.InsertCols(\"Sheet2\", \"A\", 1))\n\tassert.NoError(t, f.InsertRows(\"Sheet2\", 1, 1))\n\tdefinedNames := f.GetDefinedName()\n\tfor i, expected := range []string{\n\t\t\"Sheet1!$XFD$2\",\n\t\t\"Sheet2!$D$2\",\n\t\t\"Sheet2!$D$2:$E$3\",\n\t\t\"Sheet2!$D1:D$3\",\n\t\t\"Sheet2!C$2:$E2\",\n\t\t\"Sheet2!C:$E\",\n\t\t\"Sheet2!$D:D\",\n\t\t\"Sheet2!C:D\",\n\t\t\"Sheet2!$D:$E\",\n\t\t\"Sheet2!1:2\",\n\t} {\n\t\tassert.Equal(t, expected, definedNames[i].RefersTo)\n\t}\n\n\tf = NewFile()\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     \"Name1\",\n\t\tRefersTo: \"Sheet1!$A$1\",\n\t\tScope:    \"Sheet1\",\n\t}))\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"A\"))\n\tdefinedNames = f.GetDefinedName()\n\tassert.Equal(t, \"Sheet1!$A$1\", definedNames[0].RefersTo)\n\n\tf = NewFile()\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     \"Name1\",\n\t\tRefersTo: \"'1.A & B C'!#REF!\",\n\t\tScope:    \"Sheet1\",\n\t}))\n\tassert.NoError(t, f.RemoveCol(\"Sheet1\", \"A\"))\n\tdefinedNames = f.GetDefinedName()\n\tassert.Equal(t, \"'1.A & B C'!#REF!\", definedNames[0].RefersTo)\n\n\tf = NewFile()\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.adjustDefinedNames(\"Sheet1\", columns, 0, 0), \"XML syntax error on line 1: invalid UTF-8\")\n}\n"
  },
  {
    "path": "calc.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"container/list\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"math/big\"\n\t\"math/cmplx\"\n\t\"math/rand\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\t\"unsafe\"\n\n\t\"github.com/xuri/efp\"\n\t\"golang.org/x/text/language\"\n\t\"golang.org/x/text/message\"\n)\n\nconst (\n\t// Excel formula errors\n\tformulaErrorDIV         = \"#DIV/0!\"\n\tformulaErrorNAME        = \"#NAME?\"\n\tformulaErrorNA          = \"#N/A\"\n\tformulaErrorNUM         = \"#NUM!\"\n\tformulaErrorVALUE       = \"#VALUE!\"\n\tformulaErrorREF         = \"#REF!\"\n\tformulaErrorNULL        = \"#NULL!\"\n\tformulaErrorSPILL       = \"#SPILL!\"\n\tformulaErrorCALC        = \"#CALC!\"\n\tformulaErrorGETTINGDATA = \"#GETTING_DATA\"\n\t// Formula criteria condition enumeration\n\t_ byte = iota\n\tcriteriaEq\n\tcriteriaLe\n\tcriteriaGe\n\tcriteriaNe\n\tcriteriaL\n\tcriteriaG\n\tcriteriaErr\n\tcriteriaRegexp\n\n\tcategoryWeightAndMass\n\tcategoryDistance\n\tcategoryTime\n\tcategoryPressure\n\tcategoryForce\n\tcategoryEnergy\n\tcategoryPower\n\tcategoryMagnetism\n\tcategoryTemperature\n\tcategoryVolumeAndLiquidMeasure\n\tcategoryArea\n\tcategoryInformation\n\tcategorySpeed\n\n\tmatchModeExact      = 0\n\tmatchModeMinGreater = 1\n\tmatchModeMaxLess    = -1\n\tmatchModeWildcard   = 2\n\n\tsearchModeLinear        = 1\n\tsearchModeReverseLinear = -1\n\tsearchModeAscBinary     = 2\n\tsearchModeDescBinary    = -2\n\n\tmaxFinancialIterations = 128\n\tfinancialPrecision     = 1.0e-08\n\t// Date and time format regular expressions\n\tmonthRe    = `((jan|january)|(feb|february)|(mar|march)|(apr|april)|(may)|(jun|june)|(jul|july)|(aug|august)|(sep|september)|(oct|october)|(nov|november)|(dec|december))`\n\tdf1        = `(([0-9])+)/(([0-9])+)/(([0-9])+)`\n\tdf2        = monthRe + ` (([0-9])+), (([0-9])+)`\n\tdf3        = `(([0-9])+)-(([0-9])+)-(([0-9])+)`\n\tdf4        = `(([0-9])+)-` + monthRe + `-(([0-9])+)`\n\tdatePrefix = `^((` + df1 + `|` + df2 + `|` + df3 + `|` + df4 + `) )?`\n\ttfhh       = `(([0-9])+) (am|pm)`\n\ttfhhmm     = `(([0-9])+):(([0-9])+)( (am|pm))?`\n\ttfmmss     = `(([0-9])+):(([0-9])+\\.([0-9])+)( (am|pm))?`\n\ttfhhmmss   = `(([0-9])+):(([0-9])+):(([0-9])+(\\.([0-9])+)?)( (am|pm))?`\n\ttimeSuffix = `( (` + tfhh + `|` + tfhhmm + `|` + tfmmss + `|` + tfhhmmss + `))?$`\n)\n\nvar (\n\t// wildcardTokenRE tokenizes an Excel wildcard pattern into tilde-escaped\n\t// sequences, bare wildcards (* ?), or any other single character.\n\twildcardTokenRE = regexp.MustCompile(`~[*?~]|[*?]|[\\s\\S]`)\n\t// wildcardPatternMap maps each token produced by wildcardTokenRE to its\n\t// regular-expression equivalent. Tokens absent from the map are literals.\n\twildcardPatternMap = map[string]string{\n\t\t\"~*\": regexp.QuoteMeta(\"*\"),\n\t\t\"~?\": regexp.QuoteMeta(\"?\"),\n\t\t\"~~\": regexp.QuoteMeta(\"~\"),\n\t\t\"*\":  \".*\",\n\t\t\"?\":  \".\",\n\t}\n\t// wildcardBareTokens is the set of tokens that represent real wildcards.\n\twildcardBareTokens = map[string]bool{\"*\": true, \"?\": true}\n\t// tokenPriority defined basic arithmetic operator priority\n\ttokenPriority = map[string]int{\n\t\t\"^\":  5,\n\t\t\"*\":  4,\n\t\t\"/\":  4,\n\t\t\"+\":  3,\n\t\t\"-\":  3,\n\t\t\"&\":  2,\n\t\t\"=\":  1,\n\t\t\"<>\": 1,\n\t\t\"<\":  1,\n\t\t\"<=\": 1,\n\t\t\">\":  1,\n\t\t\">=\": 1,\n\t}\n\tmonth2num = map[string]int{\n\t\t\"january\":   1,\n\t\t\"february\":  2,\n\t\t\"march\":     3,\n\t\t\"april\":     4,\n\t\t\"may\":       5,\n\t\t\"june\":      6,\n\t\t\"july\":      7,\n\t\t\"august\":    8,\n\t\t\"september\": 9,\n\t\t\"october\":   10,\n\t\t\"november\":  11,\n\t\t\"december\":  12,\n\t\t\"jan\":       1,\n\t\t\"feb\":       2,\n\t\t\"mar\":       3,\n\t\t\"apr\":       4,\n\t\t\"jun\":       6,\n\t\t\"jul\":       7,\n\t\t\"aug\":       8,\n\t\t\"sep\":       9,\n\t\t\"oct\":       10,\n\t\t\"nov\":       11,\n\t\t\"dec\":       12,\n\t}\n\tdateFormats = map[string]*regexp.Regexp{\n\t\t\"mm/dd/yy\":    regexp.MustCompile(`^` + df1 + timeSuffix),\n\t\t\"mm dd, yy\":   regexp.MustCompile(`^` + df2 + timeSuffix),\n\t\t\"yy-mm-dd\":    regexp.MustCompile(`^` + df3 + timeSuffix),\n\t\t\"yy-mmStr-dd\": regexp.MustCompile(`^` + df4 + timeSuffix),\n\t}\n\ttimeFormats = map[string]*regexp.Regexp{\n\t\t\"hh\":       regexp.MustCompile(datePrefix + tfhh + `$`),\n\t\t\"hh:mm\":    regexp.MustCompile(datePrefix + tfhhmm + `$`),\n\t\t\"mm:ss\":    regexp.MustCompile(datePrefix + tfmmss + `$`),\n\t\t\"hh:mm:ss\": regexp.MustCompile(datePrefix + tfhhmmss + `$`),\n\t}\n\tdateOnlyFormats = []*regexp.Regexp{\n\t\tregexp.MustCompile(`^` + df1 + `$`),\n\t\tregexp.MustCompile(`^` + df2 + `$`),\n\t\tregexp.MustCompile(`^` + df3 + `$`),\n\t\tregexp.MustCompile(`^` + df4 + `$`),\n\t}\n\taddressFmtMaps = map[string]func(col, row int) (string, error){\n\t\t\"1_TRUE\": func(col, row int) (string, error) {\n\t\t\treturn CoordinatesToCellName(col, row, true)\n\t\t},\n\t\t\"1_FALSE\": func(col, row int) (string, error) {\n\t\t\treturn fmt.Sprintf(\"R%dC%d\", row, col), nil\n\t\t},\n\t\t\"2_TRUE\": func(col, row int) (string, error) {\n\t\t\tcolumn, err := ColumnNumberToName(col)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn fmt.Sprintf(\"%s$%d\", column, row), nil\n\t\t},\n\t\t\"2_FALSE\": func(col, row int) (string, error) {\n\t\t\treturn fmt.Sprintf(\"R%dC[%d]\", row, col), nil\n\t\t},\n\t\t\"3_TRUE\": func(col, row int) (string, error) {\n\t\t\tcolumn, err := ColumnNumberToName(col)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn fmt.Sprintf(\"$%s%d\", column, row), nil\n\t\t},\n\t\t\"3_FALSE\": func(col, row int) (string, error) {\n\t\t\treturn fmt.Sprintf(\"R[%d]C%d\", row, col), nil\n\t\t},\n\t\t\"4_TRUE\": func(col, row int) (string, error) {\n\t\t\treturn CoordinatesToCellName(col, row, false)\n\t\t},\n\t\t\"4_FALSE\": func(col, row int) (string, error) {\n\t\t\treturn fmt.Sprintf(\"R[%d]C[%d]\", row, col), nil\n\t\t},\n\t}\n\tformulaFnNameReplacer = strings.NewReplacer(\"_xlfn.\", \"\", \".\", \"dot\")\n\tformulaFormats        = []*regexp.Regexp{\n\t\tregexp.MustCompile(`^(\\d+)$`),\n\t\tregexp.MustCompile(`^=(.*)$`),\n\t\tregexp.MustCompile(`^<>(.*)$`),\n\t\tregexp.MustCompile(`^<=(.*)$`),\n\t\tregexp.MustCompile(`^>=(.*)$`),\n\t\tregexp.MustCompile(`^<(.*)$`),\n\t\tregexp.MustCompile(`^>(.*)$`),\n\t}\n\tformulaCriterias = []byte{\n\t\tcriteriaEq,\n\t\tcriteriaEq,\n\t\tcriteriaNe,\n\t\tcriteriaLe,\n\t\tcriteriaGe,\n\t\tcriteriaL,\n\t\tcriteriaG,\n\t}\n\t// defines numbers text in the Thai used for the BAHTTEXT formula function.\n\tth0      = \"\\u0E28\\u0E39\\u0E19\\u0E22\\u0E4C\"\n\tth1      = \"\\u0E2B\\u0E19\\u0E36\\u0E48\\u0E07\"\n\tth2      = \"\\u0E2A\\u0E2D\\u0E07\"\n\tth3      = \"\\u0E2A\\u0E32\\u0E21\"\n\tth4      = \"\\u0E2A\\u0E35\\u0E48\"\n\tth5      = \"\\u0E2B\\u0E49\\u0E32\"\n\tth6      = \"\\u0E2B\\u0E01\"\n\tth7      = \"\\u0E40\\u0E08\\u0E47\\u0E14\"\n\tth8      = \"\\u0E41\\u0E1B\\u0E14\"\n\tth9      = \"\\u0E40\\u0E01\\u0E49\\u0E32\"\n\tth10     = \"\\u0E2A\\u0E34\\u0E1A\"\n\tth11     = \"\\u0E40\\u0E2D\\u0E47\\u0E14\"\n\tth20     = \"\\u0E22\\u0E35\\u0E48\"\n\tth1e2    = \"\\u0E23\\u0E49\\u0E2D\\u0E22\"\n\tth1e3    = \"\\u0E1E\\u0E31\\u0E19\"\n\tth1e4    = \"\\u0E2B\\u0E21\\u0E37\\u0E48\\u0E19\"\n\tth1e5    = \"\\u0E41\\u0E2A\\u0E19\"\n\tth1e6    = \"\\u0E25\\u0E49\\u0E32\\u0E19\"\n\tthDot0   = \"\\u0E16\\u0E49\\u0E27\\u0E19\"\n\tthBaht   = \"\\u0E1A\\u0E32\\u0E17\"\n\tthSatang = \"\\u0E2A\\u0E15\\u0E32\\u0E07\\u0E04\\u0E4C\"\n\tthMinus  = \"\\u0E25\\u0E1A\"\n)\n\n// calcContext defines the formula execution context.\ntype calcContext struct {\n\tmu                sync.Mutex\n\tentry             string\n\tmaxCalcIterations uint\n\titerations        map[string]uint\n\titerationsCache   map[string]formulaArg\n}\n\n// cellRef defines the structure of a cell reference.\ntype cellRef struct {\n\tCol   int\n\tRow   int\n\tSheet string\n}\n\n// cellRef defines the structure of a cell range.\ntype cellRange struct {\n\tFrom cellRef\n\tTo   cellRef\n}\n\n// formulaCriteria defined formula criteria parser result.\ntype formulaCriteria struct {\n\tType      byte\n\tCondition formulaArg\n}\n\n// ArgType is the type of formula argument type.\ntype ArgType byte\n\n// Formula argument types enumeration.\nconst (\n\tArgUnknown ArgType = iota\n\tArgNumber\n\tArgString\n\tArgList\n\tArgMatrix\n\tArgError\n\tArgEmpty\n)\n\n// formulaArg is the argument of a formula or function.\ntype formulaArg struct {\n\tSheetName            string\n\tNumber               float64\n\tString               string\n\tList                 []formulaArg\n\tMatrix               [][]formulaArg\n\tBoolean              bool\n\tError                string\n\tType                 ArgType\n\tcellRefs, cellRanges *list.List\n}\n\n// Value returns a string data type of the formula argument.\nfunc (fa formulaArg) Value() (value string) {\n\tswitch fa.Type {\n\tcase ArgNumber:\n\t\tif fa.Boolean {\n\t\t\tif fa.Number == 0 {\n\t\t\t\treturn \"FALSE\"\n\t\t\t}\n\t\t\treturn \"TRUE\"\n\t\t}\n\t\treturn fmt.Sprintf(\"%g\", fa.Number)\n\tcase ArgString:\n\t\treturn fa.String\n\tcase ArgMatrix:\n\t\tif args := fa.ToList(); len(args) > 0 {\n\t\t\treturn args[0].Value()\n\t\t}\n\tcase ArgError:\n\t\treturn fa.Error\n\t}\n\treturn\n}\n\n// ToNumber returns a formula argument with number data type.\nfunc (fa formulaArg) ToNumber() formulaArg {\n\tvar n float64\n\tvar err error\n\tswitch fa.Type {\n\tcase ArgString:\n\t\tn, err = strconv.ParseFloat(fa.String, 64)\n\t\tif err != nil {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t\t}\n\tcase ArgNumber:\n\t\tn = fa.Number\n\tcase ArgMatrix:\n\t\tif args := fa.ToList(); len(args) > 0 {\n\t\t\treturn args[0].ToNumber()\n\t\t}\n\t}\n\treturn newNumberFormulaArg(n)\n}\n\n// ToBool returns a formula argument with boolean data type.\nfunc (fa formulaArg) ToBool() formulaArg {\n\tvar b bool\n\tvar err error\n\tswitch fa.Type {\n\tcase ArgString:\n\t\tb, err = strconv.ParseBool(fa.String)\n\t\tif err != nil {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t\t}\n\tcase ArgNumber:\n\t\tif fa.Number == 1 {\n\t\t\tb = true\n\t\t}\n\t}\n\treturn newBoolFormulaArg(b)\n}\n\n// ToList returns a formula argument with array data type.\nfunc (fa formulaArg) ToList() []formulaArg {\n\tswitch fa.Type {\n\tcase ArgMatrix:\n\t\tvar args []formulaArg\n\t\tfor _, row := range fa.Matrix {\n\t\t\targs = append(args, row...)\n\t\t}\n\t\treturn args\n\tcase ArgList:\n\t\treturn fa.List\n\tcase ArgNumber, ArgString, ArgError, ArgUnknown:\n\t\treturn []formulaArg{fa}\n\t}\n\treturn nil\n}\n\n// formulaFuncs is the type of the formula functions.\ntype formulaFuncs struct {\n\tf           *File\n\tctx         *calcContext\n\tsheet, cell string\n}\n\n// implicitIntersect applies Excel's implicit intersection to a matrix argument.\n// For a non-array formula, when a whole-column or whole-row reference is passed\n// to a scalar function, Excel resolves it to the single cell in the same row\n// (or column) as the formula cell. If the argument is not a matrix, it is\n// returned unchanged.\nfunc (fn *formulaFuncs) implicitIntersect(arg formulaArg) formulaArg {\n\tif arg.Type != ArgMatrix {\n\t\treturn arg\n\t}\n\t_, row, err := CellNameToCoordinates(fn.cell)\n\tif err != nil {\n\t\treturn arg\n\t}\n\t// row is 1-based; matrix is 0-indexed\n\tidx := row - 1\n\tif idx >= 0 && idx < len(arg.Matrix) && len(arg.Matrix[idx]) > 0 {\n\t\treturn arg.Matrix[idx][0]\n\t}\n\treturn arg\n}\n\n// CalcCellValue provides a function to get calculated cell value. This feature\n// is currently in working processing. Iterative calculation, implicit\n// intersection, explicit intersection, array formula, table formula and some\n// other formulas are not supported currently.\n//\n// Supported formula functions:\n//\n//\tABS\n//\tACCRINT\n//\tACCRINTM\n//\tACOS\n//\tACOSH\n//\tACOT\n//\tACOTH\n//\tADDRESS\n//\tAGGREGATE\n//\tAMORDEGRC\n//\tAMORLINC\n//\tAND\n//\tARABIC\n//\tARRAYTOTEXT\n//\tASIN\n//\tASINH\n//\tATAN\n//\tATAN2\n//\tATANH\n//\tAVEDEV\n//\tAVERAGE\n//\tAVERAGEA\n//\tAVERAGEIF\n//\tAVERAGEIFS\n//\tBAHTTEXT\n//\tBASE\n//\tBESSELI\n//\tBESSELJ\n//\tBESSELK\n//\tBESSELY\n//\tBETA.DIST\n//\tBETA.INV\n//\tBETADIST\n//\tBETAINV\n//\tBIN2DEC\n//\tBIN2HEX\n//\tBIN2OCT\n//\tBINOM.DIST\n//\tBINOM.DIST.RANGE\n//\tBINOM.INV\n//\tBINOMDIST\n//\tBITAND\n//\tBITLSHIFT\n//\tBITOR\n//\tBITRSHIFT\n//\tBITXOR\n//\tCEILING\n//\tCEILING.MATH\n//\tCEILING.PRECISE\n//\tCHAR\n//\tCHIDIST\n//\tCHIINV\n//\tCHISQ.DIST\n//\tCHISQ.DIST.RT\n//\tCHISQ.INV\n//\tCHISQ.INV.RT\n//\tCHISQ.TEST\n//\tCHITEST\n//\tCHOOSE\n//\tCLEAN\n//\tCODE\n//\tCOLUMN\n//\tCOLUMNS\n//\tCOMBIN\n//\tCOMBINA\n//\tCOMPLEX\n//\tCONCAT\n//\tCONCATENATE\n//\tCONFIDENCE\n//\tCONFIDENCE.NORM\n//\tCONFIDENCE.T\n//\tCONVERT\n//\tCORREL\n//\tCOS\n//\tCOSH\n//\tCOT\n//\tCOTH\n//\tCOUNT\n//\tCOUNTA\n//\tCOUNTBLANK\n//\tCOUNTIF\n//\tCOUNTIFS\n//\tCOUPDAYBS\n//\tCOUPDAYS\n//\tCOUPDAYSNC\n//\tCOUPNCD\n//\tCOUPNUM\n//\tCOUPPCD\n//\tCOVAR\n//\tCOVARIANCE.P\n//\tCOVARIANCE.S\n//\tCRITBINOM\n//\tCSC\n//\tCSCH\n//\tCUMIPMT\n//\tCUMPRINC\n//\tDATE\n//\tDATEDIF\n//\tDATEVALUE\n//\tDAVERAGE\n//\tDAY\n//\tDAYS\n//\tDAYS360\n//\tDB\n//\tDBCS\n//\tDCOUNT\n//\tDCOUNTA\n//\tDDB\n//\tDEC2BIN\n//\tDEC2HEX\n//\tDEC2OCT\n//\tDECIMAL\n//\tDEGREES\n//\tDELTA\n//\tDEVSQ\n//\tDGET\n//\tDISC\n//\tDMAX\n//\tDMIN\n//\tDOLLAR\n//\tDOLLARDE\n//\tDOLLARFR\n//\tDPRODUCT\n//\tDSTDEV\n//\tDSTDEVP\n//\tDSUM\n//\tDURATION\n//\tDVAR\n//\tDVARP\n//\tEDATE\n//\tEFFECT\n//\tENCODEURL\n//\tEOMONTH\n//\tERF\n//\tERF.PRECISE\n//\tERFC\n//\tERFC.PRECISE\n//\tERROR.TYPE\n//\tEUROCONVERT\n//\tEVEN\n//\tEXACT\n//\tEXP\n//\tEXPON.DIST\n//\tEXPONDIST\n//\tF.DIST\n//\tF.DIST.RT\n//\tF.INV\n//\tF.INV.RT\n//\tF.TEST\n//\tFACT\n//\tFACTDOUBLE\n//\tFALSE\n//\tFDIST\n//\tFIND\n//\tFINDB\n//\tFINV\n//\tFISHER\n//\tFISHERINV\n//\tFIXED\n//\tFLOOR\n//\tFLOOR.MATH\n//\tFLOOR.PRECISE\n//\tFORECAST\n//\tFORECAST.LINEAR\n//\tFORMULATEXT\n//\tFREQUENCY\n//\tFTEST\n//\tFV\n//\tFVSCHEDULE\n//\tGAMMA\n//\tGAMMA.DIST\n//\tGAMMA.INV\n//\tGAMMADIST\n//\tGAMMAINV\n//\tGAMMALN\n//\tGAMMALN.PRECISE\n//\tGAUSS\n//\tGCD\n//\tGEOMEAN\n//\tGESTEP\n//\tGROWTH\n//\tHARMEAN\n//\tHEX2BIN\n//\tHEX2DEC\n//\tHEX2OCT\n//\tHLOOKUP\n//\tHOUR\n//\tHYPERLINK\n//\tHYPGEOM.DIST\n//\tHYPGEOMDIST\n//\tIF\n//\tIFERROR\n//\tIFNA\n//\tIFS\n//\tIMABS\n//\tIMAGINARY\n//\tIMARGUMENT\n//\tIMCONJUGATE\n//\tIMCOS\n//\tIMCOSH\n//\tIMCOT\n//\tIMCSC\n//\tIMCSCH\n//\tIMDIV\n//\tIMEXP\n//\tIMLN\n//\tIMLOG10\n//\tIMLOG2\n//\tIMPOWER\n//\tIMPRODUCT\n//\tIMREAL\n//\tIMSEC\n//\tIMSECH\n//\tIMSIN\n//\tIMSINH\n//\tIMSQRT\n//\tIMSUB\n//\tIMSUM\n//\tIMTAN\n//\tINDEX\n//\tINDIRECT\n//\tINT\n//\tINTERCEPT\n//\tINTRATE\n//\tIPMT\n//\tIRR\n//\tISBLANK\n//\tISERR\n//\tISERROR\n//\tISEVEN\n//\tISFORMULA\n//\tISLOGICAL\n//\tISNA\n//\tISNONTEXT\n//\tISNUMBER\n//\tISO.CEILING\n//\tISODD\n//\tISOWEEKNUM\n//\tISPMT\n//\tISREF\n//\tISTEXT\n//\tKURT\n//\tLARGE\n//\tLCM\n//\tLEFT\n//\tLEFTB\n//\tLEN\n//\tLENB\n//\tLN\n//\tLOG\n//\tLOG10\n//\tLOGINV\n//\tLOGNORM.DIST\n//\tLOGNORM.INV\n//\tLOGNORMDIST\n//\tLOOKUP\n//\tLOWER\n//\tMATCH\n//\tMAX\n//\tMAXA\n//\tMAXIFS\n//\tMDETERM\n//\tMDURATION\n//\tMEDIAN\n//\tMID\n//\tMIDB\n//\tMIN\n//\tMINA\n//\tMINIFS\n//\tMINUTE\n//\tMINVERSE\n//\tMIRR\n//\tMMULT\n//\tMOD\n//\tMODE\n//\tMODE.MULT\n//\tMODE.SNGL\n//\tMONTH\n//\tMROUND\n//\tMULTINOMIAL\n//\tMUNIT\n//\tN\n//\tNA\n//\tNEGBINOM.DIST\n//\tNEGBINOMDIST\n//\tNETWORKDAYS\n//\tNETWORKDAYS.INTL\n//\tNOMINAL\n//\tNORM.DIST\n//\tNORM.INV\n//\tNORM.S.DIST\n//\tNORM.S.INV\n//\tNORMDIST\n//\tNORMINV\n//\tNORMSDIST\n//\tNORMSINV\n//\tNOT\n//\tNOW\n//\tNPER\n//\tNPV\n//\tOCT2BIN\n//\tOCT2DEC\n//\tOCT2HEX\n//\tODD\n//\tODDFPRICE\n//\tODDFYIELD\n//\tODDLPRICE\n//\tODDLYIELD\n//\tOR\n//\tPDURATION\n//\tPEARSON\n//\tPERCENTILE\n//\tPERCENTILE.EXC\n//\tPERCENTILE.INC\n//\tPERCENTRANK\n//\tPERCENTRANK.EXC\n//\tPERCENTRANK.INC\n//\tPERMUT\n//\tPERMUTATIONA\n//\tPHI\n//\tPI\n//\tPMT\n//\tPOISSON\n//\tPOISSON.DIST\n//\tPOWER\n//\tPPMT\n//\tPRICE\n//\tPRICEDISC\n//\tPRICEMAT\n//\tPROB\n//\tPRODUCT\n//\tPROPER\n//\tPV\n//\tQUARTILE\n//\tQUARTILE.EXC\n//\tQUARTILE.INC\n//\tQUOTIENT\n//\tRADIANS\n//\tRAND\n//\tRANDBETWEEN\n//\tRANK\n//\tRANK.EQ\n//\tRATE\n//\tRECEIVED\n//\tREPLACE\n//\tREPLACEB\n//\tREPT\n//\tRIGHT\n//\tRIGHTB\n//\tROMAN\n//\tROUND\n//\tROUNDDOWN\n//\tROUNDUP\n//\tROW\n//\tROWS\n//\tRRI\n//\tRSQ\n//\tSEARCH\n//\tSEARCHB\n//\tSEC\n//\tSECH\n//\tSECOND\n//\tSERIESSUM\n//\tSHEET\n//\tSHEETS\n//\tSIGN\n//\tSIN\n//\tSINH\n//\tSKEW\n//\tSKEW.P\n//\tSLN\n//\tSLOPE\n//\tSMALL\n//\tSORTBY\n//\tSQRT\n//\tSQRTPI\n//\tSTANDARDIZE\n//\tSTDEV\n//\tSTDEV.P\n//\tSTDEV.S\n//\tSTDEVA\n//\tSTDEVP\n//\tSTDEVPA\n//\tSTEYX\n//\tSUBSTITUTE\n//\tSUBTOTAL\n//\tSUM\n//\tSUMIF\n//\tSUMIFS\n//\tSUMPRODUCT\n//\tSUMSQ\n//\tSUMX2MY2\n//\tSUMX2PY2\n//\tSUMXMY2\n//\tSWITCH\n//\tSYD\n//\tT\n//\tT.DIST\n//\tT.DIST.2T\n//\tT.DIST.RT\n//\tT.INV\n//\tT.INV.2T\n//\tT.TEST\n//\tTAN\n//\tTANH\n//\tTBILLEQ\n//\tTBILLPRICE\n//\tTBILLYIELD\n//\tTDIST\n//\tTEXT\n//\tTEXTAFTER\n//\tTEXTBEFORE\n//\tTEXTJOIN\n//\tTIME\n//\tTIMEVALUE\n//\tTINV\n//\tTODAY\n//\tTRANSPOSE\n//\tTREND\n//\tTRIM\n//\tTRIMMEAN\n//\tTRUE\n//\tTRUNC\n//\tTTEST\n//\tTYPE\n//\tUNICHAR\n//\tUNICODE\n//\tUNIQUE\n//\tUPPER\n//\tVALUE\n//\tVALUETOTEXT\n//\tVAR\n//\tVAR.P\n//\tVAR.S\n//\tVARA\n//\tVARP\n//\tVARPA\n//\tVDB\n//\tVLOOKUP\n//\tWEEKDAY\n//\tWEEKNUM\n//\tWEIBULL\n//\tWEIBULL.DIST\n//\tWORKDAY\n//\tWORKDAY.INTL\n//\tXIRR\n//\tXLOOKUP\n//\tXNPV\n//\tXOR\n//\tYEAR\n//\tYEARFRAC\n//\tYIELD\n//\tYIELDDISC\n//\tYIELDMAT\n//\tZ.TEST\n//\tZTEST\nfunc (f *File) CalcCellValue(sheet, cell string, opts ...Options) (result string, err error) {\n\tentry := sheet + \"!\" + cell\n\tif cachedResult, ok := f.calcCache.Load(entry); ok {\n\t\treturn cachedResult.(string), nil\n\t}\n\toptions := f.getOptions(opts...)\n\tvar (\n\t\trawCellValue = options.RawCellValue\n\t\tstyleIdx     int\n\t\ttoken        formulaArg\n\t)\n\tif token, err = f.calcCellValue(&calcContext{\n\t\tentry:             entry,\n\t\tmaxCalcIterations: options.MaxCalcIterations,\n\t\titerations:        make(map[string]uint),\n\t\titerationsCache:   make(map[string]formulaArg),\n\t}, sheet, cell); err != nil {\n\t\tresult = token.String\n\t\treturn\n\t}\n\tif !rawCellValue {\n\t\tstyleIdx, _ = f.GetCellStyle(sheet, cell)\n\t}\n\tif token.Type == ArgNumber && !token.Boolean {\n\t\t_, precision, decimal := isNumeric(token.Value())\n\t\tif precision > 15 {\n\t\t\tresult, err = f.formattedValue(&xlsxC{S: styleIdx, V: strings.ToUpper(strconv.FormatFloat(decimal, 'G', 15, 64))}, rawCellValue, CellTypeNumber)\n\t\t\tif err == nil {\n\t\t\t\tf.calcCache.Store(entry, result)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif !strings.HasPrefix(result, \"0\") {\n\t\t\tresult, err = f.formattedValue(&xlsxC{S: styleIdx, V: strings.ToUpper(strconv.FormatFloat(decimal, 'f', -1, 64))}, rawCellValue, CellTypeNumber)\n\t\t}\n\t\tif err == nil {\n\t\t\tf.calcCache.Store(entry, result)\n\t\t}\n\t\treturn\n\t}\n\tresult, err = f.formattedValue(&xlsxC{S: styleIdx, V: token.Value()}, rawCellValue, CellTypeInlineString)\n\tif err == nil {\n\t\tf.calcCache.Store(entry, result)\n\t}\n\treturn\n}\n\n// clearCalcCache clear all calculation related caches.\nfunc (f *File) clearCalcCache() {\n\tf.calcCache.Clear()\n\tf.formulaArgCache.Clear()\n}\n\n// calcCellValue calculate cell value by given context, worksheet name and cell\n// reference.\nfunc (f *File) calcCellValue(ctx *calcContext, sheet, cell string) (result formulaArg, err error) {\n\tvar formula string\n\tif formula, err = f.getCellFormula(sheet, cell, true); err != nil {\n\t\treturn\n\t}\n\tps := efp.ExcelParser()\n\ttokens := ps.Parse(formula)\n\tif tokens == nil {\n\t\treturn f.cellResolver(ctx, sheet, cell)\n\t}\n\tresult, err = f.evalInfixExp(ctx, sheet, cell, tokens)\n\treturn\n}\n\n// getPriority calculate arithmetic operator priority.\nfunc getPriority(token efp.Token) (pri int) {\n\tpri = tokenPriority[token.TValue]\n\tif token.TValue == \"-\" && token.TType == efp.TokenTypeOperatorPrefix {\n\t\tpri = 6\n\t}\n\tif isBeginParenthesesToken(token) { // (\n\t\tpri = 0\n\t}\n\treturn\n}\n\n// newNumberFormulaArg constructs a number formula argument.\nfunc newNumberFormulaArg(n float64) formulaArg {\n\tif math.IsNaN(n) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn formulaArg{Type: ArgNumber, Number: n}\n}\n\n// newStringFormulaArg constructs a string formula argument.\nfunc newStringFormulaArg(s string) formulaArg {\n\treturn formulaArg{Type: ArgString, String: s}\n}\n\n// newMatrixFormulaArg constructs a matrix formula argument.\nfunc newMatrixFormulaArg(m [][]formulaArg) formulaArg {\n\treturn formulaArg{Type: ArgMatrix, Matrix: m}\n}\n\n// newListFormulaArg create a list formula argument.\nfunc newListFormulaArg(l []formulaArg) formulaArg {\n\treturn formulaArg{Type: ArgList, List: l}\n}\n\n// newBoolFormulaArg constructs a boolean formula argument.\nfunc newBoolFormulaArg(b bool) formulaArg {\n\tvar n float64\n\tif b {\n\t\tn = 1\n\t}\n\treturn formulaArg{Type: ArgNumber, Number: n, Boolean: true}\n}\n\n// newErrorFormulaArg create an error formula argument of a given type with a\n// specified error message.\nfunc newErrorFormulaArg(formulaError, msg string) formulaArg {\n\treturn formulaArg{Type: ArgError, String: formulaError, Error: msg}\n}\n\n// newEmptyFormulaArg create an empty formula argument.\nfunc newEmptyFormulaArg() formulaArg {\n\treturn formulaArg{Type: ArgEmpty}\n}\n\n// evalInfixExp evaluate syntax analysis by given infix expression after\n// lexical analysis. Evaluate an infix expression containing formulas by\n// stacks:\n//\n//\topd  - Operand\n//\topt  - Operator\n//\topf  - Operation formula\n//\topfd - Operand of the operation formula\n//\topft - Operator of the operation formula\n//\targs - Arguments list of the operation formula\n//\n// TODO: handle subtypes: Nothing, Text, Logical, Error, Concatenation, Intersection, Union\nfunc (f *File) evalInfixExp(ctx *calcContext, sheet, cell string, tokens []efp.Token) (formulaArg, error) {\n\tvar (\n\t\terr                             error\n\t\tinArray, inArrayRow             bool\n\t\tformulaArray                    [][]formulaArg\n\t\tformulaArrayRow                 []formulaArg\n\t\topdStack, optStack, opfStack    = NewStack(), NewStack(), NewStack()\n\t\topfdStack, opftStack, argsStack = NewStack(), NewStack(), NewStack()\n\t)\n\tfor i := 0; i < len(tokens); i++ {\n\t\ttoken := tokens[i]\n\n\t\t// out of function stack\n\t\tif opfStack.Len() == 0 {\n\t\t\tif err = f.parseToken(ctx, sheet, token, opdStack, optStack); err != nil {\n\t\t\t\treturn newEmptyFormulaArg(), err\n\t\t\t}\n\t\t}\n\n\t\t// function start\n\t\tif isFunctionStartToken(token) {\n\t\t\tif token.TValue == \"ARRAY\" {\n\t\t\t\tinArray, formulaArray = true, [][]formulaArg{}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif token.TValue == \"ARRAYROW\" {\n\t\t\t\tinArrayRow, formulaArrayRow = true, []formulaArg{}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\topfStack.Push(token)\n\t\t\targsStack.Push(list.New().Init())\n\t\t\topftStack.Push(token) // to know which operators belong to a function use the function as a separator\n\t\t\tcontinue\n\t\t}\n\n\t\t// in function stack, walk 2 token at once\n\t\tif opfStack.Len() > 0 {\n\t\t\tvar nextToken efp.Token\n\t\t\tif i+1 < len(tokens) {\n\t\t\t\tnextToken = tokens[i+1]\n\t\t\t}\n\n\t\t\t// current token is args or range, skip next token, order required: parse reference first\n\t\t\tif token.TSubType == efp.TokenSubTypeRange {\n\t\t\t\tif opftStack.Peek().(efp.Token) != opfStack.Peek().(efp.Token) {\n\t\t\t\t\trefTo := f.getDefinedNameRefTo(token.TValue, sheet)\n\t\t\t\t\tif refTo != \"\" {\n\t\t\t\t\t\ttoken.TValue = refTo\n\t\t\t\t\t}\n\t\t\t\t\t// parse reference: must reference at here\n\t\t\t\t\tresult, err := f.parseReference(ctx, sheet, token.TValue)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn result, err\n\t\t\t\t\t}\n\t\t\t\t\topfdStack.Push(result)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif nextToken.TType == efp.TokenTypeArgument || nextToken.TType == efp.TokenTypeFunction {\n\t\t\t\t\t// parse reference: reference or range at here\n\t\t\t\t\trefTo := f.getDefinedNameRefTo(token.TValue, sheet)\n\t\t\t\t\tif refTo != \"\" {\n\t\t\t\t\t\ttoken.TValue = refTo\n\t\t\t\t\t}\n\t\t\t\t\tresult, err := f.parseReference(ctx, sheet, token.TValue)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn result, err\n\t\t\t\t\t}\n\t\t\t\t\t// when current token is range, next token is argument and opfdStack not empty,\n\t\t\t\t\t// should push value to opfdStack and continue\n\t\t\t\t\tif nextToken.TType == efp.TokenTypeArgument && !opfdStack.Empty() {\n\t\t\t\t\t\topfdStack.Push(result)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\targsStack.Peek().(*list.List).PushBack(result)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// check current token is opft\n\t\t\tif err = f.parseToken(ctx, sheet, token, opfdStack, opftStack); err != nil {\n\t\t\t\treturn newEmptyFormulaArg(), err\n\t\t\t}\n\n\t\t\t// current token is arg\n\t\t\tif token.TType == efp.TokenTypeArgument {\n\t\t\t\tfor opftStack.Peek().(efp.Token) != opfStack.Peek().(efp.Token) {\n\t\t\t\t\t// calculate trigger\n\t\t\t\t\ttopOpt := opftStack.Peek().(efp.Token)\n\t\t\t\t\tif err := calculate(opfdStack, topOpt); err != nil {\n\t\t\t\t\t\targsStack.Peek().(*list.List).PushFront(newErrorFormulaArg(formulaErrorVALUE, err.Error()))\n\t\t\t\t\t}\n\t\t\t\t\topftStack.Pop()\n\t\t\t\t}\n\t\t\t\tif !opfdStack.Empty() {\n\t\t\t\t\targsStack.Peek().(*list.List).PushBack(opfdStack.Pop().(formulaArg))\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif inArrayRow && isOperand(token) {\n\t\t\t\tformulaArrayRow = append(formulaArrayRow, opfdStack.Pop().(formulaArg))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif inArrayRow && isFunctionStopToken(token) {\n\t\t\t\tformulaArray = append(formulaArray, formulaArrayRow)\n\t\t\t\tinArrayRow = false\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif inArray && isFunctionStopToken(token) {\n\t\t\t\targsStack.Peek().(*list.List).PushBack(newMatrixFormulaArg(formulaArray))\n\t\t\t\tinArray = false\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif errArg := f.evalInfixExpFunc(ctx, sheet, cell, token, nextToken, opfStack, opdStack, opftStack, opfdStack, argsStack); errArg.Type == ArgError {\n\t\t\t\treturn errArg, errors.New(errArg.Error)\n\t\t\t}\n\t\t}\n\t}\n\tfor optStack.Len() != 0 {\n\t\ttopOpt := optStack.Peek().(efp.Token)\n\t\tif err = calculate(opdStack, topOpt); err != nil {\n\t\t\treturn newEmptyFormulaArg(), err\n\t\t}\n\t\toptStack.Pop()\n\t}\n\tif opdStack.Len() == 0 {\n\t\treturn newEmptyFormulaArg(), ErrInvalidFormula\n\t}\n\treturn opdStack.Peek().(formulaArg), err\n}\n\n// evalInfixExpFunc evaluate formula function in the infix expression.\nfunc (f *File) evalInfixExpFunc(ctx *calcContext, sheet, cell string, token, nextToken efp.Token, opfStack, opdStack, opftStack, opfdStack, argsStack *Stack) formulaArg {\n\tif !isFunctionStopToken(token) {\n\t\treturn newEmptyFormulaArg()\n\t}\n\tprepareEvalInfixExp(opfStack, opftStack, opfdStack, argsStack)\n\t// call formula function to evaluate\n\targ := callFuncByName(&formulaFuncs{f: f, sheet: sheet, cell: cell, ctx: ctx},\n\t\tformulaFnNameReplacer.Replace(opfStack.Peek().(efp.Token).TValue),\n\t\t[]reflect.Value{reflect.ValueOf(argsStack.Peek().(*list.List))})\n\tif arg.Type == ArgError && opfStack.Len() == 1 {\n\t\treturn arg\n\t}\n\targsStack.Pop()\n\topftStack.Pop() // remove current function separator\n\topfStack.Pop()\n\tif opfStack.Len() > 0 { // still in function stack\n\t\tif nextToken.TType == efp.TokenTypeOperatorInfix || opftStack.Len() > 1 {\n\t\t\t// mathematics calculate in formula function\n\t\t\topfdStack.Push(arg)\n\t\t\treturn newEmptyFormulaArg()\n\t\t}\n\t\targsStack.Peek().(*list.List).PushBack(arg)\n\t\treturn newEmptyFormulaArg()\n\t}\n\tif arg.Type == ArgMatrix && len(arg.Matrix) > 0 && len(arg.Matrix[0]) > 0 {\n\t\topdStack.Push(arg.Matrix[0][0])\n\t\treturn newEmptyFormulaArg()\n\t}\n\topdStack.Push(arg)\n\treturn newEmptyFormulaArg()\n}\n\n// prepareEvalInfixExp check the token and stack state for formula function\n// evaluate.\nfunc prepareEvalInfixExp(opfStack, opftStack, opfdStack, argsStack *Stack) {\n\t// current token is function stop\n\tfor opftStack.Peek().(efp.Token) != opfStack.Peek().(efp.Token) {\n\t\t// calculate trigger\n\t\ttopOpt := opftStack.Peek().(efp.Token)\n\t\tif err := calculate(opfdStack, topOpt); err != nil {\n\t\t\targsStack.Peek().(*list.List).PushBack(newErrorFormulaArg(err.Error(), err.Error()))\n\t\t\topftStack.Pop()\n\t\t\tcontinue\n\t\t}\n\t\topftStack.Pop()\n\t}\n\targument := true\n\tif opftStack.Len() > 2 && opfdStack.Len() == 1 {\n\t\ttopOpt := opftStack.Pop()\n\t\tif opftStack.Peek().(efp.Token).TType == efp.TokenTypeOperatorInfix {\n\t\t\targument = false\n\t\t}\n\t\topftStack.Push(topOpt)\n\t}\n\t// push opfd to args\n\tif argument && opfdStack.Len() > 0 {\n\t\targsStack.Peek().(*list.List).PushBack(opfdStack.Pop().(formulaArg))\n\t}\n}\n\n// calcPow evaluate exponentiation arithmetic operations.\nfunc calcPow(rOpd, lOpd formulaArg, opdStack *Stack) error {\n\tlOpdVal := lOpd.ToNumber()\n\tif lOpdVal.Type != ArgNumber {\n\t\treturn errors.New(lOpdVal.Value())\n\t}\n\trOpdVal := rOpd.ToNumber()\n\tif rOpdVal.Type != ArgNumber {\n\t\treturn errors.New(rOpdVal.Value())\n\t}\n\topdStack.Push(newNumberFormulaArg(math.Pow(lOpdVal.Number, rOpdVal.Number)))\n\treturn nil\n}\n\n// calcEq evaluate equal arithmetic operations.\nfunc calcEq(rOpd, lOpd formulaArg, opdStack *Stack) error {\n\tif rOpd.Type == ArgString && lOpd.Type == ArgString {\n\t\topdStack.Push(newBoolFormulaArg(strings.EqualFold(lOpd.Value(), rOpd.Value())))\n\t\treturn nil\n\t}\n\topdStack.Push(newBoolFormulaArg(rOpd.Value() == lOpd.Value()))\n\treturn nil\n}\n\n// calcNEq evaluate not equal arithmetic operations.\nfunc calcNEq(rOpd, lOpd formulaArg, opdStack *Stack) error {\n\tif rOpd.Type == ArgString && lOpd.Type == ArgString {\n\t\topdStack.Push(newBoolFormulaArg(!strings.EqualFold(lOpd.Value(), rOpd.Value())))\n\t\treturn nil\n\t}\n\topdStack.Push(newBoolFormulaArg(rOpd.Value() != lOpd.Value()))\n\treturn nil\n}\n\n// calcL evaluate less than arithmetic operations.\nfunc calcL(rOpd, lOpd formulaArg, opdStack *Stack) error {\n\tif rOpd.Type == ArgNumber && lOpd.Type == ArgNumber {\n\t\topdStack.Push(newBoolFormulaArg(lOpd.Number < rOpd.Number))\n\t}\n\tif rOpd.Type == ArgString && lOpd.Type == ArgString {\n\t\topdStack.Push(newBoolFormulaArg(strings.Compare(lOpd.Value(), rOpd.Value()) == -1))\n\t}\n\tif rOpd.Type == ArgNumber && lOpd.Type == ArgString {\n\t\topdStack.Push(newBoolFormulaArg(false))\n\t}\n\tif rOpd.Type == ArgString && lOpd.Type == ArgNumber {\n\t\topdStack.Push(newBoolFormulaArg(true))\n\t}\n\treturn nil\n}\n\n// calcLe evaluate less than or equal arithmetic operations.\nfunc calcLe(rOpd, lOpd formulaArg, opdStack *Stack) error {\n\tif rOpd.Type == ArgNumber && lOpd.Type == ArgNumber {\n\t\topdStack.Push(newBoolFormulaArg(lOpd.Number <= rOpd.Number))\n\t}\n\tif rOpd.Type == ArgString && lOpd.Type == ArgString {\n\t\topdStack.Push(newBoolFormulaArg(strings.Compare(lOpd.Value(), rOpd.Value()) != 1))\n\t}\n\tif rOpd.Type == ArgNumber && lOpd.Type == ArgString {\n\t\topdStack.Push(newBoolFormulaArg(false))\n\t}\n\tif rOpd.Type == ArgString && lOpd.Type == ArgNumber {\n\t\topdStack.Push(newBoolFormulaArg(true))\n\t}\n\treturn nil\n}\n\n// calcG evaluate greater than arithmetic operations.\nfunc calcG(rOpd, lOpd formulaArg, opdStack *Stack) error {\n\tif rOpd.Type == ArgNumber && lOpd.Type == ArgNumber {\n\t\topdStack.Push(newBoolFormulaArg(lOpd.Number > rOpd.Number))\n\t}\n\tif rOpd.Type == ArgString && lOpd.Type == ArgString {\n\t\topdStack.Push(newBoolFormulaArg(strings.Compare(lOpd.Value(), rOpd.Value()) == 1))\n\t}\n\tif rOpd.Type == ArgNumber && lOpd.Type == ArgString {\n\t\topdStack.Push(newBoolFormulaArg(true))\n\t}\n\tif rOpd.Type == ArgString && lOpd.Type == ArgNumber {\n\t\topdStack.Push(newBoolFormulaArg(false))\n\t}\n\treturn nil\n}\n\n// calcGe evaluate greater than or equal arithmetic operations.\nfunc calcGe(rOpd, lOpd formulaArg, opdStack *Stack) error {\n\tif rOpd.Type == ArgNumber && lOpd.Type == ArgNumber {\n\t\topdStack.Push(newBoolFormulaArg(lOpd.Number >= rOpd.Number))\n\t}\n\tif rOpd.Type == ArgString && lOpd.Type == ArgString {\n\t\topdStack.Push(newBoolFormulaArg(strings.Compare(lOpd.Value(), rOpd.Value()) != -1))\n\t}\n\tif rOpd.Type == ArgNumber && lOpd.Type == ArgString {\n\t\topdStack.Push(newBoolFormulaArg(true))\n\t}\n\tif rOpd.Type == ArgString && lOpd.Type == ArgNumber {\n\t\topdStack.Push(newBoolFormulaArg(false))\n\t}\n\treturn nil\n}\n\n// calcSplice evaluate splice '&' operations.\nfunc calcSplice(rOpd, lOpd formulaArg, opdStack *Stack) error {\n\topdStack.Push(newStringFormulaArg(lOpd.Value() + rOpd.Value()))\n\treturn nil\n}\n\n// calcAdd evaluate addition arithmetic operations.\nfunc calcAdd(rOpd, lOpd formulaArg, opdStack *Stack) error {\n\tlOpdVal := lOpd.ToNumber()\n\tif lOpdVal.Type != ArgNumber {\n\t\treturn errors.New(lOpdVal.Value())\n\t}\n\trOpdVal := rOpd.ToNumber()\n\tif rOpdVal.Type != ArgNumber {\n\t\treturn errors.New(rOpdVal.Value())\n\t}\n\topdStack.Push(newNumberFormulaArg(lOpdVal.Number + rOpdVal.Number))\n\treturn nil\n}\n\n// calcSubtract evaluate subtraction arithmetic operations.\nfunc calcSubtract(rOpd, lOpd formulaArg, opdStack *Stack) error {\n\tif rOpd.Value() == \"\" {\n\t\trOpd = newNumberFormulaArg(0)\n\t}\n\tif lOpd.Value() == \"\" {\n\t\tlOpd = newNumberFormulaArg(0)\n\t}\n\tlOpdVal := lOpd.ToNumber()\n\tif lOpdVal.Type != ArgNumber {\n\t\treturn errors.New(lOpdVal.Value())\n\t}\n\trOpdVal := rOpd.ToNumber()\n\tif rOpdVal.Type != ArgNumber {\n\t\treturn errors.New(rOpdVal.Value())\n\t}\n\topdStack.Push(newNumberFormulaArg(lOpdVal.Number - rOpdVal.Number))\n\treturn nil\n}\n\n// calcMultiply evaluate multiplication arithmetic operations.\nfunc calcMultiply(rOpd, lOpd formulaArg, opdStack *Stack) error {\n\tlOpdVal := lOpd.ToNumber()\n\tif lOpdVal.Type != ArgNumber {\n\t\treturn errors.New(lOpdVal.Value())\n\t}\n\trOpdVal := rOpd.ToNumber()\n\tif rOpdVal.Type != ArgNumber {\n\t\treturn errors.New(rOpdVal.Value())\n\t}\n\topdStack.Push(newNumberFormulaArg(lOpdVal.Number * rOpdVal.Number))\n\treturn nil\n}\n\n// calcDiv evaluate division arithmetic operations.\nfunc calcDiv(rOpd, lOpd formulaArg, opdStack *Stack) error {\n\tlOpdVal := lOpd.ToNumber()\n\tif lOpdVal.Type != ArgNumber {\n\t\treturn errors.New(lOpdVal.Value())\n\t}\n\trOpdVal := rOpd.ToNumber()\n\tif rOpdVal.Type != ArgNumber {\n\t\treturn errors.New(rOpdVal.Value())\n\t}\n\tif rOpdVal.Number == 0 {\n\t\treturn errors.New(formulaErrorDIV)\n\t}\n\topdStack.Push(newNumberFormulaArg(lOpdVal.Number / rOpdVal.Number))\n\treturn nil\n}\n\n// calculate evaluate basic arithmetic operations.\nfunc calculate(opdStack *Stack, opt efp.Token) error {\n\tif opt.TValue == \"-\" && opt.TType == efp.TokenTypeOperatorPrefix {\n\t\tif opdStack.Len() < 1 {\n\t\t\treturn ErrInvalidFormula\n\t\t}\n\t\topd := opdStack.Pop().(formulaArg)\n\t\topdStack.Push(newNumberFormulaArg(0 - opd.ToNumber().Number))\n\t}\n\tif opt.TValue == \"-\" && opt.TType == efp.TokenTypeOperatorInfix {\n\t\tif opdStack.Len() < 2 {\n\t\t\treturn ErrInvalidFormula\n\t\t}\n\t\trOpd := opdStack.Pop().(formulaArg)\n\t\tlOpd := opdStack.Pop().(formulaArg)\n\t\tif err := calcSubtract(rOpd, lOpd, opdStack); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\ttokenCalcFunc := map[string]func(rOpd, lOpd formulaArg, opdStack *Stack) error{\n\t\t\"^\":  calcPow,\n\t\t\"*\":  calcMultiply,\n\t\t\"/\":  calcDiv,\n\t\t\"+\":  calcAdd,\n\t\t\"=\":  calcEq,\n\t\t\"<>\": calcNEq,\n\t\t\"<\":  calcL,\n\t\t\"<=\": calcLe,\n\t\t\">\":  calcG,\n\t\t\">=\": calcGe,\n\t\t\"&\":  calcSplice,\n\t}\n\tif fn, ok := tokenCalcFunc[opt.TValue]; ok {\n\t\tif opdStack.Len() < 2 {\n\t\t\treturn ErrInvalidFormula\n\t\t}\n\t\trOpd := opdStack.Pop().(formulaArg)\n\t\tlOpd := opdStack.Pop().(formulaArg)\n\t\tif opt.TValue != \"&\" {\n\t\t\tif rOpd.Value() == \"\" {\n\t\t\t\trOpd = newNumberFormulaArg(0)\n\t\t\t}\n\t\t\tif lOpd.Value() == \"\" {\n\t\t\t\tlOpd = newNumberFormulaArg(0)\n\t\t\t}\n\t\t}\n\t\tif rOpd.Type == ArgError {\n\t\t\treturn errors.New(rOpd.Value())\n\t\t}\n\t\tif lOpd.Type == ArgError {\n\t\t\treturn errors.New(lOpd.Value())\n\t\t}\n\t\treturn fn(rOpd, lOpd, opdStack)\n\t}\n\treturn nil\n}\n\n// parseOperatorPrefixToken parse operator prefix token.\nfunc (f *File) parseOperatorPrefixToken(optStack, opdStack *Stack, token efp.Token) (err error) {\n\tif optStack.Len() == 0 {\n\t\toptStack.Push(token)\n\t\treturn\n\t}\n\ttokenPriority := getPriority(token)\n\ttopOpt := optStack.Peek().(efp.Token)\n\ttopOptPriority := getPriority(topOpt)\n\tif topOpt.TValue == \"-\" && topOpt.TType == efp.TokenTypeOperatorPrefix && token.TValue == \"-\" && token.TType == efp.TokenTypeOperatorPrefix {\n\t\toptStack.Pop()\n\t\treturn\n\t}\n\tif tokenPriority > topOptPriority {\n\t\toptStack.Push(token)\n\t\treturn\n\t}\n\tfor tokenPriority <= topOptPriority {\n\t\toptStack.Pop()\n\t\tif err = calculate(opdStack, topOpt); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif optStack.Len() > 0 {\n\t\t\ttopOpt = optStack.Peek().(efp.Token)\n\t\t\ttopOptPriority = getPriority(topOpt)\n\t\t\tcontinue\n\t\t}\n\t\tbreak\n\t}\n\toptStack.Push(token)\n\treturn\n}\n\n// isFunctionStartToken determine if the token is function start.\nfunc isFunctionStartToken(token efp.Token) bool {\n\treturn token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStart\n}\n\n// isFunctionStopToken determine if the token is function stop.\nfunc isFunctionStopToken(token efp.Token) bool {\n\treturn token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStop\n}\n\n// isBeginParenthesesToken determine if the token is begin parentheses: (.\nfunc isBeginParenthesesToken(token efp.Token) bool {\n\treturn token.TType == efp.TokenTypeSubexpression && token.TSubType == efp.TokenSubTypeStart\n}\n\n// isEndParenthesesToken determine if the token is end parentheses: ).\nfunc isEndParenthesesToken(token efp.Token) bool {\n\treturn token.TType == efp.TokenTypeSubexpression && token.TSubType == efp.TokenSubTypeStop\n}\n\n// isOperatorPrefixToken determine if the token is parse operator prefix\n// token.\nfunc isOperatorPrefixToken(token efp.Token) bool {\n\t_, ok := tokenPriority[token.TValue]\n\treturn (token.TValue == \"-\" && token.TType == efp.TokenTypeOperatorPrefix) || (ok && token.TType == efp.TokenTypeOperatorInfix)\n}\n\n// isOperand determine if the token is parse operand.\nfunc isOperand(token efp.Token) bool {\n\treturn token.TType == efp.TokenTypeOperand && (token.TSubType == efp.TokenSubTypeNumber || token.TSubType == efp.TokenSubTypeText || token.TSubType == efp.TokenSubTypeLogical)\n}\n\n// tokenToFormulaArg create a formula argument by given token.\nfunc tokenToFormulaArg(token efp.Token) formulaArg {\n\tswitch token.TSubType {\n\tcase efp.TokenSubTypeLogical:\n\t\treturn newBoolFormulaArg(strings.EqualFold(token.TValue, \"TRUE\"))\n\tcase efp.TokenSubTypeNumber:\n\t\tnum, _ := strconv.ParseFloat(token.TValue, 64)\n\t\treturn newNumberFormulaArg(num)\n\tdefault:\n\t\treturn newStringFormulaArg(token.TValue)\n\t}\n}\n\n// formulaArgToToken create a token by given formula argument.\nfunc formulaArgToToken(arg formulaArg) efp.Token {\n\tswitch arg.Type {\n\tcase ArgNumber:\n\t\tif arg.Boolean {\n\t\t\treturn efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeLogical}\n\t\t}\n\t\treturn efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}\n\tdefault:\n\t\treturn efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeText}\n\t}\n}\n\n// parseToken parse basic arithmetic operator priority and evaluate based on\n// operators and operands.\nfunc (f *File) parseToken(ctx *calcContext, sheet string, token efp.Token, opdStack, optStack *Stack) error {\n\t// parse reference: must reference at here\n\tif token.TSubType == efp.TokenSubTypeRange {\n\t\trefTo := f.getDefinedNameRefTo(token.TValue, sheet)\n\t\tif refTo != \"\" {\n\t\t\ttoken.TValue = refTo\n\t\t}\n\t\tresult, err := f.parseReference(ctx, sheet, token.TValue)\n\t\tif err != nil {\n\t\t\treturn errors.New(formulaErrorNAME)\n\t\t}\n\t\ttoken = formulaArgToToken(result)\n\t}\n\tif isOperatorPrefixToken(token) {\n\t\tif err := f.parseOperatorPrefixToken(optStack, opdStack, token); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif isBeginParenthesesToken(token) { // (\n\t\toptStack.Push(token)\n\t}\n\tif isEndParenthesesToken(token) { // )\n\t\tfor !isBeginParenthesesToken(optStack.Peek().(efp.Token)) { // != (\n\t\t\ttopOpt := optStack.Peek().(efp.Token)\n\t\t\tif err := calculate(opdStack, topOpt); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\toptStack.Pop()\n\t\t}\n\t\toptStack.Pop()\n\t}\n\tif token.TType == efp.TokenTypeOperatorPostfix && !opdStack.Empty() {\n\t\ttopOpd := opdStack.Pop().(formulaArg)\n\t\topdStack.Push(newNumberFormulaArg(topOpd.Number / 100))\n\t}\n\t// opd\n\tif isOperand(token) {\n\t\topdStack.Push(tokenToFormulaArg(token))\n\t}\n\treturn nil\n}\n\n// parseRef parse reference for a cell, column name or row number.\nfunc parseRef(ref string) (cellRef, bool, bool, error) {\n\tvar (\n\t\terr, colErr, rowErr error\n\t\tcr                  cellRef\n\t\tcell                = ref\n\t\ttokens              = strings.Split(ref, \"!\")\n\t)\n\tif len(tokens) == 2 { // have a worksheet\n\t\tcr.Sheet, cell = strings.TrimSuffix(strings.TrimPrefix(tokens[0], \"'\"), \"'\"), tokens[1]\n\t}\n\tif cr.Col, cr.Row, err = CellNameToCoordinates(cell); err != nil {\n\t\tif cr.Col, colErr = ColumnNameToNumber(cell); colErr == nil { // cast to column\n\t\t\treturn cr, true, false, nil\n\t\t}\n\t\tif cr.Row, rowErr = strconv.Atoi(cell); rowErr == nil { // cast to row\n\t\t\tif cr.Row < 1 || cr.Row > TotalRows {\n\t\t\t\treturn cr, false, false, err\n\t\t\t}\n\t\t\treturn cr, false, true, nil\n\t\t}\n\t\treturn cr, false, false, err\n\t}\n\treturn cr, false, false, err\n}\n\n// prepareCellRange checking and convert cell reference to a cell range.\nfunc (cr *cellRange) prepareCellRange(col, row bool, cellRef cellRef) error {\n\tif col {\n\t\tcellRef.Row = TotalRows\n\t}\n\tif row {\n\t\tcellRef.Col = MaxColumns\n\t}\n\tif cellRef.Sheet == \"\" {\n\t\tcellRef.Sheet = cr.From.Sheet\n\t}\n\tif cr.From.Sheet != cellRef.Sheet || cr.To.Sheet != cellRef.Sheet {\n\t\treturn errors.New(\"invalid reference\")\n\t}\n\tif cr.From.Col > cellRef.Col {\n\t\tcr.From.Col = cellRef.Col\n\t}\n\tif cr.From.Row > cellRef.Row {\n\t\tcr.From.Row = cellRef.Row\n\t}\n\tif cr.To.Col < cellRef.Col {\n\t\tcr.To.Col = cellRef.Col\n\t}\n\tif cr.To.Row < cellRef.Row {\n\t\tcr.To.Row = cellRef.Row\n\t}\n\treturn nil\n}\n\n// parseReference parse reference and extract values by given reference\n// characters and default sheet name.\nfunc (f *File) parseReference(ctx *calcContext, sheet, reference string) (formulaArg, error) {\n\treference = strings.ReplaceAll(reference, \"$\", \"\")\n\tranges, cellRanges, cellRefs := strings.Split(reference, \":\"), list.New(), list.New()\n\tif len(ranges) > 1 {\n\t\tvar cr cellRange\n\t\tfor i, ref := range ranges {\n\t\t\tcellRef, col, row, err := parseRef(ref)\n\t\t\tif err != nil {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorNAME, \"invalid reference\"), errors.New(\"invalid reference\")\n\t\t\t}\n\t\t\tif i == 0 {\n\t\t\t\tif col {\n\t\t\t\t\tcellRef.Row = 1\n\t\t\t\t}\n\t\t\t\tif row {\n\t\t\t\t\tcellRef.Col = 1\n\t\t\t\t}\n\t\t\t\tif cellRef.Sheet == \"\" {\n\t\t\t\t\tcellRef.Sheet = sheet\n\t\t\t\t}\n\t\t\t\tcr.From, cr.To = cellRef, cellRef\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err := cr.prepareCellRange(col, row, cellRef); err != nil {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorNAME, err.Error()), err\n\t\t\t}\n\t\t}\n\t\tcellRanges.PushBack(cr)\n\t\treturn f.rangeResolver(ctx, cellRefs, cellRanges)\n\t}\n\tcellRef, _, _, err := parseRef(reference)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNAME, \"invalid reference\"), errors.New(\"invalid reference\")\n\t}\n\tif cellRef.Sheet == \"\" {\n\t\tcellRef.Sheet = sheet\n\t}\n\tcellRefs.PushBack(cellRef)\n\treturn f.rangeResolver(ctx, cellRefs, cellRanges)\n}\n\n// prepareValueRange prepare value range.\nfunc prepareValueRange(cr cellRange, valueRange []int) {\n\tif cr.From.Row < valueRange[0] || valueRange[0] == 0 {\n\t\tvalueRange[0] = cr.From.Row\n\t}\n\tif cr.From.Col < valueRange[2] || valueRange[2] == 0 {\n\t\tvalueRange[2] = cr.From.Col\n\t}\n\tif cr.To.Row > valueRange[1] || valueRange[1] == 0 {\n\t\tvalueRange[1] = cr.To.Row\n\t}\n\tif cr.To.Col > valueRange[3] || valueRange[3] == 0 {\n\t\tvalueRange[3] = cr.To.Col\n\t}\n}\n\n// prepareValueRef prepare value reference.\nfunc prepareValueRef(cr cellRef, valueRange []int) {\n\tif cr.Row < valueRange[0] || valueRange[0] == 0 {\n\t\tvalueRange[0] = cr.Row\n\t}\n\tif cr.Col < valueRange[2] || valueRange[2] == 0 {\n\t\tvalueRange[2] = cr.Col\n\t}\n\tif cr.Row > valueRange[1] || valueRange[1] == 0 {\n\t\tvalueRange[1] = cr.Row\n\t}\n\tif cr.Col > valueRange[3] || valueRange[3] == 0 {\n\t\tvalueRange[3] = cr.Col\n\t}\n}\n\n// cellResolver calc cell value by given worksheet name, cell reference and context.\nfunc (f *File) cellResolver(ctx *calcContext, sheet, cell string) (formulaArg, error) {\n\tvar (\n\t\targ   formulaArg\n\t\tvalue string\n\t\terr   error\n\t)\n\tref := sheet + \"!\" + cell\n\tif cached, ok := f.formulaArgCache.Load(ref); ok {\n\t\treturn cached.(formulaArg), err\n\t}\n\n\tif formula, _ := f.getCellFormula(sheet, cell, true); len(formula) != 0 {\n\t\tctx.mu.Lock()\n\t\tif ctx.entry != ref {\n\t\t\tif ctx.iterations[ref] <= f.options.MaxCalcIterations {\n\t\t\t\tctx.iterations[ref]++\n\t\t\t\tctx.mu.Unlock()\n\t\t\t\targ, _ = f.calcCellValue(ctx, sheet, cell)\n\t\t\t\tctx.iterationsCache[ref] = arg\n\t\t\t\tf.formulaArgCache.Store(ref, arg)\n\t\t\t\treturn arg, nil\n\t\t\t}\n\t\t\tctx.mu.Unlock()\n\t\t\treturn ctx.iterationsCache[ref], nil\n\t\t}\n\t\tctx.mu.Unlock()\n\t}\n\tif value, err = f.GetCellValue(sheet, cell, Options{RawCellValue: true}); err != nil {\n\t\treturn arg, err\n\t}\n\targ = newStringFormulaArg(value)\n\tcellType, _ := f.GetCellType(sheet, cell)\n\tswitch cellType {\n\tcase CellTypeBool:\n\t\targ = arg.ToBool()\n\tcase CellTypeNumber, CellTypeUnset:\n\t\tif arg.Value() == \"\" {\n\t\t\targ = newEmptyFormulaArg()\n\t\t} else {\n\t\t\targ = arg.ToNumber()\n\t\t}\n\tcase CellTypeInlineString, CellTypeSharedString:\n\tcase CellTypeFormula:\n\t\tif value == \"\" {\n\t\t\targ = newEmptyFormulaArg()\n\t\t}\n\tcase CellTypeDate:\n\t\tif value, err = f.GetCellValue(sheet, cell); err == nil {\n\t\t\tif num := newStringFormulaArg(value).ToNumber(); num.Type == ArgNumber {\n\t\t\t\targ = num\n\t\t\t}\n\t\t}\n\tdefault:\n\t\targ = newErrorFormulaArg(value, value)\n\t}\n\tf.formulaArgCache.Store(ref, arg)\n\treturn arg, err\n}\n\n// rangeResolver extract value as string from given reference and range list.\n// This function will not ignore the empty cell. For example, A1:A2:A2:B3 will\n// be reference A1:B3.\nfunc (f *File) rangeResolver(ctx *calcContext, cellRefs, cellRanges *list.List) (arg formulaArg, err error) {\n\targ.cellRefs, arg.cellRanges = cellRefs, cellRanges\n\t// value range order: from row, to row, from column, to column\n\tvalueRange := []int{0, 0, 0, 0}\n\tvar sheet string\n\t// prepare value range\n\tfor temp := cellRanges.Front(); temp != nil; temp = temp.Next() {\n\t\tcr := temp.Value.(cellRange)\n\t\trng := []int{cr.From.Col, cr.From.Row, cr.To.Col, cr.To.Row}\n\t\t_ = sortCoordinates(rng)\n\t\tcr.From.Col, cr.From.Row, cr.To.Col, cr.To.Row = rng[0], rng[1], rng[2], rng[3]\n\t\tprepareValueRange(cr, valueRange)\n\t\tif cr.From.Sheet != \"\" {\n\t\t\tsheet = cr.From.Sheet\n\t\t}\n\t}\n\tfor temp := cellRefs.Front(); temp != nil; temp = temp.Next() {\n\t\tcr := temp.Value.(cellRef)\n\t\tif cr.Sheet != \"\" {\n\t\t\tsheet = cr.Sheet\n\t\t}\n\t\tprepareValueRef(cr, valueRange)\n\t}\n\t// extract value from ranges\n\tif cellRanges.Len() > 0 {\n\t\targ.Type = ArgMatrix\n\n\t\tvar ws *xlsxWorksheet\n\t\tws, err = f.workSheetReader(sheet)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t// Detect whole column/row reference, limit to actual data range\n\t\tif valueRange[1] == TotalRows {\n\t\t\tactualMaxRow := 0\n\t\t\tfor _, rowData := range ws.SheetData.Row {\n\t\t\t\tif rowData.R > actualMaxRow {\n\t\t\t\t\tactualMaxRow = rowData.R\n\t\t\t\t}\n\t\t\t}\n\t\t\tif actualMaxRow > 0 && actualMaxRow < TotalRows {\n\t\t\t\tvalueRange[1] = actualMaxRow\n\t\t\t}\n\t\t}\n\t\tif valueRange[3] == MaxColumns {\n\t\t\tactualMaxCol := 0\n\t\t\tfor _, rowData := range ws.SheetData.Row {\n\t\t\t\tfor _, cell := range rowData.C {\n\t\t\t\t\tcol, _, err := CellNameToCoordinates(cell.R)\n\t\t\t\t\tif err == nil && col > actualMaxCol {\n\t\t\t\t\t\tactualMaxCol = col\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif actualMaxCol > 0 && actualMaxCol < MaxColumns {\n\t\t\t\tvalueRange[3] = actualMaxCol\n\t\t\t}\n\t\t}\n\n\t\tfor row := valueRange[0]; row <= valueRange[1]; row++ {\n\t\t\tcolMax := 0\n\t\t\tif 0 < row && row <= len(ws.SheetData.Row) {\n\t\t\t\trowData := &ws.SheetData.Row[row-1]\n\t\t\t\tcolMax = min(valueRange[3], len(rowData.C))\n\t\t\t}\n\t\t\tvar matrixRow []formulaArg\n\t\t\tfor col := valueRange[2]; col <= valueRange[3]; col++ {\n\t\t\t\tvalue := newEmptyFormulaArg()\n\t\t\t\tif col <= colMax {\n\t\t\t\t\tvar cell string\n\t\t\t\t\tif cell, err = CoordinatesToCellName(col, row); err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif value, err = f.cellResolver(ctx, sheet, cell); err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmatrixRow = append(matrixRow, value)\n\t\t\t}\n\t\t\targ.Matrix = append(arg.Matrix, matrixRow)\n\t\t}\n\t\treturn\n\t}\n\t// extract value from references\n\tfor temp := cellRefs.Front(); temp != nil; temp = temp.Next() {\n\t\tcr := temp.Value.(cellRef)\n\t\tvar cell string\n\t\tif cell, err = CoordinatesToCellName(cr.Col, cr.Row); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif arg, err = f.cellResolver(ctx, cr.Sheet, cell); err != nil {\n\t\t\treturn\n\t\t}\n\t\targ.cellRefs, arg.cellRanges = cellRefs, cellRanges\n\t}\n\treturn\n}\n\n// callFuncByName calls the no error or only error return function with\n// reflect by given receiver, name and parameters.\nfunc callFuncByName(receiver interface{}, name string, params []reflect.Value) (arg formulaArg) {\n\tfunction := reflect.ValueOf(receiver).MethodByName(name)\n\tif function.IsValid() {\n\t\trt := function.Call(params)\n\t\tif len(rt) == 0 {\n\t\t\treturn\n\t\t}\n\t\targ = rt[0].Interface().(formulaArg)\n\t\treturn\n\t}\n\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"not support %s function\", name))\n}\n\n// formulaCriteriaParser parse formula criteria.\nfunc formulaCriteriaParser(exp formulaArg) *formulaCriteria {\n\tprepareValue := func(cond string) (expected float64, err error) {\n\t\tpercentile := 1.0\n\t\tif strings.HasSuffix(cond, \"%\") {\n\t\t\tcond = strings.TrimSuffix(cond, \"%\")\n\t\t\tpercentile /= 100\n\t\t}\n\t\tif expected, err = strconv.ParseFloat(cond, 64); err != nil {\n\t\t\treturn\n\t\t}\n\t\texpected *= percentile\n\t\treturn\n\t}\n\tfc, val := &formulaCriteria{}, exp.Value()\n\tif val == \"\" {\n\t\treturn fc\n\t}\n\tfor i, re := range formulaFormats {\n\t\tif match := re.FindStringSubmatch(val); len(match) > 1 {\n\t\t\tfc.Condition = newStringFormulaArg(match[1])\n\t\t\tif num, err := prepareValue(match[1]); err == nil {\n\t\t\t\tfc.Condition = newNumberFormulaArg(num)\n\t\t\t}\n\t\t\tfc.Type = formulaCriterias[i]\n\t\t\treturn fc\n\t\t}\n\t}\n\thasWildcard := false\n\tpattern := wildcardTokenRE.ReplaceAllStringFunc(val, func(m string) string {\n\t\thasWildcard = hasWildcard || wildcardBareTokens[m]\n\t\tif r, ok := wildcardPatternMap[m]; ok {\n\t\t\treturn r\n\t\t}\n\t\treturn regexp.QuoteMeta(m)\n\t})\n\tif hasWildcard {\n\t\tfc.Type, fc.Condition = criteriaRegexp, newStringFormulaArg(\"(?i)^\"+pattern+\"$\")\n\t\treturn fc\n\t}\n\tfc.Type, fc.Condition = criteriaEq, newStringFormulaArg(\n\t\tstrings.NewReplacer(\"~~\", \"~\", \"~*\", \"*\", \"~?\", \"?\").Replace(val),\n\t)\n\tif num := fc.Condition.ToNumber(); num.Type == ArgNumber {\n\t\tfc.Condition = num\n\t}\n\treturn fc\n}\n\n// formulaCriteriaEval evaluate formula criteria expression.\nfunc formulaCriteriaEval(val formulaArg, criteria *formulaCriteria) (result bool, err error) {\n\ts := NewStack()\n\ttokenCalcFunc := map[byte]func(rOpd, lOpd formulaArg, opdStack *Stack) error{\n\t\tcriteriaEq: calcEq,\n\t\tcriteriaNe: calcNEq,\n\t\tcriteriaL:  calcL,\n\t\tcriteriaLe: calcLe,\n\t\tcriteriaG:  calcG,\n\t\tcriteriaGe: calcGe,\n\t}\n\tswitch criteria.Type {\n\tcase criteriaEq, criteriaLe, criteriaGe, criteriaNe, criteriaL, criteriaG:\n\t\tif fn, ok := tokenCalcFunc[criteria.Type]; ok {\n\t\t\tif _ = fn(criteria.Condition, val, s); s.Len() > 0 {\n\t\t\t\treturn s.Pop().(formulaArg).Number == 1, err\n\t\t\t}\n\t\t}\n\tcase criteriaRegexp:\n\t\tpattern := criteria.Condition.Value()\n\t\tif !strings.HasPrefix(pattern, \"^\") {\n\t\t\tpattern = \"^\" + pattern\n\t\t}\n\t\tif !strings.HasSuffix(pattern, \"$\") {\n\t\t\tpattern = pattern + \"$\"\n\t\t}\n\t\treturn regexp.MatchString(pattern, val.Value())\n\t}\n\treturn\n}\n\n// Engineering Functions\n\n// BESSELI function the modified Bessel function, which is equivalent to the\n// Bessel function evaluated for purely imaginary arguments. The syntax of\n// the Besseli function is:\n//\n//\tBESSELI(x,n)\nfunc (fn *formulaFuncs) BESSELI(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BESSELI requires 2 numeric arguments\")\n\t}\n\treturn fn.bassel(argsList, true)\n}\n\n// BESSELJ function returns the Bessel function, Jn(x), for a specified order\n// and value of x. The syntax of the function is:\n//\n//\tBESSELJ(x,n)\nfunc (fn *formulaFuncs) BESSELJ(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BESSELJ requires 2 numeric arguments\")\n\t}\n\treturn fn.bassel(argsList, false)\n}\n\n// bassel is an implementation of the formula functions BESSELI and BESSELJ.\nfunc (fn *formulaFuncs) bassel(argsList *list.List, modfied bool) formulaArg {\n\tx, n := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Back().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif n.Type != ArgNumber {\n\t\treturn n\n\t}\n\tmaxVal, x1 := 100, x.Number*0.5\n\tx2 := x1 * x1\n\tx1 = math.Pow(x1, n.Number)\n\tn1, n2, n3, n4, add := fact(n.Number), 1.0, 0.0, n.Number, false\n\tresult := x1 / n1\n\tt := result * 0.9\n\tfor result != t && maxVal != 0 {\n\t\tx1 *= x2\n\t\tn3++\n\t\tn1 *= n3\n\t\tn4++\n\t\tn2 *= n4\n\t\tt = result\n\t\tr := x1 / n1 / n2\n\t\tif modfied || add {\n\t\t\tresult += r\n\t\t} else {\n\t\t\tresult -= r\n\t\t}\n\t\tmaxVal--\n\t\tadd = !add\n\t}\n\treturn newNumberFormulaArg(result)\n}\n\n// BESSELK function calculates the modified Bessel functions, Kn(x), which are\n// also known as the hyperbolic Bessel Functions. These are the equivalent of\n// the Bessel functions, evaluated for purely imaginary arguments. The syntax\n// of the function is:\n//\n//\tBESSELK(x,n)\nfunc (fn *formulaFuncs) BESSELK(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BESSELK requires 2 numeric arguments\")\n\t}\n\tx, n := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Back().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif n.Type != ArgNumber {\n\t\treturn n\n\t}\n\tif x.Number <= 0 || n.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tvar result float64\n\tswitch math.Floor(n.Number) {\n\tcase 0:\n\t\tresult = fn.besselK0(x)\n\tcase 1:\n\t\tresult = fn.besselK1(x)\n\tdefault:\n\t\tresult = fn.besselK2(x, n)\n\t}\n\treturn newNumberFormulaArg(result)\n}\n\n// besselK0 is an implementation of the formula function BESSELK.\nfunc (fn *formulaFuncs) besselK0(x formulaArg) float64 {\n\tvar y float64\n\tif x.Number <= 2 {\n\t\tn2 := x.Number * 0.5\n\t\ty = n2 * n2\n\t\targs := list.New()\n\t\targs.PushBack(x)\n\t\targs.PushBack(newNumberFormulaArg(0))\n\t\treturn -math.Log(n2)*fn.BESSELI(args).Number +\n\t\t\t(-0.57721566 + y*(0.42278420+y*(0.23069756+y*(0.3488590e-1+y*(0.262698e-2+y*\n\t\t\t\t(0.10750e-3+y*0.74e-5))))))\n\t}\n\ty = 2 / x.Number\n\treturn math.Exp(-x.Number) / math.Sqrt(x.Number) *\n\t\t(1.25331414 + y*(-0.7832358e-1+y*(0.2189568e-1+y*(-0.1062446e-1+y*\n\t\t\t(0.587872e-2+y*(-0.251540e-2+y*0.53208e-3))))))\n}\n\n// besselK1 is an implementation of the formula function BESSELK.\nfunc (fn *formulaFuncs) besselK1(x formulaArg) float64 {\n\tvar n2, y float64\n\tif x.Number <= 2 {\n\t\tn2 = x.Number * 0.5\n\t\ty = n2 * n2\n\t\targs := list.New()\n\t\targs.PushBack(x)\n\t\targs.PushBack(newNumberFormulaArg(1))\n\t\treturn math.Log(n2)*fn.BESSELI(args).Number +\n\t\t\t(1+y*(0.15443144+y*(-0.67278579+y*(-0.18156897+y*(-0.1919402e-1+y*(-0.110404e-2+y*(-0.4686e-4)))))))/x.Number\n\t}\n\ty = 2 / x.Number\n\treturn math.Exp(-x.Number) / math.Sqrt(x.Number) *\n\t\t(1.25331414 + y*(0.23498619+y*(-0.3655620e-1+y*(0.1504268e-1+y*(-0.780353e-2+y*\n\t\t\t(0.325614e-2+y*(-0.68245e-3)))))))\n}\n\n// besselK2 is an implementation of the formula function BESSELK.\nfunc (fn *formulaFuncs) besselK2(x, n formulaArg) float64 {\n\ttox, bkm, bk, bkp := 2/x.Number, fn.besselK0(x), fn.besselK1(x), 0.0\n\tfor i := 1.0; i < n.Number; i++ {\n\t\tbkp = math.FMA(i*tox, bk, bkm)\n\t\tbkm = bk\n\t\tbk = bkp\n\t}\n\treturn bk\n}\n\n// BESSELY function returns the Bessel function, Yn(x), (also known as the\n// Weber function or the Neumann function), for a specified order and value\n// of x. The syntax of the function is:\n//\n//\tBESSELY(x,n)\nfunc (fn *formulaFuncs) BESSELY(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BESSELY requires 2 numeric arguments\")\n\t}\n\tx, n := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Back().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif n.Type != ArgNumber {\n\t\treturn n\n\t}\n\tif x.Number <= 0 || n.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tvar result float64\n\tswitch math.Floor(n.Number) {\n\tcase 0:\n\t\tresult = fn.besselY0(x)\n\tcase 1:\n\t\tresult = fn.besselY1(x)\n\tdefault:\n\t\tresult = fn.besselY2(x, n)\n\t}\n\treturn newNumberFormulaArg(result)\n}\n\n// besselY0 is an implementation of the formula function BESSELY.\nfunc (fn *formulaFuncs) besselY0(x formulaArg) float64 {\n\tvar y float64\n\tif x.Number < 8 {\n\t\ty = x.Number * x.Number\n\t\tf1 := -2957821389.0 + y*(7062834065.0+y*(-512359803.6+y*(10879881.29+y*\n\t\t\t(-86327.92757+y*228.4622733))))\n\t\tf2 := 40076544269.0 + y*(745249964.8+y*(7189466.438+y*\n\t\t\t(47447.26470+y*(226.1030244+y))))\n\t\targs := list.New()\n\t\targs.PushBack(x)\n\t\targs.PushBack(newNumberFormulaArg(0))\n\t\treturn f1/f2 + 0.636619772*fn.BESSELJ(args).Number*math.Log(x.Number)\n\t}\n\tz := 8.0 / x.Number\n\ty = z * z\n\txx := x.Number - 0.785398164\n\tf1 := 1 + y*(-0.1098628627e-2+y*(0.2734510407e-4+y*(-0.2073370639e-5+y*0.2093887211e-6)))\n\tf2 := -0.1562499995e-1 + y*(0.1430488765e-3+y*(-0.6911147651e-5+y*(0.7621095161e-6+y*\n\t\t(-0.934945152e-7))))\n\treturn math.Sqrt(0.636619772/x.Number) * (math.Sin(xx)*f1 + z*math.Cos(xx)*f2)\n}\n\n// besselY1 is an implementation of the formula function BESSELY.\nfunc (fn *formulaFuncs) besselY1(x formulaArg) float64 {\n\tif x.Number < 8 {\n\t\ty := x.Number * x.Number\n\t\tf1 := x.Number * (-0.4900604943e13 + y*(0.1275274390e13+y*(-0.5153438139e11+y*\n\t\t\t(0.7349264551e9+y*(-0.4237922726e7+y*0.8511937935e4)))))\n\t\tf2 := 0.2499580570e14 + y*(0.4244419664e12+y*(0.3733650367e10+y*(0.2245904002e8+y*\n\t\t\t(0.1020426050e6+y*(0.3549632885e3+y)))))\n\t\targs := list.New()\n\t\targs.PushBack(x)\n\t\targs.PushBack(newNumberFormulaArg(1))\n\t\treturn f1/f2 + 0.636619772*(fn.BESSELJ(args).Number*math.Log(x.Number)-1/x.Number)\n\t}\n\treturn math.Sqrt(0.636619772/x.Number) * math.Sin(x.Number-2.356194491)\n}\n\n// besselY2 is an implementation of the formula function BESSELY.\nfunc (fn *formulaFuncs) besselY2(x, n formulaArg) float64 {\n\ttox, bym, by, byp := 2/x.Number, fn.besselY0(x), fn.besselY1(x), 0.0\n\tfor i := 1.0; i < n.Number; i++ {\n\t\tbyp = math.FMA(i*tox, by, -bym)\n\t\tbym = by\n\t\tby = byp\n\t}\n\treturn by\n}\n\n// BIN2DEC function converts a Binary (a base-2 number) into a decimal number.\n// The syntax of the function is:\n//\n//\tBIN2DEC(number)\nfunc (fn *formulaFuncs) BIN2DEC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BIN2DEC requires 1 numeric argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tnumber := token.ToNumber()\n\tif number.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, number.Error)\n\t}\n\treturn fn.bin2dec(token.Value())\n}\n\n// BIN2HEX function converts a Binary (Base 2) number into a Hexadecimal\n// (Base 16) number. The syntax of the function is:\n//\n//\tBIN2HEX(number,[places])\nfunc (fn *formulaFuncs) BIN2HEX(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BIN2HEX requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BIN2HEX allows at most 2 arguments\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tnumber := token.ToNumber()\n\tif number.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, number.Error)\n\t}\n\tdecimal, newList := fn.bin2dec(token.Value()), list.New()\n\tif decimal.Type != ArgNumber {\n\t\treturn decimal\n\t}\n\tnewList.PushBack(decimal)\n\tif argsList.Len() == 2 {\n\t\tnewList.PushBack(argsList.Back().Value.(formulaArg))\n\t}\n\treturn fn.dec2x(\"BIN2HEX\", newList)\n}\n\n// BIN2OCT function converts a Binary (Base 2) number into an Octal (Base 8)\n// number. The syntax of the function is:\n//\n//\tBIN2OCT(number,[places])\nfunc (fn *formulaFuncs) BIN2OCT(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BIN2OCT requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BIN2OCT allows at most 2 arguments\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tnumber := token.ToNumber()\n\tif number.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, number.Error)\n\t}\n\tdecimal, newList := fn.bin2dec(token.Value()), list.New()\n\tif decimal.Type != ArgNumber {\n\t\treturn decimal\n\t}\n\tnewList.PushBack(decimal)\n\tif argsList.Len() == 2 {\n\t\tnewList.PushBack(argsList.Back().Value.(formulaArg))\n\t}\n\treturn fn.dec2x(\"BIN2OCT\", newList)\n}\n\n// bin2dec is an implementation of the formula function BIN2DEC.\nfunc (fn *formulaFuncs) bin2dec(number string) formulaArg {\n\tdecimal, length := 0.0, len(number)\n\tfor i := length; i > 0; i-- {\n\t\ts := string(number[length-i])\n\t\tif i == 10 && s == \"1\" {\n\t\t\tdecimal += math.Pow(-2.0, float64(i-1))\n\t\t\tcontinue\n\t\t}\n\t\tif s == \"1\" {\n\t\t\tdecimal += math.Pow(2.0, float64(i-1))\n\t\t\tcontinue\n\t\t}\n\t\tif s != \"0\" {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\treturn newNumberFormulaArg(decimal)\n}\n\n// BITAND function returns the bitwise 'AND' for two supplied integers. The\n// syntax of the function is:\n//\n//\tBITAND(number1,number2)\nfunc (fn *formulaFuncs) BITAND(argsList *list.List) formulaArg {\n\treturn fn.bitwise(\"BITAND\", argsList)\n}\n\n// BITLSHIFT function returns a supplied integer, shifted left by a specified\n// number of bits. The syntax of the function is:\n//\n//\tBITLSHIFT(number1,shift_amount)\nfunc (fn *formulaFuncs) BITLSHIFT(argsList *list.List) formulaArg {\n\treturn fn.bitwise(\"BITLSHIFT\", argsList)\n}\n\n// BITOR function returns the bitwise 'OR' for two supplied integers. The\n// syntax of the function is:\n//\n//\tBITOR(number1,number2)\nfunc (fn *formulaFuncs) BITOR(argsList *list.List) formulaArg {\n\treturn fn.bitwise(\"BITOR\", argsList)\n}\n\n// BITRSHIFT function returns a supplied integer, shifted right by a specified\n// number of bits. The syntax of the function is:\n//\n//\tBITRSHIFT(number1,shift_amount)\nfunc (fn *formulaFuncs) BITRSHIFT(argsList *list.List) formulaArg {\n\treturn fn.bitwise(\"BITRSHIFT\", argsList)\n}\n\n// BITXOR function returns the bitwise 'XOR' (exclusive 'OR') for two supplied\n// integers. The syntax of the function is:\n//\n//\tBITXOR(number1,number2)\nfunc (fn *formulaFuncs) BITXOR(argsList *list.List) formulaArg {\n\treturn fn.bitwise(\"BITXOR\", argsList)\n}\n\n// bitwise is an implementation of the formula functions BITAND, BITLSHIFT,\n// BITOR, BITRSHIFT and BITXOR.\nfunc (fn *formulaFuncs) bitwise(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 2 numeric arguments\", name))\n\t}\n\tnum1, num2 := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Back().Value.(formulaArg).ToNumber()\n\tif num1.Type != ArgNumber || num2.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tmaxVal := math.Pow(2, 48) - 1\n\tif num1.Number < 0 || num1.Number > maxVal || num2.Number < 0 || num2.Number > maxVal {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tbitwiseFuncMap := map[string]func(a, b int) int{\n\t\t\"BITAND\":    func(a, b int) int { return a & b },\n\t\t\"BITLSHIFT\": func(a, b int) int { return a << uint(b) },\n\t\t\"BITOR\":     func(a, b int) int { return a | b },\n\t\t\"BITRSHIFT\": func(a, b int) int { return a >> uint(b) },\n\t\t\"BITXOR\":    func(a, b int) int { return a ^ b },\n\t}\n\tbitwiseFunc := bitwiseFuncMap[name]\n\treturn newNumberFormulaArg(float64(bitwiseFunc(int(num1.Number), int(num2.Number))))\n}\n\n// COMPLEX function takes two arguments, representing the real and the\n// imaginary coefficients of a complex number, and from these, creates a\n// complex number. The syntax of the function is:\n//\n//\tCOMPLEX(real_num,i_num,[suffix])\nfunc (fn *formulaFuncs) COMPLEX(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COMPLEX requires at least 2 arguments\")\n\t}\n\tif argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COMPLEX allows at most 3 arguments\")\n\t}\n\trealNum, i, suffix := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Front().Next().Value.(formulaArg).ToNumber(), \"i\"\n\tif realNum.Type != ArgNumber {\n\t\treturn realNum\n\t}\n\tif i.Type != ArgNumber {\n\t\treturn i\n\t}\n\tif argsList.Len() == 3 {\n\t\tif suffix = strings.ToLower(argsList.Back().Value.(formulaArg).Value()); suffix != \"i\" && suffix != \"j\" {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t}\n\treturn newStringFormulaArg(cmplx2str(complex(realNum.Number, i.Number), suffix))\n}\n\n// cmplx2str replace complex number string characters.\nfunc cmplx2str(num complex128, suffix string) string {\n\trealPart, imagPart := fmt.Sprint(real(num)), fmt.Sprint(imag(num))\n\tisNum, i, decimal := isNumeric(realPart)\n\tif isNum && i > 15 {\n\t\trealPart = strconv.FormatFloat(decimal, 'G', 15, 64)\n\t}\n\tisNum, i, decimal = isNumeric(imagPart)\n\tif isNum && i > 15 {\n\t\timagPart = strconv.FormatFloat(decimal, 'G', 15, 64)\n\t}\n\tc := realPart\n\tif imag(num) > 0 {\n\t\tc += \"+\"\n\t}\n\tif imag(num) != 0 {\n\t\tc += imagPart + \"i\"\n\t}\n\tc = strings.TrimPrefix(c, \"(\")\n\tc = strings.TrimPrefix(c, \"+0+\")\n\tc = strings.TrimPrefix(c, \"-0+\")\n\tc = strings.TrimSuffix(c, \")\")\n\tc = strings.TrimPrefix(c, \"0+\")\n\tif strings.HasPrefix(c, \"0-\") {\n\t\tc = \"-\" + strings.TrimPrefix(c, \"0-\")\n\t}\n\tc = strings.TrimPrefix(c, \"0+\")\n\tc = strings.TrimSuffix(c, \"+0i\")\n\tc = strings.TrimSuffix(c, \"-0i\")\n\tc = strings.NewReplacer(\"+1i\", \"+i\", \"-1i\", \"-i\").Replace(c)\n\tc = strings.ReplaceAll(c, \"i\", suffix)\n\treturn c\n}\n\n// str2cmplx convert complex number string characters.\nfunc str2cmplx(c string) string {\n\tc = strings.ReplaceAll(c, \"j\", \"i\")\n\tif c == \"i\" {\n\t\tc = \"1i\"\n\t}\n\tc = strings.NewReplacer(\"+i\", \"+1i\", \"-i\", \"-1i\").Replace(c)\n\treturn c\n}\n\n// conversionUnit defined unit info for conversion.\ntype conversionUnit struct {\n\tgroup       uint8\n\tallowPrefix bool\n}\n\n// conversionUnits maps info list for unit conversion, that can be used in\n// formula function CONVERT.\nvar conversionUnits = map[string]conversionUnit{\n\t// weight and mass\n\t\"g\":        {group: categoryWeightAndMass, allowPrefix: true},\n\t\"sg\":       {group: categoryWeightAndMass, allowPrefix: false},\n\t\"lbm\":      {group: categoryWeightAndMass, allowPrefix: false},\n\t\"u\":        {group: categoryWeightAndMass, allowPrefix: true},\n\t\"ozm\":      {group: categoryWeightAndMass, allowPrefix: false},\n\t\"grain\":    {group: categoryWeightAndMass, allowPrefix: false},\n\t\"cwt\":      {group: categoryWeightAndMass, allowPrefix: false},\n\t\"shweight\": {group: categoryWeightAndMass, allowPrefix: false},\n\t\"uk_cwt\":   {group: categoryWeightAndMass, allowPrefix: false},\n\t\"lcwt\":     {group: categoryWeightAndMass, allowPrefix: false},\n\t\"hweight\":  {group: categoryWeightAndMass, allowPrefix: false},\n\t\"stone\":    {group: categoryWeightAndMass, allowPrefix: false},\n\t\"ton\":      {group: categoryWeightAndMass, allowPrefix: false},\n\t\"uk_ton\":   {group: categoryWeightAndMass, allowPrefix: false},\n\t\"LTON\":     {group: categoryWeightAndMass, allowPrefix: false},\n\t\"brton\":    {group: categoryWeightAndMass, allowPrefix: false},\n\t// distance\n\t\"m\":         {group: categoryDistance, allowPrefix: true},\n\t\"mi\":        {group: categoryDistance, allowPrefix: false},\n\t\"Nmi\":       {group: categoryDistance, allowPrefix: false},\n\t\"in\":        {group: categoryDistance, allowPrefix: false},\n\t\"ft\":        {group: categoryDistance, allowPrefix: false},\n\t\"yd\":        {group: categoryDistance, allowPrefix: false},\n\t\"ang\":       {group: categoryDistance, allowPrefix: true},\n\t\"ell\":       {group: categoryDistance, allowPrefix: false},\n\t\"ly\":        {group: categoryDistance, allowPrefix: false},\n\t\"parsec\":    {group: categoryDistance, allowPrefix: false},\n\t\"pc\":        {group: categoryDistance, allowPrefix: false},\n\t\"Pica\":      {group: categoryDistance, allowPrefix: false},\n\t\"Picapt\":    {group: categoryDistance, allowPrefix: false},\n\t\"pica\":      {group: categoryDistance, allowPrefix: false},\n\t\"survey_mi\": {group: categoryDistance, allowPrefix: false},\n\t// time\n\t\"yr\":  {group: categoryTime, allowPrefix: false},\n\t\"day\": {group: categoryTime, allowPrefix: false},\n\t\"d\":   {group: categoryTime, allowPrefix: false},\n\t\"hr\":  {group: categoryTime, allowPrefix: false},\n\t\"mn\":  {group: categoryTime, allowPrefix: false},\n\t\"min\": {group: categoryTime, allowPrefix: false},\n\t\"sec\": {group: categoryTime, allowPrefix: true},\n\t\"s\":   {group: categoryTime, allowPrefix: true},\n\t// pressure\n\t\"Pa\":   {group: categoryPressure, allowPrefix: true},\n\t\"p\":    {group: categoryPressure, allowPrefix: true},\n\t\"atm\":  {group: categoryPressure, allowPrefix: true},\n\t\"at\":   {group: categoryPressure, allowPrefix: true},\n\t\"mmHg\": {group: categoryPressure, allowPrefix: true},\n\t\"psi\":  {group: categoryPressure, allowPrefix: true},\n\t\"Torr\": {group: categoryPressure, allowPrefix: true},\n\t// force\n\t\"N\":    {group: categoryForce, allowPrefix: true},\n\t\"dyn\":  {group: categoryForce, allowPrefix: true},\n\t\"dy\":   {group: categoryForce, allowPrefix: true},\n\t\"lbf\":  {group: categoryForce, allowPrefix: false},\n\t\"pond\": {group: categoryForce, allowPrefix: true},\n\t// energy\n\t\"J\":   {group: categoryEnergy, allowPrefix: true},\n\t\"e\":   {group: categoryEnergy, allowPrefix: true},\n\t\"c\":   {group: categoryEnergy, allowPrefix: true},\n\t\"cal\": {group: categoryEnergy, allowPrefix: true},\n\t\"eV\":  {group: categoryEnergy, allowPrefix: true},\n\t\"ev\":  {group: categoryEnergy, allowPrefix: true},\n\t\"HPh\": {group: categoryEnergy, allowPrefix: false},\n\t\"hh\":  {group: categoryEnergy, allowPrefix: false},\n\t\"Wh\":  {group: categoryEnergy, allowPrefix: true},\n\t\"wh\":  {group: categoryEnergy, allowPrefix: true},\n\t\"flb\": {group: categoryEnergy, allowPrefix: false},\n\t\"BTU\": {group: categoryEnergy, allowPrefix: false},\n\t\"btu\": {group: categoryEnergy, allowPrefix: false},\n\t// power\n\t\"HP\": {group: categoryPower, allowPrefix: false},\n\t\"h\":  {group: categoryPower, allowPrefix: false},\n\t\"W\":  {group: categoryPower, allowPrefix: true},\n\t\"w\":  {group: categoryPower, allowPrefix: true},\n\t\"PS\": {group: categoryPower, allowPrefix: false},\n\t\"T\":  {group: categoryMagnetism, allowPrefix: true},\n\t\"ga\": {group: categoryMagnetism, allowPrefix: true},\n\t// temperature\n\t\"C\":    {group: categoryTemperature, allowPrefix: false},\n\t\"cel\":  {group: categoryTemperature, allowPrefix: false},\n\t\"F\":    {group: categoryTemperature, allowPrefix: false},\n\t\"fah\":  {group: categoryTemperature, allowPrefix: false},\n\t\"K\":    {group: categoryTemperature, allowPrefix: false},\n\t\"kel\":  {group: categoryTemperature, allowPrefix: false},\n\t\"Rank\": {group: categoryTemperature, allowPrefix: false},\n\t\"Reau\": {group: categoryTemperature, allowPrefix: false},\n\t// volume\n\t\"l\":        {group: categoryVolumeAndLiquidMeasure, allowPrefix: true},\n\t\"L\":        {group: categoryVolumeAndLiquidMeasure, allowPrefix: true},\n\t\"lt\":       {group: categoryVolumeAndLiquidMeasure, allowPrefix: true},\n\t\"tsp\":      {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"tspm\":     {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"tbs\":      {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"oz\":       {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"cup\":      {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"pt\":       {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"us_pt\":    {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"uk_pt\":    {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"qt\":       {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"uk_qt\":    {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"gal\":      {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"uk_gal\":   {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"ang3\":     {group: categoryVolumeAndLiquidMeasure, allowPrefix: true},\n\t\"ang^3\":    {group: categoryVolumeAndLiquidMeasure, allowPrefix: true},\n\t\"barrel\":   {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"bushel\":   {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"in3\":      {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"in^3\":     {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"ft3\":      {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"ft^3\":     {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"ly3\":      {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"ly^3\":     {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"m3\":       {group: categoryVolumeAndLiquidMeasure, allowPrefix: true},\n\t\"m^3\":      {group: categoryVolumeAndLiquidMeasure, allowPrefix: true},\n\t\"mi3\":      {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"mi^3\":     {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"yd3\":      {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"yd^3\":     {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"Nmi3\":     {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"Nmi^3\":    {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"Pica3\":    {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"Pica^3\":   {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"Picapt3\":  {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"Picapt^3\": {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"GRT\":      {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"regton\":   {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t\"MTON\":     {group: categoryVolumeAndLiquidMeasure, allowPrefix: false},\n\t// area\n\t\"ha\":       {group: categoryArea, allowPrefix: true},\n\t\"uk_acre\":  {group: categoryArea, allowPrefix: false},\n\t\"us_acre\":  {group: categoryArea, allowPrefix: false},\n\t\"ang2\":     {group: categoryArea, allowPrefix: true},\n\t\"ang^2\":    {group: categoryArea, allowPrefix: true},\n\t\"ar\":       {group: categoryArea, allowPrefix: true},\n\t\"ft2\":      {group: categoryArea, allowPrefix: false},\n\t\"ft^2\":     {group: categoryArea, allowPrefix: false},\n\t\"in2\":      {group: categoryArea, allowPrefix: false},\n\t\"in^2\":     {group: categoryArea, allowPrefix: false},\n\t\"ly2\":      {group: categoryArea, allowPrefix: false},\n\t\"ly^2\":     {group: categoryArea, allowPrefix: false},\n\t\"m2\":       {group: categoryArea, allowPrefix: true},\n\t\"m^2\":      {group: categoryArea, allowPrefix: true},\n\t\"Morgen\":   {group: categoryArea, allowPrefix: false},\n\t\"mi2\":      {group: categoryArea, allowPrefix: false},\n\t\"mi^2\":     {group: categoryArea, allowPrefix: false},\n\t\"Nmi2\":     {group: categoryArea, allowPrefix: false},\n\t\"Nmi^2\":    {group: categoryArea, allowPrefix: false},\n\t\"Pica2\":    {group: categoryArea, allowPrefix: false},\n\t\"Pica^2\":   {group: categoryArea, allowPrefix: false},\n\t\"Picapt2\":  {group: categoryArea, allowPrefix: false},\n\t\"Picapt^2\": {group: categoryArea, allowPrefix: false},\n\t\"yd2\":      {group: categoryArea, allowPrefix: false},\n\t\"yd^2\":     {group: categoryArea, allowPrefix: false},\n\t// information\n\t\"byte\": {group: categoryInformation, allowPrefix: true},\n\t\"bit\":  {group: categoryInformation, allowPrefix: true},\n\t// speed\n\t\"m/s\":   {group: categorySpeed, allowPrefix: true},\n\t\"m/sec\": {group: categorySpeed, allowPrefix: true},\n\t\"m/h\":   {group: categorySpeed, allowPrefix: true},\n\t\"m/hr\":  {group: categorySpeed, allowPrefix: true},\n\t\"mph\":   {group: categorySpeed, allowPrefix: false},\n\t\"admkn\": {group: categorySpeed, allowPrefix: false},\n\t\"kn\":    {group: categorySpeed, allowPrefix: false},\n}\n\n// unitConversions maps details of the Units of measure conversion factors,\n// organised by group.\nvar unitConversions = map[byte]map[string]float64{\n\t// conversion uses gram (g) as an intermediate unit\n\tcategoryWeightAndMass: {\n\t\t\"g\":        1,\n\t\t\"sg\":       6.85217658567918e-05,\n\t\t\"lbm\":      2.20462262184878e-03,\n\t\t\"u\":        6.02214179421676e+23,\n\t\t\"ozm\":      3.52739619495804e-02,\n\t\t\"grain\":    1.54323583529414e+01,\n\t\t\"cwt\":      2.20462262184878e-05,\n\t\t\"shweight\": 2.20462262184878e-05,\n\t\t\"uk_cwt\":   1.96841305522212e-05,\n\t\t\"lcwt\":     1.96841305522212e-05,\n\t\t\"hweight\":  1.96841305522212e-05,\n\t\t\"stone\":    1.57473044417770e-04,\n\t\t\"ton\":      1.10231131092439e-06,\n\t\t\"uk_ton\":   9.84206527611061e-07,\n\t\t\"LTON\":     9.84206527611061e-07,\n\t\t\"brton\":    9.84206527611061e-07,\n\t},\n\t// conversion uses meter (m) as an intermediate unit\n\tcategoryDistance: {\n\t\t\"m\":         1,\n\t\t\"mi\":        6.21371192237334e-04,\n\t\t\"Nmi\":       5.39956803455724e-04,\n\t\t\"in\":        3.93700787401575e+01,\n\t\t\"ft\":        3.28083989501312e+00,\n\t\t\"yd\":        1.09361329833771e+00,\n\t\t\"ang\":       1.0e+10,\n\t\t\"ell\":       8.74890638670166e-01,\n\t\t\"ly\":        1.05700083402462e-16,\n\t\t\"parsec\":    3.24077928966473e-17,\n\t\t\"pc\":        3.24077928966473e-17,\n\t\t\"Pica\":      2.83464566929134e+03,\n\t\t\"Picapt\":    2.83464566929134e+03,\n\t\t\"pica\":      2.36220472440945e+02,\n\t\t\"survey_mi\": 6.21369949494950e-04,\n\t},\n\t// conversion uses second (s) as an intermediate unit\n\tcategoryTime: {\n\t\t\"yr\":  3.16880878140289e-08,\n\t\t\"day\": 1.15740740740741e-05,\n\t\t\"d\":   1.15740740740741e-05,\n\t\t\"hr\":  2.77777777777778e-04,\n\t\t\"mn\":  1.66666666666667e-02,\n\t\t\"min\": 1.66666666666667e-02,\n\t\t\"sec\": 1,\n\t\t\"s\":   1,\n\t},\n\t// conversion uses Pascal (Pa) as an intermediate unit\n\tcategoryPressure: {\n\t\t\"Pa\":   1,\n\t\t\"p\":    1,\n\t\t\"atm\":  9.86923266716013e-06,\n\t\t\"at\":   9.86923266716013e-06,\n\t\t\"mmHg\": 7.50063755419211e-03,\n\t\t\"psi\":  1.45037737730209e-04,\n\t\t\"Torr\": 7.50061682704170e-03,\n\t},\n\t// conversion uses Newton (N) as an intermediate unit\n\tcategoryForce: {\n\t\t\"N\":    1,\n\t\t\"dyn\":  1.0e+5,\n\t\t\"dy\":   1.0e+5,\n\t\t\"lbf\":  2.24808923655339e-01,\n\t\t\"pond\": 1.01971621297793e+02,\n\t},\n\t// conversion uses Joule (J) as an intermediate unit\n\tcategoryEnergy: {\n\t\t\"J\":   1,\n\t\t\"e\":   9.99999519343231e+06,\n\t\t\"c\":   2.39006249473467e-01,\n\t\t\"cal\": 2.38846190642017e-01,\n\t\t\"eV\":  6.24145700000000e+18,\n\t\t\"ev\":  6.24145700000000e+18,\n\t\t\"HPh\": 3.72506430801000e-07,\n\t\t\"hh\":  3.72506430801000e-07,\n\t\t\"Wh\":  2.77777916238711e-04,\n\t\t\"wh\":  2.77777916238711e-04,\n\t\t\"flb\": 2.37304222192651e+01,\n\t\t\"BTU\": 9.47815067349015e-04,\n\t\t\"btu\": 9.47815067349015e-04,\n\t},\n\t// conversion uses Horsepower (HP) as an intermediate unit\n\tcategoryPower: {\n\t\t\"HP\": 1,\n\t\t\"h\":  1,\n\t\t\"W\":  7.45699871582270e+02,\n\t\t\"w\":  7.45699871582270e+02,\n\t\t\"PS\": 1.01386966542400e+00,\n\t},\n\t// conversion uses Tesla (T) as an intermediate unit\n\tcategoryMagnetism: {\n\t\t\"T\":  1,\n\t\t\"ga\": 10000,\n\t},\n\t// conversion uses litre (l) as an intermediate unit\n\tcategoryVolumeAndLiquidMeasure: {\n\t\t\"l\":        1,\n\t\t\"L\":        1,\n\t\t\"lt\":       1,\n\t\t\"tsp\":      2.02884136211058e+02,\n\t\t\"tspm\":     2.0e+02,\n\t\t\"tbs\":      6.76280454036860e+01,\n\t\t\"oz\":       3.38140227018430e+01,\n\t\t\"cup\":      4.22675283773038e+00,\n\t\t\"pt\":       2.11337641886519e+00,\n\t\t\"us_pt\":    2.11337641886519e+00,\n\t\t\"uk_pt\":    1.75975398639270e+00,\n\t\t\"qt\":       1.05668820943259e+00,\n\t\t\"uk_qt\":    8.79876993196351e-01,\n\t\t\"gal\":      2.64172052358148e-01,\n\t\t\"uk_gal\":   2.19969248299088e-01,\n\t\t\"ang3\":     1.0e+27,\n\t\t\"ang^3\":    1.0e+27,\n\t\t\"barrel\":   6.28981077043211e-03,\n\t\t\"bushel\":   2.83775932584017e-02,\n\t\t\"in3\":      6.10237440947323e+01,\n\t\t\"in^3\":     6.10237440947323e+01,\n\t\t\"ft3\":      3.53146667214886e-02,\n\t\t\"ft^3\":     3.53146667214886e-02,\n\t\t\"ly3\":      1.18093498844171e-51,\n\t\t\"ly^3\":     1.18093498844171e-51,\n\t\t\"m3\":       1.0e-03,\n\t\t\"m^3\":      1.0e-03,\n\t\t\"mi3\":      2.39912758578928e-13,\n\t\t\"mi^3\":     2.39912758578928e-13,\n\t\t\"yd3\":      1.30795061931439e-03,\n\t\t\"yd^3\":     1.30795061931439e-03,\n\t\t\"Nmi3\":     1.57426214685811e-13,\n\t\t\"Nmi^3\":    1.57426214685811e-13,\n\t\t\"Pica3\":    2.27769904358706e+07,\n\t\t\"Pica^3\":   2.27769904358706e+07,\n\t\t\"Picapt3\":  2.27769904358706e+07,\n\t\t\"Picapt^3\": 2.27769904358706e+07,\n\t\t\"GRT\":      3.53146667214886e-04,\n\t\t\"regton\":   3.53146667214886e-04,\n\t\t\"MTON\":     8.82866668037215e-04,\n\t},\n\t// conversion uses hectare (ha) as an intermediate unit\n\tcategoryArea: {\n\t\t\"ha\":       1,\n\t\t\"uk_acre\":  2.47105381467165e+00,\n\t\t\"us_acre\":  2.47104393046628e+00,\n\t\t\"ang2\":     1.0e+24,\n\t\t\"ang^2\":    1.0e+24,\n\t\t\"ar\":       1.0e+02,\n\t\t\"ft2\":      1.07639104167097e+05,\n\t\t\"ft^2\":     1.07639104167097e+05,\n\t\t\"in2\":      1.55000310000620e+07,\n\t\t\"in^2\":     1.55000310000620e+07,\n\t\t\"ly2\":      1.11725076312873e-28,\n\t\t\"ly^2\":     1.11725076312873e-28,\n\t\t\"m2\":       1.0e+04,\n\t\t\"m^2\":      1.0e+04,\n\t\t\"Morgen\":   4.0e+00,\n\t\t\"mi2\":      3.86102158542446e-03,\n\t\t\"mi^2\":     3.86102158542446e-03,\n\t\t\"Nmi2\":     2.91553349598123e-03,\n\t\t\"Nmi^2\":    2.91553349598123e-03,\n\t\t\"Pica2\":    8.03521607043214e+10,\n\t\t\"Pica^2\":   8.03521607043214e+10,\n\t\t\"Picapt2\":  8.03521607043214e+10,\n\t\t\"Picapt^2\": 8.03521607043214e+10,\n\t\t\"yd2\":      1.19599004630108e+04,\n\t\t\"yd^2\":     1.19599004630108e+04,\n\t},\n\t// conversion uses bit (bit) as an intermediate unit\n\tcategoryInformation: {\n\t\t\"bit\":  1,\n\t\t\"byte\": 0.125,\n\t},\n\t// conversion uses Meters per Second (m/s) as an intermediate unit\n\tcategorySpeed: {\n\t\t\"m/s\":   1,\n\t\t\"m/sec\": 1,\n\t\t\"m/h\":   3.60e+03,\n\t\t\"m/hr\":  3.60e+03,\n\t\t\"mph\":   2.23693629205440e+00,\n\t\t\"admkn\": 1.94260256941567e+00,\n\t\t\"kn\":    1.94384449244060e+00,\n\t},\n}\n\n// conversionMultipliers maps details of the Multiplier prefixes that can be\n// used with Units of Measure in CONVERT.\nvar conversionMultipliers = map[string]float64{\n\t\"Y\":  1e24,\n\t\"Z\":  1e21,\n\t\"E\":  1e18,\n\t\"P\":  1e15,\n\t\"T\":  1e12,\n\t\"G\":  1e9,\n\t\"M\":  1e6,\n\t\"k\":  1e3,\n\t\"h\":  1e2,\n\t\"e\":  1e1,\n\t\"da\": 1e1,\n\t\"d\":  1e-1,\n\t\"c\":  1e-2,\n\t\"m\":  1e-3,\n\t\"u\":  1e-6,\n\t\"n\":  1e-9,\n\t\"p\":  1e-12,\n\t\"f\":  1e-15,\n\t\"a\":  1e-18,\n\t\"z\":  1e-21,\n\t\"y\":  1e-24,\n\t\"Yi\": math.Pow(2, 80),\n\t\"Zi\": math.Pow(2, 70),\n\t\"Ei\": math.Pow(2, 60),\n\t\"Pi\": math.Pow(2, 50),\n\t\"Ti\": math.Pow(2, 40),\n\t\"Gi\": math.Pow(2, 30),\n\t\"Mi\": math.Pow(2, 20),\n\t\"ki\": math.Pow(2, 10),\n}\n\n// getUnitDetails check and returns the unit of measure details.\nfunc getUnitDetails(uom string) (unit string, catgory byte, res float64, ok bool) {\n\tif len(uom) == 0 {\n\t\tok = false\n\t\treturn\n\t}\n\tif unit, ok := conversionUnits[uom]; ok {\n\t\treturn uom, unit.group, 1, ok\n\t}\n\t// 1 character standard metric multiplier prefixes\n\tmultiplierType := uom[:1]\n\tuom = uom[1:]\n\tconversionUnit, ok1 := conversionUnits[uom]\n\tmultiplier, ok2 := conversionMultipliers[multiplierType]\n\tif ok1 && ok2 {\n\t\tif !conversionUnit.allowPrefix {\n\t\t\tok = false\n\t\t\treturn\n\t\t}\n\t\tunitCategory := conversionUnit.group\n\t\treturn uom, unitCategory, multiplier, true\n\t}\n\t// 2 character standard and binary metric multiplier prefixes\n\tif len(uom) > 0 {\n\t\tmultiplierType += uom[:1]\n\t\tuom = uom[1:]\n\t}\n\tconversionUnit, ok1 = conversionUnits[uom]\n\tmultiplier, ok2 = conversionMultipliers[multiplierType]\n\tif ok1 && ok2 {\n\t\tif !conversionUnit.allowPrefix {\n\t\t\tok = false\n\t\t\treturn\n\t\t}\n\t\tunitCategory := conversionUnit.group\n\t\treturn uom, unitCategory, multiplier, true\n\t}\n\tok = false\n\treturn\n}\n\n// resolveTemperatureSynonyms returns unit of measure according to a given\n// temperature synonyms.\nfunc resolveTemperatureSynonyms(uom string) string {\n\tswitch uom {\n\tcase \"fah\":\n\t\treturn \"F\"\n\tcase \"cel\":\n\t\treturn \"C\"\n\tcase \"kel\":\n\t\treturn \"K\"\n\t}\n\treturn uom\n}\n\n// convertTemperature returns converted temperature by a given unit of measure.\nfunc convertTemperature(fromUOM, toUOM string, value float64) float64 {\n\tfromUOM = resolveTemperatureSynonyms(fromUOM)\n\ttoUOM = resolveTemperatureSynonyms(toUOM)\n\tif fromUOM == toUOM {\n\t\treturn value\n\t}\n\t// convert to Kelvin\n\tswitch fromUOM {\n\tcase \"F\":\n\t\tvalue = (value-32)/1.8 + 273.15\n\tcase \"C\":\n\t\tvalue += 273.15\n\tcase \"Rank\":\n\t\tvalue /= 1.8\n\tcase \"Reau\":\n\t\tvalue = value*1.25 + 273.15\n\t}\n\t// convert from Kelvin\n\tswitch toUOM {\n\tcase \"F\":\n\t\tvalue = (value-273.15)*1.8 + 32\n\tcase \"C\":\n\t\tvalue -= 273.15\n\tcase \"Rank\":\n\t\tvalue *= 1.8\n\tcase \"Reau\":\n\t\tvalue = (value - 273.15) * 0.8\n\t}\n\treturn value\n}\n\n// CONVERT function converts a number from one unit type (e.g. Yards) to\n// another unit type (e.g. Meters). The syntax of the function is:\n//\n//\tCONVERT(number,from_unit,to_unit)\nfunc (fn *formulaFuncs) CONVERT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CONVERT requires 3 arguments\")\n\t}\n\tnum := argsList.Front().Value.(formulaArg).ToNumber()\n\tif num.Type != ArgNumber {\n\t\treturn num\n\t}\n\tfromUOM, fromCategory, fromMultiplier, ok1 := getUnitDetails(argsList.Front().Next().Value.(formulaArg).Value())\n\ttoUOM, toCategory, toMultiplier, ok2 := getUnitDetails(argsList.Back().Value.(formulaArg).Value())\n\tif !ok1 || !ok2 || fromCategory != toCategory {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tval := num.Number * fromMultiplier\n\tif fromUOM == toUOM && fromMultiplier == toMultiplier {\n\t\treturn newNumberFormulaArg(val / fromMultiplier)\n\t} else if fromUOM == toUOM {\n\t\treturn newNumberFormulaArg(val / toMultiplier)\n\t} else if fromCategory == categoryTemperature {\n\t\treturn newNumberFormulaArg(convertTemperature(fromUOM, toUOM, val))\n\t}\n\tfromConversion := unitConversions[fromCategory][fromUOM]\n\ttoConversion := unitConversions[fromCategory][toUOM]\n\tbaseValue := val * (1 / fromConversion)\n\treturn newNumberFormulaArg((baseValue * toConversion) / toMultiplier)\n}\n\n// DEC2BIN function converts a decimal number into a Binary (Base 2) number.\n// The syntax of the function is:\n//\n//\tDEC2BIN(number,[places])\nfunc (fn *formulaFuncs) DEC2BIN(argsList *list.List) formulaArg {\n\treturn fn.dec2x(\"DEC2BIN\", argsList)\n}\n\n// DEC2HEX function converts a decimal number into a Hexadecimal (Base 16)\n// number. The syntax of the function is:\n//\n//\tDEC2HEX(number,[places])\nfunc (fn *formulaFuncs) DEC2HEX(argsList *list.List) formulaArg {\n\treturn fn.dec2x(\"DEC2HEX\", argsList)\n}\n\n// DEC2OCT function converts a decimal number into an Octal (Base 8) number.\n// The syntax of the function is:\n//\n//\tDEC2OCT(number,[places])\nfunc (fn *formulaFuncs) DEC2OCT(argsList *list.List) formulaArg {\n\treturn fn.dec2x(\"DEC2OCT\", argsList)\n}\n\n// dec2x is an implementation of the formula functions DEC2BIN, DEC2HEX and\n// DEC2OCT.\nfunc (fn *formulaFuncs) dec2x(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 1 argument\", name))\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s allows at most 2 arguments\", name))\n\t}\n\tdecimal := argsList.Front().Value.(formulaArg).ToNumber()\n\tif decimal.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, decimal.Error)\n\t}\n\tmaxLimitMap := map[string]float64{\n\t\t\"DEC2BIN\": 511,\n\t\t\"HEX2BIN\": 511,\n\t\t\"OCT2BIN\": 511,\n\t\t\"BIN2HEX\": 549755813887,\n\t\t\"DEC2HEX\": 549755813887,\n\t\t\"OCT2HEX\": 549755813887,\n\t\t\"BIN2OCT\": 536870911,\n\t\t\"DEC2OCT\": 536870911,\n\t\t\"HEX2OCT\": 536870911,\n\t}\n\tminLimitMap := map[string]float64{\n\t\t\"DEC2BIN\": -512,\n\t\t\"HEX2BIN\": -512,\n\t\t\"OCT2BIN\": -512,\n\t\t\"BIN2HEX\": -549755813888,\n\t\t\"DEC2HEX\": -549755813888,\n\t\t\"OCT2HEX\": -549755813888,\n\t\t\"BIN2OCT\": -536870912,\n\t\t\"DEC2OCT\": -536870912,\n\t\t\"HEX2OCT\": -536870912,\n\t}\n\tbaseMap := map[string]int{\n\t\t\"DEC2BIN\": 2,\n\t\t\"HEX2BIN\": 2,\n\t\t\"OCT2BIN\": 2,\n\t\t\"BIN2HEX\": 16,\n\t\t\"DEC2HEX\": 16,\n\t\t\"OCT2HEX\": 16,\n\t\t\"BIN2OCT\": 8,\n\t\t\"DEC2OCT\": 8,\n\t\t\"HEX2OCT\": 8,\n\t}\n\tmaxLimit, minLimit := maxLimitMap[name], minLimitMap[name]\n\tbase := baseMap[name]\n\tif decimal.Number < minLimit || decimal.Number > maxLimit {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tn := int64(decimal.Number)\n\tbinary := strconv.FormatUint(*(*uint64)(unsafe.Pointer(&n)), base)\n\tif argsList.Len() == 2 {\n\t\tplaces := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif places.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, places.Error)\n\t\t}\n\t\tbinaryPlaces := len(binary)\n\t\tif places.Number < 0 || places.Number > 10 || binaryPlaces > int(places.Number) {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t\treturn newStringFormulaArg(strings.ToUpper(fmt.Sprintf(\"%s%s\", strings.Repeat(\"0\", int(places.Number)-binaryPlaces), binary)))\n\t}\n\tif decimal.Number < 0 && len(binary) > 10 {\n\t\treturn newStringFormulaArg(strings.ToUpper(binary[len(binary)-10:]))\n\t}\n\treturn newStringFormulaArg(strings.ToUpper(binary))\n}\n\n// DELTA function tests two numbers for equality and returns the Kronecker\n// Delta. i.e. the function returns 1 if the two supplied numbers are equal\n// and 0 otherwise. The syntax of the function is:\n//\n//\tDELTA(number1,[number2])\nfunc (fn *formulaFuncs) DELTA(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DELTA requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DELTA allows at most 2 arguments\")\n\t}\n\tnumber1 := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number1.Type != ArgNumber {\n\t\treturn number1\n\t}\n\tnumber2 := newNumberFormulaArg(0)\n\tif argsList.Len() == 2 {\n\t\tif number2 = argsList.Back().Value.(formulaArg).ToNumber(); number2.Type != ArgNumber {\n\t\t\treturn number2\n\t\t}\n\t}\n\treturn newBoolFormulaArg(number1.Number == number2.Number).ToNumber()\n}\n\n// ERF function calculates the Error Function, integrated between two supplied\n// limits. The syntax of the function is:\n//\n//\tERF(lower_limit,[upper_limit])\nfunc (fn *formulaFuncs) ERF(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ERF requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ERF allows at most 2 arguments\")\n\t}\n\tlower := argsList.Front().Value.(formulaArg).ToNumber()\n\tif lower.Type != ArgNumber {\n\t\treturn lower\n\t}\n\tif argsList.Len() == 2 {\n\t\tupper := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif upper.Type != ArgNumber {\n\t\t\treturn upper\n\t\t}\n\t\treturn newNumberFormulaArg(math.Erf(upper.Number) - math.Erf(lower.Number))\n\t}\n\treturn newNumberFormulaArg(math.Erf(lower.Number))\n}\n\n// ERFdotPRECISE function calculates the Error Function, integrated between a\n// supplied lower or upper limit and 0. The syntax of the function is:\n//\n//\tERF.PRECISE(x)\nfunc (fn *formulaFuncs) ERFdotPRECISE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ERF.PRECISE requires 1 argument\")\n\t}\n\tx := argsList.Front().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn x\n\t}\n\treturn newNumberFormulaArg(math.Erf(x.Number))\n}\n\n// erfc is an implementation of the formula functions ERFC and ERFC.PRECISE.\nfunc (fn *formulaFuncs) erfc(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 1 argument\", name))\n\t}\n\tx := argsList.Front().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn x\n\t}\n\treturn newNumberFormulaArg(math.Erfc(x.Number))\n}\n\n// ERFC function calculates the Complementary Error Function, integrated\n// between a supplied lower limit and infinity. The syntax of the function\n// is:\n//\n//\tERFC(x)\nfunc (fn *formulaFuncs) ERFC(argsList *list.List) formulaArg {\n\treturn fn.erfc(\"ERFC\", argsList)\n}\n\n// ERFCdotPRECISE function calculates the Complementary Error Function,\n// integrated between a supplied lower limit and infinity. The syntax of the\n// function is:\n//\n//\tERFC(x)\nfunc (fn *formulaFuncs) ERFCdotPRECISE(argsList *list.List) formulaArg {\n\treturn fn.erfc(\"ERFC.PRECISE\", argsList)\n}\n\n// GESTEP unction tests whether a supplied number is greater than a supplied\n// step size and returns. The syntax of the function is:\n//\n//\tGESTEP(number,[step])\nfunc (fn *formulaFuncs) GESTEP(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GESTEP requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GESTEP allows at most 2 arguments\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type != ArgNumber {\n\t\treturn number\n\t}\n\tstep := newNumberFormulaArg(0)\n\tif argsList.Len() == 2 {\n\t\tif step = argsList.Back().Value.(formulaArg).ToNumber(); step.Type != ArgNumber {\n\t\t\treturn step\n\t\t}\n\t}\n\treturn newBoolFormulaArg(number.Number >= step.Number).ToNumber()\n}\n\n// HEX2BIN function converts a Hexadecimal (Base 16) number into a Binary\n// (Base 2) number. The syntax of the function is:\n//\n//\tHEX2BIN(number,[places])\nfunc (fn *formulaFuncs) HEX2BIN(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"HEX2BIN requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"HEX2BIN allows at most 2 arguments\")\n\t}\n\tdecimal, newList := fn.hex2dec(argsList.Front().Value.(formulaArg).Value()), list.New()\n\tif decimal.Type != ArgNumber {\n\t\treturn decimal\n\t}\n\tnewList.PushBack(decimal)\n\tif argsList.Len() == 2 {\n\t\tnewList.PushBack(argsList.Back().Value.(formulaArg))\n\t}\n\treturn fn.dec2x(\"HEX2BIN\", newList)\n}\n\n// HEX2DEC function converts a hexadecimal (a base-16 number) into a decimal\n// number. The syntax of the function is:\n//\n//\tHEX2DEC(number)\nfunc (fn *formulaFuncs) HEX2DEC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"HEX2DEC requires 1 numeric argument\")\n\t}\n\treturn fn.hex2dec(argsList.Front().Value.(formulaArg).Value())\n}\n\n// HEX2OCT function converts a Hexadecimal (Base 16) number into an Octal\n// (Base 8) number. The syntax of the function is:\n//\n//\tHEX2OCT(number,[places])\nfunc (fn *formulaFuncs) HEX2OCT(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"HEX2OCT requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"HEX2OCT allows at most 2 arguments\")\n\t}\n\tdecimal, newList := fn.hex2dec(argsList.Front().Value.(formulaArg).Value()), list.New()\n\tif decimal.Type != ArgNumber {\n\t\treturn decimal\n\t}\n\tnewList.PushBack(decimal)\n\tif argsList.Len() == 2 {\n\t\tnewList.PushBack(argsList.Back().Value.(formulaArg))\n\t}\n\treturn fn.dec2x(\"HEX2OCT\", newList)\n}\n\n// hex2dec is an implementation of the formula function HEX2DEC.\nfunc (fn *formulaFuncs) hex2dec(number string) formulaArg {\n\tdecimal, length := 0.0, len(number)\n\tfor i := length; i > 0; i-- {\n\t\tnum, err := strconv.ParseInt(string(number[length-i]), 16, 64)\n\t\tif err != nil {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t\t}\n\t\tif i == 10 && string(number[length-i]) == \"F\" {\n\t\t\tdecimal += math.Pow(-16.0, float64(i-1))\n\t\t\tcontinue\n\t\t}\n\t\tdecimal += float64(num) * math.Pow(16.0, float64(i-1))\n\t}\n\treturn newNumberFormulaArg(decimal)\n}\n\n// IMABS function returns the absolute value (the modulus) of a complex\n// number. The syntax of the function is:\n//\n//\tIMABS(inumber)\nfunc (fn *formulaFuncs) IMABS(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMABS requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newNumberFormulaArg(cmplx.Abs(inumber))\n}\n\n// IMAGINARY function returns the imaginary coefficient of a supplied complex\n// number. The syntax of the function is:\n//\n//\tIMAGINARY(inumber)\nfunc (fn *formulaFuncs) IMAGINARY(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMAGINARY requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newNumberFormulaArg(imag(inumber))\n}\n\n// IMARGUMENT function returns the phase (also called the argument) of a\n// supplied complex number. The syntax of the function is:\n//\n//\tIMARGUMENT(inumber)\nfunc (fn *formulaFuncs) IMARGUMENT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMARGUMENT requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newNumberFormulaArg(cmplx.Phase(inumber))\n}\n\n// IMCONJUGATE function returns the complex conjugate of a supplied complex\n// number. The syntax of the function is:\n//\n//\tIMCONJUGATE(inumber)\nfunc (fn *formulaFuncs) IMCONJUGATE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMCONJUGATE requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(cmplx2str(cmplx.Conj(inumber), value[len(value)-1:]))\n}\n\n// IMCOS function returns the cosine of a supplied complex number. The syntax\n// of the function is:\n//\n//\tIMCOS(inumber)\nfunc (fn *formulaFuncs) IMCOS(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMCOS requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(cmplx2str(cmplx.Cos(inumber), value[len(value)-1:]))\n}\n\n// IMCOSH function returns the hyperbolic cosine of a supplied complex number. The syntax\n// of the function is:\n//\n//\tIMCOSH(inumber)\nfunc (fn *formulaFuncs) IMCOSH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMCOSH requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(cmplx2str(cmplx.Cosh(inumber), value[len(value)-1:]))\n}\n\n// IMCOT function returns the cotangent of a supplied complex number. The syntax\n// of the function is:\n//\n//\tIMCOT(inumber)\nfunc (fn *formulaFuncs) IMCOT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMCOT requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(cmplx2str(cmplx.Cot(inumber), value[len(value)-1:]))\n}\n\n// IMCSC function returns the cosecant of a supplied complex number. The syntax\n// of the function is:\n//\n//\tIMCSC(inumber)\nfunc (fn *formulaFuncs) IMCSC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMCSC requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\tnum := 1 / cmplx.Sin(inumber)\n\tif cmplx.IsInf(num) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newStringFormulaArg(cmplx2str(num, value[len(value)-1:]))\n}\n\n// IMCSCH function returns the hyperbolic cosecant of a supplied complex\n// number. The syntax of the function is:\n//\n//\tIMCSCH(inumber)\nfunc (fn *formulaFuncs) IMCSCH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMCSCH requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\tnum := 1 / cmplx.Sinh(inumber)\n\tif cmplx.IsInf(num) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newStringFormulaArg(cmplx2str(num, value[len(value)-1:]))\n}\n\n// IMDIV function calculates the quotient of two complex numbers (i.e. divides\n// one complex number by another). The syntax of the function is:\n//\n//\tIMDIV(inumber1,inumber2)\nfunc (fn *formulaFuncs) IMDIV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMDIV requires 2 arguments\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber1, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\tinumber2, err := strconv.ParseComplex(str2cmplx(argsList.Back().Value.(formulaArg).Value()), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\tnum := inumber1 / inumber2\n\tif cmplx.IsInf(num) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newStringFormulaArg(cmplx2str(num, value[len(value)-1:]))\n}\n\n// IMEXP function returns the exponential of a supplied complex number. The\n// syntax of the function is:\n//\n//\tIMEXP(inumber)\nfunc (fn *formulaFuncs) IMEXP(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMEXP requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(cmplx2str(cmplx.Exp(inumber), value[len(value)-1:]))\n}\n\n// IMLN function returns the natural logarithm of a supplied complex number.\n// The syntax of the function is:\n//\n//\tIMLN(inumber)\nfunc (fn *formulaFuncs) IMLN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMLN requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\tnum := cmplx.Log(inumber)\n\tif cmplx.IsInf(num) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newStringFormulaArg(cmplx2str(num, value[len(value)-1:]))\n}\n\n// IMLOG10 function returns the common (base 10) logarithm of a supplied\n// complex number. The syntax of the function is:\n//\n//\tIMLOG10(inumber)\nfunc (fn *formulaFuncs) IMLOG10(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMLOG10 requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\tnum := cmplx.Log10(inumber)\n\tif cmplx.IsInf(num) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newStringFormulaArg(cmplx2str(num, value[len(value)-1:]))\n}\n\n// IMLOG2 function calculates the base 2 logarithm of a supplied complex\n// number. The syntax of the function is:\n//\n//\tIMLOG2(inumber)\nfunc (fn *formulaFuncs) IMLOG2(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMLOG2 requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\tnum := cmplx.Log(inumber)\n\tif cmplx.IsInf(num) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newStringFormulaArg(cmplx2str(num/cmplx.Log(2), value[len(value)-1:]))\n}\n\n// IMPOWER function returns a supplied complex number, raised to a given\n// power. The syntax of the function is:\n//\n//\tIMPOWER(inumber,number)\nfunc (fn *formulaFuncs) IMPOWER(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMPOWER requires 2 arguments\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\tnumber, err := strconv.ParseComplex(str2cmplx(argsList.Back().Value.(formulaArg).Value()), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\tif inumber == 0 && number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tnum := cmplx.Pow(inumber, number)\n\tif cmplx.IsInf(num) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newStringFormulaArg(cmplx2str(num, value[len(value)-1:]))\n}\n\n// IMPRODUCT function calculates the product of two or more complex numbers.\n// The syntax of the function is:\n//\n//\tIMPRODUCT(number1,[number2],...)\nfunc (fn *formulaFuncs) IMPRODUCT(argsList *list.List) formulaArg {\n\tproduct := complex128(1)\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tswitch token.Type {\n\t\tcase ArgString:\n\t\t\tif token.Value() == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tval, err := strconv.ParseComplex(str2cmplx(token.Value()), 128)\n\t\t\tif err != nil {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t\t\t}\n\t\t\tproduct = product * val\n\t\tcase ArgNumber:\n\t\t\tproduct = product * complex(token.Number, 0)\n\t\tcase ArgMatrix:\n\t\t\tfor _, row := range token.Matrix {\n\t\t\t\tfor _, value := range row {\n\t\t\t\t\tif value.Value() == \"\" {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tval, err := strconv.ParseComplex(str2cmplx(value.Value()), 128)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t\t\t\t\t}\n\t\t\t\t\tproduct = product * val\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn newStringFormulaArg(cmplx2str(product, \"i\"))\n}\n\n// IMREAL function returns the real coefficient of a supplied complex number.\n// The syntax of the function is:\n//\n//\tIMREAL(inumber)\nfunc (fn *formulaFuncs) IMREAL(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMREAL requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(fmt.Sprint(real(inumber)))\n}\n\n// IMSEC function returns the secant of a supplied complex number. The syntax\n// of the function is:\n//\n//\tIMSEC(inumber)\nfunc (fn *formulaFuncs) IMSEC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMSEC requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(cmplx2str(1/cmplx.Cos(inumber), value[len(value)-1:]))\n}\n\n// IMSECH function returns the hyperbolic secant of a supplied complex number.\n// The syntax of the function is:\n//\n//\tIMSECH(inumber)\nfunc (fn *formulaFuncs) IMSECH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMSECH requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(cmplx2str(1/cmplx.Cosh(inumber), value[len(value)-1:]))\n}\n\n// IMSIN function returns the Sine of a supplied complex number. The syntax of\n// the function is:\n//\n//\tIMSIN(inumber)\nfunc (fn *formulaFuncs) IMSIN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMSIN requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(cmplx2str(cmplx.Sin(inumber), value[len(value)-1:]))\n}\n\n// IMSINH function returns the hyperbolic sine of a supplied complex number.\n// The syntax of the function is:\n//\n//\tIMSINH(inumber)\nfunc (fn *formulaFuncs) IMSINH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMSINH requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(cmplx2str(cmplx.Sinh(inumber), value[len(value)-1:]))\n}\n\n// IMSQRT function returns the square root of a supplied complex number. The\n// syntax of the function is:\n//\n//\tIMSQRT(inumber)\nfunc (fn *formulaFuncs) IMSQRT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMSQRT requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(cmplx2str(cmplx.Sqrt(inumber), value[len(value)-1:]))\n}\n\n// IMSUB function calculates the difference between two complex numbers\n// (i.e. subtracts one complex number from another). The syntax of the\n// function is:\n//\n//\tIMSUB(inumber1,inumber2)\nfunc (fn *formulaFuncs) IMSUB(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMSUB requires 2 arguments\")\n\t}\n\ti1, err := strconv.ParseComplex(str2cmplx(argsList.Front().Value.(formulaArg).Value()), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\ti2, err := strconv.ParseComplex(str2cmplx(argsList.Back().Value.(formulaArg).Value()), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(cmplx2str(i1-i2, \"i\"))\n}\n\n// IMSUM function calculates the sum of two or more complex numbers. The\n// syntax of the function is:\n//\n//\tIMSUM(inumber1,inumber2,...)\nfunc (fn *formulaFuncs) IMSUM(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMSUM requires at least 1 argument\")\n\t}\n\tvar result complex128\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tnum, err := strconv.ParseComplex(str2cmplx(token.Value()), 128)\n\t\tif err != nil {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t\t}\n\t\tresult += num\n\t}\n\treturn newStringFormulaArg(cmplx2str(result, \"i\"))\n}\n\n// IMTAN function returns the tangent of a supplied complex number. The syntax\n// of the function is:\n//\n//\tIMTAN(inumber)\nfunc (fn *formulaFuncs) IMTAN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IMTAN requires 1 argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).Value()\n\tinumber, err := strconv.ParseComplex(str2cmplx(value), 128)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, err.Error())\n\t}\n\treturn newStringFormulaArg(cmplx2str(cmplx.Tan(inumber), value[len(value)-1:]))\n}\n\n// OCT2BIN function converts an Octal (Base 8) number into a Binary (Base 2)\n// number. The syntax of the function is:\n//\n//\tOCT2BIN(number,[places])\nfunc (fn *formulaFuncs) OCT2BIN(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"OCT2BIN requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"OCT2BIN allows at most 2 arguments\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tnumber := token.ToNumber()\n\tif number.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, number.Error)\n\t}\n\tdecimal, newList := fn.oct2dec(token.Value()), list.New()\n\tnewList.PushBack(decimal)\n\tif argsList.Len() == 2 {\n\t\tnewList.PushBack(argsList.Back().Value.(formulaArg))\n\t}\n\treturn fn.dec2x(\"OCT2BIN\", newList)\n}\n\n// OCT2DEC function converts an Octal (a base-8 number) into a decimal number.\n// The syntax of the function is:\n//\n//\tOCT2DEC(number)\nfunc (fn *formulaFuncs) OCT2DEC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"OCT2DEC requires 1 numeric argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tnumber := token.ToNumber()\n\tif number.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, number.Error)\n\t}\n\treturn fn.oct2dec(token.Value())\n}\n\n// OCT2HEX function converts an Octal (Base 8) number into a Hexadecimal\n// (Base 16) number. The syntax of the function is:\n//\n//\tOCT2HEX(number,[places])\nfunc (fn *formulaFuncs) OCT2HEX(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"OCT2HEX requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"OCT2HEX allows at most 2 arguments\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tnumber := token.ToNumber()\n\tif number.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, number.Error)\n\t}\n\tdecimal, newList := fn.oct2dec(token.Value()), list.New()\n\tnewList.PushBack(decimal)\n\tif argsList.Len() == 2 {\n\t\tnewList.PushBack(argsList.Back().Value.(formulaArg))\n\t}\n\treturn fn.dec2x(\"OCT2HEX\", newList)\n}\n\n// oct2dec is an implementation of the formula function OCT2DEC.\nfunc (fn *formulaFuncs) oct2dec(number string) formulaArg {\n\tdecimal, length := 0.0, len(number)\n\tfor i := length; i > 0; i-- {\n\t\tnum, _ := strconv.Atoi(string(number[length-i]))\n\t\tif i == 10 && string(number[length-i]) == \"7\" {\n\t\t\tdecimal += math.Pow(-8.0, float64(i-1))\n\t\t\tcontinue\n\t\t}\n\t\tdecimal += float64(num) * math.Pow(8.0, float64(i-1))\n\t}\n\treturn newNumberFormulaArg(decimal)\n}\n\n// Math and Trigonometric Functions\n\n// ABS function returns the absolute value of any supplied number. The syntax\n// of the function is:\n//\n//\tABS(number)\nfunc (fn *formulaFuncs) ABS(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ABS requires 1 numeric argument\")\n\t}\n\targ := fn.implicitIntersect(argsList.Front().Value.(formulaArg)).ToNumber()\n\tif arg.Type == ArgError {\n\t\treturn arg\n\t}\n\treturn newNumberFormulaArg(math.Abs(arg.Number))\n}\n\n// ACOS function calculates the arccosine (i.e. the inverse cosine) of a given\n// number, and returns an angle, in radians, between 0 and π. The syntax of\n// the function is:\n//\n//\tACOS(number)\nfunc (fn *formulaFuncs) ACOS(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ACOS requires 1 numeric argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg).ToNumber()\n\tif arg.Type == ArgError {\n\t\treturn arg\n\t}\n\treturn newNumberFormulaArg(math.Acos(arg.Number))\n}\n\n// ACOSH function calculates the inverse hyperbolic cosine of a supplied number.\n// of the function is:\n//\n//\tACOSH(number)\nfunc (fn *formulaFuncs) ACOSH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ACOSH requires 1 numeric argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg).ToNumber()\n\tif arg.Type == ArgError {\n\t\treturn arg\n\t}\n\treturn newNumberFormulaArg(math.Acosh(arg.Number))\n}\n\n// ACOT function calculates the arccotangent (i.e. the inverse cotangent) of a\n// given number, and returns an angle, in radians, between 0 and π. The syntax\n// of the function is:\n//\n//\tACOT(number)\nfunc (fn *formulaFuncs) ACOT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ACOT requires 1 numeric argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg).ToNumber()\n\tif arg.Type == ArgError {\n\t\treturn arg\n\t}\n\treturn newNumberFormulaArg(math.Pi/2 - math.Atan(arg.Number))\n}\n\n// ACOTH function calculates the hyperbolic arccotangent (coth) of a supplied\n// value. The syntax of the function is:\n//\n//\tACOTH(number)\nfunc (fn *formulaFuncs) ACOTH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ACOTH requires 1 numeric argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg).ToNumber()\n\tif arg.Type == ArgError {\n\t\treturn arg\n\t}\n\treturn newNumberFormulaArg(math.Atanh(1 / arg.Number))\n}\n\n// AGGREGATE function returns the result of a specified operation or function,\n// applied to a list or database of values. The syntax of the function is:\n//\n//\tAGGREGATE(function_num,options,ref1,[ref2],...)\nfunc (fn *formulaFuncs) AGGREGATE(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"AGGREGATE requires at least 3 arguments\")\n\t}\n\tvar fnNum, opts formulaArg\n\tif fnNum = argsList.Front().Value.(formulaArg).ToNumber(); fnNum.Type != ArgNumber {\n\t\treturn fnNum\n\t}\n\tsubFn, ok := map[int]func(argsList *list.List) formulaArg{\n\t\t1:  fn.AVERAGE,\n\t\t2:  fn.COUNT,\n\t\t3:  fn.COUNTA,\n\t\t4:  fn.MAX,\n\t\t5:  fn.MIN,\n\t\t6:  fn.PRODUCT,\n\t\t7:  fn.STDEVdotS,\n\t\t8:  fn.STDEVdotP,\n\t\t9:  fn.SUM,\n\t\t10: fn.VARdotS,\n\t\t11: fn.VARdotP,\n\t\t12: fn.MEDIAN,\n\t\t13: fn.MODEdotSNGL,\n\t\t14: fn.LARGE,\n\t\t15: fn.SMALL,\n\t\t16: fn.PERCENTILEdotINC,\n\t\t17: fn.QUARTILEdotINC,\n\t\t18: fn.PERCENTILEdotEXC,\n\t\t19: fn.QUARTILEdotEXC,\n\t}[int(fnNum.Number)]\n\tif !ok {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"AGGREGATE has invalid function_num\")\n\t}\n\tif opts = argsList.Front().Next().Value.(formulaArg).ToNumber(); opts.Type != ArgNumber {\n\t\treturn opts\n\t}\n\t// TODO: apply option argument values to be ignored during the calculation\n\tif int(opts.Number) < 0 || int(opts.Number) > 7 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"AGGREGATE has invalid options\")\n\t}\n\tsubArgList := list.New().Init()\n\tfor arg := argsList.Front().Next().Next(); arg != nil; arg = arg.Next() {\n\t\tsubArgList.PushBack(arg.Value.(formulaArg))\n\t}\n\treturn subFn(subArgList)\n}\n\n// ARABIC function converts a Roman numeral into an Arabic numeral. The syntax\n// of the function is:\n//\n//\tARABIC(text)\nfunc (fn *formulaFuncs) ARABIC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ARABIC requires 1 numeric argument\")\n\t}\n\ttext := argsList.Front().Value.(formulaArg).Value()\n\tif countUTF16String(text) > MaxFieldLength {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\ttext = strings.ToUpper(text)\n\tnumber, actualStart, index, isNegative := 0, 0, len(text)-1, false\n\tstartIndex, subtractNumber, currentPartValue, currentCharValue, prevCharValue := 0, 0, 0, 0, -1\n\tfor index >= 0 && text[index] == ' ' {\n\t\tindex--\n\t}\n\tfor actualStart <= index && text[actualStart] == ' ' {\n\t\tactualStart++\n\t}\n\tif actualStart <= index && text[actualStart] == '-' {\n\t\tisNegative = true\n\t\tactualStart++\n\t}\n\tcharMap := map[rune]int{'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}\n\tfor index >= actualStart {\n\t\tstartIndex = index\n\t\tstartChar := text[startIndex]\n\t\tindex--\n\t\tfor index >= actualStart && (text[index]|' ') == startChar {\n\t\t\tindex--\n\t\t}\n\t\tcurrentCharValue = charMap[rune(startChar)]\n\t\tcurrentPartValue = (startIndex - index) * currentCharValue\n\t\tif currentCharValue >= prevCharValue {\n\t\t\tnumber += currentPartValue - subtractNumber\n\t\t\tprevCharValue = currentCharValue\n\t\t\tsubtractNumber = 0\n\t\t\tcontinue\n\t\t}\n\t\tsubtractNumber += currentPartValue\n\t}\n\tif subtractNumber != 0 {\n\t\tnumber -= subtractNumber\n\t}\n\tif isNegative {\n\t\tnumber = -number\n\t}\n\treturn newNumberFormulaArg(float64(number))\n}\n\n// ASIN function calculates the arcsine (i.e. the inverse sine) of a given\n// number, and returns an angle, in radians, between -π/2 and π/2. The syntax\n// of the function is:\n//\n//\tASIN(number)\nfunc (fn *formulaFuncs) ASIN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ASIN requires 1 numeric argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg).ToNumber()\n\tif arg.Type == ArgError {\n\t\treturn arg\n\t}\n\treturn newNumberFormulaArg(math.Asin(arg.Number))\n}\n\n// ASINH function calculates the inverse hyperbolic sine of a supplied number.\n// The syntax of the function is:\n//\n//\tASINH(number)\nfunc (fn *formulaFuncs) ASINH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ASINH requires 1 numeric argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg).ToNumber()\n\tif arg.Type == ArgError {\n\t\treturn arg\n\t}\n\treturn newNumberFormulaArg(math.Asinh(arg.Number))\n}\n\n// ATAN function calculates the arctangent (i.e. the inverse tangent) of a\n// given number, and returns an angle, in radians, between -π/2 and +π/2. The\n// syntax of the function is:\n//\n//\tATAN(number)\nfunc (fn *formulaFuncs) ATAN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ATAN requires 1 numeric argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg).ToNumber()\n\tif arg.Type == ArgError {\n\t\treturn arg\n\t}\n\treturn newNumberFormulaArg(math.Atan(arg.Number))\n}\n\n// ATANH function calculates the inverse hyperbolic tangent of a supplied\n// number. The syntax of the function is:\n//\n//\tATANH(number)\nfunc (fn *formulaFuncs) ATANH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ATANH requires 1 numeric argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg).ToNumber()\n\tif arg.Type == ArgError {\n\t\treturn arg\n\t}\n\treturn newNumberFormulaArg(math.Atanh(arg.Number))\n}\n\n// ATAN2 function calculates the arctangent (i.e. the inverse tangent) of a\n// given set of x and y coordinates, and returns an angle, in radians, between\n// -π/2 and +π/2. The syntax of the function is:\n//\n//\tATAN2(x_num,y_num)\nfunc (fn *formulaFuncs) ATAN2(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ATAN2 requires 2 numeric arguments\")\n\t}\n\tx := argsList.Back().Value.(formulaArg).ToNumber()\n\tif x.Type == ArgError {\n\t\treturn x\n\t}\n\ty := argsList.Front().Value.(formulaArg).ToNumber()\n\tif y.Type == ArgError {\n\t\treturn y\n\t}\n\treturn newNumberFormulaArg(math.Atan2(x.Number, y.Number))\n}\n\n// BASE function converts a number into a supplied base (radix), and returns a\n// text representation of the calculated value. The syntax of the function is:\n//\n//\tBASE(number,radix,[min_length])\nfunc (fn *formulaFuncs) BASE(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BASE requires at least 2 arguments\")\n\t}\n\tif argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BASE allows at most 3 arguments\")\n\t}\n\tvar minLength int\n\tvar err error\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tradix := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif radix.Type == ArgError {\n\t\treturn radix\n\t}\n\tif int(radix.Number) < 2 || int(radix.Number) > 36 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"radix must be an integer >= 2 and <= 36\")\n\t}\n\tif argsList.Len() > 2 {\n\t\tif minLength, err = strconv.Atoi(argsList.Back().Value.(formulaArg).Value()); err != nil {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t\t}\n\t}\n\tresult := strconv.FormatInt(int64(number.Number), int(radix.Number))\n\tif len(result) < minLength {\n\t\tresult = strings.Repeat(\"0\", minLength-len(result)) + result\n\t}\n\treturn newStringFormulaArg(strings.ToUpper(result))\n}\n\n// CEILING function rounds a supplied number away from zero, to the nearest\n// multiple of a given number. The syntax of the function is:\n//\n//\tCEILING(number,significance)\nfunc (fn *formulaFuncs) CEILING(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CEILING requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CEILING allows at most 2 arguments\")\n\t}\n\tnumber, significance, res := 0.0, 1.0, 0.0\n\tn := argsList.Front().Value.(formulaArg).ToNumber()\n\tif n.Type == ArgError {\n\t\treturn n\n\t}\n\tnumber = n.Number\n\tif number < 0 {\n\t\tsignificance = -1\n\t}\n\tif argsList.Len() > 1 {\n\t\ts := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif s.Type == ArgError {\n\t\t\treturn s\n\t\t}\n\t\tsignificance = s.Number\n\t}\n\tif significance < 0 && number > 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"negative sig to CEILING invalid\")\n\t}\n\tif argsList.Len() == 1 {\n\t\treturn newNumberFormulaArg(math.Ceil(number))\n\t}\n\tnumber, res = math.Modf(number / significance)\n\tif res > 0 {\n\t\tnumber++\n\t}\n\treturn newNumberFormulaArg(number * significance)\n}\n\n// CEILINGdotMATH function rounds a supplied number up to a supplied multiple\n// of significance. The syntax of the function is:\n//\n//\tCEILING.MATH(number,[significance],[mode])\nfunc (fn *formulaFuncs) CEILINGdotMATH(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CEILING.MATH requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CEILING.MATH allows at most 3 arguments\")\n\t}\n\tnumber, significance, mode := 0.0, 1.0, 1.0\n\tn := argsList.Front().Value.(formulaArg).ToNumber()\n\tif n.Type == ArgError {\n\t\treturn n\n\t}\n\tnumber = n.Number\n\tif number < 0 {\n\t\tsignificance = -1\n\t}\n\tif argsList.Len() > 1 {\n\t\ts := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\t\tif s.Type == ArgError {\n\t\t\treturn s\n\t\t}\n\t\tsignificance = s.Number\n\t}\n\tif argsList.Len() == 1 {\n\t\treturn newNumberFormulaArg(math.Ceil(number))\n\t}\n\tif argsList.Len() > 2 {\n\t\tm := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif m.Type == ArgError {\n\t\t\treturn m\n\t\t}\n\t\tmode = m.Number\n\t}\n\tval, res := math.Modf(number / significance)\n\tif res != 0 {\n\t\tif number > 0 {\n\t\t\tval++\n\t\t} else if mode < 0 {\n\t\t\tval--\n\t\t}\n\t}\n\treturn newNumberFormulaArg(val * significance)\n}\n\n// CEILINGdotPRECISE function rounds a supplied number up (regardless of the\n// number's sign), to the nearest multiple of a given number. The syntax of\n// the function is:\n//\n//\tCEILING.PRECISE(number,[significance])\nfunc (fn *formulaFuncs) CEILINGdotPRECISE(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CEILING.PRECISE requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CEILING.PRECISE allows at most 2 arguments\")\n\t}\n\tnumber, significance := 0.0, 1.0\n\tn := argsList.Front().Value.(formulaArg).ToNumber()\n\tif n.Type == ArgError {\n\t\treturn n\n\t}\n\tnumber = n.Number\n\tif number < 0 {\n\t\tsignificance = -1\n\t}\n\tif argsList.Len() == 1 {\n\t\treturn newNumberFormulaArg(math.Ceil(number))\n\t}\n\tif argsList.Len() > 1 {\n\t\ts := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif s.Type == ArgError {\n\t\t\treturn s\n\t\t}\n\t\tsignificance = s.Number\n\t\tsignificance = math.Abs(significance)\n\t\tif significance == 0 {\n\t\t\treturn newNumberFormulaArg(significance)\n\t\t}\n\t}\n\tval, res := math.Modf(number / significance)\n\tif res != 0 {\n\t\tif number > 0 {\n\t\t\tval++\n\t\t}\n\t}\n\treturn newNumberFormulaArg(val * significance)\n}\n\n// COMBIN function calculates the number of combinations (in any order) of a\n// given number objects from a set. The syntax of the function is:\n//\n//\tCOMBIN(number,number_chosen)\nfunc (fn *formulaFuncs) COMBIN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COMBIN requires 2 argument\")\n\t}\n\tnumber, chosen, val := 0.0, 0.0, 1.0\n\tn := argsList.Front().Value.(formulaArg).ToNumber()\n\tif n.Type == ArgError {\n\t\treturn n\n\t}\n\tnumber = n.Number\n\tc := argsList.Back().Value.(formulaArg).ToNumber()\n\tif c.Type == ArgError {\n\t\treturn c\n\t}\n\tchosen = c.Number\n\tnumber, chosen = math.Trunc(number), math.Trunc(chosen)\n\tif chosen > number {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COMBIN requires number >= number_chosen\")\n\t}\n\tif chosen == number || chosen == 0 {\n\t\treturn newNumberFormulaArg(1)\n\t}\n\tfor c := float64(1); c <= chosen; c++ {\n\t\tval *= (number + 1 - c) / c\n\t}\n\treturn newNumberFormulaArg(math.Ceil(val))\n}\n\n// COMBINA function calculates the number of combinations, with repetitions,\n// of a given number objects from a set. The syntax of the function is:\n//\n//\tCOMBINA(number,number_chosen)\nfunc (fn *formulaFuncs) COMBINA(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COMBINA requires 2 argument\")\n\t}\n\tvar number, chosen float64\n\tn := argsList.Front().Value.(formulaArg).ToNumber()\n\tif n.Type == ArgError {\n\t\treturn n\n\t}\n\tnumber = n.Number\n\tc := argsList.Back().Value.(formulaArg).ToNumber()\n\tif c.Type == ArgError {\n\t\treturn c\n\t}\n\tchosen = c.Number\n\tnumber, chosen = math.Trunc(number), math.Trunc(chosen)\n\tif number < chosen {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COMBINA requires number > number_chosen\")\n\t}\n\tif number == 0 {\n\t\treturn newNumberFormulaArg(number)\n\t}\n\targs := list.New()\n\targs.PushBack(formulaArg{\n\t\tString: fmt.Sprintf(\"%g\", number+chosen-1),\n\t\tType:   ArgString,\n\t})\n\targs.PushBack(formulaArg{\n\t\tString: fmt.Sprintf(\"%g\", number-1),\n\t\tType:   ArgString,\n\t})\n\treturn fn.COMBIN(args)\n}\n\n// COS function calculates the cosine of a given angle. The syntax of the\n// function is:\n//\n//\tCOS(number)\nfunc (fn *formulaFuncs) COS(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COS requires 1 numeric argument\")\n\t}\n\tval := argsList.Front().Value.(formulaArg).ToNumber()\n\tif val.Type == ArgError {\n\t\treturn val\n\t}\n\treturn newNumberFormulaArg(math.Cos(val.Number))\n}\n\n// COSH function calculates the hyperbolic cosine (cosh) of a supplied number.\n// The syntax of the function is:\n//\n//\tCOSH(number)\nfunc (fn *formulaFuncs) COSH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COSH requires 1 numeric argument\")\n\t}\n\tval := argsList.Front().Value.(formulaArg).ToNumber()\n\tif val.Type == ArgError {\n\t\treturn val\n\t}\n\treturn newNumberFormulaArg(math.Cosh(val.Number))\n}\n\n// COT function calculates the cotangent of a given angle. The syntax of the\n// function is:\n//\n//\tCOT(number)\nfunc (fn *formulaFuncs) COT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COT requires 1 numeric argument\")\n\t}\n\tval := argsList.Front().Value.(formulaArg).ToNumber()\n\tif val.Type == ArgError {\n\t\treturn val\n\t}\n\tif val.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(1 / math.Tan(val.Number))\n}\n\n// COTH function calculates the hyperbolic cotangent (coth) of a supplied\n// angle. The syntax of the function is:\n//\n//\tCOTH(number)\nfunc (fn *formulaFuncs) COTH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COTH requires 1 numeric argument\")\n\t}\n\tval := argsList.Front().Value.(formulaArg).ToNumber()\n\tif val.Type == ArgError {\n\t\treturn val\n\t}\n\tif val.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg((math.Exp(val.Number) + math.Exp(-val.Number)) / (math.Exp(val.Number) - math.Exp(-val.Number)))\n}\n\n// CSC function calculates the cosecant of a given angle. The syntax of the\n// function is:\n//\n//\tCSC(number)\nfunc (fn *formulaFuncs) CSC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CSC requires 1 numeric argument\")\n\t}\n\tval := argsList.Front().Value.(formulaArg).ToNumber()\n\tif val.Type == ArgError {\n\t\treturn val\n\t}\n\tif val.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(1 / math.Sin(val.Number))\n}\n\n// CSCH function calculates the hyperbolic cosecant (csch) of a supplied\n// angle. The syntax of the function is:\n//\n//\tCSCH(number)\nfunc (fn *formulaFuncs) CSCH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CSCH requires 1 numeric argument\")\n\t}\n\tval := argsList.Front().Value.(formulaArg).ToNumber()\n\tif val.Type == ArgError {\n\t\treturn val\n\t}\n\tif val.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(1 / math.Sinh(val.Number))\n}\n\n// DECIMAL function converts a text representation of a number in a specified\n// base, into a decimal value. The syntax of the function is:\n//\n//\tDECIMAL(text,radix)\nfunc (fn *formulaFuncs) DECIMAL(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DECIMAL requires 2 numeric arguments\")\n\t}\n\ttext := argsList.Front().Value.(formulaArg).Value()\n\tvar err error\n\tradix := argsList.Back().Value.(formulaArg).ToNumber()\n\tif radix.Type != ArgNumber {\n\t\treturn radix\n\t}\n\tif len(text) > 2 && (strings.HasPrefix(text, \"0x\") || strings.HasPrefix(text, \"0X\")) {\n\t\ttext = text[2:]\n\t}\n\tval, err := strconv.ParseInt(text, int(radix.Number), 64)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t}\n\treturn newNumberFormulaArg(float64(val))\n}\n\n// DEGREES function converts radians into degrees. The syntax of the function\n// is:\n//\n//\tDEGREES(angle)\nfunc (fn *formulaFuncs) DEGREES(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DEGREES requires 1 numeric argument\")\n\t}\n\tval := argsList.Front().Value.(formulaArg).ToNumber()\n\tif val.Type == ArgError {\n\t\treturn val\n\t}\n\tif val.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(180.0 / math.Pi * val.Number)\n}\n\n// EVEN function rounds a supplied number away from zero (i.e. rounds a\n// positive number up and a negative number down), to the next even number.\n// The syntax of the function is:\n//\n//\tEVEN(number)\nfunc (fn *formulaFuncs) EVEN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"EVEN requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tsign := math.Signbit(number.Number)\n\tm, frac := math.Modf(number.Number / 2)\n\tval := m * 2\n\tif frac != 0 {\n\t\tif !sign {\n\t\t\tval += 2\n\t\t} else {\n\t\t\tval -= 2\n\t\t}\n\t}\n\treturn newNumberFormulaArg(val)\n}\n\n// EXP function calculates the value of the mathematical constant e, raised to\n// the power of a given number. The syntax of the function is:\n//\n//\tEXP(number)\nfunc (fn *formulaFuncs) EXP(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"EXP requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\treturn newNumberFormulaArg(math.Exp(number.Number))\n}\n\n// fact returns the factorial of a supplied number.\nfunc fact(number float64) float64 {\n\tval := float64(1)\n\tfor i := float64(2); i <= number; i++ {\n\t\tval *= i\n\t}\n\treturn val\n}\n\n// FACT function returns the factorial of a supplied number. The syntax of the\n// function is:\n//\n//\tFACT(number)\nfunc (fn *formulaFuncs) FACT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FACT requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tif number.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(fact(number.Number))\n}\n\n// FACTDOUBLE function returns the double factorial of a supplied number. The\n// syntax of the function is:\n//\n//\tFACTDOUBLE(number)\nfunc (fn *formulaFuncs) FACTDOUBLE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FACTDOUBLE requires 1 numeric argument\")\n\t}\n\tval := 1.0\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tif number.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tfor i := math.Trunc(number.Number); i > 1; i -= 2 {\n\t\tval *= i\n\t}\n\treturn newStringFormulaArg(strings.ToUpper(fmt.Sprintf(\"%g\", val)))\n}\n\n// FLOOR function rounds a supplied number towards zero to the nearest\n// multiple of a specified significance. The syntax of the function is:\n//\n//\tFLOOR(number,significance)\nfunc (fn *formulaFuncs) FLOOR(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FLOOR requires 2 numeric arguments\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tsignificance := argsList.Back().Value.(formulaArg).ToNumber()\n\tif significance.Type == ArgError {\n\t\treturn significance\n\t}\n\tif significance.Number < 0 && number.Number >= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"invalid arguments to FLOOR\")\n\t}\n\tval := number.Number\n\tval, res := math.Modf(val / significance.Number)\n\tif res != 0 {\n\t\tif number.Number < 0 && res < 0 {\n\t\t\tval--\n\t\t}\n\t}\n\treturn newNumberFormulaArg(val * significance.Number)\n}\n\n// FLOORdotMATH function rounds a supplied number down to a supplied multiple\n// of significance. The syntax of the function is:\n//\n//\tFLOOR.MATH(number,[significance],[mode])\nfunc (fn *formulaFuncs) FLOORdotMATH(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FLOOR.MATH requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FLOOR.MATH allows at most 3 arguments\")\n\t}\n\tsignificance, mode := 1.0, 1.0\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tif number.Number < 0 {\n\t\tsignificance = -1\n\t}\n\tif argsList.Len() > 1 {\n\t\ts := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\t\tif s.Type == ArgError {\n\t\t\treturn s\n\t\t}\n\t\tsignificance = s.Number\n\t}\n\tif argsList.Len() == 1 {\n\t\treturn newNumberFormulaArg(math.Floor(number.Number))\n\t}\n\tif argsList.Len() > 2 {\n\t\tm := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif m.Type == ArgError {\n\t\t\treturn m\n\t\t}\n\t\tmode = m.Number\n\t}\n\tval, res := math.Modf(number.Number / significance)\n\tif res != 0 && number.Number < 0 && mode > 0 {\n\t\tval--\n\t}\n\treturn newNumberFormulaArg(val * significance)\n}\n\n// FLOORdotPRECISE function rounds a supplied number down to a supplied\n// multiple of significance. The syntax of the function is:\n//\n//\tFLOOR.PRECISE(number,[significance])\nfunc (fn *formulaFuncs) FLOORdotPRECISE(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FLOOR.PRECISE requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FLOOR.PRECISE allows at most 2 arguments\")\n\t}\n\tvar significance float64\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tif number.Number < 0 {\n\t\tsignificance = -1\n\t}\n\tif argsList.Len() == 1 {\n\t\treturn newNumberFormulaArg(math.Floor(number.Number))\n\t}\n\tif argsList.Len() > 1 {\n\t\ts := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif s.Type == ArgError {\n\t\t\treturn s\n\t\t}\n\t\tsignificance = s.Number\n\t\tsignificance = math.Abs(significance)\n\t\tif significance == 0 {\n\t\t\treturn newNumberFormulaArg(significance)\n\t\t}\n\t}\n\tval, res := math.Modf(number.Number / significance)\n\tif res != 0 {\n\t\tif number.Number < 0 {\n\t\t\tval--\n\t\t}\n\t}\n\treturn newNumberFormulaArg(val * significance)\n}\n\n// gcd returns the greatest common divisor of two supplied integers.\nfunc gcd(x, y float64) float64 {\n\tx, y = math.Trunc(x), math.Trunc(y)\n\tif x == 0 {\n\t\treturn y\n\t}\n\tif y == 0 {\n\t\treturn x\n\t}\n\tfor x != y {\n\t\tif x > y {\n\t\t\tx = x - y\n\t\t} else {\n\t\t\ty = y - x\n\t\t}\n\t}\n\treturn x\n}\n\n// GCD function returns the greatest common divisor of two or more supplied\n// integers. The syntax of the function is:\n//\n//\tGCD(number1,[number2],...)\nfunc (fn *formulaFuncs) GCD(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GCD requires at least 1 argument\")\n\t}\n\tvar (\n\t\tval  float64\n\t\tnums []float64\n\t)\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tswitch token.Type {\n\t\tcase ArgString:\n\t\t\tnum := token.ToNumber()\n\t\t\tif num.Type == ArgError {\n\t\t\t\treturn num\n\t\t\t}\n\t\t\tval = num.Number\n\t\tcase ArgNumber:\n\t\t\tval = token.Number\n\t\t}\n\t\tnums = append(nums, val)\n\t}\n\tif nums[0] < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GCD only accepts positive arguments\")\n\t}\n\tif len(nums) == 1 {\n\t\treturn newNumberFormulaArg(nums[0])\n\t}\n\tcd := nums[0]\n\tfor i := 1; i < len(nums); i++ {\n\t\tif nums[i] < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GCD only accepts positive arguments\")\n\t\t}\n\t\tcd = gcd(cd, nums[i])\n\t}\n\treturn newNumberFormulaArg(cd)\n}\n\n// INT function truncates a supplied number down to the closest integer. The\n// syntax of the function is:\n//\n//\tINT(number)\nfunc (fn *formulaFuncs) INT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"INT requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tval, frac := math.Modf(number.Number)\n\tif frac < 0 {\n\t\tval--\n\t}\n\treturn newNumberFormulaArg(val)\n}\n\n// ISOdotCEILING function rounds a supplied number up (regardless of the\n// number's sign), to the nearest multiple of a supplied significance. The\n// syntax of the function is:\n//\n//\tISO.CEILING(number,[significance])\nfunc (fn *formulaFuncs) ISOdotCEILING(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISO.CEILING requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISO.CEILING allows at most 2 arguments\")\n\t}\n\tvar significance float64\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tif number.Number < 0 {\n\t\tsignificance = -1\n\t}\n\tif argsList.Len() == 1 {\n\t\treturn newNumberFormulaArg(math.Ceil(number.Number))\n\t}\n\tif argsList.Len() > 1 {\n\t\ts := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif s.Type == ArgError {\n\t\t\treturn s\n\t\t}\n\t\tsignificance = s.Number\n\t\tsignificance = math.Abs(significance)\n\t\tif significance == 0 {\n\t\t\treturn newNumberFormulaArg(significance)\n\t\t}\n\t}\n\tval, res := math.Modf(number.Number / significance)\n\tif res != 0 {\n\t\tif number.Number > 0 {\n\t\t\tval++\n\t\t}\n\t}\n\treturn newNumberFormulaArg(val * significance)\n}\n\n// lcm returns the least common multiple of two supplied integers.\nfunc lcm(a, b float64) float64 {\n\ta = math.Trunc(a)\n\tb = math.Trunc(b)\n\tif a == 0 && b == 0 {\n\t\treturn 0\n\t}\n\treturn a * b / gcd(a, b)\n}\n\n// LCM function returns the least common multiple of two or more supplied\n// integers. The syntax of the function is:\n//\n//\tLCM(number1,[number2],...)\nfunc (fn *formulaFuncs) LCM(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LCM requires at least 1 argument\")\n\t}\n\tvar (\n\t\tval  float64\n\t\tnums []float64\n\t\terr  error\n\t)\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tswitch token.Type {\n\t\tcase ArgString:\n\t\t\tif token.String == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif val, err = strconv.ParseFloat(token.String, 64); err != nil {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t\t\t}\n\t\tcase ArgNumber:\n\t\t\tval = token.Number\n\t\t}\n\t\tnums = append(nums, val)\n\t}\n\tif nums[0] < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LCM only accepts positive arguments\")\n\t}\n\tif len(nums) == 1 {\n\t\treturn newNumberFormulaArg(nums[0])\n\t}\n\tcm := nums[0]\n\tfor i := 1; i < len(nums); i++ {\n\t\tif nums[i] < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LCM only accepts positive arguments\")\n\t\t}\n\t\tcm = lcm(cm, nums[i])\n\t}\n\treturn newNumberFormulaArg(cm)\n}\n\n// LN function calculates the natural logarithm of a given number. The syntax\n// of the function is:\n//\n//\tLN(number)\nfunc (fn *formulaFuncs) LN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LN requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\treturn newNumberFormulaArg(math.Log(number.Number))\n}\n\n// LOG function calculates the logarithm of a given number, to a supplied\n// base. The syntax of the function is:\n//\n//\tLOG(number,[base])\nfunc (fn *formulaFuncs) LOG(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LOG requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LOG allows at most 2 arguments\")\n\t}\n\tbase := 10.0\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tif argsList.Len() > 1 {\n\t\tb := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif b.Type == ArgError {\n\t\t\treturn b\n\t\t}\n\t\tbase = b.Number\n\t}\n\tif number.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorDIV)\n\t}\n\tif base == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorDIV)\n\t}\n\tif base == 1 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(math.Log(number.Number) / math.Log(base))\n}\n\n// LOG10 function calculates the base 10 logarithm of a given number. The\n// syntax of the function is:\n//\n//\tLOG10(number)\nfunc (fn *formulaFuncs) LOG10(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LOG10 requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\treturn newNumberFormulaArg(math.Log10(number.Number))\n}\n\n// minor function implement a minor of a matrix A is the determinant of some\n// smaller square matrix.\nfunc minor(sqMtx [][]float64, idx int) [][]float64 {\n\tvar ret [][]float64\n\tfor i := range sqMtx {\n\t\tif i == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tvar row []float64\n\t\tfor j := range sqMtx {\n\t\t\tif j == idx {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\trow = append(row, sqMtx[i][j])\n\t\t}\n\t\tret = append(ret, row)\n\t}\n\treturn ret\n}\n\n// det determinant of the 2x2 matrix.\nfunc det(sqMtx [][]float64) float64 {\n\tif len(sqMtx) == 2 {\n\t\tm00 := sqMtx[0][0]\n\t\tm01 := sqMtx[0][1]\n\t\tm10 := sqMtx[1][0]\n\t\tm11 := sqMtx[1][1]\n\t\treturn m00*m11 - m10*m01\n\t}\n\tvar res, sgn float64 = 0, 1\n\tfor j := range sqMtx {\n\t\tres += sgn * sqMtx[0][j] * det(minor(sqMtx, j))\n\t\tsgn *= -1\n\t}\n\treturn res\n}\n\n// newNumberMatrix converts a formula arguments matrix to a number matrix.\nfunc newNumberMatrix(arg formulaArg, phalanx bool) (numMtx [][]float64, ele formulaArg) {\n\trows := len(arg.Matrix)\n\tfor r, row := range arg.Matrix {\n\t\tif phalanx && len(row) != rows {\n\t\t\tele = newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t\treturn\n\t\t}\n\t\tnumMtx = append(numMtx, make([]float64, len(row)))\n\t\tfor c, cell := range row {\n\t\t\tif cell.Type != ArgNumber {\n\t\t\t\tele = newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tnumMtx[r][c] = cell.Number\n\t\t}\n\t}\n\treturn\n}\n\n// newFormulaArgMatrix converts the number formula arguments matrix to a\n// formula arguments matrix.\nfunc newFormulaArgMatrix(numMtx [][]float64) (arg [][]formulaArg) {\n\tfor r, row := range numMtx {\n\t\targ = append(arg, make([]formulaArg, len(row)))\n\t\tfor c, cell := range row {\n\t\t\targ[r][c] = newNumberFormulaArg(cell)\n\t\t}\n\t}\n\treturn\n}\n\n// MDETERM calculates the determinant of a square matrix. The\n// syntax of the function is:\n//\n//\tMDETERM(array)\nfunc (fn *formulaFuncs) MDETERM(argsList *list.List) (result formulaArg) {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MDETERM requires 1 argument\")\n\t}\n\tnumMtx, errArg := newNumberMatrix(argsList.Front().Value.(formulaArg), true)\n\tif errArg.Type == ArgError {\n\t\treturn errArg\n\t}\n\treturn newNumberFormulaArg(det(numMtx))\n}\n\n// cofactorMatrix returns the matrix A of cofactors.\nfunc cofactorMatrix(i, j int, A [][]float64) float64 {\n\tN, sign := len(A), -1.0\n\tif (i+j)%2 == 0 {\n\t\tsign = 1\n\t}\n\tvar B [][]float64\n\tB = append(B, A...)\n\tfor m := 0; m < N; m++ {\n\t\tfor n := j + 1; n < N; n++ {\n\t\t\tB[m][n-1] = B[m][n]\n\t\t}\n\t\tB[m] = B[m][:len(B[m])-1]\n\t}\n\tfor k := i + 1; k < N; k++ {\n\t\tB[k-1] = B[k]\n\t}\n\tB = B[:len(B)-1]\n\treturn sign * det(B)\n}\n\n// adjugateMatrix returns transpose of the cofactor matrix A with Cramer's\n// rule.\nfunc adjugateMatrix(A [][]float64) (adjA [][]float64) {\n\tN := len(A)\n\tvar B [][]float64\n\tfor i := 0; i < N; i++ {\n\t\tadjA = append(adjA, make([]float64, N))\n\t\tfor j := 0; j < N; j++ {\n\t\t\tfor m := 0; m < N; m++ {\n\t\t\t\tfor n := 0; n < N; n++ {\n\t\t\t\t\tfor x := len(B); x <= m; x++ {\n\t\t\t\t\t\tB = append(B, []float64{})\n\t\t\t\t\t}\n\t\t\t\t\tfor k := len(B[m]); k <= n; k++ {\n\t\t\t\t\t\tB[m] = append(B[m], 0)\n\t\t\t\t\t}\n\t\t\t\t\tB[m][n] = A[m][n]\n\t\t\t\t}\n\t\t\t}\n\t\t\tadjA[i][j] = cofactorMatrix(j, i, B)\n\t\t}\n\t}\n\treturn\n}\n\n// MINVERSE function calculates the inverse of a square matrix. The syntax of\n// the function is:\n//\n//\tMINVERSE(array)\nfunc (fn *formulaFuncs) MINVERSE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MINVERSE requires 1 argument\")\n\t}\n\tnumMtx, errArg := newNumberMatrix(argsList.Front().Value.(formulaArg), true)\n\tif errArg.Type == ArgError {\n\t\treturn errArg\n\t}\n\tif detM := det(numMtx); detM != 0 {\n\t\tdatM, invertM := 1/detM, adjugateMatrix(numMtx)\n\t\tfor i := 0; i < len(invertM); i++ {\n\t\t\tfor j := 0; j < len(invertM[i]); j++ {\n\t\t\t\tinvertM[i][j] *= datM\n\t\t\t}\n\t\t}\n\t\treturn newMatrixFormulaArg(newFormulaArgMatrix(invertM))\n\t}\n\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n}\n\n// MMULT function calculates the matrix product of two arrays\n// (representing matrices). The syntax of the function is:\n//\n//\tMMULT(array1,array2)\nfunc (fn *formulaFuncs) MMULT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MMULT requires 2 argument\")\n\t}\n\tarr1 := argsList.Front().Value.(formulaArg)\n\tarr2 := argsList.Back().Value.(formulaArg)\n\tif arr1.Type == ArgNumber && arr2.Type == ArgNumber {\n\t\treturn newNumberFormulaArg(arr1.Number * arr2.Number)\n\t}\n\tnumMtx1, errArg1 := newNumberMatrix(arr1, false)\n\tif errArg1.Type == ArgError {\n\t\treturn errArg1\n\t}\n\tnumMtx2, errArg2 := newNumberMatrix(arr2, false)\n\tif errArg2.Type == ArgError {\n\t\treturn errArg2\n\t}\n\tarray2Rows, array2Cols := len(numMtx2), len(numMtx2[0])\n\tif len(numMtx1[0]) != array2Rows {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tvar numMtx [][]float64\n\tvar row1, row []float64\n\tvar sum float64\n\tfor i := 0; i < len(numMtx1); i++ {\n\t\tnumMtx = append(numMtx, []float64{})\n\t\trow = []float64{}\n\t\trow1 = numMtx1[i]\n\t\tfor j := 0; j < array2Cols; j++ {\n\t\t\tsum = 0\n\t\t\tfor k := 0; k < array2Rows; k++ {\n\t\t\t\tsum += row1[k] * numMtx2[k][j]\n\t\t\t}\n\t\t\tfor l := len(row); l <= j; l++ {\n\t\t\t\trow = append(row, 0)\n\t\t\t}\n\t\t\trow[j] = sum\n\t\t\tnumMtx[i] = row\n\t\t}\n\t}\n\treturn newMatrixFormulaArg(newFormulaArgMatrix(numMtx))\n}\n\n// MOD function returns the remainder of a division between two supplied\n// numbers. The syntax of the function is:\n//\n//\tMOD(number,divisor)\nfunc (fn *formulaFuncs) MOD(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MOD requires 2 numeric arguments\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tdivisor := argsList.Back().Value.(formulaArg).ToNumber()\n\tif divisor.Type == ArgError {\n\t\treturn divisor\n\t}\n\tif divisor.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, \"MOD divide by zero\")\n\t}\n\ttrunc, rem := math.Modf(number.Number / divisor.Number)\n\tif rem < 0 {\n\t\ttrunc--\n\t}\n\treturn newNumberFormulaArg(number.Number - divisor.Number*trunc)\n}\n\n// MROUND function rounds a supplied number up or down to the nearest multiple\n// of a given number. The syntax of the function is:\n//\n//\tMROUND(number,multiple)\nfunc (fn *formulaFuncs) MROUND(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MROUND requires 2 numeric arguments\")\n\t}\n\tn := argsList.Front().Value.(formulaArg).ToNumber()\n\tif n.Type == ArgError {\n\t\treturn n\n\t}\n\tmultiple := argsList.Back().Value.(formulaArg).ToNumber()\n\tif multiple.Type == ArgError {\n\t\treturn multiple\n\t}\n\tif multiple.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif multiple.Number < 0 && n.Number > 0 ||\n\t\tmultiple.Number > 0 && n.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tnumber, res := math.Modf(n.Number / multiple.Number)\n\tif math.Trunc(res+0.5) > 0 {\n\t\tnumber++\n\t}\n\treturn newNumberFormulaArg(number * multiple.Number)\n}\n\n// MULTINOMIAL function calculates the ratio of the factorial of a sum of\n// supplied values to the product of factorials of those values. The syntax of\n// the function is:\n//\n//\tMULTINOMIAL(number1,[number2],...)\nfunc (fn *formulaFuncs) MULTINOMIAL(argsList *list.List) formulaArg {\n\tval, num, denom := 0.0, 0.0, 1.0\n\tvar err error\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tswitch token.Type {\n\t\tcase ArgString:\n\t\t\tif token.String == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif val, err = strconv.ParseFloat(token.String, 64); err != nil {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t\t\t}\n\t\tcase ArgNumber:\n\t\t\tval = token.Number\n\t\t}\n\t\tnum += val\n\t\tdenom *= fact(val)\n\t}\n\treturn newNumberFormulaArg(fact(num) / denom)\n}\n\n// MUNIT function returns the unit matrix for a specified dimension. The\n// syntax of the function is:\n//\n//\tMUNIT(dimension)\nfunc (fn *formulaFuncs) MUNIT(argsList *list.List) (result formulaArg) {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MUNIT requires 1 numeric argument\")\n\t}\n\tdimension := argsList.Back().Value.(formulaArg).ToNumber()\n\tif dimension.Type == ArgError || dimension.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, dimension.Error)\n\t}\n\tmatrix := make([][]formulaArg, 0, int(dimension.Number))\n\tfor i := 0; i < int(dimension.Number); i++ {\n\t\trow := make([]formulaArg, int(dimension.Number))\n\t\tfor j := 0; j < int(dimension.Number); j++ {\n\t\t\tif i == j {\n\t\t\t\trow[j] = newNumberFormulaArg(1.0)\n\t\t\t} else {\n\t\t\t\trow[j] = newNumberFormulaArg(0.0)\n\t\t\t}\n\t\t}\n\t\tmatrix = append(matrix, row)\n\t}\n\treturn newMatrixFormulaArg(matrix)\n}\n\n// ODD function ounds a supplied number away from zero (i.e. rounds a positive\n// number up and a negative number down), to the next odd number. The syntax\n// of the function is:\n//\n//\tODD(number)\nfunc (fn *formulaFuncs) ODD(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ODD requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Back().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tif number.Number == 0 {\n\t\treturn newNumberFormulaArg(1)\n\t}\n\tsign := math.Signbit(number.Number)\n\tm, frac := math.Modf((number.Number - 1) / 2)\n\tval := m*2 + 1\n\tif frac != 0 {\n\t\tif !sign {\n\t\t\tval += 2\n\t\t} else {\n\t\t\tval -= 2\n\t\t}\n\t}\n\treturn newNumberFormulaArg(val)\n}\n\n// PI function returns the value of the mathematical constant π (pi), accurate\n// to 15 digits (14 decimal places). The syntax of the function is:\n//\n//\tPI()\nfunc (fn *formulaFuncs) PI(argsList *list.List) formulaArg {\n\tif argsList.Len() != 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PI accepts no arguments\")\n\t}\n\treturn newNumberFormulaArg(math.Pi)\n}\n\n// POWER function calculates a given number, raised to a supplied power.\n// The syntax of the function is:\n//\n//\tPOWER(number,power)\nfunc (fn *formulaFuncs) POWER(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"POWER requires 2 numeric arguments\")\n\t}\n\tx := argsList.Front().Value.(formulaArg).ToNumber()\n\tif x.Type == ArgError {\n\t\treturn x\n\t}\n\ty := argsList.Back().Value.(formulaArg).ToNumber()\n\tif y.Type == ArgError {\n\t\treturn y\n\t}\n\tif x.Number == 0 && y.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif x.Number == 0 && y.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(math.Pow(x.Number, y.Number))\n}\n\n// PRODUCT function returns the product (multiplication) of a supplied set of\n// numerical values. The syntax of the function is:\n//\n//\tPRODUCT(number1,[number2],...)\nfunc (fn *formulaFuncs) PRODUCT(argsList *list.List) formulaArg {\n\tproduct := 1.0\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tswitch token.Type {\n\t\tcase ArgString:\n\t\t\tnum := token.ToNumber()\n\t\t\tif num.Type != ArgNumber {\n\t\t\t\treturn num\n\t\t\t}\n\t\t\tproduct = product * num.Number\n\t\tcase ArgNumber:\n\t\t\tproduct = product * token.Number\n\t\tcase ArgMatrix:\n\t\t\tfor _, row := range token.Matrix {\n\t\t\t\tfor _, cell := range row {\n\t\t\t\t\tif cell.Type == ArgNumber {\n\t\t\t\t\t\tproduct *= cell.Number\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn newNumberFormulaArg(product)\n}\n\n// QUOTIENT function returns the integer portion of a division between two\n// supplied numbers. The syntax of the function is:\n//\n//\tQUOTIENT(numerator,denominator)\nfunc (fn *formulaFuncs) QUOTIENT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"QUOTIENT requires 2 numeric arguments\")\n\t}\n\tx := argsList.Front().Value.(formulaArg).ToNumber()\n\tif x.Type == ArgError {\n\t\treturn x\n\t}\n\ty := argsList.Back().Value.(formulaArg).ToNumber()\n\tif y.Type == ArgError {\n\t\treturn y\n\t}\n\tif y.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(math.Trunc(x.Number / y.Number))\n}\n\n// RADIANS function converts radians into degrees. The syntax of the function is:\n//\n//\tRADIANS(angle)\nfunc (fn *formulaFuncs) RADIANS(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"RADIANS requires 1 numeric argument\")\n\t}\n\tangle := argsList.Front().Value.(formulaArg).ToNumber()\n\tif angle.Type == ArgError {\n\t\treturn angle\n\t}\n\treturn newNumberFormulaArg(math.Pi / 180.0 * angle.Number)\n}\n\n// RAND function generates a random real number between 0 and 1. The syntax of\n// the function is:\n//\n//\tRAND()\nfunc (fn *formulaFuncs) RAND(argsList *list.List) formulaArg {\n\tif argsList.Len() != 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"RAND accepts no arguments\")\n\t}\n\treturn newNumberFormulaArg(rand.New(rand.NewSource(time.Now().UnixNano())).Float64())\n}\n\n// RANDBETWEEN function generates a random integer between two supplied\n// integers. The syntax of the function is:\n//\n//\tRANDBETWEEN(bottom,top)\nfunc (fn *formulaFuncs) RANDBETWEEN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"RANDBETWEEN requires 2 numeric arguments\")\n\t}\n\tbottom := argsList.Front().Value.(formulaArg).ToNumber()\n\tif bottom.Type == ArgError {\n\t\treturn bottom\n\t}\n\ttop := argsList.Back().Value.(formulaArg).ToNumber()\n\tif top.Type == ArgError {\n\t\treturn top\n\t}\n\tif top.Number < bottom.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tnum := rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(int64(top.Number - bottom.Number + 1))\n\treturn newNumberFormulaArg(float64(num + int64(bottom.Number)))\n}\n\n// romanNumerals defined a numeral system that originated in ancient Rome and\n// remained the usual way of writing numbers throughout Europe well into the\n// Late Middle Ages.\ntype romanNumerals struct {\n\tn float64\n\ts string\n}\n\nvar romanTable = [][]romanNumerals{\n\t{\n\t\t{1000, \"M\"},\n\t\t{900, \"CM\"},\n\t\t{500, \"D\"},\n\t\t{400, \"CD\"},\n\t\t{100, \"C\"},\n\t\t{90, \"XC\"},\n\t\t{50, \"L\"},\n\t\t{40, \"XL\"},\n\t\t{10, \"X\"},\n\t\t{9, \"IX\"},\n\t\t{5, \"V\"},\n\t\t{4, \"IV\"},\n\t\t{1, \"I\"},\n\t},\n\t{\n\t\t{1000, \"M\"},\n\t\t{950, \"LM\"},\n\t\t{900, \"CM\"},\n\t\t{500, \"D\"},\n\t\t{450, \"LD\"},\n\t\t{400, \"CD\"},\n\t\t{100, \"C\"},\n\t\t{95, \"VC\"},\n\t\t{90, \"XC\"},\n\t\t{50, \"L\"},\n\t\t{45, \"VL\"},\n\t\t{40, \"XL\"},\n\t\t{10, \"X\"},\n\t\t{9, \"IX\"},\n\t\t{5, \"V\"},\n\t\t{4, \"IV\"},\n\t\t{1, \"I\"},\n\t},\n\t{\n\t\t{1000, \"M\"},\n\t\t{990, \"XM\"},\n\t\t{950, \"LM\"},\n\t\t{900, \"CM\"},\n\t\t{500, \"D\"},\n\t\t{490, \"XD\"},\n\t\t{450, \"LD\"},\n\t\t{400, \"CD\"},\n\t\t{100, \"C\"},\n\t\t{99, \"IC\"},\n\t\t{90, \"XC\"},\n\t\t{50, \"L\"},\n\t\t{45, \"VL\"},\n\t\t{40, \"XL\"},\n\t\t{10, \"X\"},\n\t\t{9, \"IX\"},\n\t\t{5, \"V\"},\n\t\t{4, \"IV\"},\n\t\t{1, \"I\"},\n\t},\n\t{\n\t\t{1000, \"M\"},\n\t\t{995, \"VM\"},\n\t\t{990, \"XM\"},\n\t\t{950, \"LM\"},\n\t\t{900, \"CM\"},\n\t\t{500, \"D\"},\n\t\t{495, \"VD\"},\n\t\t{490, \"XD\"},\n\t\t{450, \"LD\"},\n\t\t{400, \"CD\"},\n\t\t{100, \"C\"},\n\t\t{99, \"IC\"},\n\t\t{90, \"XC\"},\n\t\t{50, \"L\"},\n\t\t{45, \"VL\"},\n\t\t{40, \"XL\"},\n\t\t{10, \"X\"},\n\t\t{9, \"IX\"},\n\t\t{5, \"V\"},\n\t\t{4, \"IV\"},\n\t\t{1, \"I\"},\n\t},\n\t{\n\t\t{1000, \"M\"},\n\t\t{999, \"IM\"},\n\t\t{995, \"VM\"},\n\t\t{990, \"XM\"},\n\t\t{950, \"LM\"},\n\t\t{900, \"CM\"},\n\t\t{500, \"D\"},\n\t\t{499, \"ID\"},\n\t\t{495, \"VD\"},\n\t\t{490, \"XD\"},\n\t\t{450, \"LD\"},\n\t\t{400, \"CD\"},\n\t\t{100, \"C\"},\n\t\t{99, \"IC\"},\n\t\t{90, \"XC\"},\n\t\t{50, \"L\"},\n\t\t{45, \"VL\"},\n\t\t{40, \"XL\"},\n\t\t{10, \"X\"},\n\t\t{9, \"IX\"},\n\t\t{5, \"V\"},\n\t\t{4, \"IV\"},\n\t\t{1, \"I\"},\n\t},\n}\n\n// ROMAN function converts an arabic number to Roman. I.e. for a supplied\n// integer, the function returns a text string depicting the roman numeral\n// form of the number. The syntax of the function is:\n//\n//\tROMAN(number,[form])\nfunc (fn *formulaFuncs) ROMAN(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ROMAN requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ROMAN allows at most 2 arguments\")\n\t}\n\tvar form int\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tif argsList.Len() > 1 {\n\t\tf := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif f.Type == ArgError {\n\t\t\treturn f\n\t\t}\n\t\tform = int(f.Number)\n\t\tif form < 0 {\n\t\t\tform = 0\n\t\t} else if form > 4 {\n\t\t\tform = 4\n\t\t}\n\t}\n\tdecimalTable := romanTable[0]\n\tswitch form {\n\tcase 1:\n\t\tdecimalTable = romanTable[1]\n\tcase 2:\n\t\tdecimalTable = romanTable[2]\n\tcase 3:\n\t\tdecimalTable = romanTable[3]\n\tcase 4:\n\t\tdecimalTable = romanTable[4]\n\t}\n\tval := math.Trunc(number.Number)\n\tbuf := bytes.Buffer{}\n\tfor _, r := range decimalTable {\n\t\tfor val >= r.n {\n\t\t\tbuf.WriteString(r.s)\n\t\t\tval -= r.n\n\t\t}\n\t}\n\treturn newStringFormulaArg(buf.String())\n}\n\ntype roundMode byte\n\nconst (\n\tclosest roundMode = iota\n\tdown\n\tup\n)\n\n// round rounds a supplied number up or down.\nfunc (fn *formulaFuncs) round(number, digits float64, mode roundMode) float64 {\n\tvar significance float64\n\tif digits > 0 {\n\t\tsignificance = math.Pow(1/10.0, digits)\n\t} else {\n\t\tsignificance = math.Pow(10.0, -digits)\n\t}\n\tval, res := math.Modf(number / significance)\n\tswitch mode {\n\tcase closest:\n\t\tconst eps = 0.499999999\n\t\tif res >= eps {\n\t\t\tval++\n\t\t} else if res <= -eps {\n\t\t\tval--\n\t\t}\n\tcase down:\n\tcase up:\n\t\tif res > 0 {\n\t\t\tval++\n\t\t} else if res < 0 {\n\t\t\tval--\n\t\t}\n\t}\n\treturn val * significance\n}\n\n// ROUND function rounds a supplied number up or down, to a specified number\n// of decimal places. The syntax of the function is:\n//\n//\tROUND(number,num_digits)\nfunc (fn *formulaFuncs) ROUND(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ROUND requires 2 numeric arguments\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tdigits := argsList.Back().Value.(formulaArg).ToNumber()\n\tif digits.Type == ArgError {\n\t\treturn digits\n\t}\n\treturn newNumberFormulaArg(fn.round(number.Number, digits.Number, closest))\n}\n\n// ROUNDDOWN function rounds a supplied number down towards zero, to a\n// specified number of decimal places. The syntax of the function is:\n//\n//\tROUNDDOWN(number,num_digits)\nfunc (fn *formulaFuncs) ROUNDDOWN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ROUNDDOWN requires 2 numeric arguments\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tdigits := argsList.Back().Value.(formulaArg).ToNumber()\n\tif digits.Type == ArgError {\n\t\treturn digits\n\t}\n\treturn newNumberFormulaArg(fn.round(number.Number, digits.Number, down))\n}\n\n// ROUNDUP function rounds a supplied number up, away from zero, to a\n// specified number of decimal places. The syntax of the function is:\n//\n//\tROUNDUP(number,num_digits)\nfunc (fn *formulaFuncs) ROUNDUP(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ROUNDUP requires 2 numeric arguments\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tdigits := argsList.Back().Value.(formulaArg).ToNumber()\n\tif digits.Type == ArgError {\n\t\treturn digits\n\t}\n\treturn newNumberFormulaArg(fn.round(number.Number, digits.Number, up))\n}\n\n// SEC function calculates the secant of a given angle. The syntax of the\n// function is:\n//\n//\tSEC(number)\nfunc (fn *formulaFuncs) SEC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SEC requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\treturn newNumberFormulaArg(math.Cos(number.Number))\n}\n\n// SECH function calculates the hyperbolic secant (sech) of a supplied angle.\n// The syntax of the function is:\n//\n//\tSECH(number)\nfunc (fn *formulaFuncs) SECH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SECH requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\treturn newNumberFormulaArg(1 / math.Cosh(number.Number))\n}\n\n// SERIESSUM function returns the sum of a power series. The syntax of the\n// function is:\n//\n//\tSERIESSUM(x,n,m,coefficients)\nfunc (fn *formulaFuncs) SERIESSUM(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SERIESSUM requires 4 arguments\")\n\t}\n\tvar x, n, m formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif n = argsList.Front().Next().Value.(formulaArg).ToNumber(); n.Type != ArgNumber {\n\t\treturn n\n\t}\n\tif m = argsList.Front().Next().Next().Value.(formulaArg).ToNumber(); m.Type != ArgNumber {\n\t\treturn m\n\t}\n\tvar result, i float64\n\tfor _, coefficient := range argsList.Back().Value.(formulaArg).ToList() {\n\t\tif coefficient.Value() == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tnum := coefficient.ToNumber()\n\t\tif num.Type != ArgNumber {\n\t\t\treturn num\n\t\t}\n\t\tresult += num.Number * math.Pow(x.Number, n.Number+(m.Number*i))\n\t\ti++\n\t}\n\treturn newNumberFormulaArg(result)\n}\n\n// SIGN function returns the arithmetic sign (+1, -1 or 0) of a supplied\n// number. I.e. if the number is positive, the Sign function returns +1, if\n// the number is negative, the function returns -1 and if the number is 0\n// (zero), the function returns 0. The syntax of the function is:\n//\n//\tSIGN(number)\nfunc (fn *formulaFuncs) SIGN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SIGN requires 1 numeric argument\")\n\t}\n\tval := argsList.Front().Value.(formulaArg).ToNumber()\n\tif val.Type == ArgError {\n\t\treturn val\n\t}\n\tif val.Number < 0 {\n\t\treturn newNumberFormulaArg(-1)\n\t}\n\tif val.Number > 0 {\n\t\treturn newNumberFormulaArg(1)\n\t}\n\treturn newNumberFormulaArg(0)\n}\n\n// SIN function calculates the sine of a given angle. The syntax of the\n// function is:\n//\n//\tSIN(number)\nfunc (fn *formulaFuncs) SIN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SIN requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\treturn newNumberFormulaArg(math.Sin(number.Number))\n}\n\n// SINH function calculates the hyperbolic sine (sinh) of a supplied number.\n// The syntax of the function is:\n//\n//\tSINH(number)\nfunc (fn *formulaFuncs) SINH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SINH requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\treturn newNumberFormulaArg(math.Sinh(number.Number))\n}\n\n// SQRT function calculates the positive square root of a supplied number. The\n// syntax of the function is:\n//\n//\tSQRT(number)\nfunc (fn *formulaFuncs) SQRT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SQRT requires 1 numeric argument\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg).ToNumber()\n\tif value.Type == ArgError {\n\t\treturn value\n\t}\n\tif value.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(math.Sqrt(value.Number))\n}\n\n// SQRTPI function returns the square root of a supplied number multiplied by\n// the mathematical constant, π. The syntax of the function is:\n//\n//\tSQRTPI(number)\nfunc (fn *formulaFuncs) SQRTPI(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SQRTPI requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\treturn newNumberFormulaArg(math.Sqrt(number.Number * math.Pi))\n}\n\n// STDEV function calculates the sample standard deviation of a supplied set\n// of values. The syntax of the function is:\n//\n//\tSTDEV(number1,[number2],...)\nfunc (fn *formulaFuncs) STDEV(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"STDEV requires at least 1 argument\")\n\t}\n\treturn fn.stdev(false, argsList)\n}\n\n// STDEVdotS function calculates the sample standard deviation of a supplied\n// set of values. The syntax of the function is:\n//\n//\tSTDEV.S(number1,[number2],...)\nfunc (fn *formulaFuncs) STDEVdotS(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"STDEV.S requires at least 1 argument\")\n\t}\n\treturn fn.stdev(false, argsList)\n}\n\n// STDEVA function estimates standard deviation based on a sample. The\n// standard deviation is a measure of how widely values are dispersed from\n// the average value (the mean). The syntax of the function is:\n//\n//\tSTDEVA(number1,[number2],...)\nfunc (fn *formulaFuncs) STDEVA(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"STDEVA requires at least 1 argument\")\n\t}\n\treturn fn.stdev(true, argsList)\n}\n\n// calcStdevPow is part of the implementation stdev.\nfunc calcStdevPow(result, count float64, n, m formulaArg) (float64, float64) {\n\tif result == -1 {\n\t\tresult = math.Pow(n.Number-m.Number, 2)\n\t} else {\n\t\tresult += math.Pow(n.Number-m.Number, 2)\n\t}\n\tcount++\n\treturn result, count\n}\n\n// calcStdev is part of the implementation stdev.\nfunc calcStdev(stdeva bool, result, count float64, mean, token formulaArg) (float64, float64) {\n\tfor _, row := range token.ToList() {\n\t\tif row.Type == ArgNumber || row.Type == ArgString {\n\t\t\tif !stdeva && (row.Value() == \"TRUE\" || row.Value() == \"FALSE\") {\n\t\t\t\tcontinue\n\t\t\t} else if stdeva && (row.Value() == \"TRUE\" || row.Value() == \"FALSE\") {\n\t\t\t\tnum := row.ToBool()\n\t\t\t\tif num.Type == ArgNumber {\n\t\t\t\t\tresult, count = calcStdevPow(result, count, num, mean)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnum := row.ToNumber()\n\t\t\t\tif num.Type == ArgNumber {\n\t\t\t\t\tresult, count = calcStdevPow(result, count, num, mean)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn result, count\n}\n\n// stdev is an implementation of the formula functions STDEV and STDEVA.\nfunc (fn *formulaFuncs) stdev(stdeva bool, argsList *list.List) formulaArg {\n\tcount, result := -1.0, -1.0\n\tvar mean formulaArg\n\tif stdeva {\n\t\tmean = fn.AVERAGEA(argsList)\n\t} else {\n\t\tmean = fn.AVERAGE(argsList)\n\t}\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tswitch token.Type {\n\t\tcase ArgString, ArgNumber:\n\t\t\tif !stdeva && (token.Value() == \"TRUE\" || token.Value() == \"FALSE\") {\n\t\t\t\tcontinue\n\t\t\t} else if stdeva && (token.Value() == \"TRUE\" || token.Value() == \"FALSE\") {\n\t\t\t\tnum := token.ToBool()\n\t\t\t\tif num.Type == ArgNumber {\n\t\t\t\t\tresult, count = calcStdevPow(result, count, num, mean)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnum := token.ToNumber()\n\t\t\t\tif num.Type == ArgNumber {\n\t\t\t\t\tresult, count = calcStdevPow(result, count, num, mean)\n\t\t\t\t}\n\t\t\t}\n\t\tcase ArgList, ArgMatrix:\n\t\t\tresult, count = calcStdev(stdeva, result, count, mean, token)\n\t\t}\n\t}\n\tif count > 0 && result >= 0 {\n\t\treturn newNumberFormulaArg(math.Sqrt(result / count))\n\t}\n\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n}\n\n// POISSONdotDIST function calculates the Poisson Probability Mass Function or\n// the Cumulative Poisson Probability Function for a supplied set of\n// parameters. The syntax of the function is:\n//\n//\tPOISSON.DIST(x,mean,cumulative)\nfunc (fn *formulaFuncs) POISSONdotDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"POISSON.DIST requires 3 arguments\")\n\t}\n\treturn fn.POISSON(argsList)\n}\n\n// POISSON function calculates the Poisson Probability Mass Function or the\n// Cumulative Poisson Probability Function for a supplied set of parameters.\n// The syntax of the function is:\n//\n//\tPOISSON(x,mean,cumulative)\nfunc (fn *formulaFuncs) POISSON(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"POISSON requires 3 arguments\")\n\t}\n\tvar x, mean, cumulative formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif mean = argsList.Front().Next().Value.(formulaArg).ToNumber(); mean.Type != ArgNumber {\n\t\treturn mean\n\t}\n\tif cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError {\n\t\treturn cumulative\n\t}\n\tif x.Number < 0 || mean.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tif cumulative.Number == 1 {\n\t\tsummer := 0.0\n\t\tfloor := math.Floor(x.Number)\n\t\tfor i := 0; i <= int(floor); i++ {\n\t\t\tsummer += math.Pow(mean.Number, float64(i)) / fact(float64(i))\n\t\t}\n\t\treturn newNumberFormulaArg(math.Exp(0-mean.Number) * summer)\n\t}\n\treturn newNumberFormulaArg(math.Exp(0-mean.Number) * math.Pow(mean.Number, x.Number) / fact(x.Number))\n}\n\n// prepareProbArgs checking and prepare arguments for the formula function\n// PROB.\nfunc prepareProbArgs(argsList *list.List) []formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn []formulaArg{newErrorFormulaArg(formulaErrorVALUE, \"PROB requires at least 3 arguments\")}\n\t}\n\tif argsList.Len() > 4 {\n\t\treturn []formulaArg{newErrorFormulaArg(formulaErrorVALUE, \"PROB requires at most 4 arguments\")}\n\t}\n\tvar lower, upper formulaArg\n\txRange := argsList.Front().Value.(formulaArg)\n\tprobRange := argsList.Front().Next().Value.(formulaArg)\n\tif lower = argsList.Front().Next().Next().Value.(formulaArg); lower.Type != ArgNumber {\n\t\treturn []formulaArg{newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)}\n\t}\n\tupper = lower\n\tif argsList.Len() == 4 {\n\t\tif upper = argsList.Back().Value.(formulaArg); upper.Type != ArgNumber {\n\t\t\treturn []formulaArg{newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)}\n\t\t}\n\t}\n\tnR1, nR2 := len(xRange.Matrix), len(probRange.Matrix)\n\tif nR1 == 0 || nR2 == 0 {\n\t\treturn []formulaArg{newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)}\n\t}\n\tif nR1 != nR2 {\n\t\treturn []formulaArg{newErrorFormulaArg(formulaErrorNA, formulaErrorNA)}\n\t}\n\tnC1, nC2 := len(xRange.Matrix[0]), len(probRange.Matrix[0])\n\tif nC1 != nC2 {\n\t\treturn []formulaArg{newErrorFormulaArg(formulaErrorNA, formulaErrorNA)}\n\t}\n\treturn []formulaArg{xRange, probRange, lower, upper}\n}\n\n// PROB function calculates the probability associated with a given range. The\n// syntax of the function is:\n//\n//\tPROB(x_range,prob_range,lower_limit,[upper_limit])\nfunc (fn *formulaFuncs) PROB(argsList *list.List) formulaArg {\n\targs := prepareProbArgs(argsList)\n\tif len(args) == 1 {\n\t\treturn args[0]\n\t}\n\txRange, probRange, lower, upper := args[0], args[1], args[2], args[3]\n\tvar sum, res, fP, fW float64\n\tvar stop bool\n\tfor r := 0; r < len(xRange.Matrix) && !stop; r++ {\n\t\tfor c := 0; c < len(xRange.Matrix[0]) && !stop; c++ {\n\t\t\tp := probRange.Matrix[r][c]\n\t\t\tx := xRange.Matrix[r][c]\n\t\t\tif p.Type == ArgNumber && x.Type == ArgNumber {\n\t\t\t\tif fP, fW = p.Number, x.Number; fP < 0 || fP > 1 {\n\t\t\t\t\tstop = true\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif sum += fP; fW >= lower.Number && fW <= upper.Number {\n\t\t\t\t\tres += fP\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\tif stop || math.Abs(sum-1) > 1.0e-7 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(res)\n}\n\n// SUBTOTAL function performs a specified calculation (e.g. the sum, product,\n// average, etc.) for a supplied set of values. The syntax of the function is:\n//\n//\tSUBTOTAL(function_num,ref1,[ref2],...)\nfunc (fn *formulaFuncs) SUBTOTAL(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SUBTOTAL requires at least 2 arguments\")\n\t}\n\tvar fnNum formulaArg\n\tif fnNum = argsList.Front().Value.(formulaArg).ToNumber(); fnNum.Type != ArgNumber {\n\t\treturn fnNum\n\t}\n\tsubFn, ok := map[int]func(argsList *list.List) formulaArg{\n\t\t1: fn.AVERAGE, 101: fn.AVERAGE,\n\t\t2: fn.COUNT, 102: fn.COUNT,\n\t\t3: fn.COUNTA, 103: fn.COUNTA,\n\t\t4: fn.MAX, 104: fn.MAX,\n\t\t5: fn.MIN, 105: fn.MIN,\n\t\t6: fn.PRODUCT, 106: fn.PRODUCT,\n\t\t7: fn.STDEV, 107: fn.STDEV,\n\t\t8: fn.STDEVP, 108: fn.STDEVP,\n\t\t9: fn.SUM, 109: fn.SUM,\n\t\t10: fn.VAR, 110: fn.VAR,\n\t\t11: fn.VARP, 111: fn.VARP,\n\t}[int(fnNum.Number)]\n\tif !ok {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SUBTOTAL has invalid function_num\")\n\t}\n\tsubArgList := list.New().Init()\n\tfor arg := argsList.Front().Next(); arg != nil; arg = arg.Next() {\n\t\tsubArgList.PushBack(arg.Value.(formulaArg))\n\t}\n\treturn subFn(subArgList)\n}\n\n// SUM function adds together a supplied set of numbers and returns the sum of\n// these values. The syntax of the function is:\n//\n//\tSUM(number1,[number2],...)\nfunc (fn *formulaFuncs) SUM(argsList *list.List) formulaArg {\n\tvar sum float64\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tswitch token.Type {\n\t\tcase ArgError:\n\t\t\treturn token\n\t\tcase ArgString:\n\t\t\tif num := token.ToNumber(); num.Type == ArgNumber {\n\t\t\t\tsum += num.Number\n\t\t\t}\n\t\tcase ArgNumber:\n\t\t\tsum += token.Number\n\t\tcase ArgMatrix:\n\t\t\tfor _, row := range token.Matrix {\n\t\t\t\tfor _, value := range row {\n\t\t\t\t\tif num := value.ToNumber(); num.Type == ArgNumber {\n\t\t\t\t\t\tsum += num.Number\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn newNumberFormulaArg(sum)\n}\n\n// SUMIF function finds the values in a supplied array, that satisfy a given\n// criteria, and returns the sum of the corresponding values in a second\n// supplied array. The syntax of the function is:\n//\n//\tSUMIF(range,criteria,[sum_range])\nfunc (fn *formulaFuncs) SUMIF(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SUMIF requires at least 2 arguments\")\n\t}\n\tcriteria := formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg))\n\trangeMtx := argsList.Front().Value.(formulaArg).Matrix\n\tvar sumRange [][]formulaArg\n\tif argsList.Len() == 3 {\n\t\tsumRange = argsList.Back().Value.(formulaArg).Matrix\n\t}\n\tvar sum float64\n\tvar arg formulaArg\n\tfor rowIdx, row := range rangeMtx {\n\t\tfor colIdx, cell := range row {\n\t\t\targ = cell\n\t\t\tif arg.Type == ArgEmpty {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif ok, _ := formulaCriteriaEval(arg, criteria); ok {\n\t\t\t\tif argsList.Len() == 3 {\n\t\t\t\t\tif len(sumRange) > rowIdx && len(sumRange[rowIdx]) > colIdx {\n\t\t\t\t\t\targ = sumRange[rowIdx][colIdx]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif arg.Type == ArgNumber {\n\t\t\t\t\tsum += arg.Number\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn newNumberFormulaArg(sum)\n}\n\n// SUMIFS function finds values in one or more supplied arrays, that satisfy a\n// set of criteria, and returns the sum of the corresponding values in a\n// further supplied array. The syntax of the function is:\n//\n//\tSUMIFS(sum_range,criteria_range1,criteria1,[criteria_range2,criteria2],...)\nfunc (fn *formulaFuncs) SUMIFS(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SUMIFS requires at least 3 arguments\")\n\t}\n\tif argsList.Len()%2 != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tvar args []formulaArg\n\tsum, sumRange := 0.0, argsList.Front().Value.(formulaArg).Matrix\n\tfor arg := argsList.Front().Next(); arg != nil; arg = arg.Next() {\n\t\targs = append(args, arg.Value.(formulaArg))\n\t}\n\tfor _, ref := range formulaIfsMatch(args) {\n\t\tif ref.Row >= len(sumRange) || ref.Col >= len(sumRange[ref.Row]) {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t\tif num := sumRange[ref.Row][ref.Col].ToNumber(); num.Type == ArgNumber {\n\t\t\tsum += num.Number\n\t\t}\n\t}\n\treturn newNumberFormulaArg(sum)\n}\n\n// sumproduct is an implementation of the formula function SUMPRODUCT.\nfunc (fn *formulaFuncs) sumproduct(argsList *list.List) formulaArg {\n\tvar (\n\t\targType ArgType\n\t\tn       int\n\t\tres     []float64\n\t\tsum     float64\n\t)\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tif argType == ArgUnknown {\n\t\t\targType = token.Type\n\t\t}\n\t\tif token.Type != argType {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t\tswitch token.Type {\n\t\tcase ArgString, ArgNumber:\n\t\t\tif num := token.ToNumber(); num.Type == ArgNumber {\n\t\t\t\tsum = fn.PRODUCT(argsList).Number\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\tcase ArgMatrix:\n\t\t\targs := token.ToList()\n\t\t\tif res == nil {\n\t\t\t\tn = len(args)\n\t\t\t\tres = make([]float64, n)\n\t\t\t\tfor i := range res {\n\t\t\t\t\tres[i] = 1.0\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(args) != n {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t\t}\n\t\t\tfor i, value := range args {\n\t\t\t\tnum := value.ToNumber()\n\t\t\t\tif num.Type != ArgNumber && value.Value() != \"\" {\n\t\t\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t\t\t}\n\t\t\t\tres[i] = res[i] * num.Number\n\t\t\t}\n\t\t}\n\t}\n\tfor _, r := range res {\n\t\tsum += r\n\t}\n\treturn newNumberFormulaArg(sum)\n}\n\n// SUMPRODUCT function returns the sum of the products of the corresponding\n// values in a set of supplied arrays. The syntax of the function is:\n//\n//\tSUMPRODUCT(array1,[array2],[array3],...)\nfunc (fn *formulaFuncs) SUMPRODUCT(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SUMPRODUCT requires at least 1 argument\")\n\t}\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\tif token := arg.Value.(formulaArg); token.Type == ArgError {\n\t\t\treturn token\n\t\t}\n\t}\n\treturn fn.sumproduct(argsList)\n}\n\n// SUMSQ function returns the sum of squares of a supplied set of values. The\n// syntax of the function is:\n//\n//\tSUMSQ(number1,[number2],...)\nfunc (fn *formulaFuncs) SUMSQ(argsList *list.List) formulaArg {\n\tvar val, sq float64\n\tvar err error\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tswitch token.Type {\n\t\tcase ArgString:\n\t\t\tif token.String == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif val, err = strconv.ParseFloat(token.String, 64); err != nil {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t\t\t}\n\t\t\tsq += val * val\n\t\tcase ArgNumber:\n\t\t\tsq += token.Number * token.Number\n\t\tcase ArgMatrix:\n\t\t\tfor _, row := range token.Matrix {\n\t\t\t\tfor _, value := range row {\n\t\t\t\t\tif value.Value() == \"\" {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif val, err = strconv.ParseFloat(value.Value(), 64); err != nil {\n\t\t\t\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t\t\t\t\t}\n\t\t\t\t\tsq += val * val\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn newNumberFormulaArg(sq)\n}\n\n// sumx is an implementation of the formula functions SUMX2MY2, SUMX2PY2 and\n// SUMXMY2.\nfunc (fn *formulaFuncs) sumx(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 2 arguments\", name))\n\t}\n\tarray1 := argsList.Front().Value.(formulaArg)\n\tarray2 := argsList.Back().Value.(formulaArg)\n\tleft, right := array1.ToList(), array2.ToList()\n\tn := len(left)\n\tif n != len(right) {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tresult := 0.0\n\tfor i := 0; i < n; i++ {\n\t\tif lhs, rhs := left[i].ToNumber(), right[i].ToNumber(); lhs.Number != 0 && rhs.Number != 0 {\n\t\t\tswitch name {\n\t\t\tcase \"SUMX2MY2\":\n\t\t\t\tresult += lhs.Number*lhs.Number - rhs.Number*rhs.Number\n\t\t\tcase \"SUMX2PY2\":\n\t\t\t\tresult += lhs.Number*lhs.Number + rhs.Number*rhs.Number\n\t\t\tdefault:\n\t\t\t\tresult += (lhs.Number - rhs.Number) * (lhs.Number - rhs.Number)\n\t\t\t}\n\t\t}\n\t}\n\treturn newNumberFormulaArg(result)\n}\n\n// SUMX2MY2 function returns the sum of the differences of squares of two\n// supplied sets of values. The syntax of the function is:\n//\n//\tSUMX2MY2(array_x,array_y)\nfunc (fn *formulaFuncs) SUMX2MY2(argsList *list.List) formulaArg {\n\treturn fn.sumx(\"SUMX2MY2\", argsList)\n}\n\n// SUMX2PY2 function returns the sum of the sum of squares of two supplied sets\n// of values. The syntax of the function is:\n//\n//\tSUMX2PY2(array_x,array_y)\nfunc (fn *formulaFuncs) SUMX2PY2(argsList *list.List) formulaArg {\n\treturn fn.sumx(\"SUMX2PY2\", argsList)\n}\n\n// SUMXMY2 function returns the sum of the squares of differences between\n// corresponding values in two supplied arrays. The syntax of the function\n// is:\n//\n//\tSUMXMY2(array_x,array_y)\nfunc (fn *formulaFuncs) SUMXMY2(argsList *list.List) formulaArg {\n\treturn fn.sumx(\"SUMXMY2\", argsList)\n}\n\n// TAN function calculates the tangent of a given angle. The syntax of the\n// function is:\n//\n//\tTAN(number)\nfunc (fn *formulaFuncs) TAN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TAN requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\treturn newNumberFormulaArg(math.Tan(number.Number))\n}\n\n// TANH function calculates the hyperbolic tangent (tanh) of a supplied\n// number. The syntax of the function is:\n//\n//\tTANH(number)\nfunc (fn *formulaFuncs) TANH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TANH requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\treturn newNumberFormulaArg(math.Tanh(number.Number))\n}\n\n// TRUNC function truncates a supplied number to a specified number of decimal\n// places. The syntax of the function is:\n//\n//\tTRUNC(number,[number_digits])\nfunc (fn *formulaFuncs) TRUNC(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TRUNC requires at least 1 argument\")\n\t}\n\tvar digits, adjust, rtrim float64\n\tvar err error\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type == ArgError {\n\t\treturn number\n\t}\n\tif argsList.Len() > 1 {\n\t\td := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif d.Type == ArgError {\n\t\t\treturn d\n\t\t}\n\t\tdigits = d.Number\n\t\tdigits = math.Floor(digits)\n\t}\n\tadjust = math.Pow(10, digits)\n\tx := int((math.Abs(number.Number) - math.Abs(float64(int(number.Number)))) * adjust)\n\tif x != 0 {\n\t\tif rtrim, err = strconv.ParseFloat(strings.TrimRight(strconv.Itoa(x), \"0\"), 64); err != nil {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t\t}\n\t}\n\tif (digits > 0) && (rtrim < adjust/10) {\n\t\treturn newNumberFormulaArg(number.Number)\n\t}\n\treturn newNumberFormulaArg(float64(int(number.Number*adjust)) / adjust)\n}\n\n// Statistical Functions\n\n// AVEDEV function calculates the average deviation of a supplied set of\n// values. The syntax of the function is:\n//\n//\tAVEDEV(number1,[number2],...)\nfunc (fn *formulaFuncs) AVEDEV(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"AVEDEV requires at least 1 argument\")\n\t}\n\taverage := fn.AVERAGE(argsList)\n\tif average.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tresult, count := 0.0, 0.0\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\tnum := arg.Value.(formulaArg).ToNumber()\n\t\tif num.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t\tresult += math.Abs(num.Number - average.Number)\n\t\tcount++\n\t}\n\treturn newNumberFormulaArg(result / count)\n}\n\n// AVERAGE function returns the arithmetic mean of a list of supplied numbers.\n// The syntax of the function is:\n//\n//\tAVERAGE(number1,[number2],...)\nfunc (fn *formulaFuncs) AVERAGE(argsList *list.List) formulaArg {\n\tvar args []formulaArg\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\targs = append(args, arg.Value.(formulaArg))\n\t}\n\tcount, sum := fn.countSum(false, args)\n\tif count == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(sum / count)\n}\n\n// AVERAGEA function returns the arithmetic mean of a list of supplied numbers\n// with text cell and zero values. The syntax of the function is:\n//\n//\tAVERAGEA(number1,[number2],...)\nfunc (fn *formulaFuncs) AVERAGEA(argsList *list.List) formulaArg {\n\tvar args []formulaArg\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\targs = append(args, arg.Value.(formulaArg))\n\t}\n\tcount, sum := fn.countSum(true, args)\n\tif count == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(sum / count)\n}\n\n// AVERAGEIF function finds the values in a supplied array that satisfy a\n// specified criteria, and returns the average (i.e. the statistical mean) of\n// the corresponding values in a second supplied array. The syntax of the\n// function is:\n//\n//\tAVERAGEIF(range,criteria,[average_range])\nfunc (fn *formulaFuncs) AVERAGEIF(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"AVERAGEIF requires at least 2 arguments\")\n\t}\n\tvar (\n\t\tcriteria  = formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg))\n\t\trangeMtx  = argsList.Front().Value.(formulaArg).Matrix\n\t\tcellRange [][]formulaArg\n\t\targs      []formulaArg\n\t\tval       float64\n\t\terr       error\n\t\tok        bool\n\t)\n\tif argsList.Len() == 3 {\n\t\tcellRange = argsList.Back().Value.(formulaArg).Matrix\n\t}\n\tfor rowIdx, row := range rangeMtx {\n\t\tfor colIdx, col := range row {\n\t\t\tfromVal := col.Value()\n\t\t\tif fromVal == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif col.Type == ArgString && criteria.Condition.Type != ArgString {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tok, _ = formulaCriteriaEval(col, criteria)\n\t\t\tif ok {\n\t\t\t\tif argsList.Len() == 3 {\n\t\t\t\t\tif len(cellRange) > rowIdx && len(cellRange[rowIdx]) > colIdx {\n\t\t\t\t\t\tfromVal = cellRange[rowIdx][colIdx].Value()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif val, err = strconv.ParseFloat(fromVal, 64); err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\targs = append(args, newNumberFormulaArg(val))\n\t\t\t}\n\t\t}\n\t}\n\tcount, sum := fn.countSum(false, args)\n\tif count == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(sum / count)\n}\n\n// AVERAGEIFS function finds entries in one or more arrays, that satisfy a set\n// of supplied criteria, and returns the average (i.e. the statistical mean)\n// of the corresponding values in a further supplied array. The syntax of the\n// function is:\n//\n//\tAVERAGEIFS(average_range,criteria_range1,criteria1,[criteria_range2,criteria2],...)\nfunc (fn *formulaFuncs) AVERAGEIFS(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"AVERAGEIFS requires at least 3 arguments\")\n\t}\n\tif argsList.Len()%2 != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tvar args []formulaArg\n\tsum, sumRange := 0.0, argsList.Front().Value.(formulaArg).Matrix\n\tfor arg := argsList.Front().Next(); arg != nil; arg = arg.Next() {\n\t\targs = append(args, arg.Value.(formulaArg))\n\t}\n\tcount := 0.0\n\tfor _, ref := range formulaIfsMatch(args) {\n\t\tif num := sumRange[ref.Row][ref.Col].ToNumber(); num.Type == ArgNumber {\n\t\t\tsum += num.Number\n\t\t\tcount++\n\t\t}\n\t}\n\tif count == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, \"AVERAGEIF divide by zero\")\n\t}\n\treturn newNumberFormulaArg(sum / count)\n}\n\n// getBetaHelperContFrac continued fractions for the beta function.\nfunc getBetaHelperContFrac(fX, fA, fB float64) float64 {\n\tvar a1, b1, a2, b2, fnorm, cfnew, cf, rm float64\n\ta1, b1, b2 = 1, 1, 1-(fA+fB)/(fA+1)*fX\n\tif b2 == 0 {\n\t\ta2, fnorm, cf = 0, 1, 1\n\t} else {\n\t\ta2, fnorm = 1, 1/b2\n\t\tcf = a2 * fnorm\n\t}\n\tcfnew, rm = 1, 1\n\tfMaxIter, fMachEps := 50000.0, 2.22045e-016\n\tbfinished := false\n\tfor rm < fMaxIter && !bfinished {\n\t\tapl2m := fA + 2*rm\n\t\td2m := rm * (fB - rm) * fX / ((apl2m - 1) * apl2m)\n\t\td2m1 := -(fA + rm) * (fA + fB + rm) * fX / (apl2m * (apl2m + 1))\n\t\ta1 = math.FMA(d2m, a1, a2) * fnorm\n\t\tb1 = math.FMA(d2m, b1, b2) * fnorm\n\t\ta2 = math.FMA(d2m1*a2, fnorm, a1)\n\t\tb2 = math.FMA(d2m1*b2, fnorm, b1)\n\t\tif b2 != 0 {\n\t\t\tfnorm = 1 / b2\n\t\t\tcfnew = a2 * fnorm\n\t\t\tbfinished = math.Abs(cf-cfnew) < math.Abs(cf)*fMachEps\n\t\t}\n\t\tcf = cfnew\n\t\trm++\n\t}\n\treturn cf\n}\n\n// getLanczosSum uses a variant of the Lanczos sum with a rational function.\nfunc getLanczosSum(fZ float64) float64 {\n\tnum := []float64{\n\t\t23531376880.41075968857200767445163675473,\n\t\t42919803642.64909876895789904700198885093,\n\t\t35711959237.35566804944018545154716670596,\n\t\t17921034426.03720969991975575445893111267,\n\t\t6039542586.35202800506429164430729792107,\n\t\t1439720407.311721673663223072794912393972,\n\t\t248874557.8620541565114603864132294232163,\n\t\t31426415.58540019438061423162831820536287,\n\t\t2876370.628935372441225409051620849613599,\n\t\t186056.2653952234950402949897160456992822,\n\t\t8071.672002365816210638002902272250613822,\n\t\t210.8242777515793458725097339207133627117,\n\t\t2.506628274631000270164908177133837338626,\n\t}\n\tdenom := []float64{\n\t\t0,\n\t\t39916800,\n\t\t120543840,\n\t\t150917976,\n\t\t105258076,\n\t\t45995730,\n\t\t13339535,\n\t\t2637558,\n\t\t357423,\n\t\t32670,\n\t\t1925,\n\t\t66,\n\t\t1,\n\t}\n\tvar sumNum, sumDenom, zInv float64\n\tif fZ <= 1 {\n\t\tsumNum = num[12]\n\t\tsumDenom = denom[12]\n\t\tfor i := 11; i >= 0; i-- {\n\t\t\tsumNum = math.FMA(sumNum, fZ, num[i])\n\t\t\tsumDenom = math.FMA(sumDenom, fZ, denom[i])\n\t\t}\n\t} else {\n\t\tzInv = 1 / fZ\n\t\tsumNum = num[0]\n\t\tsumDenom = denom[0]\n\t\tfor i := 1; i <= 12; i++ {\n\t\t\tsumNum = math.FMA(sumNum, zInv, num[i])\n\t\t\tsumDenom = math.FMA(sumDenom, zInv, denom[i])\n\t\t}\n\t}\n\treturn sumNum / sumDenom\n}\n\n// getBeta return beta distribution.\nfunc getBeta(fAlpha, fBeta float64) float64 {\n\tvar fA, fB float64\n\tif fAlpha > fBeta {\n\t\tfA = fAlpha\n\t\tfB = fBeta\n\t} else {\n\t\tfA = fBeta\n\t\tfB = fAlpha\n\t}\n\tconst maxGammaArgument = 171.624376956302\n\tif fA+fB < maxGammaArgument {\n\t\treturn math.Gamma(fA) / math.Gamma(fA+fB) * math.Gamma(fB)\n\t}\n\tfg := 6.024680040776729583740234375\n\tfgm := fg - 0.5\n\tfLanczos := getLanczosSum(fA)\n\tfLanczos /= getLanczosSum(fA + fB)\n\tfLanczos *= getLanczosSum(fB)\n\tfABgm := fA + fB + fgm\n\tfLanczos *= math.Sqrt((fABgm / (fA + fgm)) / (fB + fgm))\n\tfTempA := fB / (fA + fgm)\n\tfTempB := fA / (fB + fgm)\n\tfResult := math.Exp(-fA*math.Log1p(fTempA) - fB*math.Log1p(fTempB) - fgm)\n\tfResult *= fLanczos\n\treturn fResult\n}\n\n// getBetaDistPDF is an implementation for the Beta probability density\n// function.\nfunc getBetaDistPDF(fX, fA, fB float64) float64 {\n\tif fX <= 0 || fX >= 1 {\n\t\treturn 0\n\t}\n\tfLogDblMax, fLogDblMin := math.Log(1.79769e+308), math.Log(2.22507e-308)\n\tfLogY := math.Log(0.5 - fX + 0.5)\n\tif fX < 0.1 {\n\t\tfLogY = math.Log1p(-fX)\n\t}\n\tfLogX := math.Log(fX)\n\tfAm1LogX := (fA - 1) * fLogX\n\tfBm1LogY := (fB - 1) * fLogY\n\tfLogBeta := getLogBeta(fA, fB)\n\tif fAm1LogX < fLogDblMax && fAm1LogX > fLogDblMin && fBm1LogY < fLogDblMax &&\n\t\tfBm1LogY > fLogDblMin && fLogBeta < fLogDblMax && fLogBeta > fLogDblMin &&\n\t\tfAm1LogX+fBm1LogY < fLogDblMax && fAm1LogX+fBm1LogY > fLogDblMin {\n\t\treturn math.Pow(fX, fA-1) * math.Pow(0.5-fX+0.5, fB-1) / getBeta(fA, fB)\n\t}\n\treturn math.Exp(fAm1LogX + fBm1LogY - fLogBeta)\n}\n\n// getLogBeta return beta with logarithm.\nfunc getLogBeta(fAlpha, fBeta float64) float64 {\n\tvar fA, fB float64\n\tif fAlpha > fBeta {\n\t\tfA, fB = fAlpha, fBeta\n\t} else {\n\t\tfA, fB = fBeta, fAlpha\n\t}\n\tfg := 6.024680040776729583740234375\n\tfgm := fg - 0.5\n\tfLanczos := getLanczosSum(fA)\n\tfLanczos /= getLanczosSum(fA + fB)\n\tfLanczos *= getLanczosSum(fB)\n\tfLogLanczos := math.Log(fLanczos)\n\tfABgm := fA + fB + fgm\n\tfLogLanczos += 0.5 * (math.Log(fABgm) - math.Log(fA+fgm) - math.Log(fB+fgm))\n\tfTempA := fB / (fA + fgm)\n\tfTempB := fA / (fB + fgm)\n\tfResult := -fA*math.Log1p(fTempA) - fB*math.Log1p(fTempB) - fgm\n\tfResult += fLogLanczos\n\treturn fResult\n}\n\n// getBetaDist is an implementation for the beta distribution function.\nfunc getBetaDist(fXin, fAlpha, fBeta float64) float64 {\n\tif fXin <= 0 {\n\t\treturn 0\n\t}\n\tif fXin >= 1 {\n\t\treturn 1\n\t}\n\tif fBeta == 1 {\n\t\treturn math.Pow(fXin, fAlpha)\n\t}\n\tif fAlpha == 1 {\n\t\treturn -math.Expm1(fBeta * math.Log1p(-fXin))\n\t}\n\tvar fResult float64\n\tfY, flnY := (0.5-fXin)+0.5, math.Log1p(-fXin)\n\tfX, flnX := fXin, math.Log(fXin)\n\tfA, fB := fAlpha, fBeta\n\tbReflect := fXin > fAlpha/(fAlpha+fBeta)\n\tif bReflect {\n\t\tfA = fBeta\n\t\tfB = fAlpha\n\t\tfX = fY\n\t\tfY = fXin\n\t\tflnX = flnY\n\t\tflnY = math.Log(fXin)\n\t}\n\tfResult = getBetaHelperContFrac(fX, fA, fB) / fA\n\tfP, fQ := fA/(fA+fB), fB/(fA+fB)\n\tvar fTemp float64\n\tif fA > 1 && fB > 1 && fP < 0.97 && fQ < 0.97 {\n\t\tfTemp = getBetaDistPDF(fX, fA, fB) * fX * fY\n\t} else {\n\t\tfTemp = math.Exp(fA*flnX + fB*flnY - getLogBeta(fA, fB))\n\t}\n\tfResult *= fTemp\n\tif bReflect {\n\t\tfResult = 0.5 - fResult + 0.5\n\t}\n\treturn fResult\n}\n\n// prepareBETAdotDISTArgs checking and prepare arguments for the formula\n// function BETA.DIST.\nfunc (fn *formulaFuncs) prepareBETAdotDISTArgs(argsList *list.List) formulaArg {\n\tif argsList.Len() < 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BETA.DIST requires at least 4 arguments\")\n\t}\n\tif argsList.Len() > 6 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BETA.DIST requires at most 6 arguments\")\n\t}\n\tx := argsList.Front().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn x\n\t}\n\talpha := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif alpha.Type != ArgNumber {\n\t\treturn alpha\n\t}\n\tbeta := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif beta.Type != ArgNumber {\n\t\treturn beta\n\t}\n\tif alpha.Number <= 0 || beta.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tcumulative := argsList.Front().Next().Next().Next().Value.(formulaArg).ToBool()\n\tif cumulative.Type != ArgNumber {\n\t\treturn cumulative\n\t}\n\ta, b := newNumberFormulaArg(0), newNumberFormulaArg(1)\n\tif argsList.Len() > 4 {\n\t\tif a = argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); a.Type != ArgNumber {\n\t\t\treturn a\n\t\t}\n\t}\n\tif argsList.Len() == 6 {\n\t\tif b = argsList.Back().Value.(formulaArg).ToNumber(); b.Type != ArgNumber {\n\t\t\treturn b\n\t\t}\n\t}\n\treturn newListFormulaArg([]formulaArg{x, alpha, beta, cumulative, a, b})\n}\n\n// BETAdotDIST function calculates the cumulative beta distribution function\n// or the probability density function of the Beta distribution, for a\n// supplied set of parameters. The syntax of the function is:\n//\n//\tBETA.DIST(x,alpha,beta,cumulative,[A],[B])\nfunc (fn *formulaFuncs) BETAdotDIST(argsList *list.List) formulaArg {\n\targs := fn.prepareBETAdotDISTArgs(argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tx, alpha, beta, cumulative, a, b := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5]\n\tif x.Number < a.Number || x.Number > b.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif a.Number == b.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tscale := b.Number - a.Number\n\tx.Number = (x.Number - a.Number) / scale\n\tif cumulative.Number == 1 {\n\t\treturn newNumberFormulaArg(getBetaDist(x.Number, alpha.Number, beta.Number))\n\t}\n\treturn newNumberFormulaArg(getBetaDistPDF(x.Number, alpha.Number, beta.Number) / scale)\n}\n\n// BETADIST function calculates the cumulative beta probability density\n// function for a supplied set of parameters. The syntax of the function is:\n//\n//\tBETADIST(x,alpha,beta,[A],[B])\nfunc (fn *formulaFuncs) BETADIST(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BETADIST requires at least 3 arguments\")\n\t}\n\tif argsList.Len() > 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BETADIST requires at most 5 arguments\")\n\t}\n\tx := argsList.Front().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn x\n\t}\n\talpha := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif alpha.Type != ArgNumber {\n\t\treturn alpha\n\t}\n\tbeta := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif beta.Type != ArgNumber {\n\t\treturn beta\n\t}\n\tif alpha.Number <= 0 || beta.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\ta, b := newNumberFormulaArg(0), newNumberFormulaArg(1)\n\tif argsList.Len() > 3 {\n\t\tif a = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); a.Type != ArgNumber {\n\t\t\treturn a\n\t\t}\n\t}\n\tif argsList.Len() == 5 {\n\t\tif b = argsList.Back().Value.(formulaArg).ToNumber(); b.Type != ArgNumber {\n\t\t\treturn b\n\t\t}\n\t}\n\tif x.Number < a.Number || x.Number > b.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif a.Number == b.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(getBetaDist((x.Number-a.Number)/(b.Number-a.Number), alpha.Number, beta.Number))\n}\n\n// d1mach returns double precision real machine constants.\nfunc d1mach(i int) float64 {\n\tarr := []float64{\n\t\t2.2250738585072014e-308,\n\t\t1.7976931348623158e+308,\n\t\t1.1102230246251565e-16,\n\t\t2.2204460492503131e-16,\n\t\t0.301029995663981195,\n\t}\n\tif i > len(arr) {\n\t\treturn 0\n\t}\n\treturn arr[i-1]\n}\n\n// chebyshevInit determines the number of terms for the double precision\n// orthogonal series \"dos\" needed to insure the error is no larger\n// than \"eta\". Ordinarily eta will be chosen to be one-tenth machine\n// precision.\nfunc chebyshevInit(nos int, eta float64, dos []float64) int {\n\ti, e := 0, 0.0\n\tif nos < 1 {\n\t\treturn 0\n\t}\n\tfor ii := 1; ii <= nos; ii++ {\n\t\ti = nos - ii\n\t\te += math.Abs(dos[i])\n\t\tif e > eta {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn i\n}\n\n// chebyshevEval evaluates the n-term Chebyshev series \"a\" at \"x\".\nfunc chebyshevEval(n int, x float64, a []float64) float64 {\n\tif n < 1 || n > 1000 || x < -1.1 || x > 1.1 {\n\t\treturn math.NaN()\n\t}\n\ttwox, b0, b1, b2 := x*2, 0.0, 0.0, 0.0\n\tfor i := 1; i <= n; i++ {\n\t\tb2 = b1\n\t\tb1 = b0\n\t\tb0 = twox*b1 - b2 + a[n-i]\n\t}\n\treturn (b0 - b2) * 0.5\n}\n\n// lgammacor is an implementation for the log(gamma) correction.\nfunc lgammacor(x float64) float64 {\n\talgmcs := []float64{\n\t\t0.1666389480451863247205729650822, -0.1384948176067563840732986059135e-4,\n\t\t0.9810825646924729426157171547487e-8, -0.1809129475572494194263306266719e-10,\n\t\t0.6221098041892605227126015543416e-13, -0.3399615005417721944303330599666e-15,\n\t\t0.2683181998482698748957538846666e-17, -0.2868042435334643284144622399999e-19,\n\t\t0.3962837061046434803679306666666e-21, -0.6831888753985766870111999999999e-23,\n\t\t0.1429227355942498147573333333333e-24, -0.3547598158101070547199999999999e-26,\n\t\t0.1025680058010470912000000000000e-27, -0.3401102254316748799999999999999e-29,\n\t\t0.1276642195630062933333333333333e-30,\n\t}\n\tnalgm := chebyshevInit(15, d1mach(3), algmcs)\n\txbig := 1.0 / math.Sqrt(d1mach(3))\n\txmax := math.Exp(math.Min(math.Log(d1mach(2)/12.0), -math.Log(12.0*d1mach(1))))\n\tif x < 10.0 {\n\t\treturn math.NaN()\n\t} else if x >= xmax {\n\t\treturn 4.930380657631324e-32\n\t} else if x < xbig {\n\t\ttmp := 10.0 / x\n\t\treturn chebyshevEval(nalgm, tmp*tmp*2.0-1.0, algmcs) / x\n\t}\n\treturn 1.0 / (x * 12.0)\n}\n\n// logrelerr compute the relative error logarithm.\nfunc logrelerr(x float64) float64 {\n\talnrcs := []float64{\n\t\t0.10378693562743769800686267719098e+1, -0.13364301504908918098766041553133,\n\t\t0.19408249135520563357926199374750e-1, -0.30107551127535777690376537776592e-2,\n\t\t0.48694614797154850090456366509137e-3, -0.81054881893175356066809943008622e-4,\n\t\t0.13778847799559524782938251496059e-4, -0.23802210894358970251369992914935e-5,\n\t\t0.41640416213865183476391859901989e-6, -0.73595828378075994984266837031998e-7,\n\t\t0.13117611876241674949152294345011e-7, -0.23546709317742425136696092330175e-8,\n\t\t0.42522773276034997775638052962567e-9, -0.77190894134840796826108107493300e-10,\n\t\t0.14075746481359069909215356472191e-10, -0.25769072058024680627537078627584e-11,\n\t\t0.47342406666294421849154395005938e-12, -0.87249012674742641745301263292675e-13,\n\t\t0.16124614902740551465739833119115e-13, -0.29875652015665773006710792416815e-14,\n\t\t0.55480701209082887983041321697279e-15, -0.10324619158271569595141333961932e-15,\n\t\t0.19250239203049851177878503244868e-16, -0.35955073465265150011189707844266e-17,\n\t\t0.67264542537876857892194574226773e-18, -0.12602624168735219252082425637546e-18,\n\t\t0.23644884408606210044916158955519e-19, -0.44419377050807936898878389179733e-20,\n\t\t0.83546594464034259016241293994666e-21, -0.15731559416479562574899253521066e-21,\n\t\t0.29653128740247422686154369706666e-22, -0.55949583481815947292156013226666e-23,\n\t\t0.10566354268835681048187284138666e-23, -0.19972483680670204548314999466666e-24,\n\t\t0.37782977818839361421049855999999e-25, -0.71531586889081740345038165333333e-26,\n\t\t0.13552488463674213646502024533333e-26, -0.25694673048487567430079829333333e-27,\n\t\t0.48747756066216949076459519999999e-28, -0.92542112530849715321132373333333e-29,\n\t\t0.17578597841760239233269760000000e-29, -0.33410026677731010351377066666666e-30,\n\t\t0.63533936180236187354180266666666e-31,\n\t}\n\tnlnrel := chebyshevInit(43, 0.1*d1mach(3), alnrcs)\n\tif x <= -1 {\n\t\treturn math.NaN()\n\t}\n\tif math.Abs(x) <= 0.375 {\n\t\treturn x * (1.0 - x*chebyshevEval(nlnrel, x/0.375, alnrcs))\n\t}\n\treturn math.Log(x + 1.0)\n}\n\n// logBeta is an implementation for the log of the beta distribution\n// function.\nfunc logBeta(a, b float64) float64 {\n\tcorr, p, q := 0.0, a, a\n\tif b < p {\n\t\tp = b\n\t}\n\tif b > q {\n\t\tq = b\n\t}\n\tif p < 0 {\n\t\treturn math.NaN()\n\t}\n\tif p == 0 {\n\t\treturn math.MaxFloat64\n\t}\n\tif p >= 10.0 {\n\t\tcorr = lgammacor(p) + lgammacor(q) - lgammacor(p+q)\n\t\tf1 := q * logrelerr(-p/(p+q))\n\t\treturn math.Log(q)*-0.5 + 0.918938533204672741780329736406 + corr + (p-0.5)*math.Log(p/(p+q)) + math.Nextafter(f1, f1)\n\t}\n\tif q >= 10 {\n\t\tcorr = lgammacor(q) - lgammacor(p+q)\n\t\tval, _ := math.Lgamma(p)\n\t\treturn val + corr + p - p*math.Log(p+q) + (q-0.5)*logrelerr(-p/(p+q))\n\t}\n\treturn math.Log(math.Gamma(p) * (math.Gamma(q) / math.Gamma(p+q)))\n}\n\n// pbetaRaw is a part of pbeta for the beta distribution.\nfunc pbetaRaw(alnsml, ans, eps, p, pin, q, sml, x, y float64) float64 {\n\tif q > 1.0 {\n\t\txb := p*math.Log(y) + q*math.Log(1.0-y) - logBeta(p, q) - math.Log(q)\n\t\tib := int(math.Max(xb/alnsml, 0.0))\n\t\tterm := math.Exp(xb - float64(ib)*alnsml)\n\t\tc := 1.0 / (1.0 - y)\n\t\tp1 := q * c / (p + q - 1.0)\n\t\tfinsum := 0.0\n\t\tn := int(q)\n\t\tif q == float64(n) {\n\t\t\tn = n - 1\n\t\t}\n\t\tfor i := 1; i <= n; i++ {\n\t\t\tif p1 <= 1 && term/eps <= finsum {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\txi := float64(i)\n\t\t\tterm = (q - xi + 1.0) * c * term / (p + q - xi)\n\t\t\tif term > 1.0 {\n\t\t\t\tib = ib - 1\n\t\t\t\tterm = term * sml\n\t\t\t}\n\t\t\tif ib == 0 {\n\t\t\t\tfinsum = finsum + term\n\t\t\t}\n\t\t}\n\t\tans = ans + finsum\n\t}\n\tif y != x || p != pin {\n\t\tans = 1.0 - ans\n\t}\n\tans = math.Max(math.Min(ans, 1.0), 0.0)\n\treturn ans\n}\n\n// pbeta returns distribution function of the beta distribution.\nfunc pbeta(x, pin, qin float64) (ans float64) {\n\teps := d1mach(3)\n\talneps := math.Log(eps)\n\tsml := d1mach(1)\n\talnsml := math.Log(sml)\n\ty := x\n\tp := pin\n\tq := qin\n\tif p/(p+q) < x {\n\t\ty = 1.0 - y\n\t\tp = qin\n\t\tq = pin\n\t}\n\tif (p+q)*y/(p+1.0) < eps {\n\t\txb := p*math.Log(math.Max(y, sml)) - math.Log(p) - logBeta(p, q)\n\t\tif xb > alnsml && y != 0.0 {\n\t\t\tans = math.Exp(xb)\n\t\t}\n\t\tif y != x || p != pin {\n\t\t\tans = 1.0 - ans\n\t\t}\n\t} else {\n\t\tps := q - math.Floor(q)\n\t\tif ps == 0.0 {\n\t\t\tps = 1.0\n\t\t}\n\t\txb := p*math.Log(y) - logBeta(ps, p) - math.Log(p)\n\t\tif xb >= alnsml {\n\t\t\tans = math.Exp(xb)\n\t\t\tterm := ans * p\n\t\t\tif ps != 1.0 {\n\t\t\t\tn := int(math.Max(alneps/math.Log(y), 4.0))\n\t\t\t\tfor i := 1; i <= n; i++ {\n\t\t\t\t\txi := float64(i)\n\t\t\t\t\tterm = term * (xi - ps) * y / xi\n\t\t\t\t\tans = ans + term/(p+xi)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tans = pbetaRaw(alnsml, ans, eps, p, pin, q, sml, x, y)\n\t}\n\treturn ans\n}\n\n// betainvProbIterator is a part of betainv for the inverse of the beta\n// function.\nfunc betainvProbIterator(alpha1, alpha3, beta1, beta2, beta3, logBeta, maxCumulative, prob1, prob2 float64) float64 {\n\tvar i, j, prev, prop4 float64\n\tj = 1\n\tfor prob := 0; prob < 1000; prob++ {\n\t\tprop3 := pbeta(beta3, alpha1, beta1)\n\t\tprop3 = (prop3 - prob1) * math.Exp(logBeta+prob2*math.Log(beta3)+beta2*math.Log(1.0-beta3))\n\t\tif prop3*prop4 <= 0 {\n\t\t\tprev = math.Max(math.Abs(j), maxCumulative)\n\t\t}\n\t\th := 1.0\n\t\tfor iteratorCount := 0; iteratorCount < 1000; iteratorCount++ {\n\t\t\tj = h * prop3\n\t\t\tif math.Abs(j) < prev {\n\t\t\t\ti = beta3 - j\n\t\t\t\tif i >= 0 && i <= 1.0 {\n\t\t\t\t\tif prev <= alpha3 {\n\t\t\t\t\t\treturn beta3\n\t\t\t\t\t}\n\t\t\t\t\tif math.Abs(prop3) <= alpha3 {\n\t\t\t\t\t\treturn beta3\n\t\t\t\t\t}\n\t\t\t\t\tif i != 0 && i != 1.0 {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\th /= 3.0\n\t\t}\n\t\tif i == beta3 {\n\t\t\treturn beta3\n\t\t}\n\t\tbeta3, prop4 = i, prop3\n\t}\n\treturn beta3\n}\n\n// calcBetainv is an implementation for the quantile of the beta\n// distribution.\nfunc calcBetainv(probability, alpha, beta, lower, upper float64) float64 {\n\tminCumulative, maxCumulative := 1.0e-300, 3.0e-308\n\tlowerBound, upperBound := maxCumulative, 1.0-2.22e-16\n\tneedSwap := false\n\tvar alpha1, alpha2, beta1, beta2, beta3, prob1, x, y float64\n\tif probability <= 0.5 {\n\t\tprob1, alpha1, beta1 = probability, alpha, beta\n\t} else {\n\t\tprob1, alpha1, beta1, needSwap = 1.0-probability, beta, alpha, true\n\t}\n\tlogBetaNum := logBeta(alpha, beta)\n\tprob2 := math.Sqrt(-math.Log(prob1 * prob1))\n\tprob3 := prob2 - (prob2*0.27061+2.3075)/(prob2*(prob2*0.04481+0.99229)+1)\n\tif alpha1 > 1 && beta1 > 1 {\n\t\talpha2, beta2, prob2 = 1/(alpha1+alpha1-1), 1/(beta1+beta1-1), (prob3*prob3-3)/6\n\t\tx = 2 / (alpha2 + beta2)\n\t\ty = prob3*math.Sqrt(x+prob2)/x - (beta2-alpha2)*(prob2+5/6.0-2/(x*3))\n\t\tbeta3 = alpha1 / (alpha1 + beta1*math.Exp(y+y))\n\t} else {\n\t\tbeta2, prob2 = 1/(beta1*9), beta1+beta1\n\t\tbeta2 = prob2 * math.Pow(1-beta2+prob3*math.Sqrt(beta2), 3)\n\t\tif beta2 <= 0 {\n\t\t\tbeta3 = 1 - math.Exp((math.Log((1-prob1)*beta1)+logBetaNum)/beta1)\n\t\t} else {\n\t\t\tbeta2 = (prob2 + alpha1*4 - 2) / beta2\n\t\t\tif beta2 <= 1 {\n\t\t\t\tbeta3 = math.Exp((logBetaNum + math.Log(alpha1*prob1)) / alpha1)\n\t\t\t} else {\n\t\t\t\tbeta3 = 1 - 2/(beta2+1)\n\t\t\t}\n\t\t}\n\t}\n\tbeta2, prob2 = 1-beta1, 1-alpha1\n\tif beta3 < lowerBound {\n\t\tbeta3 = lowerBound\n\t} else if beta3 > upperBound {\n\t\tbeta3 = upperBound\n\t}\n\talpha3 := math.Max(minCumulative, math.Pow(10.0, -13.0-2.5/(alpha1*alpha1)-0.5/(prob1*prob1)))\n\tbeta3 = betainvProbIterator(alpha1, alpha3, beta1, beta2, beta3, logBetaNum, maxCumulative, prob1, prob2)\n\tif needSwap {\n\t\tbeta3 = 1.0 - beta3\n\t}\n\treturn (upper-lower)*beta3 + lower\n}\n\n// betainv is an implementation of the formula functions BETAINV and\n// BETA.INV.\nfunc (fn *formulaFuncs) betainv(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 3 arguments\", name))\n\t}\n\tif argsList.Len() > 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at most 5 arguments\", name))\n\t}\n\tprobability := argsList.Front().Value.(formulaArg).ToNumber()\n\tif probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\tif probability.Number <= 0 || probability.Number >= 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\talpha := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif alpha.Type != ArgNumber {\n\t\treturn alpha\n\t}\n\tbeta := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif beta.Type != ArgNumber {\n\t\treturn beta\n\t}\n\tif alpha.Number <= 0 || beta.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\ta, b := newNumberFormulaArg(0), newNumberFormulaArg(1)\n\tif argsList.Len() > 3 {\n\t\tif a = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); a.Type != ArgNumber {\n\t\t\treturn a\n\t\t}\n\t}\n\tif argsList.Len() == 5 {\n\t\tif b = argsList.Back().Value.(formulaArg).ToNumber(); b.Type != ArgNumber {\n\t\t\treturn b\n\t\t}\n\t}\n\tif a.Number == b.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(calcBetainv(probability.Number, alpha.Number, beta.Number, a.Number, b.Number))\n}\n\n// BETAINV function uses an iterative procedure to calculate the inverse of\n// the cumulative beta probability density function for a supplied\n// probability. The syntax of the function is:\n//\n//\tBETAINV(probability,alpha,beta,[A],[B])\nfunc (fn *formulaFuncs) BETAINV(argsList *list.List) formulaArg {\n\treturn fn.betainv(\"BETAINV\", argsList)\n}\n\n// BETAdotINV function uses an iterative procedure to calculate the inverse of\n// the cumulative beta probability density function for a supplied\n// probability. The syntax of the function is:\n//\n//\tBETA.INV(probability,alpha,beta,[A],[B])\nfunc (fn *formulaFuncs) BETAdotINV(argsList *list.List) formulaArg {\n\treturn fn.betainv(\"BETA.INV\", argsList)\n}\n\n// incompleteGamma is an implementation of the incomplete gamma function.\nfunc incompleteGamma(a, x float64) float64 {\n\tmaxVal := 32\n\tsummer := 0.0\n\tfor n := 0; n <= maxVal; n++ {\n\t\tdivisor := a\n\t\tfor i := 1; i <= n; i++ {\n\t\t\tdivisor *= a + float64(i)\n\t\t}\n\t\tsummer += math.Pow(x, float64(n)) / divisor\n\t}\n\treturn math.Pow(x, a) * math.Exp(0-x) * summer\n}\n\n// binomCoeff implement binomial coefficient calculation.\nfunc binomCoeff(n, k float64) float64 {\n\treturn fact(n) / (fact(k) * fact(n-k))\n}\n\n// binomdist implement binomial distribution calculation.\nfunc binomdist(x, n, p float64) float64 {\n\treturn binomCoeff(n, x) * math.Pow(p, x) * math.Pow(1-p, n-x)\n}\n\n// BINOMdotDIST function returns the Binomial Distribution probability for a\n// given number of successes from a specified number of trials. The syntax of\n// the function is:\n//\n//\tBINOM.DIST(number_s,trials,probability_s,cumulative)\nfunc (fn *formulaFuncs) BINOMdotDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BINOM.DIST requires 4 arguments\")\n\t}\n\treturn fn.BINOMDIST(argsList)\n}\n\n// BINOMDIST function returns the Binomial Distribution probability of a\n// specified number of successes out of a specified number of trials. The\n// syntax of the function is:\n//\n//\tBINOMDIST(number_s,trials,probability_s,cumulative)\nfunc (fn *formulaFuncs) BINOMDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BINOMDIST requires 4 arguments\")\n\t}\n\tvar s, trials, probability, cumulative formulaArg\n\tif s = argsList.Front().Value.(formulaArg).ToNumber(); s.Type != ArgNumber {\n\t\treturn s\n\t}\n\tif trials = argsList.Front().Next().Value.(formulaArg).ToNumber(); trials.Type != ArgNumber {\n\t\treturn trials\n\t}\n\tif s.Number < 0 || s.Number > trials.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif probability = argsList.Back().Prev().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\n\tif probability.Number < 0 || probability.Number > 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError {\n\t\treturn cumulative\n\t}\n\tif cumulative.Number == 1 {\n\t\tbm := 0.0\n\t\tfor i := 0; i <= int(s.Number); i++ {\n\t\t\tbm += binomdist(float64(i), trials.Number, probability.Number)\n\t\t}\n\t\treturn newNumberFormulaArg(bm)\n\t}\n\treturn newNumberFormulaArg(binomdist(s.Number, trials.Number, probability.Number))\n}\n\n// BINOMdotDISTdotRANGE function returns the Binomial Distribution probability\n// for the number of successes from a specified number of trials falling into\n// a specified range.\n//\n//\tBINOM.DIST.RANGE(trials,probability_s,number_s,[number_s2])\nfunc (fn *formulaFuncs) BINOMdotDISTdotRANGE(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BINOM.DIST.RANGE requires at least 3 arguments\")\n\t}\n\tif argsList.Len() > 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BINOM.DIST.RANGE requires at most 4 arguments\")\n\t}\n\ttrials := argsList.Front().Value.(formulaArg).ToNumber()\n\tif trials.Type != ArgNumber {\n\t\treturn trials\n\t}\n\tprobability := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\tif probability.Number < 0 || probability.Number > 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tnum1 := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif num1.Type != ArgNumber {\n\t\treturn num1\n\t}\n\tif num1.Number < 0 || num1.Number > trials.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tnum2 := num1\n\tif argsList.Len() > 3 {\n\t\tif num2 = argsList.Back().Value.(formulaArg).ToNumber(); num2.Type != ArgNumber {\n\t\t\treturn num2\n\t\t}\n\t}\n\tif num2.Number < 0 || num2.Number > trials.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tsum := 0.0\n\tfor i := num1.Number; i <= num2.Number; i++ {\n\t\tsum += binomdist(i, trials.Number, probability.Number)\n\t}\n\treturn newNumberFormulaArg(sum)\n}\n\n// binominv implement inverse of the binomial distribution calculation.\nfunc binominv(n, p, alpha float64) float64 {\n\tq, i, sum, maxVal := 1-p, 0.0, 0.0, 0.0\n\tn = math.Floor(n)\n\tif q > p {\n\t\tfactor := math.Pow(q, n)\n\t\tsum = factor\n\t\tfor i = 0; i < n && sum < alpha; i++ {\n\t\t\tfactor *= (n - i) / (i + 1) * p / q\n\t\t\tsum += factor\n\t\t}\n\t\treturn i\n\t}\n\tfactor := math.Pow(p, n)\n\tsum, maxVal = 1-factor, n\n\tfor i = 0; i < maxVal && sum >= alpha; i++ {\n\t\tfactor *= (n - i) / (i + 1) * q / p\n\t\tsum -= factor\n\t}\n\treturn n - i\n}\n\n// BINOMdotINV function returns the inverse of the Cumulative Binomial\n// Distribution. The syntax of the function is:\n//\n//\tBINOM.INV(trials,probability_s,alpha)\nfunc (fn *formulaFuncs) BINOMdotINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BINOM.INV requires 3 numeric arguments\")\n\t}\n\ttrials := argsList.Front().Value.(formulaArg).ToNumber()\n\tif trials.Type != ArgNumber {\n\t\treturn trials\n\t}\n\tif trials.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tprobability := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\tif probability.Number <= 0 || probability.Number >= 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\talpha := argsList.Back().Value.(formulaArg).ToNumber()\n\tif alpha.Type != ArgNumber {\n\t\treturn alpha\n\t}\n\tif alpha.Number <= 0 || alpha.Number >= 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(binominv(trials.Number, probability.Number, alpha.Number))\n}\n\n// CHIDIST function calculates the right-tailed probability of the chi-square\n// distribution. The syntax of the function is:\n//\n//\tCHIDIST(x,degrees_freedom)\nfunc (fn *formulaFuncs) CHIDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CHIDIST requires 2 numeric arguments\")\n\t}\n\tx := argsList.Front().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tdegrees := argsList.Back().Value.(formulaArg).ToNumber()\n\tif degrees.Type != ArgNumber {\n\t\treturn degrees\n\t}\n\tlogSqrtPi, sqrtPi := math.Log(math.Sqrt(math.Pi)), 1/math.Sqrt(math.Pi)\n\tvar e, s, z, c, y float64\n\ta, x1, even := x.Number/2, x.Number, int(degrees.Number)%2 == 0\n\tif degrees.Number > 1 {\n\t\ty = math.Exp(-a)\n\t}\n\targs := list.New()\n\targs.PushBack(newNumberFormulaArg(-math.Sqrt(x1)))\n\to := fn.NORMSDIST(args)\n\ts = 2 * o.Number\n\tif even {\n\t\ts = y\n\t}\n\tif degrees.Number > 2 {\n\t\tx1 = (degrees.Number - 1) / 2\n\t\tz = 0.5\n\t\tif even {\n\t\t\tz = 1\n\t\t}\n\t\tif a > 20 {\n\t\t\te = logSqrtPi\n\t\t\tif even {\n\t\t\t\te = 0\n\t\t\t}\n\t\t\tc = math.Log(a)\n\t\t\tfor z <= x1 {\n\t\t\t\te = math.Log(z) + e\n\t\t\t\ts += math.Exp(c*z - a - e)\n\t\t\t\tz++\n\t\t\t}\n\t\t\treturn newNumberFormulaArg(s)\n\t\t}\n\t\te = sqrtPi / math.Sqrt(a)\n\t\tif even {\n\t\t\te = 1\n\t\t}\n\t\tc = 0\n\t\tfor z <= x1 {\n\t\t\te = e * (a / z)\n\t\t\tc = c + e\n\t\t\tz++\n\t\t}\n\t\treturn newNumberFormulaArg(c*y + s)\n\t}\n\treturn newNumberFormulaArg(s)\n}\n\n// CHIINV function calculates the inverse of the right-tailed probability of\n// the Chi-Square Distribution. The syntax of the function is:\n//\n//\tCHIINV(probability,deg_freedom)\nfunc (fn *formulaFuncs) CHIINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CHIINV requires 2 numeric arguments\")\n\t}\n\tprobability := argsList.Front().Value.(formulaArg).ToNumber()\n\tif probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\tif probability.Number <= 0 || probability.Number > 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tdeg := argsList.Back().Value.(formulaArg).ToNumber()\n\tif deg.Type != ArgNumber {\n\t\treturn deg\n\t}\n\tif deg.Number < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(gammainv(1-probability.Number, 0.5*deg.Number, 2.0))\n}\n\n// CHITEST function uses the chi-square test to calculate the probability that\n// the differences between two supplied data sets (of observed and expected\n// frequencies), are likely to be simply due to sampling error, or if they are\n// likely to be real. The syntax of the function is:\n//\n//\tCHITEST(actual_range,expected_range)\nfunc (fn *formulaFuncs) CHITEST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CHITEST requires 2 arguments\")\n\t}\n\tactual, expected := argsList.Front().Value.(formulaArg), argsList.Back().Value.(formulaArg)\n\tactualList, expectedList := actual.ToList(), expected.ToList()\n\trows := len(actual.Matrix)\n\tif rows == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tcolumns := len(actualList) / rows\n\tif len(actualList) != len(expectedList) || len(actualList) == 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tvar result float64\n\tvar degrees int\n\tfor i := 0; i < len(actualList); i++ {\n\t\ta, e := actualList[i].ToNumber(), expectedList[i].ToNumber()\n\t\tif a.Type == ArgNumber && e.Type == ArgNumber {\n\t\t\tif e.Number == 0 {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t\t\t}\n\t\t\tif e.Number < 0 {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t\t}\n\t\t\tresult += (a.Number - e.Number) * (a.Number - e.Number) / e.Number\n\t\t}\n\t}\n\tif rows == 1 {\n\t\tdegrees = columns - 1\n\t} else if columns == 1 {\n\t\tdegrees = rows - 1\n\t} else {\n\t\tdegrees = (columns - 1) * (rows - 1)\n\t}\n\targs := list.New()\n\targs.PushBack(newNumberFormulaArg(result))\n\targs.PushBack(newNumberFormulaArg(float64(degrees)))\n\treturn fn.CHIDIST(args)\n}\n\n// getGammaSeries calculates a power-series of the gamma function.\nfunc getGammaSeries(fA, fX float64) float64 {\n\tvar (\n\t\tfHalfMachEps = 2.22045e-016 / 2\n\t\tfDenomfactor = fA\n\t\tfSummand     = 1 / fA\n\t\tfSum         = fSummand\n\t\tnCount       = 1\n\t)\n\tfor fSummand/fSum > fHalfMachEps && nCount <= 10000 {\n\t\tfDenomfactor = fDenomfactor + 1\n\t\tfSummand = fSummand * fX / fDenomfactor\n\t\tfSum = fSum + fSummand\n\t\tnCount = nCount + 1\n\t}\n\treturn fSum\n}\n\n// getGammaContFraction returns continued fraction with odd items of the gamma\n// function.\nfunc getGammaContFraction(fA, fX float64) float64 {\n\tvar (\n\t\tfBigInv      = 2.22045e-016\n\t\tfHalfMachEps = fBigInv / 2\n\t\tfBig         = 1 / fBigInv\n\t\tfCount       = 0.0\n\t\tfY           = 1 - fA\n\t\tfDenom       = fX + 2 - fA\n\t\tfPkm1        = fX + 1\n\t\tfPkm2        = 1.0\n\t\tfQkm1        = fDenom * fX\n\t\tfQkm2        = fX\n\t\tfApprox      = fPkm1 / fQkm1\n\t\tbFinished    = false\n\t)\n\tfor !bFinished && fCount < 10000 {\n\t\tfCount = fCount + 1\n\t\tfY = fY + 1\n\t\tfDenom = fDenom + 2\n\t\tvar (\n\t\t\tfNum = fY * fCount\n\t\t\tf1   = fPkm1 * fDenom\n\t\t\tf2   = fPkm2 * fNum\n\t\t\tfPk  = math.Nextafter(f1, f1) - math.Nextafter(f2, f2)\n\t\t\tf3   = fQkm1 * fDenom\n\t\t\tf4   = fQkm2 * fNum\n\t\t\tfQk  = math.Nextafter(f3, f3) - math.Nextafter(f4, f4)\n\t\t)\n\t\tif fQk != 0 {\n\t\t\tfR := fPk / fQk\n\t\t\tbFinished = math.Abs((fApprox-fR)/fR) <= fHalfMachEps\n\t\t\tfApprox = fR\n\t\t}\n\t\tfPkm2, fPkm1, fQkm2, fQkm1 = fPkm1, fPk, fQkm1, fQk\n\t\tif math.Abs(fPk) > fBig {\n\t\t\t// reduce a fraction does not change the value\n\t\t\tfPkm2 = fPkm2 * fBigInv\n\t\t\tfPkm1 = fPkm1 * fBigInv\n\t\t\tfQkm2 = fQkm2 * fBigInv\n\t\t\tfQkm1 = fQkm1 * fBigInv\n\t\t}\n\t}\n\treturn fApprox\n}\n\n// getLogGammaHelper is a part of implementation of the function getLogGamma.\nfunc getLogGammaHelper(fZ float64) float64 {\n\t_fg := 6.024680040776729583740234375\n\tzgHelp := fZ + _fg - 0.5\n\treturn math.Log(getLanczosSum(fZ)) + (fZ-0.5)*math.Log(zgHelp) - zgHelp\n}\n\n// getGammaHelper is a part of implementation of the function getLogGamma.\nfunc getGammaHelper(fZ float64) float64 {\n\tvar (\n\t\tgamma  = getLanczosSum(fZ)\n\t\tfg     = 6.024680040776729583740234375\n\t\tzgHelp = fZ + fg - 0.5\n\t\t// avoid intermediate overflow\n\t\thalfpower = math.Pow(zgHelp, fZ/2-0.25)\n\t)\n\tgamma *= halfpower\n\tgamma /= math.Exp(zgHelp)\n\tgamma *= halfpower\n\tif fZ <= 20 && fZ == math.Floor(fZ) {\n\t\tgamma = math.Round(gamma)\n\t}\n\treturn gamma\n}\n\n// getLogGamma calculates the natural logarithm of the gamma function.\nfunc getLogGamma(fZ float64) float64 {\n\tfMaxGammaArgument := 171.624376956302\n\tif fZ >= fMaxGammaArgument {\n\t\treturn getLogGammaHelper(fZ)\n\t}\n\tif fZ >= 1.0 {\n\t\treturn math.Log(getGammaHelper(fZ))\n\t}\n\tif fZ >= 0.5 {\n\t\treturn math.Log(getGammaHelper(fZ+1) / fZ)\n\t}\n\treturn getLogGammaHelper(fZ+2) - math.Log(fZ+1) - math.Log(fZ)\n}\n\n// getLowRegIGamma returns lower regularized incomplete gamma function.\nfunc getLowRegIGamma(fA, fX float64) float64 {\n\tlnFactor := fA*math.Log(fX) - fX - getLogGamma(fA)\n\tfactor := math.Exp(lnFactor)\n\tif fX > fA+1 {\n\t\treturn 1 - factor*getGammaContFraction(fA, fX)\n\t}\n\treturn factor * getGammaSeries(fA, fX)\n}\n\n// getChiSqDistCDF returns left tail for the Chi-Square distribution.\nfunc getChiSqDistCDF(fX, fDF float64) float64 {\n\tif fX <= 0 {\n\t\treturn 0\n\t}\n\treturn getLowRegIGamma(fDF/2, fX/2)\n}\n\n// getChiSqDistPDF calculates the probability density function for the\n// Chi-Square distribution.\nfunc getChiSqDistPDF(fX, fDF float64) float64 {\n\tif fDF*fX > 1391000 {\n\t\treturn math.Exp((0.5*fDF-1)*math.Log(fX*0.5) - 0.5*fX - math.Log(2) - getLogGamma(0.5*fDF))\n\t}\n\tvar fCount, fValue float64\n\tif math.Mod(fDF, 2) < 0.5 {\n\t\tfValue = 0.5\n\t\tfCount = 2\n\t} else {\n\t\tfValue = 1 / math.Sqrt(fX*2*math.Pi)\n\t\tfCount = 1\n\t}\n\tfor fCount < fDF {\n\t\tfValue *= fX / fCount\n\t\tfCount += 2\n\t}\n\tif fX >= 1425 {\n\t\tfValue = math.Exp(math.Log(fValue) - fX/2)\n\t} else {\n\t\tfValue *= math.Exp(-fX / 2)\n\t}\n\treturn fValue\n}\n\n// CHISQdotDIST function calculates the Probability Density Function or the\n// Cumulative Distribution Function for the Chi-Square Distribution. The\n// syntax of the function is:\n//\n//\tCHISQ.DIST(x,degrees_freedom,cumulative)\nfunc (fn *formulaFuncs) CHISQdotDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CHISQ.DIST requires 3 arguments\")\n\t}\n\tvar x, degrees, cumulative formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif degrees = argsList.Front().Next().Value.(formulaArg).ToNumber(); degrees.Type != ArgNumber {\n\t\treturn degrees\n\t}\n\tif cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError {\n\t\treturn cumulative\n\t}\n\tif x.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tmaxDeg := math.Pow10(10)\n\tif degrees.Number < 1 || degrees.Number >= maxDeg {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif cumulative.Number == 1 {\n\t\treturn newNumberFormulaArg(getChiSqDistCDF(x.Number, degrees.Number))\n\t}\n\treturn newNumberFormulaArg(getChiSqDistPDF(x.Number, degrees.Number))\n}\n\n// CHISQdotDISTdotRT function calculates the right-tailed probability of the\n// Chi-Square Distribution. The syntax of the function is:\n//\n//\tCHISQ.DIST.RT(x,degrees_freedom)\nfunc (fn *formulaFuncs) CHISQdotDISTdotRT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CHISQ.DIST.RT requires 2 numeric arguments\")\n\t}\n\treturn fn.CHIDIST(argsList)\n}\n\n// CHISQdotTEST function performs the chi-square test on two supplied data sets\n// (of observed and expected frequencies), and returns the probability that\n// the differences between the sets are simply due to sampling error. The\n// syntax of the function is:\n//\n//\tCHISQ.TEST(actual_range,expected_range)\nfunc (fn *formulaFuncs) CHISQdotTEST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CHISQ.TEST requires 2 arguments\")\n\t}\n\treturn fn.CHITEST(argsList)\n}\n\n// hasChangeOfSign check if the sign has been changed.\nfunc hasChangeOfSign(u, w float64) bool {\n\treturn (u < 0 && w > 0) || (u > 0 && w < 0)\n}\n\n// calcInverseIterator directly maps the required parameters for inverse\n// distribution functions.\ntype calcInverseIterator struct {\n\tname        string\n\tfp, fDF, nT float64\n}\n\n// callBack implements the callback function for the inverse iterator.\nfunc (iterator *calcInverseIterator) callBack(x float64) float64 {\n\tif iterator.name == \"CHISQ.INV\" {\n\t\treturn iterator.fp - getChiSqDistCDF(x, iterator.fDF)\n\t}\n\treturn iterator.fp - getTDist(x, iterator.fDF, iterator.nT)\n}\n\n// inverseQuadraticInterpolation inverse quadratic interpolation with\n// additional brackets.\nfunc inverseQuadraticInterpolation(iterator calcInverseIterator, fAx, fAy, fBx, fBy float64) float64 {\n\tfYEps := 1.0e-307\n\tfXEps := 2.22045e-016\n\tfPx, fPy, fQx, fQy, fRx, fRy := fAx, fAy, fBx, fBy, fAx, fAy\n\tfSx := 0.5 * (fAx + fBx)\n\tbHasToInterpolate := true\n\tnCount := 0\n\tfor nCount < 500 && math.Abs(fRy) > fYEps && (fBx-fAx) > math.Max(math.Abs(fAx), math.Abs(fBx))*fXEps {\n\t\tif bHasToInterpolate {\n\t\t\tif fPy != fQy && fQy != fRy && fRy != fPy {\n\t\t\t\tfSx = fPx*fRy*fQy/(fRy-fPy)/(fQy-fPy) + fRx*fQy*fPy/(fQy-fRy)/(fPy-fRy) +\n\t\t\t\t\tfQx*fPy*fRy/(fPy-fQy)/(fRy-fQy)\n\t\t\t\tbHasToInterpolate = (fAx < fSx) && (fSx < fBx)\n\t\t\t} else {\n\t\t\t\tbHasToInterpolate = false\n\t\t\t}\n\t\t}\n\t\tif !bHasToInterpolate {\n\t\t\tfSx = 0.5 * (fAx + fBx)\n\t\t\tfQx, fQy = fBx, fBy\n\t\t\tbHasToInterpolate = true\n\t\t}\n\t\tfPx, fQx, fRx, fPy, fQy = fQx, fRx, fSx, fQy, fRy\n\t\tfRy = iterator.callBack(fSx)\n\t\tif hasChangeOfSign(fAy, fRy) {\n\t\t\tfBx, fBy = fRx, fRy\n\t\t} else {\n\t\t\tfAx, fAy = fRx, fRy\n\t\t}\n\t\tbHasToInterpolate = bHasToInterpolate && (math.Abs(fRy)*2 <= math.Abs(fQy))\n\t\tnCount++\n\t}\n\treturn fRx\n}\n\n// calcIterateInverse function calculates the iteration for inverse\n// distributions.\nfunc calcIterateInverse(iterator calcInverseIterator, fAx, fBx float64) float64 {\n\tfAy, fBy := iterator.callBack(fAx), iterator.callBack(fBx)\n\tvar fTemp float64\n\tvar nCount int\n\tfor nCount = 0; nCount < 1000 && !hasChangeOfSign(fAy, fBy); nCount++ {\n\t\tif math.Abs(fAy) <= math.Abs(fBy) {\n\t\t\tfTemp = fAx\n\t\t\tfAx += 2 * (fAx - fBx)\n\t\t\tif fAx < 0 {\n\t\t\t\tfAx = 0\n\t\t\t}\n\t\t\tfBx = fTemp\n\t\t\tfBy = fAy\n\t\t\tfAy = iterator.callBack(fAx)\n\t\t} else {\n\t\t\tfTemp = fBx\n\t\t\tfBx += 2 * (fBx - fAx)\n\t\t\tfAx = fTemp\n\t\t\tfAy = fBy\n\t\t\tfBy = iterator.callBack(fBx)\n\t\t}\n\t}\n\tif fAy == 0 || fBy == 0 {\n\t\treturn 0\n\t}\n\treturn inverseQuadraticInterpolation(iterator, fAx, fAy, fBx, fBy)\n}\n\n// CHISQdotINV function calculates the inverse of the left-tailed probability\n// of the Chi-Square Distribution. The syntax of the function is:\n//\n//\tCHISQ.INV(probability,degrees_freedom)\nfunc (fn *formulaFuncs) CHISQdotINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CHISQ.INV requires 2 numeric arguments\")\n\t}\n\tvar probability, degrees formulaArg\n\tif probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\tif degrees = argsList.Back().Value.(formulaArg).ToNumber(); degrees.Type != ArgNumber {\n\t\treturn degrees\n\t}\n\tif probability.Number < 0 || probability.Number >= 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif degrees.Number < 1 || degrees.Number > math.Pow10(10) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(calcIterateInverse(calcInverseIterator{\n\t\tname: \"CHISQ.INV\",\n\t\tfp:   probability.Number,\n\t\tfDF:  degrees.Number,\n\t}, degrees.Number/2, degrees.Number))\n}\n\n// CHISQdotINVdotRT function calculates the inverse of the right-tailed\n// probability of the Chi-Square Distribution. The syntax of the function is:\n//\n//\tCHISQ.INV.RT(probability,degrees_freedom)\nfunc (fn *formulaFuncs) CHISQdotINVdotRT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CHISQ.INV.RT requires 2 numeric arguments\")\n\t}\n\treturn fn.CHIINV(argsList)\n}\n\n// confidence is an implementation of the formula functions CONFIDENCE and\n// CONFIDENCE.NORM.\nfunc (fn *formulaFuncs) confidence(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 3 numeric arguments\", name))\n\t}\n\talpha := argsList.Front().Value.(formulaArg).ToNumber()\n\tif alpha.Type != ArgNumber {\n\t\treturn alpha\n\t}\n\tif alpha.Number <= 0 || alpha.Number >= 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tstdDev := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif stdDev.Type != ArgNumber {\n\t\treturn stdDev\n\t}\n\tif stdDev.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tsize := argsList.Back().Value.(formulaArg).ToNumber()\n\tif size.Type != ArgNumber {\n\t\treturn size\n\t}\n\tif size.Number < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\targs := list.New()\n\targs.Init()\n\targs.PushBack(newNumberFormulaArg(alpha.Number / 2))\n\targs.PushBack(newNumberFormulaArg(0))\n\targs.PushBack(newNumberFormulaArg(1))\n\treturn newNumberFormulaArg(-fn.NORMINV(args).Number * (stdDev.Number / math.Sqrt(size.Number)))\n}\n\n// CONFIDENCE function uses a Normal Distribution to calculate a confidence\n// value that can be used to construct the Confidence Interval for a\n// population mean, for a supplied probability and sample size. It is assumed\n// that the standard deviation of the population is known. The syntax of the\n// function is:\n//\n//\tCONFIDENCE(alpha,standard_dev,size)\nfunc (fn *formulaFuncs) CONFIDENCE(argsList *list.List) formulaArg {\n\treturn fn.confidence(\"CONFIDENCE\", argsList)\n}\n\n// CONFIDENCEdotNORM function uses a Normal Distribution to calculate a\n// confidence value that can be used to construct the confidence interval for\n// a population mean, for a supplied probability and sample size. It is\n// assumed that the standard deviation of the population is known. The syntax\n// of the function is:\n//\n//\tCONFIDENCE.NORM(alpha,standard_dev,size)\nfunc (fn *formulaFuncs) CONFIDENCEdotNORM(argsList *list.List) formulaArg {\n\treturn fn.confidence(\"CONFIDENCE.NORM\", argsList)\n}\n\n// CONFIDENCEdotT function uses a Student's T-Distribution to calculate a\n// confidence value that can be used to construct the confidence interval for\n// a population mean, for a supplied probablity and supplied sample size. It\n// is assumed that the standard deviation of the population is known. The\n// syntax of the function is:\n//\n//\tCONFIDENCE.T(alpha,standard_dev,size)\nfunc (fn *formulaFuncs) CONFIDENCEdotT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CONFIDENCE.T requires 3 arguments\")\n\t}\n\tvar alpha, standardDev, size formulaArg\n\tif alpha = argsList.Front().Value.(formulaArg).ToNumber(); alpha.Type != ArgNumber {\n\t\treturn alpha\n\t}\n\tif standardDev = argsList.Front().Next().Value.(formulaArg).ToNumber(); standardDev.Type != ArgNumber {\n\t\treturn standardDev\n\t}\n\tif size = argsList.Back().Value.(formulaArg).ToNumber(); size.Type != ArgNumber {\n\t\treturn size\n\t}\n\tif alpha.Number <= 0 || alpha.Number >= 1 || standardDev.Number <= 0 || size.Number < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif size.Number == 1 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(standardDev.Number * calcIterateInverse(calcInverseIterator{\n\t\tname: \"CONFIDENCE.T\",\n\t\tfp:   alpha.Number,\n\t\tfDF:  size.Number - 1,\n\t\tnT:   2,\n\t}, size.Number/2, size.Number) / math.Sqrt(size.Number))\n}\n\n// covar is an implementation of the formula functions COVAR, COVARIANCE.P and\n// COVARIANCE.S.\nfunc (fn *formulaFuncs) covar(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 2 arguments\", name))\n\t}\n\tarray1 := argsList.Front().Value.(formulaArg)\n\tarray2 := argsList.Back().Value.(formulaArg)\n\tleft, right := array1.ToList(), array2.ToList()\n\tn := len(left)\n\tif n != len(right) {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tl1, l2 := list.New(), list.New()\n\tl1.PushBack(array1)\n\tl2.PushBack(array2)\n\tresult, skip := 0.0, 0\n\tmean1, mean2 := fn.AVERAGE(l1), fn.AVERAGE(l2)\n\tfor i := 0; i < n; i++ {\n\t\targ1 := left[i].ToNumber()\n\t\targ2 := right[i].ToNumber()\n\t\tif arg1.Type == ArgError || arg2.Type == ArgError {\n\t\t\tskip++\n\t\t\tcontinue\n\t\t}\n\t\tresult += (arg1.Number - mean1.Number) * (arg2.Number - mean2.Number)\n\t}\n\tif name == \"COVARIANCE.S\" {\n\t\treturn newNumberFormulaArg(result / float64(n-skip-1))\n\t}\n\treturn newNumberFormulaArg(result / float64(n-skip))\n}\n\n// COVAR function calculates the covariance of two supplied sets of values. The\n// syntax of the function is:\n//\n//\tCOVAR(array1,array2)\nfunc (fn *formulaFuncs) COVAR(argsList *list.List) formulaArg {\n\treturn fn.covar(\"COVAR\", argsList)\n}\n\n// COVARIANCEdotP function calculates the population covariance of two supplied\n// sets of values. The syntax of the function is:\n//\n//\tCOVARIANCE.P(array1,array2)\nfunc (fn *formulaFuncs) COVARIANCEdotP(argsList *list.List) formulaArg {\n\treturn fn.covar(\"COVARIANCE.P\", argsList)\n}\n\n// COVARIANCEdotS function calculates the sample covariance of two supplied\n// sets of values. The syntax of the function is:\n//\n//\tCOVARIANCE.S(array1,array2)\nfunc (fn *formulaFuncs) COVARIANCEdotS(argsList *list.List) formulaArg {\n\treturn fn.covar(\"COVARIANCE.S\", argsList)\n}\n\n// calcStringCountSum is part of the implementation countSum.\nfunc calcStringCountSum(countText bool, count, sum float64, num, arg formulaArg) (float64, float64) {\n\tif countText && num.Type == ArgError && arg.String != \"\" {\n\t\tcount++\n\t}\n\tif num.Type == ArgNumber {\n\t\tsum += num.Number\n\t\tcount++\n\t}\n\treturn count, sum\n}\n\n// countSum get count and sum for a formula arguments array.\nfunc (fn *formulaFuncs) countSum(countText bool, args []formulaArg) (count, sum float64) {\n\tfor _, arg := range args {\n\t\tswitch arg.Type {\n\t\tcase ArgNumber:\n\t\t\tif countText || !arg.Boolean {\n\t\t\t\tsum += arg.Number\n\t\t\t\tcount++\n\t\t\t}\n\t\tcase ArgString:\n\t\t\tif !countText && (arg.Value() == \"TRUE\" || arg.Value() == \"FALSE\") {\n\t\t\t\tcontinue\n\t\t\t} else if countText && (arg.Value() == \"TRUE\" || arg.Value() == \"FALSE\") {\n\t\t\t\tnum := arg.ToBool()\n\t\t\t\tif num.Type == ArgNumber {\n\t\t\t\t\tcount++\n\t\t\t\t\tsum += num.Number\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tnum := arg.ToNumber()\n\t\t\tcount, sum = calcStringCountSum(countText, count, sum, num, arg)\n\t\tcase ArgList, ArgMatrix:\n\t\t\tcnt, summary := fn.countSum(countText, arg.ToList())\n\t\t\tsum += summary\n\t\t\tcount += cnt\n\t\t}\n\t}\n\treturn\n}\n\n// CORREL function calculates the Pearson Product-Moment Correlation\n// Coefficient for two sets of values. The syntax of the function is:\n//\n//\tCORREL(array1,array2)\nfunc (fn *formulaFuncs) CORREL(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CORREL requires 2 arguments\")\n\t}\n\tarray1 := argsList.Front().Value.(formulaArg)\n\tarray2 := argsList.Back().Value.(formulaArg)\n\tleft, right := array1.ToList(), array2.ToList()\n\tn := len(left)\n\tif n != len(right) {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tl1, l2, l3 := list.New(), list.New(), list.New()\n\tfor i := 0; i < n; i++ {\n\t\tif lhs, rhs := left[i].ToNumber(), right[i].ToNumber(); lhs.Number != 0 && rhs.Number != 0 {\n\t\t\tl1.PushBack(lhs)\n\t\t\tl2.PushBack(rhs)\n\t\t}\n\t}\n\tstdev1, stdev2 := fn.STDEV(l1), fn.STDEV(l2)\n\tif stdev1.Number == 0 || stdev2.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\tmean1, mean2, skip := fn.AVERAGE(l1), fn.AVERAGE(l2), 0\n\tfor i := 0; i < n; i++ {\n\t\tlhs, rhs := left[i].ToNumber(), right[i].ToNumber()\n\t\tif lhs.Number == 0 || rhs.Number == 0 {\n\t\t\tskip++\n\t\t\tcontinue\n\t\t}\n\t\tl3.PushBack(newNumberFormulaArg((lhs.Number - mean1.Number) * (rhs.Number - mean2.Number)))\n\t}\n\treturn newNumberFormulaArg(fn.SUM(l3).Number / float64(n-skip-1) / stdev1.Number / stdev2.Number)\n}\n\n// COUNT function returns the count of numeric values in a supplied set of\n// cells or values. This count includes both numbers and dates. The syntax of\n// the function is:\n//\n//\tCOUNT(value1,[value2],...)\nfunc (fn *formulaFuncs) COUNT(argsList *list.List) formulaArg {\n\tvar count int\n\tfor token := argsList.Front(); token != nil; token = token.Next() {\n\t\targ := token.Value.(formulaArg)\n\t\tswitch arg.Type {\n\t\tcase ArgString:\n\t\t\tif num := arg.ToNumber(); num.Type == ArgNumber {\n\t\t\t\tcount++\n\t\t\t}\n\t\tcase ArgNumber:\n\t\t\tcount++\n\t\tcase ArgMatrix:\n\t\t\tfor _, row := range arg.Matrix {\n\t\t\t\tfor _, cell := range row {\n\t\t\t\t\tif cell.Type == ArgNumber {\n\t\t\t\t\t\tcount++\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn newNumberFormulaArg(float64(count))\n}\n\n// COUNTA function returns the number of non-blanks within a supplied set of\n// cells or values. The syntax of the function is:\n//\n//\tCOUNTA(value1,[value2],...)\nfunc (fn *formulaFuncs) COUNTA(argsList *list.List) formulaArg {\n\tvar count int\n\tfor token := argsList.Front(); token != nil; token = token.Next() {\n\t\targ := token.Value.(formulaArg)\n\t\tswitch arg.Type {\n\t\tcase ArgString:\n\t\t\tif arg.String != \"\" {\n\t\t\t\tcount++\n\t\t\t}\n\t\tcase ArgNumber:\n\t\t\tcount++\n\t\tcase ArgMatrix:\n\t\t\tfor _, row := range arg.ToList() {\n\t\t\t\tswitch row.Type {\n\t\t\t\tcase ArgString:\n\t\t\t\t\tif row.String != \"\" {\n\t\t\t\t\t\tcount++\n\t\t\t\t\t}\n\t\t\t\tcase ArgNumber:\n\t\t\t\t\tcount++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn newNumberFormulaArg(float64(count))\n}\n\n// COUNTBLANK function returns the number of blank cells in a supplied range.\n// The syntax of the function is:\n//\n//\tCOUNTBLANK(range)\nfunc (fn *formulaFuncs) COUNTBLANK(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COUNTBLANK requires 1 argument\")\n\t}\n\tvar count float64\n\tfor _, cell := range argsList.Front().Value.(formulaArg).ToList() {\n\t\tif cell.Type == ArgEmpty {\n\t\t\tcount++\n\t\t}\n\t}\n\treturn newNumberFormulaArg(count)\n}\n\n// COUNTIF function returns the number of cells within a supplied range, that\n// satisfy a given criteria. The syntax of the function is:\n//\n//\tCOUNTIF(range,criteria)\nfunc (fn *formulaFuncs) COUNTIF(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COUNTIF requires 2 arguments\")\n\t}\n\tvar (\n\t\tcriteria = formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg))\n\t\tcount    float64\n\t)\n\tfor _, cell := range argsList.Front().Value.(formulaArg).ToList() {\n\t\tif cell.Type == ArgString && criteria.Condition.Type != ArgString {\n\t\t\tcontinue\n\t\t}\n\t\tif ok, _ := formulaCriteriaEval(cell, criteria); ok {\n\t\t\tcount++\n\t\t}\n\t}\n\treturn newNumberFormulaArg(count)\n}\n\n// formulaIfsMatch function returns cells reference array which match criteria.\nfunc formulaIfsMatch(args []formulaArg) (cellRefs []cellRef) {\n\tfor i := 0; i < len(args)-1; i += 2 {\n\t\tvar match []cellRef\n\t\tmatrix, criteria := args[i].Matrix, formulaCriteriaParser(args[i+1])\n\t\tif i == 0 {\n\t\t\tfor rowIdx, row := range matrix {\n\t\t\t\tfor colIdx, col := range row {\n\t\t\t\t\tif ok, _ := formulaCriteriaEval(col, criteria); ok {\n\t\t\t\t\t\tmatch = append(match, cellRef{Col: colIdx, Row: rowIdx})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tmatch = []cellRef{}\n\t\t\tfor _, ref := range cellRefs {\n\t\t\t\tvalue := matrix[ref.Row][ref.Col]\n\t\t\t\tif ok, _ := formulaCriteriaEval(value, criteria); ok {\n\t\t\t\t\tmatch = append(match, ref)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcellRefs = match[:]\n\t}\n\treturn\n}\n\n// COUNTIFS function returns the number of rows within a table, that satisfy a\n// set of given criteria. The syntax of the function is:\n//\n//\tCOUNTIFS(criteria_range1,criteria1,[criteria_range2,criteria2],...)\nfunc (fn *formulaFuncs) COUNTIFS(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COUNTIFS requires at least 2 arguments\")\n\t}\n\tif argsList.Len()%2 != 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tvar args []formulaArg\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\targs = append(args, arg.Value.(formulaArg))\n\t}\n\treturn newNumberFormulaArg(float64(len(formulaIfsMatch(args))))\n}\n\n// CRITBINOM function returns the inverse of the Cumulative Binomial\n// Distribution. I.e. for a specific number of independent trials, the\n// function returns the smallest value (number of successes) for which the\n// cumulative binomial distribution is greater than or equal to a specified\n// value. The syntax of the function is:\n//\n//\tCRITBINOM(trials,probability_s,alpha)\nfunc (fn *formulaFuncs) CRITBINOM(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CRITBINOM requires 3 numeric arguments\")\n\t}\n\treturn fn.BINOMdotINV(argsList)\n}\n\n// DEVSQ function calculates the sum of the squared deviations from the sample\n// mean. The syntax of the function is:\n//\n//\tDEVSQ(number1,[number2],...)\nfunc (fn *formulaFuncs) DEVSQ(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DEVSQ requires at least 1 numeric argument\")\n\t}\n\tavg, count, result := fn.AVERAGE(argsList), -1, 0.0\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\tfor _, cell := range arg.Value.(formulaArg).ToList() {\n\t\t\tif cell.Type != ArgNumber {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcount++\n\t\t\tif count == 0 {\n\t\t\t\tresult = math.Pow(cell.Number-avg.Number, 2)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tresult += math.Pow(cell.Number-avg.Number, 2)\n\t\t}\n\t}\n\tif count == -1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\treturn newNumberFormulaArg(result)\n}\n\n// FISHER function calculates the Fisher Transformation for a supplied value.\n// The syntax of the function is:\n//\n//\tFISHER(x)\nfunc (fn *formulaFuncs) FISHER(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FISHER requires 1 numeric argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tswitch token.Type {\n\tcase ArgString:\n\t\targ := token.ToNumber()\n\t\tif arg.Type == ArgNumber {\n\t\t\tif arg.Number <= -1 || arg.Number >= 1 {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t\t\t}\n\t\t\treturn newNumberFormulaArg(0.5 * math.Log((1+arg.Number)/(1-arg.Number)))\n\t\t}\n\tcase ArgNumber:\n\t\tif token.Number <= -1 || token.Number >= 1 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t\t}\n\t\treturn newNumberFormulaArg(0.5 * math.Log((1+token.Number)/(1-token.Number)))\n\t}\n\treturn newErrorFormulaArg(formulaErrorVALUE, \"FISHER requires 1 numeric argument\")\n}\n\n// FISHERINV function calculates the inverse of the Fisher Transformation and\n// returns a value between -1 and +1. The syntax of the function is:\n//\n//\tFISHERINV(y)\nfunc (fn *formulaFuncs) FISHERINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FISHERINV requires 1 numeric argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tswitch token.Type {\n\tcase ArgString:\n\t\targ := token.ToNumber()\n\t\tif arg.Type == ArgNumber {\n\t\t\treturn newNumberFormulaArg((math.Exp(2*arg.Number) - 1) / (math.Exp(2*arg.Number) + 1))\n\t\t}\n\tcase ArgNumber:\n\t\treturn newNumberFormulaArg((math.Exp(2*token.Number) - 1) / (math.Exp(2*token.Number) + 1))\n\t}\n\treturn newErrorFormulaArg(formulaErrorVALUE, \"FISHERINV requires 1 numeric argument\")\n}\n\n// FORECAST function predicts a future point on a linear trend line fitted to a\n// supplied set of x- and y- values. The syntax of the function is:\n//\n//\tFORECAST(x,known_y's,known_x's)\nfunc (fn *formulaFuncs) FORECAST(argsList *list.List) formulaArg {\n\treturn fn.pearsonProduct(\"FORECAST\", 3, argsList)\n}\n\n// FORECASTdotLINEAR function predicts a future point on a linear trend line\n// fitted to a supplied set of x- and y- values. The syntax of the function is:\n//\n//\tFORECAST.LINEAR(x,known_y's,known_x's)\nfunc (fn *formulaFuncs) FORECASTdotLINEAR(argsList *list.List) formulaArg {\n\treturn fn.pearsonProduct(\"FORECAST.LINEAR\", 3, argsList)\n}\n\n// matrixToSortedColumnList convert matrix formula arguments to a ascending\n// order list by column.\nfunc matrixToSortedColumnList(arg formulaArg) formulaArg {\n\tvar (\n\t\tmtx  []formulaArg\n\t\tcols = len(arg.Matrix[0])\n\t)\n\tfor colIdx := 0; colIdx < cols; colIdx++ {\n\t\tfor _, row := range arg.Matrix {\n\t\t\tcell := row[colIdx]\n\t\t\tif cell.Type == ArgError {\n\t\t\t\treturn cell\n\t\t\t}\n\t\t\tif cell.Type == ArgNumber {\n\t\t\t\tmtx = append(mtx, cell)\n\t\t\t}\n\t\t}\n\t}\n\targsList := newListFormulaArg(mtx)\n\tsort.Slice(argsList.List, func(i, j int) bool {\n\t\treturn argsList.List[i].Number < argsList.List[j].Number\n\t})\n\treturn argsList\n}\n\n// FREQUENCY function to count how many children fall into different age\n// ranges. The syntax of the function is:\n//\n//\tFREQUENCY(data_array,bins_array)\nfunc (fn *formulaFuncs) FREQUENCY(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FREQUENCY requires 2 arguments\")\n\t}\n\tdata, bins := argsList.Front().Value.(formulaArg), argsList.Back().Value.(formulaArg)\n\tif len(data.Matrix) == 0 {\n\t\tdata.Matrix = [][]formulaArg{{data}}\n\t}\n\tif len(bins.Matrix) == 0 {\n\t\tbins.Matrix = [][]formulaArg{{bins}}\n\t}\n\tvar (\n\t\tdataMtx, binsMtx formulaArg\n\t\tc                [][]formulaArg\n\t\ti, j             int\n\t)\n\tif dataMtx = matrixToSortedColumnList(data); dataMtx.Type != ArgList {\n\t\treturn dataMtx\n\t}\n\tif binsMtx = matrixToSortedColumnList(bins); binsMtx.Type != ArgList {\n\t\treturn binsMtx\n\t}\n\tfor row := 0; row < len(binsMtx.List)+1; row++ {\n\t\tvar rows []formulaArg\n\t\tfor col := 0; col < 1; col++ {\n\t\t\trows = append(rows, newNumberFormulaArg(0))\n\t\t}\n\t\tc = append(c, rows)\n\t}\n\tfor j = 0; j < len(binsMtx.List); j++ {\n\t\tn := 0.0\n\t\tfor i < len(dataMtx.List) && dataMtx.List[i].Number <= binsMtx.List[j].Number {\n\t\t\tn++\n\t\t\ti++\n\t\t}\n\t\tc[j] = []formulaArg{newNumberFormulaArg(n)}\n\t}\n\tc[j] = []formulaArg{newNumberFormulaArg(float64(len(dataMtx.List) - i))}\n\tif len(c) > 2 {\n\t\tc[1], c[2] = c[2], c[1]\n\t}\n\treturn newMatrixFormulaArg(c)\n}\n\n// GAMMA function returns the value of the Gamma Function, Γ(n), for a\n// specified number, n. The syntax of the function is:\n//\n//\tGAMMA(number)\nfunc (fn *formulaFuncs) GAMMA(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GAMMA requires 1 numeric argument\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GAMMA requires 1 numeric argument\")\n\t}\n\tif number.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\treturn newNumberFormulaArg(math.Gamma(number.Number))\n}\n\n// GAMMAdotDIST function returns the Gamma Distribution, which is frequently\n// used to provide probabilities for values that may have a skewed\n// distribution, such as queuing analysis.\n//\n//\tGAMMA.DIST(x,alpha,beta,cumulative)\nfunc (fn *formulaFuncs) GAMMAdotDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GAMMA.DIST requires 4 arguments\")\n\t}\n\treturn fn.GAMMADIST(argsList)\n}\n\n// GAMMADIST function returns the Gamma Distribution, which is frequently used\n// to provide probabilities for values that may have a skewed distribution,\n// such as queuing analysis.\n//\n//\tGAMMADIST(x,alpha,beta,cumulative)\nfunc (fn *formulaFuncs) GAMMADIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GAMMADIST requires 4 arguments\")\n\t}\n\tvar x, alpha, beta, cumulative formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif x.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif alpha = argsList.Front().Next().Value.(formulaArg).ToNumber(); alpha.Type != ArgNumber {\n\t\treturn alpha\n\t}\n\tif beta = argsList.Back().Prev().Value.(formulaArg).ToNumber(); beta.Type != ArgNumber {\n\t\treturn beta\n\t}\n\tif alpha.Number <= 0 || beta.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError {\n\t\treturn cumulative\n\t}\n\tif cumulative.Number == 1 {\n\t\treturn newNumberFormulaArg(incompleteGamma(alpha.Number, x.Number/beta.Number) / math.Gamma(alpha.Number))\n\t}\n\treturn newNumberFormulaArg((1 / (math.Pow(beta.Number, alpha.Number) * math.Gamma(alpha.Number))) * math.Pow(x.Number, alpha.Number-1) * math.Exp(0-(x.Number/beta.Number)))\n}\n\n// gammainv returns the inverse of the Gamma distribution for the specified\n// value.\nfunc gammainv(probability, alpha, beta float64) float64 {\n\txLo, xHi := 0.0, alpha*beta*5\n\tdx, x, xNew, result := 1024.0, 1.0, 1.0, 0.0\n\tfor i := 0; math.Abs(dx) > 8.88e-016 && i <= 256; i++ {\n\t\tresult = incompleteGamma(alpha, x/beta) / math.Gamma(alpha)\n\t\te := result - probability\n\t\tif e == 0 {\n\t\t\tdx = 0\n\t\t} else if e < 0 {\n\t\t\txLo = x\n\t\t} else {\n\t\t\txHi = x\n\t\t}\n\t\tpdf := (1 / (math.Pow(beta, alpha) * math.Gamma(alpha))) * math.Pow(x, alpha-1) * math.Exp(0-(x/beta))\n\t\tif pdf != 0 {\n\t\t\tdx = e / pdf\n\t\t\txNew = x - dx\n\t\t}\n\t\tif xNew < xLo || xNew > xHi || pdf == 0 {\n\t\t\txNew = (xLo + xHi) / 2\n\t\t\tdx = xNew - x\n\t\t}\n\t\tx = xNew\n\t}\n\treturn x\n}\n\n// GAMMAdotINV function returns the inverse of the Gamma Cumulative\n// Distribution. The syntax of the function is:\n//\n//\tGAMMA.INV(probability,alpha,beta)\nfunc (fn *formulaFuncs) GAMMAdotINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GAMMA.INV requires 3 arguments\")\n\t}\n\treturn fn.GAMMAINV(argsList)\n}\n\n// GAMMAINV function returns the inverse of the Gamma Cumulative Distribution.\n// The syntax of the function is:\n//\n//\tGAMMAINV(probability,alpha,beta)\nfunc (fn *formulaFuncs) GAMMAINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GAMMAINV requires 3 arguments\")\n\t}\n\tvar probability, alpha, beta formulaArg\n\tif probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\tif probability.Number < 0 || probability.Number >= 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif alpha = argsList.Front().Next().Value.(formulaArg).ToNumber(); alpha.Type != ArgNumber {\n\t\treturn alpha\n\t}\n\tif beta = argsList.Back().Value.(formulaArg).ToNumber(); beta.Type != ArgNumber {\n\t\treturn beta\n\t}\n\tif alpha.Number <= 0 || beta.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(gammainv(probability.Number, alpha.Number, beta.Number))\n}\n\n// GAMMALN function returns the natural logarithm of the Gamma Function, Γ\n// (n). The syntax of the function is:\n//\n//\tGAMMALN(x)\nfunc (fn *formulaFuncs) GAMMALN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GAMMALN requires 1 numeric argument\")\n\t}\n\tx := argsList.Front().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GAMMALN requires 1 numeric argument\")\n\t}\n\tif x.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\treturn newNumberFormulaArg(math.Log(math.Gamma(x.Number)))\n}\n\n// GAMMALNdotPRECISE function returns the natural logarithm of the Gamma\n// Function, Γ(n). The syntax of the function is:\n//\n//\tGAMMALN.PRECISE(x)\nfunc (fn *formulaFuncs) GAMMALNdotPRECISE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GAMMALN.PRECISE requires 1 numeric argument\")\n\t}\n\tx := argsList.Front().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif x.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(getLogGamma(x.Number))\n}\n\n// GAUSS function returns the probability that a member of a standard normal\n// population will fall between the mean and a specified number of standard\n// deviations from the mean. The syntax of the function is:\n//\n//\tGAUSS(z)\nfunc (fn *formulaFuncs) GAUSS(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GAUSS requires 1 numeric argument\")\n\t}\n\targs := list.New().Init()\n\targs.PushBack(argsList.Front().Value.(formulaArg))\n\targs.PushBack(formulaArg{Type: ArgNumber, Number: 0})\n\targs.PushBack(formulaArg{Type: ArgNumber, Number: 1})\n\targs.PushBack(newBoolFormulaArg(true))\n\tnormdist := fn.NORMDIST(args)\n\tif normdist.Type != ArgNumber {\n\t\treturn normdist\n\t}\n\treturn newNumberFormulaArg(normdist.Number - 0.5)\n}\n\n// GEOMEAN function calculates the geometric mean of a supplied set of values.\n// The syntax of the function is:\n//\n//\tGEOMEAN(number1,[number2],...)\nfunc (fn *formulaFuncs) GEOMEAN(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"GEOMEAN requires at least 1 numeric argument\")\n\t}\n\tproduct := fn.PRODUCT(argsList)\n\tif product.Type != ArgNumber {\n\t\treturn product\n\t}\n\tcount := fn.COUNT(argsList)\n\tminVal := fn.MIN(argsList)\n\tif product.Number > 0 && minVal.Number > 0 {\n\t\treturn newNumberFormulaArg(math.Pow(product.Number, 1/count.Number))\n\t}\n\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n}\n\n// getNewMatrix create matrix by given columns and rows.\nfunc getNewMatrix(c, r int) (matrix [][]float64) {\n\tfor i := 0; i < c; i++ {\n\t\tfor j := 0; j < r; j++ {\n\t\t\tfor x := len(matrix); x <= i; x++ {\n\t\t\t\tmatrix = append(matrix, []float64{})\n\t\t\t}\n\t\t\tfor y := len(matrix[i]); y <= j; y++ {\n\t\t\t\tmatrix[i] = append(matrix[i], 0)\n\t\t\t}\n\t\t\tmatrix[i][j] = 0\n\t\t}\n\t}\n\treturn\n}\n\n// approxSub subtract two values, if signs are identical and the values are\n// equal, will be returns 0 instead of calculating the subtraction.\nfunc approxSub(a, b float64) float64 {\n\tif ((a < 0 && b < 0) || (a > 0 && b > 0)) && math.Abs(a-b) < 2.22045e-016 {\n\t\treturn 0\n\t}\n\treturn a - b\n}\n\n// matrixClone return a copy of all elements of the original matrix.\nfunc matrixClone(matrix [][]float64) (cloneMatrix [][]float64) {\n\tfor i := 0; i < len(matrix); i++ {\n\t\tfor j := 0; j < len(matrix[i]); j++ {\n\t\t\tfor x := len(cloneMatrix); x <= i; x++ {\n\t\t\t\tcloneMatrix = append(cloneMatrix, []float64{})\n\t\t\t}\n\t\t\tfor k := len(cloneMatrix[i]); k <= j; k++ {\n\t\t\t\tcloneMatrix[i] = append(cloneMatrix[i], 0)\n\t\t\t}\n\t\t\tcloneMatrix[i][j] = matrix[i][j]\n\t\t}\n\t}\n\treturn\n}\n\n// trendGrowthMatrixInfo defined matrix checking result.\ntype trendGrowthMatrixInfo struct {\n\ttrendType, nCX, nCY, nRX, nRY, M, N int\n\tmtxX, mtxY                          [][]float64\n}\n\n// prepareTrendGrowthMtxX is a part of implementation of the trend growth prepare.\nfunc prepareTrendGrowthMtxX(mtxX [][]float64) [][]float64 {\n\tvar mtx [][]float64\n\tfor i := 0; i < len(mtxX); i++ {\n\t\tfor j := 0; j < len(mtxX[i]); j++ {\n\t\t\tif mtxX[i][j] == 0 {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tfor x := len(mtx); x <= j; x++ {\n\t\t\t\tmtx = append(mtx, []float64{})\n\t\t\t}\n\t\t\tfor y := len(mtx[j]); y <= i; y++ {\n\t\t\t\tmtx[j] = append(mtx[j], 0)\n\t\t\t}\n\t\t\tmtx[j][i] = mtxX[i][j]\n\t\t}\n\t}\n\treturn mtx\n}\n\n// prepareTrendGrowthMtxY is a part of implementation of the trend growth prepare.\nfunc prepareTrendGrowthMtxY(bLOG bool, mtxY [][]float64) [][]float64 {\n\tvar mtx [][]float64\n\tfor i := 0; i < len(mtxY); i++ {\n\t\tfor j := 0; j < len(mtxY[i]); j++ {\n\t\t\tif mtxY[i][j] == 0 {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tfor x := len(mtx); x <= j; x++ {\n\t\t\t\tmtx = append(mtx, []float64{})\n\t\t\t}\n\t\t\tfor y := len(mtx[j]); y <= i; y++ {\n\t\t\t\tmtx[j] = append(mtx[j], 0)\n\t\t\t}\n\t\t\tmtx[j][i] = mtxY[i][j]\n\t\t}\n\t}\n\tif bLOG {\n\t\tvar pNewY [][]float64\n\t\tfor i := 0; i < len(mtxY); i++ {\n\t\t\tfor j := 0; j < len(mtxY[i]); j++ {\n\t\t\t\tfVal := mtxY[i][j]\n\t\t\t\tif fVal <= 0 {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\tfor x := len(pNewY); x <= j; x++ {\n\t\t\t\t\tpNewY = append(pNewY, []float64{})\n\t\t\t\t}\n\t\t\t\tfor y := len(pNewY[j]); y <= i; y++ {\n\t\t\t\t\tpNewY[j] = append(pNewY[j], 0)\n\t\t\t\t}\n\t\t\t\tpNewY[j][i] = math.Log(fVal)\n\t\t\t}\n\t\t}\n\t\tmtx = pNewY\n\t}\n\treturn mtx\n}\n\n// prepareTrendGrowth check and return the result.\nfunc prepareTrendGrowth(bLOG bool, mtxX, mtxY [][]float64) (*trendGrowthMatrixInfo, formulaArg) {\n\tvar nCX, nRX, M, N, trendType int\n\tnRY, nCY := len(mtxY), len(mtxY[0])\n\tcntY := nCY * nRY\n\tnewY := prepareTrendGrowthMtxY(bLOG, mtxY)\n\tif newY == nil {\n\t\treturn nil, newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tvar newX [][]float64\n\tif len(mtxX) != 0 {\n\t\tnRX, nCX = len(mtxX), len(mtxX[0])\n\t\tif newX = prepareTrendGrowthMtxX(mtxX); newX == nil {\n\t\t\treturn nil, newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t\tif nCX == nCY && nRX == nRY {\n\t\t\ttrendType, M, N = 1, 1, cntY // simple regression\n\t\t} else if nCY != 1 && nRY != 1 {\n\t\t\treturn nil, newErrorFormulaArg(formulaErrorREF, formulaErrorREF)\n\t\t} else if nCY == 1 {\n\t\t\tif nRX != nRY {\n\t\t\t\treturn nil, newErrorFormulaArg(formulaErrorREF, formulaErrorREF)\n\t\t\t}\n\t\t\ttrendType, M, N = 2, nCX, nRY\n\t\t} else if nCX != nCY {\n\t\t\treturn nil, newErrorFormulaArg(formulaErrorREF, formulaErrorREF)\n\t\t} else {\n\t\t\ttrendType, M, N = 3, nRX, nCY\n\t\t}\n\t} else {\n\t\tnewX = getNewMatrix(nCY, nRY)\n\t\tnCX, nRX = nCY, nRY\n\t\tnum := 1.0\n\t\tfor i := 0; i < nRY; i++ {\n\t\t\tfor j := 0; j < nCY; j++ {\n\t\t\t\tnewX[j][i] = num\n\t\t\t\tnum++\n\t\t\t}\n\t\t}\n\t\ttrendType, M, N = 1, 1, cntY\n\t}\n\treturn &trendGrowthMatrixInfo{\n\t\ttrendType: trendType,\n\t\tnCX:       nCX,\n\t\tnCY:       nCY,\n\t\tnRX:       nRX,\n\t\tnRY:       nRY,\n\t\tM:         M,\n\t\tN:         N,\n\t\tmtxX:      newX,\n\t\tmtxY:      newY,\n\t}, newEmptyFormulaArg()\n}\n\n// calcPosition calculate position for matrix by given index.\nfunc calcPosition(mtx [][]float64, idx int) (row, col int) {\n\trowSize := len(mtx[0])\n\tcol = idx\n\tif rowSize > 1 {\n\t\tcol = idx / rowSize\n\t}\n\trow = idx - col*rowSize\n\treturn\n}\n\n// getDouble returns float64 data type value in the matrix by given index.\nfunc getDouble(mtx [][]float64, idx int) float64 {\n\trow, col := calcPosition(mtx, idx)\n\treturn mtx[col][row]\n}\n\n// putDouble set a float64 data type value in the matrix by given index.\nfunc putDouble(mtx [][]float64, idx int, val float64) {\n\trow, col := calcPosition(mtx, idx)\n\tmtx[col][row] = val\n}\n\n// calcMeanOverAll returns mean of the given matrix by over all element.\nfunc calcMeanOverAll(mtx [][]float64, n int) float64 {\n\tvar sum float64\n\tfor i := 0; i < len(mtx); i++ {\n\t\tfor j := 0; j < len(mtx[i]); j++ {\n\t\t\tsum += mtx[i][j]\n\t\t}\n\t}\n\treturn sum / float64(n)\n}\n\n// calcSumProduct returns uses the matrices as vectors of length M over all\n// element.\nfunc calcSumProduct(mtxA, mtxB [][]float64, m int) float64 {\n\tsum := 0.0\n\tfor i := 0; i < m; i++ {\n\t\tsum += getDouble(mtxA, i) * getDouble(mtxB, i)\n\t}\n\treturn sum\n}\n\n// calcColumnMeans calculates means of the columns of matrix.\nfunc calcColumnMeans(mtxX, mtxRes [][]float64, c, r int) {\n\tfor i := 0; i < c; i++ {\n\t\tvar sum float64\n\t\tfor k := 0; k < r; k++ {\n\t\t\tsum += mtxX[i][k]\n\t\t}\n\t\tputDouble(mtxRes, i, sum/float64(r))\n\t}\n}\n\n// calcColumnsDelta calculates subtract of the columns of matrix.\nfunc calcColumnsDelta(mtx, columnMeans [][]float64, c, r int) {\n\tfor i := 0; i < c; i++ {\n\t\tfor k := 0; k < r; k++ {\n\t\t\tmtx[i][k] = approxSub(mtx[i][k], getDouble(columnMeans, i))\n\t\t}\n\t}\n}\n\n// calcSign returns sign by given value, no mathematical signum, but used to\n// switch between adding and subtracting.\nfunc calcSign(val float64) float64 {\n\tif val > 0 {\n\t\treturn 1\n\t}\n\treturn -1\n}\n\n// calcColsMaximumNorm is a special version for use within QR\n// decomposition. Maximum norm of column index c starting in row index r;\n// matrix A has count n rows.\nfunc calcColsMaximumNorm(mtxA [][]float64, c, r, n int) float64 {\n\tvar norm float64\n\tfor row := r; row < n; row++ {\n\t\tif norm < math.Abs(mtxA[c][row]) {\n\t\t\tnorm = math.Abs(mtxA[c][row])\n\t\t}\n\t}\n\treturn norm\n}\n\n// calcFastMult returns multiply n x m matrix A with m x l matrix B to n x l matrix R.\nfunc calcFastMult(mtxA, mtxB, mtxR [][]float64, n, m, l int) {\n\tvar sum float64\n\tfor row := 0; row < n; row++ {\n\t\tfor col := 0; col < l; col++ {\n\t\t\tsum = 0.0\n\t\t\tfor k := 0; k < m; k++ {\n\t\t\t\tsum += mtxA[k][row] * mtxB[col][k]\n\t\t\t}\n\t\t\tmtxR[col][row] = sum\n\t\t}\n\t}\n}\n\n// calcRowsEuclideanNorm is a special version for use within QR\n// decomposition. Euclidean norm of column index c starting in row index r;\n// matrix a has count n rows.\nfunc calcRowsEuclideanNorm(mtxA [][]float64, c, r, n int) float64 {\n\tvar norm float64\n\tfor row := r; row < n; row++ {\n\t\tnorm += mtxA[c][row] * mtxA[c][row]\n\t}\n\treturn math.Sqrt(norm)\n}\n\n// calcRowsSumProduct is a special version for use within QR decomposition.\n// <A(a);B(b)> starting in row index r;\n// a and b are indices of columns, matrices A and B have count n rows.\nfunc calcRowsSumProduct(mtxA [][]float64, a int, mtxB [][]float64, b, r, n int) float64 {\n\tvar result float64\n\tfor row := r; row < n; row++ {\n\t\tresult += mtxA[a][row] * mtxB[b][row]\n\t}\n\treturn result\n}\n\n// calcSolveWithUpperRightTriangle solve for X in R*X=S using back substitution.\nfunc calcSolveWithUpperRightTriangle(mtxA [][]float64, vecR []float64, mtxS [][]float64, k int, bIsTransposed bool) {\n\tvar row int\n\tfor rowp1 := k; rowp1 > 0; rowp1-- {\n\t\trow = rowp1 - 1\n\t\tsum := getDouble(mtxS, row)\n\t\tfor col := rowp1; col < k; col++ {\n\t\t\tif bIsTransposed {\n\t\t\t\tsum -= mtxA[row][col] * getDouble(mtxS, col)\n\t\t\t} else {\n\t\t\t\tsum -= mtxA[col][row] * getDouble(mtxS, col)\n\t\t\t}\n\t\t}\n\t\tputDouble(mtxS, row, sum/vecR[row])\n\t}\n}\n\n// calcRowQRDecomposition calculates a QR decomposition with Householder\n// reflection.\nfunc calcRowQRDecomposition(mtxA [][]float64, vecR []float64, k, n int) bool {\n\tfor col := 0; col < k; col++ {\n\t\tscale := calcColsMaximumNorm(mtxA, col, col, n)\n\t\tif scale == 0 {\n\t\t\treturn false\n\t\t}\n\t\tfor row := col; row < n; row++ {\n\t\t\tmtxA[col][row] = mtxA[col][row] / scale\n\t\t}\n\t\teuclid := calcRowsEuclideanNorm(mtxA, col, col, n)\n\t\tfactor := 1.0 / euclid / (euclid + math.Abs(mtxA[col][col]))\n\t\tsignum := calcSign(mtxA[col][col])\n\t\tmtxA[col][col] = mtxA[col][col] + signum*euclid\n\t\tvecR[col] = -signum * scale * euclid\n\t\t// apply Householder transformation to A\n\t\tfor c := col + 1; c < k; c++ {\n\t\t\tsum := calcRowsSumProduct(mtxA, col, mtxA, c, col, n)\n\t\t\tfor row := col; row < n; row++ {\n\t\t\t\tmtxA[c][row] = mtxA[c][row] - sum*factor*mtxA[col][row]\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\n// calcApplyColsHouseholderTransformation transposed matrices A and Y.\nfunc calcApplyColsHouseholderTransformation(mtxA [][]float64, r int, mtxY [][]float64, n int) {\n\tdenominator := calcColsSumProduct(mtxA, r, mtxA, r, r, n)\n\tnumerator := calcColsSumProduct(mtxA, r, mtxY, 0, r, n)\n\tfactor := 2 * (numerator / denominator)\n\tfor col := r; col < n; col++ {\n\t\tputDouble(mtxY, col, getDouble(mtxY, col)-factor*mtxA[col][r])\n\t}\n}\n\n// calcRowMeans calculates means of the rows of matrix.\nfunc calcRowMeans(mtxX, mtxRes [][]float64, c, r int) {\n\tfor k := 0; k < r; k++ {\n\t\tvar fSum float64\n\t\tfor i := 0; i < c; i++ {\n\t\t\tfSum += mtxX[i][k]\n\t\t}\n\t\tmtxRes[k][0] = fSum / float64(c)\n\t}\n}\n\n// calcRowsDelta calculates subtract of the rows of matrix.\nfunc calcRowsDelta(mtx, rowMeans [][]float64, c, r int) {\n\tfor k := 0; k < r; k++ {\n\t\tfor i := 0; i < c; i++ {\n\t\t\tmtx[i][k] = approxSub(mtx[i][k], rowMeans[k][0])\n\t\t}\n\t}\n}\n\n// calcColumnMaximumNorm returns maximum norm of row index R starting in col\n// index C; matrix A has count N columns.\nfunc calcColumnMaximumNorm(mtxA [][]float64, r, c, n int) float64 {\n\tvar norm float64\n\tfor col := c; col < n; col++ {\n\t\tif norm < math.Abs(mtxA[col][r]) {\n\t\t\tnorm = math.Abs(mtxA[col][r])\n\t\t}\n\t}\n\treturn norm\n}\n\n// calcColsEuclideanNorm returns euclidean norm of row index R starting in\n// column index C; matrix A has count N columns.\nfunc calcColsEuclideanNorm(mtxA [][]float64, r, c, n int) float64 {\n\tvar norm float64\n\tfor col := c; col < n; col++ {\n\t\tnorm += (mtxA[col][r]) * (mtxA[col][r])\n\t}\n\treturn math.Sqrt(norm)\n}\n\n// calcColsSumProduct returns sum product for given matrix.\nfunc calcColsSumProduct(mtxA [][]float64, a int, mtxB [][]float64, b, c, n int) float64 {\n\tvar result float64\n\tfor col := c; col < n; col++ {\n\t\tresult += mtxA[col][a] * mtxB[col][b]\n\t}\n\treturn result\n}\n\n// calcColQRDecomposition same with transposed matrix A, N is count of\n// columns, k count of rows.\nfunc calcColQRDecomposition(mtxA [][]float64, vecR []float64, k, n int) bool {\n\tvar sum float64\n\tfor row := 0; row < k; row++ {\n\t\t// calculate vector u of the householder transformation\n\t\tscale := calcColumnMaximumNorm(mtxA, row, row, n)\n\t\tif scale == 0 {\n\t\t\treturn false\n\t\t}\n\t\tfor col := row; col < n; col++ {\n\t\t\tmtxA[col][row] = mtxA[col][row] / scale\n\t\t}\n\t\teuclid := calcColsEuclideanNorm(mtxA, row, row, n)\n\t\tfactor := 1 / euclid / (euclid + math.Abs(mtxA[row][row]))\n\t\tsignum := calcSign(mtxA[row][row])\n\t\tmtxA[row][row] = mtxA[row][row] + signum*euclid\n\t\tvecR[row] = -signum * scale * euclid\n\t\t// apply Householder transformation to A\n\t\tfor r := row + 1; r < k; r++ {\n\t\t\tsum = calcColsSumProduct(mtxA, row, mtxA, r, row, n)\n\t\t\tfor col := row; col < n; col++ {\n\t\t\t\tmtxA[col][r] = mtxA[col][r] - sum*factor*mtxA[col][row]\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\n// calcApplyRowsHouseholderTransformation applies a Householder transformation to a\n// column vector Y with is given as Nx1 Matrix. The vector u, from which the\n// Householder transformation is built, is the column part in matrix A, with\n// column index c, starting with row index c. A is the result of the QR\n// decomposition as obtained from calcRowQRDecomposition.\nfunc calcApplyRowsHouseholderTransformation(mtxA [][]float64, c int, mtxY [][]float64, n int) {\n\tdenominator := calcRowsSumProduct(mtxA, c, mtxA, c, c, n)\n\tnumerator := calcRowsSumProduct(mtxA, c, mtxY, 0, c, n)\n\tfactor := 2 * (numerator / denominator)\n\tfor row := c; row < n; row++ {\n\t\tputDouble(mtxY, row, getDouble(mtxY, row)-factor*mtxA[c][row])\n\t}\n}\n\n// calcTrendGrowthSimpleRegression calculate simple regression for the calcTrendGrowth.\nfunc calcTrendGrowthSimpleRegression(bConstant, bGrowth bool, mtxY, mtxX, newX, mtxRes [][]float64, meanY float64, N int) {\n\tvar meanX float64\n\tif bConstant {\n\t\tmeanX = calcMeanOverAll(mtxX, N)\n\t\tfor i := 0; i < len(mtxX); i++ {\n\t\t\tfor j := 0; j < len(mtxX[i]); j++ {\n\t\t\t\tmtxX[i][j] = approxSub(mtxX[i][j], meanX)\n\t\t\t}\n\t\t}\n\t}\n\tsumXY := calcSumProduct(mtxX, mtxY, N)\n\tsumX2 := calcSumProduct(mtxX, mtxX, N)\n\tslope := sumXY / sumX2\n\tvar help float64\n\tvar intercept float64\n\tif bConstant {\n\t\tintercept = meanY - slope*meanX\n\t\tfor i := 0; i < len(mtxRes); i++ {\n\t\t\tfor j := 0; j < len(mtxRes[i]); j++ {\n\t\t\t\thelp = newX[i][j]*slope + intercept\n\t\t\t\tif bGrowth {\n\t\t\t\t\tmtxRes[i][j] = math.Exp(help)\n\t\t\t\t} else {\n\t\t\t\t\tmtxRes[i][j] = help\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor i := 0; i < len(mtxRes); i++ {\n\t\t\tfor j := 0; j < len(mtxRes[i]); j++ {\n\t\t\t\thelp = newX[i][j] * slope\n\t\t\t\tif bGrowth {\n\t\t\t\t\tmtxRes[i][j] = math.Exp(help)\n\t\t\t\t} else {\n\t\t\t\t\tmtxRes[i][j] = help\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// calcTrendGrowthMultipleRegressionPart1 calculate multiple regression for the\n// calcTrendGrowth.\nfunc calcTrendGrowthMultipleRegressionPart1(bConstant, bGrowth bool, mtxY, mtxX, newX, mtxRes [][]float64, meanY float64, RXN, K, N int) {\n\tvecR := make([]float64, N)   // for QR decomposition\n\tmeans := getNewMatrix(K, 1)  // mean of each column\n\tslopes := getNewMatrix(1, K) // from b1 to bK\n\tif len(means) == 0 || len(slopes) == 0 {\n\t\treturn\n\t}\n\tif bConstant {\n\t\tcalcColumnMeans(mtxX, means, K, N)\n\t\tcalcColumnsDelta(mtxX, means, K, N)\n\t}\n\tif !calcRowQRDecomposition(mtxX, vecR, K, N) {\n\t\treturn\n\t}\n\t// Later on we will divide by elements of vecR, so make sure that they aren't zero.\n\tbIsSingular := false\n\tfor row := 0; row < K && !bIsSingular; row++ {\n\t\tbIsSingular = bIsSingular || vecR[row] == 0\n\t}\n\tif bIsSingular {\n\t\treturn\n\t}\n\tfor col := 0; col < K; col++ {\n\t\tcalcApplyRowsHouseholderTransformation(mtxX, col, mtxY, N)\n\t}\n\tfor col := 0; col < K; col++ {\n\t\tputDouble(slopes, col, getDouble(mtxY, col))\n\t}\n\tcalcSolveWithUpperRightTriangle(mtxX, vecR, slopes, K, false)\n\t// Fill result matrix\n\tcalcFastMult(newX, slopes, mtxRes, RXN, K, 1)\n\tif bConstant {\n\t\tintercept := meanY - calcSumProduct(means, slopes, K)\n\t\tfor row := 0; row < RXN; row++ {\n\t\t\tmtxRes[0][row] = mtxRes[0][row] + intercept\n\t\t}\n\t}\n\tif bGrowth {\n\t\tfor i := 0; i < RXN; i++ {\n\t\t\tputDouble(mtxRes, i, math.Exp(getDouble(mtxRes, i)))\n\t\t}\n\t}\n}\n\n// calcTrendGrowthMultipleRegressionPart2 calculate multiple regression for the\n// calcTrendGrowth.\nfunc calcTrendGrowthMultipleRegressionPart2(bConstant, bGrowth bool, mtxY, mtxX, newX, mtxRes [][]float64, meanY float64, nCXN, K, N int) {\n\tvecR := make([]float64, N)   // for QR decomposition\n\tmeans := getNewMatrix(K, 1)  // mean of each row\n\tslopes := getNewMatrix(K, 1) // row from b1 to bK\n\tif len(means) == 0 || len(slopes) == 0 {\n\t\treturn\n\t}\n\tif bConstant {\n\t\tcalcRowMeans(mtxX, means, N, K)\n\t\tcalcRowsDelta(mtxX, means, N, K)\n\t}\n\tif !calcColQRDecomposition(mtxX, vecR, K, N) {\n\t\treturn\n\t}\n\t// later on we will divide by elements of vecR, so make sure that they aren't zero\n\tbIsSingular := false\n\tfor row := 0; row < K && !bIsSingular; row++ {\n\t\tbIsSingular = bIsSingular || vecR[row] == 0\n\t}\n\tif bIsSingular {\n\t\treturn\n\t}\n\tfor row := 0; row < K; row++ {\n\t\tcalcApplyColsHouseholderTransformation(mtxX, row, mtxY, N)\n\t}\n\tfor col := 0; col < K; col++ {\n\t\tputDouble(slopes, col, getDouble(mtxY, col))\n\t}\n\tcalcSolveWithUpperRightTriangle(mtxX, vecR, slopes, K, true)\n\t// fill result matrix\n\tcalcFastMult(slopes, newX, mtxRes, 1, K, nCXN)\n\tif bConstant {\n\t\tfIntercept := meanY - calcSumProduct(means, slopes, K)\n\t\tfor col := 0; col < nCXN; col++ {\n\t\t\tmtxRes[col][0] = mtxRes[col][0] + fIntercept\n\t\t}\n\t}\n\tif bGrowth {\n\t\tfor i := 0; i < nCXN; i++ {\n\t\t\tputDouble(mtxRes, i, math.Exp(getDouble(mtxRes, i)))\n\t\t}\n\t}\n}\n\n// calcTrendGrowthRegression is a part of implementation of the calcTrendGrowth.\nfunc calcTrendGrowthRegression(bConstant, bGrowth bool, trendType, nCXN, nRXN, K, N int, mtxY, mtxX, newX, mtxRes [][]float64) {\n\tif len(mtxRes) == 0 {\n\t\treturn\n\t}\n\tvar meanY float64\n\tif bConstant {\n\t\tcopyX, copyY := matrixClone(mtxX), matrixClone(mtxY)\n\t\tmtxX, mtxY = copyX, copyY\n\t\tmeanY = calcMeanOverAll(mtxY, N)\n\t\tfor i := 0; i < len(mtxY); i++ {\n\t\t\tfor j := 0; j < len(mtxY[i]); j++ {\n\t\t\t\tmtxY[i][j] = approxSub(mtxY[i][j], meanY)\n\t\t\t}\n\t\t}\n\t}\n\tswitch trendType {\n\tcase 1:\n\t\tcalcTrendGrowthSimpleRegression(bConstant, bGrowth, mtxY, mtxX, newX, mtxRes, meanY, N)\n\tcase 2:\n\t\tcalcTrendGrowthMultipleRegressionPart1(bConstant, bGrowth, mtxY, mtxX, newX, mtxRes, meanY, nRXN, K, N)\n\tdefault:\n\t\tcalcTrendGrowthMultipleRegressionPart2(bConstant, bGrowth, mtxY, mtxX, newX, mtxRes, meanY, nCXN, K, N)\n\t}\n}\n\n// calcTrendGrowth returns values along a predicted exponential trend.\nfunc calcTrendGrowth(mtxY, mtxX, newX [][]float64, bConstant, bGrowth bool) ([][]float64, formulaArg) {\n\tgetMatrixParams, errArg := prepareTrendGrowth(bGrowth, mtxX, mtxY)\n\tif errArg.Type != ArgEmpty {\n\t\treturn nil, errArg\n\t}\n\ttrendType := getMatrixParams.trendType\n\tnCX := getMatrixParams.nCX\n\tnRX := getMatrixParams.nRX\n\tK := getMatrixParams.M\n\tN := getMatrixParams.N\n\tmtxX = getMatrixParams.mtxX\n\tmtxY = getMatrixParams.mtxY\n\t// checking if data samples are enough\n\tif (bConstant && (N < K+1)) || (!bConstant && (N < K)) || (N < 1) || (K < 1) {\n\t\treturn nil, errArg\n\t}\n\t// set the default newX if necessary\n\tnCXN, nRXN := nCX, nRX\n\tif len(newX) == 0 {\n\t\tnewX = matrixClone(mtxX) // mtxX will be changed to X-meanX\n\t} else {\n\t\tnRXN, nCXN = len(newX[0]), len(newX)\n\t\tif (trendType == 2 && K != nCXN) || (trendType == 3 && K != nRXN) {\n\t\t\treturn nil, errArg\n\t\t}\n\t}\n\tvar mtxRes [][]float64\n\tswitch trendType {\n\tcase 1:\n\t\tmtxRes = getNewMatrix(nCXN, nRXN)\n\tcase 2:\n\t\tmtxRes = getNewMatrix(1, nRXN)\n\tdefault:\n\t\tmtxRes = getNewMatrix(nCXN, 1)\n\t}\n\tcalcTrendGrowthRegression(bConstant, bGrowth, trendType, nCXN, nRXN, K, N, mtxY, mtxX, newX, mtxRes)\n\treturn mtxRes, errArg\n}\n\n// trendGrowth is an implementation of the formula functions GROWTH and TREND.\nfunc (fn *formulaFuncs) trendGrowth(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 1 argument\", name))\n\t}\n\tif argsList.Len() > 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s allows at most 4 arguments\", name))\n\t}\n\tvar knowY, knowX, newX [][]float64\n\tvar errArg formulaArg\n\tconstArg := newBoolFormulaArg(true)\n\tknowY, errArg = newNumberMatrix(argsList.Front().Value.(formulaArg), false)\n\tif errArg.Type == ArgError {\n\t\treturn errArg\n\t}\n\tif argsList.Len() > 1 {\n\t\tknowX, errArg = newNumberMatrix(argsList.Front().Next().Value.(formulaArg), false)\n\t\tif errArg.Type == ArgError {\n\t\t\treturn errArg\n\t\t}\n\t}\n\tif argsList.Len() > 2 {\n\t\tnewX, errArg = newNumberMatrix(argsList.Front().Next().Next().Value.(formulaArg), false)\n\t\tif errArg.Type == ArgError {\n\t\t\treturn errArg\n\t\t}\n\t}\n\tif argsList.Len() > 3 {\n\t\tif constArg = argsList.Back().Value.(formulaArg).ToBool(); constArg.Type != ArgNumber {\n\t\t\treturn constArg\n\t\t}\n\t}\n\tvar mtxNewX [][]float64\n\tfor i := 0; i < len(newX); i++ {\n\t\tfor j := 0; j < len(newX[i]); j++ {\n\t\t\tfor x := len(mtxNewX); x <= j; x++ {\n\t\t\t\tmtxNewX = append(mtxNewX, []float64{})\n\t\t\t}\n\t\t\tfor k := len(mtxNewX[j]); k <= i; k++ {\n\t\t\t\tmtxNewX[j] = append(mtxNewX[j], 0)\n\t\t\t}\n\t\t\tmtxNewX[j][i] = newX[i][j]\n\t\t}\n\t}\n\tmtx, errArg := calcTrendGrowth(knowY, knowX, mtxNewX, constArg.Number == 1, name == \"GROWTH\")\n\tif errArg.Type != ArgEmpty {\n\t\treturn errArg\n\t}\n\treturn newMatrixFormulaArg(newFormulaArgMatrix(mtx))\n}\n\n// GROWTH function calculates the exponential growth curve through a given set\n// of y-values and (optionally), one or more sets of x-values. The function\n// then extends the curve to calculate additional y-values for a further\n// supplied set of new x-values. The syntax of the function is:\n//\n//\tGROWTH(known_y's,[known_x's],[new_x's],[const])\nfunc (fn *formulaFuncs) GROWTH(argsList *list.List) formulaArg {\n\treturn fn.trendGrowth(\"GROWTH\", argsList)\n}\n\n// HARMEAN function calculates the harmonic mean of a supplied set of values.\n// The syntax of the function is:\n//\n//\tHARMEAN(number1,[number2],...)\nfunc (fn *formulaFuncs) HARMEAN(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"HARMEAN requires at least 1 argument\")\n\t}\n\tif minVal := fn.MIN(argsList); minVal.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tnumber, val, cnt := 0.0, 0.0, 0.0\n\tfor token := argsList.Front(); token != nil; token = token.Next() {\n\t\targ := token.Value.(formulaArg)\n\t\tswitch arg.Type {\n\t\tcase ArgString:\n\t\t\tnum := arg.ToNumber()\n\t\t\tif num.Type != ArgNumber {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tnumber = num.Number\n\t\tcase ArgNumber:\n\t\t\tnumber = arg.Number\n\t\t}\n\t\tif number <= 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t\t}\n\t\tval += 1 / number\n\t\tcnt++\n\t}\n\treturn newNumberFormulaArg(1 / (val / cnt))\n}\n\n// checkHYPGEOMDISTArgs checking arguments for the formula function HYPGEOMDIST\n// and HYPGEOM.DIST.\nfunc checkHYPGEOMDISTArgs(sampleS, numberSample, populationS, numberPop formulaArg) bool {\n\treturn sampleS.Number < 0 ||\n\t\tsampleS.Number > math.Min(numberSample.Number, populationS.Number) ||\n\t\tsampleS.Number < math.Max(0, numberSample.Number-numberPop.Number+populationS.Number) ||\n\t\tnumberSample.Number <= 0 ||\n\t\tnumberSample.Number > numberPop.Number ||\n\t\tpopulationS.Number <= 0 ||\n\t\tpopulationS.Number > numberPop.Number ||\n\t\tnumberPop.Number <= 0\n}\n\n// prepareHYPGEOMDISTArgs prepare arguments for the formula function\n// HYPGEOMDIST and HYPGEOM.DIST.\nfunc (fn *formulaFuncs) prepareHYPGEOMDISTArgs(name string, argsList *list.List) formulaArg {\n\tif name == \"HYPGEOMDIST\" && argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"HYPGEOMDIST requires 4 numeric arguments\")\n\t}\n\tif name == \"HYPGEOM.DIST\" && argsList.Len() != 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"HYPGEOM.DIST requires 5 arguments\")\n\t}\n\tvar sampleS, numberSample, populationS, numberPop, cumulative formulaArg\n\tif sampleS = argsList.Front().Value.(formulaArg).ToNumber(); sampleS.Type != ArgNumber {\n\t\treturn sampleS\n\t}\n\tif numberSample = argsList.Front().Next().Value.(formulaArg).ToNumber(); numberSample.Type != ArgNumber {\n\t\treturn numberSample\n\t}\n\tif populationS = argsList.Front().Next().Next().Value.(formulaArg).ToNumber(); populationS.Type != ArgNumber {\n\t\treturn populationS\n\t}\n\tif numberPop = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); numberPop.Type != ArgNumber {\n\t\treturn numberPop\n\t}\n\tif checkHYPGEOMDISTArgs(sampleS, numberSample, populationS, numberPop) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif name == \"HYPGEOM.DIST\" {\n\t\tif cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type != ArgNumber {\n\t\t\treturn cumulative\n\t\t}\n\t}\n\treturn newListFormulaArg([]formulaArg{sampleS, numberSample, populationS, numberPop, cumulative})\n}\n\n// HYPGEOMdotDIST function returns the value of the hypergeometric distribution\n// for a specified number of successes from a population sample. The function\n// can calculate the cumulative distribution or the probability density\n// function. The syntax of the function is:\n//\n//\tHYPGEOM.DIST(sample_s,number_sample,population_s,number_pop,cumulative)\nfunc (fn *formulaFuncs) HYPGEOMdotDIST(argsList *list.List) formulaArg {\n\targs := fn.prepareHYPGEOMDISTArgs(\"HYPGEOM.DIST\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsampleS, numberSample, populationS, numberPop, cumulative := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4]\n\tif cumulative.Number == 1 {\n\t\tvar res float64\n\t\tfor i := 0; i <= int(sampleS.Number); i++ {\n\t\t\tres += binomCoeff(populationS.Number, float64(i)) *\n\t\t\t\tbinomCoeff(numberPop.Number-populationS.Number, numberSample.Number-float64(i)) /\n\t\t\t\tbinomCoeff(numberPop.Number, numberSample.Number)\n\t\t}\n\t\treturn newNumberFormulaArg(res)\n\t}\n\treturn newNumberFormulaArg(binomCoeff(populationS.Number, sampleS.Number) *\n\t\tbinomCoeff(numberPop.Number-populationS.Number, numberSample.Number-sampleS.Number) /\n\t\tbinomCoeff(numberPop.Number, numberSample.Number))\n}\n\n// HYPGEOMDIST function returns the value of the hypergeometric distribution\n// for a given number of successes from a sample of a population. The syntax\n// of the function is:\n//\n//\tHYPGEOMDIST(sample_s,number_sample,population_s,number_pop)\nfunc (fn *formulaFuncs) HYPGEOMDIST(argsList *list.List) formulaArg {\n\targs := fn.prepareHYPGEOMDISTArgs(\"HYPGEOMDIST\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsampleS, numberSample, populationS, numberPop := args.List[0], args.List[1], args.List[2], args.List[3]\n\treturn newNumberFormulaArg(binomCoeff(populationS.Number, sampleS.Number) *\n\t\tbinomCoeff(numberPop.Number-populationS.Number, numberSample.Number-sampleS.Number) /\n\t\tbinomCoeff(numberPop.Number, numberSample.Number))\n}\n\n// INTERCEPT function calculates the intercept (the value at the intersection\n// of the y axis) of the linear regression line through a supplied set of x-\n// and y- values. The syntax of the function is:\n//\n//\tINTERCEPT(known_y's,known_x's)\nfunc (fn *formulaFuncs) INTERCEPT(argsList *list.List) formulaArg {\n\treturn fn.pearsonProduct(\"INTERCEPT\", 2, argsList)\n}\n\n// KURT function calculates the kurtosis of a supplied set of values. The\n// syntax of the function is:\n//\n//\tKURT(number1,[number2],...)\nfunc (fn *formulaFuncs) KURT(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"KURT requires at least 1 argument\")\n\t}\n\tmean, stdev := fn.AVERAGE(argsList), fn.STDEV(argsList)\n\tif stdev.Number > 0 {\n\t\tcount, summer := 0.0, 0.0\n\t\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\t\ttoken := arg.Value.(formulaArg)\n\t\t\tswitch token.Type {\n\t\t\tcase ArgString, ArgNumber:\n\t\t\t\tnum := token.ToNumber()\n\t\t\t\tif num.Type == ArgError {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tsummer += math.Pow((num.Number-mean.Number)/stdev.Number, 4)\n\t\t\t\tcount++\n\t\t\tcase ArgList, ArgMatrix:\n\t\t\t\tfor _, row := range token.ToList() {\n\t\t\t\t\tif row.Type == ArgNumber || row.Type == ArgString {\n\t\t\t\t\t\tnum := row.ToNumber()\n\t\t\t\t\t\tif num.Type == ArgError {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsummer += math.Pow((num.Number-mean.Number)/stdev.Number, 4)\n\t\t\t\t\t\tcount++\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif count > 3 {\n\t\t\treturn newNumberFormulaArg(summer*(count*(count+1)/((count-1)*(count-2)*(count-3))) - (3 * math.Pow(count-1, 2) / ((count - 2) * (count - 3))))\n\t\t}\n\t}\n\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n}\n\n// EXPONdotDIST function returns the value of the exponential distribution for\n// a give value of x. The user can specify whether the probability density\n// function or the cumulative distribution function is used. The syntax of the\n// Expondist function is:\n//\n//\tEXPON.DIST(x,lambda,cumulative)\nfunc (fn *formulaFuncs) EXPONdotDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"EXPON.DIST requires 3 arguments\")\n\t}\n\treturn fn.EXPONDIST(argsList)\n}\n\n// EXPONDIST function returns the value of the exponential distribution for a\n// give value of x. The user can specify whether the probability density\n// function or the cumulative distribution function is used. The syntax of the\n// Expondist function is:\n//\n//\tEXPONDIST(x,lambda,cumulative)\nfunc (fn *formulaFuncs) EXPONDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"EXPONDIST requires 3 arguments\")\n\t}\n\tvar x, lambda, cumulative formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif lambda = argsList.Front().Next().Value.(formulaArg).ToNumber(); lambda.Type != ArgNumber {\n\t\treturn lambda\n\t}\n\tif cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError {\n\t\treturn cumulative\n\t}\n\tif x.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif lambda.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif cumulative.Number == 1 {\n\t\treturn newNumberFormulaArg(1 - math.Exp(-lambda.Number*x.Number))\n\t}\n\treturn newNumberFormulaArg(lambda.Number * math.Exp(-lambda.Number*x.Number))\n}\n\n// FdotDIST function calculates the Probability Density Function or the\n// Cumulative Distribution Function for the F Distribution. This function is\n// frequently used to measure the degree of diversity between two data\n// sets. The syntax of the function is:\n//\n//\tF.DIST(x,deg_freedom1,deg_freedom2,cumulative)\nfunc (fn *formulaFuncs) FdotDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"F.DIST requires 4 arguments\")\n\t}\n\tvar x, deg1, deg2, cumulative formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif deg1 = argsList.Front().Next().Value.(formulaArg).ToNumber(); deg1.Type != ArgNumber {\n\t\treturn deg1\n\t}\n\tif deg2 = argsList.Front().Next().Next().Value.(formulaArg).ToNumber(); deg2.Type != ArgNumber {\n\t\treturn deg2\n\t}\n\tif cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError {\n\t\treturn cumulative\n\t}\n\tif x.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tmaxDeg := math.Pow10(10)\n\tif deg1.Number < 1 || deg1.Number >= maxDeg {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif deg2.Number < 1 || deg2.Number >= maxDeg {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif cumulative.Number == 1 {\n\t\treturn newNumberFormulaArg(1 - getBetaDist(deg2.Number/(deg2.Number+deg1.Number*x.Number), deg2.Number/2, deg1.Number/2))\n\t}\n\treturn newNumberFormulaArg(math.Gamma((deg2.Number+deg1.Number)/2) / (math.Gamma(deg1.Number/2) * math.Gamma(deg2.Number/2)) * math.Pow(deg1.Number/deg2.Number, deg1.Number/2) * (math.Pow(x.Number, (deg1.Number-2)/2) / math.Pow(1+(deg1.Number/deg2.Number)*x.Number, (deg1.Number+deg2.Number)/2)))\n}\n\n// FDIST function calculates the (right-tailed) F Probability Distribution,\n// which measures the degree of diversity between two data sets. The syntax\n// of the function is:\n//\n//\tFDIST(x,deg_freedom1,deg_freedom2)\nfunc (fn *formulaFuncs) FDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FDIST requires 3 arguments\")\n\t}\n\tvar x, deg1, deg2 formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif deg1 = argsList.Front().Next().Value.(formulaArg).ToNumber(); deg1.Type != ArgNumber {\n\t\treturn deg1\n\t}\n\tif deg2 = argsList.Back().Value.(formulaArg).ToNumber(); deg2.Type != ArgNumber {\n\t\treturn deg2\n\t}\n\tif x.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tmaxDeg := math.Pow10(10)\n\tif deg1.Number < 1 || deg1.Number >= maxDeg {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif deg2.Number < 1 || deg2.Number >= maxDeg {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\targs := list.New()\n\targs.PushBack(newNumberFormulaArg(deg1.Number * x.Number / (deg1.Number*x.Number + deg2.Number)))\n\targs.PushBack(newNumberFormulaArg(0.5 * deg1.Number))\n\targs.PushBack(newNumberFormulaArg(0.5 * deg2.Number))\n\targs.PushBack(newNumberFormulaArg(0))\n\targs.PushBack(newNumberFormulaArg(1))\n\treturn newNumberFormulaArg(1 - fn.BETADIST(args).Number)\n}\n\n// FdotDISTdotRT function calculates the (right-tailed) F Probability\n// Distribution, which measures the degree of diversity between two data sets.\n// The syntax of the function is:\n//\n//\tF.DIST.RT(x,deg_freedom1,deg_freedom2)\nfunc (fn *formulaFuncs) FdotDISTdotRT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"F.DIST.RT requires 3 arguments\")\n\t}\n\treturn fn.FDIST(argsList)\n}\n\n// prepareFinvArgs checking and prepare arguments for the formula functions\n// F.INV, F.INV.RT and FINV.\nfunc (fn *formulaFuncs) prepareFinvArgs(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 3 arguments\", name))\n\t}\n\tvar probability, d1, d2 formulaArg\n\tif probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\tif d1 = argsList.Front().Next().Value.(formulaArg).ToNumber(); d1.Type != ArgNumber {\n\t\treturn d1\n\t}\n\tif d2 = argsList.Back().Value.(formulaArg).ToNumber(); d2.Type != ArgNumber {\n\t\treturn d2\n\t}\n\tif probability.Number <= 0 || probability.Number > 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif d1.Number < 1 || d1.Number >= math.Pow10(10) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif d2.Number < 1 || d2.Number >= math.Pow10(10) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newListFormulaArg([]formulaArg{probability, d1, d2})\n}\n\n// FdotINV function calculates the inverse of the Cumulative F Distribution\n// for a supplied probability. The syntax of the F.Inv function is:\n//\n//\tF.INV(probability,deg_freedom1,deg_freedom2)\nfunc (fn *formulaFuncs) FdotINV(argsList *list.List) formulaArg {\n\targs := fn.prepareFinvArgs(\"F.INV\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tprobability, d1, d2 := args.List[0], args.List[1], args.List[2]\n\treturn newNumberFormulaArg((1/calcBetainv(1-probability.Number, d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number))\n}\n\n// FdotINVdotRT function calculates the inverse of the (right-tailed) F\n// Probability Distribution for a supplied probability. The syntax of the\n// function is:\n//\n//\tF.INV.RT(probability,deg_freedom1,deg_freedom2)\nfunc (fn *formulaFuncs) FdotINVdotRT(argsList *list.List) formulaArg {\n\targs := fn.prepareFinvArgs(\"F.INV.RT\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tprobability, d1, d2 := args.List[0], args.List[1], args.List[2]\n\treturn newNumberFormulaArg((1/calcBetainv(1-(1-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number))\n}\n\n// FINV function calculates the inverse of the (right-tailed) F Probability\n// Distribution for a supplied probability. The syntax of the function is:\n//\n//\tFINV(probability,deg_freedom1,deg_freedom2)\nfunc (fn *formulaFuncs) FINV(argsList *list.List) formulaArg {\n\targs := fn.prepareFinvArgs(\"FINV\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tprobability, d1, d2 := args.List[0], args.List[1], args.List[2]\n\treturn newNumberFormulaArg((1/calcBetainv(1-(1-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number))\n}\n\n// FdotTEST function returns the F-Test for two supplied arrays. I.e. the\n// function returns the two-tailed probability that the variances in the two\n// supplied arrays are not significantly different. The syntax of the Ftest\n// function is:\n//\n//\tF.TEST(array1,array2)\nfunc (fn *formulaFuncs) FdotTEST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"F.TEST requires 2 arguments\")\n\t}\n\tarray1 := argsList.Front().Value.(formulaArg)\n\tarray2 := argsList.Back().Value.(formulaArg)\n\tleft, right := array1.ToList(), array2.ToList()\n\tcollectMatrix := func(args []formulaArg) (n, accu float64) {\n\t\tvar p, sum float64\n\t\tfor _, arg := range args {\n\t\t\tif num := arg.ToNumber(); num.Type == ArgNumber {\n\t\t\t\tx := num.Number - p\n\t\t\t\ty := x / (n + 1)\n\t\t\t\tp += y\n\t\t\t\taccu += n * x * y\n\t\t\t\tn++\n\t\t\t\tsum += num.Number\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\tnums, accu := collectMatrix(left)\n\tf3 := nums - 1\n\tif nums == 1 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\tf1 := accu / (nums - 1)\n\tif f1 == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\tnums, accu = collectMatrix(right)\n\tf4 := nums - 1\n\tif nums == 1 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\tf2 := accu / (nums - 1)\n\tif f2 == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\targs := list.New()\n\targs.PushBack(newNumberFormulaArg(f1 / f2))\n\targs.PushBack(newNumberFormulaArg(f3))\n\targs.PushBack(newNumberFormulaArg(f4))\n\tprobability := (1 - fn.FDIST(args).Number) * 2\n\tif probability > 1 {\n\t\tprobability = 2 - probability\n\t}\n\treturn newNumberFormulaArg(probability)\n}\n\n// FTEST function returns the F-Test for two supplied arrays. I.e. the function\n// returns the two-tailed probability that the variances in the two supplied\n// arrays are not significantly different. The syntax of the Ftest function\n// is:\n//\n//\tFTEST(array1,array2)\nfunc (fn *formulaFuncs) FTEST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FTEST requires 2 arguments\")\n\t}\n\treturn fn.FdotTEST(argsList)\n}\n\n// LOGINV function calculates the inverse of the Cumulative Log-Normal\n// Distribution Function of x, for a supplied probability. The syntax of the\n// function is:\n//\n//\tLOGINV(probability,mean,standard_dev)\nfunc (fn *formulaFuncs) LOGINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LOGINV requires 3 arguments\")\n\t}\n\tvar probability, mean, stdDev formulaArg\n\tif probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\tif mean = argsList.Front().Next().Value.(formulaArg).ToNumber(); mean.Type != ArgNumber {\n\t\treturn mean\n\t}\n\tif stdDev = argsList.Back().Value.(formulaArg).ToNumber(); stdDev.Type != ArgNumber {\n\t\treturn stdDev\n\t}\n\tif probability.Number <= 0 || probability.Number >= 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif stdDev.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\targs := list.New()\n\targs.PushBack(probability)\n\targs.PushBack(newNumberFormulaArg(0))\n\targs.PushBack(newNumberFormulaArg(1))\n\tnorminv := fn.NORMINV(args)\n\treturn newNumberFormulaArg(math.Exp(mean.Number + stdDev.Number*norminv.Number))\n}\n\n// LOGNORMdotINV function calculates the inverse of the Cumulative Log-Normal\n// Distribution Function of x, for a supplied probability. The syntax of the\n// function is:\n//\n//\tLOGNORM.INV(probability,mean,standard_dev)\nfunc (fn *formulaFuncs) LOGNORMdotINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LOGNORM.INV requires 3 arguments\")\n\t}\n\treturn fn.LOGINV(argsList)\n}\n\n// LOGNORMdotDIST function calculates the Log-Normal Probability Density\n// Function or the Cumulative Log-Normal Distribution Function for a supplied\n// value of x. The syntax of the function is:\n//\n//\tLOGNORM.DIST(x,mean,standard_dev,cumulative)\nfunc (fn *formulaFuncs) LOGNORMdotDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LOGNORM.DIST requires 4 arguments\")\n\t}\n\tvar x, mean, stdDev, cumulative formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif mean = argsList.Front().Next().Value.(formulaArg).ToNumber(); mean.Type != ArgNumber {\n\t\treturn mean\n\t}\n\tif stdDev = argsList.Back().Prev().Value.(formulaArg).ToNumber(); stdDev.Type != ArgNumber {\n\t\treturn stdDev\n\t}\n\tif cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError {\n\t\treturn cumulative\n\t}\n\tif x.Number <= 0 || stdDev.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif cumulative.Number == 1 {\n\t\targs := list.New()\n\t\targs.PushBack(newNumberFormulaArg((math.Log(x.Number) - mean.Number) / stdDev.Number))\n\t\targs.PushBack(newNumberFormulaArg(0))\n\t\targs.PushBack(newNumberFormulaArg(1))\n\t\targs.PushBack(cumulative)\n\t\treturn fn.NORMDIST(args)\n\t}\n\treturn newNumberFormulaArg((1 / (math.Sqrt(2*math.Pi) * stdDev.Number * x.Number)) *\n\t\tmath.Exp(0-(math.Pow(math.Log(x.Number)-mean.Number, 2)/(2*math.Pow(stdDev.Number, 2)))))\n}\n\n// LOGNORMDIST function calculates the Cumulative Log-Normal Distribution\n// Function at a supplied value of x. The syntax of the function is:\n//\n//\tLOGNORMDIST(x,mean,standard_dev)\nfunc (fn *formulaFuncs) LOGNORMDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LOGNORMDIST requires 3 arguments\")\n\t}\n\tvar x, mean, stdDev formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif mean = argsList.Front().Next().Value.(formulaArg).ToNumber(); mean.Type != ArgNumber {\n\t\treturn mean\n\t}\n\tif stdDev = argsList.Back().Value.(formulaArg).ToNumber(); stdDev.Type != ArgNumber {\n\t\treturn stdDev\n\t}\n\tif x.Number <= 0 || stdDev.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\targs := list.New()\n\targs.PushBack(newNumberFormulaArg((math.Log(x.Number) - mean.Number) / stdDev.Number))\n\treturn fn.NORMSDIST(args)\n}\n\n// MODE function returns the statistical mode (the most frequently occurring\n// value) of a list of supplied numbers. If there are 2 or more most\n// frequently occurring values in the supplied data, the function returns the\n// lowest of these values The syntax of the function is:\n//\n//\tMODE(number1,[number2],...)\nfunc (fn *formulaFuncs) MODE(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MODE requires at least 1 argument\")\n\t}\n\tvar values []float64\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\tcells := arg.Value.(formulaArg)\n\t\tif cells.Type != ArgMatrix && cells.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t\tfor _, cell := range cells.ToList() {\n\t\t\tif cell.Type == ArgNumber {\n\t\t\t\tvalues = append(values, cell.Number)\n\t\t\t}\n\t\t}\n\t}\n\tsort.Float64s(values)\n\tcnt := len(values)\n\tvar count, modeCnt int\n\tvar mode float64\n\tfor i := 0; i < cnt; i++ {\n\t\tcount = 0\n\t\tfor j := 0; j < cnt; j++ {\n\t\t\tif j != i && values[j] == values[i] {\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t\tif count > modeCnt {\n\t\t\tmodeCnt = count\n\t\t\tmode = values[i]\n\t\t}\n\t}\n\tif modeCnt == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\treturn newNumberFormulaArg(mode)\n}\n\n// MODEdotMULT function returns a vertical array of the statistical modes\n// (the most frequently occurring values) within a list of supplied numbers.\n// The syntax of the function is:\n//\n//\tMODE.MULT(number1,[number2],...)\nfunc (fn *formulaFuncs) MODEdotMULT(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MODE.MULT requires at least 1 argument\")\n\t}\n\tvar values []float64\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\tcells := arg.Value.(formulaArg)\n\t\tif cells.Type != ArgMatrix && cells.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t\tfor _, cell := range cells.ToList() {\n\t\t\tif cell.Type == ArgNumber {\n\t\t\t\tvalues = append(values, cell.Number)\n\t\t\t}\n\t\t}\n\t}\n\tsort.Float64s(values)\n\tcnt := len(values)\n\tvar count, modeCnt int\n\tvar mtx [][]formulaArg\n\tfor i := 0; i < cnt; i++ {\n\t\tcount = 0\n\t\tfor j := i + 1; j < cnt; j++ {\n\t\t\tif values[i] == values[j] {\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t\tif count > modeCnt {\n\t\t\tmodeCnt = count\n\t\t\tmtx = [][]formulaArg{}\n\t\t\tmtx = append(mtx, []formulaArg{newNumberFormulaArg(values[i])})\n\t\t} else if count == modeCnt {\n\t\t\tmtx = append(mtx, []formulaArg{newNumberFormulaArg(values[i])})\n\t\t}\n\t}\n\tif modeCnt == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\treturn newMatrixFormulaArg(mtx)\n}\n\n// MODEdotSNGL function returns the statistical mode (the most frequently\n// occurring value) within a list of supplied numbers. If there are 2 or more\n// most frequently occurring values in the supplied data, the function returns\n// the lowest of these values. The syntax of the function is:\n//\n//\tMODE.SNGL(number1,[number2],...)\nfunc (fn *formulaFuncs) MODEdotSNGL(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MODE.SNGL requires at least 1 argument\")\n\t}\n\treturn fn.MODE(argsList)\n}\n\n// NEGBINOMdotDIST function calculates the probability mass function or the\n// cumulative distribution function for the Negative Binomial Distribution.\n// This gives the probability that there will be a given number of failures\n// before a required number of successes is achieved. The syntax of the\n// function is:\n//\n//\tNEGBINOM.DIST(number_f,number_s,probability_s,cumulative)\nfunc (fn *formulaFuncs) NEGBINOMdotDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NEGBINOM.DIST requires 4 arguments\")\n\t}\n\tvar f, s, probability, cumulative formulaArg\n\tif f = argsList.Front().Value.(formulaArg).ToNumber(); f.Type != ArgNumber {\n\t\treturn f\n\t}\n\tif s = argsList.Front().Next().Value.(formulaArg).ToNumber(); s.Type != ArgNumber {\n\t\treturn s\n\t}\n\tif probability = argsList.Front().Next().Next().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\tif cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type != ArgNumber {\n\t\treturn cumulative\n\t}\n\tif f.Number < 0 || s.Number < 1 || probability.Number < 0 || probability.Number > 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif cumulative.Number == 1 {\n\t\treturn newNumberFormulaArg(1 - getBetaDist(1-probability.Number, f.Number+1, s.Number))\n\t}\n\treturn newNumberFormulaArg(binomCoeff(f.Number+s.Number-1, s.Number-1) * math.Pow(probability.Number, s.Number) * math.Pow(1-probability.Number, f.Number))\n}\n\n// NEGBINOMDIST function calculates the Negative Binomial Distribution for a\n// given set of parameters. This gives the probability that there will be a\n// specified number of failures before a required number of successes is\n// achieved. The syntax of the function is:\n//\n//\tNEGBINOMDIST(number_f,number_s,probability_s)\nfunc (fn *formulaFuncs) NEGBINOMDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NEGBINOMDIST requires 3 arguments\")\n\t}\n\tvar f, s, probability formulaArg\n\tif f = argsList.Front().Value.(formulaArg).ToNumber(); f.Type != ArgNumber {\n\t\treturn f\n\t}\n\tif s = argsList.Front().Next().Value.(formulaArg).ToNumber(); s.Type != ArgNumber {\n\t\treturn s\n\t}\n\tif probability = argsList.Back().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\tif f.Number < 0 || s.Number < 1 || probability.Number < 0 || probability.Number > 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(binomCoeff(f.Number+s.Number-1, s.Number-1) * math.Pow(probability.Number, s.Number) * math.Pow(1-probability.Number, f.Number))\n}\n\n// NORMdotDIST function calculates the Normal Probability Density Function or\n// the Cumulative Normal Distribution. Function for a supplied set of\n// parameters. The syntax of the function is:\n//\n//\tNORM.DIST(x,mean,standard_dev,cumulative)\nfunc (fn *formulaFuncs) NORMdotDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NORM.DIST requires 4 arguments\")\n\t}\n\treturn fn.NORMDIST(argsList)\n}\n\n// NORMDIST function calculates the Normal Probability Density Function or the\n// Cumulative Normal Distribution. Function for a supplied set of parameters.\n// The syntax of the function is:\n//\n//\tNORMDIST(x,mean,standard_dev,cumulative)\nfunc (fn *formulaFuncs) NORMDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NORMDIST requires 4 arguments\")\n\t}\n\tvar x, mean, stdDev, cumulative formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif mean = argsList.Front().Next().Value.(formulaArg).ToNumber(); mean.Type != ArgNumber {\n\t\treturn mean\n\t}\n\tif stdDev = argsList.Back().Prev().Value.(formulaArg).ToNumber(); stdDev.Type != ArgNumber {\n\t\treturn stdDev\n\t}\n\tif cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError {\n\t\treturn cumulative\n\t}\n\tif stdDev.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tif cumulative.Number == 1 {\n\t\treturn newNumberFormulaArg(0.5 * (1 + math.Erf((x.Number-mean.Number)/(stdDev.Number*math.Sqrt(2)))))\n\t}\n\treturn newNumberFormulaArg((1 / (math.Sqrt(2*math.Pi) * stdDev.Number)) * math.Exp(0-(math.Pow(x.Number-mean.Number, 2)/(2*(stdDev.Number*stdDev.Number)))))\n}\n\n// NORMdotINV function calculates the inverse of the Cumulative Normal\n// Distribution Function for a supplied value of x, and a supplied\n// distribution mean & standard deviation. The syntax of the function is:\n//\n//\tNORM.INV(probability,mean,standard_dev)\nfunc (fn *formulaFuncs) NORMdotINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NORM.INV requires 3 arguments\")\n\t}\n\treturn fn.NORMINV(argsList)\n}\n\n// NORMINV function calculates the inverse of the Cumulative Normal\n// Distribution Function for a supplied value of x, and a supplied\n// distribution mean & standard deviation. The syntax of the function is:\n//\n//\tNORMINV(probability,mean,standard_dev)\nfunc (fn *formulaFuncs) NORMINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NORMINV requires 3 arguments\")\n\t}\n\tvar prob, mean, stdDev formulaArg\n\tif prob = argsList.Front().Value.(formulaArg).ToNumber(); prob.Type != ArgNumber {\n\t\treturn prob\n\t}\n\tif mean = argsList.Front().Next().Value.(formulaArg).ToNumber(); mean.Type != ArgNumber {\n\t\treturn mean\n\t}\n\tif stdDev = argsList.Back().Value.(formulaArg).ToNumber(); stdDev.Type != ArgNumber {\n\t\treturn stdDev\n\t}\n\tif prob.Number < 0 || prob.Number > 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tif stdDev.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tinv, err := norminv(prob.Number)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(err.Error(), err.Error())\n\t}\n\treturn newNumberFormulaArg(inv*stdDev.Number + mean.Number)\n}\n\n// NORMdotSdotDIST function calculates the Standard Normal Cumulative\n// Distribution Function for a supplied value. The syntax of the function\n// is:\n//\n//\tNORM.S.DIST(z)\nfunc (fn *formulaFuncs) NORMdotSdotDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NORM.S.DIST requires 2 numeric arguments\")\n\t}\n\targs := list.New().Init()\n\targs.PushBack(argsList.Front().Value.(formulaArg))\n\targs.PushBack(formulaArg{Type: ArgNumber, Number: 0})\n\targs.PushBack(formulaArg{Type: ArgNumber, Number: 1})\n\targs.PushBack(argsList.Back().Value.(formulaArg))\n\treturn fn.NORMDIST(args)\n}\n\n// NORMSDIST function calculates the Standard Normal Cumulative Distribution\n// Function for a supplied value. The syntax of the function is:\n//\n//\tNORMSDIST(z)\nfunc (fn *formulaFuncs) NORMSDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NORMSDIST requires 1 numeric argument\")\n\t}\n\targs := list.New().Init()\n\targs.PushBack(argsList.Front().Value.(formulaArg))\n\targs.PushBack(formulaArg{Type: ArgNumber, Number: 0})\n\targs.PushBack(formulaArg{Type: ArgNumber, Number: 1})\n\targs.PushBack(formulaArg{Type: ArgNumber, Number: 1, Boolean: true})\n\treturn fn.NORMDIST(args)\n}\n\n// NORMSINV function calculates the inverse of the Standard Normal Cumulative\n// Distribution Function for a supplied probability value. The syntax of the\n// function is:\n//\n//\tNORMSINV(probability)\nfunc (fn *formulaFuncs) NORMSINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NORMSINV requires 1 numeric argument\")\n\t}\n\targs := list.New().Init()\n\targs.PushBack(argsList.Front().Value.(formulaArg))\n\targs.PushBack(formulaArg{Type: ArgNumber, Number: 0})\n\targs.PushBack(formulaArg{Type: ArgNumber, Number: 1})\n\treturn fn.NORMINV(args)\n}\n\n// NORMdotSdotINV function calculates the inverse of the Standard Normal\n// Cumulative Distribution Function for a supplied probability value. The\n// syntax of the function is:\n//\n//\tNORM.S.INV(probability)\nfunc (fn *formulaFuncs) NORMdotSdotINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NORM.S.INV requires 1 numeric argument\")\n\t}\n\targs := list.New().Init()\n\targs.PushBack(argsList.Front().Value.(formulaArg))\n\targs.PushBack(formulaArg{Type: ArgNumber, Number: 0})\n\targs.PushBack(formulaArg{Type: ArgNumber, Number: 1})\n\treturn fn.NORMINV(args)\n}\n\n// norminv returns the inverse of the normal cumulative distribution for the\n// specified value.\nfunc norminv(p float64) (float64, error) {\n\ta := map[int]float64{\n\t\t1: -3.969683028665376e+01, 2: 2.209460984245205e+02, 3: -2.759285104469687e+02,\n\t\t4: 1.383577518672690e+02, 5: -3.066479806614716e+01, 6: 2.506628277459239e+00,\n\t}\n\tb := map[int]float64{\n\t\t1: -5.447609879822406e+01, 2: 1.615858368580409e+02, 3: -1.556989798598866e+02,\n\t\t4: 6.680131188771972e+01, 5: -1.328068155288572e+01,\n\t}\n\tc := map[int]float64{\n\t\t1: -7.784894002430293e-03, 2: -3.223964580411365e-01, 3: -2.400758277161838e+00,\n\t\t4: -2.549732539343734e+00, 5: 4.374664141464968e+00, 6: 2.938163982698783e+00,\n\t}\n\td := map[int]float64{\n\t\t1: 7.784695709041462e-03, 2: 3.224671290700398e-01, 3: 2.445134137142996e+00,\n\t\t4: 3.754408661907416e+00,\n\t}\n\tpLow := 0.02425   // Use lower region approx. below this\n\tpHigh := 1 - pLow // Use upper region approx. above this\n\tif 0 < p && p < pLow {\n\t\t// Rational approximation for lower region.\n\t\tq := math.Sqrt(-2 * math.Log(p))\n\t\treturn (((((c[1]*q+c[2])*q+c[3])*q+c[4])*q+c[5])*q + c[6]) /\n\t\t\t((((d[1]*q+d[2])*q+d[3])*q+d[4])*q + 1), nil\n\t} else if pLow <= p && p <= pHigh {\n\t\t// Rational approximation for central region.\n\t\tq := p - 0.5\n\t\tr := q * q\n\t\tf1 := ((((a[1]*r+a[2])*r+a[3])*r+a[4])*r + a[5]) * r\n\t\tf2 := (b[1]*r + b[2]) * r\n\t\tf3 := ((math.Nextafter(f2, f2)+b[3])*r + b[4]) * r\n\t\tf4 := (math.Nextafter(f3, f3) + b[5]) * r\n\t\treturn (math.Nextafter(f1, f1) + a[6]) * q /\n\t\t\t(math.Nextafter(f4, f4) + 1), nil\n\t} else if pHigh < p && p < 1 {\n\t\t// Rational approximation for upper region.\n\t\tq := math.Sqrt(-2 * math.Log(1-p))\n\t\treturn -(((((c[1]*q+c[2])*q+c[3])*q+c[4])*q+c[5])*q + c[6]) /\n\t\t\t((((d[1]*q+d[2])*q+d[3])*q+d[4])*q + 1), nil\n\t}\n\treturn 0, errors.New(formulaErrorNUM)\n}\n\n// kth is an implementation of the formula functions LARGE and SMALL.\nfunc (fn *formulaFuncs) kth(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 2 arguments\", name))\n\t}\n\tarray := argsList.Front().Value.(formulaArg).ToList()\n\targK := argsList.Back().Value.(formulaArg).ToNumber()\n\tif argK.Type != ArgNumber {\n\t\treturn argK\n\t}\n\tk := int(argK.Number)\n\tif k < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"k should be > 0\")\n\t}\n\tvar data []float64\n\tfor _, arg := range array {\n\t\tif arg.Type == ArgNumber {\n\t\t\tdata = append(data, arg.Number)\n\t\t}\n\t}\n\tif len(data) < k {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"k should be <= length of array\")\n\t}\n\tsort.Float64s(data)\n\tif name == \"LARGE\" {\n\t\treturn newNumberFormulaArg(data[len(data)-k])\n\t}\n\treturn newNumberFormulaArg(data[k-1])\n}\n\n// LARGE function returns the k'th largest value from an array of numeric\n// values. The syntax of the function is:\n//\n//\tLARGE(array,k)\nfunc (fn *formulaFuncs) LARGE(argsList *list.List) formulaArg {\n\treturn fn.kth(\"LARGE\", argsList)\n}\n\n// MAX function returns the largest value from a supplied set of numeric\n// values. The syntax of the function is:\n//\n//\tMAX(number1,[number2],...)\nfunc (fn *formulaFuncs) MAX(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MAX requires at least 1 argument\")\n\t}\n\treturn fn.maxValue(false, argsList)\n}\n\n// MAXA function returns the largest value from a supplied set of numeric\n// values, while counting text and the logical value FALSE as the value 0 and\n// counting the logical value TRUE as the value 1. The syntax of the function\n// is:\n//\n//\tMAXA(number1,[number2],...)\nfunc (fn *formulaFuncs) MAXA(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MAXA requires at least 1 argument\")\n\t}\n\treturn fn.maxValue(true, argsList)\n}\n\n// MAXIFS function returns the maximum value from a subset of values that are\n// specified according to one or more criteria. The syntax of the function\n// is:\n//\n//\tMAXIFS(max_range,criteria_range1,criteria1,[criteria_range2,criteria2],...)\nfunc (fn *formulaFuncs) MAXIFS(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MAXIFS requires at least 3 arguments\")\n\t}\n\tif argsList.Len()%2 != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tvar args []formulaArg\n\tmaxVal, maxRange := -math.MaxFloat64, argsList.Front().Value.(formulaArg).Matrix\n\tfor arg := argsList.Front().Next(); arg != nil; arg = arg.Next() {\n\t\targs = append(args, arg.Value.(formulaArg))\n\t}\n\tfor _, ref := range formulaIfsMatch(args) {\n\t\tif num := maxRange[ref.Row][ref.Col].ToNumber(); num.Type == ArgNumber && maxVal < num.Number {\n\t\t\tmaxVal = num.Number\n\t\t}\n\t}\n\tif maxVal == -math.MaxFloat64 {\n\t\tmaxVal = 0\n\t}\n\treturn newNumberFormulaArg(maxVal)\n}\n\n// calcListMatrixMax is part of the implementation max.\nfunc calcListMatrixMax(maxa bool, maxVal float64, arg formulaArg) float64 {\n\tfor _, cell := range arg.ToList() {\n\t\tif cell.Type == ArgNumber && cell.Number > maxVal {\n\t\t\tif maxa && cell.Boolean || !cell.Boolean {\n\t\t\t\tmaxVal = cell.Number\n\t\t\t}\n\t\t}\n\t}\n\treturn maxVal\n}\n\n// maxValue is an implementation of the formula functions MAX and MAXA.\nfunc (fn *formulaFuncs) maxValue(maxa bool, argsList *list.List) formulaArg {\n\tmaxVal := -math.MaxFloat64\n\tfor token := argsList.Front(); token != nil; token = token.Next() {\n\t\targ := token.Value.(formulaArg)\n\t\tswitch arg.Type {\n\t\tcase ArgString:\n\t\t\tif !maxa && (arg.Value() == \"TRUE\" || arg.Value() == \"FALSE\") {\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\tnum := arg.ToBool()\n\t\t\t\tif num.Type == ArgNumber && num.Number > maxVal {\n\t\t\t\t\tmaxVal = num.Number\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tnum := arg.ToNumber()\n\t\t\tif num.Type != ArgError && num.Number > maxVal {\n\t\t\t\tmaxVal = num.Number\n\t\t\t}\n\t\tcase ArgNumber:\n\t\t\tif arg.Number > maxVal {\n\t\t\t\tmaxVal = arg.Number\n\t\t\t}\n\t\tcase ArgList, ArgMatrix:\n\t\t\tmaxVal = calcListMatrixMax(maxa, maxVal, arg)\n\t\tcase ArgError:\n\t\t\treturn arg\n\t\t}\n\t}\n\tif maxVal == -math.MaxFloat64 {\n\t\tmaxVal = 0\n\t}\n\treturn newNumberFormulaArg(maxVal)\n}\n\n// MEDIAN function returns the statistical median (the middle value) of a list\n// of supplied numbers. The syntax of the function is:\n//\n//\tMEDIAN(number1,[number2],...)\nfunc (fn *formulaFuncs) MEDIAN(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MEDIAN requires at least 1 argument\")\n\t}\n\tvar values []float64\n\tvar median float64\n\tfor token := argsList.Front(); token != nil; token = token.Next() {\n\t\targ := token.Value.(formulaArg)\n\t\tswitch arg.Type {\n\t\tcase ArgString:\n\t\t\tvalue := arg.ToNumber()\n\t\t\tif value.Type != ArgNumber {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\tvalues = append(values, value.Number)\n\t\tcase ArgNumber:\n\t\t\tvalues = append(values, arg.Number)\n\t\tcase ArgMatrix:\n\t\t\tfor _, row := range arg.Matrix {\n\t\t\t\tfor _, cell := range row {\n\t\t\t\t\tif cell.Type == ArgNumber {\n\t\t\t\t\t\tvalues = append(values, cell.Number)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif len(values) == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tsort.Float64s(values)\n\tif len(values)%2 == 0 {\n\t\tmedian = (values[len(values)/2-1] + values[len(values)/2]) / 2\n\t} else {\n\t\tmedian = values[len(values)/2]\n\t}\n\treturn newNumberFormulaArg(median)\n}\n\n// MIN function returns the smallest value from a supplied set of numeric\n// values. The syntax of the function is:\n//\n//\tMIN(number1,[number2],...)\nfunc (fn *formulaFuncs) MIN(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MIN requires at least 1 argument\")\n\t}\n\treturn fn.minValue(false, argsList)\n}\n\n// MINA function returns the smallest value from a supplied set of numeric\n// values, while counting text and the logical value FALSE as the value 0 and\n// counting the logical value TRUE as the value 1. The syntax of the function\n// is:\n//\n//\tMINA(number1,[number2],...)\nfunc (fn *formulaFuncs) MINA(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MINA requires at least 1 argument\")\n\t}\n\treturn fn.minValue(true, argsList)\n}\n\n// MINIFS function returns the minimum value from a subset of values that are\n// specified according to one or more criteria. The syntax of the function\n// is:\n//\n//\tMINIFS(min_range,criteria_range1,criteria1,[criteria_range2,criteria2],...)\nfunc (fn *formulaFuncs) MINIFS(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MINIFS requires at least 3 arguments\")\n\t}\n\tif argsList.Len()%2 != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tvar args []formulaArg\n\tminVal, minRange := math.MaxFloat64, argsList.Front().Value.(formulaArg).Matrix\n\tfor arg := argsList.Front().Next(); arg != nil; arg = arg.Next() {\n\t\targs = append(args, arg.Value.(formulaArg))\n\t}\n\tfor _, ref := range formulaIfsMatch(args) {\n\t\tif num := minRange[ref.Row][ref.Col].ToNumber(); num.Type == ArgNumber && minVal > num.Number {\n\t\t\tminVal = num.Number\n\t\t}\n\t}\n\tif minVal == math.MaxFloat64 {\n\t\tminVal = 0\n\t}\n\treturn newNumberFormulaArg(minVal)\n}\n\n// calcListMatrixMin is part of the implementation min.\nfunc calcListMatrixMin(mina bool, minVal float64, arg formulaArg) float64 {\n\tfor _, cell := range arg.ToList() {\n\t\tif cell.Type == ArgNumber && cell.Number < minVal {\n\t\t\tif mina && cell.Boolean || !cell.Boolean {\n\t\t\t\tminVal = cell.Number\n\t\t\t}\n\t\t}\n\t}\n\treturn minVal\n}\n\n// minValue is an implementation of the formula functions MIN and MINA.\nfunc (fn *formulaFuncs) minValue(mina bool, argsList *list.List) formulaArg {\n\tminVal := math.MaxFloat64\n\tfor token := argsList.Front(); token != nil; token = token.Next() {\n\t\targ := token.Value.(formulaArg)\n\t\tswitch arg.Type {\n\t\tcase ArgString:\n\t\t\tif !mina && (arg.Value() == \"TRUE\" || arg.Value() == \"FALSE\") {\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\tnum := arg.ToBool()\n\t\t\t\tif num.Type == ArgNumber && num.Number < minVal {\n\t\t\t\t\tminVal = num.Number\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tnum := arg.ToNumber()\n\t\t\tif num.Type != ArgError && num.Number < minVal {\n\t\t\t\tminVal = num.Number\n\t\t\t}\n\t\tcase ArgNumber:\n\t\t\tif arg.Number < minVal {\n\t\t\t\tminVal = arg.Number\n\t\t\t}\n\t\tcase ArgList, ArgMatrix:\n\t\t\tminVal = calcListMatrixMin(mina, minVal, arg)\n\t\tcase ArgError:\n\t\t\treturn arg\n\t\t}\n\t}\n\tif minVal == math.MaxFloat64 {\n\t\tminVal = 0\n\t}\n\treturn newNumberFormulaArg(minVal)\n}\n\n// pearsonProduct is an implementation of the formula functions FORECAST,\n// FORECAST.LINEAR, INTERCEPT, PEARSON, RSQ and SLOPE.\nfunc (fn *formulaFuncs) pearsonProduct(name string, n int, argsList *list.List) formulaArg {\n\tif argsList.Len() != n {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires %d arguments\", name, n))\n\t}\n\tvar fx formulaArg\n\tarray1 := argsList.Back().Value.(formulaArg).ToList()\n\tarray2 := argsList.Front().Value.(formulaArg).ToList()\n\tif name == \"PEARSON\" || name == \"RSQ\" {\n\t\tarray1, array2 = array2, array1\n\t}\n\tif n == 3 {\n\t\tif fx = argsList.Front().Value.(formulaArg).ToNumber(); fx.Type != ArgNumber {\n\t\t\treturn fx\n\t\t}\n\t\tarray2 = argsList.Front().Next().Value.(formulaArg).ToList()\n\t}\n\tif len(array1) != len(array2) {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tvar sum, deltaX, deltaY, x, y, length float64\n\tfor i := 0; i < len(array1); i++ {\n\t\tnum1, num2 := array1[i], array2[i]\n\t\tif num1.Type != ArgNumber || num2.Type != ArgNumber {\n\t\t\tcontinue\n\t\t}\n\t\tx += num1.Number\n\t\ty += num2.Number\n\t\tlength++\n\t}\n\tx /= length\n\ty /= length\n\tfor i := 0; i < len(array1); i++ {\n\t\tnum1, num2 := array1[i], array2[i]\n\t\tif num1.Type != ArgNumber || num2.Type != ArgNumber {\n\t\t\tcontinue\n\t\t}\n\t\tsum += (num1.Number - x) * (num2.Number - y)\n\t\tdeltaX += (num1.Number - x) * (num1.Number - x)\n\t\tdeltaY += (num2.Number - y) * (num2.Number - y)\n\t}\n\tif sum*deltaX*deltaY == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(map[string]float64{\n\t\t\"FORECAST\":        y + sum/deltaX*(fx.Number-x),\n\t\t\"FORECAST.LINEAR\": y + sum/deltaX*(fx.Number-x),\n\t\t\"INTERCEPT\":       y - sum/deltaX*x,\n\t\t\"PEARSON\":         sum / math.Sqrt(deltaX*deltaY),\n\t\t\"RSQ\":             math.Pow(sum/math.Sqrt(deltaX*deltaY), 2),\n\t\t\"SLOPE\":           sum / deltaX,\n\t}[name])\n}\n\n// PEARSON function calculates the Pearson Product-Moment Correlation\n// Coefficient for two sets of values. The syntax of the function is:\n//\n//\tPEARSON(array1,array2)\nfunc (fn *formulaFuncs) PEARSON(argsList *list.List) formulaArg {\n\treturn fn.pearsonProduct(\"PEARSON\", 2, argsList)\n}\n\n// PERCENTILEdotEXC function returns the k'th percentile (i.e. the value below\n// which k% of the data values fall) for a supplied range of values and a\n// supplied k (between 0 & 1 exclusive).The syntax of the function is:\n//\n//\tPERCENTILE.EXC(array,k)\nfunc (fn *formulaFuncs) PERCENTILEdotEXC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PERCENTILE.EXC requires 2 arguments\")\n\t}\n\tarray := argsList.Front().Value.(formulaArg).ToList()\n\tk := argsList.Back().Value.(formulaArg).ToNumber()\n\tif k.Type != ArgNumber {\n\t\treturn k\n\t}\n\tif k.Number <= 0 || k.Number >= 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tvar numbers []float64\n\tfor _, arg := range array {\n\t\tif arg.Type == ArgError {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t\tif arg.Type == ArgNumber {\n\t\t\tnumbers = append(numbers, arg.Number)\n\t\t}\n\t}\n\tcnt := len(numbers)\n\tsort.Float64s(numbers)\n\tidx := k.Number * (float64(cnt) + 1)\n\tbase := math.Floor(idx)\n\tnext := base - 1\n\tproportion := math.Nextafter(idx, idx) - base\n\treturn newNumberFormulaArg(numbers[int(next)] + ((numbers[int(base)] - numbers[int(next)]) * proportion))\n}\n\n// PERCENTILEdotINC function returns the k'th percentile (i.e. the value below\n// which k% of the data values fall) for a supplied range of values and a\n// supplied k. The syntax of the function is:\n//\n//\tPERCENTILE.INC(array,k)\nfunc (fn *formulaFuncs) PERCENTILEdotINC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PERCENTILE.INC requires 2 arguments\")\n\t}\n\treturn fn.PERCENTILE(argsList)\n}\n\n// PERCENTILE function returns the k'th percentile (i.e. the value below which\n// k% of the data values fall) for a supplied range of values and a supplied\n// k. The syntax of the function is:\n//\n//\tPERCENTILE(array,k)\nfunc (fn *formulaFuncs) PERCENTILE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PERCENTILE requires 2 arguments\")\n\t}\n\tarray := argsList.Front().Value.(formulaArg).ToList()\n\tk := argsList.Back().Value.(formulaArg).ToNumber()\n\tif k.Type != ArgNumber {\n\t\treturn k\n\t}\n\tif k.Number < 0 || k.Number > 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tvar numbers []float64\n\tfor _, arg := range array {\n\t\tif arg.Type == ArgError {\n\t\t\treturn arg\n\t\t}\n\t\tif arg.Type == ArgNumber {\n\t\t\tnumbers = append(numbers, arg.Number)\n\t\t}\n\t}\n\tcnt := len(numbers)\n\tsort.Float64s(numbers)\n\tidx := k.Number * (float64(cnt) - 1)\n\tbase := math.Floor(idx)\n\tif idx == base {\n\t\treturn newNumberFormulaArg(numbers[int(idx)])\n\t}\n\tnext := base + 1\n\tproportion := math.Nextafter(idx, idx) - base\n\treturn newNumberFormulaArg(numbers[int(base)] + ((numbers[int(next)] - numbers[int(base)]) * proportion))\n}\n\n// percentrank is an implementation of the formula functions PERCENTRANK and\n// PERCENTRANK.INC.\nfunc (fn *formulaFuncs) percentrank(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 && argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 2 or 3 arguments\", name))\n\t}\n\tarray := argsList.Front().Value.(formulaArg).ToList()\n\tx := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tvar numbers []float64\n\tfor _, arg := range array {\n\t\tif arg.Type == ArgError {\n\t\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t\t}\n\t\tif arg.Type == ArgNumber {\n\t\t\tnumbers = append(numbers, arg.Number)\n\t\t}\n\t}\n\tcnt := len(numbers)\n\tsort.Float64s(numbers)\n\tif x.Number < numbers[0] || x.Number > numbers[cnt-1] {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tpos, significance := float64(inFloat64Slice(numbers, x.Number)), newNumberFormulaArg(3)\n\tif argsList.Len() == 3 {\n\t\tif significance = argsList.Back().Value.(formulaArg).ToNumber(); significance.Type != ArgNumber {\n\t\t\treturn significance\n\t\t}\n\t\tif significance.Number < 1 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s arguments significance should be > 1\", name))\n\t\t}\n\t}\n\tif pos == -1 {\n\t\tpos = 0\n\t\tcmp := numbers[0]\n\t\tfor cmp < x.Number {\n\t\t\tpos++\n\t\t\tcmp = numbers[int(pos)]\n\t\t}\n\t\tpos--\n\t\tpos += (x.Number - numbers[int(pos)]) / (cmp - numbers[int(pos)])\n\t}\n\tpow := math.Pow(10, significance.Number)\n\tdigit := pow * pos / (float64(cnt) - 1)\n\tif name == \"PERCENTRANK.EXC\" {\n\t\tdigit = pow * (pos + 1) / (float64(cnt) + 1)\n\t}\n\treturn newNumberFormulaArg(math.Floor(digit) / pow)\n}\n\n// PERCENTRANKdotEXC function calculates the relative position, between 0 and\n// 1 (exclusive), of a specified value within a supplied array. The syntax of\n// the function is:\n//\n//\tPERCENTRANK.EXC(array,x,[significance])\nfunc (fn *formulaFuncs) PERCENTRANKdotEXC(argsList *list.List) formulaArg {\n\treturn fn.percentrank(\"PERCENTRANK.EXC\", argsList)\n}\n\n// PERCENTRANKdotINC function calculates the relative position, between 0 and\n// 1 (inclusive), of a specified value within a supplied array.The syntax of\n// the function is:\n//\n//\tPERCENTRANK.INC(array,x,[significance])\nfunc (fn *formulaFuncs) PERCENTRANKdotINC(argsList *list.List) formulaArg {\n\treturn fn.percentrank(\"PERCENTRANK.INC\", argsList)\n}\n\n// PERCENTRANK function calculates the relative position of a specified value,\n// within a set of values, as a percentage. The syntax of the function is:\n//\n//\tPERCENTRANK(array,x,[significance])\nfunc (fn *formulaFuncs) PERCENTRANK(argsList *list.List) formulaArg {\n\treturn fn.percentrank(\"PERCENTRANK\", argsList)\n}\n\n// PERMUT function calculates the number of permutations of a specified number\n// of objects from a set of objects. The syntax of the function is:\n//\n//\tPERMUT(number,number_chosen)\nfunc (fn *formulaFuncs) PERMUT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PERMUT requires 2 numeric arguments\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tchosen := argsList.Back().Value.(formulaArg).ToNumber()\n\tif number.Type != ArgNumber {\n\t\treturn number\n\t}\n\tif chosen.Type != ArgNumber {\n\t\treturn chosen\n\t}\n\tif number.Number < chosen.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\treturn newNumberFormulaArg(math.Round(fact(number.Number) / fact(number.Number-chosen.Number)))\n}\n\n// PERMUTATIONA function calculates the number of permutations, with\n// repetitions, of a specified number of objects from a set. The syntax of\n// the function is:\n//\n//\tPERMUTATIONA(number,number_chosen)\nfunc (fn *formulaFuncs) PERMUTATIONA(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PERMUTATIONA requires 2 numeric arguments\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tchosen := argsList.Back().Value.(formulaArg).ToNumber()\n\tif number.Type != ArgNumber {\n\t\treturn number\n\t}\n\tif chosen.Type != ArgNumber {\n\t\treturn chosen\n\t}\n\tnum, numChosen := math.Floor(number.Number), math.Floor(chosen.Number)\n\tif num < 0 || numChosen < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\treturn newNumberFormulaArg(math.Pow(num, numChosen))\n}\n\n// PHI function returns the value of the density function for a standard normal\n// distribution for a supplied number. The syntax of the function is:\n//\n//\tPHI(x)\nfunc (fn *formulaFuncs) PHI(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PHI requires 1 argument\")\n\t}\n\tx := argsList.Front().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn x\n\t}\n\treturn newNumberFormulaArg(0.39894228040143268 * math.Exp(-(x.Number*x.Number)/2))\n}\n\n// QUARTILE function returns a requested quartile of a supplied range of\n// values. The syntax of the function is:\n//\n//\tQUARTILE(array,quart)\nfunc (fn *formulaFuncs) QUARTILE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"QUARTILE requires 2 arguments\")\n\t}\n\tquart := argsList.Back().Value.(formulaArg).ToNumber()\n\tif quart.Type != ArgNumber {\n\t\treturn quart\n\t}\n\tif quart.Number < 0 || quart.Number > 4 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\targs := list.New().Init()\n\targs.PushBack(argsList.Front().Value.(formulaArg))\n\targs.PushBack(newNumberFormulaArg(quart.Number / 4))\n\treturn fn.PERCENTILE(args)\n}\n\n// QUARTILEdotEXC function returns a requested quartile of a supplied range of\n// values, based on a percentile range of 0 to 1 exclusive. The syntax of the\n// function is:\n//\n//\tQUARTILE.EXC(array,quart)\nfunc (fn *formulaFuncs) QUARTILEdotEXC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"QUARTILE.EXC requires 2 arguments\")\n\t}\n\tquart := argsList.Back().Value.(formulaArg).ToNumber()\n\tif quart.Type != ArgNumber {\n\t\treturn quart\n\t}\n\tif quart.Number <= 0 || quart.Number >= 4 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\targs := list.New().Init()\n\targs.PushBack(argsList.Front().Value.(formulaArg))\n\targs.PushBack(newNumberFormulaArg(quart.Number / 4))\n\treturn fn.PERCENTILEdotEXC(args)\n}\n\n// QUARTILEdotINC function returns a requested quartile of a supplied range of\n// values. The syntax of the function is:\n//\n//\tQUARTILE.INC(array,quart)\nfunc (fn *formulaFuncs) QUARTILEdotINC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"QUARTILE.INC requires 2 arguments\")\n\t}\n\treturn fn.QUARTILE(argsList)\n}\n\n// rank is an implementation of the formula functions RANK and RANK.EQ.\nfunc (fn *formulaFuncs) rank(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 2 arguments\", name))\n\t}\n\tif argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at most 3 arguments\", name))\n\t}\n\tnum := argsList.Front().Value.(formulaArg).ToNumber()\n\tif num.Type != ArgNumber {\n\t\treturn num\n\t}\n\tvar arr []float64\n\tfor _, arg := range argsList.Front().Next().Value.(formulaArg).ToList() {\n\t\tif arg.Type == ArgNumber {\n\t\t\tarr = append(arr, arg.Number)\n\t\t}\n\t}\n\tsort.Float64s(arr)\n\torder := newNumberFormulaArg(0)\n\tif argsList.Len() == 3 {\n\t\tif order = argsList.Back().Value.(formulaArg).ToNumber(); order.Type != ArgNumber {\n\t\t\treturn order\n\t\t}\n\t}\n\tif order.Number == 0 {\n\t\tsort.Sort(sort.Reverse(sort.Float64Slice(arr)))\n\t}\n\tif idx := inFloat64Slice(arr, num.Number); idx != -1 {\n\t\treturn newNumberFormulaArg(float64(idx + 1))\n\t}\n\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n}\n\n// RANKdotEQ function returns the statistical rank of a given value, within a\n// supplied array of values. If there are duplicate values in the list, these\n// are given the same rank. The syntax of the function is:\n//\n//\tRANK.EQ(number,ref,[order])\nfunc (fn *formulaFuncs) RANKdotEQ(argsList *list.List) formulaArg {\n\treturn fn.rank(\"RANK.EQ\", argsList)\n}\n\n// RANK function returns the statistical rank of a given value, within a\n// supplied array of values. If there are duplicate values in the list, these\n// are given the same rank. The syntax of the function is:\n//\n//\tRANK(number,ref,[order])\nfunc (fn *formulaFuncs) RANK(argsList *list.List) formulaArg {\n\treturn fn.rank(\"RANK\", argsList)\n}\n\n// RSQ function calculates the square of the Pearson Product-Moment Correlation\n// Coefficient for two supplied sets of values. The syntax of the function\n// is:\n//\n//\tRSQ(known_y's,known_x's)\nfunc (fn *formulaFuncs) RSQ(argsList *list.List) formulaArg {\n\treturn fn.pearsonProduct(\"RSQ\", 2, argsList)\n}\n\n// skew is an implementation of the formula functions SKEW and SKEW.P.\nfunc (fn *formulaFuncs) skew(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 1 argument\", name))\n\t}\n\tmean := fn.AVERAGE(argsList)\n\tvar stdDev formulaArg\n\tvar count, summer float64\n\tif name == \"SKEW\" {\n\t\tstdDev = fn.STDEV(argsList)\n\t} else {\n\t\tstdDev = fn.STDEVP(argsList)\n\t}\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tswitch token.Type {\n\t\tcase ArgNumber, ArgString:\n\t\t\tnum := token.ToNumber()\n\t\t\tif num.Type == ArgError {\n\t\t\t\treturn num\n\t\t\t}\n\t\t\tsummer += math.Pow((num.Number-mean.Number)/stdDev.Number, 3)\n\t\t\tcount++\n\t\tcase ArgList, ArgMatrix:\n\t\t\tfor _, cell := range token.ToList() {\n\t\t\t\tif cell.Type != ArgNumber {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tsummer += math.Pow((cell.Number-mean.Number)/stdDev.Number, 3)\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t}\n\tif count > 2 {\n\t\tif name == \"SKEW\" {\n\t\t\treturn newNumberFormulaArg(summer * (count / ((count - 1) * (count - 2))))\n\t\t}\n\t\treturn newNumberFormulaArg(summer / count)\n\t}\n\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n}\n\n// SKEW function calculates the skewness of the distribution of a supplied set\n// of values. The syntax of the function is:\n//\n//\tSKEW(number1,[number2],...)\nfunc (fn *formulaFuncs) SKEW(argsList *list.List) formulaArg {\n\treturn fn.skew(\"SKEW\", argsList)\n}\n\n// SKEWdotP function calculates the skewness of the distribution of a supplied\n// set of values. The syntax of the function is:\n//\n//\tSKEW.P(number1,[number2],...)\nfunc (fn *formulaFuncs) SKEWdotP(argsList *list.List) formulaArg {\n\treturn fn.skew(\"SKEW.P\", argsList)\n}\n\n// SLOPE returns the slope of the linear regression line through data points in\n// known_y's and known_x's. The slope is the vertical distance divided by the\n// horizontal distance between any two points on the line, which is the rate\n// of change along the regression line. The syntax of the function is:\n//\n//\tSLOPE(known_y's,known_x's)\nfunc (fn *formulaFuncs) SLOPE(argsList *list.List) formulaArg {\n\treturn fn.pearsonProduct(\"SLOPE\", 2, argsList)\n}\n\n// SMALL function returns the k'th smallest value from an array of numeric\n// values. The syntax of the function is:\n//\n//\tSMALL(array,k)\nfunc (fn *formulaFuncs) SMALL(argsList *list.List) formulaArg {\n\treturn fn.kth(\"SMALL\", argsList)\n}\n\n// STANDARDIZE function returns a normalized value of a distribution that is\n// characterized by a supplied mean and standard deviation. The syntax of the\n// function is:\n//\n//\tSTANDARDIZE(x,mean,standard_dev)\nfunc (fn *formulaFuncs) STANDARDIZE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"STANDARDIZE requires 3 arguments\")\n\t}\n\tx := argsList.Front().Value.(formulaArg).ToNumber()\n\tif x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tmean := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif mean.Type != ArgNumber {\n\t\treturn mean\n\t}\n\tstdDev := argsList.Back().Value.(formulaArg).ToNumber()\n\tif stdDev.Type != ArgNumber {\n\t\treturn stdDev\n\t}\n\tif stdDev.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\treturn newNumberFormulaArg((x.Number - mean.Number) / stdDev.Number)\n}\n\n// stdevp is an implementation of the formula functions STDEVP, STDEV.P and\n// STDEVPA.\nfunc (fn *formulaFuncs) stdevp(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 1 argument\", name))\n\t}\n\tfnName := \"VARP\"\n\tif name == \"STDEVPA\" {\n\t\tfnName = \"VARPA\"\n\t}\n\tvarp := fn.vars(fnName, argsList)\n\tif varp.Type != ArgNumber {\n\t\treturn varp\n\t}\n\treturn newNumberFormulaArg(math.Sqrt(varp.Number))\n}\n\n// STDEVP function calculates the standard deviation of a supplied set of\n// values. The syntax of the function is:\n//\n//\tSTDEVP(number1,[number2],...)\nfunc (fn *formulaFuncs) STDEVP(argsList *list.List) formulaArg {\n\treturn fn.stdevp(\"STDEVP\", argsList)\n}\n\n// STDEVdotP function calculates the standard deviation of a supplied set of\n// values.\n//\n//\tSTDEV.P( number1, [number2], ... )\nfunc (fn *formulaFuncs) STDEVdotP(argsList *list.List) formulaArg {\n\treturn fn.stdevp(\"STDEV.P\", argsList)\n}\n\n// STDEVPA function calculates the standard deviation of a supplied set of\n// values. The syntax of the function is:\n//\n//\tSTDEVPA(number1,[number2],...)\nfunc (fn *formulaFuncs) STDEVPA(argsList *list.List) formulaArg {\n\treturn fn.stdevp(\"STDEVPA\", argsList)\n}\n\n// STEYX function calculates the standard error for the line of best fit,\n// through a supplied set of x- and y- values. The syntax of the function is:\n//\n//\tSTEYX(known_y's,known_x's)\nfunc (fn *formulaFuncs) STEYX(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"STEYX requires 2 arguments\")\n\t}\n\tarray1 := argsList.Back().Value.(formulaArg).ToList()\n\tarray2 := argsList.Front().Value.(formulaArg).ToList()\n\tif len(array1) != len(array2) {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tvar count, sumX, sumY, squareX, squareY, sigmaXY float64\n\tfor i := 0; i < len(array1); i++ {\n\t\tnum1, num2 := array1[i], array2[i]\n\t\tif num1.Type != ArgNumber || num2.Type != ArgNumber {\n\t\t\tcontinue\n\t\t}\n\t\tsumX += num1.Number\n\t\tsumY += num2.Number\n\t\tsquareX += num1.Number * num1.Number\n\t\tsquareY += num2.Number * num2.Number\n\t\tsigmaXY += num1.Number * num2.Number\n\t\tcount++\n\t}\n\tif count < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\tdx, dy := sumX/count, sumY/count\n\tsigma1 := squareY - 2*dy*sumY + count*dy*dy\n\tsigma2 := sigmaXY - dy*sumX - sumY*dx + count*dy*dx\n\tsigma3 := squareX - 2*dx*sumX + count*dx*dx\n\treturn newNumberFormulaArg(math.Sqrt((sigma1 - (sigma2*sigma2)/sigma3) / (count - 2)))\n}\n\n// getTDist is an implementation for the beta distribution probability density\n// function.\nfunc getTDist(T, fDF, nType float64) float64 {\n\tvar res float64\n\tswitch nType {\n\tcase 1:\n\t\tres = 0.5 * getBetaDist(fDF/(fDF+T*T), fDF/2, 0.5)\n\tcase 2:\n\t\tres = getBetaDist(fDF/(fDF+T*T), fDF/2, 0.5)\n\tcase 3:\n\t\tres = math.Pow(1+(T*T/fDF), -(fDF+1)/2) / (math.Sqrt(fDF) * getBeta(0.5, fDF/2.0))\n\tcase 4:\n\t\tX := fDF / (T*T + fDF)\n\t\tR := 0.5 * getBetaDist(X, 0.5*fDF, 0.5)\n\t\tres = 1 - R\n\t\tif T < 0 {\n\t\t\tres = R\n\t\t}\n\t}\n\treturn res\n}\n\n// TdotDIST function calculates the one-tailed Student's T Distribution, which\n// is a continuous probability distribution that is frequently used for\n// testing hypotheses on small sample data sets. The syntax of the function\n// is:\n//\n//\tT.DIST(x,degrees_freedom,cumulative)\nfunc (fn *formulaFuncs) TdotDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"T.DIST requires 3 arguments\")\n\t}\n\tvar x, degrees, cumulative formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif degrees = argsList.Front().Next().Value.(formulaArg).ToNumber(); degrees.Type != ArgNumber {\n\t\treturn degrees\n\t}\n\tif cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type != ArgNumber {\n\t\treturn cumulative\n\t}\n\tif cumulative.Number == 1 && degrees.Number < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif cumulative.Number == 0 {\n\t\tif degrees.Number < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t\tif degrees.Number == 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t\t}\n\t\treturn newNumberFormulaArg(getTDist(x.Number, degrees.Number, 3))\n\t}\n\treturn newNumberFormulaArg(getTDist(x.Number, degrees.Number, 4))\n}\n\n// TdotDISTdot2T function calculates the two-tailed Student's T Distribution,\n// which is a continuous probability distribution that is frequently used for\n// testing hypotheses on small sample data sets. The syntax of the function\n// is:\n//\n//\tT.DIST.2T(x,degrees_freedom)\nfunc (fn *formulaFuncs) TdotDISTdot2T(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"T.DIST.2T requires 2 arguments\")\n\t}\n\tvar x, degrees formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif degrees = argsList.Back().Value.(formulaArg).ToNumber(); degrees.Type != ArgNumber {\n\t\treturn degrees\n\t}\n\tif x.Number < 0 || degrees.Number < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(getTDist(x.Number, degrees.Number, 2))\n}\n\n// TdotDISTdotRT function calculates the right-tailed Student's T Distribution,\n// which is a continuous probability distribution that is frequently used for\n// testing hypotheses on small sample data sets. The syntax of the function\n// is:\n//\n//\tT.DIST.RT(x,degrees_freedom)\nfunc (fn *formulaFuncs) TdotDISTdotRT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"T.DIST.RT requires 2 arguments\")\n\t}\n\tvar x, degrees formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif degrees = argsList.Back().Value.(formulaArg).ToNumber(); degrees.Type != ArgNumber {\n\t\treturn degrees\n\t}\n\tif degrees.Number < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tv := getTDist(x.Number, degrees.Number, 1)\n\tif x.Number < 0 {\n\t\tv = 1 - v\n\t}\n\treturn newNumberFormulaArg(v)\n}\n\n// TDIST function calculates the Student's T Distribution, which is a\n// continuous probability distribution that is frequently used for testing\n// hypotheses on small sample data sets. The syntax of the function is:\n//\n//\tTDIST(x,degrees_freedom,tails)\nfunc (fn *formulaFuncs) TDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TDIST requires 3 arguments\")\n\t}\n\tvar x, degrees, tails formulaArg\n\tif x = argsList.Front().Value.(formulaArg).ToNumber(); x.Type != ArgNumber {\n\t\treturn x\n\t}\n\tif degrees = argsList.Front().Next().Value.(formulaArg).ToNumber(); degrees.Type != ArgNumber {\n\t\treturn degrees\n\t}\n\tif tails = argsList.Back().Value.(formulaArg).ToNumber(); tails.Type != ArgNumber {\n\t\treturn tails\n\t}\n\tif x.Number < 0 || degrees.Number < 1 || (tails.Number != 1 && tails.Number != 2) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(getTDist(x.Number, degrees.Number, tails.Number))\n}\n\n// TdotINV function calculates the left-tailed inverse of the Student's T\n// Distribution, which is a continuous probability distribution that is\n// frequently used for testing hypotheses on small sample data sets. The\n// syntax of the function is:\n//\n//\tT.INV(probability,degrees_freedom)\nfunc (fn *formulaFuncs) TdotINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"T.INV requires 2 arguments\")\n\t}\n\tvar probability, degrees formulaArg\n\tif probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\tif degrees = argsList.Back().Value.(formulaArg).ToNumber(); degrees.Type != ArgNumber {\n\t\treturn degrees\n\t}\n\tif probability.Number <= 0 || probability.Number >= 1 || degrees.Number < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif probability.Number < 0.5 {\n\t\treturn newNumberFormulaArg(-calcIterateInverse(calcInverseIterator{\n\t\t\tname: \"T.INV\",\n\t\t\tfp:   1 - probability.Number,\n\t\t\tfDF:  degrees.Number,\n\t\t\tnT:   4,\n\t\t}, degrees.Number/2, degrees.Number))\n\t}\n\treturn newNumberFormulaArg(calcIterateInverse(calcInverseIterator{\n\t\tname: \"T.INV\",\n\t\tfp:   probability.Number,\n\t\tfDF:  degrees.Number,\n\t\tnT:   4,\n\t}, degrees.Number/2, degrees.Number))\n}\n\n// TdotINVdot2T function calculates the inverse of the two-tailed Student's T\n// Distribution, which is a continuous probability distribution that is\n// frequently used for testing hypotheses on small sample data sets. The\n// syntax of the function is:\n//\n//\tT.INV.2T(probability,degrees_freedom)\nfunc (fn *formulaFuncs) TdotINVdot2T(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"T.INV.2T requires 2 arguments\")\n\t}\n\tvar probability, degrees formulaArg\n\tif probability = argsList.Front().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {\n\t\treturn probability\n\t}\n\tif degrees = argsList.Back().Value.(formulaArg).ToNumber(); degrees.Type != ArgNumber {\n\t\treturn degrees\n\t}\n\tif probability.Number <= 0 || probability.Number > 1 || degrees.Number < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(calcIterateInverse(calcInverseIterator{\n\t\tname: \"T.INV.2T\",\n\t\tfp:   probability.Number,\n\t\tfDF:  degrees.Number,\n\t\tnT:   2,\n\t}, degrees.Number/2, degrees.Number))\n}\n\n// TINV function calculates the inverse of the two-tailed Student's T\n// Distribution, which is a continuous probability distribution that is\n// frequently used for testing hypotheses on small sample data sets. The\n// syntax of the function is:\n//\n//\tTINV(probability,degrees_freedom)\nfunc (fn *formulaFuncs) TINV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TINV requires 2 arguments\")\n\t}\n\treturn fn.TdotINVdot2T(argsList)\n}\n\n// TREND function calculates the linear trend line through a given set of\n// y-values and (optionally), a given set of x-values. The function then\n// extends the linear trendline to calculate additional y-values for a further\n// supplied set of new x-values. The syntax of the function is:\n//\n//\tTREND(known_y's,[known_x's],[new_x's],[const])\nfunc (fn *formulaFuncs) TREND(argsList *list.List) formulaArg {\n\treturn fn.trendGrowth(\"TREND\", argsList)\n}\n\n// tTest calculates the probability associated with the Student's T Test.\nfunc tTest(bTemplin bool, mtx1, mtx2 [][]formulaArg, c1, c2, r1, r2 int) (float64, float64, bool) {\n\tvar cnt1, cnt2, sum1, sumSqr1, sum2, sumSqr2 float64\n\tvar fVal formulaArg\n\tfor i := 0; i < c1; i++ {\n\t\tfor j := 0; j < r1; j++ {\n\t\t\tif fVal = mtx1[i][j]; fVal.Type == ArgNumber {\n\t\t\t\tsum1 += fVal.Number\n\t\t\t\tsumSqr1 += fVal.Number * fVal.Number\n\t\t\t\tcnt1++\n\t\t\t}\n\t\t}\n\t}\n\tfor i := 0; i < c2; i++ {\n\t\tfor j := 0; j < r2; j++ {\n\t\t\tif fVal = mtx2[i][j]; fVal.Type == ArgNumber {\n\t\t\t\tsum2 += fVal.Number\n\t\t\t\tsumSqr2 += fVal.Number * fVal.Number\n\t\t\t\tcnt2++\n\t\t\t}\n\t\t}\n\t}\n\tif cnt1 < 2.0 || cnt2 < 2.0 {\n\t\treturn 0, 0, false\n\t}\n\tif bTemplin {\n\t\tfS1 := (sumSqr1 - sum1*sum1/cnt1) / (cnt1 - 1) / cnt1\n\t\tfS2 := (sumSqr2 - sum2*sum2/cnt2) / (cnt2 - 1) / cnt2\n\t\tif fS1+fS2 == 0 {\n\t\t\treturn 0, 0, false\n\t\t}\n\t\tc := fS1 / (fS1 + fS2)\n\t\treturn math.Abs(sum1/cnt1-sum2/cnt2) / math.Sqrt(fS1+fS2), 1 / (c*c/(cnt1-1) + (1-c)*(1-c)/(cnt2-1)), true\n\t}\n\tfS1 := (sumSqr1 - sum1*sum1/cnt1) / (cnt1 - 1)\n\tfS2 := (sumSqr2 - sum2*sum2/cnt2) / (cnt2 - 1)\n\treturn math.Abs(sum1/cnt1-sum2/cnt2) / math.Sqrt((cnt1-1)*fS1+(cnt2-1)*fS2) * math.Sqrt(cnt1*cnt2*(cnt1+cnt2-2)/(cnt1+cnt2)), cnt1 + cnt2 - 2, true\n}\n\n// tTest is an implementation of the formula function TTEST.\nfunc (fn *formulaFuncs) tTest(mtx1, mtx2 [][]formulaArg, fTails, fTyp float64) formulaArg {\n\tvar fT, fF float64\n\tc1, c2, r1, r2, ok := len(mtx1), len(mtx2), len(mtx1[0]), len(mtx2[0]), true\n\tif fTyp == 1 {\n\t\tif c1 != c2 || r1 != r2 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t\t}\n\t\tvar cnt, sum1, sum2, sumSqrD float64\n\t\tvar fVal1, fVal2 formulaArg\n\t\tfor i := 0; i < c1; i++ {\n\t\t\tfor j := 0; j < r1; j++ {\n\t\t\t\tfVal1, fVal2 = mtx1[i][j], mtx2[i][j]\n\t\t\t\tif fVal1.Type != ArgNumber || fVal2.Type != ArgNumber {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tsum1 += fVal1.Number\n\t\t\t\tsum2 += fVal2.Number\n\t\t\t\tsumSqrD += (fVal1.Number - fVal2.Number) * (fVal1.Number - fVal2.Number)\n\t\t\t\tcnt++\n\t\t\t}\n\t\t}\n\t\tif cnt < 1 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t\tsumD := sum1 - sum2\n\t\tdivider := cnt*sumSqrD - sumD*sumD\n\t\tif divider == 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t\t}\n\t\tfT = math.Abs(sumD) * math.Sqrt((cnt-1)/divider)\n\t\tfF = cnt - 1\n\t} else if fTyp == 2 {\n\t\tfT, fF, ok = tTest(false, mtx1, mtx2, c1, c2, r1, r2)\n\t} else {\n\t\tfT, fF, ok = tTest(true, mtx1, mtx2, c1, c2, r1, r2)\n\t}\n\tif !ok {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(getTDist(fT, fF, fTails))\n}\n\n// TTEST function calculates the probability associated with the Student's T\n// Test, which is commonly used for identifying whether two data sets are\n// likely to have come from the same two underlying populations with the same\n// mean. The syntax of the function is:\n//\n//\tTTEST(array1,array2,tails,type)\nfunc (fn *formulaFuncs) TTEST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TTEST requires 4 arguments\")\n\t}\n\tvar array1, array2, tails, typeArg formulaArg\n\tarray1 = argsList.Front().Value.(formulaArg)\n\tarray2 = argsList.Front().Next().Value.(formulaArg)\n\tif tails = argsList.Front().Next().Next().Value.(formulaArg); tails.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif typeArg = argsList.Back().Value.(formulaArg); typeArg.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif len(array1.Matrix) == 0 || len(array2.Matrix) == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif tails.Number != 1 && tails.Number != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif typeArg.Number != 1 && typeArg.Number != 2 && typeArg.Number != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn fn.tTest(array1.Matrix, array2.Matrix, tails.Number, typeArg.Number)\n}\n\n// TdotTEST function calculates the probability associated with the Student's T\n// Test, which is commonly used for identifying whether two data sets are\n// likely to have come from the same two underlying populations with the same\n// mean. The syntax of the function is:\n//\n//\tT.TEST(array1,array2,tails,type)\nfunc (fn *formulaFuncs) TdotTEST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"T.TEST requires 4 arguments\")\n\t}\n\treturn fn.TTEST(argsList)\n}\n\n// TRIMMEAN function calculates the trimmed mean (or truncated mean) of a\n// supplied set of values. The syntax of the function is:\n//\n//\tTRIMMEAN(array,percent)\nfunc (fn *formulaFuncs) TRIMMEAN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TRIMMEAN requires 2 arguments\")\n\t}\n\tpercent := argsList.Back().Value.(formulaArg).ToNumber()\n\tif percent.Type != ArgNumber {\n\t\treturn percent\n\t}\n\tif percent.Number < 0 || percent.Number >= 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tvar arr []float64\n\tarrArg := argsList.Front().Value.(formulaArg).ToList()\n\tfor _, cell := range arrArg {\n\t\tif cell.Type != ArgNumber {\n\t\t\tcontinue\n\t\t}\n\t\tarr = append(arr, cell.Number)\n\t}\n\tdiscard := math.Floor(float64(len(arr)) * percent.Number / 2)\n\tsort.Float64s(arr)\n\tfor i := 0; i < int(discard); i++ {\n\t\tif len(arr) > 0 {\n\t\t\tarr = arr[1:]\n\t\t}\n\t\tif len(arr) > 0 {\n\t\t\tarr = arr[:len(arr)-1]\n\t\t}\n\t}\n\n\targs := list.New().Init()\n\tfor _, ele := range arr {\n\t\targs.PushBack(newNumberFormulaArg(ele))\n\t}\n\treturn fn.AVERAGE(args)\n}\n\n// vars is an implementation of the formula functions VAR, VARA, VARP, VAR.P\n// VAR.S and VARPA.\nfunc (fn *formulaFuncs) vars(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 1 argument\", name))\n\t}\n\tsummerA, summerB, count := 0.0, 0.0, 0.0\n\tminimum := 0.0\n\tif name == \"VAR\" || name == \"VAR.S\" || name == \"VARA\" {\n\t\tminimum = 1.0\n\t}\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\tfor _, token := range arg.Value.(formulaArg).ToList() {\n\t\t\tif token.Value() == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tnum := token.ToNumber()\n\t\t\tif token.Value() != \"TRUE\" && num.Type == ArgNumber {\n\t\t\t\tsummerA += num.Number * num.Number\n\t\t\t\tsummerB += num.Number\n\t\t\t\tcount++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tnum = token.ToBool()\n\t\t\tif num.Type == ArgNumber {\n\t\t\t\tsummerA += num.Number * num.Number\n\t\t\t\tsummerB += num.Number\n\t\t\t\tcount++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif name == \"VARA\" || name == \"VARPA\" {\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t}\n\tif count > minimum {\n\t\tsummerA *= count\n\t\tsummerB *= summerB\n\t\treturn newNumberFormulaArg((summerA - summerB) / (count * (count - minimum)))\n\t}\n\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n}\n\n// VAR function returns the sample variance of a supplied set of values. The\n// syntax of the function is:\n//\n//\tVAR(number1,[number2],...)\nfunc (fn *formulaFuncs) VAR(argsList *list.List) formulaArg {\n\treturn fn.vars(\"VAR\", argsList)\n}\n\n// VARA function calculates the sample variance of a supplied set of values.\n// The syntax of the function is:\n//\n//\tVARA(number1,[number2],...)\nfunc (fn *formulaFuncs) VARA(argsList *list.List) formulaArg {\n\treturn fn.vars(\"VARA\", argsList)\n}\n\n// VARP function returns the Variance of a given set of values. The syntax of\n// the function is:\n//\n//\tVARP(number1,[number2],...)\nfunc (fn *formulaFuncs) VARP(argsList *list.List) formulaArg {\n\treturn fn.vars(\"VARP\", argsList)\n}\n\n// VARdotP function returns the Variance of a given set of values. The syntax\n// of the function is:\n//\n//\tVAR.P(number1,[number2],...)\nfunc (fn *formulaFuncs) VARdotP(argsList *list.List) formulaArg {\n\treturn fn.vars(\"VAR.P\", argsList)\n}\n\n// VARdotS function calculates the sample variance of a supplied set of\n// values. The syntax of the function is:\n//\n//\tVAR.S(number1,[number2],...)\nfunc (fn *formulaFuncs) VARdotS(argsList *list.List) formulaArg {\n\treturn fn.vars(\"VAR.S\", argsList)\n}\n\n// VARPA function returns the Variance of a given set of values. The syntax of\n// the function is:\n//\n//\tVARPA(number1,[number2],...)\nfunc (fn *formulaFuncs) VARPA(argsList *list.List) formulaArg {\n\treturn fn.vars(\"VARPA\", argsList)\n}\n\n// WEIBULL function calculates the Weibull Probability Density Function or the\n// Weibull Cumulative Distribution Function for a supplied set of parameters.\n// The syntax of the function is:\n//\n//\tWEIBULL(x,alpha,beta,cumulative)\nfunc (fn *formulaFuncs) WEIBULL(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"WEIBULL requires 4 arguments\")\n\t}\n\tx := argsList.Front().Value.(formulaArg).ToNumber()\n\talpha := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tbeta := argsList.Back().Prev().Value.(formulaArg).ToNumber()\n\tif alpha.Type == ArgNumber && beta.Type == ArgNumber && x.Type == ArgNumber {\n\t\tif alpha.Number < 0 || alpha.Number <= 0 || beta.Number <= 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t\t}\n\t\tcumulative := argsList.Back().Value.(formulaArg).ToBool()\n\t\tif cumulative.Boolean && cumulative.Number == 1 {\n\t\t\treturn newNumberFormulaArg(1 - math.Exp(0-math.Pow(x.Number/beta.Number, alpha.Number)))\n\t\t}\n\t\treturn newNumberFormulaArg((alpha.Number / math.Pow(beta.Number, alpha.Number)) *\n\t\t\tmath.Pow(x.Number, alpha.Number-1) * math.Exp(0-math.Pow(x.Number/beta.Number, alpha.Number)))\n\t}\n\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n}\n\n// WEIBULLdotDIST function calculates the Weibull Probability Density Function\n// or the Weibull Cumulative Distribution Function for a supplied set of\n// parameters. The syntax of the function is:\n//\n//\tWEIBULL.DIST(x,alpha,beta,cumulative)\nfunc (fn *formulaFuncs) WEIBULLdotDIST(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"WEIBULL.DIST requires 4 arguments\")\n\t}\n\treturn fn.WEIBULL(argsList)\n}\n\n// ZdotTEST function calculates the one-tailed probability value of the\n// Z-Test. The syntax of the function is:\n//\n//\tZ.TEST(array,x,[sigma])\nfunc (fn *formulaFuncs) ZdotTEST(argsList *list.List) formulaArg {\n\targsLen := argsList.Len()\n\tif argsLen < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"Z.TEST requires at least 2 arguments\")\n\t}\n\tif argsLen > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"Z.TEST accepts at most 3 arguments\")\n\t}\n\treturn fn.ZTEST(argsList)\n}\n\n// ZTEST function calculates the one-tailed probability value of the Z-Test.\n// The syntax of the function is:\n//\n//\tZTEST(array,x,[sigma])\nfunc (fn *formulaFuncs) ZTEST(argsList *list.List) formulaArg {\n\targsLen := argsList.Len()\n\tif argsLen < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ZTEST requires at least 2 arguments\")\n\t}\n\tif argsLen > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ZTEST accepts at most 3 arguments\")\n\t}\n\tarrArg, arrArgs := argsList.Front().Value.(formulaArg), list.New()\n\tarrArgs.PushBack(arrArg)\n\tarr := fn.AVERAGE(arrArgs)\n\tif arr.Type == ArgError {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tx := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif x.Type == ArgError {\n\t\treturn x\n\t}\n\tsigma := argsList.Back().Value.(formulaArg).ToNumber()\n\tif sigma.Type == ArgError {\n\t\treturn sigma\n\t}\n\tif argsLen != 3 {\n\t\tsigma = fn.STDEV(arrArgs).ToNumber()\n\t}\n\tnormsdistArg := list.New()\n\tdiv := sigma.Number / math.Sqrt(float64(len(arrArg.ToList())))\n\tif div == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\tnormsdistArg.PushBack(newNumberFormulaArg((arr.Number - x.Number) / div))\n\treturn newNumberFormulaArg(1 - fn.NORMSDIST(normsdistArg).Number)\n}\n\n// Information Functions\n\n// ERRORdotTYPE function receives an error value and returns an integer, that\n// tells you the type of the supplied error. The syntax of the function is:\n//\n//\tERROR.TYPE(error_val)\nfunc (fn *formulaFuncs) ERRORdotTYPE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ERROR.TYPE requires 1 argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tif token.Type == ArgError {\n\t\tfor i, errType := range []string{\n\t\t\tformulaErrorNULL, formulaErrorDIV, formulaErrorVALUE, formulaErrorREF,\n\t\t\tformulaErrorNAME, formulaErrorNUM, formulaErrorNA,\n\t\t} {\n\t\t\tif errType == token.String {\n\t\t\t\treturn newNumberFormulaArg(float64(i) + 1)\n\t\t\t}\n\t\t}\n\t}\n\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n}\n\n// ISBLANK function tests if a specified cell is blank (empty) and if so,\n// returns TRUE; Otherwise the function returns FALSE. The syntax of the\n// function is:\n//\n//\tISBLANK(value)\nfunc (fn *formulaFuncs) ISBLANK(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISBLANK requires 1 argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tswitch token.Type {\n\tcase ArgUnknown, ArgEmpty:\n\t\treturn newBoolFormulaArg(true)\n\tdefault:\n\t\treturn newBoolFormulaArg(false)\n\t}\n}\n\n// ISERR function tests if an initial supplied expression (or value) returns\n// any Excel Error, except the #N/A error. If so, the function returns the\n// logical value TRUE; If the supplied value is not an error or is the #N/A\n// error, the ISERR function returns FALSE. The syntax of the function is:\n//\n//\tISERR(value)\nfunc (fn *formulaFuncs) ISERR(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISERR requires 1 argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tresult := false\n\tif token.Type == ArgError {\n\t\tfor _, errType := range []string{\n\t\t\tformulaErrorDIV, formulaErrorNAME, formulaErrorNUM,\n\t\t\tformulaErrorVALUE, formulaErrorREF, formulaErrorNULL,\n\t\t\tformulaErrorSPILL, formulaErrorCALC, formulaErrorGETTINGDATA,\n\t\t} {\n\t\t\tif errType == token.String {\n\t\t\t\tresult = true\n\t\t\t}\n\t\t}\n\t}\n\treturn newBoolFormulaArg(result)\n}\n\n// ISERROR function tests if an initial supplied expression (or value) returns\n// an Excel Error, and if so, returns the logical value TRUE; Otherwise the\n// function returns FALSE. The syntax of the function is:\n//\n//\tISERROR(value)\nfunc (fn *formulaFuncs) ISERROR(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISERROR requires 1 argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tresult := false\n\tif token.Type == ArgError {\n\t\tfor _, errType := range []string{\n\t\t\tformulaErrorDIV, formulaErrorNAME, formulaErrorNA, formulaErrorNUM,\n\t\t\tformulaErrorVALUE, formulaErrorREF, formulaErrorNULL, formulaErrorSPILL,\n\t\t\tformulaErrorCALC, formulaErrorGETTINGDATA,\n\t\t} {\n\t\t\tif errType == token.String {\n\t\t\t\tresult = true\n\t\t\t}\n\t\t}\n\t}\n\treturn newBoolFormulaArg(result)\n}\n\n// ISEVEN function tests if a supplied number (or numeric expression)\n// evaluates to an even number, and if so, returns TRUE; Otherwise, the\n// function returns FALSE. The syntax of the function is:\n//\n//\tISEVEN(value)\nfunc (fn *formulaFuncs) ISEVEN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISEVEN requires 1 argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tswitch token.Type {\n\tcase ArgEmpty:\n\t\treturn newBoolFormulaArg(true)\n\tcase ArgNumber, ArgString:\n\t\tnum := token.ToNumber()\n\t\tif num.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t\tif num.Number == 1 {\n\t\t\treturn newBoolFormulaArg(false)\n\t\t}\n\t\treturn newBoolFormulaArg(num.Number == num.Number/2*2)\n\tdefault:\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n}\n\n// ISFORMULA function tests if a specified cell contains a formula, and if so,\n// returns TRUE; Otherwise, the function returns FALSE. The syntax of the\n// function is:\n//\n//\tISFORMULA(reference)\nfunc (fn *formulaFuncs) ISFORMULA(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISFORMULA requires 1 argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg)\n\tif arg.cellRefs != nil && arg.cellRefs.Len() == 1 {\n\t\tref := arg.cellRefs.Front().Value.(cellRef)\n\t\tcell, _ := CoordinatesToCellName(ref.Col, ref.Row)\n\t\tif formula, _ := fn.f.GetCellFormula(ref.Sheet, cell); len(formula) > 0 {\n\t\t\treturn newBoolFormulaArg(true)\n\t\t}\n\t}\n\treturn newBoolFormulaArg(false)\n}\n\n// ISLOGICAL function tests if a supplied value (or expression) returns a\n// logical value (i.e. evaluates to True or False). If so, the function\n// returns TRUE; Otherwise, it returns FALSE. The syntax of the function is:\n//\n//\tISLOGICAL(value)\nfunc (fn *formulaFuncs) ISLOGICAL(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISLOGICAL requires 1 argument\")\n\t}\n\tval := argsList.Front().Value.(formulaArg).Value()\n\tif strings.EqualFold(\"TRUE\", val) || strings.EqualFold(\"FALSE\", val) {\n\t\treturn newBoolFormulaArg(true)\n\t}\n\treturn newBoolFormulaArg(false)\n}\n\n// ISNA function tests if an initial supplied expression (or value) returns\n// the Excel #N/A Error, and if so, returns TRUE; Otherwise the function\n// returns FALSE. The syntax of the function is:\n//\n//\tISNA(value)\nfunc (fn *formulaFuncs) ISNA(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISNA requires 1 argument\")\n\t}\n\tif token := argsList.Front().Value.(formulaArg); token.Type == ArgError && token.String == formulaErrorNA {\n\t\treturn newBoolFormulaArg(true)\n\t}\n\treturn newBoolFormulaArg(false)\n}\n\n// ISNONTEXT function tests if a supplied value is text. If not, the\n// function returns TRUE; If the supplied value is text, the function returns\n// FALSE. The syntax of the function is:\n//\n//\tISNONTEXT(value)\nfunc (fn *formulaFuncs) ISNONTEXT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISNONTEXT requires 1 argument\")\n\t}\n\tif argsList.Front().Value.(formulaArg).Type == ArgString {\n\t\treturn newBoolFormulaArg(false)\n\t}\n\treturn newBoolFormulaArg(true)\n}\n\n// ISNUMBER function tests if a supplied value is a number. If so,\n// the function returns TRUE; Otherwise it returns FALSE. The syntax of the\n// function is:\n//\n//\tISNUMBER(value)\nfunc (fn *formulaFuncs) ISNUMBER(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISNUMBER requires 1 argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg)\n\targ = fn.implicitIntersect(arg)\n\tif arg.Type == ArgNumber {\n\t\treturn newBoolFormulaArg(true)\n\t}\n\treturn newBoolFormulaArg(false)\n}\n\n// ISODD function tests if a supplied number (or numeric expression) evaluates\n// to an odd number, and if so, returns TRUE; Otherwise, the function returns\n// FALSE. The syntax of the function is:\n//\n//\tISODD(value)\nfunc (fn *formulaFuncs) ISODD(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISODD requires 1 argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg).ToNumber()\n\tif arg.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif int(arg.Number) != int(arg.Number)/2*2 {\n\t\treturn newBoolFormulaArg(true)\n\t}\n\treturn newBoolFormulaArg(false)\n}\n\n// ISREF function tests if a supplied value is a reference. If so, the\n// function returns TRUE; Otherwise it returns FALSE. The syntax of the\n// function is:\n//\n//\tISREF(value)\nfunc (fn *formulaFuncs) ISREF(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISREF requires 1 argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg)\n\tif arg.cellRanges != nil && arg.cellRanges.Len() > 0 || arg.cellRefs != nil && arg.cellRefs.Len() > 0 {\n\t\treturn newBoolFormulaArg(true)\n\t}\n\treturn newBoolFormulaArg(false)\n}\n\n// ISTEXT function tests if a supplied value is text, and if so, returns TRUE;\n// Otherwise, the function returns FALSE. The syntax of the function is:\n//\n//\tISTEXT(value)\nfunc (fn *formulaFuncs) ISTEXT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISTEXT requires 1 argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tif token.ToNumber().Type != ArgError {\n\t\treturn newBoolFormulaArg(false)\n\t}\n\treturn newBoolFormulaArg(token.Type == ArgString)\n}\n\n// N function converts data into a numeric value. The syntax of the function\n// is:\n//\n//\tN(value)\nfunc (fn *formulaFuncs) N(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"N requires 1 argument\")\n\t}\n\ttoken, num := argsList.Front().Value.(formulaArg), 0.0\n\tif token.Type == ArgError {\n\t\treturn token\n\t}\n\tif arg := token.ToNumber(); arg.Type == ArgNumber {\n\t\tnum = arg.Number\n\t}\n\tif token.Value() == \"TRUE\" {\n\t\tnum = 1\n\t}\n\treturn newNumberFormulaArg(num)\n}\n\n// NA function returns the Excel #N/A error. This error message has the\n// meaning 'value not available' and is produced when an Excel Formula is\n// unable to find a value that it needs. The syntax of the function is:\n//\n//\tNA()\nfunc (fn *formulaFuncs) NA(argsList *list.List) formulaArg {\n\tif argsList.Len() != 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NA accepts no arguments\")\n\t}\n\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n}\n\n// SHEET function returns the Sheet number for a specified reference. The\n// syntax of the function is:\n//\n//\tSHEET([value])\nfunc (fn *formulaFuncs) SHEET(argsList *list.List) formulaArg {\n\tif argsList.Len() > 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SHEET accepts at most 1 argument\")\n\t}\n\tif argsList.Len() == 0 {\n\t\tidx, _ := fn.f.GetSheetIndex(fn.sheet)\n\t\treturn newNumberFormulaArg(float64(idx + 1))\n\t}\n\targ := argsList.Front().Value.(formulaArg)\n\tif sheetIdx, _ := fn.f.GetSheetIndex(arg.Value()); sheetIdx != -1 {\n\t\treturn newNumberFormulaArg(float64(sheetIdx + 1))\n\t}\n\tif arg.cellRanges != nil && arg.cellRanges.Len() > 0 {\n\t\tif sheetIdx, _ := fn.f.GetSheetIndex(arg.cellRanges.Front().Value.(cellRange).From.Sheet); sheetIdx != -1 {\n\t\t\treturn newNumberFormulaArg(float64(sheetIdx + 1))\n\t\t}\n\t}\n\tif arg.cellRefs != nil && arg.cellRefs.Len() > 0 {\n\t\tif sheetIdx, _ := fn.f.GetSheetIndex(arg.cellRefs.Front().Value.(cellRef).Sheet); sheetIdx != -1 {\n\t\t\treturn newNumberFormulaArg(float64(sheetIdx + 1))\n\t\t}\n\t}\n\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n}\n\n// SHEETS function returns the number of sheets in a supplied reference. The\n// result includes sheets that are Visible, Hidden or Very Hidden. The syntax\n// of the function is:\n//\n//\tSHEETS([reference])\nfunc (fn *formulaFuncs) SHEETS(argsList *list.List) formulaArg {\n\tif argsList.Len() > 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SHEETS accepts at most 1 argument\")\n\t}\n\tif argsList.Len() == 0 {\n\t\treturn newNumberFormulaArg(float64(len(fn.f.GetSheetList())))\n\t}\n\targ := argsList.Front().Value.(formulaArg)\n\tsheetMap := map[string]struct{}{}\n\tif arg.cellRanges != nil && arg.cellRanges.Len() > 0 {\n\t\tfor rng := arg.cellRanges.Front(); rng != nil; rng = rng.Next() {\n\t\t\tsheetMap[rng.Value.(cellRange).From.Sheet] = struct{}{}\n\t\t}\n\t}\n\tif arg.cellRefs != nil && arg.cellRefs.Len() > 0 {\n\t\tfor ref := arg.cellRefs.Front(); ref != nil; ref = ref.Next() {\n\t\t\tsheetMap[ref.Value.(cellRef).Sheet] = struct{}{}\n\t\t}\n\t}\n\tif len(sheetMap) > 0 {\n\t\treturn newNumberFormulaArg(float64(len(sheetMap)))\n\t}\n\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n}\n\n// TYPE function returns an integer that represents the value's data type. The\n// syntax of the function is:\n//\n//\tTYPE(value)\nfunc (fn *formulaFuncs) TYPE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TYPE requires 1 argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tswitch token.Type {\n\tcase ArgError:\n\t\treturn newNumberFormulaArg(16)\n\tcase ArgMatrix:\n\t\treturn newNumberFormulaArg(64)\n\tcase ArgNumber, ArgEmpty:\n\t\tif token.Boolean {\n\t\t\treturn newNumberFormulaArg(4)\n\t\t}\n\t\treturn newNumberFormulaArg(1)\n\tdefault:\n\t\treturn newNumberFormulaArg(2)\n\t}\n}\n\n// T function tests if a supplied value is text and if so, returns the\n// supplied text; Otherwise, the function returns an empty text string. The\n// syntax of the function is:\n//\n//\tT(value)\nfunc (fn *formulaFuncs) T(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"T requires 1 argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tif token.Type == ArgError {\n\t\treturn token\n\t}\n\tif token.Type == ArgNumber {\n\t\treturn newStringFormulaArg(\"\")\n\t}\n\treturn newStringFormulaArg(token.Value())\n}\n\n// Logical Functions\n\n// AND function tests a number of supplied conditions and returns TRUE or\n// FALSE. The syntax of the function is:\n//\n//\tAND(logical_test1,[logical_test2],...)\nfunc (fn *formulaFuncs) AND(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"AND requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 30 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"AND accepts at most 30 arguments\")\n\t}\n\tand := true\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tswitch token.Type {\n\t\tcase ArgUnknown:\n\t\t\tcontinue\n\t\tcase ArgString:\n\t\t\tif token.String == \"TRUE\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif token.String == \"FALSE\" {\n\t\t\t\treturn newStringFormulaArg(token.String)\n\t\t\t}\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\tcase ArgNumber:\n\t\t\tand = and && token.Number != 0\n\t\tcase ArgMatrix:\n\t\t\t// TODO\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t}\n\treturn newBoolFormulaArg(and)\n}\n\n// FALSE function returns the logical value FALSE. The syntax of the\n// function is:\n//\n//\tFALSE()\nfunc (fn *formulaFuncs) FALSE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FALSE takes no arguments\")\n\t}\n\treturn newBoolFormulaArg(false)\n}\n\n// IFERROR function receives two values (or expressions) and tests if the\n// first of these evaluates to an error. The syntax of the function is:\n//\n//\tIFERROR(value,value_if_error)\nfunc (fn *formulaFuncs) IFERROR(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IFERROR requires 2 arguments\")\n\t}\n\tvalue := argsList.Front().Value.(formulaArg)\n\tif value.Type != ArgError {\n\t\tif value.Type == ArgEmpty {\n\t\t\treturn newNumberFormulaArg(0)\n\t\t}\n\t\treturn value\n\t}\n\treturn argsList.Back().Value.(formulaArg)\n}\n\n// IFNA function tests if an initial supplied value (or expression) evaluates\n// to the Excel #N/A error. If so, the function returns a second supplied\n// value; Otherwise the function returns the first supplied value. The syntax\n// of the function is:\n//\n//\tIFNA(value,value_if_na)\nfunc (fn *formulaFuncs) IFNA(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IFNA requires 2 arguments\")\n\t}\n\targ := argsList.Front().Value.(formulaArg)\n\tif arg.Type == ArgError && arg.String == formulaErrorNA {\n\t\treturn argsList.Back().Value.(formulaArg)\n\t}\n\treturn arg\n}\n\n// IFS function tests a number of supplied conditions and returns the result\n// corresponding to the first condition that evaluates to TRUE. If none of\n// the supplied conditions evaluate to TRUE, the function returns the #N/A\n// error.\n//\n//\tIFS(logical_test1,value_if_true1,[logical_test2,value_if_true2],...)\nfunc (fn *formulaFuncs) IFS(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IFS requires at least 2 arguments\")\n\t}\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\tif arg.Value.(formulaArg).ToBool().Number == 1 {\n\t\t\treturn arg.Next().Value.(formulaArg)\n\t\t}\n\t\targ = arg.Next()\n\t}\n\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n}\n\n// NOT function returns the opposite to a supplied logical value. The syntax\n// of the function is:\n//\n//\tNOT(logical)\nfunc (fn *formulaFuncs) NOT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NOT requires 1 argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tswitch token.Type {\n\tcase ArgString, ArgList:\n\t\tif strings.ToUpper(token.String) == \"TRUE\" {\n\t\t\treturn newBoolFormulaArg(false)\n\t\t}\n\t\tif strings.ToUpper(token.String) == \"FALSE\" {\n\t\t\treturn newBoolFormulaArg(true)\n\t\t}\n\tcase ArgNumber:\n\t\treturn newBoolFormulaArg(!(token.Number != 0))\n\tcase ArgError:\n\t\treturn token\n\t}\n\treturn newErrorFormulaArg(formulaErrorVALUE, \"NOT expects 1 boolean or numeric argument\")\n}\n\n// OR function tests a number of supplied conditions and returns either TRUE\n// or FALSE. The syntax of the function is:\n//\n//\tOR(logical_test1,[logical_test2],...)\nfunc (fn *formulaFuncs) OR(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"OR requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 30 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"OR accepts at most 30 arguments\")\n\t}\n\tvar or bool\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tswitch token.Type {\n\t\tcase ArgUnknown:\n\t\t\tcontinue\n\t\tcase ArgString:\n\t\t\tif token.String == \"FALSE\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif token.String == \"TRUE\" {\n\t\t\t\tor = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\tcase ArgNumber:\n\t\t\tif or = token.Number != 0; or {\n\t\t\t\treturn newStringFormulaArg(strings.ToUpper(strconv.FormatBool(or)))\n\t\t\t}\n\t\tcase ArgMatrix:\n\t\t\targs := list.New()\n\t\t\tfor _, arg := range token.ToList() {\n\t\t\t\targs.PushBack(arg)\n\t\t\t}\n\t\t\treturn fn.OR(args)\n\t\t}\n\t}\n\treturn newBoolFormulaArg(or)\n}\n\n// SWITCH function compares a number of supplied values to a supplied test\n// expression and returns a result corresponding to the first value that\n// matches the test expression. A default value can be supplied, to be\n// returned if none of the supplied values match the test expression. The\n// syntax of the function is:\n//\n//\tSWITCH(expression,value1,result1,[value2,result2],[value3,result3],...,[default])\nfunc (fn *formulaFuncs) SWITCH(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SWITCH requires at least 3 arguments\")\n\t}\n\ttarget := argsList.Front().Value.(formulaArg)\n\targCount := argsList.Len() - 1\n\tswitchCount := int(math.Floor(float64(argCount) / 2))\n\thasDefaultClause := argCount%2 != 0\n\tresult := newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\tif hasDefaultClause {\n\t\tresult = argsList.Back().Value.(formulaArg)\n\t}\n\tif switchCount > 0 {\n\t\targ := argsList.Front()\n\t\tfor i := 0; i < switchCount; i++ {\n\t\t\targ = arg.Next()\n\t\t\tif target.Value() == arg.Value.(formulaArg).Value() {\n\t\t\t\tresult = arg.Next().Value.(formulaArg)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\targ = arg.Next()\n\t\t}\n\t}\n\treturn result\n}\n\n// TRUE function returns the logical value TRUE. The syntax of the function\n// is:\n//\n//\tTRUE()\nfunc (fn *formulaFuncs) TRUE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TRUE takes no arguments\")\n\t}\n\treturn newBoolFormulaArg(true)\n}\n\n// calcXor checking if numeric cell exists and count it by given arguments\n// sequence for the formula function XOR.\nfunc calcXor(argsList *list.List) formulaArg {\n\tcount, ok := 0, false\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\ttoken := arg.Value.(formulaArg)\n\t\tswitch token.Type {\n\t\tcase ArgError:\n\t\t\treturn token\n\t\tcase ArgNumber:\n\t\t\tok = true\n\t\t\tif token.Number != 0 {\n\t\t\t\tcount++\n\t\t\t}\n\t\tcase ArgMatrix:\n\t\t\tfor _, value := range token.ToList() {\n\t\t\t\tif num := value.ToNumber(); num.Type == ArgNumber {\n\t\t\t\t\tok = true\n\t\t\t\t\tif num.Number != 0 {\n\t\t\t\t\t\tcount++\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif !ok {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\treturn newBoolFormulaArg(count%2 != 0)\n}\n\n// XOR function returns the Exclusive Or logical operation for one or more\n// supplied conditions. I.e. the Xor function returns TRUE if an odd number\n// of the supplied conditions evaluate to TRUE, and FALSE otherwise. The\n// syntax of the function is:\n//\n//\tXOR(logical_test1,[logical_test2],...)\nfunc (fn *formulaFuncs) XOR(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"XOR requires at least 1 argument\")\n\t}\n\treturn calcXor(argsList)\n}\n\n// Date and Time Functions\n\n// DATE returns a date, from a user-supplied year, month and day. The syntax\n// of the function is:\n//\n//\tDATE(year,month,day)\nfunc (fn *formulaFuncs) DATE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DATE requires 3 number arguments\")\n\t}\n\tyear := argsList.Front().Value.(formulaArg).ToNumber()\n\tmonth := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tday := argsList.Back().Value.(formulaArg).ToNumber()\n\tif year.Type != ArgNumber || month.Type != ArgNumber || day.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DATE requires 3 number arguments\")\n\t}\n\td := makeDate(int(year.Number), time.Month(month.Number), int(day.Number))\n\treturn newNumberFormulaArg(daysBetween(excelMinTime1900.Unix(), d) + 1)\n}\n\n// calcDateDif is an implementation of the formula function DATEDIF,\n// calculation difference between two dates.\nfunc calcDateDif(unit string, diff float64, seq []int, startArg, endArg formulaArg) float64 {\n\tey, sy, em, sm, ed, sd := seq[0], seq[1], seq[2], seq[3], seq[4], seq[5]\n\tswitch unit {\n\tcase \"d\":\n\t\tdiff = endArg.Number - startArg.Number\n\tcase \"md\":\n\t\tsmMD := em\n\t\tif ed < sd {\n\t\t\tsmMD--\n\t\t}\n\t\tdiff = endArg.Number - daysBetween(excelMinTime1900.Unix(), makeDate(ey, time.Month(smMD), sd)) - 1\n\tcase \"ym\":\n\t\tdiff = float64(em - sm)\n\t\tif ed < sd {\n\t\t\tdiff--\n\t\t}\n\t\tif diff < 0 {\n\t\t\tdiff += 12\n\t\t}\n\tcase \"yd\":\n\t\tsyYD := sy\n\t\tif em < sm || (em == sm && ed < sd) {\n\t\t\tsyYD++\n\t\t}\n\t\ts := daysBetween(excelMinTime1900.Unix(), makeDate(syYD, time.Month(em), ed))\n\t\te := daysBetween(excelMinTime1900.Unix(), makeDate(sy, time.Month(sm), sd))\n\t\tdiff = s - e\n\t}\n\treturn diff\n}\n\n// DATEDIF function calculates the number of days, months, or years between\n// two dates. The syntax of the function is:\n//\n//\tDATEDIF(start_date,end_date,unit)\nfunc (fn *formulaFuncs) DATEDIF(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DATEDIF requires 3 number arguments\")\n\t}\n\tstartArg, endArg := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif startArg.Type != ArgNumber || endArg.Type != ArgNumber {\n\t\treturn startArg\n\t}\n\tif startArg.Number > endArg.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"start_date > end_date\")\n\t}\n\tif startArg.Number == endArg.Number {\n\t\treturn newNumberFormulaArg(0)\n\t}\n\tunit := strings.ToLower(argsList.Back().Value.(formulaArg).Value())\n\tstartDate, endDate := timeFromExcelTime(startArg.Number, false), timeFromExcelTime(endArg.Number, false)\n\tsy, smm, sd := startDate.Date()\n\tey, emm, ed := endDate.Date()\n\tsm, em, diff := int(smm), int(emm), 0.0\n\tswitch unit {\n\tcase \"y\":\n\t\tdiff = float64(ey - sy)\n\t\tif em < sm || (em == sm && ed < sd) {\n\t\t\tdiff--\n\t\t}\n\tcase \"m\":\n\t\tyDiff := ey - sy\n\t\tmDiff := em - sm\n\t\tif ed < sd {\n\t\t\tmDiff--\n\t\t}\n\t\tif mDiff < 0 {\n\t\t\tyDiff--\n\t\t\tmDiff += 12\n\t\t}\n\t\tdiff = float64(yDiff*12 + mDiff)\n\tcase \"d\", \"md\", \"ym\", \"yd\":\n\t\tdiff = calcDateDif(unit, diff, []int{ey, sy, em, sm, ed, sd}, startArg, endArg)\n\tdefault:\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DATEDIF has invalid unit\")\n\t}\n\treturn newNumberFormulaArg(diff)\n}\n\n// isDateOnlyFmt check if the given string matches date-only format regular expressions.\nfunc isDateOnlyFmt(dateString string) bool {\n\tfor _, df := range dateOnlyFormats {\n\t\tsubMatch := df.FindStringSubmatch(dateString)\n\t\tif len(subMatch) > 1 {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// isTimeOnlyFmt check if the given string matches time-only format regular expressions.\nfunc isTimeOnlyFmt(timeString string) bool {\n\tfor _, tf := range timeFormats {\n\t\tsubMatch := tf.FindStringSubmatch(timeString)\n\t\tif len(subMatch) > 1 {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// strToTimePatternHandler1 parse and convert the given string in pattern\n// hh to the time.\nfunc strToTimePatternHandler1(subMatch []string) (h, m int, s float64, err error) {\n\th, err = strconv.Atoi(subMatch[0])\n\treturn\n}\n\n// strToTimePatternHandler2 parse and convert the given string in pattern\n// hh:mm to the time.\nfunc strToTimePatternHandler2(subMatch []string) (h, m int, s float64, err error) {\n\tif h, err = strconv.Atoi(subMatch[0]); err != nil {\n\t\treturn\n\t}\n\tm, err = strconv.Atoi(subMatch[2])\n\treturn\n}\n\n// strToTimePatternHandler3 parse and convert the given string in pattern\n// mm:ss to the time.\nfunc strToTimePatternHandler3(subMatch []string) (h, m int, s float64, err error) {\n\tif m, err = strconv.Atoi(subMatch[0]); err != nil {\n\t\treturn\n\t}\n\ts, err = strconv.ParseFloat(subMatch[2], 64)\n\treturn\n}\n\n// strToTimePatternHandler4 parse and convert the given string in pattern\n// hh:mm:ss to the time.\nfunc strToTimePatternHandler4(subMatch []string) (h, m int, s float64, err error) {\n\tif h, err = strconv.Atoi(subMatch[0]); err != nil {\n\t\treturn\n\t}\n\tif m, err = strconv.Atoi(subMatch[2]); err != nil {\n\t\treturn\n\t}\n\ts, err = strconv.ParseFloat(subMatch[4], 64)\n\treturn\n}\n\n// strToTime parse and convert the given string to the time.\nfunc strToTime(str string) (int, int, float64, bool, bool, formulaArg) {\n\tvar subMatch []string\n\tpattern := \"\"\n\tfor key, tf := range timeFormats {\n\t\tsubMatch = tf.FindStringSubmatch(str)\n\t\tif len(subMatch) > 1 {\n\t\t\tpattern = key\n\t\t\tbreak\n\t\t}\n\t}\n\tif pattern == \"\" {\n\t\treturn 0, 0, 0, false, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tdateIsEmpty := subMatch[1] == \"\"\n\tsubMatch = subMatch[49:]\n\tvar (\n\t\tl              = len(subMatch)\n\t\tlast           = subMatch[l-1]\n\t\tam             = last == \"am\"\n\t\tpm             = last == \"pm\"\n\t\thours, minutes int\n\t\tseconds        float64\n\t\terr            error\n\t)\n\tif handler, ok := map[string]func(match []string) (int, int, float64, error){\n\t\t\"hh\":       strToTimePatternHandler1,\n\t\t\"hh:mm\":    strToTimePatternHandler2,\n\t\t\"mm:ss\":    strToTimePatternHandler3,\n\t\t\"hh:mm:ss\": strToTimePatternHandler4,\n\t}[pattern]; ok {\n\t\tif hours, minutes, seconds, err = handler(subMatch); err != nil {\n\t\t\treturn 0, 0, 0, false, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t}\n\tif minutes >= 60 {\n\t\treturn 0, 0, 0, false, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif am || pm {\n\t\tif hours > 12 || seconds >= 60 {\n\t\t\treturn 0, 0, 0, false, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t} else if hours == 12 {\n\t\t\thours = 0\n\t\t}\n\t} else if hours >= 24 || seconds >= 10000 {\n\t\treturn 0, 0, 0, false, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\treturn hours, minutes, seconds, pm, dateIsEmpty, newEmptyFormulaArg()\n}\n\n// strToDatePatternHandler1 parse and convert the given string in pattern\n// mm/dd/yy to the date.\nfunc strToDatePatternHandler1(subMatch []string) (int, int, int, bool, error) {\n\tvar year, month, day int\n\tvar err error\n\tif month, err = strconv.Atoi(subMatch[1]); err != nil {\n\t\treturn 0, 0, 0, false, err\n\t}\n\tif day, err = strconv.Atoi(subMatch[3]); err != nil {\n\t\treturn 0, 0, 0, false, err\n\t}\n\tif year, err = strconv.Atoi(subMatch[5]); err != nil {\n\t\treturn 0, 0, 0, false, err\n\t}\n\tif year < 0 || year > 9999 || (year > 99 && year < 1900) {\n\t\treturn 0, 0, 0, false, ErrParameterInvalid\n\t}\n\treturn formatYear(year), month, day, subMatch[8] == \"\", err\n}\n\n// strToDatePatternHandler2 parse and convert the given string in pattern mm\n// dd, yy to the date.\nfunc strToDatePatternHandler2(subMatch []string) (int, int, int, bool, error) {\n\tvar year, month, day int\n\tvar err error\n\tmonth = month2num[subMatch[1]]\n\tif day, err = strconv.Atoi(subMatch[14]); err != nil {\n\t\treturn 0, 0, 0, false, err\n\t}\n\tif year, err = strconv.Atoi(subMatch[16]); err != nil {\n\t\treturn 0, 0, 0, false, err\n\t}\n\tif year < 0 || year > 9999 || (year > 99 && year < 1900) {\n\t\treturn 0, 0, 0, false, ErrParameterInvalid\n\t}\n\treturn formatYear(year), month, day, subMatch[19] == \"\", err\n}\n\n// strToDatePatternHandler3 parse and convert the given string in pattern\n// yy-mm-dd to the date.\nfunc strToDatePatternHandler3(subMatch []string) (int, int, int, bool, error) {\n\tvar year, month, day int\n\tv1, err := strconv.Atoi(subMatch[1])\n\tif err != nil {\n\t\treturn 0, 0, 0, false, err\n\t}\n\tv2, err := strconv.Atoi(subMatch[3])\n\tif err != nil {\n\t\treturn 0, 0, 0, false, err\n\t}\n\tv3, err := strconv.Atoi(subMatch[5])\n\tif err != nil {\n\t\treturn 0, 0, 0, false, err\n\t}\n\tif v1 >= 1900 && v1 < 10000 {\n\t\tyear = v1\n\t\tmonth = v2\n\t\tday = v3\n\t} else if v1 > 0 && v1 < 13 {\n\t\tmonth = v1\n\t\tday = v2\n\t\tyear = v3\n\t} else {\n\t\treturn 0, 0, 0, false, ErrParameterInvalid\n\t}\n\treturn year, month, day, subMatch[8] == \"\", err\n}\n\n// strToDatePatternHandler4 parse and convert the given string in pattern\n// yy-mmStr-dd, yy to the date.\nfunc strToDatePatternHandler4(subMatch []string) (int, int, int, bool, error) {\n\tvar year, month, day int\n\tvar err error\n\tif year, err = strconv.Atoi(subMatch[16]); err != nil {\n\t\treturn 0, 0, 0, false, err\n\t}\n\tmonth = month2num[subMatch[3]]\n\tif day, err = strconv.Atoi(subMatch[1]); err != nil {\n\t\treturn 0, 0, 0, false, err\n\t}\n\treturn formatYear(year), month, day, subMatch[19] == \"\", err\n}\n\n// strToDate parse and convert the given string to the date.\nfunc strToDate(str string) (int, int, int, bool, formulaArg) {\n\tvar subMatch []string\n\tpattern := \"\"\n\tfor key, df := range dateFormats {\n\t\tsubMatch = df.FindStringSubmatch(str)\n\t\tif len(subMatch) > 1 {\n\t\t\tpattern = key\n\t\t\tbreak\n\t\t}\n\t}\n\tif pattern == \"\" {\n\t\treturn 0, 0, 0, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tvar (\n\t\ttimeIsEmpty      bool\n\t\tyear, month, day int\n\t\terr              error\n\t)\n\tif handler, ok := map[string]func(match []string) (int, int, int, bool, error){\n\t\t\"mm/dd/yy\":    strToDatePatternHandler1,\n\t\t\"mm dd, yy\":   strToDatePatternHandler2,\n\t\t\"yy-mm-dd\":    strToDatePatternHandler3,\n\t\t\"yy-mmStr-dd\": strToDatePatternHandler4,\n\t}[pattern]; ok {\n\t\tif year, month, day, timeIsEmpty, err = handler(subMatch); err != nil {\n\t\t\treturn 0, 0, 0, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t}\n\tif !validateDate(year, month, day) {\n\t\treturn 0, 0, 0, false, newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\treturn year, month, day, timeIsEmpty, newEmptyFormulaArg()\n}\n\n// DATEVALUE function converts a text representation of a date into an Excel\n// date. For example, the function converts a text string representing a\n// date, into the serial number that represents the date in Excels' date-time\n// code. The syntax of the function is:\n//\n//\tDATEVALUE(date_text)\nfunc (fn *formulaFuncs) DATEVALUE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DATEVALUE requires 1 argument\")\n\t}\n\tdateText := argsList.Front().Value.(formulaArg).Value()\n\tif !isDateOnlyFmt(dateText) {\n\t\tif _, _, _, _, _, err := strToTime(dateText); err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t}\n\ty, m, d, _, err := strToDate(dateText)\n\tif err.Type == ArgError {\n\t\treturn err\n\t}\n\treturn newNumberFormulaArg(daysBetween(excelMinTime1900.Unix(), makeDate(y, time.Month(m), d)) + 1)\n}\n\n// DAY function returns the day of a date, represented by a serial number. The\n// day is given as an integer ranging from 1 to 31. The syntax of the\n// function is:\n//\n//\tDAY(serial_number)\nfunc (fn *formulaFuncs) DAY(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DAY requires exactly 1 argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg)\n\tnum := arg.ToNumber()\n\tif num.Type != ArgNumber {\n\t\tdateString := strings.ToLower(arg.Value())\n\t\tif !isDateOnlyFmt(dateString) {\n\t\t\tif _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\t_, _, day, _, err := strToDate(dateString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t\treturn newNumberFormulaArg(float64(day))\n\t}\n\tif num.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"DAY only accepts positive argument\")\n\t}\n\tif num.Number <= 60 {\n\t\treturn newNumberFormulaArg(math.Mod(num.Number, 31.0))\n\t}\n\treturn newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Day()))\n}\n\n// DAYS function returns the number of days between two supplied dates. The\n// syntax of the function is:\n//\n//\tDAYS(end_date,start_date)\nfunc (fn *formulaFuncs) DAYS(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DAYS requires 2 arguments\")\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tend, start := args.List[0], args.List[1]\n\treturn newNumberFormulaArg(end.Number - start.Number)\n}\n\n// DAYS360 function returns the number of days between 2 dates, based on a\n// 360-day year (12 x 30 months). The syntax of the function is:\n//\n//\tDAYS360(start_date,end_date,[method])\nfunc (fn *formulaFuncs) DAYS360(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DAYS360 requires at least 2 arguments\")\n\t}\n\tif argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DAYS360 requires at most 3 arguments\")\n\t}\n\tstartDate := toExcelDateArg(argsList.Front().Value.(formulaArg))\n\tif startDate.Type != ArgNumber {\n\t\treturn startDate\n\t}\n\tendDate := toExcelDateArg(argsList.Front().Next().Value.(formulaArg))\n\tif endDate.Type != ArgNumber {\n\t\treturn endDate\n\t}\n\tstart, end := timeFromExcelTime(startDate.Number, false), timeFromExcelTime(endDate.Number, false)\n\tsy, sm, sd, ey, em, ed := start.Year(), int(start.Month()), start.Day(), end.Year(), int(end.Month()), end.Day()\n\tmethod := newBoolFormulaArg(false)\n\tif argsList.Len() > 2 {\n\t\tif method = argsList.Back().Value.(formulaArg).ToBool(); method.Type != ArgNumber {\n\t\t\treturn method\n\t\t}\n\t}\n\tif method.Number == 1 {\n\t\tif sd == 31 {\n\t\t\tsd--\n\t\t}\n\t\tif ed == 31 {\n\t\t\ted--\n\t\t}\n\t} else {\n\t\tif getDaysInMonth(sy, sm) == sd {\n\t\t\tsd = 30\n\t\t}\n\t\tif ed > 30 {\n\t\t\tif sd < 30 {\n\t\t\t\tem++\n\t\t\t\ted = 1\n\t\t\t} else {\n\t\t\t\ted = 30\n\t\t\t}\n\t\t}\n\t}\n\treturn newNumberFormulaArg(float64(360*(ey-sy) + 30*(em-sm) + (ed - sd)))\n}\n\n// ISOWEEKNUM function returns the ISO week number of a supplied date. The\n// syntax of the function is:\n//\n//\tISOWEEKNUM(date)\nfunc (fn *formulaFuncs) ISOWEEKNUM(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISOWEEKNUM requires 1 argument\")\n\t}\n\tdate := argsList.Front().Value.(formulaArg)\n\tnum := date.ToNumber()\n\tweekNum := 0\n\tif num.Type != ArgNumber {\n\t\tdateString := strings.ToLower(date.Value())\n\t\tif !isDateOnlyFmt(dateString) {\n\t\t\tif _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\ty, m, d, _, err := strToDate(dateString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t\t_, weekNum = time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC).ISOWeek()\n\t} else {\n\t\tif num.Number < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t\t_, weekNum = timeFromExcelTime(num.Number, false).ISOWeek()\n\t}\n\treturn newNumberFormulaArg(float64(weekNum))\n}\n\n// EDATE function returns a date that is a specified number of months before or\n// after a supplied start date. The syntax of function is:\n//\n//\tEDATE(start_date,months)\nfunc (fn *formulaFuncs) EDATE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"EDATE requires 2 arguments\")\n\t}\n\tdate := argsList.Front().Value.(formulaArg)\n\tnum := date.ToNumber()\n\tvar dateTime time.Time\n\tif num.Type != ArgNumber {\n\t\tdateString := strings.ToLower(date.Value())\n\t\tif !isDateOnlyFmt(dateString) {\n\t\t\tif _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\ty, m, d, _, err := strToDate(dateString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t\tdateTime = time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.Now().Location())\n\t} else {\n\t\tif num.Number < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t\tdateTime = timeFromExcelTime(num.Number, false)\n\t}\n\tmonth := argsList.Back().Value.(formulaArg).ToNumber()\n\tif month.Type != ArgNumber {\n\t\treturn month\n\t}\n\ty, d := dateTime.Year(), dateTime.Day()\n\tm := int(dateTime.Month()) + int(month.Number)\n\tif month.Number < 0 {\n\t\ty -= int(math.Ceil(-1 * float64(m) / 12))\n\t}\n\tif month.Number > 11 {\n\t\ty += int(math.Floor(float64(m) / 12))\n\t}\n\tif m = m % 12; m < 0 {\n\t\tm += 12\n\t}\n\tif d > 28 {\n\t\tif days := getDaysInMonth(y, m); d > days {\n\t\t\td = days\n\t\t}\n\t}\n\tresult, _ := timeToExcelTime(time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC), false)\n\treturn newNumberFormulaArg(result)\n}\n\n// EOMONTH function returns the last day of the month, that is a specified\n// number of months before or after an initial supplied start date. The syntax\n// of the function is:\n//\n//\tEOMONTH(start_date,months)\nfunc (fn *formulaFuncs) EOMONTH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"EOMONTH requires 2 arguments\")\n\t}\n\tdate := argsList.Front().Value.(formulaArg)\n\tnum := date.ToNumber()\n\tvar dateTime time.Time\n\tif num.Type != ArgNumber {\n\t\tdateString := strings.ToLower(date.Value())\n\t\tif !isDateOnlyFmt(dateString) {\n\t\t\tif _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\ty, m, d, _, err := strToDate(dateString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t\tdateTime = time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.Now().Location())\n\t} else {\n\t\tif num.Number < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t\tdateTime = timeFromExcelTime(num.Number, false)\n\t}\n\tmonths := argsList.Back().Value.(formulaArg).ToNumber()\n\tif months.Type != ArgNumber {\n\t\treturn months\n\t}\n\ty, m := dateTime.Year(), int(dateTime.Month())+int(months.Number)-1\n\tif m < 0 {\n\t\ty -= int(math.Ceil(-1 * float64(m) / 12))\n\t}\n\tif m > 11 {\n\t\ty += int(math.Floor(float64(m) / 12))\n\t}\n\tif m = m % 12; m < 0 {\n\t\tm += 12\n\t}\n\tresult, _ := timeToExcelTime(time.Date(y, time.Month(m+1), getDaysInMonth(y, m+1), 0, 0, 0, 0, time.UTC), false)\n\treturn newNumberFormulaArg(result)\n}\n\n// HOUR function returns an integer representing the hour component of a\n// supplied Excel time. The syntax of the function is:\n//\n//\tHOUR(serial_number)\nfunc (fn *formulaFuncs) HOUR(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"HOUR requires exactly 1 argument\")\n\t}\n\tdate := argsList.Front().Value.(formulaArg)\n\tnum := date.ToNumber()\n\tif num.Type != ArgNumber {\n\t\ttimeString := strings.ToLower(date.Value())\n\t\tif !isTimeOnlyFmt(timeString) {\n\t\t\t_, _, _, _, err := strToDate(timeString)\n\t\t\tif err.Type == ArgError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\th, _, _, pm, _, err := strToTime(timeString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t\tif pm {\n\t\t\th += 12\n\t\t}\n\t\treturn newNumberFormulaArg(float64(h))\n\t}\n\tif num.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"HOUR only accepts positive argument\")\n\t}\n\treturn newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Hour()))\n}\n\n// MINUTE function returns an integer representing the minute component of a\n// supplied Excel time. The syntax of the function is:\n//\n//\tMINUTE(serial_number)\nfunc (fn *formulaFuncs) MINUTE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MINUTE requires exactly 1 argument\")\n\t}\n\tdate := argsList.Front().Value.(formulaArg)\n\tnum := date.ToNumber()\n\tif num.Type != ArgNumber {\n\t\ttimeString := strings.ToLower(date.Value())\n\t\tif !isTimeOnlyFmt(timeString) {\n\t\t\t_, _, _, _, err := strToDate(timeString)\n\t\t\tif err.Type == ArgError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\t_, m, _, _, _, err := strToTime(timeString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t\treturn newNumberFormulaArg(float64(m))\n\t}\n\tif num.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"MINUTE only accepts positive argument\")\n\t}\n\treturn newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Minute()))\n}\n\n// MONTH function returns the month of a date represented by a serial number.\n// The month is given as an integer, ranging from 1 (January) to 12\n// (December). The syntax of the function is:\n//\n//\tMONTH(serial_number)\nfunc (fn *formulaFuncs) MONTH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MONTH requires exactly 1 argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg)\n\tnum := arg.ToNumber()\n\tif num.Type != ArgNumber {\n\t\tdateString := strings.ToLower(arg.Value())\n\t\tif !isDateOnlyFmt(dateString) {\n\t\t\tif _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\t_, month, _, _, err := strToDate(dateString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t\treturn newNumberFormulaArg(float64(month))\n\t}\n\tif num.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"MONTH only accepts positive argument\")\n\t}\n\treturn newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Month()))\n}\n\n// genWeekendMask generate weekend mask of a series of seven 0's and 1's which\n// represent the seven weekdays, starting from Monday.\nfunc genWeekendMask(weekend int) []byte {\n\tif masks, ok := map[int][]int{\n\t\t1: {5, 6}, 2: {6, 0}, 3: {0, 1}, 4: {1, 2}, 5: {2, 3}, 6: {3, 4}, 7: {4, 5},\n\t\t11: {6}, 12: {0}, 13: {1}, 14: {2}, 15: {3}, 16: {4}, 17: {5},\n\t}[weekend]; ok {\n\t\tmask := make([]byte, 7)\n\t\tfor _, idx := range masks {\n\t\t\tmask[idx] = 1\n\t\t}\n\t\treturn mask\n\t}\n\treturn nil\n}\n\n// isWorkday check if the date is workday.\nfunc isWorkday(weekendMask []byte, date float64) bool {\n\tdateTime := timeFromExcelTime(date, false)\n\tweekday := dateTime.Weekday()\n\tif weekday == time.Sunday {\n\t\tweekday = 7\n\t}\n\treturn weekendMask[weekday-1] == 0\n}\n\n// prepareWorkday returns weekend mask and workdays pre week by given days\n// counted as weekend.\nfunc prepareWorkday(weekend formulaArg) ([]byte, int) {\n\tweekendArg := weekend.ToNumber()\n\tif weekendArg.Type != ArgNumber {\n\t\treturn nil, 0\n\t}\n\tvar weekendMask []byte\n\tvar workdaysPerWeek int\n\tif len(weekend.Value()) == 7 {\n\t\t// possible string values for the weekend argument\n\t\tfor _, mask := range weekend.Value() {\n\t\t\tif mask != '0' && mask != '1' {\n\t\t\t\treturn nil, 0\n\t\t\t}\n\t\t\tweekendMask = append(weekendMask, byte(mask)-48)\n\t\t}\n\t} else {\n\t\tweekendMask = genWeekendMask(int(weekendArg.Number))\n\t}\n\tfor _, mask := range weekendMask {\n\t\tif mask == 0 {\n\t\t\tworkdaysPerWeek++\n\t\t}\n\t}\n\treturn weekendMask, workdaysPerWeek\n}\n\n// toExcelDateArg function converts a text representation of a time, into an\n// Excel date time number formula argument.\nfunc toExcelDateArg(arg formulaArg) formulaArg {\n\tnum := arg.ToNumber()\n\tif num.Type != ArgNumber {\n\t\tdateString := strings.ToLower(arg.Value())\n\t\tif !isDateOnlyFmt(dateString) {\n\t\t\tif _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\ty, m, d, _, err := strToDate(dateString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t\tnum.Number, _ = timeToExcelTime(time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC), false)\n\t\treturn newNumberFormulaArg(num.Number)\n\t}\n\tif arg.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn num\n}\n\n// prepareHolidays function converts array type formula arguments to into an\n// Excel date time number formula arguments list.\nfunc prepareHolidays(args formulaArg) []int {\n\tvar holidays []int\n\tfor _, arg := range args.ToList() {\n\t\tnum := toExcelDateArg(arg)\n\t\tif num.Type != ArgNumber {\n\t\t\tcontinue\n\t\t}\n\t\tholidays = append(holidays, int(math.Ceil(num.Number)))\n\t}\n\treturn holidays\n}\n\n// workdayIntl is an implementation of the formula function WORKDAY.INTL.\nfunc workdayIntl(endDate, sign int, holidays []int, weekendMask []byte, startDate float64) int {\n\tfor i := 0; i < len(holidays); i++ {\n\t\tholiday := holidays[i]\n\t\tif sign > 0 {\n\t\t\tif holiday > endDate {\n\t\t\t\tbreak\n\t\t\t}\n\t\t} else {\n\t\t\tif holiday < endDate {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif sign > 0 {\n\t\t\tif holiday > int(math.Ceil(startDate)) {\n\t\t\t\tif isWorkday(weekendMask, float64(holiday)) {\n\t\t\t\t\tendDate += sign\n\t\t\t\t\tfor !isWorkday(weekendMask, float64(endDate)) {\n\t\t\t\t\t\tendDate += sign\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif holiday < int(math.Ceil(startDate)) {\n\t\t\t\tif isWorkday(weekendMask, float64(holiday)) {\n\t\t\t\t\tendDate += sign\n\t\t\t\t\tfor !isWorkday(weekendMask, float64(endDate)) {\n\t\t\t\t\t\tendDate += sign\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn endDate\n}\n\n// NETWORKDAYS function calculates the number of work days between two supplied\n// dates (including the start and end date). The calculation includes all\n// weekdays (Mon - Fri), excluding a supplied list of holidays. The syntax of\n// the function is:\n//\n//\tNETWORKDAYS(start_date,end_date,[holidays])\nfunc (fn *formulaFuncs) NETWORKDAYS(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NETWORKDAYS requires at least 2 arguments\")\n\t}\n\tif argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NETWORKDAYS requires at most 3 arguments\")\n\t}\n\targs := list.New()\n\targs.PushBack(argsList.Front().Value.(formulaArg))\n\targs.PushBack(argsList.Front().Next().Value.(formulaArg))\n\targs.PushBack(newNumberFormulaArg(1))\n\tif argsList.Len() == 3 {\n\t\targs.PushBack(argsList.Back().Value.(formulaArg))\n\t}\n\treturn fn.NETWORKDAYSdotINTL(args)\n}\n\n// NETWORKDAYSdotINTL function calculates the number of whole work days between\n// two supplied dates, excluding weekends and holidays. The function allows\n// the user to specify which days are counted as weekends and holidays. The\n// syntax of the function is:\n//\n//\tNETWORKDAYS.INTL(start_date,end_date,[weekend],[holidays])\nfunc (fn *formulaFuncs) NETWORKDAYSdotINTL(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NETWORKDAYS.INTL requires at least 2 arguments\")\n\t}\n\tif argsList.Len() > 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NETWORKDAYS.INTL requires at most 4 arguments\")\n\t}\n\tstartDate := toExcelDateArg(argsList.Front().Value.(formulaArg))\n\tif startDate.Type != ArgNumber {\n\t\treturn startDate\n\t}\n\tendDate := toExcelDateArg(argsList.Front().Next().Value.(formulaArg))\n\tif endDate.Type != ArgNumber {\n\t\treturn endDate\n\t}\n\tweekend := newNumberFormulaArg(1)\n\tif argsList.Len() > 2 {\n\t\tweekend = argsList.Front().Next().Next().Value.(formulaArg)\n\t}\n\tvar holidays []int\n\tif argsList.Len() == 4 {\n\t\tholidays = prepareHolidays(argsList.Back().Value.(formulaArg))\n\t\tsort.Ints(holidays)\n\t}\n\tweekendMask, workdaysPerWeek := prepareWorkday(weekend)\n\tif workdaysPerWeek == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tsign := 1\n\tif startDate.Number > endDate.Number {\n\t\tsign = -1\n\t\tstartDate.Number, endDate.Number = endDate.Number, startDate.Number\n\t}\n\toffset := endDate.Number - startDate.Number\n\tcount := int(math.Floor(offset/7) * float64(workdaysPerWeek))\n\tdaysMod := int(offset) % 7\n\tfor daysMod >= 0 {\n\t\tif isWorkday(weekendMask, endDate.Number-float64(daysMod)) {\n\t\t\tcount++\n\t\t}\n\t\tdaysMod--\n\t}\n\tfor i := 0; i < len(holidays); i++ {\n\t\tholiday := float64(holidays[i])\n\t\tif isWorkday(weekendMask, holiday) && holiday >= startDate.Number && holiday <= endDate.Number {\n\t\t\tcount--\n\t\t}\n\t}\n\treturn newNumberFormulaArg(float64(sign * count))\n}\n\n// WORKDAY function returns a date that is a supplied number of working days\n// (excluding weekends and holidays) ahead of a given start date. The syntax\n// of the function is:\n//\n//\tWORKDAY(start_date,days,[holidays])\nfunc (fn *formulaFuncs) WORKDAY(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"WORKDAY requires at least 2 arguments\")\n\t}\n\tif argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"WORKDAY requires at most 3 arguments\")\n\t}\n\targs := list.New()\n\targs.PushBack(argsList.Front().Value.(formulaArg))\n\targs.PushBack(argsList.Front().Next().Value.(formulaArg))\n\targs.PushBack(newNumberFormulaArg(1))\n\tif argsList.Len() == 3 {\n\t\targs.PushBack(argsList.Back().Value.(formulaArg))\n\t}\n\treturn fn.WORKDAYdotINTL(args)\n}\n\n// WORKDAYdotINTL function returns a date that is a supplied number of working\n// days (excluding weekends and holidays) ahead of a given start date. The\n// function allows the user to specify which days of the week are counted as\n// weekends. The syntax of the function is:\n//\n//\tWORKDAY.INTL(start_date,days,[weekend],[holidays])\nfunc (fn *formulaFuncs) WORKDAYdotINTL(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"WORKDAY.INTL requires at least 2 arguments\")\n\t}\n\tif argsList.Len() > 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"WORKDAY.INTL requires at most 4 arguments\")\n\t}\n\tstartDate := toExcelDateArg(argsList.Front().Value.(formulaArg))\n\tif startDate.Type != ArgNumber {\n\t\treturn startDate\n\t}\n\tdays := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif days.Type != ArgNumber {\n\t\treturn days\n\t}\n\tweekend := newNumberFormulaArg(1)\n\tif argsList.Len() > 2 {\n\t\tweekend = argsList.Front().Next().Next().Value.(formulaArg)\n\t}\n\tvar holidays []int\n\tif argsList.Len() == 4 {\n\t\tholidays = prepareHolidays(argsList.Back().Value.(formulaArg))\n\t\tsort.Ints(holidays)\n\t}\n\tif days.Number == 0 {\n\t\treturn newNumberFormulaArg(math.Ceil(startDate.Number))\n\t}\n\tweekendMask, workdaysPerWeek := prepareWorkday(weekend)\n\tif workdaysPerWeek == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tsign := 1\n\tif days.Number < 0 {\n\t\tsign = -1\n\t}\n\toffset := int(days.Number) / workdaysPerWeek\n\tdaysMod := int(days.Number) % workdaysPerWeek\n\tendDate := int(math.Ceil(startDate.Number)) + offset*7\n\tif daysMod == 0 {\n\t\tfor !isWorkday(weekendMask, float64(endDate)) {\n\t\t\tendDate -= sign\n\t\t}\n\t} else {\n\t\tfor daysMod != 0 {\n\t\t\tendDate += sign\n\t\t\tif isWorkday(weekendMask, float64(endDate)) {\n\t\t\t\tif daysMod < 0 {\n\t\t\t\t\tdaysMod++\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tdaysMod--\n\t\t\t}\n\t\t}\n\t}\n\treturn newNumberFormulaArg(float64(workdayIntl(endDate, sign, holidays, weekendMask, startDate.Number)))\n}\n\n// YEAR function returns an integer representing the year of a supplied date.\n// The syntax of the function is:\n//\n//\tYEAR(serial_number)\nfunc (fn *formulaFuncs) YEAR(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"YEAR requires exactly 1 argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg)\n\tnum := arg.ToNumber()\n\tif num.Type != ArgNumber {\n\t\tdateString := strings.ToLower(arg.Value())\n\t\tif !isDateOnlyFmt(dateString) {\n\t\t\tif _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tyear, _, _, _, err := strToDate(dateString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t\treturn newNumberFormulaArg(float64(year))\n\t}\n\tif num.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"YEAR only accepts positive argument\")\n\t}\n\treturn newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Year()))\n}\n\n// yearFracBasisCond is an implementation of the yearFracBasis1.\nfunc yearFracBasisCond(sy, sm, sd, ey, em, ed int) bool {\n\treturn (isLeapYear(sy) && (sm < 2 || (sm == 2 && sd <= 29))) || (isLeapYear(ey) && (em > 2 || (em == 2 && ed == 29)))\n}\n\n// yearFracBasis0 function returns the fraction of a year that between two\n// supplied dates in US (NASD) 30/360 type of day.\nfunc yearFracBasis0(startDate, endDate float64) (dayDiff, daysInYear float64) {\n\tstartTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false)\n\tsy, smM, sd := startTime.Date()\n\tey, emM, ed := endTime.Date()\n\tsm, em := int(smM), int(emM)\n\tif sd == 31 {\n\t\tsd--\n\t}\n\tif sd == 30 && ed == 31 {\n\t\ted--\n\t} else if leap := isLeapYear(sy); sm == 2 && ((leap && sd == 29) || (!leap && sd == 28)) {\n\t\tsd = 30\n\t\tif leap := isLeapYear(ey); em == 2 && ((leap && ed == 29) || (!leap && ed == 28)) {\n\t\t\ted = 30\n\t\t}\n\t}\n\tdayDiff = float64((ey-sy)*360 + (em-sm)*30 + (ed - sd))\n\tdaysInYear = 360\n\treturn\n}\n\n// yearFracBasis1 function returns the fraction of a year that between two\n// supplied dates in actual type of day.\nfunc yearFracBasis1(startDate, endDate float64) (dayDiff, daysInYear float64) {\n\tstartTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false)\n\tsy, smM, sd := startTime.Date()\n\tey, emM, ed := endTime.Date()\n\tsm, em := int(smM), int(emM)\n\tdayDiff = endDate - startDate\n\tisYearDifferent := sy != ey\n\tif isYearDifferent && (ey != sy+1 || sm < em || (sm == em && sd < ed)) {\n\t\tdayCount := 0\n\t\tfor y := sy; y <= ey; y++ {\n\t\t\tdayCount += getYearDays(y, 1)\n\t\t}\n\t\tdaysInYear = float64(dayCount) / float64(ey-sy+1)\n\t} else {\n\t\tif !isYearDifferent && isLeapYear(sy) {\n\t\t\tdaysInYear = 366\n\t\t} else {\n\t\t\tif isYearDifferent && yearFracBasisCond(sy, sm, sd, ey, em, ed) {\n\t\t\t\tdaysInYear = 366\n\t\t\t} else {\n\t\t\t\tdaysInYear = 365\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// yearFracBasis4 function returns the fraction of a year that between two\n// supplied dates in European 30/360 type of day.\nfunc yearFracBasis4(startDate, endDate float64) (dayDiff, daysInYear float64) {\n\tstartTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false)\n\tsy, smM, sd := startTime.Date()\n\tey, emM, ed := endTime.Date()\n\tsm, em := int(smM), int(emM)\n\tif sd == 31 {\n\t\tsd--\n\t}\n\tif ed == 31 {\n\t\ted--\n\t}\n\tdayDiff = float64((ey-sy)*360 + (em-sm)*30 + (ed - sd))\n\tdaysInYear = 360\n\treturn\n}\n\n// yearFrac is an implementation of the formula function YEARFRAC.\nfunc yearFrac(startDate, endDate float64, basis int) formulaArg {\n\tstartTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false)\n\tif startTime.Equal(endTime) {\n\t\treturn newNumberFormulaArg(0)\n\t}\n\tvar dayDiff, daysInYear float64\n\tswitch basis {\n\tcase 0:\n\t\tdayDiff, daysInYear = yearFracBasis0(startDate, endDate)\n\tcase 1:\n\t\tdayDiff, daysInYear = yearFracBasis1(startDate, endDate)\n\tcase 2:\n\t\tdayDiff = endDate - startDate\n\t\tdaysInYear = 360\n\tcase 3:\n\t\tdayDiff = endDate - startDate\n\t\tdaysInYear = 365\n\tcase 4:\n\t\tdayDiff, daysInYear = yearFracBasis4(startDate, endDate)\n\tdefault:\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"invalid basis\")\n\t}\n\treturn newNumberFormulaArg(dayDiff / daysInYear)\n}\n\n// getYearDays return days of the year with specifying the type of day count\n// basis to be used.\nfunc getYearDays(year, basis int) int {\n\tswitch basis {\n\tcase 1:\n\t\tif isLeapYear(year) {\n\t\t\treturn 366\n\t\t}\n\t\treturn 365\n\tcase 3:\n\t\treturn 365\n\tdefault:\n\t\treturn 360\n\t}\n}\n\n// YEARFRAC function returns the fraction of a year that is represented by the\n// number of whole days between two supplied dates. The syntax of the\n// function is:\n//\n//\tYEARFRAC(start_date,end_date,[basis])\nfunc (fn *formulaFuncs) YEARFRAC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 && argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"YEARFRAC requires 3 or 4 arguments\")\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tstart, end := args.List[0], args.List[1]\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 3 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn basis\n\t\t}\n\t}\n\treturn yearFrac(start.Number, end.Number, int(basis.Number))\n}\n\n// NOW function returns the current date and time. The function receives no\n// arguments and therefore. The syntax of the function is:\n//\n//\tNOW()\nfunc (fn *formulaFuncs) NOW(argsList *list.List) formulaArg {\n\tif argsList.Len() != 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NOW accepts no arguments\")\n\t}\n\tnow := time.Now()\n\t_, offset := now.Zone()\n\treturn newNumberFormulaArg(25569.0 + float64(now.Unix()+int64(offset))/86400)\n}\n\n// SECOND function returns an integer representing the second component of a\n// supplied Excel time. The syntax of the function is:\n//\n//\tSECOND(serial_number)\nfunc (fn *formulaFuncs) SECOND(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SECOND requires exactly 1 argument\")\n\t}\n\tdate := argsList.Front().Value.(formulaArg)\n\tnum := date.ToNumber()\n\tif num.Type != ArgNumber {\n\t\ttimeString := strings.ToLower(date.Value())\n\t\tif !isTimeOnlyFmt(timeString) {\n\t\t\t_, _, _, _, err := strToDate(timeString)\n\t\t\tif err.Type == ArgError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\t_, _, s, _, _, err := strToTime(timeString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t\treturn newNumberFormulaArg(float64(int(s) % 60))\n\t}\n\tif num.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"SECOND only accepts positive argument\")\n\t}\n\treturn newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Second()))\n}\n\n// TIME function accepts three integer arguments representing hours, minutes\n// and seconds, and returns an Excel time. I.e. the function returns the\n// decimal value that represents the time in Excel. The syntax of the\n// function is:\n//\n//\tTIME(hour,minute,second)\nfunc (fn *formulaFuncs) TIME(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TIME requires 3 number arguments\")\n\t}\n\th := argsList.Front().Value.(formulaArg).ToNumber()\n\tm := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\ts := argsList.Back().Value.(formulaArg).ToNumber()\n\tif h.Type != ArgNumber || m.Type != ArgNumber || s.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TIME requires 3 number arguments\")\n\t}\n\tt := (h.Number*3600 + m.Number*60 + s.Number) / 86400\n\tif t < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(t)\n}\n\n// TIMEVALUE function converts a text representation of a time, into an Excel\n// time. The syntax of the function is:\n//\n//\tTIMEVALUE(time_text)\nfunc (fn *formulaFuncs) TIMEVALUE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TIMEVALUE requires exactly 1 argument\")\n\t}\n\tdate := argsList.Front().Value.(formulaArg)\n\ttimeString := strings.ToLower(date.Value())\n\tif !isTimeOnlyFmt(timeString) {\n\t\t_, _, _, _, err := strToDate(timeString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t}\n\th, m, s, pm, _, err := strToTime(timeString)\n\tif err.Type == ArgError {\n\t\treturn err\n\t}\n\tif pm {\n\t\th += 12\n\t}\n\targs := list.New()\n\targs.PushBack(newNumberFormulaArg(float64(h)))\n\targs.PushBack(newNumberFormulaArg(float64(m)))\n\targs.PushBack(newNumberFormulaArg(s))\n\treturn fn.TIME(args)\n}\n\n// TODAY function returns the current date. The function has no arguments and\n// therefore. The syntax of the function is:\n//\n//\tTODAY()\nfunc (fn *formulaFuncs) TODAY(argsList *list.List) formulaArg {\n\tif argsList.Len() != 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TODAY accepts no arguments\")\n\t}\n\tnow := time.Now()\n\t_, offset := now.Zone()\n\treturn newNumberFormulaArg(daysBetween(excelMinTime1900.Unix(), now.Unix()+int64(offset)) + 1)\n}\n\n// makeDate return date as a Unix time, the number of seconds elapsed since\n// January 1, 1970 UTC.\nfunc makeDate(y int, m time.Month, d int) int64 {\n\tif y == 1900 && int(m) <= 2 {\n\t\td--\n\t}\n\tdate := time.Date(y, m, d, 0, 0, 0, 0, time.UTC)\n\treturn date.Unix()\n}\n\n// daysBetween return time interval of the given start timestamp and end\n// timestamp.\nfunc daysBetween(startDate, endDate int64) float64 {\n\treturn float64(int(0.5 + float64((endDate-startDate)/86400)))\n}\n\n// WEEKDAY function returns an integer representing the day of the week for a\n// supplied date. The syntax of the function is:\n//\n//\tWEEKDAY(serial_number,[return_type])\nfunc (fn *formulaFuncs) WEEKDAY(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"WEEKDAY requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"WEEKDAY allows at most 2 arguments\")\n\t}\n\tsn := argsList.Front().Value.(formulaArg)\n\tnum := sn.ToNumber()\n\tweekday, returnType := 0, 1\n\tif num.Type != ArgNumber {\n\t\tdateString := strings.ToLower(sn.Value())\n\t\tif !isDateOnlyFmt(dateString) {\n\t\t\tif _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\ty, m, d, _, err := strToDate(dateString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t\tweekday = int(time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.Now().Location()).Weekday())\n\t} else {\n\t\tif num.Number < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t\tweekday = int(timeFromExcelTime(num.Number, false).Weekday())\n\t}\n\tif argsList.Len() == 2 {\n\t\treturnTypeArg := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif returnTypeArg.Type != ArgNumber {\n\t\t\treturn returnTypeArg\n\t\t}\n\t\treturnType = int(returnTypeArg.Number)\n\t}\n\tif returnType == 2 {\n\t\treturnType = 11\n\t}\n\tweekday++\n\tif returnType == 1 {\n\t\treturn newNumberFormulaArg(float64(weekday))\n\t}\n\tif returnType == 3 {\n\t\treturn newNumberFormulaArg(float64((weekday + 6 - 1) % 7))\n\t}\n\tif returnType >= 11 && returnType <= 17 {\n\t\treturn newNumberFormulaArg(float64((weekday+6-(returnType-10))%7 + 1))\n\t}\n\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n}\n\n// weeknum is an implementation of the formula function WEEKNUM.\nfunc (fn *formulaFuncs) weeknum(snTime time.Time, returnType int) formulaArg {\n\tdays := snTime.YearDay()\n\tweekMod, weekNum := days%7, math.Ceil(float64(days)/7)\n\tif weekMod == 0 {\n\t\tweekMod = 7\n\t}\n\tyear := snTime.Year()\n\tfirstWeekday := int(time.Date(year, time.January, 1, 0, 0, 0, 0, time.UTC).Weekday())\n\tvar offset int\n\tswitch returnType {\n\tcase 1, 17:\n\t\toffset = 0\n\tcase 2, 11, 21:\n\t\toffset = 1\n\tcase 12, 13, 14, 15, 16:\n\t\toffset = returnType - 10\n\tdefault:\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tpadding := offset + 7 - firstWeekday\n\tif padding > 7 {\n\t\tpadding -= 7\n\t}\n\tif weekMod > padding {\n\t\tweekNum++\n\t}\n\tif returnType == 21 && (firstWeekday == 0 || firstWeekday > 4) {\n\t\tif weekNum--; weekNum < 1 {\n\t\t\tif weekNum = 52; int(time.Date(year-1, time.January, 1, 0, 0, 0, 0, time.UTC).Weekday()) < 4 {\n\t\t\t\tweekNum++\n\t\t\t}\n\t\t}\n\t}\n\treturn newNumberFormulaArg(weekNum)\n}\n\n// WEEKNUM function returns an integer representing the week number (from 1 to\n// 53) of the year. The syntax of the function is:\n//\n//\tWEEKNUM(serial_number,[return_type])\nfunc (fn *formulaFuncs) WEEKNUM(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"WEEKNUM requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"WEEKNUM allows at most 2 arguments\")\n\t}\n\tsn := argsList.Front().Value.(formulaArg)\n\tnum, returnType := sn.ToNumber(), 1\n\tvar snTime time.Time\n\tif num.Type != ArgNumber {\n\t\tdateString := strings.ToLower(sn.Value())\n\t\tif !isDateOnlyFmt(dateString) {\n\t\t\tif _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\ty, m, d, _, err := strToDate(dateString)\n\t\tif err.Type == ArgError {\n\t\t\treturn err\n\t\t}\n\t\tsnTime = time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.Now().Location())\n\t} else {\n\t\tif num.Number < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t\tsnTime = timeFromExcelTime(num.Number, false)\n\t}\n\tif argsList.Len() == 2 {\n\t\treturnTypeArg := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif returnTypeArg.Type != ArgNumber {\n\t\t\treturn returnTypeArg\n\t\t}\n\t\treturnType = int(returnTypeArg.Number)\n\t}\n\treturn fn.weeknum(snTime, returnType)\n}\n\n// Text Functions\n\n// prepareToText checking and prepare arguments for the formula functions\n// ARRAYTOTEXT and VALUETOTEXT.\nfunc prepareToText(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 1 argument\", name))\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s allows at most 2 arguments\", name))\n\t}\n\tformat := newNumberFormulaArg(0)\n\tif argsList.Len() == 2 {\n\t\tif format = argsList.Back().Value.(formulaArg).ToNumber(); format.Type != ArgNumber {\n\t\t\treturn format\n\t\t}\n\t}\n\tif format.Number != 0 && format.Number != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\treturn format\n}\n\n// ARRAYTOTEXT function returns an array of text values from any specified\n// range. It passes text values unchanged, and converts non-text values to\n// text. The syntax of the function is:\n//\n//\tARRAYTOTEXT(array,[format])\nfunc (fn *formulaFuncs) ARRAYTOTEXT(argsList *list.List) formulaArg {\n\tvar mtx [][]string\n\tformat := prepareToText(\"ARRAYTOTEXT\", argsList)\n\tif format.Type != ArgNumber {\n\t\treturn format\n\t}\n\tfor _, rows := range argsList.Front().Value.(formulaArg).Matrix {\n\t\tvar row []string\n\t\tfor _, cell := range rows {\n\t\t\tif num := cell.ToNumber(); num.Type != ArgNumber && format.Number == 1 {\n\t\t\t\trow = append(row, fmt.Sprintf(\"\\\"%s\\\"\", cell.Value()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\trow = append(row, cell.Value())\n\t\t}\n\t\tmtx = append(mtx, row)\n\t}\n\tvar text []string\n\tfor _, row := range mtx {\n\t\tif format.Number == 1 {\n\t\t\ttext = append(text, strings.Join(row, \",\"))\n\t\t\tcontinue\n\t\t}\n\t\ttext = append(text, strings.Join(row, \", \"))\n\t}\n\tif format.Number == 1 {\n\t\treturn newStringFormulaArg(fmt.Sprintf(\"{%s}\", strings.Join(text, \";\")))\n\t}\n\treturn newStringFormulaArg(strings.Join(text, \", \"))\n}\n\n// bahttextAppendDigit appends a digit to the passed string.\nfunc bahttextAppendDigit(text string, digit int) string {\n\tif 0 <= digit && digit <= 9 {\n\t\treturn text + []string{th0, th1, th2, th3, th4, th5, th6, th7, th8, th9}[digit]\n\t}\n\treturn text\n}\n\n// bahttextAppendPow10 appends a value raised to a power of 10: digit*10^pow10.\nfunc bahttextAppendPow10(text string, digit, pow10 int) string {\n\ttext = bahttextAppendDigit(text, digit)\n\tswitch pow10 {\n\tcase 2:\n\t\ttext += th1e2\n\tcase 3:\n\t\ttext += th1e3\n\tcase 4:\n\t\ttext += th1e4\n\tcase 5:\n\t\ttext += th1e5\n\t}\n\treturn text\n}\n\n// bahttextAppendBlock appends a block of 6 digits to the passed string.\nfunc bahttextAppendBlock(text string, val int) string {\n\tif val >= 100000 {\n\t\ttext = bahttextAppendPow10(text, val/100000, 5)\n\t\tval %= 100000\n\t}\n\tif val >= 10000 {\n\t\ttext = bahttextAppendPow10(text, val/10000, 4)\n\t\tval %= 10000\n\t}\n\tif val >= 1000 {\n\t\ttext = bahttextAppendPow10(text, val/1000, 3)\n\t\tval %= 1000\n\t}\n\tif val >= 100 {\n\t\ttext = bahttextAppendPow10(text, val/100, 2)\n\t\tval %= 100\n\t}\n\tif val > 0 {\n\t\tn10 := val / 10\n\t\tn1 := val % 10\n\t\tif n10 >= 1 {\n\t\t\tif n10 >= 3 {\n\t\t\t\ttext = bahttextAppendDigit(text, n10)\n\t\t\t} else if n10 == 2 {\n\t\t\t\ttext += th20\n\t\t\t}\n\t\t\ttext += th10\n\t\t}\n\t\tif n10 > 0 && n1 == 1 {\n\t\t\ttext += th11\n\t\t} else if n1 > 0 {\n\t\t\ttext = bahttextAppendDigit(text, n1)\n\t\t}\n\t}\n\treturn text\n}\n\n// BAHTTEXT function converts a number into Thai text, with the suffix \"Baht\".\n// The syntax of the function is:\n//\n//\tBAHTTEXT(number)\nfunc (fn *formulaFuncs) BAHTTEXT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"BAHTTEXT requires 1 numeric argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg)\n\tnumber := token.ToNumber()\n\tif number.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, number.Error)\n\t}\n\tminus := number.Number < 0\n\tnum := math.Floor(math.Abs(number.Number)*100 + 0.5)\n\t// split baht and satang\n\tsplitBlock := func(val, size float64) (float64, int) {\n\t\tinteger, frac := math.Modf((val + 0.1) / size)\n\t\tfrac = frac*size + 0.1\n\t\treturn integer, int(frac)\n\t}\n\tbaht, satang := splitBlock(num, 100)\n\tvar text string\n\tif baht == 0 {\n\t\tif satang == 0 {\n\t\t\ttext += th0\n\t\t}\n\t} else {\n\t\tfor baht > 0 {\n\t\t\tvar block string\n\t\t\tvar nBlock int\n\t\t\tbaht, nBlock = splitBlock(baht, 1.0e6)\n\t\t\tif nBlock > 0 {\n\t\t\t\tblock = bahttextAppendBlock(block, nBlock)\n\t\t\t}\n\t\t\tif baht > 0 {\n\t\t\t\tblock = th1e6 + block\n\t\t\t}\n\t\t\ttext = block + text\n\t\t}\n\t}\n\tif len(text) > 0 {\n\t\ttext += thBaht\n\t}\n\tif satang == 0 {\n\t\ttext += thDot0\n\t} else {\n\t\ttext = bahttextAppendBlock(text, satang)\n\t\ttext += thSatang\n\t}\n\tif minus {\n\t\ttext = thMinus + text\n\t}\n\treturn newStringFormulaArg(text)\n}\n\n// CHAR function returns the character relating to a supplied character set\n// number (from 1 to 255). The syntax of the function is:\n//\n//\tCHAR(number)\nfunc (fn *formulaFuncs) CHAR(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CHAR requires 1 argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg).ToNumber()\n\tif arg.Type != ArgNumber {\n\t\treturn arg\n\t}\n\tnum := int(arg.Number)\n\tif num < 0 || num > MaxFieldLength {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\treturn newStringFormulaArg(fmt.Sprintf(\"%c\", num))\n}\n\n// CLEAN removes all non-printable characters from a supplied text string. The\n// syntax of the function is:\n//\n//\tCLEAN(text)\nfunc (fn *formulaFuncs) CLEAN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CLEAN requires 1 argument\")\n\t}\n\tb := bytes.Buffer{}\n\tfor _, c := range argsList.Front().Value.(formulaArg).Value() {\n\t\tif c > 31 {\n\t\t\tb.WriteRune(c)\n\t\t}\n\t}\n\treturn newStringFormulaArg(b.String())\n}\n\n// CODE function converts the first character of a supplied text string into\n// the associated numeric character set code used by your computer. The\n// syntax of the function is:\n//\n//\tCODE(text)\nfunc (fn *formulaFuncs) CODE(argsList *list.List) formulaArg {\n\treturn fn.code(\"CODE\", argsList)\n}\n\n// code is an implementation of the formula functions CODE and UNICODE.\nfunc (fn *formulaFuncs) code(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 1 argument\", name))\n\t}\n\ttext := argsList.Front().Value.(formulaArg).Value()\n\tif len(text) == 0 {\n\t\tif name == \"CODE\" {\n\t\t\treturn newNumberFormulaArg(0)\n\t\t}\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\treturn newNumberFormulaArg(float64(text[0]))\n}\n\n// CONCAT function joins together a series of supplied text strings into one\n// combined text string.\n//\n//\tCONCAT(text1,[text2],...)\nfunc (fn *formulaFuncs) CONCAT(argsList *list.List) formulaArg {\n\treturn fn.concat(argsList)\n}\n\n// CONCATENATE function joins together a series of supplied text strings into\n// one combined text string.\n//\n//\tCONCATENATE(text1,[text2],...)\nfunc (fn *formulaFuncs) CONCATENATE(argsList *list.List) formulaArg {\n\treturn fn.concat(argsList)\n}\n\n// concat is an implementation of the formula functions CONCAT and\n// CONCATENATE.\nfunc (fn *formulaFuncs) concat(argsList *list.List) formulaArg {\n\tvar buf bytes.Buffer\n\tfor arg := argsList.Front(); arg != nil; arg = arg.Next() {\n\t\tfor _, cell := range arg.Value.(formulaArg).ToList() {\n\t\t\tif cell.Type == ArgError {\n\t\t\t\treturn cell\n\t\t\t}\n\t\t\tbuf.WriteString(cell.Value())\n\t\t}\n\t}\n\treturn newStringFormulaArg(buf.String())\n}\n\n// DBCS converts half-width (single-byte) letters within a character string to\n// full-width (double-byte) characters. The syntax of the function is:\n//\n//\tDBCS(text)\nfunc (fn *formulaFuncs) DBCS(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DBCS requires 1 argument\")\n\t}\n\targ := argsList.Front().Value.(formulaArg)\n\tif arg.Type == ArgError {\n\t\treturn arg\n\t}\n\tif fn.f.options.CultureInfo == CultureNameJaJP ||\n\t\tfn.f.options.CultureInfo == CultureNameZhCN ||\n\t\tfn.f.options.CultureInfo == CultureNameZhTW {\n\t\tvar chars []string\n\t\tfor _, r := range arg.Value() {\n\t\t\tcode := r\n\t\t\tif code == 32 {\n\t\t\t\tcode = 12288\n\t\t\t} else {\n\t\t\t\tcode += 65248\n\t\t\t}\n\t\t\tif (code < 32 || code > 126) && r != 165 && code < 65381 {\n\t\t\t\tchars = append(chars, string(code))\n\t\t\t} else {\n\t\t\t\tchars = append(chars, string(r))\n\t\t\t}\n\t\t}\n\t\treturn newStringFormulaArg(strings.Join(chars, \"\"))\n\t}\n\treturn arg\n}\n\n// EXACT function tests if two supplied text strings or values are exactly\n// equal and if so, returns TRUE; Otherwise, the function returns FALSE. The\n// function is case-sensitive. The syntax of the function is:\n//\n//\tEXACT(text1,text2)\nfunc (fn *formulaFuncs) EXACT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"EXACT requires 2 arguments\")\n\t}\n\ttext1 := argsList.Front().Value.(formulaArg).Value()\n\ttext2 := argsList.Back().Value.(formulaArg).Value()\n\treturn newBoolFormulaArg(text1 == text2)\n}\n\n// FIXED function rounds a supplied number to a specified number of decimal\n// places and then converts this into text. The syntax of the function is:\n//\n//\tFIXED(number,[decimals],[no_commas])\nfunc (fn *formulaFuncs) FIXED(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FIXED requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FIXED allows at most 3 arguments\")\n\t}\n\tnumArg := argsList.Front().Value.(formulaArg).ToNumber()\n\tif numArg.Type != ArgNumber {\n\t\treturn numArg\n\t}\n\tprecision, decimals, noCommas := 0, 0, false\n\ts := strings.Split(argsList.Front().Value.(formulaArg).Value(), \".\")\n\tif argsList.Len() == 1 && len(s) == 2 {\n\t\tprecision = len(s[1])\n\t\tdecimals = len(s[1])\n\t}\n\tif argsList.Len() >= 2 {\n\t\tdecimalsArg := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\t\tif decimalsArg.Type != ArgNumber {\n\t\t\treturn decimalsArg\n\t\t}\n\t\tdecimals = int(decimalsArg.Number)\n\t}\n\tif argsList.Len() == 3 {\n\t\tnoCommasArg := argsList.Back().Value.(formulaArg).ToBool()\n\t\tif noCommasArg.Type == ArgError {\n\t\t\treturn noCommasArg\n\t\t}\n\t\tnoCommas = noCommasArg.Boolean\n\t}\n\tn := math.Pow(10, float64(decimals))\n\tr := numArg.Number * n\n\tfixed := float64(int(r+math.Copysign(0.5, r))) / n\n\tif decimals > 0 {\n\t\tprecision = decimals\n\t}\n\tif noCommas {\n\t\treturn newStringFormulaArg(fmt.Sprintf(fmt.Sprintf(\"%%.%df\", precision), fixed))\n\t}\n\tp := message.NewPrinter(language.English)\n\treturn newStringFormulaArg(p.Sprintf(fmt.Sprintf(\"%%.%df\", precision), fixed))\n}\n\n// FIND function returns the position of a specified character or sub-string\n// within a supplied text string. The function is case-sensitive. The syntax\n// of the function is:\n//\n//\tFIND(find_text,within_text,[start_num])\nfunc (fn *formulaFuncs) FIND(argsList *list.List) formulaArg {\n\treturn fn.find(\"FIND\", argsList)\n}\n\n// FINDB counts each double-byte character as 2 when you have enabled the\n// editing of a language that supports DBCS and then set it as the default\n// language. Otherwise, FINDB counts each character as 1. The syntax of the\n// function is:\n//\n//\tFINDB(find_text,within_text,[start_num])\nfunc (fn *formulaFuncs) FINDB(argsList *list.List) formulaArg {\n\treturn fn.find(\"FINDB\", argsList)\n}\n\n// prepareFindArgs checking and prepare arguments for the formula functions\n// FIND, FINDB, SEARCH and SEARCHB.\nfunc (fn *formulaFuncs) prepareFindArgs(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 2 arguments\", name))\n\t}\n\tif argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s allows at most 3 arguments\", name))\n\t}\n\tstartNum := 1\n\tif argsList.Len() == 3 {\n\t\tnumArg := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif numArg.Type != ArgNumber {\n\t\t\treturn numArg\n\t\t}\n\t\tif numArg.Number < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t\tstartNum = int(numArg.Number)\n\t}\n\treturn newListFormulaArg([]formulaArg{newNumberFormulaArg(float64(startNum))})\n}\n\n// find is an implementation of the formula functions FIND, FINDB, SEARCH and\n// SEARCHB.\nfunc (fn *formulaFuncs) find(name string, argsList *list.List) formulaArg {\n\targs := fn.prepareFindArgs(name, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tfindTextArg := argsList.Front().Value.(formulaArg)\n\twithinText := argsList.Front().Next().Value.(formulaArg).Value()\n\tstartNum := int(args.List[0].Number)\n\tdbcs, search := name == \"FINDB\" || name == \"SEARCHB\", name == \"SEARCH\" || name == \"SEARCHB\"\n\tfind := func(findText string) formulaArg {\n\t\tif findText == \"\" {\n\t\t\treturn newNumberFormulaArg(float64(startNum))\n\t\t}\n\t\tif search {\n\t\t\tfindText, withinText = strings.ToUpper(findText), strings.ToUpper(withinText)\n\t\t}\n\t\toffset, ok := matchPattern(findText, withinText, dbcs, startNum)\n\t\tif !ok {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t\tresult := offset\n\t\tif dbcs {\n\t\t\tvar pre int\n\t\t\tfor idx := range withinText {\n\t\t\t\tif pre > offset {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif idx-pre > 1 {\n\t\t\t\t\tresult++\n\t\t\t\t}\n\t\t\t\tpre = idx\n\t\t\t}\n\t\t}\n\t\treturn newNumberFormulaArg(float64(result))\n\t}\n\tif findTextArg.Type == ArgMatrix {\n\t\tvar mtx [][]formulaArg\n\t\tfor _, row := range findTextArg.Matrix {\n\t\t\tvar array []formulaArg\n\t\t\tfor _, findText := range row {\n\t\t\t\tarray = append(array, find(findText.Value()))\n\t\t\t}\n\t\t\tmtx = append(mtx, array)\n\t\t}\n\t\treturn newMatrixFormulaArg(mtx)\n\t}\n\treturn find(findTextArg.Value())\n}\n\n// LEFT function returns a specified number of characters from the start of a\n// supplied text string. The syntax of the function is:\n//\n//\tLEFT(text,[num_chars])\nfunc (fn *formulaFuncs) LEFT(argsList *list.List) formulaArg {\n\treturn fn.leftRight(\"LEFT\", argsList)\n}\n\n// LEFTB returns the first character or characters in a text string, based on\n// the number of bytes you specify. The syntax of the function is:\n//\n//\tLEFTB(text,[num_bytes])\nfunc (fn *formulaFuncs) LEFTB(argsList *list.List) formulaArg {\n\treturn fn.leftRight(\"LEFTB\", argsList)\n}\n\n// leftRight is an implementation of the formula functions LEFT, LEFTB, RIGHT,\n// RIGHTB.\nfunc (fn *formulaFuncs) leftRight(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 1 argument\", name))\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s allows at most 2 arguments\", name))\n\t}\n\ttext, numChars := argsList.Front().Value.(formulaArg).Value(), 1\n\tif argsList.Len() == 2 {\n\t\tnumArg := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif numArg.Type != ArgNumber {\n\t\t\treturn numArg\n\t\t}\n\t\tif numArg.Number < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t\tnumChars = int(numArg.Number)\n\t}\n\tif name == \"LEFTB\" || name == \"RIGHTB\" {\n\t\tif len(text) > numChars {\n\t\t\tif name == \"LEFTB\" {\n\t\t\t\treturn newStringFormulaArg(text[:numChars])\n\t\t\t}\n\t\t\t// RIGHTB\n\t\t\treturn newStringFormulaArg(text[len(text)-numChars:])\n\t\t}\n\t\treturn newStringFormulaArg(text)\n\t}\n\t// LEFT/RIGHT\n\tif countUTF16String(text) > numChars {\n\t\tif name == \"LEFT\" {\n\t\t\treturn newStringFormulaArg(truncateUTF16Units(text, numChars))\n\t\t}\n\t\t// RIGHT\n\t\treturn newStringFormulaArg(string([]rune(text)[utf8.RuneCountInString(text)-numChars:]))\n\t}\n\treturn newStringFormulaArg(text)\n}\n\n// LEN returns the length of a supplied text string. The syntax of the\n// function is:\n//\n//\tLEN(text)\nfunc (fn *formulaFuncs) LEN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LEN requires 1 string argument\")\n\t}\n\treturn newNumberFormulaArg(float64(countUTF16String(argsList.Front().Value.(formulaArg).Value())))\n}\n\n// LENB returns the number of bytes used to represent the characters in a text\n// string. LENB counts 2 bytes per character only when a DBCS language is set\n// as the default language. Otherwise LENB behaves the same as LEN, counting\n// 1 byte per character. The syntax of the function is:\n//\n//\tLENB(text)\nfunc (fn *formulaFuncs) LENB(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LENB requires 1 string argument\")\n\t}\n\tresult := 0\n\tfor _, r := range argsList.Front().Value.(formulaArg).Value() {\n\t\tb := utf8.RuneLen(r)\n\t\tif b == 1 {\n\t\t\tresult++\n\t\t} else if b > 1 {\n\t\t\tresult += 2\n\t\t}\n\t}\n\treturn newNumberFormulaArg(float64(result))\n}\n\n// LOWER converts all characters in a supplied text string to lower case. The\n// syntax of the function is:\n//\n//\tLOWER(text)\nfunc (fn *formulaFuncs) LOWER(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"LOWER requires 1 argument\")\n\t}\n\treturn newStringFormulaArg(strings.ToLower(argsList.Front().Value.(formulaArg).Value()))\n}\n\n// MID function returns a specified number of characters from the middle of a\n// supplied text string. The syntax of the function is:\n//\n//\tMID(text,start_num,num_chars)\nfunc (fn *formulaFuncs) MID(argsList *list.List) formulaArg {\n\treturn fn.mid(\"MID\", argsList)\n}\n\n// MIDB returns a specific number of characters from a text string, starting\n// at the position you specify, based on the number of bytes you specify. The\n// syntax of the function is:\n//\n//\tMID(text,start_num,num_chars)\nfunc (fn *formulaFuncs) MIDB(argsList *list.List) formulaArg {\n\treturn fn.mid(\"MIDB\", argsList)\n}\n\n// mid is an implementation of the formula functions MID and MIDB.\nfunc (fn *formulaFuncs) mid(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 3 arguments\", name))\n\t}\n\ttext := argsList.Front().Value.(formulaArg).Value()\n\tstartNumArg, numCharsArg := argsList.Front().Next().Value.(formulaArg).ToNumber(), argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif startNumArg.Type != ArgNumber {\n\t\treturn startNumArg\n\t}\n\tif numCharsArg.Type != ArgNumber {\n\t\treturn numCharsArg\n\t}\n\tstartNum := int(startNumArg.Number)\n\tif startNum < 1 || numCharsArg.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif name == \"MIDB\" {\n\t\tvar result string\n\t\tvar cnt, offset int\n\t\tfor _, char := range text {\n\t\t\toffset++\n\t\t\tvar dbcs bool\n\t\t\tif utf8.RuneLen(char) > 1 {\n\t\t\t\tdbcs = true\n\t\t\t\toffset++\n\t\t\t}\n\t\t\tif cnt == int(numCharsArg.Number) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif offset+1 > startNum {\n\t\t\t\tif dbcs {\n\t\t\t\t\tif cnt+2 > int(numCharsArg.Number) {\n\t\t\t\t\t\tresult += string(char)[:1]\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tresult += string(char)\n\t\t\t\t\tcnt += 2\n\t\t\t\t} else {\n\t\t\t\t\tresult += string(char)\n\t\t\t\t\tcnt++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn newStringFormulaArg(result)\n\t}\n\t// MID\n\ttextLen := countUTF16String(text)\n\tif startNum > textLen {\n\t\treturn newStringFormulaArg(\"\")\n\t}\n\tstartNum--\n\tendNum := startNum + int(numCharsArg.Number)\n\tif endNum > textLen+1 {\n\t\treturn newStringFormulaArg(string([]rune(text)[startNum:]))\n\t}\n\treturn newStringFormulaArg(string([]rune(text)[startNum:endNum]))\n}\n\n// PROPER converts all characters in a supplied text string to proper case\n// (i.e. all letters that do not immediately follow another letter are set to\n// upper case and all other characters are lower case). The syntax of the\n// function is:\n//\n//\tPROPER(text)\nfunc (fn *formulaFuncs) PROPER(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PROPER requires 1 argument\")\n\t}\n\tbuf := bytes.Buffer{}\n\tisLetter := false\n\tfor _, char := range argsList.Front().Value.(formulaArg).Value() {\n\t\tif !isLetter && unicode.IsLetter(char) {\n\t\t\tbuf.WriteRune(unicode.ToUpper(char))\n\t\t} else {\n\t\t\tbuf.WriteRune(unicode.ToLower(char))\n\t\t}\n\t\tisLetter = unicode.IsLetter(char)\n\t}\n\treturn newStringFormulaArg(buf.String())\n}\n\n// REPLACE function replaces all or part of a text string with another string.\n// The syntax of the function is:\n//\n//\tREPLACE(old_text,start_num,num_chars,new_text)\nfunc (fn *formulaFuncs) REPLACE(argsList *list.List) formulaArg {\n\treturn fn.replace(\"REPLACE\", argsList)\n}\n\n// REPLACEB replaces part of a text string, based on the number of bytes you\n// specify, with a different text string.\n//\n//\tREPLACEB(old_text,start_num,num_chars,new_text)\nfunc (fn *formulaFuncs) REPLACEB(argsList *list.List) formulaArg {\n\treturn fn.replace(\"REPLACEB\", argsList)\n}\n\n// replace is an implementation of the formula functions REPLACE and REPLACEB.\n// TODO: support DBCS include Japanese, Chinese (Simplified), Chinese\n// (Traditional), and Korean.\nfunc (fn *formulaFuncs) replace(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 4 arguments\", name))\n\t}\n\tsourceText, targetText := argsList.Front().Value.(formulaArg).Value(), argsList.Back().Value.(formulaArg).Value()\n\tstartNumArg, numCharsArg := argsList.Front().Next().Value.(formulaArg).ToNumber(), argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif startNumArg.Type != ArgNumber {\n\t\treturn startNumArg\n\t}\n\tif numCharsArg.Type != ArgNumber {\n\t\treturn numCharsArg\n\t}\n\tsourceTextLen, startIdx := len(sourceText), int(startNumArg.Number)\n\tif startIdx > sourceTextLen {\n\t\tstartIdx = sourceTextLen + 1\n\t}\n\tendIdx := startIdx + int(numCharsArg.Number)\n\tif endIdx > sourceTextLen {\n\t\tendIdx = sourceTextLen + 1\n\t}\n\tif startIdx < 1 || endIdx < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tresult := sourceText[:startIdx-1] + targetText + sourceText[endIdx-1:]\n\treturn newStringFormulaArg(result)\n}\n\n// REPT function returns a supplied text string, repeated a specified number\n// of times. The syntax of the function is:\n//\n//\tREPT(text,number_times)\nfunc (fn *formulaFuncs) REPT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"REPT requires 2 arguments\")\n\t}\n\ttext := argsList.Front().Value.(formulaArg)\n\tif text.Type != ArgString {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"REPT requires first argument to be a string\")\n\t}\n\ttimes := argsList.Back().Value.(formulaArg).ToNumber()\n\tif times.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"REPT requires second argument to be a number\")\n\t}\n\tif times.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"REPT requires second argument to be >= 0\")\n\t}\n\tif times.Number == 0 {\n\t\treturn newStringFormulaArg(\"\")\n\t}\n\tbuf := bytes.Buffer{}\n\tfor i := 0; i < int(times.Number); i++ {\n\t\tbuf.WriteString(text.Value())\n\t}\n\treturn newStringFormulaArg(buf.String())\n}\n\n// RIGHT function returns a specified number of characters from the end of a\n// supplied text string. The syntax of the function is:\n//\n//\tRIGHT(text,[num_chars])\nfunc (fn *formulaFuncs) RIGHT(argsList *list.List) formulaArg {\n\treturn fn.leftRight(\"RIGHT\", argsList)\n}\n\n// RIGHTB returns the last character or characters in a text string, based on\n// the number of bytes you specify. The syntax of the function is:\n//\n//\tRIGHTB(text,[num_bytes])\nfunc (fn *formulaFuncs) RIGHTB(argsList *list.List) formulaArg {\n\treturn fn.leftRight(\"RIGHTB\", argsList)\n}\n\n// SEARCH function returns the position of a specified character or sub-string\n// within a supplied text string. The syntax of the function is:\n//\n//\tSEARCH(search_text,within_text,[start_num])\nfunc (fn *formulaFuncs) SEARCH(argsList *list.List) formulaArg {\n\treturn fn.find(\"SEARCH\", argsList)\n}\n\n// SEARCHB functions locate one text string within a second text string, and\n// return the number of the starting position of the first text string from the\n// first character of the second text string. The syntax of the function is:\n//\n//\tSEARCHB(search_text,within_text,[start_num])\nfunc (fn *formulaFuncs) SEARCHB(argsList *list.List) formulaArg {\n\treturn fn.find(\"SEARCHB\", argsList)\n}\n\n// SUBSTITUTE function replaces one or more instances of a given text string,\n// within an original text string. The syntax of the function is:\n//\n//\tSUBSTITUTE(text,old_text,new_text,[instance_num])\nfunc (fn *formulaFuncs) SUBSTITUTE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 && argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SUBSTITUTE requires 3 or 4 arguments\")\n\t}\n\ttext, sourceText := argsList.Front().Value.(formulaArg), argsList.Front().Next().Value.(formulaArg)\n\ttargetText, instanceNum := argsList.Front().Next().Next().Value.(formulaArg), 0\n\tif argsList.Len() == 3 {\n\t\treturn newStringFormulaArg(strings.ReplaceAll(text.Value(), sourceText.Value(), targetText.Value()))\n\t}\n\tinstanceNumArg := argsList.Back().Value.(formulaArg).ToNumber()\n\tif instanceNumArg.Type != ArgNumber {\n\t\treturn instanceNumArg\n\t}\n\tinstanceNum = int(instanceNumArg.Number)\n\tif instanceNum < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"instance_num should be > 0\")\n\t}\n\tstr, sourceTextLen, count, chars, pos := text.Value(), len(sourceText.Value()), instanceNum, 0, -1\n\tfor {\n\t\tcount--\n\t\tindex := strings.Index(str, sourceText.Value())\n\t\tif index == -1 {\n\t\t\tpos = -1\n\t\t\tbreak\n\t\t} else {\n\t\t\tpos = index + chars\n\t\t\tif count == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tidx := sourceTextLen + index\n\t\t\tchars += idx\n\t\t\tstr = str[idx:]\n\t\t}\n\t}\n\tif pos == -1 {\n\t\treturn newStringFormulaArg(text.Value())\n\t}\n\tpre, post := text.Value()[:pos], text.Value()[pos+sourceTextLen:]\n\treturn newStringFormulaArg(pre + targetText.Value() + post)\n}\n\n// TEXT function converts a supplied numeric value into text, in a\n// user-specified format. The syntax of the function is:\n//\n//\tTEXT(value,format_text)\nfunc (fn *formulaFuncs) TEXT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TEXT requires 2 arguments\")\n\t}\n\tvalue, fmtText := argsList.Front().Value.(formulaArg), argsList.Back().Value.(formulaArg)\n\tif value.Type == ArgError {\n\t\treturn value\n\t}\n\tif fmtText.Type == ArgError {\n\t\treturn fmtText\n\t}\n\tcellType := CellTypeNumber\n\tif num := value.ToNumber(); num.Type != ArgNumber {\n\t\tcellType = CellTypeSharedString\n\t}\n\treturn newStringFormulaArg(format(value.Value(), fmtText.Value(), false, cellType, nil))\n}\n\n// prepareTextAfterBefore checking and prepare arguments for the formula\n// functions TEXTAFTER and TEXTBEFORE.\nfunc (fn *formulaFuncs) prepareTextAfterBefore(name string, argsList *list.List) formulaArg {\n\targsLen := argsList.Len()\n\tif argsLen < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 2 arguments\", name))\n\t}\n\tif argsLen > 6 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s accepts at most 6 arguments\", name))\n\t}\n\ttext, delimiter := argsList.Front().Value.(formulaArg), argsList.Front().Next().Value.(formulaArg)\n\tinstanceNum, matchMode, matchEnd, ifNotFound := newNumberFormulaArg(1), newBoolFormulaArg(false), newBoolFormulaArg(false), newEmptyFormulaArg()\n\tif argsLen > 2 {\n\t\tinstanceNum = argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\t\tif instanceNum.Type != ArgNumber {\n\t\t\treturn instanceNum\n\t\t}\n\t}\n\tif argsLen > 3 {\n\t\tmatchMode = argsList.Front().Next().Next().Next().Value.(formulaArg).ToBool()\n\t\tif matchMode.Type != ArgNumber {\n\t\t\treturn matchMode\n\t\t}\n\t\tif matchMode.Number == 1 {\n\t\t\ttext, delimiter = newStringFormulaArg(strings.ToLower(text.Value())), newStringFormulaArg(strings.ToLower(delimiter.Value()))\n\t\t}\n\t}\n\tif argsLen > 4 {\n\t\tmatchEnd = argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToBool()\n\t\tif matchEnd.Type != ArgNumber {\n\t\t\treturn matchEnd\n\t\t}\n\t}\n\tif argsLen > 5 {\n\t\tifNotFound = argsList.Back().Value.(formulaArg)\n\t}\n\tif text.Value() == \"\" {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tlenArgsList := list.New().Init()\n\tlenArgsList.PushBack(text)\n\ttextLen := fn.LEN(lenArgsList)\n\tif instanceNum.Number == 0 || instanceNum.Number > textLen.Number {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\treverseSearch, startPos := instanceNum.Number < 0, 0.0\n\tif reverseSearch {\n\t\tstartPos = textLen.Number\n\t}\n\treturn newListFormulaArg([]formulaArg{\n\t\ttext, delimiter, instanceNum, matchMode, matchEnd, ifNotFound,\n\t\ttextLen, newBoolFormulaArg(reverseSearch), newNumberFormulaArg(startPos),\n\t})\n}\n\n// textAfterBeforeSearch is an implementation of the formula functions TEXTAFTER\n// and TEXTBEFORE.\nfunc textAfterBeforeSearch(text string, delimiter []string, startPos int, reverseSearch bool) (int, string) {\n\tidx := -1\n\tvar modifiedDelimiter string\n\tfor i := 0; i < len(delimiter); i++ {\n\t\tnextDelimiter := delimiter[i]\n\t\tnextIdx := strings.Index(text[startPos:], nextDelimiter)\n\t\tif nextIdx != -1 {\n\t\t\tnextIdx += startPos\n\t\t}\n\t\tif reverseSearch {\n\t\t\tnextIdx = strings.LastIndex(text[:startPos], nextDelimiter)\n\t\t}\n\t\tif idx == -1 || (((nextIdx < idx && !reverseSearch) || (nextIdx > idx && reverseSearch)) && idx != -1) {\n\t\t\tidx = nextIdx\n\t\t\tmodifiedDelimiter = nextDelimiter\n\t\t}\n\t}\n\treturn idx, modifiedDelimiter\n}\n\n// textAfterBeforeResult is an implementation of the formula functions TEXTAFTER\n// and TEXTBEFORE.\nfunc textAfterBeforeResult(name, modifiedDelimiter string, text []rune, foundIdx, repeatZero, textLen int, matchEndActive, matchEnd, reverseSearch bool) formulaArg {\n\tif name == \"TEXTAFTER\" {\n\t\tendPos := len(modifiedDelimiter)\n\t\tif (repeatZero > 1 || matchEndActive) && matchEnd && reverseSearch {\n\t\t\tendPos = 0\n\t\t}\n\t\tif foundIdx+endPos >= textLen {\n\t\t\treturn newEmptyFormulaArg()\n\t\t}\n\t\treturn newStringFormulaArg(string(text[foundIdx+endPos : textLen]))\n\t}\n\treturn newStringFormulaArg(string(text[:foundIdx]))\n}\n\n// textAfterBefore is an implementation of the formula functions TEXTAFTER and\n// TEXTBEFORE.\nfunc (fn *formulaFuncs) textAfterBefore(name string, argsList *list.List) formulaArg {\n\targs := fn.prepareTextAfterBefore(name, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tvar (\n\t\ttext                 = []rune(argsList.Front().Value.(formulaArg).Value())\n\t\tmodifiedText         = args.List[0].Value()\n\t\tdelimiter            = []string{args.List[1].Value()}\n\t\tinstanceNum          = args.List[2].Number\n\t\tmatchEnd             = args.List[4].Number == 1\n\t\tifNotFound           = args.List[5]\n\t\ttextLen              = args.List[6]\n\t\treverseSearch        = args.List[7].Number == 1\n\t\tfoundIdx             = -1\n\t\trepeatZero, startPos int\n\t\tmatchEndActive       bool\n\t\tmodifiedDelimiter    string\n\t)\n\tif reverseSearch {\n\t\tstartPos = int(args.List[8].Number)\n\t}\n\tfor i := 0; i < int(math.Abs(instanceNum)); i++ {\n\t\tfoundIdx, modifiedDelimiter = textAfterBeforeSearch(modifiedText, delimiter, startPos, reverseSearch)\n\t\tif foundIdx == 0 {\n\t\t\trepeatZero++\n\t\t}\n\t\tif foundIdx == -1 {\n\t\t\tif matchEnd && i == int(math.Abs(instanceNum))-1 {\n\t\t\t\tif foundIdx = int(textLen.Number); reverseSearch {\n\t\t\t\t\tfoundIdx = 0\n\t\t\t\t}\n\t\t\t\tmatchEndActive = true\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tif startPos = foundIdx + len(modifiedDelimiter); reverseSearch {\n\t\t\tstartPos = foundIdx - len(modifiedDelimiter)\n\t\t}\n\t}\n\tif foundIdx == -1 {\n\t\treturn ifNotFound\n\t}\n\treturn textAfterBeforeResult(name, modifiedDelimiter, text, foundIdx, repeatZero, int(textLen.Number), matchEndActive, matchEnd, reverseSearch)\n}\n\n// TEXTAFTER function returns the text that occurs after a given substring or\n// delimiter. The syntax of the function is:\n//\n//\tTEXTAFTER(text,delimiter,[instance_num],[match_mode],[match_end],[if_not_found])\nfunc (fn *formulaFuncs) TEXTAFTER(argsList *list.List) formulaArg {\n\treturn fn.textAfterBefore(\"TEXTAFTER\", argsList)\n}\n\n// TEXTBEFORE function returns text that occurs before a given character or\n// string. The syntax of the function is:\n//\n//\tTEXTBEFORE(text,delimiter,[instance_num],[match_mode],[match_end],[if_not_found])\nfunc (fn *formulaFuncs) TEXTBEFORE(argsList *list.List) formulaArg {\n\treturn fn.textAfterBefore(\"TEXTBEFORE\", argsList)\n}\n\n// TEXTJOIN function joins together a series of supplied text strings into one\n// combined text string. The user can specify a delimiter to add between the\n// individual text items, if required. The syntax of the function is:\n//\n//\tTEXTJOIN([delimiter],[ignore_empty],text1,[text2],...)\nfunc (fn *formulaFuncs) TEXTJOIN(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TEXTJOIN requires at least 3 arguments\")\n\t}\n\tif argsList.Len() > 252 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TEXTJOIN accepts at most 252 arguments\")\n\t}\n\tdelimiter := argsList.Front().Value.(formulaArg)\n\tignoreEmpty := argsList.Front().Next().Value.(formulaArg)\n\tif ignoreEmpty.Type != ArgNumber || !ignoreEmpty.Boolean {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\targs, ok := textJoin(argsList.Front().Next().Next(), []string{}, ignoreEmpty.Number != 0)\n\tif ok.Type != ArgNumber {\n\t\treturn ok\n\t}\n\tresult := strings.Join(args, delimiter.Value())\n\tif countUTF16String(result) > TotalCellChars {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"TEXTJOIN function exceeds %d characters\", TotalCellChars))\n\t}\n\treturn newStringFormulaArg(result)\n}\n\n// textJoin is an implementation of the formula function TEXTJOIN.\nfunc textJoin(arg *list.Element, arr []string, ignoreEmpty bool) ([]string, formulaArg) {\n\tfor arg.Next(); arg != nil; arg = arg.Next() {\n\t\tswitch arg.Value.(formulaArg).Type {\n\t\tcase ArgError:\n\t\t\treturn arr, arg.Value.(formulaArg)\n\t\tcase ArgString, ArgEmpty:\n\t\t\tval := arg.Value.(formulaArg).Value()\n\t\t\tif val != \"\" || !ignoreEmpty {\n\t\t\t\tarr = append(arr, val)\n\t\t\t}\n\t\tcase ArgNumber:\n\t\t\tarr = append(arr, arg.Value.(formulaArg).Value())\n\t\tcase ArgMatrix:\n\t\t\tfor _, row := range arg.Value.(formulaArg).Matrix {\n\t\t\t\targList := list.New().Init()\n\t\t\t\tfor _, ele := range row {\n\t\t\t\t\targList.PushBack(ele)\n\t\t\t\t}\n\t\t\t\tif argList.Len() > 0 {\n\t\t\t\t\targs, _ := textJoin(argList.Front(), []string{}, ignoreEmpty)\n\t\t\t\t\tarr = append(arr, args...)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn arr, newBoolFormulaArg(true)\n}\n\n// TRIM removes extra spaces (i.e. all spaces except for single spaces between\n// words or characters) from a supplied text string. The syntax of the\n// function is:\n//\n//\tTRIM(text)\nfunc (fn *formulaFuncs) TRIM(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TRIM requires 1 argument\")\n\t}\n\treturn newStringFormulaArg(strings.TrimSpace(argsList.Front().Value.(formulaArg).Value()))\n}\n\n// UNICHAR returns the Unicode character that is referenced by the given\n// numeric value. The syntax of the function is:\n//\n//\tUNICHAR(number)\nfunc (fn *formulaFuncs) UNICHAR(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"UNICHAR requires 1 argument\")\n\t}\n\tnumArg := argsList.Front().Value.(formulaArg).ToNumber()\n\tif numArg.Type != ArgNumber {\n\t\treturn numArg\n\t}\n\tif numArg.Number <= 0 || numArg.Number > 55295 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\treturn newStringFormulaArg(string(rune(numArg.Number)))\n}\n\n// UNICODE function returns the code point for the first character of a\n// supplied text string. The syntax of the function is:\n//\n//\tUNICODE(text)\nfunc (fn *formulaFuncs) UNICODE(argsList *list.List) formulaArg {\n\treturn fn.code(\"UNICODE\", argsList)\n}\n\n// UNIQUE function returns a list of unique values in a list or range. The\n// syntax of the function is:\n//\n//\tUNIQUE(array,[by_col],[exactly_once])\nfunc (fn *formulaFuncs) UNIQUE(argsList *list.List) formulaArg {\n\targs, errArg := getFormulaUniqueArgs(argsList)\n\tif errArg != nil {\n\t\treturn *errArg\n\t}\n\tif args.byColumn {\n\t\targs.cellRange, args.cols, args.rows = transposeFormulaArgsList(args.cellRange, args.cols, args.rows)\n\t}\n\tcounts := map[string]int{}\n\tfor i := 0; i < len(args.cellRange); i += args.cols {\n\t\tkey := concatValues(args.cellRange[i : i+args.cols])\n\n\t\tif _, ok := counts[key]; !ok {\n\t\t\tcounts[key] = 0\n\t\t}\n\t\tcounts[key]++\n\t}\n\tuniqueAxes := [][]formulaArg{}\n\tfor i := 0; i < len(args.cellRange); i += args.cols {\n\t\tkey := concatValues(args.cellRange[i : i+args.cols])\n\n\t\tif (args.exactlyOnce && counts[key] == 1) || (!args.exactlyOnce && counts[key] >= 1) {\n\t\t\tuniqueAxes = append(uniqueAxes, args.cellRange[i:i+args.cols])\n\t\t}\n\t\tdelete(counts, key)\n\t}\n\tif args.byColumn {\n\t\tuniqueAxes = transposeFormulaArgsMatrix(uniqueAxes)\n\t}\n\treturn newMatrixFormulaArg(uniqueAxes)\n}\n\n// transposeFormulaArgsMatrix transposes a 2D slice of formulaArg.\nfunc transposeFormulaArgsMatrix(args [][]formulaArg) [][]formulaArg {\n\tif len(args) == 0 {\n\t\treturn args\n\t}\n\ttransposedArgs := make([][]formulaArg, len(args[0]))\n\tfor i := 0; i < len(args[0]); i++ {\n\t\ttransposedArgs[i] = make([]formulaArg, len(args))\n\t}\n\tfor i := 0; i < len(args); i++ {\n\t\tfor j := 0; j < len(args[i]); j++ {\n\t\t\ttransposedArgs[j][i] = args[i][j]\n\t\t}\n\t}\n\treturn transposedArgs\n}\n\n// transposeFormulaArgsList transposes a flat slice of formulaArg given the\n// number of columns and rows.\nfunc transposeFormulaArgsList(args []formulaArg, cols, rows int) ([]formulaArg, int, int) {\n\ttransposedArgs := make([]formulaArg, len(args))\n\tfor i := 0; i < rows; i++ {\n\t\tfor j := 0; j < cols; j++ {\n\t\t\ttransposedArgs[j*rows+i] = args[i*cols+j]\n\t\t}\n\t}\n\treturn transposedArgs, rows, cols\n}\n\n// concatValues concatenates the values of a slice of formulaArg into a single\n// string.\nfunc concatValues(args []formulaArg) string {\n\tval := \"\"\n\tfor _, arg := range args {\n\t\t// Call to Value is cheap.\n\t\tval += arg.Value()\n\t}\n\treturn val\n}\n\n// uniqueArgs holds the parsed arguments for the UNIQUE function.\ntype uniqueArgs struct {\n\tcellRange   []formulaArg\n\tcols        int\n\trows        int\n\tbyColumn    bool\n\texactlyOnce bool\n}\n\n// getFormulaUniqueArgs parses and validates the arguments for the UNIQUE\n// function.\nfunc getFormulaUniqueArgs(argsList *list.List) (uniqueArgs, *formulaArg) {\n\tres := uniqueArgs{}\n\targsLen := argsList.Len()\n\tif argsLen == 0 {\n\t\terrArg := newErrorFormulaArg(formulaErrorVALUE, \"UNIQUE requires at least 1 argument\")\n\t\treturn res, &errArg\n\t}\n\tif argsLen > 3 {\n\t\tmsg := fmt.Sprintf(\"UNIQUE takes at most 3 arguments, received %d arguments\", argsLen)\n\t\terrArg := newErrorFormulaArg(formulaErrorVALUE, msg)\n\n\t\treturn res, &errArg\n\t}\n\tfirstArg := argsList.Front()\n\tres.cellRange = firstArg.Value.(formulaArg).ToList()\n\tif len(res.cellRange) == 0 {\n\t\terrArg := newErrorFormulaArg(formulaErrorVALUE, \"missing first argument to UNIQUE\")\n\t\treturn res, &errArg\n\t}\n\tif res.cellRange[0].Type == ArgError {\n\t\treturn res, &res.cellRange[0]\n\t}\n\trmin, rmax := calcColsRowsMinMax(false, argsList)\n\tcmin, cmax := calcColsRowsMinMax(true, argsList)\n\tres.cols, res.rows = cmax-cmin+1, rmax-rmin+1\n\tsecondArg := firstArg.Next()\n\tif secondArg == nil {\n\t\treturn res, nil\n\t}\n\targByColumn := secondArg.Value.(formulaArg).ToBool()\n\tif argByColumn.Type == ArgError {\n\t\treturn res, &argByColumn\n\t}\n\tres.byColumn = (argByColumn.Value() == \"TRUE\")\n\tthirdArg := secondArg.Next()\n\tif thirdArg == nil {\n\t\treturn res, nil\n\t}\n\targExactlyOnce := thirdArg.Value.(formulaArg).ToBool()\n\tif argExactlyOnce.Type == ArgError {\n\t\treturn res, &argExactlyOnce\n\t}\n\tres.exactlyOnce = (argExactlyOnce.Value() == \"TRUE\")\n\treturn res, nil\n}\n\n// UPPER converts all characters in a supplied text string to upper case. The\n// syntax of the function is:\n//\n//\tUPPER(text)\nfunc (fn *formulaFuncs) UPPER(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"UPPER requires 1 argument\")\n\t}\n\treturn newStringFormulaArg(strings.ToUpper(argsList.Front().Value.(formulaArg).Value()))\n}\n\n// VALUE function converts a text string into a numeric value. The syntax of\n// the function is:\n//\n//\tVALUE(text)\nfunc (fn *formulaFuncs) VALUE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"VALUE requires 1 argument\")\n\t}\n\ttext := strings.ReplaceAll(argsList.Front().Value.(formulaArg).Value(), \",\", \"\")\n\tpercent := 1.0\n\tif strings.HasSuffix(text, \"%\") {\n\t\tpercent, text = 0.01, strings.TrimSuffix(text, \"%\")\n\t}\n\tdecimal := big.Float{}\n\tif _, ok := decimal.SetString(text); ok {\n\t\tvalue, _ := decimal.Float64()\n\t\treturn newNumberFormulaArg(value * percent)\n\t}\n\tdateValue, timeValue, errTime := 0.0, 0.0, false\n\tif !isDateOnlyFmt(text) {\n\t\th, m, s, _, _, err := strToTime(text)\n\t\terrTime = err.Type == ArgError\n\t\tif !errTime {\n\t\t\ttimeValue = (float64(h)*3600 + float64(m)*60 + s) / 86400\n\t\t}\n\t}\n\ty, m, d, _, err := strToDate(text)\n\terrDate := err.Type == ArgError\n\tif !errDate {\n\t\tdateValue = daysBetween(excelMinTime1900.Unix(), makeDate(y, time.Month(m), d)) + 1\n\t}\n\tif errTime && errDate {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\treturn newNumberFormulaArg(dateValue + timeValue)\n}\n\n// VALUETOTEXT function returns text from any specified value. It passes text\n// values unchanged, and converts non-text values to text.\n//\n//\tVALUETOTEXT(value,[format])\nfunc (fn *formulaFuncs) VALUETOTEXT(argsList *list.List) formulaArg {\n\tformat := prepareToText(\"VALUETOTEXT\", argsList)\n\tif format.Type != ArgNumber {\n\t\treturn format\n\t}\n\tcell := argsList.Front().Value.(formulaArg)\n\tif num := cell.ToNumber(); num.Type != ArgNumber && format.Number == 1 {\n\t\treturn newStringFormulaArg(fmt.Sprintf(\"\\\"%s\\\"\", cell.Value()))\n\t}\n\treturn newStringFormulaArg(cell.Value())\n}\n\n// Conditional Functions\n\n// IF function tests a supplied condition and returns one result if the\n// condition evaluates to TRUE, and another result if the condition evaluates\n// to FALSE. The syntax of the function is:\n//\n//\tIF(logical_test,value_if_true,value_if_false)\nfunc (fn *formulaFuncs) IF(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IF requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IF accepts at most 3 arguments\")\n\t}\n\ttoken := fn.implicitIntersect(argsList.Front().Value.(formulaArg))\n\tvar (\n\t\tcond   bool\n\t\terr    error\n\t\tresult formulaArg\n\t)\n\tswitch token.Type {\n\tcase ArgString:\n\t\tif cond, err = strconv.ParseBool(token.Value()); err != nil {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t\t}\n\tcase ArgNumber:\n\t\tcond = token.Number == 1\n\t}\n\n\tif argsList.Len() == 1 {\n\t\treturn newBoolFormulaArg(cond)\n\t}\n\tif cond {\n\t\tvalue := fn.implicitIntersect(argsList.Front().Next().Value.(formulaArg))\n\t\tswitch value.Type {\n\t\tcase ArgNumber:\n\t\t\tresult = value.ToNumber()\n\t\tdefault:\n\t\t\tresult = newStringFormulaArg(value.Value())\n\t\t}\n\t\treturn result\n\t}\n\tif argsList.Len() == 3 {\n\t\tvalue := fn.implicitIntersect(argsList.Back().Value.(formulaArg))\n\t\tswitch value.Type {\n\t\tcase ArgNumber:\n\t\t\tresult = value.ToNumber()\n\t\tdefault:\n\t\t\tresult = newStringFormulaArg(value.Value())\n\t\t}\n\t}\n\treturn result\n}\n\n// Lookup and Reference Functions\n\n// ADDRESS function takes a row and a column number and returns a cell\n// reference as a text string. The syntax of the function is:\n//\n//\tADDRESS(row_num,column_num,[abs_num],[a1],[sheet_text])\nfunc (fn *formulaFuncs) ADDRESS(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ADDRESS requires at least 2 arguments\")\n\t}\n\tif argsList.Len() > 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ADDRESS requires at most 5 arguments\")\n\t}\n\trowNum := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rowNum.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif rowNum.Number > TotalRows {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tcolNum := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif colNum.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tabsNum := newNumberFormulaArg(1)\n\tif argsList.Len() >= 3 {\n\t\tabsNum = argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\t\tif absNum.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t}\n\tif absNum.Number < 1 || absNum.Number > 4 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\ta1 := newBoolFormulaArg(true)\n\tif argsList.Len() >= 4 {\n\t\ta1 = argsList.Front().Next().Next().Next().Value.(formulaArg).ToBool()\n\t\tif a1.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t}\n\tvar sheetText string\n\tif argsList.Len() == 5 {\n\t\tsheetText = fmt.Sprintf(\"%s!\", argsList.Back().Value.(formulaArg).Value())\n\t}\n\tformatter := addressFmtMaps[fmt.Sprintf(\"%d_%s\", int(absNum.Number), a1.Value())]\n\taddr, err := formatter(int(colNum.Number), int(rowNum.Number))\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\treturn newStringFormulaArg(fmt.Sprintf(\"%s%s\", sheetText, addr))\n}\n\n// ANCHORARRAY function returns the entire spilled range for the dynamic array\n// in cell. The syntax of the function is:\n//\n//\tANCHORARRAY(cell)\nfunc (fn *formulaFuncs) ANCHORARRAY(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ANCHORARRAY requires 1 numeric argument\")\n\t}\n\tws, err := fn.f.workSheetReader(fn.sheet)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t}\n\tref := argsList.Front().Value.(formulaArg).cellRefs.Front().Value.(cellRef)\n\tcell := ws.SheetData.Row[ref.Row-1].C[ref.Col-1]\n\tif cell.F == nil {\n\t\treturn newEmptyFormulaArg()\n\t}\n\tcoordinates, err := rangeRefToCoordinates(cell.F.Ref)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t}\n\t_ = sortCoordinates(coordinates)\n\tvar mtx [][]formulaArg\n\tfor c := coordinates[0]; c <= coordinates[2]; c++ {\n\t\tvar row []formulaArg\n\t\tfor r := coordinates[1]; r <= coordinates[3]; r++ {\n\t\t\tcellName, _ := CoordinatesToCellName(c, r)\n\t\t\tresult, err := fn.f.CalcCellValue(ref.Sheet, cellName, Options{RawCellValue: true})\n\t\t\tif err != nil {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, err.Error())\n\t\t\t}\n\t\t\targ := newStringFormulaArg(result)\n\t\t\tif num := arg.ToNumber(); num.Type == ArgNumber {\n\t\t\t\targ = num\n\t\t\t}\n\t\t\trow = append(row, arg)\n\t\t}\n\t\tmtx = append(mtx, row)\n\t}\n\treturn newMatrixFormulaArg(mtx)\n}\n\n// CHOOSE function returns a value from an array, that corresponds to a\n// supplied index number (position). The syntax of the function is:\n//\n//\tCHOOSE(index_num,value1,[value2],...)\nfunc (fn *formulaFuncs) CHOOSE(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CHOOSE requires 2 arguments\")\n\t}\n\tidx, err := strconv.Atoi(argsList.Front().Value.(formulaArg).Value())\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"CHOOSE requires first argument of type number\")\n\t}\n\tif argsList.Len() <= idx {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"index_num should be <= to the number of values\")\n\t}\n\targ := argsList.Front()\n\tfor i := 0; i < idx; i++ {\n\t\targ = arg.Next()\n\t}\n\treturn arg.Value.(formulaArg)\n}\n\n// matchPatternToRegExp convert find text pattern to regular expression.\nfunc matchPatternToRegExp(findText string, dbcs bool) (string, bool) {\n\tvar (\n\t\texp      string\n\t\twildCard bool\n\t\tmark     = \".\"\n\t)\n\tif dbcs {\n\t\tmark = \"(?:(?:[\\\\x00-\\\\x0081])|(?:[\\\\xFF61-\\\\xFFA0])|(?:[\\\\xF8F1-\\\\xF8F4])|[0-9A-Za-z])\"\n\t}\n\tfor _, char := range findText {\n\t\tif strings.ContainsAny(string(char), \".+$^[](){}|/\") {\n\t\t\texp += fmt.Sprintf(\"\\\\%s\", string(char))\n\t\t\tcontinue\n\t\t}\n\t\tif char == '?' {\n\t\t\twildCard = true\n\t\t\texp += mark\n\t\t\tcontinue\n\t\t}\n\t\tif char == '*' {\n\t\t\twildCard = true\n\t\t\texp += \".*\"\n\t\t\tcontinue\n\t\t}\n\t\texp += string(char)\n\t}\n\treturn fmt.Sprintf(\"^%s\", exp), wildCard\n}\n\n// matchPattern finds whether the text matches or satisfies the pattern\n// string. The pattern supports '*' and '?' wildcards in the pattern string.\nfunc matchPattern(findText, withinText string, dbcs bool, startNum int) (int, bool) {\n\texp, wildCard := matchPatternToRegExp(findText, dbcs)\n\toffset := 1\n\tfor idx := range withinText {\n\t\tif offset < startNum {\n\t\t\toffset++\n\t\t\tcontinue\n\t\t}\n\t\tif wildCard {\n\t\t\tif ok, _ := regexp.MatchString(exp, withinText[idx:]); ok {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif strings.Index(withinText[idx:], findText) == 0 {\n\t\t\tbreak\n\t\t}\n\t\toffset++\n\t}\n\treturn offset, countUTF16String(withinText) != offset-1\n}\n\n// compareFormulaArg compares the left-hand sides and the right-hand sides'\n// formula arguments by given conditions such as case-sensitive, if exact\n// match, and make compare result as formula criteria condition type.\nfunc compareFormulaArg(lhs, rhs, matchMode formulaArg, caseSensitive bool) byte {\n\tif lhs.Type != rhs.Type {\n\t\treturn criteriaNe\n\t}\n\tswitch lhs.Type {\n\tcase ArgNumber:\n\t\tif lhs.Number == rhs.Number {\n\t\t\treturn criteriaEq\n\t\t}\n\t\tif lhs.Number < rhs.Number {\n\t\t\treturn criteriaL\n\t\t}\n\t\treturn criteriaG\n\tcase ArgString:\n\t\tls, rs := lhs.Value(), rhs.Value()\n\t\tif !caseSensitive {\n\t\t\tls, rs = strings.ToLower(ls), strings.ToLower(rs)\n\t\t}\n\t\tif matchMode.Number == matchModeWildcard {\n\t\t\tif _, ok := matchPattern(rs, ls, false, 0); ok {\n\t\t\t\treturn criteriaEq\n\t\t\t}\n\t\t}\n\t\treturn map[int]byte{1: criteriaG, -1: criteriaL, 0: criteriaEq}[strings.Compare(ls, rs)]\n\tcase ArgEmpty:\n\t\treturn criteriaEq\n\tcase ArgList:\n\t\treturn compareFormulaArgList(lhs, rhs, matchMode, caseSensitive)\n\tcase ArgMatrix:\n\t\treturn compareFormulaArgMatrix(lhs, rhs, matchMode, caseSensitive)\n\tdefault:\n\t\treturn criteriaErr\n\t}\n}\n\n// compareFormulaArgList compares the left-hand sides and the right-hand sides\n// list type formula arguments.\nfunc compareFormulaArgList(lhs, rhs, matchMode formulaArg, caseSensitive bool) byte {\n\tif len(lhs.List) < len(rhs.List) {\n\t\treturn criteriaL\n\t}\n\tif len(lhs.List) > len(rhs.List) {\n\t\treturn criteriaG\n\t}\n\tfor arg := range lhs.List {\n\t\tcriteria := compareFormulaArg(lhs.List[arg], rhs.List[arg], matchMode, caseSensitive)\n\t\tif criteria != criteriaEq {\n\t\t\treturn criteria\n\t\t}\n\t}\n\treturn criteriaEq\n}\n\n// compareFormulaArgMatrix compares the left-hand sides and the right-hand sides'\n// matrix type formula arguments.\nfunc compareFormulaArgMatrix(lhs, rhs, matchMode formulaArg, caseSensitive bool) byte {\n\tif len(lhs.Matrix) < len(rhs.Matrix) {\n\t\treturn criteriaL\n\t}\n\tif len(lhs.Matrix) > len(rhs.Matrix) {\n\t\treturn criteriaG\n\t}\n\tfor i := range lhs.Matrix {\n\t\tleft, right := lhs.Matrix[i], rhs.Matrix[i]\n\t\tif len(left) < len(right) {\n\t\t\treturn criteriaL\n\t\t}\n\t\tif len(left) > len(right) {\n\t\t\treturn criteriaG\n\t\t}\n\t\tfor arg := range left {\n\t\t\tcriteria := compareFormulaArg(left[arg], right[arg], matchMode, caseSensitive)\n\t\t\tif criteria != criteriaEq {\n\t\t\t\treturn criteria\n\t\t\t}\n\t\t}\n\t}\n\treturn criteriaEq\n}\n\n// COLUMN function returns the first column number within a supplied reference\n// or the number of the current column. The syntax of the function is:\n//\n//\tCOLUMN([reference])\nfunc (fn *formulaFuncs) COLUMN(argsList *list.List) formulaArg {\n\tif argsList.Len() > 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COLUMN requires at most 1 argument\")\n\t}\n\tif argsList.Len() == 1 {\n\t\tif argsList.Front().Value.(formulaArg).cellRanges != nil && argsList.Front().Value.(formulaArg).cellRanges.Len() > 0 {\n\t\t\treturn newNumberFormulaArg(float64(argsList.Front().Value.(formulaArg).cellRanges.Front().Value.(cellRange).From.Col))\n\t\t}\n\t\tif argsList.Front().Value.(formulaArg).cellRefs != nil && argsList.Front().Value.(formulaArg).cellRefs.Len() > 0 {\n\t\t\treturn newNumberFormulaArg(float64(argsList.Front().Value.(formulaArg).cellRefs.Front().Value.(cellRef).Col))\n\t\t}\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"invalid reference\")\n\t}\n\tcol, _, _ := CellNameToCoordinates(fn.cell)\n\treturn newNumberFormulaArg(float64(col))\n}\n\n// calcColsRowsMinMax calculation min and max value for given formula arguments\n// sequence of the formula functions COLUMNS and ROWS.\nfunc calcColsRowsMinMax(cols bool, argsList *list.List) (minVal, maxVal int) {\n\tgetVal := func(cols bool, cell cellRef) int {\n\t\tif cols {\n\t\t\treturn cell.Col\n\t\t}\n\t\treturn cell.Row\n\t}\n\tif argsList.Front().Value.(formulaArg).cellRanges != nil && argsList.Front().Value.(formulaArg).cellRanges.Len() > 0 {\n\t\tcrs := argsList.Front().Value.(formulaArg).cellRanges\n\t\tfor cr := crs.Front(); cr != nil; cr = cr.Next() {\n\t\t\tif minVal == 0 {\n\t\t\t\tminVal = getVal(cols, cr.Value.(cellRange).From)\n\t\t\t}\n\t\t\tif maxVal < getVal(cols, cr.Value.(cellRange).To) {\n\t\t\t\tmaxVal = getVal(cols, cr.Value.(cellRange).To)\n\t\t\t}\n\t\t}\n\t}\n\tif argsList.Front().Value.(formulaArg).cellRefs != nil && argsList.Front().Value.(formulaArg).cellRefs.Len() > 0 {\n\t\tcr := argsList.Front().Value.(formulaArg).cellRefs\n\t\tfor refs := cr.Front(); refs != nil; refs = refs.Next() {\n\t\t\tif minVal == 0 {\n\t\t\t\tminVal = getVal(cols, refs.Value.(cellRef))\n\t\t\t}\n\t\t\tif maxVal < getVal(cols, refs.Value.(cellRef)) {\n\t\t\t\tmaxVal = getVal(cols, refs.Value.(cellRef))\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// COLUMNS function receives an Excel range and returns the number of columns\n// that are contained within the range. The syntax of the function is:\n//\n//\tCOLUMNS(array)\nfunc (fn *formulaFuncs) COLUMNS(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"COLUMNS requires 1 argument\")\n\t}\n\tminVal, maxVal := calcColsRowsMinMax(true, argsList)\n\tif maxVal == MaxColumns {\n\t\treturn newNumberFormulaArg(float64(MaxColumns))\n\t}\n\tresult := maxVal - minVal + 1\n\tif maxVal == minVal {\n\t\tif minVal == 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"invalid reference\")\n\t\t}\n\t\treturn newNumberFormulaArg(float64(1))\n\t}\n\treturn newNumberFormulaArg(float64(result))\n}\n\n// FORMULATEXT function returns a formula as a text string. The syntax of the\n// function is:\n//\n//\tFORMULATEXT(reference)\nfunc (fn *formulaFuncs) FORMULATEXT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FORMULATEXT requires 1 argument\")\n\t}\n\trefs := argsList.Front().Value.(formulaArg).cellRefs\n\tcol, row := 0, 0\n\tif refs != nil && refs.Len() > 0 {\n\t\tcol, row = refs.Front().Value.(cellRef).Col, refs.Front().Value.(cellRef).Row\n\t}\n\tranges := argsList.Front().Value.(formulaArg).cellRanges\n\tif ranges != nil && ranges.Len() > 0 {\n\t\tcol, row = ranges.Front().Value.(cellRange).From.Col, ranges.Front().Value.(cellRange).From.Row\n\t}\n\tcell, err := CoordinatesToCellName(col, row)\n\tif err != nil {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tformula, _ := fn.f.GetCellFormula(fn.sheet, cell)\n\treturn newStringFormulaArg(formula)\n}\n\n// checkHVLookupArgs checking arguments, prepare extract mode, lookup value,\n// and data for the formula functions HLOOKUP and VLOOKUP.\nfunc checkHVLookupArgs(name string, argsList *list.List) (idx int, lookupValue, tableArray, matchMode, errArg formulaArg) {\n\tunit := map[string]string{\n\t\t\"HLOOKUP\": \"row\",\n\t\t\"VLOOKUP\": \"col\",\n\t}[name]\n\tif argsList.Len() < 3 {\n\t\terrArg = newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 3 arguments\", name))\n\t\treturn\n\t}\n\tif argsList.Len() > 4 {\n\t\terrArg = newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at most 4 arguments\", name))\n\t\treturn\n\t}\n\tlookupValue = argsList.Front().Value.(formulaArg)\n\ttableArray = argsList.Front().Next().Value.(formulaArg)\n\tif tableArray.Type != ArgMatrix {\n\t\terrArg = newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires second argument of table array\", name))\n\t\treturn\n\t}\n\targ := argsList.Front().Next().Next().Value.(formulaArg)\n\tif arg.Type != ArgNumber || arg.Boolean {\n\t\terrArg = newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires numeric %s argument\", name, unit))\n\t\treturn\n\t}\n\tidx, matchMode = int(arg.Number)-1, newNumberFormulaArg(matchModeMaxLess)\n\tif argsList.Len() == 4 {\n\t\trangeLookup := argsList.Back().Value.(formulaArg).ToBool()\n\t\tif rangeLookup.Type == ArgError {\n\t\t\terrArg = rangeLookup\n\t\t\treturn\n\t\t}\n\t\tif rangeLookup.Number == 0 {\n\t\t\tmatchMode = newNumberFormulaArg(matchModeWildcard)\n\t\t}\n\t}\n\treturn\n}\n\n// HLOOKUP function 'looks up' a given value in the top row of a data array\n// (or table), and returns the corresponding value from another row of the\n// array. The syntax of the function is:\n//\n//\tHLOOKUP(lookup_value,table_array,row_index_num,[range_lookup])\nfunc (fn *formulaFuncs) HLOOKUP(argsList *list.List) formulaArg {\n\trowIdx, lookupValue, tableArray, matchMode, errArg := checkHVLookupArgs(\"HLOOKUP\", argsList)\n\tif errArg.Type == ArgError {\n\t\treturn errArg\n\t}\n\tvar matchIdx int\n\tvar wasExact bool\n\tif matchMode.Number == matchModeWildcard || len(tableArray.Matrix) == TotalRows {\n\t\tmatchIdx, wasExact = lookupLinearSearch(false, lookupValue, tableArray, matchMode, newNumberFormulaArg(searchModeLinear))\n\t} else {\n\t\tmatchIdx, wasExact = lookupBinarySearch(false, lookupValue, tableArray, matchMode, newNumberFormulaArg(searchModeAscBinary))\n\t}\n\tif matchIdx == -1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, \"HLOOKUP no result found\")\n\t}\n\tif rowIdx < 0 || rowIdx >= len(tableArray.Matrix) {\n\t\treturn newErrorFormulaArg(formulaErrorNA, \"HLOOKUP has invalid row index\")\n\t}\n\trow := tableArray.Matrix[rowIdx]\n\tif wasExact || matchMode.Number == matchModeWildcard {\n\t\treturn row[matchIdx]\n\t}\n\treturn newErrorFormulaArg(formulaErrorNA, \"HLOOKUP no result found\")\n}\n\n// HYPERLINK function creates a hyperlink to a specified location. The syntax\n// of the function is:\n//\n//\tHYPERLINK(link_location,[friendly_name])\nfunc (fn *formulaFuncs) HYPERLINK(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"HYPERLINK requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"HYPERLINK allows at most 2 arguments\")\n\t}\n\treturn newStringFormulaArg(argsList.Back().Value.(formulaArg).Value())\n}\n\n// calcMatch returns the position of the value by given match type, criteria\n// and lookup array for the formula function MATCH.\n// matchType only contains -1, and 1.\nfunc calcMatchMatrix(vertical bool, matchType int, criteria *formulaCriteria, lookupArray [][]formulaArg) formulaArg {\n\tidx := -1\n\tcalc := func(i int, arg formulaArg) bool {\n\t\tswitch matchType {\n\t\tcase -1:\n\t\t\tif ok, _ := formulaCriteriaEval(arg, &formulaCriteria{\n\t\t\t\tType: criteriaGe, Condition: criteria.Condition,\n\t\t\t}); ok {\n\t\t\t\tidx = i\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif criteria.Condition.Type == ArgNumber {\n\t\t\t\treturn true\n\t\t\t}\n\t\tcase 1:\n\t\t\tif ok, _ := formulaCriteriaEval(arg, &formulaCriteria{\n\t\t\t\tType: criteriaLe, Condition: criteria.Condition,\n\t\t\t}); ok {\n\t\t\t\tidx = i\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif criteria.Condition.Type == ArgNumber {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\tif vertical {\n\t\tfor i, row := range lookupArray {\n\t\t\tif ok := calc(i, row[0]); ok {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor i, cell := range lookupArray[0] {\n\t\t\tif ok := calc(i, cell); ok {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tif idx == -1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\treturn newNumberFormulaArg(float64(idx + 1))\n}\n\n// calcMatch returns the position of the value by given match type, criteria\n// and lookup array for the formula function MATCH.\nfunc calcMatch(matchType int, criteria *formulaCriteria, lookupArray []formulaArg) formulaArg {\n\tidx := -1\n\tswitch matchType {\n\tcase 0:\n\t\tfor i, arg := range lookupArray {\n\t\t\tif ok, _ := formulaCriteriaEval(arg, criteria); ok {\n\t\t\t\treturn newNumberFormulaArg(float64(i + 1))\n\t\t\t}\n\t\t}\n\tcase -1:\n\t\tfor i, arg := range lookupArray {\n\t\t\tif ok, _ := formulaCriteriaEval(arg, &formulaCriteria{\n\t\t\t\tType: criteriaGe, Condition: criteria.Condition,\n\t\t\t}); ok {\n\t\t\t\tidx = i\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif criteria.Condition.Type == ArgNumber {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\tcase 1:\n\t\tfor i, arg := range lookupArray {\n\t\t\tif ok, _ := formulaCriteriaEval(arg, &formulaCriteria{\n\t\t\t\tType: criteriaLe, Condition: criteria.Condition,\n\t\t\t}); ok {\n\t\t\t\tidx = i\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif criteria.Condition.Type == ArgNumber {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tif idx == -1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\treturn newNumberFormulaArg(float64(idx + 1))\n}\n\n// MATCH function looks up a value in an array, and returns the position of\n// the value within the array. The user can specify that the function should\n// only return a result if an exact match is found, or that the function\n// should return the position of the closest match (above or below), if an\n// exact match is not found. The syntax of the Match function is:\n//\n//\tMATCH(lookup_value,lookup_array,[match_type])\nfunc (fn *formulaFuncs) MATCH(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 && argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MATCH requires 1 or 2 arguments\")\n\t}\n\tvar (\n\t\tmatchType      = 1\n\t\tlookupArray    []formulaArg\n\t\tlookupArrayArg = argsList.Front().Next().Value.(formulaArg)\n\t\tlookupArrayErr = \"MATCH arguments lookup_array should be one-dimensional array\"\n\t)\n\tif argsList.Len() == 3 {\n\t\tmatchTypeArg := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif matchTypeArg.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MATCH requires numeric match_type argument\")\n\t\t}\n\t\tif matchTypeArg.Number == -1 || matchTypeArg.Number == 0 {\n\t\t\tmatchType = int(matchTypeArg.Number)\n\t\t}\n\t}\n\tswitch lookupArrayArg.Type {\n\tcase ArgMatrix:\n\t\tif len(lookupArrayArg.Matrix) != 1 && len(lookupArrayArg.Matrix[0]) != 1 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNA, lookupArrayErr)\n\t\t}\n\t\tlookupArray = lookupArrayArg.ToList()\n\tdefault:\n\t\treturn newErrorFormulaArg(formulaErrorNA, lookupArrayErr)\n\t}\n\treturn calcMatch(matchType, formulaCriteriaParser(argsList.Front().Value.(formulaArg)), lookupArray)\n}\n\n// TRANSPOSE function 'transposes' an array of cells (i.e. the function copies\n// a horizontal range of cells into a vertical range and vice versa). The\n// syntax of the function is:\n//\n//\tTRANSPOSE(array)\nfunc (fn *formulaFuncs) TRANSPOSE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TRANSPOSE requires 1 argument\")\n\t}\n\targs := argsList.Back().Value.(formulaArg).ToList()\n\trmin, rmax := calcColsRowsMinMax(false, argsList)\n\tcmin, cmax := calcColsRowsMinMax(true, argsList)\n\tcols, rows := cmax-cmin+1, rmax-rmin+1\n\tsrc := make([][]formulaArg, 0)\n\tfor i := 0; i < len(args); i += cols {\n\t\tsrc = append(src, args[i:i+cols])\n\t}\n\tmtx := make([][]formulaArg, cols)\n\tfor r, row := range src {\n\t\tcolIdx := r % rows\n\t\tfor c, cell := range row {\n\t\t\trowIdx := c % cols\n\t\t\tif len(mtx[rowIdx]) == 0 {\n\t\t\t\tmtx[rowIdx] = make([]formulaArg, rows)\n\t\t\t}\n\t\t\tmtx[rowIdx][colIdx] = cell\n\t\t}\n\t}\n\treturn newMatrixFormulaArg(mtx)\n}\n\n// lookupLinearSearch sequentially checks each look value of the lookup array until\n// a match is found or the whole list has been searched.\nfunc lookupLinearSearch(vertical bool, lookupValue, lookupArray, matchMode, searchMode formulaArg) (int, bool) {\n\tmatchIdx, wasExact := -1, false\n\tlinearSearch := func(i int, cell, lhs formulaArg) bool {\n\t\tif lookupValue.Type == ArgNumber {\n\t\t\tif lhs = cell.ToNumber(); lhs.Type == ArgError {\n\t\t\t\tlhs = cell\n\t\t\t}\n\t\t} else if lookupValue.Type == ArgMatrix {\n\t\t\tlhs = lookupArray\n\t\t} else if lookupArray.Type == ArgString {\n\t\t\tlhs = newStringFormulaArg(cell.Value())\n\t\t}\n\t\tif compareFormulaArg(lhs, lookupValue, matchMode, false) == criteriaEq {\n\t\t\tmatchIdx = i\n\t\t\twasExact = true\n\t\t\tif searchMode.Number == searchModeLinear {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\tif matchMode.Number == matchModeMinGreater || matchMode.Number == matchModeMaxLess {\n\t\t\tmatchIdx = int(calcMatchMatrix(vertical, int(matchMode.Number), formulaCriteriaParser(lookupValue), lookupArray.Matrix).Number)\n\t\t\treturn false\n\t\t}\n\t\treturn false\n\t}\n\n\tif vertical {\n\t\tfor i, row := range lookupArray.Matrix {\n\t\t\tlhs := row[0]\n\t\t\tif linearSearch(i, lhs, lhs) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor i, lhs := range lookupArray.Matrix[0] {\n\t\t\tif linearSearch(i, lhs, lhs) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn matchIdx, wasExact\n}\n\n// VLOOKUP function 'looks up' a given value in the left-hand column of a\n// data array (or table), and returns the corresponding value from another\n// column of the array. The syntax of the function is:\n//\n//\tVLOOKUP(lookup_value,table_array,col_index_num,[range_lookup])\nfunc (fn *formulaFuncs) VLOOKUP(argsList *list.List) formulaArg {\n\tcolIdx, lookupValue, tableArray, matchMode, errArg := checkHVLookupArgs(\"VLOOKUP\", argsList)\n\tif errArg.Type == ArgError {\n\t\treturn errArg\n\t}\n\tvar matchIdx int\n\tvar wasExact bool\n\tif matchMode.Number == matchModeWildcard || len(tableArray.Matrix) == TotalRows {\n\t\tmatchIdx, wasExact = lookupLinearSearch(true, lookupValue, tableArray, matchMode, newNumberFormulaArg(searchModeLinear))\n\t} else {\n\t\tmatchIdx, wasExact = lookupBinarySearch(true, lookupValue, tableArray, matchMode, newNumberFormulaArg(searchModeAscBinary))\n\t}\n\tif matchIdx == -1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, \"VLOOKUP no result found\")\n\t}\n\tmtx := tableArray.Matrix[matchIdx]\n\tif colIdx < 0 || colIdx >= len(mtx) {\n\t\treturn newErrorFormulaArg(formulaErrorNA, \"VLOOKUP has invalid column index\")\n\t}\n\tif wasExact || matchMode.Number == matchModeWildcard {\n\t\treturn mtx[colIdx]\n\t}\n\treturn newErrorFormulaArg(formulaErrorNA, \"VLOOKUP no result found\")\n}\n\n// lookupBinarySearch finds the position of a target value when range lookup\n// is TRUE, if the data of table array can't guarantee be sorted, it will\n// return wrong result.\nfunc lookupBinarySearch(vertical bool, lookupValue, lookupArray, matchMode, searchMode formulaArg) (matchIdx int, wasExact bool) {\n\tvar tableArray []formulaArg\n\tif vertical {\n\t\tfor _, row := range lookupArray.Matrix {\n\t\t\ttableArray = append(tableArray, row[0])\n\t\t}\n\t} else {\n\t\ttableArray = lookupArray.Matrix[0]\n\t}\n\tlow, high, lastMatchIdx := 0, len(tableArray)-1, -1\n\tcount := high\n\tfor low <= high {\n\t\tmid := low + (high-low)/2\n\t\tcell := tableArray[mid]\n\t\tlhs := cell\n\t\tif lookupValue.Type == ArgNumber {\n\t\t\tif lhs = cell.ToNumber(); lhs.Type == ArgError {\n\t\t\t\tlhs = cell\n\t\t\t}\n\t\t} else if lookupValue.Type == ArgMatrix && vertical {\n\t\t\tlhs = lookupArray\n\t\t} else if lookupValue.Type == ArgString {\n\t\t\tlhs = newStringFormulaArg(cell.Value())\n\t\t}\n\t\tresult := compareFormulaArg(lhs, lookupValue, matchMode, false)\n\t\tswitch result {\n\t\tcase criteriaEq:\n\t\t\tmatchIdx, wasExact = mid, true\n\t\t\tif searchMode.Number == searchModeDescBinary {\n\t\t\t\tmatchIdx = count - matchIdx\n\t\t\t}\n\t\t\treturn\n\t\tcase criteriaG:\n\t\t\thigh = mid - 1\n\t\tcase criteriaL:\n\t\t\tmatchIdx = mid\n\t\t\tif cell.Type != ArgEmpty {\n\t\t\t\tlastMatchIdx = matchIdx\n\t\t\t}\n\t\t\tlow = mid + 1\n\t\tdefault:\n\t\t\treturn -1, false\n\t\t}\n\t}\n\tmatchIdx, wasExact = lastMatchIdx, true\n\treturn\n}\n\n// checkLookupArgs checking arguments, prepare lookup value, and data for the\n// formula function LOOKUP.\nfunc checkLookupArgs(argsList *list.List) (arrayForm bool, lookupValue, lookupVector, errArg formulaArg) {\n\tif argsList.Len() < 2 {\n\t\terrArg = newErrorFormulaArg(formulaErrorVALUE, \"LOOKUP requires at least 2 arguments\")\n\t\treturn\n\t}\n\tif argsList.Len() > 3 {\n\t\terrArg = newErrorFormulaArg(formulaErrorVALUE, \"LOOKUP requires at most 3 arguments\")\n\t\treturn\n\t}\n\tlookupValue = newStringFormulaArg(argsList.Front().Value.(formulaArg).Value())\n\tlookupVector = argsList.Front().Next().Value.(formulaArg)\n\tif lookupVector.Type != ArgMatrix && lookupVector.Type != ArgList {\n\t\terrArg = newErrorFormulaArg(formulaErrorVALUE, \"LOOKUP requires second argument of table array\")\n\t\treturn\n\t}\n\tarrayForm = lookupVector.Type == ArgMatrix\n\tif arrayForm && len(lookupVector.Matrix) == 0 {\n\t\terrArg = newErrorFormulaArg(formulaErrorVALUE, \"LOOKUP requires not empty range as second argument\")\n\t}\n\treturn\n}\n\n// iterateLookupArgs iterate arguments to extract columns and calculate match\n// index for the formula function LOOKUP.\nfunc iterateLookupArgs(lookupValue, lookupVector formulaArg) ([]formulaArg, int, bool) {\n\tcols, matchIdx, ok := lookupCol(lookupVector, 0), -1, false\n\tfor idx, col := range cols {\n\t\tlhs := lookupValue\n\t\tswitch col.Type {\n\t\tcase ArgNumber:\n\t\t\tlhs = lhs.ToNumber()\n\t\t\tif !col.Boolean {\n\t\t\t\tif lhs.Type == ArgError {\n\t\t\t\t\tlhs = lookupValue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcompare := compareFormulaArg(lhs, col, newNumberFormulaArg(matchModeMaxLess), false)\n\t\t// Find exact match\n\t\tif compare == criteriaEq {\n\t\t\tmatchIdx = idx\n\t\t\tbreak\n\t\t}\n\t\t// Find the nearest match if lookup value is more than or equal to the first value in lookup vector\n\t\tif idx == 0 {\n\t\t\tok = compare == criteriaG\n\t\t} else if ok && compare == criteriaL && matchIdx == -1 {\n\t\t\tmatchIdx = idx - 1\n\t\t}\n\t}\n\treturn cols, matchIdx, ok\n}\n\n// index is an implementation of the formula function INDEX.\nfunc (fn *formulaFuncs) index(array formulaArg, rowIdx, colIdx int) formulaArg {\n\tvar cells []formulaArg\n\tif array.Type == ArgMatrix {\n\t\tcellMatrix := array.Matrix\n\t\tif rowIdx < -1 || rowIdx >= len(cellMatrix) {\n\t\t\treturn newErrorFormulaArg(formulaErrorREF, \"INDEX row_num out of range\")\n\t\t}\n\t\tif rowIdx == -1 {\n\t\t\tif colIdx >= len(cellMatrix[0]) {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorREF, \"INDEX col_num out of range\")\n\t\t\t}\n\t\t\tvar column [][]formulaArg\n\t\t\tfor _, cells = range cellMatrix {\n\t\t\t\tcolumn = append(column, []formulaArg{cells[colIdx]})\n\t\t\t}\n\t\t\treturn newMatrixFormulaArg(column)\n\t\t}\n\t\tcells = cellMatrix[rowIdx]\n\t}\n\tif colIdx < -1 || colIdx >= len(cells) {\n\t\treturn newErrorFormulaArg(formulaErrorREF, \"INDEX col_num out of range\")\n\t}\n\treturn newListFormulaArg(cells)\n}\n\n// validateMatchMode check the number of match mode if be equal to 0, 1, -1 or\n// 2.\nfunc validateMatchMode(mode float64) bool {\n\treturn mode == matchModeExact || mode == matchModeMinGreater || mode == matchModeMaxLess || mode == matchModeWildcard\n}\n\n// validateSearchMode check the number of search mode if be equal to 1, -1, 2\n// or -2.\nfunc validateSearchMode(mode float64) bool {\n\treturn mode == searchModeLinear || mode == searchModeReverseLinear || mode == searchModeAscBinary || mode == searchModeDescBinary\n}\n\n// prepareXlookupArgs checking and prepare arguments for the formula function\n// XLOOKUP.\nfunc (fn *formulaFuncs) prepareXlookupArgs(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"XLOOKUP requires at least 3 arguments\")\n\t}\n\tif argsList.Len() > 6 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"XLOOKUP allows at most 6 arguments\")\n\t}\n\tlookupValue := argsList.Front().Value.(formulaArg)\n\tlookupArray := argsList.Front().Next().Value.(formulaArg)\n\treturnArray := argsList.Front().Next().Next().Value.(formulaArg)\n\tifNotFond := newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\tmatchMode, searchMode := newNumberFormulaArg(matchModeExact), newNumberFormulaArg(searchModeLinear)\n\tif argsList.Len() > 3 {\n\t\tifNotFond = argsList.Front().Next().Next().Next().Value.(formulaArg)\n\t}\n\tif argsList.Len() > 4 {\n\t\tif matchMode = argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); matchMode.Type != ArgNumber {\n\t\t\treturn matchMode\n\t\t}\n\t}\n\tif argsList.Len() > 5 {\n\t\tif searchMode = argsList.Back().Value.(formulaArg).ToNumber(); searchMode.Type != ArgNumber {\n\t\t\treturn searchMode\n\t\t}\n\t}\n\tif lookupArray.Type != ArgMatrix || returnArray.Type != ArgMatrix {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tif !validateMatchMode(matchMode.Number) || !validateSearchMode(searchMode.Number) {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\treturn newListFormulaArg([]formulaArg{lookupValue, lookupArray, returnArray, ifNotFond, matchMode, searchMode})\n}\n\n// xlookup is an implementation of the formula function XLOOKUP.\nfunc (fn *formulaFuncs) xlookup(lookupRows, lookupCols, returnArrayRows, returnArrayCols, matchIdx int,\n\tcondition1, condition2, condition3, condition4 bool, returnArray formulaArg,\n) formulaArg {\n\tvar result [][]formulaArg\n\tfor rowIdx, row := range returnArray.Matrix {\n\t\tfor colIdx, cell := range row {\n\t\t\tif condition1 {\n\t\t\t\tif condition2 {\n\t\t\t\t\tresult = append(result, []formulaArg{cell})\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif returnArrayRows > 1 && returnArrayCols > 1 {\n\t\t\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif condition3 {\n\t\t\t\tif returnArrayCols != lookupCols {\n\t\t\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t\t\t}\n\t\t\t\tif colIdx == matchIdx {\n\t\t\t\t\tresult = append(result, []formulaArg{cell})\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tif condition4 {\n\t\t\t\tif returnArrayRows != lookupRows {\n\t\t\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t\t\t}\n\t\t\t\tif rowIdx == matchIdx {\n\t\t\t\t\tif len(result) == 0 {\n\t\t\t\t\t\tresult = append(result, []formulaArg{cell})\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tresult[0] = append(result[0], cell)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tarray := newMatrixFormulaArg(result)\n\tcells := array.ToList()\n\tif len(cells) == 1 {\n\t\treturn cells[0]\n\t}\n\treturn array\n}\n\n// XLOOKUP function searches a range or an array, and then returns the item\n// corresponding to the first match it finds. If no match exists, then\n// XLOOKUP can return the closest (approximate) match. The syntax of the\n// function is:\n//\n//\tXLOOKUP(lookup_value,lookup_array,return_array,[if_not_found],[match_mode],[search_mode])\nfunc (fn *formulaFuncs) XLOOKUP(argsList *list.List) formulaArg {\n\targs := fn.prepareXlookupArgs(argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tlookupValue, lookupArray, returnArray, ifNotFond, matchMode, searchMode := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5]\n\tlookupRows, lookupCols := len(lookupArray.Matrix), 0\n\tif lookupRows > 0 {\n\t\tlookupCols = len(lookupArray.Matrix[0])\n\t}\n\tif lookupRows != 1 && lookupCols != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tverticalLookup := lookupRows >= lookupCols\n\tvar matchIdx int\n\tswitch searchMode.Number {\n\tcase searchModeLinear, searchModeReverseLinear:\n\t\tmatchIdx, _ = lookupLinearSearch(verticalLookup, lookupValue, lookupArray, matchMode, searchMode)\n\tdefault:\n\t\tmatchIdx, _ = lookupBinarySearch(verticalLookup, lookupValue, lookupArray, matchMode, searchMode)\n\t}\n\tif matchIdx == -1 {\n\t\treturn ifNotFond\n\t}\n\treturnArrayRows, returnArrayCols := len(returnArray.Matrix), len(returnArray.Matrix[0])\n\tcondition1 := lookupRows == 1 && lookupCols == 1\n\tcondition2 := returnArrayRows == 1 || returnArrayCols == 1\n\tcondition3 := lookupRows == 1 && lookupCols > 1\n\tcondition4 := lookupRows > 1 && lookupCols == 1\n\treturn fn.xlookup(lookupRows, lookupCols, returnArrayRows, returnArrayCols, matchIdx, condition1, condition2, condition3, condition4, returnArray)\n}\n\n// INDEX function returns a reference to a cell that lies in a specified row\n// and column of a range of cells. The syntax of the function is:\n//\n//\tINDEX(array,row_num,[col_num])\nfunc (fn *formulaFuncs) INDEX(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 || argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"INDEX requires 2 or 3 arguments\")\n\t}\n\tarray := argsList.Front().Value.(formulaArg)\n\tif array.Type != ArgMatrix && array.Type != ArgList {\n\t\tarray = newMatrixFormulaArg([][]formulaArg{{array}})\n\t}\n\trowArg := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif rowArg.Type != ArgNumber {\n\t\treturn rowArg\n\t}\n\trowIdx, colIdx := int(rowArg.Number)-1, -1\n\tif argsList.Len() == 3 {\n\t\tcolArg := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif colArg.Type != ArgNumber {\n\t\t\treturn colArg\n\t\t}\n\t\tcolIdx = int(colArg.Number) - 1\n\t}\n\tif rowIdx == -1 && colIdx == -1 {\n\t\tif len(array.ToList()) != 1 {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t\treturn array.ToList()[0]\n\t}\n\tcells := fn.index(array, rowIdx, colIdx)\n\tif cells.Type != ArgList {\n\t\treturn cells\n\t}\n\tif colIdx == -1 {\n\t\treturn newMatrixFormulaArg([][]formulaArg{cells.List})\n\t}\n\treturn cells.List[colIdx]\n}\n\n// INDIRECT function converts a text string into a cell reference. The syntax\n// of the Indirect function is:\n//\n//\tINDIRECT(ref_text,[a1])\nfunc (fn *formulaFuncs) INDIRECT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 && argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"INDIRECT requires 1 or 2 arguments\")\n\t}\n\trefText := argsList.Front().Value.(formulaArg).Value()\n\ta1 := newBoolFormulaArg(true)\n\tif argsList.Len() == 2 {\n\t\tif a1 = argsList.Back().Value.(formulaArg).ToBool(); a1.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t}\n\tR1C1ToA1 := func(ref string) (cell string, err error) {\n\t\tparts := strings.Split(strings.TrimLeft(ref, \"R\"), \"C\")\n\t\tif len(parts) != 2 {\n\t\t\treturn\n\t\t}\n\t\trow, err := strconv.Atoi(parts[0])\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tcol, err := strconv.Atoi(parts[1])\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tcell, err = CoordinatesToCellName(col, row)\n\t\treturn\n\t}\n\trefs := strings.Split(refText, \":\")\n\tfromRef, toRef := refs[0], \"\"\n\tif len(refs) == 2 {\n\t\ttoRef = refs[1]\n\t}\n\tif a1.Number == 0 {\n\t\tfrom, err := R1C1ToA1(refs[0])\n\t\tif err != nil {\n\t\t\treturn newErrorFormulaArg(formulaErrorREF, formulaErrorREF)\n\t\t}\n\t\tfromRef = from\n\t\tif len(refs) == 2 {\n\t\t\tto, err := R1C1ToA1(refs[1])\n\t\t\tif err != nil {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorREF, formulaErrorREF)\n\t\t\t}\n\t\t\ttoRef = to\n\t\t}\n\t}\n\tif len(refs) == 1 {\n\t\tvalue, err := fn.f.GetCellValue(fn.sheet, fromRef)\n\t\tif err != nil {\n\t\t\treturn newErrorFormulaArg(formulaErrorREF, formulaErrorREF)\n\t\t}\n\t\treturn newStringFormulaArg(value)\n\t}\n\targ, _ := fn.f.parseReference(fn.ctx, fn.sheet, fromRef+\":\"+toRef)\n\treturn arg\n}\n\n// LOOKUP function performs an approximate match lookup in a one-column or\n// one-row range, and returns the corresponding value from another one-column\n// or one-row range. The syntax of the function is:\n//\n//\tLOOKUP(lookup_value,lookup_vector,[result_vector])\nfunc (fn *formulaFuncs) LOOKUP(argsList *list.List) formulaArg {\n\tarrayForm, lookupValue, lookupVector, errArg := checkLookupArgs(argsList)\n\tif errArg.Type == ArgError {\n\t\treturn errArg\n\t}\n\tcols, matchIdx, ok := iterateLookupArgs(lookupValue, lookupVector)\n\tif ok && matchIdx == -1 {\n\t\tmatchIdx = len(cols) - 1\n\t}\n\tvar column []formulaArg\n\tif argsList.Len() == 3 {\n\t\tcolumn = lookupCol(argsList.Back().Value.(formulaArg), 0)\n\t} else if arrayForm && len(lookupVector.Matrix[0]) > 1 {\n\t\tcolumn = lookupCol(lookupVector, 1)\n\t} else {\n\t\tcolumn = cols\n\t}\n\tif matchIdx < 0 || matchIdx >= len(column) {\n\t\treturn newErrorFormulaArg(formulaErrorNA, \"LOOKUP no result found\")\n\t}\n\treturn column[matchIdx]\n}\n\n// lookupCol extract columns for LOOKUP.\nfunc lookupCol(arr formulaArg, idx int) []formulaArg {\n\tcol := arr.List\n\tif arr.Type == ArgMatrix {\n\t\tcol = nil\n\t\tfor _, r := range arr.Matrix {\n\t\t\tif len(r) > 0 {\n\t\t\t\tcol = append(col, r[idx])\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcol = append(col, newEmptyFormulaArg())\n\t\t}\n\t}\n\treturn col\n}\n\n// ROW function returns the first row number within a supplied reference or\n// the number of the current row. The syntax of the function is:\n//\n//\tROW([reference])\nfunc (fn *formulaFuncs) ROW(argsList *list.List) formulaArg {\n\tif argsList.Len() > 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ROW requires at most 1 argument\")\n\t}\n\tif argsList.Len() == 1 {\n\t\tif argsList.Front().Value.(formulaArg).cellRanges != nil && argsList.Front().Value.(formulaArg).cellRanges.Len() > 0 {\n\t\t\treturn newNumberFormulaArg(float64(argsList.Front().Value.(formulaArg).cellRanges.Front().Value.(cellRange).From.Row))\n\t\t}\n\t\tif argsList.Front().Value.(formulaArg).cellRefs != nil && argsList.Front().Value.(formulaArg).cellRefs.Len() > 0 {\n\t\t\treturn newNumberFormulaArg(float64(argsList.Front().Value.(formulaArg).cellRefs.Front().Value.(cellRef).Row))\n\t\t}\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"invalid reference\")\n\t}\n\t_, row, _ := CellNameToCoordinates(fn.cell)\n\treturn newNumberFormulaArg(float64(row))\n}\n\n// ROWS function takes an Excel range and returns the number of rows that are\n// contained within the range. The syntax of the function is:\n//\n//\tROWS(array)\nfunc (fn *formulaFuncs) ROWS(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ROWS requires 1 argument\")\n\t}\n\tminVal, maxVal := calcColsRowsMinMax(false, argsList)\n\tif maxVal == TotalRows {\n\t\treturn newNumberFormulaArg(TotalRows)\n\t}\n\tresult := maxVal - minVal + 1\n\tif maxVal == minVal {\n\t\tif minVal == 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"invalid reference\")\n\t\t}\n\t\treturn newNumberFormulaArg(float64(1))\n\t}\n\treturn newNumberFormulaArg(float64(result))\n}\n\n// Web Functions\n\n// ENCODEURL function returns a URL-encoded string, replacing certain\n// non-alphanumeric characters with the percentage symbol (%) and a\n// hexadecimal number. The syntax of the function is:\n//\n//\tENCODEURL(url)\nfunc (fn *formulaFuncs) ENCODEURL(argsList *list.List) formulaArg {\n\tif argsList.Len() != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ENCODEURL requires 1 argument\")\n\t}\n\ttoken := argsList.Front().Value.(formulaArg).Value()\n\treturn newStringFormulaArg(strings.ReplaceAll(url.QueryEscape(token), \"+\", \"%20\"))\n}\n\n// Financial Functions\n\n// validateFrequency check the number of coupon payments per year if be equal to 1, 2 or 4.\nfunc validateFrequency(freq float64) bool {\n\treturn freq == 1 || freq == 2 || freq == 4\n}\n\n// ACCRINT function returns the accrued interest in a security that pays\n// periodic interest. The syntax of the function is:\n//\n//\tACCRINT(issue,first_interest,settlement,rate,par,frequency,[basis],[calc_method])\nfunc (fn *formulaFuncs) ACCRINT(argsList *list.List) formulaArg {\n\tif argsList.Len() < 6 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ACCRINT requires at least 6 arguments\")\n\t}\n\tif argsList.Len() > 8 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ACCRINT allows at most 8 arguments\")\n\t}\n\targs := fn.prepareDataValueArgs(3, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tissue, settlement := args.List[0], args.List[2]\n\trate := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tpar := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tfrequency := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber || par.Type != ArgNumber || frequency.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif !validateFrequency(frequency.Number) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() >= 7 {\n\t\tif basis = argsList.Front().Next().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\tif argsList.Len() == 8 {\n\t\tif cm := argsList.Back().Value.(formulaArg).ToBool(); cm.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t}\n\tfrac1 := yearFrac(issue.Number, settlement.Number, int(basis.Number))\n\tif frac1.Type != ArgNumber {\n\t\treturn frac1\n\t}\n\treturn newNumberFormulaArg(par.Number * rate.Number * frac1.Number)\n}\n\n// ACCRINTM function returns the accrued interest in a security that pays\n// interest at maturity. The syntax of the function is:\n//\n//\tACCRINTM(issue,settlement,rate,[par],[basis])\nfunc (fn *formulaFuncs) ACCRINTM(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 && argsList.Len() != 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ACCRINTM requires 4 or 5 arguments\")\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tissue, settlement := args.List[0], args.List[1]\n\tif settlement.Number < issue.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\trate := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tpar := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber || par.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif par.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 5 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\tfrac := yearFrac(issue.Number, settlement.Number, int(basis.Number))\n\tif frac.Type != ArgNumber {\n\t\treturn frac\n\t}\n\treturn newNumberFormulaArg(frac.Number * rate.Number * par.Number)\n}\n\n// prepareAmorArgs checking and prepare arguments for the formula functions\n// AMORDEGRC and AMORLINC.\nfunc (fn *formulaFuncs) prepareAmorArgs(name string, argsList *list.List) formulaArg {\n\tcost := argsList.Front().Value.(formulaArg).ToNumber()\n\tif cost.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires cost to be number argument\", name))\n\t}\n\tif cost.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires cost >= 0\", name))\n\t}\n\targs := list.New().Init()\n\targs.PushBack(argsList.Front().Next().Value.(formulaArg))\n\tdatePurchased := fn.DATEVALUE(args)\n\tif datePurchased.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\targs.Init()\n\targs.PushBack(argsList.Front().Next().Next().Value.(formulaArg))\n\tfirstPeriod := fn.DATEVALUE(args)\n\tif firstPeriod.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif firstPeriod.Number < datePurchased.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tsalvage := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif salvage.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif salvage.Number < 0 || salvage.Number > cost.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tperiod := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif period.Type != ArgNumber || period.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\trate := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber || rate.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 7 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\treturn newListFormulaArg([]formulaArg{cost, datePurchased, firstPeriod, salvage, period, rate, basis})\n}\n\n// AMORDEGRC function is provided for users of the French accounting system.\n// The function calculates the prorated linear depreciation of an asset for a\n// specified accounting period. The syntax of the function is:\n//\n//\tAMORDEGRC(cost,date_purchased,first_period,salvage,period,rate,[basis])\nfunc (fn *formulaFuncs) AMORDEGRC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 6 && argsList.Len() != 7 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"AMORDEGRC requires 6 or 7 arguments\")\n\t}\n\targs := fn.prepareAmorArgs(\"AMORDEGRC\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tcost, datePurchased, firstPeriod, salvage, period, rate, basis := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5], args.List[6]\n\tif rate.Number >= 0.5 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"AMORDEGRC requires rate to be < 0.5\")\n\t}\n\tassetsLife, amorCoeff := 1/rate.Number, 2.5\n\tif assetsLife < 3 {\n\t\tamorCoeff = 1\n\t} else if assetsLife < 5 {\n\t\tamorCoeff = 1.5\n\t} else if assetsLife <= 6 {\n\t\tamorCoeff = 2\n\t}\n\trate.Number *= amorCoeff\n\tfrac := yearFrac(datePurchased.Number, firstPeriod.Number, int(basis.Number))\n\tif frac.Type != ArgNumber {\n\t\treturn frac\n\t}\n\tnRate := float64(int((frac.Number * cost.Number * rate.Number) + 0.5))\n\tcost.Number -= nRate\n\trest := cost.Number - salvage.Number\n\tfor n := 0; n < int(period.Number); n++ {\n\t\tnRate = float64(int((cost.Number * rate.Number) + 0.5))\n\t\trest -= nRate\n\t\tif rest < 0 {\n\t\t\tswitch int(period.Number) - n {\n\t\t\tcase 0:\n\t\t\tcase 1:\n\t\t\t\treturn newNumberFormulaArg(float64(int((cost.Number * 0.5) + 0.5)))\n\t\t\tdefault:\n\t\t\t\treturn newNumberFormulaArg(0)\n\t\t\t}\n\t\t}\n\t\tcost.Number -= nRate\n\t}\n\treturn newNumberFormulaArg(nRate)\n}\n\n// AMORLINC function is provided for users of the French accounting system.\n// The function calculates the prorated linear depreciation of an asset for a\n// specified accounting period. The syntax of the function is:\n//\n//\tAMORLINC(cost,date_purchased,first_period,salvage,period,rate,[basis])\nfunc (fn *formulaFuncs) AMORLINC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 6 && argsList.Len() != 7 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"AMORLINC requires 6 or 7 arguments\")\n\t}\n\targs := fn.prepareAmorArgs(\"AMORLINC\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tcost, datePurchased, firstPeriod, salvage, period, rate, basis := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5], args.List[6]\n\tfrac := yearFrac(datePurchased.Number, firstPeriod.Number, int(basis.Number))\n\tif frac.Type != ArgNumber {\n\t\treturn frac\n\t}\n\trate1 := frac.Number * cost.Number * rate.Number\n\tif period.Number == 0 {\n\t\treturn newNumberFormulaArg(rate1)\n\t}\n\trate2 := cost.Number * rate.Number\n\tdelta := cost.Number - salvage.Number\n\tperiods := int((delta - rate1) / rate2)\n\tif int(period.Number) <= periods {\n\t\treturn newNumberFormulaArg(rate2)\n\t} else if int(period.Number)-1 == periods {\n\t\treturn newNumberFormulaArg(delta - rate2*float64(periods) - math.Nextafter(rate1, rate1))\n\t}\n\treturn newNumberFormulaArg(0)\n}\n\n// prepareCouponArgs checking and prepare arguments for the formula functions\n// COUPDAYBS, COUPDAYS, COUPDAYSNC, COUPPCD, COUPNUM and COUPNCD.\nfunc (fn *formulaFuncs) prepareCouponArgs(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 && argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 3 or 4 arguments\", name))\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity := args.List[0], args.List[1]\n\tif settlement.Number >= maturity.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires maturity > settlement\", name))\n\t}\n\tfrequency := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif frequency.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif !validateFrequency(frequency.Number) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 4 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\treturn newListFormulaArg([]formulaArg{settlement, maturity, frequency, basis})\n}\n\n// is30BasisMethod determine if the financial day count basis rules is 30/360\n// methods.\nfunc is30BasisMethod(basis int) bool {\n\treturn basis == 0 || basis == 4\n}\n\n// getDaysInMonthRange return the day by given year, month range and day count\n// basis.\nfunc getDaysInMonthRange(fromMonth, toMonth int) int {\n\tif fromMonth > toMonth {\n\t\treturn 0\n\t}\n\treturn (toMonth - fromMonth + 1) * 30\n}\n\n// getDayOnBasis returns the day by given date and day count basis.\nfunc getDayOnBasis(y, m, d, basis int) int {\n\tif !is30BasisMethod(basis) {\n\t\treturn d\n\t}\n\tday := d\n\tdim := getDaysInMonth(y, m)\n\tif day > 30 || d >= dim || day >= dim {\n\t\tday = 30\n\t}\n\treturn day\n}\n\n// coupdays returns the number of days that base on date range and the day\n// count basis to be used.\nfunc coupdays(from, to time.Time, basis int) float64 {\n\tdays := 0\n\tfromY, fromM, fromD := from.Date()\n\ttoY, toM, toD := to.Date()\n\tfromDay, toDay := getDayOnBasis(fromY, int(fromM), fromD, basis), getDayOnBasis(toY, int(toM), toD, basis)\n\tif !is30BasisMethod(basis) {\n\t\treturn (daysBetween(excelMinTime1900.Unix(), makeDate(toY, toM, toDay)) + 1) - (daysBetween(excelMinTime1900.Unix(), makeDate(fromY, fromM, fromDay)) + 1)\n\t}\n\tif basis == 0 {\n\t\tif (int(fromM) == 2 || fromDay < 30) && toD == 31 {\n\t\t\ttoDay = 31\n\t\t}\n\t} else {\n\t\tif int(fromM) == 2 && fromDay == 30 {\n\t\t\tfromDay = getDaysInMonth(fromY, 2)\n\t\t}\n\t\tif int(toM) == 2 && toDay == 30 {\n\t\t\ttoDay = getDaysInMonth(toY, 2)\n\t\t}\n\t}\n\tif fromY < toY || (fromY == toY && int(fromM) < int(toM)) {\n\t\tdays = 30 - fromDay + 1\n\t\tfromD = 1\n\t\tfromDay = 1\n\t\tdate := time.Date(fromY, fromM, fromD, 0, 0, 0, 0, time.UTC).AddDate(0, 1, 0)\n\t\tif date.Year() < toY {\n\t\t\tdays += getDaysInMonthRange(int(date.Month()), 12)\n\t\t\tdate = date.AddDate(0, 13-int(date.Month()), 0)\n\t\t}\n\t\tdays += getDaysInMonthRange(int(date.Month()), int(toM)-1)\n\t}\n\tif days += toDay - fromDay; days > 0 {\n\t\treturn float64(days)\n\t}\n\treturn 0\n}\n\n// COUPDAYBS function calculates the number of days from the beginning of a\n// coupon's period to the settlement date. The syntax of the function is:\n//\n//\tCOUPDAYBS(settlement,maturity,frequency,[basis])\nfunc (fn *formulaFuncs) COUPDAYBS(argsList *list.List) formulaArg {\n\targs := fn.prepareCouponArgs(\"COUPDAYBS\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement := timeFromExcelTime(args.List[0].Number, false)\n\tpcd := timeFromExcelTime(fn.COUPPCD(argsList).Number, false)\n\treturn newNumberFormulaArg(coupdays(pcd, settlement, int(args.List[3].Number)))\n}\n\n// COUPDAYS function calculates the number of days in a coupon period that\n// contains the settlement date. The syntax of the function is:\n//\n//\tCOUPDAYS(settlement,maturity,frequency,[basis])\nfunc (fn *formulaFuncs) COUPDAYS(argsList *list.List) formulaArg {\n\targs := fn.prepareCouponArgs(\"COUPDAYS\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tfreq := args.List[2].Number\n\tbasis := int(args.List[3].Number)\n\tif basis == 1 {\n\t\tpcd := timeFromExcelTime(fn.COUPPCD(argsList).Number, false)\n\t\tnext := pcd.AddDate(0, 12/int(freq), 0)\n\t\treturn newNumberFormulaArg(coupdays(pcd, next, basis))\n\t}\n\treturn newNumberFormulaArg(float64(getYearDays(0, basis)) / freq)\n}\n\n// COUPDAYSNC function calculates the number of days from the settlement date\n// to the next coupon date. The syntax of the function is:\n//\n//\tCOUPDAYSNC(settlement,maturity,frequency,[basis])\nfunc (fn *formulaFuncs) COUPDAYSNC(argsList *list.List) formulaArg {\n\targs := fn.prepareCouponArgs(\"COUPDAYSNC\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement := timeFromExcelTime(args.List[0].Number, false)\n\tbasis := int(args.List[3].Number)\n\tncd := timeFromExcelTime(fn.COUPNCD(argsList).Number, false)\n\treturn newNumberFormulaArg(coupdays(settlement, ncd, basis))\n}\n\n// coupons is an implementation of the formula functions COUPNCD and COUPPCD.\nfunc (fn *formulaFuncs) coupons(name string, arg formulaArg) formulaArg {\n\tsettlement := timeFromExcelTime(arg.List[0].Number, false)\n\tmaturity := timeFromExcelTime(arg.List[1].Number, false)\n\tmaturityDays := (maturity.Year()-settlement.Year())*12 + (int(maturity.Month()) - int(settlement.Month()))\n\tcoupon := 12 / int(arg.List[2].Number)\n\tmod := maturityDays % coupon\n\tyear := settlement.Year()\n\tmonth := int(settlement.Month())\n\tif mod == 0 && settlement.Day() >= maturity.Day() {\n\t\tmonth += coupon\n\t} else {\n\t\tmonth += mod\n\t}\n\tif name != \"COUPNCD\" {\n\t\tmonth -= coupon\n\t}\n\tif month > 11 {\n\t\tyear++\n\t\tmonth -= 12\n\t} else if month < 0 {\n\t\tyear--\n\t\tmonth += 12\n\t}\n\tday, lastDay := maturity.Day(), time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC)\n\tdays := getDaysInMonth(lastDay.Year(), int(lastDay.Month()))\n\tif getDaysInMonth(maturity.Year(), int(maturity.Month())) == maturity.Day() {\n\t\tday = days\n\t} else if day > 27 && day > days {\n\t\tday = days\n\t}\n\treturn newNumberFormulaArg(daysBetween(excelMinTime1900.Unix(), makeDate(year, time.Month(month), day)) + 1)\n}\n\n// COUPNCD function calculates the number of coupons payable, between a\n// security's settlement date and maturity date, rounded up to the nearest\n// whole coupon. The syntax of the function is:\n//\n//\tCOUPNCD(settlement,maturity,frequency,[basis])\nfunc (fn *formulaFuncs) COUPNCD(argsList *list.List) formulaArg {\n\targs := fn.prepareCouponArgs(\"COUPNCD\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\treturn fn.coupons(\"COUPNCD\", args)\n}\n\n// COUPNUM function calculates the number of coupons payable, between a\n// security's settlement date and maturity date, rounded up to the nearest\n// whole coupon. The syntax of the function is:\n//\n//\tCOUPNUM(settlement,maturity,frequency,[basis])\nfunc (fn *formulaFuncs) COUPNUM(argsList *list.List) formulaArg {\n\targs := fn.prepareCouponArgs(\"COUPNUM\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tfrac := yearFrac(args.List[0].Number, args.List[1].Number, 0)\n\treturn newNumberFormulaArg(math.Ceil(frac.Number * args.List[2].Number))\n}\n\n// COUPPCD function returns the previous coupon date, before the settlement\n// date for a security. The syntax of the function is:\n//\n//\tCOUPPCD(settlement,maturity,frequency,[basis])\nfunc (fn *formulaFuncs) COUPPCD(argsList *list.List) formulaArg {\n\targs := fn.prepareCouponArgs(\"COUPPCD\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\treturn fn.coupons(\"COUPPCD\", args)\n}\n\n// CUMIPMT function calculates the cumulative interest paid on a loan or\n// investment, between two specified periods. The syntax of the function is:\n//\n//\tCUMIPMT(rate,nper,pv,start_period,end_period,type)\nfunc (fn *formulaFuncs) CUMIPMT(argsList *list.List) formulaArg {\n\treturn fn.cumip(\"CUMIPMT\", argsList)\n}\n\n// CUMPRINC function calculates the cumulative payment on the principal of a\n// loan or investment, between two specified periods. The syntax of the\n// function is:\n//\n//\tCUMPRINC(rate,nper,pv,start_period,end_period,type)\nfunc (fn *formulaFuncs) CUMPRINC(argsList *list.List) formulaArg {\n\treturn fn.cumip(\"CUMPRINC\", argsList)\n}\n\n// cumip is an implementation of the formula functions CUMIPMT and CUMPRINC.\nfunc (fn *formulaFuncs) cumip(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 6 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 6 arguments\", name))\n\t}\n\trate := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tnper := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif nper.Type != ArgNumber {\n\t\treturn nper\n\t}\n\tpv := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif pv.Type != ArgNumber {\n\t\treturn pv\n\t}\n\tstart := argsList.Back().Prev().Prev().Value.(formulaArg).ToNumber()\n\tif start.Type != ArgNumber {\n\t\treturn start\n\t}\n\tend := argsList.Back().Prev().Value.(formulaArg).ToNumber()\n\tif end.Type != ArgNumber {\n\t\treturn end\n\t}\n\ttyp := argsList.Back().Value.(formulaArg).ToNumber()\n\tif typ.Type != ArgNumber {\n\t\treturn typ\n\t}\n\tif typ.Number != 0 && typ.Number != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tif start.Number < 1 || start.Number > end.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tnum := 0.0\n\tfor per := start.Number; per <= end.Number; per++ {\n\t\targs := list.New().Init()\n\t\targs.PushBack(rate)\n\t\targs.PushBack(newNumberFormulaArg(per))\n\t\targs.PushBack(nper)\n\t\targs.PushBack(pv)\n\t\targs.PushBack(newNumberFormulaArg(0))\n\t\targs.PushBack(typ)\n\t\tif name == \"CUMIPMT\" {\n\t\t\tnum += fn.IPMT(args).Number\n\t\t\tcontinue\n\t\t}\n\t\tnum += fn.PPMT(args).Number\n\t}\n\treturn newNumberFormulaArg(num)\n}\n\n// calcDbArgsCompare implements common arguments' comparison for DB and DDB.\nfunc calcDbArgsCompare(cost, salvage, life, period formulaArg) bool {\n\treturn (cost.Number <= 0) || ((salvage.Number / cost.Number) < 0) || (life.Number <= 0) || (period.Number < 1)\n}\n\n// DB function calculates the depreciation of an asset, using the Fixed\n// Declining Balance Method, for each period of the asset's lifetime. The\n// syntax of the function is:\n//\n//\tDB(cost,salvage,life,period,[month])\nfunc (fn *formulaFuncs) DB(argsList *list.List) formulaArg {\n\tif argsList.Len() < 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DB requires at least 4 arguments\")\n\t}\n\tif argsList.Len() > 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DB allows at most 5 arguments\")\n\t}\n\tcost := argsList.Front().Value.(formulaArg).ToNumber()\n\tif cost.Type != ArgNumber {\n\t\treturn cost\n\t}\n\tsalvage := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif salvage.Type != ArgNumber {\n\t\treturn salvage\n\t}\n\tlife := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif life.Type != ArgNumber {\n\t\treturn life\n\t}\n\tperiod := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif period.Type != ArgNumber {\n\t\treturn period\n\t}\n\tmonth := newNumberFormulaArg(12)\n\tif argsList.Len() == 5 {\n\t\tif month = argsList.Back().Value.(formulaArg).ToNumber(); month.Type != ArgNumber {\n\t\t\treturn month\n\t\t}\n\t}\n\tif cost.Number == 0 {\n\t\treturn newNumberFormulaArg(0)\n\t}\n\tif calcDbArgsCompare(cost, salvage, life, period) || (month.Number < 1) {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tdr := 1 - math.Pow(salvage.Number/cost.Number, 1/life.Number)\n\tdr = math.Round(dr*1000) / 1000\n\tpd, depreciation := 0.0, 0.0\n\tfor per := 1; per <= int(period.Number); per++ {\n\t\tif per == 1 {\n\t\t\tdepreciation = cost.Number * dr * month.Number / 12\n\t\t} else if per == int(life.Number+1) {\n\t\t\tdepreciation = (cost.Number - pd) * dr * (12 - month.Number) / 12\n\t\t} else {\n\t\t\tdepreciation = (cost.Number - pd) * dr\n\t\t}\n\t\tpd += depreciation\n\t}\n\treturn newNumberFormulaArg(depreciation)\n}\n\n// DDB function calculates the depreciation of an asset, using the Double\n// Declining Balance Method, or another specified depreciation rate. The\n// syntax of the function is:\n//\n//\tDDB(cost,salvage,life,period,[factor])\nfunc (fn *formulaFuncs) DDB(argsList *list.List) formulaArg {\n\tif argsList.Len() < 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DDB requires at least 4 arguments\")\n\t}\n\tif argsList.Len() > 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DDB allows at most 5 arguments\")\n\t}\n\tcost := argsList.Front().Value.(formulaArg).ToNumber()\n\tif cost.Type != ArgNumber {\n\t\treturn cost\n\t}\n\tsalvage := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif salvage.Type != ArgNumber {\n\t\treturn salvage\n\t}\n\tlife := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif life.Type != ArgNumber {\n\t\treturn life\n\t}\n\tperiod := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif period.Type != ArgNumber {\n\t\treturn period\n\t}\n\tfactor := newNumberFormulaArg(2)\n\tif argsList.Len() == 5 {\n\t\tif factor = argsList.Back().Value.(formulaArg).ToNumber(); factor.Type != ArgNumber {\n\t\t\treturn factor\n\t\t}\n\t}\n\tif cost.Number == 0 {\n\t\treturn newNumberFormulaArg(0)\n\t}\n\tif calcDbArgsCompare(cost, salvage, life, period) || (factor.Number <= 0.0) || (period.Number > life.Number) {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tpd, depreciation := 0.0, 0.0\n\tfor per := 1; per <= int(period.Number); per++ {\n\t\tdepreciation = math.Min((cost.Number-pd)*(factor.Number/life.Number), cost.Number-salvage.Number-pd)\n\t\tpd += depreciation\n\t}\n\treturn newNumberFormulaArg(depreciation)\n}\n\n// prepareDataValueArgs convert first N arguments to data value for the\n// formula functions.\nfunc (fn *formulaFuncs) prepareDataValueArgs(n int, argsList *list.List) formulaArg {\n\tl := list.New()\n\tvar dataValues []formulaArg\n\tgetDateValue := func(arg formulaArg, l *list.List) formulaArg {\n\t\tswitch arg.Type {\n\t\tcase ArgNumber:\n\t\t\tbreak\n\t\tcase ArgString:\n\t\t\tnum := arg.ToNumber()\n\t\t\tif num.Type == ArgNumber {\n\t\t\t\targ = num\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tl.Init()\n\t\t\tl.PushBack(arg)\n\t\t\targ = fn.DATEVALUE(l)\n\t\t\tif arg.Type == ArgError {\n\t\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t\t}\n\t\tdefault:\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t\treturn arg\n\t}\n\tfor i, arg := 0, argsList.Front(); i < n; arg = arg.Next() {\n\t\tdataValue := getDateValue(arg.Value.(formulaArg), l)\n\t\tif dataValue.Type != ArgNumber {\n\t\t\treturn dataValue\n\t\t}\n\t\tdataValues = append(dataValues, dataValue)\n\t\ti++\n\t}\n\treturn newListFormulaArg(dataValues)\n}\n\n// discIntrate is an implementation of the formula functions DISC and INTRATE.\nfunc (fn *formulaFuncs) discIntrate(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 && argsList.Len() != 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 4 or 5 arguments\", name))\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity, argName := args.List[0], args.List[1], \"pr\"\n\tif maturity.Number <= settlement.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires maturity > settlement\", name))\n\t}\n\tprInvestment := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif prInvestment.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif prInvestment.Number <= 0 {\n\t\tif name == \"INTRATE\" {\n\t\t\targName = \"investment\"\n\t\t}\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires %s > 0\", name, argName))\n\t}\n\tredemption := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif redemption.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif redemption.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires redemption > 0\", name))\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 5 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\tfrac := yearFrac(settlement.Number, maturity.Number, int(basis.Number))\n\tif frac.Type != ArgNumber {\n\t\treturn frac\n\t}\n\tif name == \"INTRATE\" {\n\t\treturn newNumberFormulaArg((redemption.Number - prInvestment.Number) / prInvestment.Number / frac.Number)\n\t}\n\treturn newNumberFormulaArg((redemption.Number - prInvestment.Number) / redemption.Number / frac.Number)\n}\n\n// DISC function calculates the Discount Rate for a security. The syntax of\n// the function is:\n//\n//\tDISC(settlement,maturity,pr,redemption,[basis])\nfunc (fn *formulaFuncs) DISC(argsList *list.List) formulaArg {\n\treturn fn.discIntrate(\"DISC\", argsList)\n}\n\n// DOLLAR function rounds a supplied number to a specified number of decimal\n// places and then converts this into a text string with a currency format. The\n// syntax of the function is:\n//\n//\tDOLLAR(number,[decimals])\nfunc (fn *formulaFuncs) DOLLAR(argsList *list.List) formulaArg {\n\tif argsList.Len() == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DOLLAR requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DOLLAR requires 1 or 2 arguments\")\n\t}\n\tnumArg := argsList.Front().Value.(formulaArg)\n\tn := numArg.ToNumber()\n\tif n.Type != ArgNumber {\n\t\treturn n\n\t}\n\tdecimals, dot, value := 2, \".\", numArg.Value()\n\tif argsList.Len() == 2 {\n\t\td := argsList.Back().Value.(formulaArg).ToNumber()\n\t\tif d.Type != ArgNumber {\n\t\t\treturn d\n\t\t}\n\t\tif d.Number < 0 {\n\t\t\tvalue = strconv.FormatFloat(fn.round(n.Number, d.Number, down), 'f', -1, 64)\n\t\t}\n\t\tif d.Number >= 128 {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"decimal value should be less than 128\")\n\t\t}\n\t\tif decimals = int(d.Number); decimals < 0 {\n\t\t\tdecimals, dot = 0, \"\"\n\t\t}\n\t}\n\tsymbol := map[CultureName]string{\n\t\tCultureNameUnknown: \"$\",\n\t\tCultureNameEnUS:    \"$\",\n\t\tCultureNameJaJP:    \"¥\",\n\t\tCultureNameKoKR:    \"\\u20a9\",\n\t\tCultureNameZhCN:    \"¥\",\n\t\tCultureNameZhTW:    \"NT$\",\n\t}[fn.f.options.CultureInfo]\n\tnumFmtCode := fmt.Sprintf(\"%s#,##0%s%s;(%s#,##0%s%s)\",\n\t\tsymbol, dot, strings.Repeat(\"0\", decimals), symbol, dot, strings.Repeat(\"0\", decimals))\n\treturn newStringFormulaArg(format(value, numFmtCode, false, CellTypeNumber, nil))\n}\n\n// DOLLARDE function converts a dollar value in fractional notation, into a\n// dollar value expressed as a decimal. The syntax of the function is:\n//\n//\tDOLLARDE(fractional_dollar,fraction)\nfunc (fn *formulaFuncs) DOLLARDE(argsList *list.List) formulaArg {\n\treturn fn.dollar(\"DOLLARDE\", argsList)\n}\n\n// DOLLARFR function converts a dollar value in decimal notation, into a\n// dollar value that is expressed in fractional notation. The syntax of the\n// function is:\n//\n//\tDOLLARFR(decimal_dollar,fraction)\nfunc (fn *formulaFuncs) DOLLARFR(argsList *list.List) formulaArg {\n\treturn fn.dollar(\"DOLLARFR\", argsList)\n}\n\n// dollar is an implementation of the formula functions DOLLARDE and DOLLARFR.\nfunc (fn *formulaFuncs) dollar(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 2 arguments\", name))\n\t}\n\tdollar := argsList.Front().Value.(formulaArg).ToNumber()\n\tif dollar.Type != ArgNumber {\n\t\treturn dollar\n\t}\n\tfrac := argsList.Back().Value.(formulaArg).ToNumber()\n\tif frac.Type != ArgNumber {\n\t\treturn frac\n\t}\n\tif frac.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif frac.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\tcents := math.Mod(dollar.Number, 1)\n\tif name == \"DOLLARDE\" {\n\t\tcents /= frac.Number\n\t\tcents *= math.Pow(10, math.Ceil(math.Log10(frac.Number)))\n\t} else {\n\t\tcents *= frac.Number\n\t\tcents *= math.Pow(10, -math.Ceil(math.Log10(frac.Number)))\n\t}\n\treturn newNumberFormulaArg(math.Floor(dollar.Number) + cents)\n}\n\n// prepareDurationArgs checking and prepare arguments for the formula\n// functions DURATION and MDURATION.\nfunc (fn *formulaFuncs) prepareDurationArgs(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 5 && argsList.Len() != 6 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 5 or 6 arguments\", name))\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity := args.List[0], args.List[1]\n\tif settlement.Number >= maturity.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires maturity > settlement\", name))\n\t}\n\tcoupon := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif coupon.Type != ArgNumber {\n\t\treturn coupon\n\t}\n\tif coupon.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires coupon >= 0\", name))\n\t}\n\tyld := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif yld.Type != ArgNumber {\n\t\treturn yld\n\t}\n\tif yld.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires yld >= 0\", name))\n\t}\n\tfrequency := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif frequency.Type != ArgNumber {\n\t\treturn frequency\n\t}\n\tif !validateFrequency(frequency.Number) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 6 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\treturn newListFormulaArg([]formulaArg{settlement, maturity, coupon, yld, frequency, basis})\n}\n\n// duration is an implementation of the formula function DURATION.\nfunc (fn *formulaFuncs) duration(settlement, maturity, coupon, yld, frequency, basis formulaArg) formulaArg {\n\tfrac := yearFrac(settlement.Number, maturity.Number, int(basis.Number))\n\tif frac.Type != ArgNumber {\n\t\treturn frac\n\t}\n\targumments := list.New().Init()\n\targumments.PushBack(settlement)\n\targumments.PushBack(maturity)\n\targumments.PushBack(frequency)\n\targumments.PushBack(basis)\n\tcoups := fn.COUPNUM(argumments)\n\tduration := 0.0\n\tp := 0.0\n\tcoupon.Number *= 100 / frequency.Number\n\tyld.Number /= frequency.Number\n\tyld.Number++\n\tdiff := frac.Number*frequency.Number - coups.Number\n\tfor t := 1.0; t < coups.Number; t++ {\n\t\ttDiff := t + diff\n\t\tadd := coupon.Number / math.Pow(yld.Number, tDiff)\n\t\tp += add\n\t\tduration += tDiff * add\n\t}\n\tadd := (coupon.Number + 100) / math.Pow(yld.Number, coups.Number+diff)\n\tp += add\n\tduration += (coups.Number + diff) * add\n\tduration /= p\n\tduration /= frequency.Number\n\treturn newNumberFormulaArg(duration)\n}\n\n// DURATION function calculates the Duration (specifically, the Macaulay\n// Duration) of a security that pays periodic interest, assuming a par value\n// of $100. The syntax of the function is:\n//\n//\tDURATION(settlement,maturity,coupon,yld,frequency,[basis])\nfunc (fn *formulaFuncs) DURATION(argsList *list.List) formulaArg {\n\targs := fn.prepareDurationArgs(\"DURATION\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\treturn fn.duration(args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5])\n}\n\n// EFFECT function returns the effective annual interest rate for a given\n// nominal interest rate and number of compounding periods per year. The\n// syntax of the function is:\n//\n//\tEFFECT(nominal_rate,npery)\nfunc (fn *formulaFuncs) EFFECT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"EFFECT requires 2 arguments\")\n\t}\n\trate := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tnpery := argsList.Back().Value.(formulaArg).ToNumber()\n\tif npery.Type != ArgNumber {\n\t\treturn npery\n\t}\n\tif rate.Number <= 0 || npery.Number < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(math.Pow(1+rate.Number/npery.Number, npery.Number) - 1)\n}\n\n// EUROCONVERT function convert a number to euro or from euro to a\n// participating currency. You can also use it to convert a number from one\n// participating currency to another by using the euro as an intermediary\n// (triangulation). The syntax of the function is:\n//\n//\tEUROCONVERT(number,sourcecurrency,targetcurrency[,fullprecision,triangulationprecision])\nfunc (fn *formulaFuncs) EUROCONVERT(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"EUROCONVERT requires at least 3 arguments\")\n\t}\n\tif argsList.Len() > 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"EUROCONVERT allows at most 5 arguments\")\n\t}\n\tnumber := argsList.Front().Value.(formulaArg).ToNumber()\n\tif number.Type != ArgNumber {\n\t\treturn number\n\t}\n\tsourceCurrency := argsList.Front().Next().Value.(formulaArg).Value()\n\ttargetCurrency := argsList.Front().Next().Next().Value.(formulaArg).Value()\n\tfullPrec, triangulationPrec := newBoolFormulaArg(false), newNumberFormulaArg(0)\n\tif argsList.Len() >= 4 {\n\t\tif fullPrec = argsList.Front().Next().Next().Next().Value.(formulaArg).ToBool(); fullPrec.Type != ArgNumber {\n\t\t\treturn fullPrec\n\t\t}\n\t}\n\tif argsList.Len() == 5 {\n\t\tif triangulationPrec = argsList.Back().Value.(formulaArg).ToNumber(); triangulationPrec.Type != ArgNumber {\n\t\t\treturn triangulationPrec\n\t\t}\n\t}\n\tconvertTable := map[string][]float64{\n\t\t\"EUR\": {1.0, 2},\n\t\t\"ATS\": {13.7603, 2},\n\t\t\"BEF\": {40.3399, 0},\n\t\t\"DEM\": {1.95583, 2},\n\t\t\"ESP\": {166.386, 0},\n\t\t\"FIM\": {5.94573, 2},\n\t\t\"FRF\": {6.55957, 2},\n\t\t\"IEP\": {0.787564, 2},\n\t\t\"ITL\": {1936.27, 0},\n\t\t\"LUF\": {40.3399, 0},\n\t\t\"NLG\": {2.20371, 2},\n\t\t\"PTE\": {200.482, 2},\n\t\t\"GRD\": {340.750, 2},\n\t\t\"SIT\": {239.640, 2},\n\t\t\"MTL\": {0.429300, 2},\n\t\t\"CYP\": {0.585274, 2},\n\t\t\"SKK\": {30.1260, 2},\n\t\t\"EEK\": {15.6466, 2},\n\t\t\"LVL\": {0.702804, 2},\n\t\t\"LTL\": {3.45280, 2},\n\t}\n\tsource, ok := convertTable[sourceCurrency]\n\tif !ok {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\ttarget, ok := convertTable[targetCurrency]\n\tif !ok {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif sourceCurrency == targetCurrency {\n\t\treturn number\n\t}\n\tvar res float64\n\tif sourceCurrency == \"EUR\" {\n\t\tres = number.Number * target[0]\n\t} else {\n\t\tintermediate := number.Number / source[0]\n\t\tif triangulationPrec.Number != 0 {\n\t\t\tratio := math.Pow(10, triangulationPrec.Number)\n\t\t\tintermediate = math.Round(intermediate*ratio) / ratio\n\t\t}\n\t\tres = intermediate * target[0]\n\t}\n\tif fullPrec.Number != 1 {\n\t\tratio := math.Pow(10, target[1])\n\t\tres = math.Round(res*ratio) / ratio\n\t}\n\treturn newNumberFormulaArg(res)\n}\n\n// FV function calculates the Future Value of an investment with periodic\n// constant payments and a constant interest rate. The syntax of the function\n// is:\n//\n//\tFV(rate,nper,[pmt],[pv],[type])\nfunc (fn *formulaFuncs) FV(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FV requires at least 3 arguments\")\n\t}\n\tif argsList.Len() > 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FV allows at most 5 arguments\")\n\t}\n\trate := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tnper := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif nper.Type != ArgNumber {\n\t\treturn nper\n\t}\n\tpmt := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif pmt.Type != ArgNumber {\n\t\treturn pmt\n\t}\n\tpv, typ := newNumberFormulaArg(0), newNumberFormulaArg(0)\n\tif argsList.Len() >= 4 {\n\t\tif pv = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); pv.Type != ArgNumber {\n\t\t\treturn pv\n\t\t}\n\t}\n\tif argsList.Len() == 5 {\n\t\tif typ = argsList.Back().Value.(formulaArg).ToNumber(); typ.Type != ArgNumber {\n\t\t\treturn typ\n\t\t}\n\t}\n\tif typ.Number != 0 && typ.Number != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tif rate.Number != 0 {\n\t\treturn newNumberFormulaArg(-pv.Number*math.Pow(1+rate.Number, nper.Number) - pmt.Number*(1+rate.Number*typ.Number)*(math.Pow(1+rate.Number, nper.Number)-1)/rate.Number)\n\t}\n\treturn newNumberFormulaArg(-pv.Number - pmt.Number*nper.Number)\n}\n\n// FVSCHEDULE function calculates the Future Value of an investment with a\n// variable interest rate. The syntax of the function is:\n//\n//\tFVSCHEDULE(principal,schedule)\nfunc (fn *formulaFuncs) FVSCHEDULE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"FVSCHEDULE requires 2 arguments\")\n\t}\n\tpri := argsList.Front().Value.(formulaArg).ToNumber()\n\tif pri.Type != ArgNumber {\n\t\treturn pri\n\t}\n\tprincipal := pri.Number\n\tfor _, arg := range argsList.Back().Value.(formulaArg).ToList() {\n\t\tif arg.Value() == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\trate := arg.ToNumber()\n\t\tif rate.Type != ArgNumber {\n\t\t\treturn rate\n\t\t}\n\t\tprincipal *= 1 + rate.Number\n\t}\n\treturn newNumberFormulaArg(principal)\n}\n\n// INTRATE function calculates the interest rate for a fully invested\n// security. The syntax of the function is:\n//\n//\tINTRATE(settlement,maturity,investment,redemption,[basis])\nfunc (fn *formulaFuncs) INTRATE(argsList *list.List) formulaArg {\n\treturn fn.discIntrate(\"INTRATE\", argsList)\n}\n\n// IPMT function calculates the interest payment, during a specific period of a\n// loan or investment that is paid in constant periodic payments, with a\n// constant interest rate. The syntax of the function is:\n//\n//\tIPMT(rate,per,nper,pv,[fv],[type])\nfunc (fn *formulaFuncs) IPMT(argsList *list.List) formulaArg {\n\treturn fn.ipmt(\"IPMT\", argsList)\n}\n\n// calcIpmt is part of the implementation ipmt.\nfunc calcIpmt(name string, typ, per, pmt, pv, rate formulaArg) formulaArg {\n\tcapital, interest, principal := pv.Number, 0.0, 0.0\n\tfor i := 1; i <= int(per.Number); i++ {\n\t\tif typ.Number != 0 && i == 1 {\n\t\t\tinterest = 0\n\t\t} else {\n\t\t\tinterest = -capital * rate.Number\n\t\t}\n\t\tprincipal = pmt.Number - interest\n\t\tcapital += principal\n\t}\n\tif name == \"IPMT\" {\n\t\treturn newNumberFormulaArg(interest)\n\t}\n\treturn newNumberFormulaArg(principal)\n}\n\n// ipmt is an implementation of the formula functions IPMT and PPMT.\nfunc (fn *formulaFuncs) ipmt(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() < 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 4 arguments\", name))\n\t}\n\tif argsList.Len() > 6 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s allows at most 6 arguments\", name))\n\t}\n\trate := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tper := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif per.Type != ArgNumber {\n\t\treturn per\n\t}\n\tnper := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif nper.Type != ArgNumber {\n\t\treturn nper\n\t}\n\tpv := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif pv.Type != ArgNumber {\n\t\treturn pv\n\t}\n\tfv, typ := newNumberFormulaArg(0), newNumberFormulaArg(0)\n\tif argsList.Len() >= 5 {\n\t\tif fv = argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); fv.Type != ArgNumber {\n\t\t\treturn fv\n\t\t}\n\t}\n\tif argsList.Len() == 6 {\n\t\tif typ = argsList.Back().Value.(formulaArg).ToNumber(); typ.Type != ArgNumber {\n\t\t\treturn typ\n\t\t}\n\t}\n\tif typ.Number != 0 && typ.Number != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tif per.Number <= 0 || per.Number > nper.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\targs := list.New().Init()\n\targs.PushBack(rate)\n\targs.PushBack(nper)\n\targs.PushBack(pv)\n\targs.PushBack(fv)\n\targs.PushBack(typ)\n\tpmt := fn.PMT(args)\n\treturn calcIpmt(name, typ, per, pmt, pv, rate)\n}\n\n// IRR function returns the Internal Rate of Return for a supplied series of\n// periodic cash flows (i.e. an initial investment value and a series of net\n// income values). The syntax of the function is:\n//\n//\tIRR(values,[guess])\nfunc (fn *formulaFuncs) IRR(argsList *list.List) formulaArg {\n\tif argsList.Len() < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IRR requires at least 1 argument\")\n\t}\n\tif argsList.Len() > 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"IRR allows at most 2 arguments\")\n\t}\n\tvalues, guess := argsList.Front().Value.(formulaArg).ToList(), newNumberFormulaArg(0.1)\n\tif argsList.Len() > 1 {\n\t\tif guess = argsList.Back().Value.(formulaArg).ToNumber(); guess.Type != ArgNumber {\n\t\t\treturn guess\n\t\t}\n\t}\n\tx1, x2 := newNumberFormulaArg(0), guess\n\targs := list.New().Init()\n\targs.PushBack(x1)\n\tfor _, v := range values {\n\t\targs.PushBack(v)\n\t}\n\tf1 := fn.NPV(args)\n\targs.Front().Value = x2\n\tf2 := fn.NPV(args)\n\tfor i := 0; i < maxFinancialIterations; i++ {\n\t\tif f1.Number*f2.Number < 0 {\n\t\t\tbreak\n\t\t}\n\t\tif math.Abs(f1.Number) < math.Abs(f2.Number) {\n\t\t\tx1.Number += 1.6 * (x1.Number - x2.Number)\n\t\t\targs.Front().Value = x1\n\t\t\tf1 = fn.NPV(args)\n\t\t\tcontinue\n\t\t}\n\t\tx2.Number += 1.6 * (x2.Number - x1.Number)\n\t\targs.Front().Value = x2\n\t\tf2 = fn.NPV(args)\n\t}\n\tif f1.Number*f2.Number > 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\targs.Front().Value = x1\n\tf := fn.NPV(args)\n\tvar rtb, dx, xMid, fMid float64\n\tif f.Number < 0 {\n\t\trtb = x1.Number\n\t\tdx = x2.Number - x1.Number\n\t} else {\n\t\trtb = x2.Number\n\t\tdx = x1.Number - x2.Number\n\t}\n\tfor i := 0; i < maxFinancialIterations; i++ {\n\t\tdx *= 0.5\n\t\txMid = rtb + dx\n\t\targs.Front().Value = newNumberFormulaArg(xMid)\n\t\tfMid = fn.NPV(args).Number\n\t\tif fMid <= 0 {\n\t\t\trtb = xMid\n\t\t}\n\t\tif math.Abs(fMid) < financialPrecision || math.Abs(dx) < financialPrecision {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn newNumberFormulaArg(xMid)\n}\n\n// ISPMT function calculates the interest paid during a specific period of a\n// loan or investment. The syntax of the function is:\n//\n//\tISPMT(rate,per,nper,pv)\nfunc (fn *formulaFuncs) ISPMT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ISPMT requires 4 arguments\")\n\t}\n\trate := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tper := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif per.Type != ArgNumber {\n\t\treturn per\n\t}\n\tnper := argsList.Back().Prev().Value.(formulaArg).ToNumber()\n\tif nper.Type != ArgNumber {\n\t\treturn nper\n\t}\n\tpv := argsList.Back().Value.(formulaArg).ToNumber()\n\tif pv.Type != ArgNumber {\n\t\treturn pv\n\t}\n\tpr, payment, num := pv.Number, pv.Number/nper.Number, 0.0\n\tfor i := 0; i <= int(per.Number); i++ {\n\t\tnum = rate.Number * pr * -1\n\t\tpr -= payment\n\t\tif i == int(nper.Number) {\n\t\t\tnum = 0\n\t\t}\n\t}\n\treturn newNumberFormulaArg(num)\n}\n\n// MDURATION function calculates the Modified Macaulay Duration of a security\n// that pays periodic interest, assuming a par value of $100. The syntax of\n// the function is:\n//\n//\tMDURATION(settlement,maturity,coupon,yld,frequency,[basis])\nfunc (fn *formulaFuncs) MDURATION(argsList *list.List) formulaArg {\n\targs := fn.prepareDurationArgs(\"MDURATION\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tduration := fn.duration(args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5])\n\tif duration.Type != ArgNumber {\n\t\treturn duration\n\t}\n\treturn newNumberFormulaArg(duration.Number / (1 + args.List[3].Number/args.List[4].Number))\n}\n\n// MIRR function returns the Modified Internal Rate of Return for a supplied\n// series of periodic cash flows (i.e. a set of values, which includes an\n// initial investment value and a series of net income values). The syntax of\n// the function is:\n//\n//\tMIRR(values,finance_rate,reinvest_rate)\nfunc (fn *formulaFuncs) MIRR(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"MIRR requires 3 arguments\")\n\t}\n\tvalues := argsList.Front().Value.(formulaArg).ToList()\n\tfinanceRate := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif financeRate.Type != ArgNumber {\n\t\treturn financeRate\n\t}\n\treinvestRate := argsList.Back().Value.(formulaArg).ToNumber()\n\tif reinvestRate.Type != ArgNumber {\n\t\treturn reinvestRate\n\t}\n\tn, fr, rr, npvPos, npvNeg := len(values), 1+financeRate.Number, 1+reinvestRate.Number, 0.0, 0.0\n\tfor i, v := range values {\n\t\tval := v.ToNumber()\n\t\tif val.Number >= 0 {\n\t\t\tnpvPos += val.Number / math.Pow(rr, float64(i))\n\t\t\tcontinue\n\t\t}\n\t\tnpvNeg += val.Number / math.Pow(fr, float64(i))\n\t}\n\tif npvNeg == 0 || npvPos == 0 || reinvestRate.Number <= -1 {\n\t\treturn newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)\n\t}\n\treturn newNumberFormulaArg(math.Pow(-npvPos*math.Pow(rr, float64(n))/(npvNeg*rr), 1/(float64(n)-1)) - 1)\n}\n\n// NOMINAL function returns the nominal interest rate for a given effective\n// interest rate and number of compounding periods per year. The syntax of\n// the function is:\n//\n//\tNOMINAL(effect_rate,npery)\nfunc (fn *formulaFuncs) NOMINAL(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NOMINAL requires 2 arguments\")\n\t}\n\trate := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tnpery := argsList.Back().Value.(formulaArg).ToNumber()\n\tif npery.Type != ArgNumber {\n\t\treturn npery\n\t}\n\tif rate.Number <= 0 || npery.Number < 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(npery.Number * (math.Pow(rate.Number+1, 1/npery.Number) - 1))\n}\n\n// NPER function calculates the number of periods required to pay off a loan,\n// for a constant periodic payment and a constant interest rate. The syntax\n// of the function is:\n//\n//\tNPER(rate,pmt,pv,[fv],[type])\nfunc (fn *formulaFuncs) NPER(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NPER requires at least 3 arguments\")\n\t}\n\tif argsList.Len() > 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NPER allows at most 5 arguments\")\n\t}\n\trate := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tpmt := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif pmt.Type != ArgNumber {\n\t\treturn pmt\n\t}\n\tpv := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif pv.Type != ArgNumber {\n\t\treturn pv\n\t}\n\tfv, typ := newNumberFormulaArg(0), newNumberFormulaArg(0)\n\tif argsList.Len() >= 4 {\n\t\tif fv = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); fv.Type != ArgNumber {\n\t\t\treturn fv\n\t\t}\n\t}\n\tif argsList.Len() == 5 {\n\t\tif typ = argsList.Back().Value.(formulaArg).ToNumber(); typ.Type != ArgNumber {\n\t\t\treturn typ\n\t\t}\n\t}\n\tif typ.Number != 0 && typ.Number != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tif pmt.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif rate.Number != 0 {\n\t\tp := math.Log((pmt.Number*(1+rate.Number*typ.Number)/rate.Number-fv.Number)/(pv.Number+pmt.Number*(1+rate.Number*typ.Number)/rate.Number)) / math.Log(1+rate.Number)\n\t\treturn newNumberFormulaArg(p)\n\t}\n\treturn newNumberFormulaArg((-pv.Number - fv.Number) / pmt.Number)\n}\n\n// NPV function calculates the Net Present Value of an investment, based on a\n// supplied discount rate, and a series of future payments and income. The\n// syntax of the function is:\n//\n//\tNPV(rate,value1,[value2],[value3],...)\nfunc (fn *formulaFuncs) NPV(argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"NPV requires at least 2 arguments\")\n\t}\n\trate := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tval, i := 0.0, 1\n\tfor arg := argsList.Front().Next(); arg != nil; arg = arg.Next() {\n\t\tnum := arg.Value.(formulaArg).ToNumber()\n\t\tif num.Type != ArgNumber {\n\t\t\tcontinue\n\t\t}\n\t\tval += num.Number / math.Pow(1+rate.Number, float64(i))\n\t\ti++\n\t}\n\treturn newNumberFormulaArg(val)\n}\n\n// aggrBetween is a part of implementation of the formula function ODDFPRICE.\nfunc aggrBetween(startPeriod, endPeriod float64, initialValue []float64, f func(acc []float64, index float64) []float64) []float64 {\n\tvar s []float64\n\tif startPeriod <= endPeriod {\n\t\tfor i := startPeriod; i <= endPeriod; i++ {\n\t\t\ts = append(s, i)\n\t\t}\n\t} else {\n\t\tfor i := startPeriod; i >= endPeriod; i-- {\n\t\t\ts = append(s, i)\n\t\t}\n\t}\n\treturn fold(f, initialValue, s)\n}\n\n// fold is a part of implementation of the formula function ODDFPRICE.\nfunc fold(f func(acc []float64, index float64) []float64, state []float64, source []float64) []float64 {\n\tlength, value := len(source), state\n\tfor index := 0; length > index; index++ {\n\t\tvalue = f(value, source[index])\n\t}\n\treturn value\n}\n\n// changeMonth is a part of implementation of the formula function ODDFPRICE.\nfunc changeMonth(date time.Time, numMonths float64, returnLastMonth bool) time.Time {\n\toffsetDay := 0\n\tif returnLastMonth && date.Day() == getDaysInMonth(date.Year(), int(date.Month())) {\n\t\toffsetDay--\n\t}\n\tnewDate := date.AddDate(0, int(numMonths), offsetDay)\n\tif returnLastMonth {\n\t\tlastDay := getDaysInMonth(newDate.Year(), int(newDate.Month()))\n\t\treturn timeFromExcelTime(daysBetween(excelMinTime1900.Unix(), makeDate(newDate.Year(), newDate.Month(), lastDay))+1, false)\n\t}\n\treturn newDate\n}\n\n// datesAggregate is a part of implementation of the formula function\n// ODDFPRICE.\nfunc datesAggregate(startDate, endDate time.Time, numMonths float64, f func(pcd, ncd time.Time) float64, acc float64, returnLastMonth bool) (time.Time, time.Time, float64) {\n\tfrontDate, trailingDate := startDate, endDate\n\ts1 := frontDate.After(endDate) || frontDate.Equal(endDate)\n\ts2 := endDate.After(frontDate) || endDate.Equal(frontDate)\n\tstop := s2\n\tif numMonths > 0 {\n\t\tstop = s1\n\t}\n\tfor !stop {\n\t\ttrailingDate = frontDate\n\t\tfrontDate = changeMonth(frontDate, numMonths, returnLastMonth)\n\t\tfn := f(frontDate, trailingDate)\n\t\tacc += fn\n\t\ts1 = frontDate.After(endDate) || frontDate.Equal(endDate)\n\t\ts2 = endDate.After(frontDate) || endDate.Equal(frontDate)\n\t\tstop = s2\n\t\tif numMonths > 0 {\n\t\t\tstop = s1\n\t\t}\n\t}\n\treturn frontDate, trailingDate, acc\n}\n\n// coupNumber is a part of implementation of the formula function ODDFPRICE.\nfunc coupNumber(maturity, settlement, numMonths float64) float64 {\n\tmaturityTime, settlementTime := timeFromExcelTime(maturity, false), timeFromExcelTime(settlement, false)\n\tmy, mm, md := maturityTime.Year(), maturityTime.Month(), maturityTime.Day()\n\tsy, sm, sd := settlementTime.Year(), settlementTime.Month(), settlementTime.Day()\n\tcouponsTemp, endOfMonthTemp := 0.0, getDaysInMonth(my, int(mm)) == md\n\tendOfMonth := endOfMonthTemp\n\tif !endOfMonthTemp && mm != 2 && md > 28 && md < getDaysInMonth(my, int(mm)) {\n\t\tendOfMonth = getDaysInMonth(sy, int(sm)) == sd\n\t}\n\tstartDate := changeMonth(settlementTime, 0, endOfMonth)\n\tcoupons := couponsTemp\n\tif startDate.After(settlementTime) {\n\t\tcoupons++\n\t}\n\tdate := changeMonth(startDate, numMonths, endOfMonth)\n\tf := func(pcd, ncd time.Time) float64 {\n\t\treturn 1\n\t}\n\t_, _, result := datesAggregate(date, maturityTime, numMonths, f, coupons, endOfMonth)\n\treturn result\n}\n\n// prepareOddYldOrPrArg checking and prepare yield or price arguments for the\n// formula functions ODDFPRICE, ODDFYIELD, ODDLPRICE and ODDLYIELD.\nfunc prepareOddYldOrPrArg(name string, arg formulaArg) formulaArg {\n\tyldOrPr := arg.ToNumber()\n\tif yldOrPr.Type != ArgNumber {\n\t\treturn yldOrPr\n\t}\n\tif (name == \"ODDFPRICE\" || name == \"ODDLPRICE\") && yldOrPr.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires yld >= 0\", name))\n\t}\n\tif (name == \"ODDFYIELD\" || name == \"ODDLYIELD\") && yldOrPr.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires pr > 0\", name))\n\t}\n\treturn yldOrPr\n}\n\n// prepareOddfArgs checking and prepare arguments for the formula\n// functions ODDFPRICE and ODDFYIELD.\nfunc (fn *formulaFuncs) prepareOddfArgs(name string, argsList *list.List) formulaArg {\n\tdateValues := fn.prepareDataValueArgs(4, argsList)\n\tif dateValues.Type != ArgList {\n\t\treturn dateValues\n\t}\n\tsettlement, maturity, issue, firstCoupon := dateValues.List[0], dateValues.List[1], dateValues.List[2], dateValues.List[3]\n\tif issue.Number >= settlement.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires settlement > issue\", name))\n\t}\n\tif settlement.Number >= firstCoupon.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires first_coupon > settlement\", name))\n\t}\n\tif firstCoupon.Number >= maturity.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires maturity > first_coupon\", name))\n\t}\n\trate := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tif rate.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires rate >= 0\", name))\n\t}\n\tyldOrPr := prepareOddYldOrPrArg(name, argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg))\n\tif yldOrPr.Type != ArgNumber {\n\t\treturn yldOrPr\n\t}\n\tredemption := argsList.Front().Next().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif redemption.Type != ArgNumber {\n\t\treturn redemption\n\t}\n\tif redemption.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires redemption > 0\", name))\n\t}\n\tfrequency := argsList.Front().Next().Next().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif frequency.Type != ArgNumber {\n\t\treturn frequency\n\t}\n\tif !validateFrequency(frequency.Number) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 9 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\treturn newListFormulaArg([]formulaArg{settlement, maturity, issue, firstCoupon, rate, yldOrPr, redemption, frequency, basis})\n}\n\n// ODDFPRICE function calculates the price per $100 face value of a security\n// with an odd (short or long) first period. The syntax of the function is:\n//\n//\tODDFPRICE(settlement,maturity,issue,first_coupon,rate,yld,redemption,frequency,[basis])\nfunc (fn *formulaFuncs) ODDFPRICE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 8 && argsList.Len() != 9 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ODDFPRICE requires 8 or 9 arguments\")\n\t}\n\targs := fn.prepareOddfArgs(\"ODDFPRICE\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity, issue, firstCoupon, rate, yld, redemption, frequency, basisArg := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5], args.List[6], args.List[7], args.List[8]\n\tif basisArg.Number < 0 || basisArg.Number > 4 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"invalid basis\")\n\t}\n\tissueTime := timeFromExcelTime(issue.Number, false)\n\tsettlementTime := timeFromExcelTime(settlement.Number, false)\n\tmaturityTime := timeFromExcelTime(maturity.Number, false)\n\tfirstCouponTime := timeFromExcelTime(firstCoupon.Number, false)\n\tbasis := int(basisArg.Number)\n\tmonthDays := getDaysInMonth(maturityTime.Year(), int(maturityTime.Month()))\n\treturnLastMonth := monthDays == maturityTime.Day()\n\tnumMonths := 12 / frequency.Number\n\tnumMonthsNeg := -numMonths\n\tmat := changeMonth(maturityTime, numMonthsNeg, returnLastMonth)\n\tpcd, _, _ := datesAggregate(mat, firstCouponTime, numMonthsNeg, func(d1, d2 time.Time) float64 {\n\t\treturn 0\n\t}, 0, returnLastMonth)\n\tif !pcd.Equal(firstCouponTime) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tfnArgs := list.New().Init()\n\tfnArgs.PushBack(settlement)\n\tfnArgs.PushBack(maturity)\n\tfnArgs.PushBack(frequency)\n\tfnArgs.PushBack(basisArg)\n\te := fn.COUPDAYS(fnArgs)\n\tn := fn.COUPNUM(fnArgs)\n\tm := frequency.Number\n\tdfc := coupdays(issueTime, firstCouponTime, basis)\n\tif dfc < e.Number {\n\t\tdsc := coupdays(settlementTime, firstCouponTime, basis)\n\t\ta := coupdays(issueTime, settlementTime, basis)\n\t\tx := yld.Number/m + 1\n\t\ty := dsc / e.Number\n\t\tp1 := x\n\t\tp3 := math.Pow(p1, n.Number-1+y)\n\t\tterm1 := redemption.Number / p3\n\t\tterm2 := 100 * rate.Number / m * dfc / e.Number / math.Pow(p1, y)\n\t\tf := func(acc []float64, index float64) []float64 {\n\t\t\treturn []float64{acc[0] + 100*rate.Number/m/math.Pow(p1, index-1+y)}\n\t\t}\n\t\tterm3 := aggrBetween(2, math.Floor(n.Number), []float64{0}, f)\n\t\tp2 := rate.Number / m\n\t\tterm4 := a / e.Number * p2 * 100\n\t\treturn newNumberFormulaArg(term1 + term2 + term3[0] - term4)\n\t}\n\tfnArgs.Init()\n\tfnArgs.PushBack(issue)\n\tfnArgs.PushBack(firstCoupon)\n\tfnArgs.PushBack(frequency)\n\tnc := fn.COUPNUM(fnArgs)\n\tlastCoupon := firstCoupon.Number\n\taggrFunc := func(acc []float64, index float64) []float64 {\n\t\tlastCouponTime := timeFromExcelTime(lastCoupon, false)\n\t\tearlyCoupon := daysBetween(excelMinTime1900.Unix(), makeDate(lastCouponTime.Year(), time.Month(float64(lastCouponTime.Month())+numMonthsNeg), lastCouponTime.Day())) + 1\n\t\tearlyCouponTime := timeFromExcelTime(earlyCoupon, false)\n\t\tnl := e.Number\n\t\tif basis == 1 {\n\t\t\tnl = coupdays(earlyCouponTime, lastCouponTime, basis)\n\t\t}\n\t\tdci := coupdays(issueTime, lastCouponTime, basis)\n\t\tif index > 1 {\n\t\t\tdci = nl\n\t\t}\n\t\tstartDate := earlyCoupon\n\t\tif issue.Number > earlyCoupon {\n\t\t\tstartDate = issue.Number\n\t\t}\n\t\tendDate := lastCoupon\n\t\tif settlement.Number < lastCoupon {\n\t\t\tendDate = settlement.Number\n\t\t}\n\t\tstartDateTime := timeFromExcelTime(startDate, false)\n\t\tendDateTime := timeFromExcelTime(endDate, false)\n\t\ta := coupdays(startDateTime, endDateTime, basis)\n\t\tlastCoupon = earlyCoupon\n\t\tdcnl := acc[0]\n\t\tanl := acc[1]\n\t\treturn []float64{dcnl + dci/nl, anl + a/nl}\n\t}\n\tag := aggrBetween(math.Floor(nc.Number), 1, []float64{0, 0}, aggrFunc)\n\tdcnl, anl := ag[0], ag[1]\n\tdsc := 0.0\n\tfnArgs.Init()\n\tfnArgs.PushBack(settlement)\n\tfnArgs.PushBack(firstCoupon)\n\tfnArgs.PushBack(frequency)\n\tif basis == 2 || basis == 3 {\n\t\td := timeFromExcelTime(fn.COUPNCD(fnArgs).Number, false)\n\t\tdsc = coupdays(settlementTime, d, basis)\n\t} else {\n\t\td := timeFromExcelTime(fn.COUPPCD(fnArgs).Number, false)\n\t\ta := coupdays(d, settlementTime, basis)\n\t\tdsc = e.Number - a\n\t}\n\tnq := coupNumber(firstCoupon.Number, settlement.Number, numMonths)\n\tfnArgs.Init()\n\tfnArgs.PushBack(firstCoupon)\n\tfnArgs.PushBack(maturity)\n\tfnArgs.PushBack(frequency)\n\tfnArgs.PushBack(basisArg)\n\tn = fn.COUPNUM(fnArgs)\n\tx := yld.Number/m + 1\n\ty := dsc / e.Number\n\tp1 := x\n\tp3 := math.Pow(p1, y+nq+n.Number)\n\tterm1 := redemption.Number / p3\n\tterm2 := 100 * rate.Number / m * dcnl / math.Pow(p1, nq+y)\n\tf := func(acc []float64, index float64) []float64 {\n\t\treturn []float64{acc[0] + 100*rate.Number/m/math.Pow(p1, index+nq+y)}\n\t}\n\tterm3 := aggrBetween(1, math.Floor(n.Number), []float64{0}, f)\n\tterm4 := 100 * rate.Number / m * anl\n\treturn newNumberFormulaArg(term1 + term2 + term3[0] - term4)\n}\n\n// getODDFPRICE is a part of implementation of the formula function ODDFPRICE.\nfunc getODDFPRICE(f func(yld float64) float64, x, cnt, prec float64) float64 {\n\tconst maxCnt = 20.0\n\td := func(f func(yld float64) float64, x float64) float64 {\n\t\treturn (f(x+prec) - f(x-prec)) / (2 * prec)\n\t}\n\tfx, Fx := f(x), d(f, x)\n\tnewX := x - (fx / Fx)\n\tif math.Abs(newX-x) < prec {\n\t\treturn newX\n\t} else if cnt > maxCnt {\n\t\treturn newX\n\t}\n\treturn getODDFPRICE(f, newX, cnt+1, prec)\n}\n\n// ODDFYIELD function calculates the yield of a security with an odd (short or\n// long) first period. The syntax of the function is:\n//\n//\tODDFYIELD(settlement,maturity,issue,first_coupon,rate,pr,redemption,frequency,[basis])\nfunc (fn *formulaFuncs) ODDFYIELD(argsList *list.List) formulaArg {\n\tif argsList.Len() != 8 && argsList.Len() != 9 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"ODDFYIELD requires 8 or 9 arguments\")\n\t}\n\targs := fn.prepareOddfArgs(\"ODDFYIELD\", argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity, issue, firstCoupon, rate, pr, redemption, frequency, basisArg := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5], args.List[6], args.List[7], args.List[8]\n\tif basisArg.Number < 0 || basisArg.Number > 4 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"invalid basis\")\n\t}\n\tsettlementTime := timeFromExcelTime(settlement.Number, false)\n\tmaturityTime := timeFromExcelTime(maturity.Number, false)\n\tyears := coupdays(settlementTime, maturityTime, int(basisArg.Number))\n\tpx := pr.Number - 100\n\tnum := rate.Number*years*100 - px\n\tdenum := px/4 + years*px/2 + years*100\n\tguess := num / denum\n\tf := func(yld float64) float64 {\n\t\tfnArgs := list.New().Init()\n\t\tfnArgs.PushBack(settlement)\n\t\tfnArgs.PushBack(maturity)\n\t\tfnArgs.PushBack(issue)\n\t\tfnArgs.PushBack(firstCoupon)\n\t\tfnArgs.PushBack(rate)\n\t\tfnArgs.PushBack(newNumberFormulaArg(yld))\n\t\tfnArgs.PushBack(redemption)\n\t\tfnArgs.PushBack(frequency)\n\t\tfnArgs.PushBack(basisArg)\n\t\treturn pr.Number - fn.ODDFPRICE(fnArgs).Number\n\t}\n\tif result := getODDFPRICE(f, guess, 0, 1e-7); !math.IsInf(result, 0) {\n\t\treturn newNumberFormulaArg(result)\n\t}\n\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n}\n\n// prepareOddlArgs checking and prepare arguments for the formula\n// functions ODDLPRICE and ODDLYIELD.\nfunc (fn *formulaFuncs) prepareOddlArgs(name string, argsList *list.List) formulaArg {\n\tdateValues := fn.prepareDataValueArgs(3, argsList)\n\tif dateValues.Type != ArgList {\n\t\treturn dateValues\n\t}\n\tsettlement, maturity, lastInterest := dateValues.List[0], dateValues.List[1], dateValues.List[2]\n\tif lastInterest.Number >= settlement.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires settlement > last_interest\", name))\n\t}\n\tif settlement.Number >= maturity.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires maturity > settlement\", name))\n\t}\n\trate := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tif rate.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires rate >= 0\", name))\n\t}\n\tyldOrPr := prepareOddYldOrPrArg(name, argsList.Front().Next().Next().Next().Next().Value.(formulaArg))\n\tif yldOrPr.Type != ArgNumber {\n\t\treturn yldOrPr\n\t}\n\tredemption := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif redemption.Type != ArgNumber {\n\t\treturn redemption\n\t}\n\tif redemption.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires redemption > 0\", name))\n\t}\n\tfrequency := argsList.Front().Next().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif frequency.Type != ArgNumber {\n\t\treturn frequency\n\t}\n\tif !validateFrequency(frequency.Number) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 8 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\treturn newListFormulaArg([]formulaArg{settlement, maturity, lastInterest, rate, yldOrPr, redemption, frequency, basis})\n}\n\n// oddl is an implementation of the formula functions ODDLPRICE and ODDLYIELD.\nfunc (fn *formulaFuncs) oddl(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 7 && argsList.Len() != 8 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 7 or 8 arguments\", name))\n\t}\n\targs := fn.prepareOddlArgs(name, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity, lastInterest, rate, prOrYld, redemption, frequency, basisArg := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5], args.List[6], args.List[7]\n\tif basisArg.Number < 0 || basisArg.Number > 4 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"invalid basis\")\n\t}\n\tsettlementTime := timeFromExcelTime(settlement.Number, false)\n\tmaturityTime := timeFromExcelTime(maturity.Number, false)\n\tbasis := int(basisArg.Number)\n\tnumMonths := 12 / frequency.Number\n\tfnArgs := list.New().Init()\n\tfnArgs.PushBack(lastInterest)\n\tfnArgs.PushBack(maturity)\n\tfnArgs.PushBack(frequency)\n\tfnArgs.PushBack(basisArg)\n\tnc := fn.COUPNUM(fnArgs)\n\tearlyCoupon := lastInterest.Number\n\taggrFunc := func(acc []float64, index float64) []float64 {\n\t\tearlyCouponTime := timeFromExcelTime(earlyCoupon, false)\n\t\tlateCouponTime := changeMonth(earlyCouponTime, numMonths, false)\n\t\tlateCoupon, _ := timeToExcelTime(lateCouponTime, false)\n\t\tnl := coupdays(earlyCouponTime, lateCouponTime, basis)\n\t\tdci := coupdays(earlyCouponTime, maturityTime, basis)\n\t\tif index < nc.Number {\n\t\t\tdci = nl\n\t\t}\n\t\tvar a float64\n\t\tif lateCoupon < settlement.Number {\n\t\t\ta = dci\n\t\t} else if earlyCoupon < settlement.Number {\n\t\t\ta = coupdays(earlyCouponTime, settlementTime, basis)\n\t\t}\n\t\tstartDate := earlyCoupon\n\t\tif settlement.Number > earlyCoupon {\n\t\t\tstartDate = settlement.Number\n\t\t}\n\t\tendDate := lateCoupon\n\t\tif maturity.Number < lateCoupon {\n\t\t\tendDate = maturity.Number\n\t\t}\n\t\tstartDateTime := timeFromExcelTime(startDate, false)\n\t\tendDateTime := timeFromExcelTime(endDate, false)\n\t\tdsc := coupdays(startDateTime, endDateTime, basis)\n\t\tearlyCoupon = lateCoupon\n\t\tdcnl := acc[0]\n\t\tanl := acc[1]\n\t\tdscnl := acc[2]\n\t\treturn []float64{dcnl + dci/nl, anl + a/nl, dscnl + dsc/nl}\n\t}\n\tag := aggrBetween(1, math.Floor(nc.Number), []float64{0, 0, 0}, aggrFunc)\n\tdcnl, anl, dscnl := ag[0], ag[1], ag[2]\n\tx := 100.0 * rate.Number / frequency.Number\n\tterm1 := dcnl*x + redemption.Number\n\tif name == \"ODDLPRICE\" {\n\t\tterm2 := dscnl*prOrYld.Number/frequency.Number + 1\n\t\tterm3 := anl * x\n\t\treturn newNumberFormulaArg(term1/term2 - term3)\n\t}\n\tterm2 := anl*x + prOrYld.Number\n\tterm3 := frequency.Number / dscnl\n\treturn newNumberFormulaArg((term1 - term2) / term2 * term3)\n}\n\n// ODDLPRICE function calculates the price per $100 face value of a security\n// with an odd (short or long) last period. The syntax of the function is:\n//\n//\tODDLPRICE(settlement,maturity,last_interest,rate,yld,redemption,frequency,[basis])\nfunc (fn *formulaFuncs) ODDLPRICE(argsList *list.List) formulaArg {\n\treturn fn.oddl(\"ODDLPRICE\", argsList)\n}\n\n// ODDLYIELD function calculates the yield of a security with an odd (short or\n// long) last period. The syntax of the function is:\n//\n//\tODDLYIELD(settlement,maturity,last_interest,rate,pr,redemption,frequency,[basis])\nfunc (fn *formulaFuncs) ODDLYIELD(argsList *list.List) formulaArg {\n\treturn fn.oddl(\"ODDLYIELD\", argsList)\n}\n\n// PDURATION function calculates the number of periods required for an\n// investment to reach a specified future value. The syntax of the function\n// is:\n//\n//\tPDURATION(rate,pv,fv)\nfunc (fn *formulaFuncs) PDURATION(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PDURATION requires 3 arguments\")\n\t}\n\trate := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tpv := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif pv.Type != ArgNumber {\n\t\treturn pv\n\t}\n\tfv := argsList.Back().Value.(formulaArg).ToNumber()\n\tif fv.Type != ArgNumber {\n\t\treturn fv\n\t}\n\tif rate.Number <= 0 || pv.Number <= 0 || fv.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg((math.Log(fv.Number) - math.Log(pv.Number)) / math.Log(1+rate.Number))\n}\n\n// PMT function calculates the constant periodic payment required to pay off\n// (or partially pay off) a loan or investment, with a constant interest\n// rate, over a specified period. The syntax of the function is:\n//\n//\tPMT(rate,nper,pv,[fv],[type])\nfunc (fn *formulaFuncs) PMT(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PMT requires at least 3 arguments\")\n\t}\n\tif argsList.Len() > 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PMT allows at most 5 arguments\")\n\t}\n\trate := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tnper := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif nper.Type != ArgNumber {\n\t\treturn nper\n\t}\n\tpv := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif pv.Type != ArgNumber {\n\t\treturn pv\n\t}\n\tfv, typ := newNumberFormulaArg(0), newNumberFormulaArg(0)\n\tif argsList.Len() >= 4 {\n\t\tif fv = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); fv.Type != ArgNumber {\n\t\t\treturn fv\n\t\t}\n\t}\n\tif argsList.Len() == 5 {\n\t\tif typ = argsList.Back().Value.(formulaArg).ToNumber(); typ.Type != ArgNumber {\n\t\t\treturn typ\n\t\t}\n\t}\n\tif typ.Number != 0 && typ.Number != 1 {\n\t\treturn newErrorFormulaArg(formulaErrorNA, formulaErrorNA)\n\t}\n\tif rate.Number != 0 {\n\t\tp := (-fv.Number - pv.Number*math.Pow(1+rate.Number, nper.Number)) / (1 + rate.Number*typ.Number) / ((math.Pow(1+rate.Number, nper.Number) - 1) / rate.Number)\n\t\treturn newNumberFormulaArg(p)\n\t}\n\treturn newNumberFormulaArg((-pv.Number - fv.Number) / nper.Number)\n}\n\n// PPMT function calculates the payment on the principal, during a specific\n// period of a loan or investment that is paid in constant periodic payments,\n// with a constant interest rate. The syntax of the function is:\n//\n//\tPPMT(rate,per,nper,pv,[fv],[type])\nfunc (fn *formulaFuncs) PPMT(argsList *list.List) formulaArg {\n\treturn fn.ipmt(\"PPMT\", argsList)\n}\n\n// price is an implementation of the formula function PRICE.\nfunc (fn *formulaFuncs) price(settlement, maturity, rate, yld, redemption, frequency, basis formulaArg) formulaArg {\n\tif basis.Number < 0 || basis.Number > 4 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"invalid basis\")\n\t}\n\targsList := list.New().Init()\n\targsList.PushBack(settlement)\n\targsList.PushBack(maturity)\n\targsList.PushBack(frequency)\n\targsList.PushBack(basis)\n\te := fn.COUPDAYS(argsList)\n\tdsc := fn.COUPDAYSNC(argsList).Number / e.Number\n\tn := fn.COUPNUM(argsList)\n\ta := fn.COUPDAYBS(argsList)\n\tret := 0.0\n\tif n.Number > 1 {\n\t\tret = redemption.Number / math.Pow(1+yld.Number/frequency.Number, n.Number-1+dsc)\n\t\tret -= 100 * rate.Number / frequency.Number * a.Number / e.Number\n\t\tt1 := 100 * rate.Number / frequency.Number\n\t\tt2 := 1 + yld.Number/frequency.Number\n\t\tfor k := 0.0; k < n.Number; k++ {\n\t\t\tret += t1 / math.Pow(t2, k+dsc)\n\t\t}\n\t} else {\n\t\tdsc = e.Number - a.Number\n\t\tt1 := 100*(rate.Number/frequency.Number) + redemption.Number\n\t\tt2 := (yld.Number/frequency.Number)*(dsc/e.Number) + 1\n\t\tt3 := 100 * (rate.Number / frequency.Number) * (a.Number / e.Number)\n\t\tret = t1/t2 - t3\n\t}\n\treturn newNumberFormulaArg(ret)\n}\n\n// checkPriceYieldArgs checking and prepare arguments for the formula functions\n// PRICE and YIELD.\nfunc checkPriceYieldArgs(name string, rate, prYld, redemption, frequency formulaArg) formulaArg {\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tif rate.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf(\"%s requires rate >= 0\", name))\n\t}\n\tif prYld.Type != ArgNumber {\n\t\treturn prYld\n\t}\n\tif redemption.Type != ArgNumber {\n\t\treturn redemption\n\t}\n\tif name == \"PRICE\" {\n\t\tif prYld.Number < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, \"PRICE requires yld >= 0\")\n\t\t}\n\t\tif redemption.Number <= 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, \"PRICE requires redemption > 0\")\n\t\t}\n\t}\n\tif name == \"YIELD\" {\n\t\tif prYld.Number <= 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, \"YIELD requires pr > 0\")\n\t\t}\n\t\tif redemption.Number < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, \"YIELD requires redemption >= 0\")\n\t\t}\n\t}\n\tif frequency.Type != ArgNumber {\n\t\treturn frequency\n\t}\n\tif !validateFrequency(frequency.Number) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newEmptyFormulaArg()\n}\n\n// priceYield is an implementation of the formula functions PRICE and YIELD.\nfunc (fn *formulaFuncs) priceYield(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 6 && argsList.Len() != 7 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 6 or 7 arguments\", name))\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity := args.List[0], args.List[1]\n\trate := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tprYld := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tredemption := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tfrequency := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif arg := checkPriceYieldArgs(name, rate, prYld, redemption, frequency); arg.Type != ArgEmpty {\n\t\treturn arg\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 7 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\tif name == \"PRICE\" {\n\t\treturn fn.price(settlement, maturity, rate, prYld, redemption, frequency, basis)\n\t}\n\treturn fn.yield(settlement, maturity, rate, prYld, redemption, frequency, basis)\n}\n\n// PRICE function calculates the price, per $100 face value of a security that\n// pays periodic interest. The syntax of the function is:\n//\n//\tPRICE(settlement,maturity,rate,yld,redemption,frequency,[basis])\nfunc (fn *formulaFuncs) PRICE(argsList *list.List) formulaArg {\n\treturn fn.priceYield(\"PRICE\", argsList)\n}\n\n// PRICEDISC function calculates the price, per $100 face value of a\n// discounted security. The syntax of the function is:\n//\n//\tPRICEDISC(settlement,maturity,discount,redemption,[basis])\nfunc (fn *formulaFuncs) PRICEDISC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 && argsList.Len() != 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PRICEDISC requires 4 or 5 arguments\")\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity := args.List[0], args.List[1]\n\tif maturity.Number <= settlement.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"PRICEDISC requires maturity > settlement\")\n\t}\n\tdiscount := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif discount.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif discount.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"PRICEDISC requires discount > 0\")\n\t}\n\tredemption := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif redemption.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif redemption.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"PRICEDISC requires redemption > 0\")\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 5 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\tfrac := yearFrac(settlement.Number, maturity.Number, int(basis.Number))\n\tif frac.Type != ArgNumber {\n\t\treturn frac\n\t}\n\treturn newNumberFormulaArg(redemption.Number * (1 - discount.Number*frac.Number))\n}\n\n// PRICEMAT function calculates the price, per $100 face value of a security\n// that pays interest at maturity. The syntax of the function is:\n//\n//\tPRICEMAT(settlement,maturity,issue,rate,yld,[basis])\nfunc (fn *formulaFuncs) PRICEMAT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 5 && argsList.Len() != 6 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PRICEMAT requires 5 or 6 arguments\")\n\t}\n\targs := fn.prepareDataValueArgs(3, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity, issue := args.List[0], args.List[1], args.List[2]\n\tif settlement.Number >= maturity.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"PRICEMAT requires maturity > settlement\")\n\t}\n\tif issue.Number >= settlement.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"PRICEMAT requires settlement > issue\")\n\t}\n\trate := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tif rate.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"PRICEMAT requires rate >= 0\")\n\t}\n\tyld := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif yld.Type != ArgNumber {\n\t\treturn yld\n\t}\n\tif yld.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"PRICEMAT requires yld >= 0\")\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 6 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\tdsm := yearFrac(settlement.Number, maturity.Number, int(basis.Number))\n\tif dsm.Type != ArgNumber {\n\t\treturn dsm\n\t}\n\tdis := yearFrac(issue.Number, settlement.Number, int(basis.Number))\n\tdim := yearFrac(issue.Number, maturity.Number, int(basis.Number))\n\treturn newNumberFormulaArg(((1+dim.Number*rate.Number)/(1+dsm.Number*yld.Number) - dis.Number*rate.Number) * 100)\n}\n\n// PV function calculates the Present Value of an investment, based on a\n// series of future payments. The syntax of the function is:\n//\n//\tPV(rate,nper,pmt,[fv],[type])\nfunc (fn *formulaFuncs) PV(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PV requires at least 3 arguments\")\n\t}\n\tif argsList.Len() > 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"PV allows at most 5 arguments\")\n\t}\n\trate := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tnper := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif nper.Type != ArgNumber {\n\t\treturn nper\n\t}\n\tpmt := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif pmt.Type != ArgNumber {\n\t\treturn pmt\n\t}\n\tfv := newNumberFormulaArg(0)\n\tif argsList.Len() >= 4 {\n\t\tif fv = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); fv.Type != ArgNumber {\n\t\t\treturn fv\n\t\t}\n\t}\n\tt := newNumberFormulaArg(0)\n\tif argsList.Len() == 5 {\n\t\tif t = argsList.Back().Value.(formulaArg).ToNumber(); t.Type != ArgNumber {\n\t\t\treturn t\n\t\t}\n\t\tif t.Number != 0 {\n\t\t\tt.Number = 1\n\t\t}\n\t}\n\tif rate.Number == 0 {\n\t\treturn newNumberFormulaArg(-pmt.Number*nper.Number - fv.Number)\n\t}\n\treturn newNumberFormulaArg((((1-math.Pow(1+rate.Number, nper.Number))/rate.Number)*pmt.Number*(1+rate.Number*t.Number) - fv.Number) / math.Pow(1+rate.Number, nper.Number))\n}\n\n// rate is an implementation of the formula function RATE.\nfunc (fn *formulaFuncs) rate(nper, pmt, pv, fv, t, guess formulaArg) formulaArg {\n\tmaxIter, iter, isClose, epsMax, rate := 100, 0, false, 1e-6, guess.Number\n\tfor iter < maxIter && !isClose {\n\t\tt1 := math.Pow(rate+1, nper.Number)\n\t\tt2 := math.Pow(rate+1, nper.Number-1)\n\t\trt := rate*t.Number + 1\n\t\tp0 := pmt.Number * (t1 - 1)\n\t\tf1 := fv.Number + t1*pv.Number + p0*rt/rate\n\t\tn1 := nper.Number * t2 * pv.Number\n\t\tn2 := p0 * rt / math.Pow(rate, 2)\n\t\tf2 := math.Nextafter(n1, n1) - math.Nextafter(n2, n2)\n\t\tf3 := (nper.Number*pmt.Number*t2*rt + p0*t.Number) / rate\n\t\tdelta := f1 / (f2 + f3)\n\t\tif math.Abs(delta) < epsMax {\n\t\t\tisClose = true\n\t\t}\n\t\titer++\n\t\trate -= delta\n\t}\n\treturn newNumberFormulaArg(rate)\n}\n\n// RATE function calculates the interest rate required to pay off a specified\n// amount of a loan, or to reach a target amount on an investment, over a\n// given period. The syntax of the function is:\n//\n//\tRATE(nper,pmt,pv,[fv],[type],[guess])\nfunc (fn *formulaFuncs) RATE(argsList *list.List) formulaArg {\n\tif argsList.Len() < 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"RATE requires at least 3 arguments\")\n\t}\n\tif argsList.Len() > 6 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"RATE allows at most 6 arguments\")\n\t}\n\tnper := argsList.Front().Value.(formulaArg).ToNumber()\n\tif nper.Type != ArgNumber {\n\t\treturn nper\n\t}\n\tpmt := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif pmt.Type != ArgNumber {\n\t\treturn pmt\n\t}\n\tpv := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif pv.Type != ArgNumber {\n\t\treturn pv\n\t}\n\tfv := newNumberFormulaArg(0)\n\tif argsList.Len() >= 4 {\n\t\tif fv = argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber(); fv.Type != ArgNumber {\n\t\t\treturn fv\n\t\t}\n\t}\n\tt := newNumberFormulaArg(0)\n\tif argsList.Len() >= 5 {\n\t\tif t = argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); t.Type != ArgNumber {\n\t\t\treturn t\n\t\t}\n\t\tif t.Number != 0 {\n\t\t\tt.Number = 1\n\t\t}\n\t}\n\tguess := newNumberFormulaArg(0.1)\n\tif argsList.Len() == 6 {\n\t\tif guess = argsList.Back().Value.(formulaArg).ToNumber(); guess.Type != ArgNumber {\n\t\t\treturn guess\n\t\t}\n\t}\n\treturn fn.rate(nper, pmt, pv, fv, t, guess)\n}\n\n// RECEIVED function calculates the amount received at maturity for a fully\n// invested security. The syntax of the function is:\n//\n//\tRECEIVED(settlement,maturity,investment,discount,[basis])\nfunc (fn *formulaFuncs) RECEIVED(argsList *list.List) formulaArg {\n\tif argsList.Len() < 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"RECEIVED requires at least 4 arguments\")\n\t}\n\tif argsList.Len() > 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"RECEIVED allows at most 5 arguments\")\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity := args.List[0], args.List[1]\n\tinvestment := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif investment.Type != ArgNumber {\n\t\treturn investment\n\t}\n\tdiscount := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif discount.Type != ArgNumber {\n\t\treturn discount\n\t}\n\tif discount.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"RECEIVED requires discount > 0\")\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 5 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\tfrac := yearFrac(settlement.Number, maturity.Number, int(basis.Number))\n\tif frac.Type != ArgNumber {\n\t\treturn frac\n\t}\n\treturn newNumberFormulaArg(investment.Number / (1 - discount.Number*frac.Number))\n}\n\n// RRI function calculates the equivalent interest rate for an investment with\n// specified present value, future value and duration. The syntax of the\n// function is:\n//\n//\tRRI(nper,pv,fv)\nfunc (fn *formulaFuncs) RRI(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"RRI requires 3 arguments\")\n\t}\n\tnper := argsList.Front().Value.(formulaArg).ToNumber()\n\tpv := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tfv := argsList.Back().Value.(formulaArg).ToNumber()\n\tif nper.Type != ArgNumber || pv.Type != ArgNumber || fv.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif nper.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"RRI requires nper argument to be > 0\")\n\t}\n\tif pv.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"RRI requires pv argument to be > 0\")\n\t}\n\tif fv.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"RRI requires fv argument to be >= 0\")\n\t}\n\treturn newNumberFormulaArg(math.Pow(fv.Number/pv.Number, 1/nper.Number) - 1)\n}\n\n// SLN function calculates the straight line depreciation of an asset for one\n// period. The syntax of the function is:\n//\n//\tSLN(cost,salvage,life)\nfunc (fn *formulaFuncs) SLN(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SLN requires 3 arguments\")\n\t}\n\tcost := argsList.Front().Value.(formulaArg).ToNumber()\n\tsalvage := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tlife := argsList.Back().Value.(formulaArg).ToNumber()\n\tif cost.Type != ArgNumber || salvage.Type != ArgNumber || life.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif life.Number == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"SLN requires life argument to be > 0\")\n\t}\n\treturn newNumberFormulaArg((cost.Number - salvage.Number) / life.Number)\n}\n\n// SYD function calculates the sum-of-years' digits depreciation for a\n// specified period in the lifetime of an asset. The syntax of the function\n// is:\n//\n//\tSYD(cost,salvage,life,per)\nfunc (fn *formulaFuncs) SYD(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SYD requires 4 arguments\")\n\t}\n\tcost := argsList.Front().Value.(formulaArg).ToNumber()\n\tsalvage := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tlife := argsList.Back().Prev().Value.(formulaArg).ToNumber()\n\tper := argsList.Back().Value.(formulaArg).ToNumber()\n\tif cost.Type != ArgNumber || salvage.Type != ArgNumber || life.Type != ArgNumber || per.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tif life.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"SYD requires life argument to be > 0\")\n\t}\n\tif per.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"SYD requires per argument to be > 0\")\n\t}\n\tif per.Number > life.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(((cost.Number - salvage.Number) * (life.Number - per.Number + 1) * 2) / (life.Number * (life.Number + 1)))\n}\n\n// TBILLEQ function calculates the bond-equivalent yield for a Treasury Bill.\n// The syntax of the function is:\n//\n//\tTBILLEQ(settlement,maturity,discount)\nfunc (fn *formulaFuncs) TBILLEQ(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TBILLEQ requires 3 arguments\")\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity := args.List[0], args.List[1]\n\tdsm := maturity.Number - settlement.Number\n\tif dsm > 365 || maturity.Number <= settlement.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tdiscount := argsList.Back().Value.(formulaArg).ToNumber()\n\tif discount.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif discount.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg((365 * discount.Number) / (360 - discount.Number*dsm))\n}\n\n// TBILLPRICE function returns the price, per $100 face value, of a Treasury\n// Bill. The syntax of the function is:\n//\n//\tTBILLPRICE(settlement,maturity,discount)\nfunc (fn *formulaFuncs) TBILLPRICE(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TBILLPRICE requires 3 arguments\")\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity := args.List[0], args.List[1]\n\tdsm := maturity.Number - settlement.Number\n\tif dsm > 365 || maturity.Number <= settlement.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tdiscount := argsList.Back().Value.(formulaArg).ToNumber()\n\tif discount.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif discount.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(100 * (1 - discount.Number*dsm/360))\n}\n\n// TBILLYIELD function calculates the yield of a Treasury Bill. The syntax of\n// the function is:\n//\n//\tTBILLYIELD(settlement,maturity,pr)\nfunc (fn *formulaFuncs) TBILLYIELD(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"TBILLYIELD requires 3 arguments\")\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity := args.List[0], args.List[1]\n\tdsm := maturity.Number - settlement.Number\n\tif dsm > 365 || maturity.Number <= settlement.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tpr := argsList.Back().Value.(formulaArg).ToNumber()\n\tif pr.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif pr.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(((100 - pr.Number) / pr.Number) * (360 / dsm))\n}\n\n// prepareVdbArgs checking and prepare arguments for the formula function\n// VDB.\nfunc (fn *formulaFuncs) prepareVdbArgs(argsList *list.List) formulaArg {\n\tcost := argsList.Front().Value.(formulaArg).ToNumber()\n\tif cost.Type != ArgNumber {\n\t\treturn cost\n\t}\n\tif cost.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"VDB requires cost >= 0\")\n\t}\n\tsalvage := argsList.Front().Next().Value.(formulaArg).ToNumber()\n\tif salvage.Type != ArgNumber {\n\t\treturn salvage\n\t}\n\tif salvage.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"VDB requires salvage >= 0\")\n\t}\n\tlife := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif life.Type != ArgNumber {\n\t\treturn life\n\t}\n\tif life.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"VDB requires life > 0\")\n\t}\n\tstartPeriod := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif startPeriod.Type != ArgNumber {\n\t\treturn startPeriod\n\t}\n\tif startPeriod.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"VDB requires start_period > 0\")\n\t}\n\tendPeriod := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif endPeriod.Type != ArgNumber {\n\t\treturn endPeriod\n\t}\n\tif startPeriod.Number > endPeriod.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"VDB requires start_period <= end_period\")\n\t}\n\tif endPeriod.Number > life.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"VDB requires end_period <= life\")\n\t}\n\tfactor := newNumberFormulaArg(2)\n\tif argsList.Len() > 5 {\n\t\tif factor = argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); factor.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t\tif factor.Number < 0 {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"VDB requires factor >= 0\")\n\t\t}\n\t}\n\treturn newListFormulaArg([]formulaArg{cost, salvage, life, startPeriod, endPeriod, factor})\n}\n\n// vdb is a part of implementation of the formula function VDB.\nfunc (fn *formulaFuncs) vdb(cost, salvage, life, life1, period, factor formulaArg) formulaArg {\n\tvar ddb, vdb, sln, term float64\n\tendInt, cs, nowSln := math.Ceil(period.Number), cost.Number-salvage.Number, false\n\tddbArgs := list.New()\n\tfor i := 1.0; i <= endInt; i++ {\n\t\tif !nowSln {\n\t\t\tddbArgs.Init()\n\t\t\tddbArgs.PushBack(cost)\n\t\t\tddbArgs.PushBack(salvage)\n\t\t\tddbArgs.PushBack(life)\n\t\t\tddbArgs.PushBack(newNumberFormulaArg(i))\n\t\t\tddbArgs.PushBack(factor)\n\t\t\tddb = fn.DDB(ddbArgs).Number\n\t\t\tsln = cs / (life1.Number - i + 1)\n\t\t\tif sln > ddb && i != endInt {\n\t\t\t\tterm = sln\n\t\t\t\tnowSln = true\n\t\t\t} else {\n\t\t\t\tterm = ddb\n\t\t\t\tcs -= ddb\n\t\t\t}\n\t\t} else {\n\t\t\tterm = sln\n\t\t}\n\t\tif i == endInt {\n\t\t\tterm *= period.Number + 1 - endInt\n\t\t}\n\t\tvdb += term\n\t}\n\treturn newNumberFormulaArg(vdb)\n}\n\n// VDB function calculates the depreciation of an asset, using the Double\n// Declining Balance Method, or another specified depreciation rate, for a\n// specified period (including partial periods). The syntax of the function\n// is:\n//\n//\tVDB(cost,salvage,life,start_period,end_period,[factor],[no_switch])\nfunc (fn *formulaFuncs) VDB(argsList *list.List) formulaArg {\n\tif argsList.Len() < 5 || argsList.Len() > 7 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"VDB requires 5 or 7 arguments\")\n\t}\n\targs := fn.prepareVdbArgs(argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tcost, salvage, life, startPeriod, endPeriod, factor := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5]\n\tnoSwitch := newBoolFormulaArg(false)\n\tif argsList.Len() > 6 {\n\t\tif noSwitch = argsList.Back().Value.(formulaArg).ToBool(); noSwitch.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\tstartInt, endInt, vdb, ddbArgs := math.Floor(startPeriod.Number), math.Ceil(endPeriod.Number), newNumberFormulaArg(0), list.New()\n\tif noSwitch.Number == 1 {\n\t\tfor i := startInt + 1; i <= endInt; i++ {\n\t\t\tddbArgs.Init()\n\t\t\tddbArgs.PushBack(cost)\n\t\t\tddbArgs.PushBack(salvage)\n\t\t\tddbArgs.PushBack(life)\n\t\t\tddbArgs.PushBack(newNumberFormulaArg(i))\n\t\t\tddbArgs.PushBack(factor)\n\t\t\tterm := fn.DDB(ddbArgs)\n\t\t\tswitch i {\n\t\t\tcase startInt + 1:\n\t\t\t\tterm.Number *= math.Min(endPeriod.Number, startInt+1) - startPeriod.Number\n\t\t\tcase endInt:\n\t\t\t\tterm.Number *= endPeriod.Number + 1 - endInt\n\t\t\t}\n\t\t\tvdb.Number += term.Number\n\t\t}\n\t\treturn vdb\n\t}\n\tcost.Number -= fn.vdb(cost, salvage, life, life, startPeriod, factor).Number\n\treturn fn.vdb(cost, salvage, life, newNumberFormulaArg(life.Number-startPeriod.Number), newNumberFormulaArg(endPeriod.Number-startPeriod.Number), factor)\n}\n\n// prepareXArgs prepare arguments for the formula function XIRR and XNPV.\nfunc (fn *formulaFuncs) prepareXArgs(values, dates formulaArg) (valuesArg, datesArg []float64, err formulaArg) {\n\tfor _, arg := range values.ToList() {\n\t\tif numArg := arg.ToNumber(); numArg.Type == ArgNumber {\n\t\t\tvaluesArg = append(valuesArg, numArg.Number)\n\t\t\tcontinue\n\t\t}\n\t\terr = newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\treturn\n\t}\n\tif len(valuesArg) < 2 {\n\t\terr = newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\treturn\n\t}\n\tdate := 0.0\n\tfor _, arg := range dates.ToList() {\n\t\tif arg.Type == ArgNumber {\n\t\t\tdatesArg = append(datesArg, arg.Number)\n\t\t\tif arg.Number < date {\n\t\t\t\terr = newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdate = arg.Number\n\t\t\tcontinue\n\t\t}\n\t\terr = newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\treturn\n\t}\n\tif len(valuesArg) != len(datesArg) {\n\t\terr = newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\treturn\n\t}\n\terr = newEmptyFormulaArg()\n\treturn\n}\n\n// xirr is an implementation of the formula function XIRR.\nfunc (fn *formulaFuncs) xirr(values, dates []float64, guess float64) formulaArg {\n\tpositive, negative := false, false\n\tfor i := 0; i < len(values); i++ {\n\t\tif values[i] > 0 {\n\t\t\tpositive = true\n\t\t}\n\t\tif values[i] < 0 {\n\t\t\tnegative = true\n\t\t}\n\t}\n\tif !positive || !negative {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\tresult, epsMax, count, maxIterate, err := guess, 1e-10, 0, 50, false\n\tfor {\n\t\tresultValue := xirrPart1(values, dates, result)\n\t\tnewRate := result - resultValue/xirrPart2(values, dates, result)\n\t\tepsRate := math.Abs(newRate - result)\n\t\tresult = newRate\n\t\tcount++\n\t\tif epsRate <= epsMax || math.Abs(resultValue) <= epsMax {\n\t\t\tbreak\n\t\t}\n\t\tif count > maxIterate {\n\t\t\terr = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif err || math.IsNaN(result) || math.IsInf(result, 0) {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t}\n\treturn newNumberFormulaArg(result)\n}\n\n// xirrPart1 is a part of implementation of the formula function XIRR.\nfunc xirrPart1(values, dates []float64, rate float64) float64 {\n\tr := rate + 1\n\tresult := values[0]\n\tvlen := len(values)\n\tfirstDate := dates[0]\n\tfor i := 1; i < vlen; i++ {\n\t\tresult += values[i] / math.Pow(r, (dates[i]-firstDate)/365)\n\t}\n\treturn result\n}\n\n// xirrPart2 is a part of implementation of the formula function XIRR.\nfunc xirrPart2(values, dates []float64, rate float64) float64 {\n\tr := rate + 1\n\tresult := 0.0\n\tvlen := len(values)\n\tfirstDate := dates[0]\n\tfor i := 1; i < vlen; i++ {\n\t\tfrac := (dates[i] - firstDate) / 365\n\t\tresult -= frac * values[i] / math.Pow(r, frac+1)\n\t}\n\treturn result\n}\n\n// XIRR function returns the Internal Rate of Return for a supplied series of\n// cash flows (i.e. a set of values, which includes an initial investment\n// value and a series of net income values) occurring at a series of supplied\n// dates. The syntax of the function is:\n//\n//\tXIRR(values,dates,[guess])\nfunc (fn *formulaFuncs) XIRR(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 && argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"XIRR requires 2 or 3 arguments\")\n\t}\n\tvalues, dates, err := fn.prepareXArgs(argsList.Front().Value.(formulaArg), argsList.Front().Next().Value.(formulaArg))\n\tif err.Type != ArgEmpty {\n\t\treturn err\n\t}\n\tguess := newNumberFormulaArg(0)\n\tif argsList.Len() == 3 {\n\t\tif guess = argsList.Back().Value.(formulaArg).ToNumber(); guess.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t\tif guess.Number <= -1 {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"XIRR requires guess > -1\")\n\t\t}\n\t}\n\treturn fn.xirr(values, dates, guess.Number)\n}\n\n// XNPV function calculates the Net Present Value for a schedule of cash flows\n// that is not necessarily periodic. The syntax of the function is:\n//\n//\tXNPV(rate,values,dates)\nfunc (fn *formulaFuncs) XNPV(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"XNPV requires 3 arguments\")\n\t}\n\trate := argsList.Front().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tif rate.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"XNPV requires rate > 0\")\n\t}\n\tvalues, dates, err := fn.prepareXArgs(argsList.Front().Next().Value.(formulaArg), argsList.Back().Value.(formulaArg))\n\tif err.Type != ArgEmpty {\n\t\treturn err\n\t}\n\tdate1, xnpv := dates[0], 0.0\n\tfor idx, value := range values {\n\t\txnpv += value / math.Pow(1+rate.Number, (dates[idx]-date1)/365)\n\t}\n\treturn newNumberFormulaArg(xnpv)\n}\n\n// yield is an implementation of the formula function YIELD.\nfunc (fn *formulaFuncs) yield(settlement, maturity, rate, pr, redemption, frequency, basis formulaArg) formulaArg {\n\tpriceN, yield1, yield2 := newNumberFormulaArg(0), newNumberFormulaArg(0), newNumberFormulaArg(1)\n\tprice1 := fn.price(settlement, maturity, rate, yield1, redemption, frequency, basis)\n\tif price1.Type != ArgNumber {\n\t\treturn price1\n\t}\n\tprice2 := fn.price(settlement, maturity, rate, yield2, redemption, frequency, basis)\n\tyieldN := newNumberFormulaArg((yield2.Number - yield1.Number) * 0.5)\n\tfor iter := 0; iter < 100 && priceN.Number != pr.Number; iter++ {\n\t\tpriceN = fn.price(settlement, maturity, rate, yieldN, redemption, frequency, basis)\n\t\tif pr.Number == price1.Number {\n\t\t\treturn yield1\n\t\t} else if pr.Number == price2.Number {\n\t\t\treturn yield2\n\t\t} else if pr.Number == priceN.Number {\n\t\t\treturn yieldN\n\t\t} else if pr.Number < price2.Number {\n\t\t\tyield2.Number *= 2.0\n\t\t\tprice2 = fn.price(settlement, maturity, rate, yield2, redemption, frequency, basis)\n\t\t\tyieldN.Number = (yield2.Number - yield1.Number) * 0.5\n\t\t} else {\n\t\t\tif pr.Number < priceN.Number {\n\t\t\t\tyield1 = yieldN\n\t\t\t\tprice1 = priceN\n\t\t\t} else {\n\t\t\t\tyield2 = yieldN\n\t\t\t\tprice2 = priceN\n\t\t\t}\n\t\t\tf1 := (yield2.Number - yield1.Number) * ((pr.Number - price2.Number) / (price1.Number - price2.Number))\n\t\t\tyieldN.Number = yield2.Number - math.Nextafter(f1, f1)\n\t\t}\n\t}\n\treturn yieldN\n}\n\n// YIELD function calculates the Yield of a security that pays periodic\n// interest. The syntax of the function is:\n//\n//\tYIELD(settlement,maturity,rate,pr,redemption,frequency,[basis])\nfunc (fn *formulaFuncs) YIELD(argsList *list.List) formulaArg {\n\treturn fn.priceYield(\"YIELD\", argsList)\n}\n\n// YIELDDISC function calculates the annual yield of a discounted security.\n// The syntax of the function is:\n//\n//\tYIELDDISC(settlement,maturity,pr,redemption,[basis])\nfunc (fn *formulaFuncs) YIELDDISC(argsList *list.List) formulaArg {\n\tif argsList.Len() != 4 && argsList.Len() != 5 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"YIELDDISC requires 4 or 5 arguments\")\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity := args.List[0], args.List[1]\n\tpr := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif pr.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif pr.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"YIELDDISC requires pr > 0\")\n\t}\n\tredemption := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif redemption.Type != ArgNumber {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tif redemption.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"YIELDDISC requires redemption > 0\")\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 5 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\tfrac := yearFrac(settlement.Number, maturity.Number, int(basis.Number))\n\tif frac.Type != ArgNumber {\n\t\treturn frac\n\t}\n\treturn newNumberFormulaArg((redemption.Number/pr.Number - 1) / frac.Number)\n}\n\n// YIELDMAT function calculates the annual yield of a security that pays\n// interest at maturity. The syntax of the function is:\n//\n//\tYIELDMAT(settlement,maturity,issue,rate,pr,[basis])\nfunc (fn *formulaFuncs) YIELDMAT(argsList *list.List) formulaArg {\n\tif argsList.Len() != 5 && argsList.Len() != 6 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"YIELDMAT requires 5 or 6 arguments\")\n\t}\n\targs := fn.prepareDataValueArgs(2, argsList)\n\tif args.Type != ArgList {\n\t\treturn args\n\t}\n\tsettlement, maturity := args.List[0], args.List[1]\n\targ := list.New().Init()\n\tissue := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()\n\tif issue.Type != ArgNumber {\n\t\targ.PushBack(argsList.Front().Next().Next().Value.(formulaArg))\n\t\tissue = fn.DATEVALUE(arg)\n\t\tif issue.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t\t}\n\t}\n\tif issue.Number >= settlement.Number {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"YIELDMAT requires settlement > issue\")\n\t}\n\trate := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif rate.Type != ArgNumber {\n\t\treturn rate\n\t}\n\tif rate.Number < 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"YIELDMAT requires rate >= 0\")\n\t}\n\tpr := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()\n\tif pr.Type != ArgNumber {\n\t\treturn pr\n\t}\n\tif pr.Number <= 0 {\n\t\treturn newErrorFormulaArg(formulaErrorNUM, \"YIELDMAT requires pr > 0\")\n\t}\n\tbasis := newNumberFormulaArg(0)\n\tif argsList.Len() == 6 {\n\t\tif basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\tdim := yearFrac(issue.Number, maturity.Number, int(basis.Number))\n\tif dim.Type != ArgNumber {\n\t\treturn dim\n\t}\n\tdis := yearFrac(issue.Number, settlement.Number, int(basis.Number))\n\tdsm := yearFrac(settlement.Number, maturity.Number, int(basis.Number))\n\tf1 := dim.Number * rate.Number\n\tresult := 1 + math.Nextafter(f1, f1)\n\tresult /= pr.Number/100 + dis.Number*rate.Number\n\tresult--\n\tresult /= dsm.Number\n\treturn newNumberFormulaArg(result)\n}\n\n// Database Functions\n\n// calcDatabase defines the structure for formula database.\ntype calcDatabase struct {\n\tcol, row int\n\tindexMap map[int]int\n\tdatabase [][]formulaArg\n\tcriteria [][]formulaArg\n}\n\n// newCalcDatabase function returns formula database by given data range of\n// cells containing the database, field and criteria range.\nfunc newCalcDatabase(database, field, criteria formulaArg) *calcDatabase {\n\tdb := calcDatabase{\n\t\tindexMap: make(map[int]int),\n\t\tdatabase: database.Matrix,\n\t\tcriteria: criteria.Matrix,\n\t}\n\texp := len(database.Matrix) < 2 || len(database.Matrix[0]) < 1 ||\n\t\tlen(criteria.Matrix) < 2 || len(criteria.Matrix[0]) < 1\n\tif field.Type != ArgEmpty {\n\t\tif db.col = db.columnIndex(database.Matrix, field); exp || db.col < 0 || len(db.database[0]) <= db.col {\n\t\t\treturn nil\n\t\t}\n\t\treturn &db\n\t}\n\tif db.col = -1; exp {\n\t\treturn nil\n\t}\n\treturn &db\n}\n\n// columnIndex return index by specifies column field within the database for\n// which user want to return the count of non-blank cells.\nfunc (db *calcDatabase) columnIndex(database [][]formulaArg, field formulaArg) int {\n\tnum := field.ToNumber()\n\tif num.Type != ArgNumber && len(database) > 0 {\n\t\tfor i := 0; i < len(database[0]); i++ {\n\t\t\tif title := database[0][i]; strings.EqualFold(title.Value(), field.Value()) {\n\t\t\t\treturn i\n\t\t\t}\n\t\t}\n\t\treturn -1\n\t}\n\treturn int(num.Number - 1)\n}\n\n// criteriaEval evaluate formula criteria expression.\nfunc (db *calcDatabase) criteriaEval() bool {\n\tvar (\n\t\tcolumns, rows = len(db.criteria[0]), len(db.criteria)\n\t\tcriteria      = db.criteria\n\t\tk             int\n\t\tmatched       bool\n\t)\n\tif len(db.indexMap) == 0 {\n\t\tfields := criteria[0]\n\t\tfor j := 0; j < columns; j++ {\n\t\t\tif k = db.columnIndex(db.database, fields[j]); k < 0 {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tdb.indexMap[j] = k\n\t\t}\n\t}\n\tfor i := 1; !matched && i < rows; i++ {\n\t\tmatched = true\n\t\tfor j := 0; matched && j < columns; j++ {\n\t\t\tcriteriaExp := db.criteria[i][j]\n\t\t\tif criteriaExp.Value() == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcriteria := formulaCriteriaParser(criteriaExp)\n\t\t\tcell := db.database[db.row][db.indexMap[j]]\n\t\t\tmatched, _ = formulaCriteriaEval(cell, criteria)\n\t\t}\n\t}\n\treturn matched\n}\n\n// value returns the current cell value.\nfunc (db *calcDatabase) value() formulaArg {\n\tif db.col == -1 {\n\t\treturn db.database[db.row][len(db.database[db.row])-1]\n\t}\n\treturn db.database[db.row][db.col]\n}\n\n// next will return true if find the matched cell in the database.\nfunc (db *calcDatabase) next() bool {\n\tmatched, rows := false, len(db.database)\n\tfor !matched && db.row < rows {\n\t\tif db.row++; db.row < rows {\n\t\t\tmatched = db.criteriaEval()\n\t\t}\n\t}\n\treturn matched\n}\n\n// database is an implementation of the formula functions DAVERAGE, DMAX and DMIN.\nfunc (fn *formulaFuncs) database(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires 3 arguments\", name))\n\t}\n\tdatabase := argsList.Front().Value.(formulaArg)\n\tfield := argsList.Front().Next().Value.(formulaArg)\n\tcriteria := argsList.Back().Value.(formulaArg)\n\tdb := newCalcDatabase(database, field, criteria)\n\tif db == nil {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\targs := list.New()\n\tfor db.next() {\n\t\targs.PushBack(db.value())\n\t}\n\tswitch name {\n\tcase \"DMAX\":\n\t\treturn fn.MAX(args)\n\tcase \"DMIN\":\n\t\treturn fn.MIN(args)\n\tcase \"DPRODUCT\":\n\t\treturn fn.PRODUCT(args)\n\tcase \"DSTDEV\":\n\t\treturn fn.STDEV(args)\n\tcase \"DSTDEVP\":\n\t\treturn fn.STDEVP(args)\n\tcase \"DSUM\":\n\t\treturn fn.SUM(args)\n\tcase \"DVAR\":\n\t\treturn fn.VAR(args)\n\tcase \"DVARP\":\n\t\treturn fn.VARP(args)\n\tdefault:\n\t\treturn fn.AVERAGE(args)\n\t}\n}\n\n// DAVERAGE function calculates the average (statistical mean) of values in a\n// field (column) in a database for selected records, that satisfy\n// user-specified criteria. The syntax of the function is:\n//\n//\tDAVERAGE(database,field,criteria)\nfunc (fn *formulaFuncs) DAVERAGE(argsList *list.List) formulaArg {\n\treturn fn.database(\"DAVERAGE\", argsList)\n}\n\n// dcount is an implementation of the formula functions DCOUNT and DCOUNTA.\nfunc (fn *formulaFuncs) dcount(name string, argsList *list.List) formulaArg {\n\tif argsList.Len() < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s requires at least 2 arguments\", name))\n\t}\n\tif argsList.Len() > 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"%s allows at most 3 arguments\", name))\n\t}\n\tfield := newEmptyFormulaArg()\n\tcriteria := argsList.Back().Value.(formulaArg)\n\tif argsList.Len() > 2 {\n\t\tfield = argsList.Front().Next().Value.(formulaArg)\n\t}\n\tdatabase := argsList.Front().Value.(formulaArg)\n\tdb := newCalcDatabase(database, field, criteria)\n\tif db == nil {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\targs := list.New()\n\tfor db.next() {\n\t\targs.PushBack(db.value())\n\t}\n\tif name == \"DCOUNT\" {\n\t\treturn fn.COUNT(args)\n\t}\n\treturn fn.COUNTA(args)\n}\n\n// DCOUNT function returns the number of cells containing numeric values, in a\n// field (column) of a database for selected records only. The records to be\n// included in the count are those that satisfy a set of one or more\n// user-specified criteria. The syntax of the function is:\n//\n//\tDCOUNT(database,[field],criteria)\nfunc (fn *formulaFuncs) DCOUNT(argsList *list.List) formulaArg {\n\treturn fn.dcount(\"DCOUNT\", argsList)\n}\n\n// DCOUNTA function returns the number of non-blank cells, in a field\n// (column) of a database for selected records only. The records to be\n// included in the count are those that satisfy a set of one or more\n// user-specified criteria. The syntax of the function is:\n//\n//\tDCOUNTA(database,[field],criteria)\nfunc (fn *formulaFuncs) DCOUNTA(argsList *list.List) formulaArg {\n\treturn fn.dcount(\"DCOUNTA\", argsList)\n}\n\n// DGET function returns a single value from a column of a database. The record\n// is selected via a set of one or more user-specified criteria. The syntax of\n// the function is:\n//\n//\tDGET(database,field,criteria)\nfunc (fn *formulaFuncs) DGET(argsList *list.List) formulaArg {\n\tif argsList.Len() != 3 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DGET requires 3 arguments\")\n\t}\n\tdatabase := argsList.Front().Value.(formulaArg)\n\tfield := argsList.Front().Next().Value.(formulaArg)\n\tcriteria := argsList.Back().Value.(formulaArg)\n\tdb := newCalcDatabase(database, field, criteria)\n\tif db == nil {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\t}\n\tvalue := newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)\n\tif db.next() {\n\t\tif value = db.value(); db.next() {\n\t\t\treturn newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)\n\t\t}\n\t}\n\treturn value\n}\n\n// DMAX function finds the maximum value in a field (column) in a database for\n// selected records only. The records to be included in the calculation are\n// defined by a set of one or more user-specified criteria. The syntax of the\n// function is:\n//\n//\tDMAX(database,field,criteria)\nfunc (fn *formulaFuncs) DMAX(argsList *list.List) formulaArg {\n\treturn fn.database(\"DMAX\", argsList)\n}\n\n// DMIN function finds the minimum value in a field (column) in a database for\n// selected records only. The records to be included in the calculation are\n// defined by a set of one or more user-specified criteria. The syntax of the\n// function is:\n//\n//\tDMIN(database,field,criteria)\nfunc (fn *formulaFuncs) DMIN(argsList *list.List) formulaArg {\n\treturn fn.database(\"DMIN\", argsList)\n}\n\n// DPRODUCT function calculates the product of a field (column) in a database\n// for selected records, that satisfy user-specified criteria. The syntax of\n// the function is:\n//\n//\tDPRODUCT(database,field,criteria)\nfunc (fn *formulaFuncs) DPRODUCT(argsList *list.List) formulaArg {\n\treturn fn.database(\"DPRODUCT\", argsList)\n}\n\n// DSTDEV function calculates the sample standard deviation of a field\n// (column) in a database for selected records only. The records to be\n// included in the calculation are defined by a set of one or more\n// user-specified criteria. The syntax of the function is:\n//\n//\tDSTDEV(database,field,criteria)\nfunc (fn *formulaFuncs) DSTDEV(argsList *list.List) formulaArg {\n\treturn fn.database(\"DSTDEV\", argsList)\n}\n\n// DSTDEVP function calculates the standard deviation of a field (column) in a\n// database for selected records only. The records to be included in the\n// calculation are defined by a set of one or more user-specified criteria.\n// The syntax of the function is:\n//\n//\tDSTDEVP(database,field,criteria)\nfunc (fn *formulaFuncs) DSTDEVP(argsList *list.List) formulaArg {\n\treturn fn.database(\"DSTDEVP\", argsList)\n}\n\n// DSUM function calculates the sum of a field (column) in a database for\n// selected records, that satisfy user-specified criteria. The syntax of the\n// function is:\n//\n//\tDSUM(database,field,criteria)\nfunc (fn *formulaFuncs) DSUM(argsList *list.List) formulaArg {\n\treturn fn.database(\"DSUM\", argsList)\n}\n\n// DVAR function calculates the sample variance of a field (column) in a\n// database for selected records only. The records to be included in the\n// calculation are defined by a set of one or more user-specified criteria.\n// The syntax of the function is:\n//\n//\tDVAR(database,field,criteria)\nfunc (fn *formulaFuncs) DVAR(argsList *list.List) formulaArg {\n\treturn fn.database(\"DVAR\", argsList)\n}\n\n// DVARP function calculates the variance (for an entire population), of the\n// values in a field (column) in a database for selected records only. The\n// records to be included in the calculation are defined by a set of one or\n// more user-specified criteria. The syntax of the function is:\n//\n//\tDVARP(database,field,criteria)\nfunc (fn *formulaFuncs) DVARP(argsList *list.List) formulaArg {\n\treturn fn.database(\"DVARP\", argsList)\n}\n\n// DISPIMG function calculates the Kingsoft WPS Office embedded image ID. The\n// syntax of the function is:\n//\n//\tDISPIMG(picture_name,display_mode)\nfunc (fn *formulaFuncs) DISPIMG(argsList *list.List) formulaArg {\n\tif argsList.Len() != 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"DISPIMG requires 2 numeric arguments\")\n\t}\n\treturn argsList.Front().Value.(formulaArg)\n}\n\n// sortbyArgs holds the parsed arguments for the SORTBY function.\ntype sortbyArgs struct {\n\tarray    []formulaArg\n\tcols     int\n\trows     int\n\tsortKeys []sortbyKey\n}\n\n// sortbyKey represents a single sort key with its associated array and order.\ntype sortbyKey struct {\n\tbyArray   []formulaArg\n\tcols      int\n\trows      int\n\tascending bool // true = ascending (1 or omitted), false = descending (-1)\n}\n\n// rowWithKeys pairs a data row with its corresponding sort key values.\ntype rowWithKeys struct {\n\trowData  []formulaArg\n\tsortKeys [][]formulaArg // up to 3 sets of sort keys\n}\n\n// SORTBY function sorts the contents of a range or array based on the values\n// in a corresponding range or array. The syntax of the function is:\n//\n//\tSORTBY(array,by_array1,[sort_order1],[by_array2,sort_order2],[by_array3, sort_order3])\nfunc (fn *formulaFuncs) SORTBY(argsList *list.List) formulaArg {\n\targs, errArg := prepareSortbyArgs(argsList)\n\tif errArg != nil {\n\t\treturn *errArg\n\t}\n\trowsWithKeys := make([]rowWithKeys, args.rows)\n\tfor i := 0; i < args.rows; i++ {\n\t\trowsWithKeys[i].rowData = args.array[i*args.cols : (i+1)*args.cols]\n\t\trowsWithKeys[i].sortKeys = make([][]formulaArg, len(args.sortKeys))\n\t\tfor keyIdx, sortKey := range args.sortKeys {\n\t\t\trowsWithKeys[i].sortKeys[keyIdx] = sortKey.byArray[i*sortKey.cols : (i+1)*sortKey.cols]\n\t\t}\n\t}\n\tsort.Slice(rowsWithKeys, func(i, j int) bool {\n\t\treturn compareRowsForSortby(rowsWithKeys[i], rowsWithKeys[j], args.sortKeys)\n\t})\n\tresult := make([][]formulaArg, args.rows)\n\tfor i, row := range rowsWithKeys {\n\t\tresult[i] = row.rowData\n\t}\n\treturn newMatrixFormulaArg(result)\n}\n\n// checkSortbyArgs checking arguments for the formula function SORTBY.\nfunc checkSortbyArgs(argsList *list.List) formulaArg {\n\targsLen := argsList.Len()\n\tif argsLen < 2 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"SORTBY requires at least 2 arguments\")\n\t}\n\tif argsLen > 7 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"SORTBY takes at most 7 arguments, received %d\", argsLen))\n\t}\n\tif argsLen != 2 && argsLen != 3 && argsLen != 5 && argsLen != 7 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"SORTBY requires 2, 3, 5, or 7 arguments, received %d\", argsLen))\n\t}\n\tarrArg := argsList.Front().Value.(formulaArg).ToList()\n\tif len(arrArg) == 0 {\n\t\treturn newErrorFormulaArg(formulaErrorVALUE, \"missing first argument to SORTBY\")\n\t}\n\tif arrArg[0].Type == ArgError {\n\t\treturn arrArg[0]\n\t}\n\treturn newListFormulaArg(arrArg)\n}\n\n// prepareSortbyArgs prepare arguments for the formula function SORTBY.\nfunc prepareSortbyArgs(argsList *list.List) (sortbyArgs, *formulaArg) {\n\tres := sortbyArgs{}\n\targs := checkSortbyArgs(argsList)\n\tif args.Type != ArgList {\n\t\treturn res, &args\n\t}\n\tres.array = args.List\n\targsLen := argsList.Len()\n\tarray := argsList.Front()\n\ttempList := list.New()\n\ttempList.PushBack(array.Value)\n\trmin, rmax := calcColsRowsMinMax(false, tempList)\n\tcmin, cmax := calcColsRowsMinMax(true, tempList)\n\tres.cols, res.rows = cmax-cmin+1, rmax-rmin+1\n\tbyArray := array.Next()\n\tkeyCount := 0\n\tfor byArray != nil && keyCount < 3 {\n\t\tvar key sortbyKey\n\t\tkey.byArray = byArray.Value.(formulaArg).ToList()\n\t\tif len(key.byArray) == 0 {\n\t\t\terrArg := newErrorFormulaArg(formulaErrorVALUE, \"missing by_array argument to SORTBY\")\n\t\t\treturn res, &errArg\n\t\t}\n\t\tif key.byArray[0].Type == ArgError {\n\t\t\treturn res, &key.byArray[0]\n\t\t}\n\t\ttempList := list.New()\n\t\ttempList.PushBack(byArray.Value)\n\t\trmin, rmax := calcColsRowsMinMax(false, tempList)\n\t\tcmin, cmax := calcColsRowsMinMax(true, tempList)\n\t\tkey.cols, key.rows = cmax-cmin+1, rmax-rmin+1\n\t\tif key.rows != res.rows {\n\t\t\terrArg := newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"by_array dimensions (%d rows) do not match array dimensions (%d rows)\",\n\t\t\t\tkey.rows, res.rows))\n\t\t\treturn res, &errArg\n\t\t}\n\t\tkey.ascending = true\n\t\tnextByArray, errArg := parseSortOrderArg(byArray.Next(), &key, keyCount, argsLen)\n\t\tif errArg != nil {\n\t\t\treturn res, errArg\n\t\t}\n\t\tbyArray = nextByArray\n\t\tres.sortKeys = append(res.sortKeys, key)\n\t\tkeyCount++\n\t}\n\treturn res, nil\n}\n\n// parseSortOrderArg processes the optional sort_order argument for a SORTBY\n// key. It updates the key's ascending field and returns the next element in the\n// list. Returns an error if the sort_order value is invalid.\nfunc parseSortOrderArg(byArray *list.Element, key *sortbyKey, keyCount, argsLen int) (*list.Element, *formulaArg) {\n\tif byArray == nil {\n\t\treturn nil, nil\n\t}\n\tsortOrderArg := byArray.Value.(formulaArg).ToNumber()\n\texpectedSortOrder := false\n\tif keyCount == 0 && (argsLen == 3 || argsLen == 5 || argsLen == 7) {\n\t\texpectedSortOrder = true\n\t} else if keyCount == 1 && (argsLen == 5 || argsLen == 7) {\n\t\texpectedSortOrder = true\n\t} else if keyCount == 2 && argsLen == 7 {\n\t\texpectedSortOrder = true\n\t}\n\tif sortOrderArg.Type == ArgError {\n\t\tif expectedSortOrder {\n\t\t\treturn byArray, &sortOrderArg\n\t\t}\n\t\treturn byArray, nil\n\t}\n\tswitch sortOrderArg.Number {\n\tcase -1:\n\t\tkey.ascending = false\n\t\treturn byArray.Next(), nil\n\tcase 1:\n\t\tkey.ascending = true\n\t\treturn byArray.Next(), nil\n\t}\n\terrArg := newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf(\"sort_order must be 1 or -1, received %v\", sortOrderArg.Number))\n\treturn byArray, &errArg\n}\n\n// compareRowsForSortby compares two rows using multiple sort keys.\n// Returns true if row i should come before row j.\nfunc compareRowsForSortby(i, j rowWithKeys, sortKeys []sortbyKey) bool {\n\tfor idx, sortKey := range sortKeys {\n\t\tlhs, rhs := i.sortKeys[idx], j.sortKeys[idx]\n\t\tfor colIdx := 0; colIdx < len(lhs) && colIdx < len(rhs); colIdx++ {\n\t\t\tcriteria := compareFormulaArg(lhs[colIdx], rhs[colIdx], newNumberFormulaArg(matchModeMaxLess), false)\n\t\t\tif criteria == criteriaEq {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif sortKey.ascending {\n\t\t\t\treturn criteria == criteriaL\n\t\t\t}\n\t\t\treturn criteria == criteriaG\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "calc_test.go",
    "content": "package excelize\n\nimport (\n\t\"container/list\"\n\t\"math\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/xuri/efp\"\n)\n\nfunc prepareCalcData(cellData [][]interface{}) *File {\n\tf := NewFile()\n\tfor r, row := range cellData {\n\t\tfor c, value := range row {\n\t\t\tcell, _ := CoordinatesToCellName(c+1, r+1)\n\t\t\t_ = f.SetCellValue(\"Sheet1\", cell, value)\n\t\t}\n\t}\n\treturn f\n}\n\nfunc TestCalcCellValue(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{1, 4, nil, \"Month\", \"Team\", \"Sales\"},\n\t\t{2, 5, nil, \"Jan\", \"North 1\", 36693},\n\t\t{3, nil, nil, \"Jan\", \"North 2\", 22100},\n\t\t{0, nil, nil, \"Jan\", \"South 1\", 53321},\n\t\t{nil, nil, nil, \"Jan\", \"South 2\", 34440},\n\t\t{nil, nil, nil, \"Feb\", \"North 1\", 29889},\n\t\t{nil, nil, nil, \"Feb\", \"North 2\", 50090},\n\t\t{nil, nil, nil, \"Feb\", \"South 1\", 32080},\n\t\t{nil, nil, nil, \"Feb\", \"South 2\", 45500},\n\t}\n\tmathCalc := map[string]string{\n\t\t\"2^3\":                   \"8\",\n\t\t\"1=1\":                   \"TRUE\",\n\t\t\"1=2\":                   \"FALSE\",\n\t\t\"1<2\":                   \"TRUE\",\n\t\t\"3<2\":                   \"FALSE\",\n\t\t\"1<\\\"-1\\\"\":              \"TRUE\",\n\t\t\"\\\"-1\\\"<1\":              \"FALSE\",\n\t\t\"\\\"-1\\\"<\\\"-2\\\"\":         \"TRUE\",\n\t\t\"2<=3\":                  \"TRUE\",\n\t\t\"2<=1\":                  \"FALSE\",\n\t\t\"1<=\\\"-1\\\"\":             \"TRUE\",\n\t\t\"\\\"-1\\\"<=1\":             \"FALSE\",\n\t\t\"\\\"-1\\\"<=\\\"-2\\\"\":        \"TRUE\",\n\t\t\"2>1\":                   \"TRUE\",\n\t\t\"2>3\":                   \"FALSE\",\n\t\t\"1>\\\"-1\\\"\":              \"FALSE\",\n\t\t\"\\\"-1\\\">-1\":             \"TRUE\",\n\t\t\"\\\"-1\\\">\\\"-2\\\"\":         \"FALSE\",\n\t\t\"2>=1\":                  \"TRUE\",\n\t\t\"2>=3\":                  \"FALSE\",\n\t\t\"1>=\\\"-1\\\"\":             \"FALSE\",\n\t\t\"\\\"-1\\\">=-1\":            \"TRUE\",\n\t\t\"\\\"-1\\\">=\\\"-2\\\"\":        \"FALSE\",\n\t\t\"-----1+1\":              \"0\",\n\t\t\"------1+1\":             \"2\",\n\t\t\"---1---1\":              \"-2\",\n\t\t\"---1----1\":             \"0\",\n\t\t\"1&2\":                   \"12\",\n\t\t\"15%\":                   \"0.15\",\n\t\t\"1+20%\":                 \"1.2\",\n\t\t\"{1}+2\":                 \"3\",\n\t\t\"1+{2}\":                 \"3\",\n\t\t\"{1}+{2}\":               \"3\",\n\t\t\"A1+(B1-C1)\":            \"5\",\n\t\t\"A1+(C1-B1)\":            \"-3\",\n\t\t\"A1&B1&C1\":              \"14\",\n\t\t\"B1+C1\":                 \"4\",\n\t\t\"C1+B1\":                 \"4\",\n\t\t\"C1+C1\":                 \"0\",\n\t\t\"\\\"A\\\"=\\\"A\\\"\":           \"TRUE\",\n\t\t\"\\\"A\\\"<>\\\"A\\\"\":          \"FALSE\",\n\t\t\"TRUE()&FALSE()\":        \"TRUEFALSE\",\n\t\t\"TRUE()&FALSE()<>FALSE\": \"TRUE\",\n\t\t\"TRUE()&\\\"1\\\"\":          \"TRUE1\",\n\t\t\"TRUE<>FALSE()\":         \"TRUE\",\n\t\t\"TRUE<>1&\\\"x\\\"\":         \"TRUE\",\n\t\t// Engineering Functions\n\t\t// BESSELI\n\t\t\"BESSELI(4.5,1)\":    \"15.3892227537359\",\n\t\t\"BESSELI(32,1)\":     \"5502845511211.25\",\n\t\t\"BESSELI({32},1)\":   \"5502845511211.25\",\n\t\t\"BESSELI(32,{1})\":   \"5502845511211.25\",\n\t\t\"BESSELI({32},{1})\": \"5502845511211.25\",\n\t\t// BESSELJ\n\t\t\"BESSELJ(1.9,2)\":     \"0.329925727692387\",\n\t\t\"BESSELJ({1.9},2)\":   \"0.329925727692387\",\n\t\t\"BESSELJ(1.9,{2})\":   \"0.329925727692387\",\n\t\t\"BESSELJ({1.9},{2})\": \"0.329925727692387\",\n\t\t// BESSELK\n\t\t\"BESSELK(0.05,0)\":  \"3.11423403428966\",\n\t\t\"BESSELK(0.05,1)\":  \"19.9096743272486\",\n\t\t\"BESSELK(0.05,2)\":  \"799.501207124235\",\n\t\t\"BESSELK(3,2)\":     \"0.0615104585619118\",\n\t\t\"BESSELK({3},2)\":   \"0.0615104585619118\",\n\t\t\"BESSELK(3,{2})\":   \"0.0615104585619118\",\n\t\t\"BESSELK({3},{2})\": \"0.0615104585619118\",\n\t\t// BESSELY\n\t\t\"BESSELY(0.05,0)\":  \"-1.97931100684153\",\n\t\t\"BESSELY(0.05,1)\":  \"-12.789855163794\",\n\t\t\"BESSELY(0.05,2)\":  \"-509.61489554492\",\n\t\t\"BESSELY(9,2)\":     \"-0.229082087487741\",\n\t\t\"BESSELY({9},2)\":   \"-0.229082087487741\",\n\t\t\"BESSELY(9,{2})\":   \"-0.229082087487741\",\n\t\t\"BESSELY({9},{2})\": \"-0.229082087487741\",\n\t\t// BIN2DEC\n\t\t\"BIN2DEC(\\\"10\\\")\":         \"2\",\n\t\t\"BIN2DEC(\\\"11\\\")\":         \"3\",\n\t\t\"BIN2DEC(\\\"0000000010\\\")\": \"2\",\n\t\t\"BIN2DEC(\\\"1111111110\\\")\": \"-2\",\n\t\t\"BIN2DEC(\\\"110\\\")\":        \"6\",\n\t\t\"BIN2DEC({\\\"110\\\"})\":      \"6\",\n\t\t// BIN2HEX\n\t\t\"BIN2HEX(\\\"10\\\")\":         \"2\",\n\t\t\"BIN2HEX(\\\"0000000001\\\")\": \"1\",\n\t\t\"BIN2HEX(\\\"10\\\",10)\":      \"0000000002\",\n\t\t\"BIN2HEX(\\\"1111111110\\\")\": \"FFFFFFFFFE\",\n\t\t\"BIN2HEX(\\\"11101\\\")\":      \"1D\",\n\t\t\"BIN2HEX({\\\"11101\\\"})\":    \"1D\",\n\t\t// BIN2OCT\n\t\t\"BIN2OCT(\\\"101\\\")\":        \"5\",\n\t\t\"BIN2OCT(\\\"0000000001\\\")\": \"1\",\n\t\t\"BIN2OCT(\\\"10\\\",10)\":      \"0000000002\",\n\t\t\"BIN2OCT(\\\"1111111110\\\")\": \"7777777776\",\n\t\t\"BIN2OCT(\\\"1110\\\")\":       \"16\",\n\t\t\"BIN2OCT({\\\"1110\\\"})\":     \"16\",\n\t\t// BITAND\n\t\t\"BITAND(13,14)\":     \"12\",\n\t\t\"BITAND({13},14)\":   \"12\",\n\t\t\"BITAND(13,{14})\":   \"12\",\n\t\t\"BITAND({13},{14})\": \"12\",\n\t\t// BITLSHIFT\n\t\t\"BITLSHIFT(5,2)\":     \"20\",\n\t\t\"BITLSHIFT({3},5)\":   \"96\",\n\t\t\"BITLSHIFT(3,5)\":     \"96\",\n\t\t\"BITLSHIFT(3,{5})\":   \"96\",\n\t\t\"BITLSHIFT({3},{5})\": \"96\",\n\t\t// BITOR\n\t\t\"BITOR(9,12)\":     \"13\",\n\t\t\"BITOR({9},12)\":   \"13\",\n\t\t\"BITOR(9,{12})\":   \"13\",\n\t\t\"BITOR({9},{12})\": \"13\",\n\t\t// BITRSHIFT\n\t\t\"BITRSHIFT(20,2)\":     \"5\",\n\t\t\"BITRSHIFT(52,4)\":     \"3\",\n\t\t\"BITRSHIFT({52},4)\":   \"3\",\n\t\t\"BITRSHIFT(52,{4})\":   \"3\",\n\t\t\"BITRSHIFT({52},{4})\": \"3\",\n\t\t// BITXOR\n\t\t\"BITXOR(5,6)\":      \"3\",\n\t\t\"BITXOR(9,12)\":     \"5\",\n\t\t\"BITXOR({9},12)\":   \"5\",\n\t\t\"BITXOR(9,{12})\":   \"5\",\n\t\t\"BITXOR({9},{12})\": \"5\",\n\t\t// COMPLEX\n\t\t\"COMPLEX(5,2)\":         \"5+2i\",\n\t\t\"COMPLEX(5,-9)\":        \"5-9i\",\n\t\t\"COMPLEX(-1,2,\\\"j\\\")\":  \"-1+2j\",\n\t\t\"COMPLEX(10,-5,\\\"i\\\")\": \"10-5i\",\n\t\t\"COMPLEX(0,5)\":         \"5i\",\n\t\t\"COMPLEX(3,0)\":         \"3\",\n\t\t\"COMPLEX(0,-2)\":        \"-2i\",\n\t\t\"COMPLEX(0,0)\":         \"0\",\n\t\t\"COMPLEX(0,-1,\\\"j\\\")\":  \"-j\",\n\t\t// CONVERT\n\t\t\"CONVERT(20.2,\\\"m\\\",\\\"yd\\\")\":                    \"22.0909886264217\",\n\t\t\"CONVERT(20.2,\\\"cm\\\",\\\"yd\\\")\":                   \"0.220909886264217\",\n\t\t\"CONVERT(0.2,\\\"gal\\\",\\\"tsp\\\")\":                  \"153.6\",\n\t\t\"CONVERT(5,\\\"gal\\\",\\\"l\\\")\":                      \"18.92705892\",\n\t\t\"CONVERT(0.02,\\\"Gm\\\",\\\"m\\\")\":                    \"20000000\",\n\t\t\"CONVERT(0,\\\"C\\\",\\\"F\\\")\":                        \"32\",\n\t\t\"CONVERT(1,\\\"ly^2\\\",\\\"ly^2\\\")\":                  \"1\",\n\t\t\"CONVERT(0.00194255938572296,\\\"sg\\\",\\\"ozm\\\")\":   \"1\",\n\t\t\"CONVERT(5,\\\"kg\\\",\\\"kg\\\")\":                      \"5\",\n\t\t\"CONVERT(4.5359237E-01,\\\"kg\\\",\\\"lbm\\\")\":         \"1\",\n\t\t\"CONVERT(0.2,\\\"kg\\\",\\\"hg\\\")\":                    \"2\",\n\t\t\"CONVERT(12.345000000000001,\\\"km\\\",\\\"m\\\")\":      \"12345\",\n\t\t\"CONVERT(12345,\\\"m\\\",\\\"km\\\")\":                   \"12.345\",\n\t\t\"CONVERT(0.621371192237334,\\\"mi\\\",\\\"km\\\")\":      \"1\",\n\t\t\"CONVERT(1.23450000000000E+05,\\\"ang\\\",\\\"um\\\")\":  \"12.345\",\n\t\t\"CONVERT(1.23450000000000E+02,\\\"kang\\\",\\\"um\\\")\": \"12.345\",\n\t\t\"CONVERT(1000,\\\"dal\\\",\\\"hl\\\")\":                  \"100\",\n\t\t\"CONVERT(1,\\\"yd\\\",\\\"ft\\\")\":                      \"2.99999999999999\",\n\t\t\"CONVERT(20,\\\"C\\\",\\\"F\\\")\":                       \"68\",\n\t\t\"CONVERT(68,\\\"F\\\",\\\"C\\\")\":                       \"20\",\n\t\t\"CONVERT(293.15,\\\"K\\\",\\\"F\\\")\":                   \"68\",\n\t\t\"CONVERT(68,\\\"F\\\",\\\"K\\\")\":                       \"293.15\",\n\t\t\"CONVERT(-273.15,\\\"C\\\",\\\"K\\\")\":                  \"0\",\n\t\t\"CONVERT(-459.67,\\\"F\\\",\\\"K\\\")\":                  \"0\",\n\t\t\"CONVERT(295.65,\\\"K\\\",\\\"C\\\")\":                   \"22.5\",\n\t\t\"CONVERT(22.5,\\\"C\\\",\\\"K\\\")\":                     \"295.65\",\n\t\t\"CONVERT(1667.85,\\\"C\\\",\\\"K\\\")\":                  \"1941\",\n\t\t\"CONVERT(3034.13,\\\"F\\\",\\\"K\\\")\":                  \"1941\",\n\t\t\"CONVERT(3493.8,\\\"Rank\\\",\\\"K\\\")\":                \"1941\",\n\t\t\"CONVERT(1334.28,\\\"Reau\\\",\\\"K\\\")\":               \"1941\",\n\t\t\"CONVERT(1941,\\\"K\\\",\\\"Rank\\\")\":                  \"3493.8\",\n\t\t\"CONVERT(1941,\\\"K\\\",\\\"Reau\\\")\":                  \"1334.28\",\n\t\t\"CONVERT(123.45,\\\"K\\\",\\\"kel\\\")\":                 \"123.45\",\n\t\t\"CONVERT(123.45,\\\"C\\\",\\\"cel\\\")\":                 \"123.45\",\n\t\t\"CONVERT(123.45,\\\"F\\\",\\\"fah\\\")\":                 \"123.45\",\n\t\t\"CONVERT(16,\\\"bit\\\",\\\"byte\\\")\":                  \"2\",\n\t\t\"CONVERT(1,\\\"kbyte\\\",\\\"byte\\\")\":                 \"1000\",\n\t\t\"CONVERT(1,\\\"kibyte\\\",\\\"byte\\\")\":                \"1024\",\n\t\t// DEC2BIN\n\t\t\"DEC2BIN(2)\":    \"10\",\n\t\t\"DEC2BIN(3)\":    \"11\",\n\t\t\"DEC2BIN(2,10)\": \"0000000010\",\n\t\t\"DEC2BIN(-2)\":   \"1111111110\",\n\t\t\"DEC2BIN(6)\":    \"110\",\n\t\t// DEC2HEX\n\t\t\"DEC2HEX(10)\":    \"A\",\n\t\t\"DEC2HEX(31)\":    \"1F\",\n\t\t\"DEC2HEX(16,10)\": \"0000000010\",\n\t\t\"DEC2HEX(-16)\":   \"FFFFFFFFF0\",\n\t\t\"DEC2HEX(273)\":   \"111\",\n\t\t// DEC2OCT\n\t\t\"DEC2OCT(8)\":    \"10\",\n\t\t\"DEC2OCT(18)\":   \"22\",\n\t\t\"DEC2OCT(8,10)\": \"0000000010\",\n\t\t\"DEC2OCT(-8)\":   \"7777777770\",\n\t\t\"DEC2OCT(237)\":  \"355\",\n\t\t// DELTA\n\t\t\"DELTA(5,4)\":       \"0\",\n\t\t\"DELTA(1.00001,1)\": \"0\",\n\t\t\"DELTA(1.23,1.23)\": \"1\",\n\t\t\"DELTA(1)\":         \"0\",\n\t\t\"DELTA(0)\":         \"1\",\n\t\t// ERF\n\t\t\"ERF(1.5)\":   \"0.966105146475311\",\n\t\t\"ERF(0,1.5)\": \"0.966105146475311\",\n\t\t\"ERF(1,2)\":   \"0.152621472069238\",\n\t\t// ERF.PRECISE\n\t\t\"ERF.PRECISE(-1)\":  \"-0.842700792949715\",\n\t\t\"ERF.PRECISE(1.5)\": \"0.966105146475311\",\n\t\t// ERFC\n\t\t\"ERFC(0)\":   \"1\",\n\t\t\"ERFC(0.5)\": \"0.479500122186953\",\n\t\t\"ERFC(-1)\":  \"1.84270079294971\",\n\t\t// ERFC.PRECISE\n\t\t\"ERFC.PRECISE(0)\":   \"1\",\n\t\t\"ERFC.PRECISE(0.5)\": \"0.479500122186953\",\n\t\t\"ERFC.PRECISE(-1)\":  \"1.84270079294971\",\n\t\t// GESTEP\n\t\t\"GESTEP(1.2,0.001)\":  \"1\",\n\t\t\"GESTEP(0.05,0.05)\":  \"1\",\n\t\t\"GESTEP(-0.00001,0)\": \"0\",\n\t\t\"GESTEP(-0.00001)\":   \"0\",\n\t\t// HEX2BIN\n\t\t\"HEX2BIN(\\\"2\\\")\":          \"10\",\n\t\t\"HEX2BIN(\\\"0000000001\\\")\": \"1\",\n\t\t\"HEX2BIN(\\\"2\\\",10)\":       \"0000000010\",\n\t\t\"HEX2BIN(\\\"F0\\\")\":         \"11110000\",\n\t\t\"HEX2BIN(\\\"1D\\\")\":         \"11101\",\n\t\t// HEX2DEC\n\t\t\"HEX2DEC(\\\"A\\\")\":          \"10\",\n\t\t\"HEX2DEC(\\\"1F\\\")\":         \"31\",\n\t\t\"HEX2DEC(\\\"0000000010\\\")\": \"16\",\n\t\t\"HEX2DEC(\\\"FFFFFFFFF0\\\")\": \"-16\",\n\t\t\"HEX2DEC(\\\"111\\\")\":        \"273\",\n\t\t\"HEX2DEC(\\\"\\\")\":           \"0\",\n\t\t// HEX2OCT\n\t\t\"HEX2OCT(\\\"A\\\")\":          \"12\",\n\t\t\"HEX2OCT(\\\"000000000F\\\")\": \"17\",\n\t\t\"HEX2OCT(\\\"8\\\",10)\":       \"0000000010\",\n\t\t\"HEX2OCT(\\\"FFFFFFFFF8\\\")\": \"7777777770\",\n\t\t\"HEX2OCT(\\\"1F3\\\")\":        \"763\",\n\t\t\"HEX2OCT({\\\"1F3\\\"})\":      \"763\",\n\t\t// IMABS\n\t\t\"IMABS(\\\"2j\\\")\":              \"2\",\n\t\t\"IMABS(\\\"-1+2i\\\")\":           \"2.23606797749979\",\n\t\t\"IMABS(COMPLEX(-1,2,\\\"j\\\"))\": \"2.23606797749979\",\n\t\t// IMAGINARY\n\t\t\"IMAGINARY(\\\"5+2i\\\")\": \"2\",\n\t\t\"IMAGINARY(\\\"2-i\\\")\":  \"-1\",\n\t\t\"IMAGINARY(6)\":        \"0\",\n\t\t\"IMAGINARY(\\\"3i\\\")\":   \"3\",\n\t\t\"IMAGINARY(\\\"4+i\\\")\":  \"1\",\n\t\t// IMARGUMENT\n\t\t\"IMARGUMENT(\\\"5+2i\\\")\": \"0.380506377112365\",\n\t\t\"IMARGUMENT(\\\"2-i\\\")\":  \"-0.463647609000806\",\n\t\t\"IMARGUMENT(6)\":        \"0\",\n\t\t// IMCONJUGATE\n\t\t\"IMCONJUGATE(\\\"5+2i\\\")\": \"5-2i\",\n\t\t\"IMCONJUGATE(\\\"2-i\\\")\":  \"2+i\",\n\t\t\"IMCONJUGATE(6)\":        \"6\",\n\t\t\"IMCONJUGATE(\\\"3i\\\")\":   \"-3i\",\n\t\t\"IMCONJUGATE(\\\"4+i\\\")\":  \"4-i\",\n\t\t// IMCOS\n\t\t\"IMCOS(0)\":          \"1\",\n\t\t\"IMCOS(0.5)\":        \"0.877582561890373\",\n\t\t\"IMCOS(\\\"3+0.5i\\\")\": \"-1.11634124452615-0.0735369737112366i\",\n\t\t// IMCOSH\n\t\t\"IMCOSH(0.5)\":           \"1.12762596520638\",\n\t\t\"IMCOSH(\\\"3+0.5i\\\")\":    \"8.83520460650099+4.80282508274303i\",\n\t\t\"IMCOSH(\\\"2-i\\\")\":       \"2.03272300701967-3.0518977991518i\",\n\t\t\"IMCOSH(COMPLEX(1,-1))\": \"0.833730025131149-0.988897705762865i\",\n\t\t// IMCOT\n\t\t\"IMCOT(0.5)\":           \"1.83048772171245\",\n\t\t\"IMCOT(\\\"3+0.5i\\\")\":    \"-0.479345578747373-2.01609252150623i\",\n\t\t\"IMCOT(\\\"2-i\\\")\":       \"-0.171383612909185+0.821329797493852i\",\n\t\t\"IMCOT(COMPLEX(1,-1))\": \"0.217621561854403+0.868014142895925i\",\n\t\t// IMCSC\n\t\t\"IMCSC(\\\"j\\\")\": \"-0.850918128239322j\",\n\t\t// IMCSCH\n\t\t\"IMCSCH(COMPLEX(1,-1))\": \"0.303931001628426+0.621518017170428i\",\n\t\t// IMDIV\n\t\t\"IMDIV(\\\"5+2i\\\",\\\"1+i\\\")\":          \"3.5-1.5i\",\n\t\t\"IMDIV(\\\"2+2i\\\",\\\"2+i\\\")\":          \"1.2+0.4i\",\n\t\t\"IMDIV(COMPLEX(5,2),COMPLEX(0,1))\": \"2-5i\",\n\t\t// IMEXP\n\t\t\"IMEXP(0)\":             \"1\",\n\t\t\"IMEXP(0.5)\":           \"1.64872127070013\",\n\t\t\"IMEXP(\\\"1-2i\\\")\":      \"-1.13120438375681-2.47172667200482i\",\n\t\t\"IMEXP(COMPLEX(1,-1))\": \"1.46869393991589-2.28735528717884i\",\n\t\t// IMLN\n\t\t\"IMLN(0.5)\":           \"-0.693147180559945\",\n\t\t\"IMLN(\\\"3+0.5i\\\")\":    \"1.11231177576217+0.165148677414627i\",\n\t\t\"IMLN(\\\"2-i\\\")\":       \"0.80471895621705-0.463647609000806i\",\n\t\t\"IMLN(COMPLEX(1,-1))\": \"0.346573590279973-0.785398163397448i\",\n\t\t// IMLOG10\n\t\t\"IMLOG10(0.5)\":           \"-0.301029995663981\",\n\t\t\"IMLOG10(\\\"3+0.5i\\\")\":    \"0.483070866369516+0.0717231592947926i\",\n\t\t\"IMLOG10(\\\"2-i\\\")\":       \"0.349485002168009-0.201359598136687i\",\n\t\t\"IMLOG10(COMPLEX(1,-1))\": \"0.150514997831991-0.34109408846046i\",\n\t\t// IMREAL\n\t\t\"IMREAL(\\\"5+2i\\\")\":     \"5\",\n\t\t\"IMREAL(\\\"2+2i\\\")\":     \"2\",\n\t\t\"IMREAL(6)\":            \"6\",\n\t\t\"IMREAL(\\\"3i\\\")\":       \"0\",\n\t\t\"IMREAL(COMPLEX(4,1))\": \"4\",\n\t\t// IMSEC\n\t\t\"IMSEC(0.5)\":           \"1.13949392732455\",\n\t\t\"IMSEC(\\\"3+0.5i\\\")\":    \"-0.89191317974033+0.0587531781817398i\",\n\t\t\"IMSEC(\\\"2-i\\\")\":       \"-0.41314934426694-0.687527438655479i\",\n\t\t\"IMSEC(COMPLEX(1,-1))\": \"0.498337030555187-0.591083841721045i\",\n\t\t// IMSECH\n\t\t\"IMSECH(0.5)\":           \"0.886818883970074\",\n\t\t\"IMSECH(\\\"3+0.5i\\\")\":    \"0.0873665779621303-0.0474925494901607i\",\n\t\t\"IMSECH(\\\"2-i\\\")\":       \"0.151176298265577+0.226973675393722i\",\n\t\t\"IMSECH(COMPLEX(1,-1))\": \"0.498337030555187+0.591083841721045i\",\n\t\t// IMSIN\n\t\t\"IMSIN(0.5)\":           \"0.479425538604203\",\n\t\t\"IMSIN(\\\"3+0.5i\\\")\":    \"0.15913058529844-0.515880442452527i\",\n\t\t\"IMSIN(\\\"2-i\\\")\":       \"1.40311925062204+0.489056259041294i\",\n\t\t\"IMSIN(COMPLEX(1,-1))\": \"1.29845758141598-0.634963914784736i\",\n\t\t// IMSINH\n\t\t\"IMSINH(-0)\":            \"0\",\n\t\t\"IMSINH(0.5)\":           \"0.521095305493747\",\n\t\t\"IMSINH(\\\"3+0.5i\\\")\":    \"8.79151234349371+4.82669427481082i\",\n\t\t\"IMSINH(\\\"2-i\\\")\":       \"1.95960104142161-3.16577851321617i\",\n\t\t\"IMSINH(COMPLEX(1,-1))\": \"0.634963914784736-1.29845758141598i\",\n\t\t// IMSQRT\n\t\t\"IMSQRT(\\\"i\\\")\":     \"0.707106781186548+0.707106781186548i\",\n\t\t\"IMSQRT(\\\"2-i\\\")\":   \"1.45534669022535-0.343560749722512i\",\n\t\t\"IMSQRT(\\\"5+2i\\\")\":  \"2.27872385417085+0.438842116902254i\",\n\t\t\"IMSQRT(6)\":         \"2.44948974278318\",\n\t\t\"IMSQRT(\\\"-2-4i\\\")\": \"1.11178594050284-1.79890743994787i\",\n\t\t// IMSUB\n\t\t\"IMSUB(\\\"5+i\\\",\\\"1+4i\\\")\":          \"4-3i\",\n\t\t\"IMSUB(\\\"9+2i\\\",6)\":                \"3+2i\",\n\t\t\"IMSUB(COMPLEX(5,2),COMPLEX(0,1))\": \"5+i\",\n\t\t// IMSUM\n\t\t\"IMSUM(\\\"1-i\\\",\\\"5+10i\\\",2)\":       \"8+9i\",\n\t\t\"IMSUM(COMPLEX(5,2),COMPLEX(0,1))\": \"5+3i\",\n\t\t// IMTAN\n\t\t\"IMTAN(-0)\":            \"0\",\n\t\t\"IMTAN(0.5)\":           \"0.54630248984379\",\n\t\t\"IMTAN(\\\"3+0.5i\\\")\":    \"-0.111621050771583+0.469469993425885i\",\n\t\t\"IMTAN(\\\"2-i\\\")\":       \"-0.243458201185725-1.16673625724092i\",\n\t\t\"IMTAN(COMPLEX(1,-1))\": \"0.271752585319512-1.08392332733869i\",\n\t\t// OCT2BIN\n\t\t\"OCT2BIN(\\\"5\\\")\":          \"101\",\n\t\t\"OCT2BIN(\\\"0000000001\\\")\": \"1\",\n\t\t\"OCT2BIN(\\\"2\\\",10)\":       \"0000000010\",\n\t\t\"OCT2BIN(\\\"7777777770\\\")\": \"1111111000\",\n\t\t\"OCT2BIN(\\\"16\\\")\":         \"1110\",\n\t\t// OCT2DEC\n\t\t\"OCT2DEC(\\\"10\\\")\":         \"8\",\n\t\t\"OCT2DEC(\\\"22\\\")\":         \"18\",\n\t\t\"OCT2DEC(\\\"0000000010\\\")\": \"8\",\n\t\t\"OCT2DEC(\\\"7777777770\\\")\": \"-8\",\n\t\t\"OCT2DEC(\\\"355\\\")\":        \"237\",\n\t\t// OCT2HEX\n\t\t\"OCT2HEX(\\\"10\\\")\":         \"8\",\n\t\t\"OCT2HEX(\\\"0000000007\\\")\": \"7\",\n\t\t\"OCT2HEX(\\\"10\\\",10)\":      \"0000000008\",\n\t\t\"OCT2HEX(\\\"7777777770\\\")\": \"FFFFFFFFF8\",\n\t\t\"OCT2HEX(\\\"763\\\")\":        \"1F3\",\n\t\t// Math and Trigonometric Functions\n\t\t// ABS\n\t\t\"ABS(-1)\":      \"1\",\n\t\t\"ABS(-6.5)\":    \"6.5\",\n\t\t\"ABS(6.5)\":     \"6.5\",\n\t\t\"ABS(0)\":       \"0\",\n\t\t\"ABS(2-4.5)\":   \"2.5\",\n\t\t\"ABS(ABS(-1))\": \"1\",\n\t\t// ACOS\n\t\t\"ACOS(-1)\":     \"3.14159265358979\",\n\t\t\"ACOS(0)\":      \"1.5707963267949\",\n\t\t\"ACOS(ABS(0))\": \"1.5707963267949\",\n\t\t// ACOSH\n\t\t\"ACOSH(1)\":        \"0\",\n\t\t\"ACOSH(2.5)\":      \"1.56679923697241\",\n\t\t\"ACOSH(5)\":        \"2.29243166956118\",\n\t\t\"ACOSH(ACOSH(5))\": \"1.47138332153668\",\n\t\t// _xlfn.ACOT\n\t\t\"_xlfn.ACOT(1)\":             \"0.785398163397448\",\n\t\t\"_xlfn.ACOT(-2)\":            \"2.67794504458899\",\n\t\t\"_xlfn.ACOT(0)\":             \"1.5707963267949\",\n\t\t\"_xlfn.ACOT(_xlfn.ACOT(0))\": \"0.566911504941009\",\n\t\t// _xlfn.ACOTH\n\t\t\"_xlfn.ACOTH(-5)\":      \"-0.202732554054082\",\n\t\t\"_xlfn.ACOTH(1.1)\":     \"1.52226121886171\",\n\t\t\"_xlfn.ACOTH(2)\":       \"0.549306144334055\",\n\t\t\"_xlfn.ACOTH(ABS(-2))\": \"0.549306144334055\",\n\t\t// _xlfn.AGGREGATE\n\t\t\"_xlfn.AGGREGATE(1,0,A1:A6)\":    \"1.5\",\n\t\t\"_xlfn.AGGREGATE(2,0,A1:A6)\":    \"4\",\n\t\t\"_xlfn.AGGREGATE(3,0,A1:A6)\":    \"4\",\n\t\t\"_xlfn.AGGREGATE(4,0,A1:A6)\":    \"3\",\n\t\t\"_xlfn.AGGREGATE(5,0,A1:A6)\":    \"0\",\n\t\t\"_xlfn.AGGREGATE(6,0,A1:A6)\":    \"0\",\n\t\t\"_xlfn.AGGREGATE(7,0,A1:A6)\":    \"1.29099444873581\",\n\t\t\"_xlfn.AGGREGATE(8,0,A1:A6)\":    \"1.11803398874989\",\n\t\t\"_xlfn.AGGREGATE(9,0,A1:A6)\":    \"6\",\n\t\t\"_xlfn.AGGREGATE(10,0,A1:A6)\":   \"1.66666666666667\",\n\t\t\"_xlfn.AGGREGATE(11,0,A1:A6)\":   \"1.25\",\n\t\t\"_xlfn.AGGREGATE(12,0,A1:A6)\":   \"1.5\",\n\t\t\"_xlfn.AGGREGATE(14,0,A1:A6,1)\": \"3\",\n\t\t\"_xlfn.AGGREGATE(15,0,A1:A6,1)\": \"0\",\n\t\t\"_xlfn.AGGREGATE(16,0,A1:A6,1)\": \"3\",\n\t\t\"_xlfn.AGGREGATE(17,0,A1:A6,1)\": \"0.75\",\n\t\t\"_xlfn.AGGREGATE(19,0,A1:A6,1)\": \"0.25\",\n\t\t// ARABIC\n\t\t\"_xlfn.ARABIC(\\\"IV\\\")\":       \"4\",\n\t\t\"_xlfn.ARABIC(\\\"-IV\\\")\":      \"-4\",\n\t\t\"_xlfn.ARABIC(\\\"MCXX\\\")\":     \"1120\",\n\t\t\"_xlfn.ARABIC(\\\"\\\")\":         \"0\",\n\t\t\"_xlfn.ARABIC(\\\" ll  lc \\\")\": \"-50\",\n\t\t// ASIN\n\t\t\"ASIN(-1)\":      \"-1.5707963267949\",\n\t\t\"ASIN(0)\":       \"0\",\n\t\t\"ASIN(ASIN(0))\": \"0\",\n\t\t// ASINH\n\t\t\"ASINH(0)\":        \"0\",\n\t\t\"ASINH(-0.5)\":     \"-0.481211825059603\",\n\t\t\"ASINH(2)\":        \"1.44363547517881\",\n\t\t\"ASINH(ASINH(0))\": \"0\",\n\t\t// ATAN\n\t\t\"ATAN(-1)\":      \"-0.785398163397448\",\n\t\t\"ATAN(0)\":       \"0\",\n\t\t\"ATAN(1)\":       \"0.785398163397448\",\n\t\t\"ATAN(ATAN(0))\": \"0\",\n\t\t// ATANH\n\t\t\"ATANH(-0.8)\":     \"-1.09861228866811\",\n\t\t\"ATANH(0)\":        \"0\",\n\t\t\"ATANH(0.5)\":      \"0.549306144334055\",\n\t\t\"ATANH(ATANH(0))\": \"0\",\n\t\t// ATAN2\n\t\t\"ATAN2(1,1)\":          \"0.785398163397448\",\n\t\t\"ATAN2(1,-1)\":         \"-0.785398163397448\",\n\t\t\"ATAN2(4,0)\":          \"0\",\n\t\t\"ATAN2(4,ATAN2(4,0))\": \"0\",\n\t\t// BASE\n\t\t\"BASE(12,2)\":          \"1100\",\n\t\t\"BASE(12,2,8)\":        \"00001100\",\n\t\t\"BASE(100000,16)\":     \"186A0\",\n\t\t\"BASE(BASE(12,2),16)\": \"44C\",\n\t\t// CEILING\n\t\t\"CEILING(22.25,0.1)\":              \"22.3\",\n\t\t\"CEILING(22.25,0.5)\":              \"22.5\",\n\t\t\"CEILING(22.25,1)\":                \"23\",\n\t\t\"CEILING(22.25,10)\":               \"30\",\n\t\t\"CEILING(22.25,20)\":               \"40\",\n\t\t\"CEILING(-22.25,-0.1)\":            \"-22.3\",\n\t\t\"CEILING(-22.25,-1)\":              \"-23\",\n\t\t\"CEILING(-22.25,-5)\":              \"-25\",\n\t\t\"CEILING(22.25)\":                  \"23\",\n\t\t\"CEILING(CEILING(22.25,0.1),0.1)\": \"22.3\",\n\t\t// _xlfn.CEILING.MATH\n\t\t\"_xlfn.CEILING.MATH(15.25,1)\":                       \"16\",\n\t\t\"_xlfn.CEILING.MATH(15.25,0.1)\":                     \"15.3\",\n\t\t\"_xlfn.CEILING.MATH(15.25,5)\":                       \"20\",\n\t\t\"_xlfn.CEILING.MATH(-15.25,1)\":                      \"-15\",\n\t\t\"_xlfn.CEILING.MATH(-15.25,1,1)\":                    \"-15\", // should be 16\n\t\t\"_xlfn.CEILING.MATH(-15.25,10)\":                     \"-10\",\n\t\t\"_xlfn.CEILING.MATH(-15.25)\":                        \"-15\",\n\t\t\"_xlfn.CEILING.MATH(-15.25,-5,-1)\":                  \"-10\",\n\t\t\"_xlfn.CEILING.MATH(_xlfn.CEILING.MATH(15.25,1),1)\": \"16\",\n\t\t// _xlfn.CEILING.PRECISE\n\t\t\"_xlfn.CEILING.PRECISE(22.25,0.1)\":                          \"22.3\",\n\t\t\"_xlfn.CEILING.PRECISE(22.25,0.5)\":                          \"22.5\",\n\t\t\"_xlfn.CEILING.PRECISE(22.25,1)\":                            \"23\",\n\t\t\"_xlfn.CEILING.PRECISE(22.25)\":                              \"23\",\n\t\t\"_xlfn.CEILING.PRECISE(22.25,10)\":                           \"30\",\n\t\t\"_xlfn.CEILING.PRECISE(22.25,0)\":                            \"0\",\n\t\t\"_xlfn.CEILING.PRECISE(-22.25,1)\":                           \"-22\",\n\t\t\"_xlfn.CEILING.PRECISE(-22.25,-1)\":                          \"-22\",\n\t\t\"_xlfn.CEILING.PRECISE(-22.25,5)\":                           \"-20\",\n\t\t\"_xlfn.CEILING.PRECISE(_xlfn.CEILING.PRECISE(22.25,0.1),5)\": \"25\",\n\t\t// COMBIN\n\t\t\"COMBIN(6,1)\":           \"6\",\n\t\t\"COMBIN(6,2)\":           \"15\",\n\t\t\"COMBIN(6,3)\":           \"20\",\n\t\t\"COMBIN(6,4)\":           \"15\",\n\t\t\"COMBIN(6,5)\":           \"6\",\n\t\t\"COMBIN(6,6)\":           \"1\",\n\t\t\"COMBIN(0,0)\":           \"1\",\n\t\t\"COMBIN(6,COMBIN(0,0))\": \"6\",\n\t\t// _xlfn.COMBINA\n\t\t\"_xlfn.COMBINA(6,1)\":                  \"6\",\n\t\t\"_xlfn.COMBINA(6,2)\":                  \"21\",\n\t\t\"_xlfn.COMBINA(6,3)\":                  \"56\",\n\t\t\"_xlfn.COMBINA(6,4)\":                  \"126\",\n\t\t\"_xlfn.COMBINA(6,5)\":                  \"252\",\n\t\t\"_xlfn.COMBINA(6,6)\":                  \"462\",\n\t\t\"_xlfn.COMBINA(0,0)\":                  \"0\",\n\t\t\"_xlfn.COMBINA(0,_xlfn.COMBINA(0,0))\": \"0\",\n\t\t// COS\n\t\t\"COS(0.785398163)\": \"0.707106781467586\",\n\t\t\"COS(0)\":           \"1\",\n\t\t\"-COS(0)\":          \"-1\",\n\t\t\"COS(COS(0))\":      \"0.54030230586814\",\n\t\t// COSH\n\t\t\"COSH(0)\":       \"1\",\n\t\t\"COSH(0.5)\":     \"1.12762596520638\",\n\t\t\"COSH(-2)\":      \"3.76219569108363\",\n\t\t\"COSH(COSH(0))\": \"1.54308063481524\",\n\t\t// _xlfn.COT\n\t\t\"_xlfn.COT(0.785398163397448)\": \"1\",\n\t\t\"_xlfn.COT(_xlfn.COT(0.45))\":   \"-0.545473116787229\",\n\t\t// _xlfn.COTH\n\t\t\"_xlfn.COTH(-3.14159265358979)\": \"-1.00374187319732\",\n\t\t\"_xlfn.COTH(_xlfn.COTH(1))\":     \"1.15601401811395\",\n\t\t// _xlfn.CSC\n\t\t\"_xlfn.CSC(-6)\":              \"3.57889954725441\",\n\t\t\"_xlfn.CSC(1.5707963267949)\": \"1\",\n\t\t\"_xlfn.CSC(_xlfn.CSC(1))\":    \"1.07785184031088\",\n\t\t// _xlfn.CSCH\n\t\t\"_xlfn.CSCH(-3.14159265358979)\": \"-0.0865895375300472\",\n\t\t\"_xlfn.CSCH(_xlfn.CSCH(1))\":     \"1.04451010395518\",\n\t\t// _xlfn.DECIMAL\n\t\t\"_xlfn.DECIMAL(\\\"1100\\\",2)\":    \"12\",\n\t\t\"_xlfn.DECIMAL(\\\"186A0\\\",16)\":  \"100000\",\n\t\t\"_xlfn.DECIMAL(\\\"31L0\\\",32)\":   \"100000\",\n\t\t\"_xlfn.DECIMAL(\\\"70122\\\",8)\":   \"28754\",\n\t\t\"_xlfn.DECIMAL(\\\"0x70122\\\",8)\": \"28754\",\n\t\t// DEGREES\n\t\t\"DEGREES(1)\":          \"57.2957795130823\",\n\t\t\"DEGREES(2.5)\":        \"143.239448782706\",\n\t\t\"DEGREES(DEGREES(1))\": \"3282.80635001174\",\n\t\t// EVEN\n\t\t\"EVEN(23)\":   \"24\",\n\t\t\"EVEN(2.22)\": \"4\",\n\t\t\"EVEN(0)\":    \"0\",\n\t\t\"EVEN(-0.3)\": \"-2\",\n\t\t\"EVEN(-11)\":  \"-12\",\n\t\t\"EVEN(-4)\":   \"-4\",\n\t\t\"EVEN((0))\":  \"0\",\n\t\t// EXP\n\t\t\"EXP(100)\":    \"2.68811714181614E+43\",\n\t\t\"EXP(0.1)\":    \"1.10517091807565\",\n\t\t\"EXP(0)\":      \"1\",\n\t\t\"EXP(-5)\":     \"0.00673794699908547\",\n\t\t\"EXP(EXP(0))\": \"2.71828182845905\",\n\t\t// FACT\n\t\t\"FACT(3)\":       \"6\",\n\t\t\"FACT(6)\":       \"720\",\n\t\t\"FACT(10)\":      \"3628800\",\n\t\t\"FACT(FACT(3))\": \"720\",\n\t\t// FACTDOUBLE\n\t\t\"FACTDOUBLE(5)\":             \"15\",\n\t\t\"FACTDOUBLE(8)\":             \"384\",\n\t\t\"FACTDOUBLE(13)\":            \"135135\",\n\t\t\"FACTDOUBLE(FACTDOUBLE(1))\": \"1\",\n\t\t// FLOOR\n\t\t\"FLOOR(26.75,0.1)\":        \"26.7\",\n\t\t\"FLOOR(26.75,0.5)\":        \"26.5\",\n\t\t\"FLOOR(26.75,1)\":          \"26\",\n\t\t\"FLOOR(26.75,10)\":         \"20\",\n\t\t\"FLOOR(26.75,20)\":         \"20\",\n\t\t\"FLOOR(-26.75,-0.1)\":      \"-26.7\",\n\t\t\"FLOOR(-26.75,-1)\":        \"-26\",\n\t\t\"FLOOR(-26.75,-5)\":        \"-25\",\n\t\t\"FLOOR(-2.05,2)\":          \"-4\",\n\t\t\"FLOOR(FLOOR(26.75,1),1)\": \"26\",\n\t\t// _xlfn.FLOOR.MATH\n\t\t\"_xlfn.FLOOR.MATH(58.55)\":                  \"58\",\n\t\t\"_xlfn.FLOOR.MATH(58.55,0.1)\":              \"58.5\",\n\t\t\"_xlfn.FLOOR.MATH(58.55,5)\":                \"55\",\n\t\t\"_xlfn.FLOOR.MATH(58.55,1,1)\":              \"58\",\n\t\t\"_xlfn.FLOOR.MATH(-58.55,1)\":               \"-59\",\n\t\t\"_xlfn.FLOOR.MATH(-58.55,1,-1)\":            \"-58\",\n\t\t\"_xlfn.FLOOR.MATH(-58.55,1,1)\":             \"-59\", // should be -58\n\t\t\"_xlfn.FLOOR.MATH(-58.55,10)\":              \"-60\",\n\t\t\"_xlfn.FLOOR.MATH(_xlfn.FLOOR.MATH(1),10)\": \"0\",\n\t\t// _xlfn.FLOOR.PRECISE\n\t\t\"_xlfn.FLOOR.PRECISE(26.75,0.1)\":                     \"26.7\",\n\t\t\"_xlfn.FLOOR.PRECISE(26.75,0.5)\":                     \"26.5\",\n\t\t\"_xlfn.FLOOR.PRECISE(26.75,1)\":                       \"26\",\n\t\t\"_xlfn.FLOOR.PRECISE(26.75)\":                         \"26\",\n\t\t\"_xlfn.FLOOR.PRECISE(26.75,10)\":                      \"20\",\n\t\t\"_xlfn.FLOOR.PRECISE(26.75,0)\":                       \"0\",\n\t\t\"_xlfn.FLOOR.PRECISE(-26.75,1)\":                      \"-27\",\n\t\t\"_xlfn.FLOOR.PRECISE(-26.75,-1)\":                     \"-27\",\n\t\t\"_xlfn.FLOOR.PRECISE(-26.75,-5)\":                     \"-30\",\n\t\t\"_xlfn.FLOOR.PRECISE(_xlfn.FLOOR.PRECISE(26.75),-5)\": \"25\",\n\t\t// GCD\n\t\t\"GCD(0)\":        \"0\",\n\t\t\"GCD(1,0)\":      \"1\",\n\t\t\"GCD(\\\"0\\\",1)\":  \"1\",\n\t\t\"GCD(1,5)\":      \"1\",\n\t\t\"GCD(15,10,25)\": \"5\",\n\t\t\"GCD(0,8,12)\":   \"4\",\n\t\t\"GCD(7,2)\":      \"1\",\n\t\t\"GCD(1,GCD(1))\": \"1\",\n\t\t// INT\n\t\t\"INT(100.9)\":  \"100\",\n\t\t\"INT(5.22)\":   \"5\",\n\t\t\"INT(5.99)\":   \"5\",\n\t\t\"INT(-6.1)\":   \"-7\",\n\t\t\"INT(-100.9)\": \"-101\",\n\t\t\"INT(INT(0))\": \"0\",\n\t\t// ISO.CEILING\n\t\t\"ISO.CEILING(22.25)\":              \"23\",\n\t\t\"ISO.CEILING(22.25,1)\":            \"23\",\n\t\t\"ISO.CEILING(22.25,0.1)\":          \"22.3\",\n\t\t\"ISO.CEILING(22.25,10)\":           \"30\",\n\t\t\"ISO.CEILING(-22.25,1)\":           \"-22\",\n\t\t\"ISO.CEILING(-22.25,0.1)\":         \"-22.2\",\n\t\t\"ISO.CEILING(-22.25,5)\":           \"-20\",\n\t\t\"ISO.CEILING(-22.25,0)\":           \"0\",\n\t\t\"ISO.CEILING(1,ISO.CEILING(1,0))\": \"0\",\n\t\t// LCM\n\t\t\"LCM(1,5)\":        \"5\",\n\t\t\"LCM(15,10,25)\":   \"150\",\n\t\t\"LCM(1,8,12)\":     \"24\",\n\t\t\"LCM(7,2)\":        \"14\",\n\t\t\"LCM(7)\":          \"7\",\n\t\t\"LCM(\\\"\\\",1)\":     \"1\",\n\t\t\"LCM(0,0)\":        \"0\",\n\t\t\"LCM(0,LCM(0,0))\": \"0\",\n\t\t// LN\n\t\t\"LN(1)\":       \"0\",\n\t\t\"LN(100)\":     \"4.60517018598809\",\n\t\t\"LN(0.5)\":     \"-0.693147180559945\",\n\t\t\"LN(LN(100))\": \"1.5271796258079\",\n\t\t// LOG\n\t\t\"LOG(64,2)\":     \"6\",\n\t\t\"LOG(100)\":      \"2\",\n\t\t\"LOG(4,0.5)\":    \"-2\",\n\t\t\"LOG(500)\":      \"2.69897000433602\",\n\t\t\"LOG(LOG(100))\": \"0.301029995663981\",\n\t\t// LOG10\n\t\t\"LOG10(100)\":        \"2\",\n\t\t\"LOG10(1000)\":       \"3\",\n\t\t\"LOG10(0.001)\":      \"-3\",\n\t\t\"LOG10(25)\":         \"1.39794000867204\",\n\t\t\"LOG10(LOG10(100))\": \"0.301029995663981\",\n\t\t// IMLOG2\n\t\t\"IMLOG2(\\\"5+2i\\\")\": \"2.42899049756379+0.548954663286635i\",\n\t\t\"IMLOG2(\\\"2-i\\\")\":  \"1.16096404744368-0.668902106225488i\",\n\t\t\"IMLOG2(6)\":        \"2.58496250072116\",\n\t\t\"IMLOG2(\\\"3i\\\")\":   \"1.58496250072116+2.2661800709136i\",\n\t\t\"IMLOG2(\\\"4+i\\\")\":  \"2.04373142062517+0.353429502416735i\",\n\t\t// IMPOWER\n\t\t\"IMPOWER(\\\"2-i\\\",2)\":   \"3-4i\",\n\t\t\"IMPOWER(\\\"2-i\\\",3)\":   \"2-11i\",\n\t\t\"IMPOWER(9,0.5)\":       \"3\",\n\t\t\"IMPOWER(\\\"2+4i\\\",-2)\": \"-0.03-0.04i\",\n\t\t// IMPRODUCT\n\t\t\"IMPRODUCT(3,6)\":                       \"18\",\n\t\t\"IMPRODUCT(\\\"\\\",3,SUM(6))\":             \"18\",\n\t\t\"IMPRODUCT(\\\"1-i\\\",\\\"5+10i\\\",2)\":       \"30+10i\",\n\t\t\"IMPRODUCT(COMPLEX(5,2),COMPLEX(0,1))\": \"-2+5i\",\n\t\t\"IMPRODUCT(A1:C1)\":                     \"4\",\n\t\t// MINVERSE\n\t\t\"MINVERSE(A1:B2)\": \"-0\",\n\t\t// MMULT\n\t\t\"MMULT(0,0)\":         \"0\",\n\t\t\"MMULT(2,4)\":         \"8\",\n\t\t\"MMULT(A4:A4,A4:A4)\": \"0\",\n\t\t// MOD\n\t\t\"MOD(6,4)\":        \"2\",\n\t\t\"MOD(6,3)\":        \"0\",\n\t\t\"MOD(6,2.5)\":      \"1\",\n\t\t\"MOD(6,1.333)\":    \"0.668\",\n\t\t\"MOD(-10.23,1)\":   \"0.77\",\n\t\t\"MOD(MOD(1,1),1)\": \"0\",\n\t\t// MROUND\n\t\t\"MROUND(333.7,0.5)\":     \"333.5\",\n\t\t\"MROUND(333.8,1)\":       \"334\",\n\t\t\"MROUND(333.3,2)\":       \"334\",\n\t\t\"MROUND(555.3,400)\":     \"400\",\n\t\t\"MROUND(555,1000)\":      \"1000\",\n\t\t\"MROUND(-555.7,-1)\":     \"-556\",\n\t\t\"MROUND(-555.4,-1)\":     \"-555\",\n\t\t\"MROUND(-1555,-1000)\":   \"-2000\",\n\t\t\"MROUND(MROUND(1,1),1)\": \"1\",\n\t\t// MULTINOMIAL\n\t\t\"MULTINOMIAL(3,1,2,5)\":        \"27720\",\n\t\t\"MULTINOMIAL(\\\"\\\",3,1,2,5)\":   \"27720\",\n\t\t\"MULTINOMIAL(MULTINOMIAL(1))\": \"1\",\n\t\t// _xlfn.MUNIT\n\t\t\"_xlfn.MUNIT(4)\": \"1\",\n\t\t// ODD\n\t\t\"ODD(22)\":     \"23\",\n\t\t\"ODD(1.22)\":   \"3\",\n\t\t\"ODD(1.22+4)\": \"7\",\n\t\t\"ODD(0)\":      \"1\",\n\t\t\"ODD(-1.3)\":   \"-3\",\n\t\t\"ODD(-10)\":    \"-11\",\n\t\t\"ODD(-3)\":     \"-3\",\n\t\t\"ODD(ODD(1))\": \"1\",\n\t\t// PI\n\t\t\"PI()\": \"3.14159265358979\",\n\t\t// POWER\n\t\t\"POWER(4,2)\":          \"16\",\n\t\t\"POWER(4,POWER(1,1))\": \"4\",\n\t\t// PRODUCT\n\t\t\"PRODUCT(3,6)\":            \"18\",\n\t\t\"PRODUCT(\\\"3\\\",\\\"6\\\")\":    \"18\",\n\t\t\"PRODUCT(PRODUCT(1),3,6)\": \"18\",\n\t\t\"PRODUCT(C1:C2)\":          \"1\",\n\t\t// QUOTIENT\n\t\t\"QUOTIENT(5,2)\":             \"2\",\n\t\t\"QUOTIENT(4.5,3.1)\":         \"1\",\n\t\t\"QUOTIENT(-10,3)\":           \"-3\",\n\t\t\"QUOTIENT(QUOTIENT(1,2),3)\": \"0\",\n\t\t// RADIANS\n\t\t\"RADIANS(50)\":           \"0.872664625997165\",\n\t\t\"RADIANS(-180)\":         \"-3.14159265358979\",\n\t\t\"RADIANS(180)\":          \"3.14159265358979\",\n\t\t\"RADIANS(360)\":          \"6.28318530717959\",\n\t\t\"RADIANS(RADIANS(360))\": \"0.109662271123215\",\n\t\t// ROMAN\n\t\t\"ROMAN(499,0)\":       \"CDXCIX\",\n\t\t\"ROMAN(1999,0)\":      \"MCMXCIX\",\n\t\t\"ROMAN(1999,1)\":      \"MLMVLIV\",\n\t\t\"ROMAN(1999,2)\":      \"MXMIX\",\n\t\t\"ROMAN(1999,3)\":      \"MVMIV\",\n\t\t\"ROMAN(1999,4)\":      \"MIM\",\n\t\t\"ROMAN(1999,-1)\":     \"MCMXCIX\",\n\t\t\"ROMAN(1999,5)\":      \"MIM\",\n\t\t\"ROMAN(1999,ODD(1))\": \"MLMVLIV\",\n\t\t// ROUND\n\t\t\"ROUND(100.319,1)\":       \"100.3\",\n\t\t\"ROUND(5.28,1)\":          \"5.3\",\n\t\t\"ROUND(5.9999,3)\":        \"6\",\n\t\t\"ROUND(99.5,0)\":          \"100\",\n\t\t\"ROUND(-6.3,0)\":          \"-6\",\n\t\t\"ROUND(-100.5,0)\":        \"-101\",\n\t\t\"ROUND(-22.45,1)\":        \"-22.5\",\n\t\t\"ROUND(999,-1)\":          \"1000\",\n\t\t\"ROUND(991,-1)\":          \"990\",\n\t\t\"ROUND(ROUND(100,1),-1)\": \"100\",\n\t\t// ROUNDDOWN\n\t\t\"ROUNDDOWN(99.999,1)\":            \"99.9\",\n\t\t\"ROUNDDOWN(99.999,2)\":            \"99.99\",\n\t\t\"ROUNDDOWN(99.999,0)\":            \"99\",\n\t\t\"ROUNDDOWN(99.999,-1)\":           \"90\",\n\t\t\"ROUNDDOWN(-99.999,2)\":           \"-99.99\",\n\t\t\"ROUNDDOWN(-99.999,-1)\":          \"-90\",\n\t\t\"ROUNDDOWN(ROUNDDOWN(100,1),-1)\": \"100\",\n\t\t// ROUNDUP\n\t\t\"ROUNDUP(11.111,1)\":          \"11.2\",\n\t\t\"ROUNDUP(11.111,2)\":          \"11.12\",\n\t\t\"ROUNDUP(11.111,0)\":          \"12\",\n\t\t\"ROUNDUP(11.111,-1)\":         \"20\",\n\t\t\"ROUNDUP(-11.111,2)\":         \"-11.12\",\n\t\t\"ROUNDUP(-11.111,-1)\":        \"-20\",\n\t\t\"ROUNDUP(ROUNDUP(100,1),-1)\": \"100\",\n\t\t// SEARCH\n\t\t\"SEARCH(\\\"s\\\",F1)\":           \"1\",\n\t\t\"SEARCH(\\\"s\\\",F1,2)\":         \"5\",\n\t\t\"SEARCH(\\\"e\\\",F1)\":           \"4\",\n\t\t\"SEARCH(\\\"e*\\\",F1)\":          \"4\",\n\t\t\"SEARCH(\\\"?e\\\",F1)\":          \"3\",\n\t\t\"SEARCH(\\\"??e\\\",F1)\":         \"2\",\n\t\t\"SEARCH(6,F2)\":               \"2\",\n\t\t\"SEARCH(\\\"?\\\",\\\"你好world\\\")\":  \"1\",\n\t\t\"SEARCH(\\\"?l\\\",\\\"你好world\\\")\": \"5\",\n\t\t\"SEARCH(\\\"?+\\\",\\\"你好 1+2\\\")\":  \"4\",\n\t\t\"SEARCH(\\\" ?+\\\",\\\"你好 1+2\\\")\": \"3\",\n\t\t// SEARCHB\n\t\t\"SEARCHB(\\\"s\\\",F1)\":           \"1\",\n\t\t\"SEARCHB(\\\"s\\\",F1,2)\":         \"5\",\n\t\t\"SEARCHB(\\\"e\\\",F1)\":           \"4\",\n\t\t\"SEARCHB(\\\"e*\\\",F1)\":          \"4\",\n\t\t\"SEARCHB(\\\"?e\\\",F1)\":          \"3\",\n\t\t\"SEARCHB(\\\"??e\\\",F1)\":         \"2\",\n\t\t\"SEARCHB(6,F2)\":               \"2\",\n\t\t\"SEARCHB(\\\"?\\\",\\\"你好world\\\")\":  \"5\",\n\t\t\"SEARCHB(\\\"?l\\\",\\\"你好world\\\")\": \"7\",\n\t\t\"SEARCHB(\\\"?+\\\",\\\"你好 1+2\\\")\":  \"6\",\n\t\t\"SEARCHB(\\\" ?+\\\",\\\"你好 1+2\\\")\": \"5\",\n\t\t// SEC\n\t\t\"_xlfn.SEC(-3.14159265358979)\": \"-1\",\n\t\t\"_xlfn.SEC(0)\":                 \"1\",\n\t\t\"_xlfn.SEC(_xlfn.SEC(0))\":      \"0.54030230586814\",\n\t\t// SECH\n\t\t\"_xlfn.SECH(-3.14159265358979)\": \"0.0862667383340547\",\n\t\t\"_xlfn.SECH(0)\":                 \"1\",\n\t\t\"_xlfn.SECH(_xlfn.SECH(0))\":     \"0.648054273663885\",\n\t\t// SERIESSUM\n\t\t\"SERIESSUM(1,2,3,A1:A4)\": \"6\",\n\t\t\"SERIESSUM(1,2,3,A1:B5)\": \"15\",\n\t\t// SIGN\n\t\t\"SIGN(9.5)\":        \"1\",\n\t\t\"SIGN(-9.5)\":       \"-1\",\n\t\t\"SIGN(0)\":          \"0\",\n\t\t\"SIGN(0.00000001)\": \"1\",\n\t\t\"SIGN(6-7)\":        \"-1\",\n\t\t\"SIGN(SIGN(-1))\":   \"-1\",\n\t\t// SIN\n\t\t\"SIN(0.785398163)\": \"0.707106780905509\",\n\t\t\"SIN(SIN(1))\":      \"0.745624141665558\",\n\t\t// SINH\n\t\t\"SINH(0)\":       \"0\",\n\t\t\"SINH(0.5)\":     \"0.521095305493747\",\n\t\t\"SINH(-2)\":      \"-3.62686040784702\",\n\t\t\"SINH(SINH(0))\": \"0\",\n\t\t// SQRT\n\t\t\"SQRT(4)\":        \"2\",\n\t\t\"SQRT(SQRT(16))\": \"2\",\n\t\t// SQRTPI\n\t\t\"SQRTPI(5)\":         \"3.96332729760601\",\n\t\t\"SQRTPI(0.2)\":       \"0.792665459521202\",\n\t\t\"SQRTPI(100)\":       \"17.7245385090552\",\n\t\t\"SQRTPI(0)\":         \"0\",\n\t\t\"SQRTPI(SQRTPI(0))\": \"0\",\n\t\t// STDEV\n\t\t\"STDEV(F2:F9)\":         \"10724.9782875238\",\n\t\t\"STDEV(MUNIT(2))\":      \"0.577350269189626\",\n\t\t\"STDEV(0,INT(0))\":      \"0\",\n\t\t\"STDEV(INT(1),INT(1))\": \"0\",\n\t\t// STDEV.S\n\t\t\"STDEV.S(F2:F9)\": \"10724.9782875238\",\n\t\t// STDEVA\n\t\t\"STDEVA(F2:F9)\":    \"10724.9782875238\",\n\t\t\"STDEVA(MUNIT(2))\": \"0.577350269189626\",\n\t\t\"STDEVA(0,INT(0))\": \"0\",\n\t\t// POISSON.DIST\n\t\t\"POISSON.DIST(20,25,FALSE)\": \"0.0519174686084913\",\n\t\t\"POISSON.DIST(35,40,TRUE)\":  \"0.242414197690103\",\n\t\t// POISSON\n\t\t\"POISSON(20,25,FALSE)\": \"0.0519174686084913\",\n\t\t\"POISSON(35,40,TRUE)\":  \"0.242414197690103\",\n\t\t// SUBTOTAL\n\t\t\"SUBTOTAL(1,A1:A6)\":         \"1.5\",\n\t\t\"SUBTOTAL(2,A1:A6)\":         \"4\",\n\t\t\"SUBTOTAL(3,A1:A6)\":         \"4\",\n\t\t\"SUBTOTAL(4,A1:A6)\":         \"3\",\n\t\t\"SUBTOTAL(5,A1:A6)\":         \"0\",\n\t\t\"SUBTOTAL(6,A1:A6)\":         \"0\",\n\t\t\"SUBTOTAL(7,A1:A6)\":         \"1.29099444873581\",\n\t\t\"SUBTOTAL(8,A1:A6)\":         \"1.11803398874989\",\n\t\t\"SUBTOTAL(9,A1:A6)\":         \"6\",\n\t\t\"SUBTOTAL(10,A1:A6)\":        \"1.66666666666667\",\n\t\t\"SUBTOTAL(11,A1:A6)\":        \"1.25\",\n\t\t\"SUBTOTAL(101,A1:A6)\":       \"1.5\",\n\t\t\"SUBTOTAL(102,A1:A6)\":       \"4\",\n\t\t\"SUBTOTAL(103,A1:A6)\":       \"4\",\n\t\t\"SUBTOTAL(104,A1:A6)\":       \"3\",\n\t\t\"SUBTOTAL(105,A1:A6)\":       \"0\",\n\t\t\"SUBTOTAL(106,A1:A6)\":       \"0\",\n\t\t\"SUBTOTAL(107,A1:A6)\":       \"1.29099444873581\",\n\t\t\"SUBTOTAL(108,A1:A6)\":       \"1.11803398874989\",\n\t\t\"SUBTOTAL(109,A1:A6)\":       \"6\",\n\t\t\"SUBTOTAL(109,A1:A6,A1:A6)\": \"12\",\n\t\t\"SUBTOTAL(110,A1:A6)\":       \"1.66666666666667\",\n\t\t\"SUBTOTAL(111,A1:A6)\":       \"1.25\",\n\t\t\"SUBTOTAL(111,A1:A6,A1:A6)\": \"1.25\",\n\t\t// SUM\n\t\t\"SUM(1,2)\":                           \"3\",\n\t\t\"SUM(\\\"1\\\",\\\"2\\\")\":                   \"3\",\n\t\t\"SUM(\\\"\\\",1,2)\":                      \"3\",\n\t\t\"SUM(1,2+3)\":                         \"6\",\n\t\t\"SUM(SUM(1,2),2)\":                    \"5\",\n\t\t\"(-2-SUM(-4+7))*5\":                   \"-25\",\n\t\t\"SUM(1,2,3,4,5,6,7)\":                 \"28\",\n\t\t\"SUM(1,2)+SUM(1,2)\":                  \"6\",\n\t\t\"1+SUM(SUM(1,2*3),4)\":                \"12\",\n\t\t\"1+SUM(SUM(1,-2*3),4)\":               \"0\",\n\t\t\"(-2-SUM(-4*(7+7)))*5\":               \"270\",\n\t\t\"SUM(SUM(1+2/1)*2-3/2,2)\":            \"6.5\",\n\t\t\"((3+5*2)+3)/5+(-6)/4*2+3\":           \"3.2\",\n\t\t\"1+SUM(SUM(1,2*3),4)*-4/2+5+(4+2)*3\": \"2\",\n\t\t\"1+SUM(SUM(1,2*3),4)*4/3+5+(4+2)*3\":  \"38.6666666666667\",\n\t\t\"SUM(1+ROW())\":                       \"2\",\n\t\t\"SUM((SUM(2))+1)\":                    \"3\",\n\t\t\"IF(2<0, 1, (4))\":                    \"4\",\n\t\t\"IF(2>0, (1), 4)\":                    \"1\",\n\t\t\"IF(2>0, (A1)*2.5, 4)\":               \"2.5\",\n\t\t\"SUM({1,2,3,4,\\\"\\\"})\":                \"10\",\n\t\t// SUMIF\n\t\t\"SUMIF(F1:F5, \\\"\\\")\":             \"0\",\n\t\t\"SUMIF(A1:A5, \\\"3\\\")\":            \"3\",\n\t\t\"SUMIF(F1:F5, \\\"=36693\\\")\":       \"36693\",\n\t\t\"SUMIF(F1:F5, \\\"<100\\\")\":         \"0\",\n\t\t\"SUMIF(F1:F5, \\\"<=36693\\\")\":      \"93233\",\n\t\t\"SUMIF(F1:F5, \\\">100\\\")\":         \"146554\",\n\t\t\"SUMIF(F1:F5, \\\">=100\\\")\":        \"146554\",\n\t\t\"SUMIF(F1:F5, \\\">=text\\\")\":       \"0\",\n\t\t\"SUMIF(F1:F5, \\\"*Jan\\\",F2:F5)\":   \"0\",\n\t\t\"SUMIF(D3:D7,\\\"Jan\\\",F2:F5)\":     \"112114\",\n\t\t\"SUMIF(D2:D9,\\\"Feb\\\",F2:F9)\":     \"157559\",\n\t\t\"SUMIF(E2:E9,\\\"North 1\\\",F2:F9)\": \"66582\",\n\t\t\"SUMIF(E2:E9,\\\"North*\\\",F2:F9)\":  \"138772\",\n\t\t\"SUMIF(D1:D3,\\\"Month\\\",D1:D3)\":   \"0\",\n\t\t// SUMPRODUCT\n\t\t\"SUMPRODUCT(A1,B1)\":             \"4\",\n\t\t\"SUMPRODUCT(A1:A2,B1:B2)\":       \"14\",\n\t\t\"SUMPRODUCT(A1:A3,B1:B3)\":       \"14\",\n\t\t\"SUMPRODUCT(A1:B3)\":             \"15\",\n\t\t\"SUMPRODUCT(A1:A3,B1:B3,B2:B4)\": \"20\",\n\t\t// SUMSQ\n\t\t\"SUMSQ(A1:A4)\":              \"14\",\n\t\t\"SUMSQ(A1,B1,A2,B2,6)\":      \"82\",\n\t\t\"SUMSQ(\\\"\\\",A1,B1,A2,B2,6)\": \"82\",\n\t\t\"SUMSQ(1,SUMSQ(1))\":         \"2\",\n\t\t\"SUMSQ(\\\"1\\\",SUMSQ(1))\":     \"2\",\n\t\t\"SUMSQ(MUNIT(3))\":           \"3\",\n\t\t// SUMX2MY2\n\t\t\"SUMX2MY2(A1:A4,B1:B4)\": \"-36\",\n\t\t// SUMX2PY2\n\t\t\"SUMX2PY2(A1:A4,B1:B4)\": \"46\",\n\t\t// SUMXMY2\n\t\t\"SUMXMY2(A1:A4,B1:B4)\": \"18\",\n\t\t// TAN\n\t\t\"TAN(1.047197551)\": \"1.73205080678249\",\n\t\t\"TAN(0)\":           \"0\",\n\t\t\"TAN(TAN(0))\":      \"0\",\n\t\t// TANH\n\t\t\"TANH(0)\":       \"0\",\n\t\t\"TANH(0.5)\":     \"0.46211715726001\",\n\t\t\"TANH(-2)\":      \"-0.964027580075817\",\n\t\t\"TANH(TANH(0))\": \"0\",\n\t\t// TRUNC\n\t\t\"TRUNC(99.999,1)\":    \"99.9\",\n\t\t\"TRUNC(99.999,2)\":    \"99.99\",\n\t\t\"TRUNC(99.999)\":      \"99\",\n\t\t\"TRUNC(99.999,-1)\":   \"90\",\n\t\t\"TRUNC(-99.999,2)\":   \"-99.99\",\n\t\t\"TRUNC(-99.999,-1)\":  \"-90\",\n\t\t\"TRUNC(TRUNC(1),-1)\": \"0\",\n\t\t// Statistical Functions\n\t\t// AVEDEV\n\t\t\"AVEDEV(1,2)\":          \"0.5\",\n\t\t\"AVERAGE(A1:A4,B1:B4)\": \"2.5\",\n\t\t// AVERAGE\n\t\t\"AVERAGE(INT(1))\": \"1\",\n\t\t\"AVERAGE(A1)\":     \"1\",\n\t\t\"AVERAGE(A1:A2)\":  \"1.5\",\n\t\t\"AVERAGE(D2:F9)\":  \"38014.125\",\n\t\t// AVERAGEA\n\t\t\"AVERAGEA(INT(1))\": \"1\",\n\t\t\"AVERAGEA(A1)\":     \"1\",\n\t\t\"AVERAGEA(\\\"1\\\")\":  \"1\",\n\t\t\"AVERAGEA(A1:A2)\":  \"1.5\",\n\t\t\"AVERAGEA(D2:F9)\":  \"12671.375\",\n\t\t// BETA.DIST\n\t\t\"BETA.DIST(0.4,4,5,TRUE,0,1)\":  \"0.4059136\",\n\t\t\"BETA.DIST(0.6,4,5,FALSE,0,1)\": \"1.548288\",\n\t\t// BETADIST\n\t\t\"BETADIST(0.4,4,5)\":         \"0.4059136\",\n\t\t\"BETADIST(0.4,4,5,0,1)\":     \"0.4059136\",\n\t\t\"BETADIST(0.4,4,5,0.4,1)\":   \"0\",\n\t\t\"BETADIST(1,2,2,1,3)\":       \"0\",\n\t\t\"BETADIST(0.4,4,5,0.2,0.4)\": \"1\",\n\t\t\"BETADIST(0.4,4,1)\":         \"0.0256\",\n\t\t\"BETADIST(0.4,1,5)\":         \"0.92224\",\n\t\t\"BETADIST(3,4,6,2,4)\":       \"0.74609375\",\n\t\t\"BETADIST(0.4,2,100)\":       \"1\",\n\t\t\"BETADIST(0.75,3,4)\":        \"0.96240234375\",\n\t\t\"BETADIST(0.2,0.7,4)\":       \"0.71794309318323\",\n\t\t\"BETADIST(0.01,3,4)\":        \"1.955359E-05\",\n\t\t\"BETADIST(0.75,130,140)\":    \"1\",\n\t\t// BETAINV\n\t\t\"BETAINV(0.2,4,5,0,1)\": \"0.303225844664082\",\n\t\t// BETA.INV\n\t\t\"BETA.INV(0.2,4,5,0,1)\": \"0.303225844664082\",\n\t\t// BINOMDIST\n\t\t\"BINOMDIST(10,100,0.5,FALSE)\": \"1.36554263874631E-17\",\n\t\t\"BINOMDIST(50,100,0.5,FALSE)\": \"0.0795892373871787\",\n\t\t\"BINOMDIST(65,100,0.5,FALSE)\": \"0.000863855665741652\",\n\t\t\"BINOMDIST(10,100,0.5,TRUE)\":  \"1.53164508771899E-17\",\n\t\t\"BINOMDIST(50,100,0.5,TRUE)\":  \"0.539794618693589\",\n\t\t\"BINOMDIST(65,100,0.5,TRUE)\":  \"0.999105034804256\",\n\t\t// BINOM.DIST\n\t\t\"BINOM.DIST(10,100,0.5,FALSE)\": \"1.36554263874631E-17\",\n\t\t\"BINOM.DIST(50,100,0.5,FALSE)\": \"0.0795892373871787\",\n\t\t\"BINOM.DIST(65,100,0.5,FALSE)\": \"0.000863855665741652\",\n\t\t\"BINOM.DIST(10,100,0.5,TRUE)\":  \"1.53164508771899E-17\",\n\t\t\"BINOM.DIST(50,100,0.5,TRUE)\":  \"0.539794618693589\",\n\t\t\"BINOM.DIST(65,100,0.5,TRUE)\":  \"0.999105034804256\",\n\t\t// BINOM.DIST.RANGE\n\t\t\"BINOM.DIST.RANGE(100,0.5,0,40)\":   \"0.0284439668204904\",\n\t\t\"BINOM.DIST.RANGE(100,0.5,45,55)\":  \"0.728746975926165\",\n\t\t\"BINOM.DIST.RANGE(100,0.5,50,100)\": \"0.539794618693589\",\n\t\t\"BINOM.DIST.RANGE(100,0.5,50)\":     \"0.0795892373871787\",\n\t\t// BINOM.INV\n\t\t\"BINOM.INV(0,0.5,0.75)\":   \"0\",\n\t\t\"BINOM.INV(0.1,0.1,0.75)\": \"0\",\n\t\t\"BINOM.INV(0.6,0.4,0.75)\": \"0\",\n\t\t\"BINOM.INV(2,0.4,0.75)\":   \"1\",\n\t\t\"BINOM.INV(100,0.5,20%)\":  \"46\",\n\t\t\"BINOM.INV(100,0.5,50%)\":  \"50\",\n\t\t\"BINOM.INV(100,0.5,90%)\":  \"56\",\n\t\t// CHIDIST\n\t\t\"CHIDIST(0.5,3)\": \"0.918891411654676\",\n\t\t\"CHIDIST(8,3)\":   \"0.0460117056892314\",\n\t\t\"CHIDIST(40,4)\":  \"4.32842260712097E-08\",\n\t\t\"CHIDIST(42,4)\":  \"1.66816329414062E-08\",\n\t\t// CHIINV\n\t\t\"CHIINV(0.5,1)\":  \"0.454936423119572\",\n\t\t\"CHIINV(0.75,1)\": \"0.101531044267622\",\n\t\t\"CHIINV(0.1,2)\":  \"4.60517018598809\",\n\t\t\"CHIINV(0.8,2)\":  \"0.446287102628419\",\n\t\t// CHISQ.DIST\n\t\t\"CHISQ.DIST(0,2,TRUE)\":        \"0\",\n\t\t\"CHISQ.DIST(4,1,TRUE)\":        \"0.954499736103642\",\n\t\t\"CHISQ.DIST(1180,1180,FALSE)\": \"0.00821093706387967\",\n\t\t\"CHISQ.DIST(2,1,FALSE)\":       \"0.103776874355149\",\n\t\t\"CHISQ.DIST(3,2,FALSE)\":       \"0.111565080074215\",\n\t\t\"CHISQ.DIST(2,3,FALSE)\":       \"0.207553748710297\",\n\t\t\"CHISQ.DIST(1425,1,FALSE)\":    \"3.88315098887099E-312\",\n\t\t\"CHISQ.DIST(3,2,TRUE)\":        \"0.77686983985157\",\n\t\t// CHISQ.DIST.RT\n\t\t\"CHISQ.DIST.RT(0.5,3)\": \"0.918891411654676\",\n\t\t\"CHISQ.DIST.RT(8,3)\":   \"0.0460117056892314\",\n\t\t\"CHISQ.DIST.RT(40,4)\":  \"4.32842260712097E-08\",\n\t\t\"CHISQ.DIST.RT(42,4)\":  \"1.66816329414062E-08\",\n\t\t// CHISQ.INV\n\t\t\"CHISQ.INV(0,2)\":    \"0\",\n\t\t\"CHISQ.INV(0.75,1)\": \"1.32330369693147\",\n\t\t\"CHISQ.INV(0.1,2)\":  \"0.210721031315653\",\n\t\t\"CHISQ.INV(0.8,2)\":  \"3.2188758248682\",\n\t\t\"CHISQ.INV(0.25,3)\": \"1.21253290304567\",\n\t\t// CHISQ.INV.RT\n\t\t\"CHISQ.INV.RT(0.75,1)\": \"0.101531044267622\",\n\t\t\"CHISQ.INV.RT(0.1,2)\":  \"4.60517018598809\",\n\t\t\"CHISQ.INV.RT(0.8,2)\":  \"0.446287102628419\",\n\t\t// CONFIDENCE\n\t\t\"CONFIDENCE(0.05,0.07,100)\": \"0.0137197479028414\",\n\t\t// CONFIDENCE.NORM\n\t\t\"CONFIDENCE.NORM(0.05,0.07,100)\": \"0.0137197479028414\",\n\t\t// CONFIDENCE.T\n\t\t\"CONFIDENCE.T(0.05,0.07,100)\": \"0.0138895186611049\",\n\t\t// CORREL\n\t\t\"CORREL(A1:A5,B1:B5)\": \"1\",\n\t\t// COUNT\n\t\t\"COUNT()\":                              \"0\",\n\t\t\"COUNT(E1:F2,\\\"text\\\",1,INT(2),\\\"0\\\")\": \"4\",\n\t\t// COUNTA\n\t\t\"COUNTA()\":                              \"0\",\n\t\t\"COUNTA(A1:A5,B2:B5,\\\"text\\\",1,INT(2))\": \"8\",\n\t\t\"COUNTA(COUNTA(1),MUNIT(1))\":            \"2\",\n\t\t\"COUNTA(D1:D2)\":                         \"2\",\n\t\t// COUNTBLANK\n\t\t\"COUNTBLANK(MUNIT(1))\": \"0\",\n\t\t\"COUNTBLANK(1)\":        \"0\",\n\t\t\"COUNTBLANK(B1:C1)\":    \"1\",\n\t\t\"COUNTBLANK(C1)\":       \"0\",\n\t\t// COUNTIF\n\t\t\"COUNTIF(D1:D9,\\\"Jan\\\")\":     \"4\",\n\t\t\"COUNTIF(D1:D9,\\\"<>Jan\\\")\":   \"5\",\n\t\t\"COUNTIF(A1:F9,\\\">=50000\\\")\": \"2\",\n\t\t\"COUNTIF(A1:F9,TRUE)\":        \"0\",\n\t\t// COUNTIFS\n\t\t\"COUNTIFS(A1:A9,2,D1:D9,\\\"Jan\\\")\":          \"1\",\n\t\t\"COUNTIFS(F1:F9,\\\">20000\\\",D1:D9,\\\"Jan\\\")\": \"4\",\n\t\t\"COUNTIFS(F1:F9,\\\">60000\\\",D1:D9,\\\"Jan\\\")\": \"0\",\n\t\t// CRITBINOM\n\t\t\"CRITBINOM(0,0.5,0.75)\":   \"0\",\n\t\t\"CRITBINOM(0.1,0.1,0.75)\": \"0\",\n\t\t\"CRITBINOM(0.6,0.4,0.75)\": \"0\",\n\t\t\"CRITBINOM(2,0.4,0.75)\":   \"1\",\n\t\t\"CRITBINOM(100,0.5,20%)\":  \"46\",\n\t\t\"CRITBINOM(100,0.5,50%)\":  \"50\",\n\t\t\"CRITBINOM(100,0.5,90%)\":  \"56\",\n\t\t// DEVSQ\n\t\t\"DEVSQ(1,3,5,2,9,7)\": \"47.5\",\n\t\t\"DEVSQ(A1:D2)\":       \"10\",\n\t\t// FISHER\n\t\t\"FISHER(-0.9)\":    \"-1.47221948958322\",\n\t\t\"FISHER(-0.25)\":   \"-0.255412811882995\",\n\t\t\"FISHER(0.8)\":     \"1.09861228866811\",\n\t\t\"FISHER(\\\"0.8\\\")\": \"1.09861228866811\",\n\t\t\"FISHER(INT(0))\":  \"0\",\n\t\t// FISHERINV\n\t\t\"FISHERINV(-0.2)\":   \"-0.197375320224904\",\n\t\t\"FISHERINV(INT(0))\": \"0\",\n\t\t\"FISHERINV(\\\"0\\\")\":  \"0\",\n\t\t\"FISHERINV(2.8)\":    \"0.992631520201128\",\n\t\t// FORECAST\n\t\t\"FORECAST(7,A1:A7,B1:B7)\": \"4\",\n\t\t// FORECAST.LINEAR\n\t\t\"FORECAST.LINEAR(7,A1:A7,B1:B7)\": \"4\",\n\t\t// FREQUENCY\n\t\t\"SUM(FREQUENCY(A2,B2))\":       \"1\",\n\t\t\"SUM(FREQUENCY(A1:A5,B1:B2))\": \"4\",\n\t\t// GAMMA\n\t\t\"GAMMA(0.1)\":     \"9.51350769866873\",\n\t\t\"GAMMA(INT(1))\":  \"1\",\n\t\t\"GAMMA(1.5)\":     \"0.886226925452758\",\n\t\t\"GAMMA(5.5)\":     \"52.3427777845535\",\n\t\t\"GAMMA(\\\"5.5\\\")\": \"52.3427777845535\",\n\t\t// GAMMA.DIST\n\t\t\"GAMMA.DIST(6,3,2,FALSE)\": \"0.112020903827694\",\n\t\t\"GAMMA.DIST(6,3,2,TRUE)\":  \"0.576809918873156\",\n\t\t// GAMMADIST\n\t\t\"GAMMADIST(6,3,2,FALSE)\": \"0.112020903827694\",\n\t\t\"GAMMADIST(6,3,2,TRUE)\":  \"0.576809918873156\",\n\t\t// GAMMA.INV\n\t\t\"GAMMA.INV(0.5,3,2)\":   \"5.34812062744712\",\n\t\t\"GAMMA.INV(0.5,0.5,1)\": \"0.227468211559786\",\n\t\t// GAMMAINV\n\t\t\"GAMMAINV(0.5,3,2)\":   \"5.34812062744712\",\n\t\t\"GAMMAINV(0.5,0.5,1)\": \"0.227468211559786\",\n\t\t// GAMMALN\n\t\t\"GAMMALN(4.5)\":    \"2.45373657084244\",\n\t\t\"GAMMALN(INT(1))\": \"0\",\n\t\t// GAMMALN.PRECISE\n\t\t\"GAMMALN.PRECISE(0.4)\": \"0.796677817701784\",\n\t\t\"GAMMALN.PRECISE(4.5)\": \"2.45373657084244\",\n\t\t// GAUSS\n\t\t\"GAUSS(-5)\":    \"-0.499999713348428\",\n\t\t\"GAUSS(0)\":     \"0\",\n\t\t\"GAUSS(\\\"0\\\")\": \"0\",\n\t\t\"GAUSS(0.1)\":   \"0.039827837277029\",\n\t\t\"GAUSS(2.5)\":   \"0.493790334674224\",\n\t\t// GEOMEAN\n\t\t\"GEOMEAN(2.5,3,0.5,1,3)\": \"1.6226711115996\",\n\t\t// HARMEAN\n\t\t\"HARMEAN(2.5,3,0.5,1,3)\":               \"1.22950819672131\",\n\t\t\"HARMEAN(\\\"2.5\\\",3,0.5,1,INT(3),\\\"\\\")\": \"1.22950819672131\",\n\t\t// HYPGEOM.DIST\n\t\t\"HYPGEOM.DIST(0,3,3,9,TRUE)\":   \"0.238095238095238\",\n\t\t\"HYPGEOM.DIST(1,3,3,9,TRUE)\":   \"0.773809523809524\",\n\t\t\"HYPGEOM.DIST(2,3,3,9,TRUE)\":   \"0.988095238095238\",\n\t\t\"HYPGEOM.DIST(3,3,3,9,TRUE)\":   \"1\",\n\t\t\"HYPGEOM.DIST(1,4,4,12,FALSE)\": \"0.452525252525253\",\n\t\t\"HYPGEOM.DIST(2,4,4,12,FALSE)\": \"0.339393939393939\",\n\t\t\"HYPGEOM.DIST(3,4,4,12,FALSE)\": \"0.0646464646464646\",\n\t\t\"HYPGEOM.DIST(4,4,4,12,FALSE)\": \"0.00202020202020202\",\n\t\t// HYPGEOMDIST\n\t\t\"HYPGEOMDIST(1,4,4,12)\": \"0.452525252525253\",\n\t\t\"HYPGEOMDIST(2,4,4,12)\": \"0.339393939393939\",\n\t\t\"HYPGEOMDIST(3,4,4,12)\": \"0.0646464646464646\",\n\t\t\"HYPGEOMDIST(4,4,4,12)\": \"0.00202020202020202\",\n\t\t// INTERCEPT\n\t\t\"INTERCEPT(A1:A4,B1:B4)\": \"-3\",\n\t\t// KURT\n\t\t\"KURT(F1:F9)\":           \"-1.03350350255137\",\n\t\t\"KURT(F1,F2:F9)\":        \"-1.03350350255137\",\n\t\t\"KURT(INT(1),MUNIT(2))\": \"-3.33333333333334\",\n\t\t// EXPON.DIST\n\t\t\"EXPON.DIST(0.5,1,TRUE)\":  \"0.393469340287367\",\n\t\t\"EXPON.DIST(0.5,1,FALSE)\": \"0.606530659712633\",\n\t\t\"EXPON.DIST(2,1,TRUE)\":    \"0.864664716763387\",\n\t\t// EXPONDIST\n\t\t\"EXPONDIST(0.5,1,TRUE)\":  \"0.393469340287367\",\n\t\t\"EXPONDIST(0.5,1,FALSE)\": \"0.606530659712633\",\n\t\t\"EXPONDIST(2,1,TRUE)\":    \"0.864664716763387\",\n\t\t// FDIST\n\t\t\"FDIST(5,1,2)\": \"0.154845745271483\",\n\t\t// F.DIST\n\t\t\"F.DIST(1,2,5,TRUE)\":  \"0.568798849628308\",\n\t\t\"F.DIST(1,2,5,FALSE)\": \"0.308000821694066\",\n\t\t// F.DIST.RT\n\t\t\"F.DIST.RT(5,1,2)\": \"0.154845745271483\",\n\t\t// F.INV\n\t\t\"F.INV(0.9,2,5)\": \"3.77971607877395\",\n\t\t// FINV\n\t\t\"FINV(0.2,1,2)\":   \"3.55555555555555\",\n\t\t\"FINV(0.6,1,2)\":   \"0.380952380952381\",\n\t\t\"FINV(0.6,2,2)\":   \"0.666666666666667\",\n\t\t\"FINV(0.6,4,4)\":   \"0.763454070045235\",\n\t\t\"FINV(0.5,4,8)\":   \"0.914645355977072\",\n\t\t\"FINV(0.1,79,86)\": \"1.32646097270444\",\n\t\t\"FINV(1,40,5)\":    \"0\",\n\t\t// F.INV.RT\n\t\t\"F.INV.RT(0.2,1,2)\":   \"3.55555555555555\",\n\t\t\"F.INV.RT(0.6,1,2)\":   \"0.380952380952381\",\n\t\t\"F.INV.RT(0.6,2,2)\":   \"0.666666666666667\",\n\t\t\"F.INV.RT(0.6,4,4)\":   \"0.763454070045235\",\n\t\t\"F.INV.RT(0.5,4,8)\":   \"0.914645355977072\",\n\t\t\"F.INV.RT(0.1,79,86)\": \"1.32646097270444\",\n\t\t\"F.INV.RT(1,40,5)\":    \"0\",\n\t\t// LOGINV\n\t\t\"LOGINV(0.3,2,0.2)\": \"6.6533460753367\",\n\t\t// LOGINV\n\t\t\"LOGNORM.INV(0.3,2,0.2)\": \"6.6533460753367\",\n\t\t// LOGNORM.DIST\n\t\t\"LOGNORM.DIST(0.5,10,5,FALSE)\": \"0.0162104821842127\",\n\t\t\"LOGNORM.DIST(12,10,5,TRUE)\":   \"0.0664171147992078\",\n\t\t// LOGNORMDIST\n\t\t\"LOGNORMDIST(12,10,5)\": \"0.0664171147992078\",\n\t\t// NEGBINOM.DIST\n\t\t\"NEGBINOM.DIST(6,12,0.5,FALSE)\":  \"0.047210693359375\",\n\t\t\"NEGBINOM.DIST(12,12,0.5,FALSE)\": \"0.0805901288986206\",\n\t\t\"NEGBINOM.DIST(15,12,0.5,FALSE)\": \"0.057564377784729\",\n\t\t\"NEGBINOM.DIST(12,12,0.5,TRUE)\":  \"0.58059012889862\",\n\t\t\"NEGBINOM.DIST(15,12,0.5,TRUE)\":  \"0.778965830802917\",\n\t\t// NEGBINOMDIST\n\t\t\"NEGBINOMDIST(6,12,0.5)\":  \"0.047210693359375\",\n\t\t\"NEGBINOMDIST(12,12,0.5)\": \"0.0805901288986206\",\n\t\t\"NEGBINOMDIST(15,12,0.5)\": \"0.057564377784729\",\n\t\t// NORM.DIST\n\t\t\"NORM.DIST(0.8,1,0.3,TRUE)\": \"0.252492537546923\",\n\t\t\"NORM.DIST(50,40,20,FALSE)\": \"0.017603266338215\",\n\t\t// NORMDIST\n\t\t\"NORMDIST(0.8,1,0.3,TRUE)\": \"0.252492537546923\",\n\t\t\"NORMDIST(50,40,20,FALSE)\": \"0.017603266338215\",\n\t\t// NORM.INV\n\t\t\"NORM.INV(0.6,5,2)\": \"5.50669420572\",\n\t\t// NORMINV\n\t\t\"NORMINV(0.6,5,2)\":     \"5.50669420572\",\n\t\t\"NORMINV(0.99,40,1.5)\": \"43.489521811582\",\n\t\t\"NORMINV(0.02,40,1.5)\": \"36.9193766364954\",\n\t\t// NORM.S.DIST\n\t\t\"NORM.S.DIST(0.8,TRUE)\": \"0.788144601416603\",\n\t\t// NORMSDIST\n\t\t\"NORMSDIST(1.333333)\": \"0.908788725604095\",\n\t\t\"NORMSDIST(0)\":        \"0.5\",\n\t\t// NORM.S.INV\n\t\t\"NORM.S.INV(0.25)\": \"-0.674489750223423\",\n\t\t// NORMSINV\n\t\t\"NORMSINV(0.25)\": \"-0.674489750223423\",\n\t\t// LARGE\n\t\t\"LARGE(A1:A5,1)\": \"3\",\n\t\t\"LARGE(A1:B5,2)\": \"4\",\n\t\t\"LARGE(A1,1)\":    \"1\",\n\t\t\"LARGE(A1:F2,1)\": \"36693\",\n\t\t// MAX\n\t\t\"MAX(1)\":           \"1\",\n\t\t\"MAX(TRUE())\":      \"1\",\n\t\t\"MAX(0.5,TRUE())\":  \"1\",\n\t\t\"MAX(FALSE())\":     \"0\",\n\t\t\"MAX(MUNIT(2))\":    \"1\",\n\t\t\"MAX(INT(1))\":      \"1\",\n\t\t\"MAX(\\\"0\\\",\\\"2\\\")\": \"2\",\n\t\t// MAXA\n\t\t\"MAXA(1)\":          \"1\",\n\t\t\"MAXA(TRUE())\":     \"1\",\n\t\t\"MAXA(0.5,TRUE())\": \"1\",\n\t\t\"MAXA(FALSE())\":    \"0\",\n\t\t\"MAXA(MUNIT(2))\":   \"1\",\n\t\t\"MAXA(INT(1))\":     \"1\",\n\t\t\"MAXA(A1:B4,MUNIT(1),INT(0),1,E1:F2,\\\"\\\")\": \"36693\",\n\t\t// MAXIFS\n\t\t\"MAXIFS(F2:F4,A2:A4,\\\">0\\\")\": \"36693\",\n\t\t// MEDIAN\n\t\t\"MEDIAN(A1:A5,12)\":               \"2\",\n\t\t\"MEDIAN(A1:A5)\":                  \"1.5\",\n\t\t\"MEDIAN(A1:A5,MEDIAN(A1:A5,12))\": \"2\",\n\t\t\"MEDIAN(\\\"0\\\",\\\"2\\\")\":            \"1\",\n\t\t// MIN\n\t\t\"MIN(1)\":           \"1\",\n\t\t\"MIN(TRUE())\":      \"1\",\n\t\t\"MIN(0.5,FALSE())\": \"0\",\n\t\t\"MIN(FALSE())\":     \"0\",\n\t\t\"MIN(MUNIT(2))\":    \"0\",\n\t\t\"MIN(INT(1))\":      \"1\",\n\t\t\"MIN(2,\\\"1\\\")\":     \"1\",\n\t\t// MINA\n\t\t\"MINA(1)\":           \"1\",\n\t\t\"MINA(TRUE())\":      \"1\",\n\t\t\"MINA(0.5,FALSE())\": \"0\",\n\t\t\"MINA(FALSE())\":     \"0\",\n\t\t\"MINA(MUNIT(2))\":    \"0\",\n\t\t\"MINA(INT(1))\":      \"1\",\n\t\t\"MINA(A1:B4,MUNIT(1),INT(0),1,E1:F2,\\\"\\\")\": \"0\",\n\t\t// MINIFS\n\t\t\"MINIFS(F2:F4,A2:A4,\\\">0\\\")\": \"22100\",\n\t\t// PEARSON\n\t\t\"PEARSON(A1:A4,B1:B4)\": \"1\",\n\t\t// PERCENTILE.EXC\n\t\t\"PERCENTILE.EXC(A1:A4,0.2)\": \"0\",\n\t\t\"PERCENTILE.EXC(A1:A4,0.6)\": \"2\",\n\t\t// PERCENTILE.INC\n\t\t\"PERCENTILE.INC(A1:A4,0.2)\": \"0.6\",\n\t\t// PERCENTILE\n\t\t\"PERCENTILE(A1:A4,0.2)\": \"0.6\",\n\t\t\"PERCENTILE(0,0)\":       \"0\",\n\t\t// PERCENTRANK.EXC\n\t\t\"PERCENTRANK.EXC(A1:B4,0)\":     \"0.142\",\n\t\t\"PERCENTRANK.EXC(A1:B4,2)\":     \"0.428\",\n\t\t\"PERCENTRANK.EXC(A1:B4,2.5)\":   \"0.5\",\n\t\t\"PERCENTRANK.EXC(A1:B4,2.6,1)\": \"0.5\",\n\t\t\"PERCENTRANK.EXC(A1:B4,5)\":     \"0.857\",\n\t\t// PERCENTRANK.INC\n\t\t\"PERCENTRANK.INC(A1:B4,0)\":     \"0\",\n\t\t\"PERCENTRANK.INC(A1:B4,2)\":     \"0.4\",\n\t\t\"PERCENTRANK.INC(A1:B4,2.5)\":   \"0.5\",\n\t\t\"PERCENTRANK.INC(A1:B4,2.6,1)\": \"0.5\",\n\t\t\"PERCENTRANK.INC(A1:B4,5)\":     \"1\",\n\t\t// PERCENTRANK\n\t\t\"PERCENTRANK(A1:B4,0)\":     \"0\",\n\t\t\"PERCENTRANK(A1:B4,2)\":     \"0.4\",\n\t\t\"PERCENTRANK(A1:B4,2.5)\":   \"0.5\",\n\t\t\"PERCENTRANK(A1:B4,2.6,1)\": \"0.5\",\n\t\t\"PERCENTRANK(A1:B4,5)\":     \"1\",\n\t\t// PERMUT\n\t\t\"PERMUT(6,6)\":  \"720\",\n\t\t\"PERMUT(7,6)\":  \"5040\",\n\t\t\"PERMUT(10,6)\": \"151200\",\n\t\t// PERMUTATIONA\n\t\t\"PERMUTATIONA(6,6)\": \"46656\",\n\t\t\"PERMUTATIONA(7,6)\": \"117649\",\n\t\t// PHI\n\t\t\"PHI(-1.5)\": \"0.129517595665892\",\n\t\t\"PHI(0)\":    \"0.398942280401433\",\n\t\t\"PHI(0.1)\":  \"0.396952547477012\",\n\t\t\"PHI(1)\":    \"0.241970724519143\",\n\t\t// QUARTILE\n\t\t\"QUARTILE(A1:A4,2)\": \"1.5\",\n\t\t// QUARTILE.EXC\n\t\t\"QUARTILE.EXC(A1:A4,1)\": \"0.25\",\n\t\t\"QUARTILE.EXC(A1:A4,2)\": \"1.5\",\n\t\t\"QUARTILE.EXC(A1:A4,3)\": \"2.75\",\n\t\t// QUARTILE.INC\n\t\t\"QUARTILE.INC(A1:A4,0)\": \"0\",\n\t\t// RANK\n\t\t\"RANK(1,A1:B5)\":   \"5\",\n\t\t\"RANK(1,A1:B5,0)\": \"5\",\n\t\t\"RANK(1,A1:B5,1)\": \"2\",\n\t\t// RANK.EQ\n\t\t\"RANK.EQ(1,A1:B5)\":   \"5\",\n\t\t\"RANK.EQ(1,A1:B5,0)\": \"5\",\n\t\t\"RANK.EQ(1,A1:B5,1)\": \"2\",\n\t\t// RSQ\n\t\t\"RSQ(A1:A4,B1:B4)\": \"1\",\n\t\t// SKEW\n\t\t\"SKEW(1,2,3,4,3)\": \"-0.404796008910937\",\n\t\t\"SKEW(A1:B2)\":     \"0\",\n\t\t\"SKEW(A1:D3)\":     \"0\",\n\t\t// SKEW.P\n\t\t\"SKEW.P(1,2,3,4,3)\": \"-0.27154541788364\",\n\t\t\"SKEW.P(A1:B2)\":     \"0\",\n\t\t\"SKEW.P(A1:D3)\":     \"0\",\n\t\t// SLOPE\n\t\t\"SLOPE(A1:A4,B1:B4)\": \"1\",\n\t\t// SMALL\n\t\t\"SMALL(A1:A5,1)\": \"0\",\n\t\t\"SMALL(A1:B5,2)\": \"1\",\n\t\t\"SMALL(A1,1)\":    \"1\",\n\t\t\"SMALL(A1:F2,1)\": \"1\",\n\t\t// STANDARDIZE\n\t\t\"STANDARDIZE(5.5,5,2)\":   \"0.25\",\n\t\t\"STANDARDIZE(12,15,1.5)\": \"-2\",\n\t\t\"STANDARDIZE(-2,0,5)\":    \"-0.4\",\n\t\t// STDEVP\n\t\t\"STDEVP(A1:B2,6,-1)\": \"2.40947204913349\",\n\t\t// STDEV.P\n\t\t\"STDEV.P(A1:B2,6,-1)\": \"2.40947204913349\",\n\t\t// STDEVPA\n\t\t\"STDEVPA(1,3,5,2)\":               \"1.4790199457749\",\n\t\t\"STDEVPA(1,3,5,2,1,0)\":           \"1.63299316185545\",\n\t\t\"STDEVPA(1,3,5,2,TRUE,\\\"text\\\")\": \"1.63299316185545\",\n\t\t// T.DIST\n\t\t\"T.DIST(1,10,TRUE)\":   \"0.82955343384897\",\n\t\t\"T.DIST(-1,10,TRUE)\":  \"0.17044656615103\",\n\t\t\"T.DIST(-1,10,FALSE)\": \"0.230361989229139\",\n\t\t// T.DIST.2T\n\t\t\"T.DIST.2T(1,10)\": \"0.34089313230206\",\n\t\t// T.DIST.RT\n\t\t\"T.DIST.RT(1,10)\":  \"0.17044656615103\",\n\t\t\"T.DIST.RT(-1,10)\": \"0.82955343384897\",\n\t\t// TDIST\n\t\t\"TDIST(1,10,1)\": \"0.17044656615103\",\n\t\t\"TDIST(1,10,2)\": \"0.34089313230206\",\n\t\t// T.INV\n\t\t\"T.INV(0.25,10)\": \"-0.699812061312432\",\n\t\t\"T.INV(0.75,10)\": \"0.699812061312432\",\n\t\t// T.INV.2T\n\t\t\"T.INV.2T(1,10)\":   \"0\",\n\t\t\"T.INV.2T(0.5,10)\": \"0.699812061312432\",\n\t\t// TINV\n\t\t\"TINV(1,10)\":   \"0\",\n\t\t\"TINV(0.5,10)\": \"0.699812061312432\",\n\t\t// TRIMMEAN\n\t\t\"TRIMMEAN(A1:B4,10%)\": \"2.5\",\n\t\t\"TRIMMEAN(A1:B4,70%)\": \"2.5\",\n\t\t// VAR\n\t\t\"VAR(1,3,5,0,C1)\":      \"4.91666666666667\",\n\t\t\"VAR(1,3,5,0,C1,TRUE)\": \"4\",\n\t\t// VARA\n\t\t\"VARA(1,3,5,0,C1)\":      \"4.91666666666667\",\n\t\t\"VARA(1,3,5,0,C1,TRUE)\": \"4\",\n\t\t// VARP\n\t\t\"VARP(A1:A5)\":           \"1.25\",\n\t\t\"VARP(1,3,5,0,C1,TRUE)\": \"3.2\",\n\t\t// VAR.P\n\t\t\"VAR.P(A1:A5)\": \"1.25\",\n\t\t// VAR.S\n\t\t\"VAR.S(1,3,5,0,C1)\":      \"4.91666666666667\",\n\t\t\"VAR.S(1,3,5,0,C1,TRUE)\": \"4\",\n\t\t// VARPA\n\t\t\"VARPA(1,3,5,0,C1)\":      \"3.6875\",\n\t\t\"VARPA(1,3,5,0,C1,TRUE)\": \"3.2\",\n\t\t// WEIBULL\n\t\t\"WEIBULL(1,3,1,FALSE)\":  \"1.10363832351433\",\n\t\t\"WEIBULL(2,5,1.5,TRUE)\": \"0.985212776817482\",\n\t\t// WEIBULL.DIST\n\t\t\"WEIBULL.DIST(1,3,1,FALSE)\":  \"1.10363832351433\",\n\t\t\"WEIBULL.DIST(2,5,1.5,TRUE)\": \"0.985212776817482\",\n\t\t// Information Functions\n\t\t// ERROR.TYPE\n\t\t\"ERROR.TYPE(1/0)\":           \"2\",\n\t\t\"ERROR.TYPE(COT(0))\":        \"2\",\n\t\t\"ERROR.TYPE(XOR(\\\"text\\\"))\": \"3\",\n\t\t\"ERROR.TYPE(HEX2BIN(2,1))\":  \"6\",\n\t\t\"ERROR.TYPE(NA())\":          \"7\",\n\t\t// ISBLANK\n\t\t\"ISBLANK(A1)\": \"FALSE\",\n\t\t\"ISBLANK(A5)\": \"TRUE\",\n\t\t// ISERR\n\t\t\"ISERR(A1)\":           \"FALSE\",\n\t\t\"ISERR(NA())\":         \"FALSE\",\n\t\t\"ISERR(POWER(0,-1)))\": \"TRUE\",\n\t\t// ISERROR\n\t\t\"ISERROR(A1)\":          \"FALSE\",\n\t\t\"ISERROR(NA())\":        \"TRUE\",\n\t\t\"ISERROR(\\\"#VALUE!\\\")\": \"FALSE\",\n\t\t// ISEVEN\n\t\t\"ISEVEN(A1)\": \"FALSE\",\n\t\t\"ISEVEN(A2)\": \"TRUE\",\n\t\t\"ISEVEN(G1)\": \"TRUE\",\n\t\t// ISFORMULA\n\t\t\"ISFORMULA(A1)\":    \"FALSE\",\n\t\t\"ISFORMULA(\\\"A\\\")\": \"FALSE\",\n\t\t// ISLOGICAL\n\t\t\"ISLOGICAL(TRUE)\":      \"TRUE\",\n\t\t\"ISLOGICAL(FALSE)\":     \"TRUE\",\n\t\t\"ISLOGICAL(A1=A2)\":     \"TRUE\",\n\t\t\"ISLOGICAL(\\\"true\\\")\":  \"TRUE\",\n\t\t\"ISLOGICAL(\\\"false\\\")\": \"TRUE\",\n\t\t\"ISLOGICAL(A1)\":        \"FALSE\",\n\t\t\"ISLOGICAL(20/5)\":      \"FALSE\",\n\t\t// ISNA\n\t\t\"ISNA(A1)\":   \"FALSE\",\n\t\t\"ISNA(NA())\": \"TRUE\",\n\t\t// ISNONTEXT\n\t\t\"ISNONTEXT(A1)\":           \"TRUE\",\n\t\t\"ISNONTEXT(A5)\":           \"TRUE\",\n\t\t\"ISNONTEXT(\\\"Excelize\\\")\": \"FALSE\",\n\t\t\"ISNONTEXT(NA())\":         \"TRUE\",\n\t\t// ISNUMBER\n\t\t\"ISNUMBER(A1)\":    \"TRUE\",\n\t\t\"ISNUMBER(D1)\":    \"FALSE\",\n\t\t\"ISNUMBER(A1:B1)\": \"TRUE\",\n\t\t// ISODD\n\t\t\"ISODD(A1)\": \"TRUE\",\n\t\t\"ISODD(A2)\": \"FALSE\",\n\t\t// ISREF\n\t\t\"ISREF(B1)\":       \"TRUE\",\n\t\t\"ISREF(B1:B2)\":    \"TRUE\",\n\t\t\"ISREF(\\\"text\\\")\": \"FALSE\",\n\t\t\"ISREF(B1*B2)\":    \"FALSE\",\n\t\t// ISTEXT\n\t\t\"ISTEXT(D1)\": \"TRUE\",\n\t\t\"ISTEXT(A1)\": \"FALSE\",\n\t\t// N\n\t\t\"N(10)\":     \"10\",\n\t\t\"N(\\\"10\\\")\": \"10\",\n\t\t\"N(\\\"x\\\")\":  \"0\",\n\t\t\"N(TRUE)\":   \"1\",\n\t\t\"N(FALSE)\":  \"0\",\n\t\t// SHEET\n\t\t\"SHEET()\":           \"1\",\n\t\t\"SHEET(\\\"Sheet1\\\")\": \"1\",\n\t\t// SHEETS\n\t\t\"SHEETS()\":   \"1\",\n\t\t\"SHEETS(A1)\": \"1\",\n\t\t// TYPE\n\t\t\"TYPE(2)\":        \"1\",\n\t\t\"TYPE(10/2)\":     \"1\",\n\t\t\"TYPE(C2)\":       \"1\",\n\t\t\"TYPE(\\\"text\\\")\": \"2\",\n\t\t\"TYPE(TRUE)\":     \"4\",\n\t\t\"TYPE(NA())\":     \"16\",\n\t\t\"TYPE(MUNIT(2))\": \"64\",\n\t\t// T\n\t\t\"T(\\\"text\\\")\": \"text\",\n\t\t\"T(N(10))\":    \"\",\n\t\t// Logical Functions\n\t\t// AND\n\t\t\"AND(0)\":                  \"FALSE\",\n\t\t\"AND(1)\":                  \"TRUE\",\n\t\t\"AND(1,0)\":                \"FALSE\",\n\t\t\"AND(0,1)\":                \"FALSE\",\n\t\t\"AND(1=1)\":                \"TRUE\",\n\t\t\"AND(1<2)\":                \"TRUE\",\n\t\t\"AND(1>2,2<3,2>0,3>1)\":    \"FALSE\",\n\t\t\"AND(1=1),1=1\":            \"TRUE\",\n\t\t\"AND(\\\"TRUE\\\",\\\"FALSE\\\")\": \"FALSE\",\n\t\t// FALSE\n\t\t\"FALSE()\": \"FALSE\",\n\t\t// IFERROR\n\t\t\"IFERROR(1/2,0)\":             \"0.5\",\n\t\t\"IFERROR(ISERROR(),0)\":       \"0\",\n\t\t\"IFERROR(1/0,0)\":             \"0\",\n\t\t\"IFERROR(G1,2)\":              \"0\",\n\t\t\"IFERROR(B2/MROUND(A2,1),0)\": \"2.5\",\n\t\t// IFNA\n\t\t\"IFNA(1,\\\"not found\\\")\":                   \"1\",\n\t\t\"IFNA(NA(),\\\"not found\\\")\":                \"not found\",\n\t\t\"IFNA(HLOOKUP(D2,D:D,1,2),\\\"not found\\\")\": \"not found\",\n\t\t// IFS\n\t\t\"IFS(4>1,5/4,4<-1,-5/4,TRUE,0)\":     \"1.25\",\n\t\t\"IFS(-2>1,5/-2,-2<-1,-5/-2,TRUE,0)\": \"2.5\",\n\t\t\"IFS(0>1,5/0,0<-1,-5/0,TRUE,0)\":     \"0\",\n\t\t// NOT\n\t\t\"NOT(FALSE())\":     \"TRUE\",\n\t\t\"NOT(\\\"false\\\")\":   \"TRUE\",\n\t\t\"NOT(\\\"true\\\")\":    \"FALSE\",\n\t\t\"NOT(ISBLANK(B1))\": \"TRUE\",\n\t\t// OR\n\t\t\"OR(1)\":                  \"TRUE\",\n\t\t\"OR(0)\":                  \"FALSE\",\n\t\t\"OR(1=2,2=2)\":            \"TRUE\",\n\t\t\"OR(1=2,2=3)\":            \"FALSE\",\n\t\t\"OR(1=1,2=3)\":            \"TRUE\",\n\t\t\"OR(\\\"TRUE\\\",\\\"FALSE\\\")\": \"TRUE\",\n\t\t\"OR(A1:B1)\":              \"TRUE\",\n\t\t// SWITCH\n\t\t\"SWITCH(1,1,\\\"A\\\",2,\\\"B\\\",3,\\\"C\\\",\\\"N\\\")\": \"A\",\n\t\t\"SWITCH(3,1,\\\"A\\\",2,\\\"B\\\",3,\\\"C\\\",\\\"N\\\")\": \"C\",\n\t\t\"SWITCH(4,1,\\\"A\\\",2,\\\"B\\\",3,\\\"C\\\",\\\"N\\\")\": \"N\",\n\t\t// TRUE\n\t\t\"TRUE()\": \"TRUE\",\n\t\t// XOR\n\t\t\"XOR(1>0,2>0)\":                       \"FALSE\",\n\t\t\"XOR(1>0,0>1)\":                       \"TRUE\",\n\t\t\"XOR(1>0,0>1,INT(0),INT(1),A1:A4,2)\": \"FALSE\",\n\t\t// Date and Time Functions\n\t\t// DATE\n\t\t\"DATE(2020,10,21)\":   \"44125\",\n\t\t\"DATE(2020,10,21)+1\": \"44126\",\n\t\t\"DATE(1900,1,1)\":     \"1\",\n\t\t// DATEDIF\n\t\t\"DATEDIF(43101,43101,\\\"D\\\")\":  \"0\",\n\t\t\"DATEDIF(43101,43891,\\\"d\\\")\":  \"790\",\n\t\t\"DATEDIF(43101,43891,\\\"Y\\\")\":  \"2\",\n\t\t\"DATEDIF(42156,44242,\\\"y\\\")\":  \"5\",\n\t\t\"DATEDIF(43101,43891,\\\"M\\\")\":  \"26\",\n\t\t\"DATEDIF(42171,44242,\\\"m\\\")\":  \"67\",\n\t\t\"DATEDIF(42156,44454,\\\"MD\\\")\": \"14\",\n\t\t\"DATEDIF(42171,44242,\\\"md\\\")\": \"30\",\n\t\t\"DATEDIF(43101,43891,\\\"YM\\\")\": \"2\",\n\t\t\"DATEDIF(42171,44242,\\\"ym\\\")\": \"7\",\n\t\t\"DATEDIF(43101,43891,\\\"YD\\\")\": \"59\",\n\t\t\"DATEDIF(36526,73110,\\\"YD\\\")\": \"60\",\n\t\t\"DATEDIF(42171,44242,\\\"yd\\\")\": \"244\",\n\t\t// DATEVALUE\n\t\t\"DATEVALUE(\\\"01/01/16\\\")\":   \"42370\",\n\t\t\"DATEVALUE(\\\"01/01/2016\\\")\": \"42370\",\n\t\t\"DATEVALUE(\\\"01/01/29\\\")\":   \"47119\",\n\t\t\"DATEVALUE(\\\"01/01/30\\\")\":   \"10959\",\n\t\t// DAY\n\t\t\"DAY(0)\":                                \"0\",\n\t\t\"DAY(INT(7))\":                           \"7\",\n\t\t\"DAY(\\\"35\\\")\":                           \"4\",\n\t\t\"DAY(42171)\":                            \"16\",\n\t\t\"DAY(\\\"2-28-1900\\\")\":                    \"28\",\n\t\t\"DAY(\\\"31-May-2015\\\")\":                  \"31\",\n\t\t\"DAY(\\\"01/03/2019 12:14:16\\\")\":          \"3\",\n\t\t\"DAY(\\\"January 25, 2020 01 AM\\\")\":       \"25\",\n\t\t\"DAY(\\\"January 25, 2020 01:03 AM\\\")\":    \"25\",\n\t\t\"DAY(\\\"January 25, 2020 12:00:00 AM\\\")\": \"25\",\n\t\t\"DAY(\\\"1900-1-1\\\")\":                     \"1\",\n\t\t\"DAY(\\\"12-1-1900\\\")\":                    \"1\",\n\t\t\"DAY(\\\"3-January-1900\\\")\":               \"3\",\n\t\t\"DAY(\\\"3-February-2000\\\")\":              \"3\",\n\t\t\"DAY(\\\"3-February-2008\\\")\":              \"3\",\n\t\t\"DAY(\\\"01/25/20\\\")\":                     \"25\",\n\t\t\"DAY(\\\"01/25/31\\\")\":                     \"25\",\n\t\t// DAYS\n\t\t\"DAYS(2,1)\":                           \"1\",\n\t\t\"DAYS(INT(2),INT(1))\":                 \"1\",\n\t\t\"DAYS(\\\"02/02/2015\\\",\\\"01/01/2015\\\")\": \"32\",\n\t\t// DAYS360\n\t\t\"DAYS360(\\\"10/10/2020\\\", \\\"10/10/2020\\\")\":       \"0\",\n\t\t\"DAYS360(\\\"01/30/1999\\\", \\\"02/28/1999\\\")\":       \"28\",\n\t\t\"DAYS360(\\\"01/31/1999\\\", \\\"02/28/1999\\\")\":       \"28\",\n\t\t\"DAYS360(\\\"12/12/1999\\\", \\\"08/31/1999\\\")\":       \"-101\",\n\t\t\"DAYS360(\\\"12/12/1999\\\", \\\"11/30/1999\\\")\":       \"-12\",\n\t\t\"DAYS360(\\\"12/12/1999\\\", \\\"11/30/1999\\\",TRUE)\":  \"-12\",\n\t\t\"DAYS360(\\\"01/31/1999\\\", \\\"03/31/1999\\\",TRUE)\":  \"60\",\n\t\t\"DAYS360(\\\"01/31/1999\\\", \\\"03/31/2000\\\",FALSE)\": \"420\",\n\t\t// EDATE\n\t\t\"EDATE(\\\"01/01/2021\\\",-1)\": \"44166\",\n\t\t\"EDATE(\\\"01/31/2020\\\",1)\":  \"43890\",\n\t\t\"EDATE(\\\"01/29/2020\\\",12)\": \"44225\",\n\t\t\"EDATE(\\\"6/12/2021\\\",-14)\": \"43933\",\n\t\t// EOMONTH\n\t\t\"EOMONTH(\\\"01/01/2021\\\",-1)\":  \"44196\",\n\t\t\"EOMONTH(\\\"01/29/2020\\\",12)\":  \"44227\",\n\t\t\"EOMONTH(\\\"01/12/2021\\\",-18)\": \"43677\",\n\t\t// HOUR\n\t\t\"HOUR(1)\":                    \"0\",\n\t\t\"HOUR(43543.5032060185)\":     \"12\",\n\t\t\"HOUR(\\\"43543.5032060185\\\")\": \"12\",\n\t\t\"HOUR(\\\"13:00:55\\\")\":         \"13\",\n\t\t\"HOUR(\\\"1:00 PM\\\")\":          \"13\",\n\t\t\"HOUR(\\\"12/09/2015 08:55\\\")\": \"8\",\n\t\t// ISOWEEKNUM\n\t\t\"ISOWEEKNUM(42370)\":          \"53\",\n\t\t\"ISOWEEKNUM(\\\"42370\\\")\":      \"53\",\n\t\t\"ISOWEEKNUM(\\\"01/01/2005\\\")\": \"53\",\n\t\t\"ISOWEEKNUM(\\\"02/02/2005\\\")\": \"5\",\n\t\t// MINUTE\n\t\t\"MINUTE(1)\":                    \"0\",\n\t\t\"MINUTE(0.04)\":                 \"57\",\n\t\t\"MINUTE(\\\"0.04\\\")\":             \"57\",\n\t\t\"MINUTE(\\\"13:35:55\\\")\":         \"35\",\n\t\t\"MINUTE(\\\"12/09/2015 08:55\\\")\": \"55\",\n\t\t// MONTH\n\t\t\"MONTH(42171)\":           \"6\",\n\t\t\"MONTH(\\\"31-May-2015\\\")\": \"5\",\n\t\t// YEAR\n\t\t\"YEAR(15)\":              \"1900\",\n\t\t\"YEAR(\\\"15\\\")\":          \"1900\",\n\t\t\"YEAR(2048)\":            \"1905\",\n\t\t\"YEAR(42171)\":           \"2015\",\n\t\t\"YEAR(\\\"29-May-2015\\\")\": \"2015\",\n\t\t\"YEAR(\\\"05/03/1984\\\")\":  \"1984\",\n\t\t// YEARFRAC\n\t\t\"YEARFRAC(42005,42005)\":                      \"0\",\n\t\t\"YEARFRAC(42005,42094)\":                      \"0.25\",\n\t\t\"YEARFRAC(42005,42094,0)\":                    \"0.25\",\n\t\t\"YEARFRAC(42005,42094,1)\":                    \"0.243835616438356\",\n\t\t\"YEARFRAC(42005,42094,2)\":                    \"0.247222222222222\",\n\t\t\"YEARFRAC(42005,42094,3)\":                    \"0.243835616438356\",\n\t\t\"YEARFRAC(42005,42094,4)\":                    \"0.247222222222222\",\n\t\t\"YEARFRAC(\\\"01/01/2015\\\",\\\"03/31/2015\\\")\":    \"0.25\",\n\t\t\"YEARFRAC(\\\"01/01/2015\\\",\\\"03/31/2015\\\",0)\":  \"0.25\",\n\t\t\"YEARFRAC(\\\"01/01/2015\\\",\\\"03/31/2015\\\",1)\":  \"0.243835616438356\",\n\t\t\"YEARFRAC(\\\"01/01/2015\\\",\\\"03/31/2015\\\",2)\":  \"0.247222222222222\",\n\t\t\"YEARFRAC(\\\"01/01/2015\\\",\\\"03/31/2015\\\",3)\":  \"0.243835616438356\",\n\t\t\"YEARFRAC(\\\"01/01/2015\\\",\\\"03/31/2015\\\",4)\":  \"0.247222222222222\",\n\t\t\"YEARFRAC(\\\"01/01/2015\\\",42094)\":             \"0.25\",\n\t\t\"YEARFRAC(42005,\\\"03/31/2015\\\",0)\":           \"0.25\",\n\t\t\"YEARFRAC(\\\"01/31/2015\\\",\\\"03/31/2015\\\")\":    \"0.166666666666667\",\n\t\t\"YEARFRAC(\\\"01/30/2015\\\",\\\"03/31/2015\\\")\":    \"0.166666666666667\",\n\t\t\"YEARFRAC(\\\"02/29/2000\\\", \\\"02/29/2008\\\")\":   \"8\",\n\t\t\"YEARFRAC(\\\"02/29/2000\\\", \\\"02/29/2008\\\",1)\": \"7.99817518248175\",\n\t\t\"YEARFRAC(\\\"02/29/2000\\\", \\\"01/29/2001\\\",1)\": \"0.915300546448087\",\n\t\t\"YEARFRAC(\\\"02/29/2000\\\", \\\"03/29/2000\\\",1)\": \"0.0792349726775956\",\n\t\t\"YEARFRAC(\\\"01/31/2000\\\", \\\"03/29/2000\\\",4)\": \"0.163888888888889\",\n\t\t// SECOND\n\t\t\"SECOND(\\\"13:35:55\\\")\":            \"55\",\n\t\t\"SECOND(\\\"13:10:60\\\")\":            \"0\",\n\t\t\"SECOND(\\\"13:10:61\\\")\":            \"1\",\n\t\t\"SECOND(\\\"08:17:00\\\")\":            \"0\",\n\t\t\"SECOND(\\\"12/09/2015 08:55\\\")\":    \"0\",\n\t\t\"SECOND(\\\"12/09/2011 08:17:23\\\")\": \"23\",\n\t\t\"SECOND(\\\"43543.5032060185\\\")\":    \"37\",\n\t\t\"SECOND(43543.5032060185)\":        \"37\",\n\t\t// TIME\n\t\t\"TIME(5,44,32)\":             \"0.239259259259259\",\n\t\t\"TIME(\\\"5\\\",\\\"44\\\",\\\"32\\\")\": \"0.239259259259259\",\n\t\t\"TIME(0,0,73)\":              \"0.000844907407407407\",\n\t\t// TIMEVALUE\n\t\t\"TIMEVALUE(\\\"2:23\\\")\":             \"0.0993055555555555\",\n\t\t\"TIMEVALUE(\\\"2:23 am\\\")\":          \"0.0993055555555555\",\n\t\t\"TIMEVALUE(\\\"2:23 PM\\\")\":          \"0.599305555555556\",\n\t\t\"TIMEVALUE(\\\"14:23:00\\\")\":         \"0.599305555555556\",\n\t\t\"TIMEVALUE(\\\"00:02:23\\\")\":         \"0.00165509259259259\",\n\t\t\"TIMEVALUE(\\\"01/01/2011 02:23\\\")\": \"0.0993055555555555\",\n\t\t// WEEKDAY\n\t\t\"WEEKDAY(0)\":                 \"7\",\n\t\t\"WEEKDAY(47119)\":             \"2\",\n\t\t\"WEEKDAY(\\\"12/25/2012\\\")\":    \"3\",\n\t\t\"WEEKDAY(\\\"12/25/2012\\\",1)\":  \"3\",\n\t\t\"WEEKDAY(\\\"12/25/2012\\\",2)\":  \"2\",\n\t\t\"WEEKDAY(\\\"12/25/2012\\\",3)\":  \"1\",\n\t\t\"WEEKDAY(\\\"12/25/2012\\\",11)\": \"2\",\n\t\t\"WEEKDAY(\\\"12/25/2012\\\",12)\": \"1\",\n\t\t\"WEEKDAY(\\\"12/25/2012\\\",13)\": \"7\",\n\t\t\"WEEKDAY(\\\"12/25/2012\\\",14)\": \"6\",\n\t\t\"WEEKDAY(\\\"12/25/2012\\\",15)\": \"5\",\n\t\t\"WEEKDAY(\\\"12/25/2012\\\",16)\": \"4\",\n\t\t\"WEEKDAY(\\\"12/25/2012\\\",17)\": \"3\",\n\t\t// WEEKNUM\n\t\t\"WEEKNUM(\\\"01/01/2011\\\")\":    \"1\",\n\t\t\"WEEKNUM(\\\"01/03/2011\\\")\":    \"2\",\n\t\t\"WEEKNUM(\\\"01/13/2008\\\")\":    \"3\",\n\t\t\"WEEKNUM(\\\"01/21/2008\\\")\":    \"4\",\n\t\t\"WEEKNUM(\\\"01/30/2008\\\")\":    \"5\",\n\t\t\"WEEKNUM(\\\"02/04/2008\\\")\":    \"6\",\n\t\t\"WEEKNUM(\\\"01/02/2017\\\",2)\":  \"2\",\n\t\t\"WEEKNUM(\\\"01/02/2017\\\",12)\": \"1\",\n\t\t\"WEEKNUM(\\\"12/31/2017\\\",21)\": \"52\",\n\t\t\"WEEKNUM(\\\"01/01/2017\\\",21)\": \"52\",\n\t\t\"WEEKNUM(\\\"01/01/2021\\\",21)\": \"53\",\n\t\t// Text Functions\n\t\t// ARRAYTOTEXT\n\t\t\"ARRAYTOTEXT(A1:D2)\":   \"1, 4, , Month, 2, 5, , Jan\",\n\t\t\"ARRAYTOTEXT(A1:D2,0)\": \"1, 4, , Month, 2, 5, , Jan\",\n\t\t\"ARRAYTOTEXT(A1:D2,1)\": \"{1,4,,\\\"Month\\\";2,5,,\\\"Jan\\\"}\",\n\t\t// BAHTTEXT\n\t\t\"BAHTTEXT(-1.1)\":    \"\\u0e25\\u0e1a\\u0e2b\\u0e19\\u0e36\\u0e48\\u0e07\\u0e1a\\u0e32\\u0e17\\u0e2a\\u0e34\\u0e1a\\u0e2a\\u0e15\\u0e32\\u0e07\\u0e04\\u0e4c\",\n\t\t\"BAHTTEXT(0)\":       \"\\u0e28\\u0e39\\u0e19\\u0e22\\u0e4c\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(1)\":       \"\\u0e2b\\u0e19\\u0e36\\u0e48\\u0e07\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(1.1)\":     \"\\u0e2b\\u0e19\\u0e36\\u0e48\\u0e07\\u0e1a\\u0e32\\u0e17\\u0e2a\\u0e34\\u0e1a\\u0e2a\\u0e15\\u0e32\\u0e07\\u0e04\\u0e4c\",\n\t\t\"BAHTTEXT(2)\":       \"\\u0e2a\\u0e2d\\u0e07\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(3)\":       \"\\u0e2a\\u0e32\\u0e21\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(4)\":       \"\\u0e2a\\u0e35\\u0e48\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(5)\":       \"\\u0e2b\\u0e49\\u0e32\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(6)\":       \"\\u0e2b\\u0e01\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(7)\":       \"\\u0e40\\u0e08\\u0e47\\u0e14\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(8)\":       \"\\u0e41\\u0e1b\\u0e14\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(9)\":       \"\\u0e40\\u0e01\\u0e49\\u0e32\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(10)\":      \"\\u0e2a\\u0e34\\u0e1a\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(11)\":      \"\\u0e2a\\u0e34\\u0e1a\\u0e40\\u0e2d\\u0e47\\u0e14\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(15)\":      \"\\u0e2a\\u0e34\\u0e1a\\u0e2b\\u0e49\\u0e32\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(20)\":      \"\\u0e22\\u0e35\\u0e48\\u0e2a\\u0e34\\u0e1a\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(100)\":     \"\\u0e2b\\u0e19\\u0e36\\u0e48\\u0e07\\u0e23\\u0e49\\u0e2d\\u0e22\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(130)\":     \"\\u0e2b\\u0e19\\u0e36\\u0e48\\u0e07\\u0e23\\u0e49\\u0e2d\\u0e22\\u0e2a\\u0e32\\u0e21\\u0e2a\\u0e34\\u0e1a\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(1000)\":    \"\\u0e2b\\u0e19\\u0e36\\u0e48\\u0e07\\u0e1e\\u0e31\\u0e19\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(10000)\":   \"\\u0e2b\\u0e19\\u0e36\\u0e48\\u0e07\\u0e2b\\u0e21\\u0e37\\u0e48\\u0e19\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(100000)\":  \"\\u0e2b\\u0e19\\u0e36\\u0e48\\u0e07\\u0e41\\u0e2a\\u0e19\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t\"BAHTTEXT(1000000)\": \"\\u0e2b\\u0e19\\u0e36\\u0e48\\u0e07\\u0e25\\u0e49\\u0e32\\u0e19\\u0e1a\\u0e32\\u0e17\\u0e16\\u0e49\\u0e27\\u0e19\",\n\t\t// CHAR\n\t\t\"CHAR(65)\": \"A\",\n\t\t\"CHAR(97)\": \"a\",\n\t\t\"CHAR(63)\": \"?\",\n\t\t\"CHAR(51)\": \"3\",\n\t\t// CLEAN\n\t\t\"CLEAN(\\\"\\u0009clean text\\\")\": \"clean text\",\n\t\t\"CLEAN(0)\":                    \"0\",\n\t\t// CODE\n\t\t\"CODE(\\\"Alpha\\\")\": \"65\",\n\t\t\"CODE(\\\"alpha\\\")\": \"97\",\n\t\t\"CODE(\\\"?\\\")\":     \"63\",\n\t\t\"CODE(\\\"3\\\")\":     \"51\",\n\t\t\"CODE(\\\"\\\")\":      \"0\",\n\t\t// CONCAT\n\t\t\"CONCAT(TRUE(),1,FALSE(),\\\"0\\\",INT(2))\": \"TRUE1FALSE02\",\n\t\t\"CONCAT(MUNIT(2))\":                      \"1001\",\n\t\t\"CONCAT(A1:B2)\":                         \"1425\",\n\t\t// CONCATENATE\n\t\t\"CONCATENATE(TRUE(),1,FALSE(),\\\"0\\\",INT(2))\": \"TRUE1FALSE02\",\n\t\t\"CONCATENATE(MUNIT(2))\":                      \"1001\",\n\t\t\"CONCATENATE(A1:B2)\":                         \"1425\",\n\t\t// DBCS\n\t\t\"DBCS(\\\"\\\")\":        \"\",\n\t\t\"DBCS(123.456)\":     \"123.456\",\n\t\t\"DBCS(\\\"123.456\\\")\": \"123.456\",\n\t\t// EXACT\n\t\t\"EXACT(1,\\\"1\\\")\":     \"TRUE\",\n\t\t\"EXACT(1,1)\":         \"TRUE\",\n\t\t\"EXACT(\\\"A\\\",\\\"a\\\")\": \"FALSE\",\n\t\t// FIXED\n\t\t\"FIXED(5123.591)\":         \"5,123.591\",\n\t\t\"FIXED(5123.591,1)\":       \"5,123.6\",\n\t\t\"FIXED(5123.591,0)\":       \"5,124\",\n\t\t\"FIXED(5123.591,-1)\":      \"5,120\",\n\t\t\"FIXED(5123.591,-2)\":      \"5,100\",\n\t\t\"FIXED(5123.591,-3,TRUE)\": \"5000\",\n\t\t\"FIXED(5123.591,-5)\":      \"0\",\n\t\t\"FIXED(-77262.23973,-5)\":  \"-100,000\",\n\t\t// FIND\n\t\t\"FIND(\\\"T\\\",\\\"Original Text\\\")\":   \"10\",\n\t\t\"FIND(\\\"t\\\",\\\"Original Text\\\")\":   \"13\",\n\t\t\"FIND(\\\"i\\\",\\\"Original Text\\\")\":   \"3\",\n\t\t\"FIND(\\\"i\\\",\\\"Original Text\\\",4)\": \"5\",\n\t\t\"FIND(\\\"\\\",\\\"Original Text\\\")\":    \"1\",\n\t\t\"FIND(\\\"\\\",\\\"Original Text\\\",2)\":  \"2\",\n\t\t\"FIND(\\\"s\\\",\\\"Sales\\\",2)\":         \"5\",\n\t\t\"FIND(D1:E2,\\\"Month\\\")\":           \"1\",\n\t\t// FINDB\n\t\t\"FINDB(\\\"T\\\",\\\"Original Text\\\")\":   \"10\",\n\t\t\"FINDB(\\\"t\\\",\\\"Original Text\\\")\":   \"13\",\n\t\t\"FINDB(\\\"i\\\",\\\"Original Text\\\")\":   \"3\",\n\t\t\"FINDB(\\\"i\\\",\\\"Original Text\\\",4)\": \"5\",\n\t\t\"FINDB(\\\"\\\",\\\"Original Text\\\")\":    \"1\",\n\t\t\"FINDB(\\\"\\\",\\\"Original Text\\\",2)\":  \"2\",\n\t\t\"FINDB(\\\"s\\\",\\\"Sales\\\",2)\":         \"5\",\n\t\t// LEFT\n\t\t\"LEFT(\\\"Original Text\\\")\":    \"O\",\n\t\t\"LEFT(\\\"Original Text\\\",4)\":  \"Orig\",\n\t\t\"LEFT(\\\"Original Text\\\",0)\":  \"\",\n\t\t\"LEFT(\\\"Original Text\\\",13)\": \"Original Text\",\n\t\t\"LEFT(\\\"Original Text\\\",20)\": \"Original Text\",\n\t\t\"LEFT(\\\"オリジナルテキスト\\\")\":        \"オ\",\n\t\t\"LEFT(\\\"オリジナルテキスト\\\",2)\":      \"オリ\",\n\t\t\"LEFT(\\\"オリジナルテキスト\\\",5)\":      \"オリジナル\",\n\t\t\"LEFT(\\\"オリジナルテキスト\\\",7)\":      \"オリジナルテキ\",\n\t\t\"LEFT(\\\"オリジナルテキスト\\\",20)\":     \"オリジナルテキスト\",\n\t\t// LEFTB\n\t\t\"LEFTB(\\\"Original Text\\\")\":    \"O\",\n\t\t\"LEFTB(\\\"Original Text\\\",4)\":  \"Orig\",\n\t\t\"LEFTB(\\\"Original Text\\\",0)\":  \"\",\n\t\t\"LEFTB(\\\"Original Text\\\",13)\": \"Original Text\",\n\t\t\"LEFTB(\\\"Original Text\\\",20)\": \"Original Text\",\n\t\t// LEN\n\t\t\"LEN(\\\"\\\")\":              \"0\",\n\t\t\"LEN(D1)\":                \"5\",\n\t\t\"LEN(\\\"テキスト\\\")\":          \"4\",\n\t\t\"LEN(\\\"オリジナルテキスト\\\")\":     \"9\",\n\t\t\"LEN(7+LEN(A1&B1&C1))\":   \"1\",\n\t\t\"LEN(8+LEN(A1+(C1-B1)))\": \"2\",\n\t\t// LENB\n\t\t\"LENB(\\\"\\\")\":          \"0\",\n\t\t\"LENB(D1)\":            \"5\",\n\t\t\"LENB(\\\"テキスト\\\")\":      \"8\",\n\t\t\"LENB(\\\"オリジナルテキスト\\\")\": \"18\",\n\t\t// LOWER\n\t\t\"LOWER(\\\"test\\\")\":     \"test\",\n\t\t\"LOWER(\\\"TEST\\\")\":     \"test\",\n\t\t\"LOWER(\\\"Test\\\")\":     \"test\",\n\t\t\"LOWER(\\\"TEST 123\\\")\": \"test 123\",\n\t\t// MID\n\t\t\"MID(\\\"Original Text\\\",7,1)\": \"a\",\n\t\t\"MID(\\\"Original Text\\\",4,7)\": \"ginal T\",\n\t\t\"MID(\\\"255 years\\\",3,1)\":     \"5\",\n\t\t\"MID(\\\"text\\\",3,6)\":          \"xt\",\n\t\t\"MID(\\\"text\\\",6,0)\":          \"\",\n\t\t\"MID(\\\"你好World\\\",5,1)\":       \"r\",\n\t\t\"MID(\\\"\\u30AA\\u30EA\\u30B8\\u30CA\\u30EB\\u30C6\\u30AD\\u30B9\\u30C8\\\",6,4)\": \"\\u30C6\\u30AD\\u30B9\\u30C8\",\n\t\t\"MID(\\\"\\u30AA\\u30EA\\u30B8\\u30CA\\u30EB\\u30C6\\u30AD\\u30B9\\u30C8\\\",3,5)\": \"\\u30B8\\u30CA\\u30EB\\u30C6\\u30AD\",\n\t\t// MIDB\n\t\t\"MIDB(\\\"Original Text\\\",7,1)\": \"a\",\n\t\t\"MIDB(\\\"Original Text\\\",4,7)\": \"ginal T\",\n\t\t\"MIDB(\\\"255 years\\\",3,1)\":     \"5\",\n\t\t\"MIDB(\\\"text\\\",3,6)\":          \"xt\",\n\t\t\"MIDB(\\\"text\\\",6,0)\":          \"\",\n\t\t\"MIDB(\\\"你好World\\\",5,1)\":       \"W\",\n\t\t\"MIDB(\\\"\\u30AA\\u30EA\\u30B8\\u30CA\\u30EB\\u30C6\\u30AD\\u30B9\\u30C8\\\",6,4)\": \"\\u30B8\\u30CA\",\n\t\t\"MIDB(\\\"\\u30AA\\u30EA\\u30B8\\u30CA\\u30EB\\u30C6\\u30AD\\u30B9\\u30C8\\\",3,5)\": \"\\u30EA\\u30B8\\xe3\",\n\t\t// PROPER\n\t\t\"PROPER(\\\"this is a test sentence\\\")\": \"This Is A Test Sentence\",\n\t\t\"PROPER(\\\"THIS IS A TEST SENTENCE\\\")\": \"This Is A Test Sentence\",\n\t\t\"PROPER(\\\"123tEST teXT\\\")\":            \"123Test Text\",\n\t\t\"PROPER(\\\"Mr. SMITH's address\\\")\":     \"Mr. Smith'S Address\",\n\t\t// REPLACE\n\t\t\"REPLACE(\\\"test string\\\",7,3,\\\"X\\\")\":          \"test sXng\",\n\t\t\"REPLACE(\\\"second test string\\\",8,4,\\\"XXX\\\")\": \"second XXX string\",\n\t\t\"REPLACE(\\\"text\\\",5,0,\\\" and char\\\")\":         \"text and char\",\n\t\t\"REPLACE(\\\"text\\\",1,20,\\\"char and \\\")\":        \"char and \",\n\t\t// REPLACEB\n\t\t\"REPLACEB(\\\"test string\\\",7,3,\\\"X\\\")\":          \"test sXng\",\n\t\t\"REPLACEB(\\\"second test string\\\",8,4,\\\"XXX\\\")\": \"second XXX string\",\n\t\t\"REPLACEB(\\\"text\\\",5,0,\\\" and char\\\")\":         \"text and char\",\n\t\t\"REPLACEB(\\\"text\\\",1,20,\\\"char and \\\")\":        \"char and \",\n\t\t// REPT\n\t\t\"REPT(\\\"*\\\",0)\":  \"\",\n\t\t\"REPT(\\\"*\\\",1)\":  \"*\",\n\t\t\"REPT(\\\"**\\\",2)\": \"****\",\n\t\t// RIGHT\n\t\t\"RIGHT(\\\"Original Text\\\")\":    \"t\",\n\t\t\"RIGHT(\\\"Original Text\\\",4)\":  \"Text\",\n\t\t\"RIGHT(\\\"Original Text\\\",0)\":  \"\",\n\t\t\"RIGHT(\\\"Original Text\\\",13)\": \"Original Text\",\n\t\t\"RIGHT(\\\"Original Text\\\",20)\": \"Original Text\",\n\t\t\"RIGHT(\\\"オリジナルテキスト\\\")\":        \"ト\",\n\t\t\"RIGHT(\\\"オリジナルテキスト\\\",2)\":      \"スト\",\n\t\t\"RIGHT(\\\"オリジナルテキスト\\\",4)\":      \"テキスト\",\n\t\t\"RIGHT(\\\"オリジナルテキスト\\\",7)\":      \"ジナルテキスト\",\n\t\t\"RIGHT(\\\"オリジナルテキスト\\\",20)\":     \"オリジナルテキスト\",\n\t\t// RIGHTB\n\t\t\"RIGHTB(\\\"Original Text\\\")\":    \"t\",\n\t\t\"RIGHTB(\\\"Original Text\\\",4)\":  \"Text\",\n\t\t\"RIGHTB(\\\"Original Text\\\",0)\":  \"\",\n\t\t\"RIGHTB(\\\"Original Text\\\",13)\": \"Original Text\",\n\t\t\"RIGHTB(\\\"Original Text\\\",20)\": \"Original Text\",\n\t\t// SUBSTITUTE\n\t\t\"SUBSTITUTE(\\\"abab\\\",\\\"a\\\",\\\"X\\\")\":                      \"XbXb\",\n\t\t\"SUBSTITUTE(\\\"abab\\\",\\\"a\\\",\\\"X\\\",2)\":                    \"abXb\",\n\t\t\"SUBSTITUTE(\\\"abab\\\",\\\"x\\\",\\\"X\\\",2)\":                    \"abab\",\n\t\t\"SUBSTITUTE(\\\"John is 5 years old\\\",\\\"John\\\",\\\"Jack\\\")\": \"Jack is 5 years old\",\n\t\t\"SUBSTITUTE(\\\"John is 5 years old\\\",\\\"5\\\",\\\"6\\\")\":       \"John is 6 years old\",\n\t\t// TEXT\n\t\t\"TEXT(\\\"07/07/2015\\\",\\\"mm/dd/yyyy\\\")\":        \"07/07/2015\",\n\t\t\"TEXT(42192,\\\"mm/dd/yyyy\\\")\":                 \"07/07/2015\",\n\t\t\"TEXT(42192,\\\"mmm dd yyyy\\\")\":                \"Jul 07 2015\",\n\t\t\"TEXT(0.75,\\\"hh:mm\\\")\":                       \"18:00\",\n\t\t\"TEXT(36.363636,\\\"0.00\\\")\":                   \"36.36\",\n\t\t\"TEXT(567.9,\\\"$#,##0.00\\\")\":                  \"$567.90\",\n\t\t\"TEXT(-5,\\\"+ $#,##0.00;- $#,##0.00;$0.00\\\")\": \"- $5.00\",\n\t\t\"TEXT(5,\\\"+ $#,##0.00;- $#,##0.00;$0.00\\\")\":  \"+ $5.00\",\n\t\t// TEXTAFTER\n\t\t\"TEXTAFTER(\\\"Red riding hood's, red hood\\\",\\\"hood\\\")\":               \"'s, red hood\",\n\t\t\"TEXTAFTER(\\\"Red riding hood's, red hood\\\",\\\"HOOD\\\",1,1)\":           \"'s, red hood\",\n\t\t\"TEXTAFTER(\\\"Red riding hood's, red hood\\\",\\\"basket\\\",1,0,0,\\\"x\\\")\": \"x\",\n\t\t\"TEXTAFTER(\\\"Red riding hood's, red hood\\\",\\\"basket\\\",1,0,1,\\\"x\\\")\": \"\",\n\t\t\"TEXTAFTER(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",-1)\":            \"\",\n\t\t\"TEXTAFTER(\\\"Jones,Bob\\\",\\\",\\\")\":                                    \"Bob\",\n\t\t\"TEXTAFTER(\\\"12 ft x 20 ft\\\",\\\" x \\\")\":                              \"20 ft\",\n\t\t\"TEXTAFTER(\\\"ABX-112-Red-Y\\\",\\\"-\\\",1)\":                              \"112-Red-Y\",\n\t\t\"TEXTAFTER(\\\"ABX-112-Red-Y\\\",\\\"-\\\",2)\":                              \"Red-Y\",\n\t\t\"TEXTAFTER(\\\"ABX-112-Red-Y\\\",\\\"-\\\",-1)\":                             \"Y\",\n\t\t\"TEXTAFTER(\\\"ABX-112-Red-Y\\\",\\\"-\\\",-2)\":                             \"Red-Y\",\n\t\t\"TEXTAFTER(\\\"ABX-112-Red-Y\\\",\\\"-\\\",-3)\":                             \"112-Red-Y\",\n\t\t\"TEXTAFTER(\\\"ABX-123-Red-XYZ\\\",\\\"-\\\",-4,0,1)\":                       \"ABX-123-Red-XYZ\",\n\t\t\"TEXTAFTER(\\\"ABX-123-Red-XYZ\\\",\\\"A\\\")\":                              \"BX-123-Red-XYZ\",\n\t\t// TEXTBEFORE\n\t\t\"TEXTBEFORE(\\\"Red riding hood's, red hood\\\",\\\"hood\\\")\":               \"Red riding \",\n\t\t\"TEXTBEFORE(\\\"Red riding hood's, red hood\\\",\\\"HOOD\\\",1,1)\":           \"Red riding \",\n\t\t\"TEXTBEFORE(\\\"Red riding hood's, red hood\\\",\\\"basket\\\",1,0,0,\\\"x\\\")\": \"x\",\n\t\t\"TEXTBEFORE(\\\"Red riding hood's, red hood\\\",\\\"basket\\\",1,0,1,\\\"x\\\")\": \"Red riding hood's, red hood\",\n\t\t\"TEXTBEFORE(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",-1)\":            \"Red riding hood's, red \",\n\t\t\"TEXTBEFORE(\\\"Jones,Bob\\\",\\\",\\\")\":                                    \"Jones\",\n\t\t\"TEXTBEFORE(\\\"12 ft x 20 ft\\\",\\\" x \\\")\":                              \"12 ft\",\n\t\t\"TEXTBEFORE(\\\"ABX-112-Red-Y\\\",\\\"-\\\",1)\":                              \"ABX\",\n\t\t\"TEXTBEFORE(\\\"ABX-112-Red-Y\\\",\\\"-\\\",2)\":                              \"ABX-112\",\n\t\t\"TEXTBEFORE(\\\"ABX-112-Red-Y\\\",\\\"-\\\",-1)\":                             \"ABX-112-Red\",\n\t\t\"TEXTBEFORE(\\\"ABX-112-Red-Y\\\",\\\"-\\\",-2)\":                             \"ABX-112\",\n\t\t\"TEXTBEFORE(\\\"ABX-123-Red-XYZ\\\",\\\"-\\\",4,0,1)\":                        \"ABX-123-Red-XYZ\",\n\t\t\"TEXTBEFORE(\\\"ABX-112-Red-Y\\\",\\\"A\\\")\":                                \"\",\n\t\t// TEXTJOIN\n\t\t\"TEXTJOIN(\\\"-\\\",TRUE,1,2,3,4)\":  \"1-2-3-4\",\n\t\t\"TEXTJOIN(A4,TRUE,A1:B2)\":       \"1040205\",\n\t\t\"TEXTJOIN(\\\",\\\",FALSE,A1:C2)\":   \"1,4,,2,5,\",\n\t\t\"TEXTJOIN(\\\",\\\",TRUE,A1:C2)\":    \"1,4,2,5\",\n\t\t\"TEXTJOIN(\\\",\\\",TRUE,MUNIT(2))\": \"1,0,0,1\",\n\t\t// TRIM\n\t\t\"TRIM(\\\" trim text \\\")\": \"trim text\",\n\t\t\"TRIM(0)\":               \"0\",\n\t\t// UNICHAR\n\t\t\"UNICHAR(65)\": \"A\",\n\t\t\"UNICHAR(97)\": \"a\",\n\t\t\"UNICHAR(63)\": \"?\",\n\t\t\"UNICHAR(51)\": \"3\",\n\t\t// UNICODE\n\t\t\"UNICODE(\\\"Alpha\\\")\": \"65\",\n\t\t\"UNICODE(\\\"alpha\\\")\": \"97\",\n\t\t\"UNICODE(\\\"?\\\")\":     \"63\",\n\t\t\"UNICODE(\\\"3\\\")\":     \"51\",\n\t\t// UNIQUE\n\t\t\"TEXTJOIN(\\\",\\\", TRUE, UNIQUE(D2:D9))\":               \"Jan,Feb\",\n\t\t\"TEXTJOIN(\\\",\\\", TRUE, UNIQUE(D2:D9, FALSE, FALSE))\": \"Jan,Feb\",\n\t\t\"TEXTJOIN(\\\",\\\", TRUE, UNIQUE(E2:E9, FALSE, FALSE))\": \"North 1,North 2,South 1,South 2\",\n\t\t\"TEXTJOIN(\\\",\\\", TRUE, UNIQUE(D2:D9, FALSE, TRUE))\":  \"\",\n\t\t// UPPER\n\t\t\"UPPER(\\\"test\\\")\":     \"TEST\",\n\t\t\"UPPER(\\\"TEST\\\")\":     \"TEST\",\n\t\t\"UPPER(\\\"Test\\\")\":     \"TEST\",\n\t\t\"UPPER(\\\"TEST 123\\\")\": \"TEST 123\",\n\t\t// VALUE\n\t\t\"VALUE(\\\"50\\\")\":                  \"50\",\n\t\t\"VALUE(\\\"1.0E-07\\\")\":             \"0.0000001\",\n\t\t\"VALUE(\\\"5,000\\\")\":               \"5000\",\n\t\t\"VALUE(\\\"20%\\\")\":                 \"0.2\",\n\t\t\"VALUE(\\\"12:00:00\\\")\":            \"0.5\",\n\t\t\"VALUE(\\\"01/02/2006 15:04:05\\\")\": \"38719.6278356481\",\n\t\t// VALUETOTEXT\n\t\t\"VALUETOTEXT(A1)\":   \"1\",\n\t\t\"VALUETOTEXT(A1,0)\": \"1\",\n\t\t\"VALUETOTEXT(A1,1)\": \"1\",\n\t\t\"VALUETOTEXT(D1)\":   \"Month\",\n\t\t\"VALUETOTEXT(D1,0)\": \"Month\",\n\t\t\"VALUETOTEXT(D1,1)\": \"\\\"Month\\\"\",\n\t\t// Conditional Functions\n\t\t// IF\n\t\t\"IF(1=1)\":                                   \"TRUE\",\n\t\t\"IF(1<>1)\":                                  \"FALSE\",\n\t\t\"IF(5<0, \\\"negative\\\", \\\"positive\\\")\":       \"positive\",\n\t\t\"IF(-2<0, \\\"negative\\\", \\\"positive\\\")\":      \"negative\",\n\t\t\"IF(1=1, \\\"equal\\\", \\\"notequal\\\")\":          \"equal\",\n\t\t\"IF(1<>1, \\\"equal\\\", \\\"notequal\\\")\":         \"notequal\",\n\t\t\"IF(\\\"A\\\"=\\\"A\\\", \\\"equal\\\", \\\"notequal\\\")\":  \"equal\",\n\t\t\"IF(\\\"A\\\"<>\\\"A\\\", \\\"equal\\\", \\\"notequal\\\")\": \"notequal\",\n\t\t\"IF(FALSE,0,ROUND(4/2,0))\":                  \"2\",\n\t\t\"IF(TRUE,ROUND(4/2,0),0)\":                   \"2\",\n\t\t\"IF(A4>0.4,\\\"TRUE\\\",\\\"FALSE\\\")\":             \"FALSE\",\n\t\t// Excel Lookup and Reference Functions\n\t\t// ADDRESS\n\t\t\"ADDRESS(1,1,1,TRUE)\":            \"$A$1\",\n\t\t\"ADDRESS(1,2,1,TRUE)\":            \"$B$1\",\n\t\t\"ADDRESS(1,1,1,FALSE)\":           \"R1C1\",\n\t\t\"ADDRESS(1,2,1,FALSE)\":           \"R1C2\",\n\t\t\"ADDRESS(1,1,2,TRUE)\":            \"A$1\",\n\t\t\"ADDRESS(1,2,2,TRUE)\":            \"B$1\",\n\t\t\"ADDRESS(1,1,2,FALSE)\":           \"R1C[1]\",\n\t\t\"ADDRESS(1,2,2,FALSE)\":           \"R1C[2]\",\n\t\t\"ADDRESS(1,1,3,TRUE)\":            \"$A1\",\n\t\t\"ADDRESS(1,2,3,TRUE)\":            \"$B1\",\n\t\t\"ADDRESS(1,1,3,FALSE)\":           \"R[1]C1\",\n\t\t\"ADDRESS(1,2,3,FALSE)\":           \"R[1]C2\",\n\t\t\"ADDRESS(1,1,4,TRUE)\":            \"A1\",\n\t\t\"ADDRESS(1,2,4,TRUE)\":            \"B1\",\n\t\t\"ADDRESS(1,1,4,FALSE)\":           \"R[1]C[1]\",\n\t\t\"ADDRESS(1,2,4,FALSE)\":           \"R[1]C[2]\",\n\t\t\"ADDRESS(1,1,4,TRUE,\\\"\\\")\":       \"!A1\",\n\t\t\"ADDRESS(1,2,4,TRUE,\\\"\\\")\":       \"!B1\",\n\t\t\"ADDRESS(1,1,4,TRUE,\\\"Sheet1\\\")\": \"Sheet1!A1\",\n\t\t// CHOOSE\n\t\t\"CHOOSE(4,\\\"red\\\",\\\"blue\\\",\\\"green\\\",\\\"brown\\\")\": \"brown\",\n\t\t\"CHOOSE(1,\\\"red\\\",\\\"blue\\\",\\\"green\\\",\\\"brown\\\")\": \"red\",\n\t\t\"SUM(CHOOSE(A2,A1,B1:B2,A1:A3,A1:A4))\":           \"9\",\n\t\t// COLUMN\n\t\t\"COLUMN()\":                \"3\",\n\t\t\"COLUMN(Sheet1!A1)\":       \"1\",\n\t\t\"COLUMN(Sheet1!A1:B1:C1)\": \"1\",\n\t\t\"COLUMN(Sheet1!F1:G1)\":    \"6\",\n\t\t\"COLUMN(H1)\":              \"8\",\n\t\t// COLUMNS\n\t\t\"COLUMNS(B1)\":                   \"1\",\n\t\t\"COLUMNS(1:1)\":                  \"16384\",\n\t\t\"COLUMNS(Sheet1!1:1)\":           \"16384\",\n\t\t\"COLUMNS(B1:E5)\":                \"4\",\n\t\t\"COLUMNS(Sheet1!E5:H7:B1)\":      \"7\",\n\t\t\"COLUMNS(E5:H7:B1:C1:Z1:C1:B1)\": \"25\",\n\t\t\"COLUMNS(E5:B1)\":                \"4\",\n\t\t\"COLUMNS(EM38:HZ81)\":            \"92\",\n\t\t// HLOOKUP\n\t\t\"HLOOKUP(D2,D2:D8,1,FALSE)\":          \"Jan\",\n\t\t\"HLOOKUP(F3,F3:F8,3,FALSE)\":          \"34440\",\n\t\t\"HLOOKUP(INT(F3),F3:F8,3,FALSE)\":     \"34440\",\n\t\t\"HLOOKUP(MUNIT(1),MUNIT(1),1,FALSE)\": \"1\",\n\t\t// HYPERLINK\n\t\t\"HYPERLINK(\\\"https://github.com/xuri/excelize\\\")\":              \"https://github.com/xuri/excelize\",\n\t\t\"HYPERLINK(\\\"https://github.com/xuri/excelize\\\",\\\"Excelize\\\")\": \"Excelize\",\n\t\t// VLOOKUP\n\t\t\"VLOOKUP(D2,D:D,1,FALSE)\":            \"Jan\",\n\t\t\"VLOOKUP(D2,D1:D10,1)\":               \"Jan\",\n\t\t\"VLOOKUP(D2,D1:D11,1)\":               \"Feb\",\n\t\t\"VLOOKUP(D2,D1:D10,1,FALSE)\":         \"Jan\",\n\t\t\"VLOOKUP(INT(36693),F2:F2,1,FALSE)\":  \"36693\",\n\t\t\"VLOOKUP(INT(F2),F3:F9,1)\":           \"32080\",\n\t\t\"VLOOKUP(INT(F2),F3:F9,1,TRUE)\":      \"32080\",\n\t\t\"VLOOKUP(MUNIT(3),MUNIT(3),1)\":       \"0\",\n\t\t\"VLOOKUP(A1,A3:B5,1)\":                \"0\",\n\t\t\"VLOOKUP(A1:A2,A1:A1,1)\":             \"1\",\n\t\t\"VLOOKUP(MUNIT(1),MUNIT(1),1,FALSE)\": \"1\",\n\t\t// INDEX\n\t\t\"INDEX(0,0,0)\":          \"0\",\n\t\t\"INDEX(A1,0,0)\":         \"1\",\n\t\t\"INDEX(A1:A1,0,0)\":      \"1\",\n\t\t\"SUM(INDEX(A1:B1,1))\":   \"5\",\n\t\t\"SUM(INDEX(A1:B1,1,0))\": \"5\",\n\t\t\"SUM(INDEX(A1:B2,2,0))\": \"7\",\n\t\t\"SUM(INDEX(A1:B4,0,2))\": \"9\",\n\t\t\"SUM(INDEX(E1:F5,5,2))\": \"34440\",\n\t\t// INDIRECT\n\t\t\"INDIRECT(\\\"E1\\\")\":                   \"Team\",\n\t\t\"INDIRECT(\\\"E\\\"&1)\":                  \"Team\",\n\t\t\"INDIRECT(\\\"E\\\"&ROW())\":              \"Team\",\n\t\t\"INDIRECT(\\\"E\\\"&ROW(),TRUE)\":         \"Team\",\n\t\t\"INDIRECT(\\\"R1C5\\\",FALSE)\":           \"Team\",\n\t\t\"INDIRECT(\\\"R\\\"&1&\\\"C\\\"&5,FALSE)\":    \"Team\",\n\t\t\"SUM(INDIRECT(\\\"A1:B2\\\"))\":           \"12\",\n\t\t\"SUM(INDIRECT(\\\"A1:B2\\\",TRUE))\":      \"12\",\n\t\t\"SUM(INDIRECT(\\\"R1C1:R2C2\\\",FALSE))\": \"12\",\n\t\t// LOOKUP\n\t\t\"LOOKUP(F8,F8:F9,F8:F9)\":      \"32080\",\n\t\t\"LOOKUP(F8,F8:F9,D8:D9)\":      \"Feb\",\n\t\t\"LOOKUP(E3,E2:E5,F2:F5)\":      \"22100\",\n\t\t\"LOOKUP(E3,E2:F5)\":            \"22100\",\n\t\t\"LOOKUP(F3+1,F3:F4,F3:F4)\":    \"22100\",\n\t\t\"LOOKUP(F4+1,F3:F4,F3:F4)\":    \"53321\",\n\t\t\"LOOKUP(1,MUNIT(1))\":          \"1\",\n\t\t\"LOOKUP(1,MUNIT(1),MUNIT(1))\": \"1\",\n\t\t// ROW\n\t\t\"ROW()\":                \"1\",\n\t\t\"ROW(Sheet1!A1)\":       \"1\",\n\t\t\"ROW(Sheet1!A1:B2:C3)\": \"1\",\n\t\t\"ROW(Sheet1!F5:G6)\":    \"5\",\n\t\t\"ROW(A8)\":              \"8\",\n\t\t// ROWS\n\t\t\"ROWS(B1)\":                    \"1\",\n\t\t\"ROWS(B:B)\":                   \"1048576\",\n\t\t\"ROWS(Sheet1!B:B)\":            \"1048576\",\n\t\t\"ROWS(B1:E5)\":                 \"5\",\n\t\t\"ROWS(Sheet1!E5:H7:B1)\":       \"7\",\n\t\t\"ROWS(E5:H8:B2:C3:Z26:C3:B2)\": \"25\",\n\t\t\"ROWS(E5:B1)\":                 \"5\",\n\t\t\"ROWS(EM38:HZ81)\":             \"44\",\n\t\t// Web Functions\n\t\t// ENCODEURL\n\t\t\"ENCODEURL(\\\"https://xuri.me/excelize/en/?q=Save As\\\")\": \"https%3A%2F%2Fxuri.me%2Fexcelize%2Fen%2F%3Fq%3DSave%20As\",\n\t\t// Financial Functions\n\t\t// ACCRINT\n\t\t\"ACCRINT(\\\"01/01/2012\\\",\\\"04/01/2012\\\",\\\"12/31/2013\\\",8%,10000,4,0,TRUE)\":  \"1600\",\n\t\t\"ACCRINT(\\\"01/01/2012\\\",\\\"04/01/2012\\\",\\\"12/31/2013\\\",8%,10000,4,0,FALSE)\": \"1600\",\n\t\t// ACCRINTM\n\t\t\"ACCRINTM(\\\"01/01/2012\\\",\\\"12/31/2012\\\",8%,10000)\":   \"800\",\n\t\t\"ACCRINTM(\\\"01/01/2012\\\",\\\"12/31/2012\\\",8%,10000,3)\": \"800\",\n\t\t// AMORDEGRC\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,20%)\":    \"42\",\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,20%,4)\":  \"42\",\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,40%,4)\":  \"42\",\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,25%,4)\":  \"41\",\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",109,1,25%,4)\": \"54\",\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",110,2,25%,4)\": \"0\",\n\t\t// AMORLINC\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,20%,4)\":  \"30\",\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,0%,4)\":   \"0\",\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,20,15%,4)\": \"0\",\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,6,15%,4)\":  \"0.6875\",\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,0,15%,4)\":  \"16.8125\",\n\t\t// COUPDAYBS\n\t\t\"COUPDAYBS(\\\"02/24/2000\\\",\\\"11/24/2000\\\",4,4)\": \"0\",\n\t\t\"COUPDAYBS(\\\"03/27/2000\\\",\\\"11/29/2000\\\",4,4)\": \"28\",\n\t\t\"COUPDAYBS(\\\"02/29/2000\\\",\\\"04/01/2000\\\",4,4)\": \"58\",\n\t\t\"COUPDAYBS(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4)\":   \"66\",\n\t\t\"COUPDAYBS(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,1)\": \"68\",\n\t\t\"COUPDAYBS(\\\"10/31/2011\\\",\\\"02/26/2012\\\",4,0)\": \"65\",\n\t\t// COUPDAYS\n\t\t\"COUPDAYS(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4)\":   \"90\",\n\t\t\"COUPDAYS(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,1)\": \"92\",\n\t\t// COUPDAYSNC\n\t\t\"COUPDAYSNC(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4)\": \"24\",\n\t\t\"COUPDAYSNC(\\\"04/01/2012\\\",\\\"03/31/2020\\\",2)\": \"179\",\n\t\t// COUPNCD\n\t\t\"COUPNCD(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4)\":   \"40568\",\n\t\t\"COUPNCD(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,0)\": \"40568\",\n\t\t\"COUPNCD(\\\"10/25/2011\\\",\\\"01/01/2012\\\",4)\":   \"40909\",\n\t\t\"COUPNCD(\\\"04/01/2012\\\",\\\"03/31/2020\\\",2)\":   \"41182\",\n\t\t\"COUPNCD(\\\"01/01/2000\\\",\\\"08/30/2001\\\",2)\":   \"36585\",\n\t\t// COUPNUM\n\t\t\"COUPNUM(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4)\":   \"8\",\n\t\t\"COUPNUM(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,0)\": \"8\",\n\t\t\"COUPNUM(\\\"09/30/2017\\\",\\\"03/31/2021\\\",4,0)\": \"14\",\n\t\t// COUPPCD\n\t\t\"COUPPCD(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4)\":   \"40476\",\n\t\t\"COUPPCD(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,0)\": \"40476\",\n\t\t\"COUPPCD(\\\"10/25/2011\\\",\\\"01/01/2012\\\",4)\":   \"40817\",\n\t\t// CUMIPMT\n\t\t\"CUMIPMT(0.05/12,60,50000,1,12,0)\":  \"-2294.97753732664\",\n\t\t\"CUMIPMT(0.05/12,60,50000,13,24,0)\": \"-1833.10006657389\",\n\t\t// CUMPRINC\n\t\t\"CUMPRINC(0.05/12,60,50000,1,12,0)\":  \"-9027.76264907988\",\n\t\t\"CUMPRINC(0.05/12,60,50000,13,24,0)\": \"-9489.64011983263\",\n\t\t// DB\n\t\t\"DB(0,1000,5,1)\":       \"0\",\n\t\t\"DB(10000,1000,5,1)\":   \"3690\",\n\t\t\"DB(10000,1000,5,2)\":   \"2328.39\",\n\t\t\"DB(10000,1000,5,1,6)\": \"1845\",\n\t\t\"DB(10000,1000,5,6,6)\": \"238.527124587882\",\n\t\t// DDB\n\t\t\"DDB(0,1000,5,1)\":     \"0\",\n\t\t\"DDB(10000,1000,5,1)\": \"4000\",\n\t\t\"DDB(10000,1000,5,2)\": \"2400\",\n\t\t\"DDB(10000,1000,5,3)\": \"1440\",\n\t\t\"DDB(10000,1000,5,4)\": \"864\",\n\t\t\"DDB(10000,1000,5,5)\": \"296\",\n\t\t// DISC\n\t\t\"DISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,100)\": \"0.01\",\n\t\t// DOLLAR\n\t\t\"DOLLAR(1234.56)\":     \"$1,234.56\",\n\t\t\"DOLLAR(1234.56,0)\":   \"$1,235\",\n\t\t\"DOLLAR(1234.56,1)\":   \"$1,234.6\",\n\t\t\"DOLLAR(1234.56,2)\":   \"$1,234.56\",\n\t\t\"DOLLAR(1234.56,3)\":   \"$1,234.560\",\n\t\t\"DOLLAR(1234.56,-2)\":  \"$1,200\",\n\t\t\"DOLLAR(1234.56,-3)\":  \"$1,000\",\n\t\t\"DOLLAR(-1234.56,3)\":  \"($1,234.560)\",\n\t\t\"DOLLAR(-1234.56,-3)\": \"($1,000)\",\n\t\t// DOLLARDE\n\t\t\"DOLLARDE(1.01,16)\": \"1.0625\",\n\t\t// DOLLARFR\n\t\t\"DOLLARFR(1.0625,16)\": \"1.01\",\n\t\t// DURATION\n\t\t\"DURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,8%,4)\": \"6.67442279848313\",\n\t\t// EFFECT\n\t\t\"EFFECT(0.1,4)\":   \"0.103812890625\",\n\t\t\"EFFECT(0.025,2)\": \"0.02515625\",\n\t\t// EUROCONVERT\n\t\t\"EUROCONVERT(1.47,\\\"EUR\\\",\\\"EUR\\\")\":         \"1.47\",\n\t\t\"EUROCONVERT(1.47,\\\"EUR\\\",\\\"DEM\\\")\":         \"2.88\",\n\t\t\"EUROCONVERT(1.47,\\\"FRF\\\",\\\"DEM\\\")\":         \"0.44\",\n\t\t\"EUROCONVERT(1.47,\\\"FRF\\\",\\\"DEM\\\",FALSE)\":   \"0.44\",\n\t\t\"EUROCONVERT(1.47,\\\"FRF\\\",\\\"DEM\\\",FALSE,3)\": \"0.44\",\n\t\t\"EUROCONVERT(1.47,\\\"FRF\\\",\\\"DEM\\\",TRUE,3)\":  \"0.43810592\",\n\t\t// FV\n\t\t\"FV(0.05/12,60,-1000)\":   \"68006.0828408434\",\n\t\t\"FV(0.1/4,16,-2000,0,1)\": \"39729.4608941662\",\n\t\t\"FV(0,16,-2000)\":         \"32000\",\n\t\t// FVSCHEDULE\n\t\t\"FVSCHEDULE(10000,A1:A5)\": \"240000\",\n\t\t\"FVSCHEDULE(10000,0.5)\":   \"15000\",\n\t\t// INTRATE\n\t\t\"INTRATE(\\\"04/01/2005\\\",\\\"03/31/2010\\\",1000,2125)\": \"0.225\",\n\t\t// IPMT\n\t\t\"IPMT(0.05/12,2,60,50000)\":   \"-205.26988187972\",\n\t\t\"IPMT(0.035/4,2,8,0,5000,1)\": \"5.25745523782908\",\n\t\t// ISPMT\n\t\t\"ISPMT(0.05/12,1,60,50000)\": \"-204.861111111111\",\n\t\t\"ISPMT(0.05/12,2,60,50000)\": \"-201.388888888889\",\n\t\t\"ISPMT(0.05/12,2,1,50000)\":  \"208.333333333333\",\n\t\t// MDURATION\n\t\t\"MDURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,8%,4)\": \"6.54355176321876\",\n\t\t// NOMINAL\n\t\t\"NOMINAL(0.025,12)\": \"0.0247180352381129\",\n\t\t// NPER\n\t\t\"NPER(0.04,-6000,50000)\":           \"10.3380350715077\",\n\t\t\"NPER(0,-6000,50000)\":              \"8.33333333333333\",\n\t\t\"NPER(0.06/4,-2000,60000,30000,1)\": \"52.7947737092748\",\n\t\t// NPV\n\t\t\"NPV(0.02,-5000,\\\"\\\",800)\": \"-4133.02575932334\",\n\t\t// ODDFPRICE\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2)\":       \"107.691830256629\",\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,4,1)\":     \"106.766915010929\",\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,4,3)\":     \"106.7819138147\",\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,4,4)\":     \"106.771913772467\",\n\t\t\"ODDFPRICE(\\\"11/11/2008\\\",\\\"03/01/2021\\\",\\\"10/15/2008\\\",\\\"03/01/2009\\\",7.85%,6.25%,100,2,1)\":   \"113.597717474079\",\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"09/30/2017\\\",5.5%,3.5%,100,4,0)\":     \"106.72930611878\",\n\t\t\"ODDFPRICE(\\\"11/11/2008\\\",\\\"03/29/2021\\\",\\\"08/15/2008\\\",\\\"03/29/2009\\\",0.0785,0.0625,100,2,1)\": \"113.61826640814\",\n\t\t// ODDFYIELD\n\t\t\"ODDFYIELD(\\\"05/01/2017\\\",\\\"06/30/2021\\\",\\\"03/15/2017\\\",\\\"06/30/2017\\\",5.5%,102,100,1)\":   \"0.0495998049937776\",\n\t\t\"ODDFYIELD(\\\"05/01/2017\\\",\\\"06/30/2021\\\",\\\"03/15/2017\\\",\\\"06/30/2017\\\",5.5%,102,100,2)\":   \"0.0496289417392839\",\n\t\t\"ODDFYIELD(\\\"05/01/2017\\\",\\\"06/30/2021\\\",\\\"03/15/2017\\\",\\\"06/30/2017\\\",5.5%,102,100,4,1)\": \"0.0464750282973541\",\n\t\t// ODDLPRICE\n\t\t\"ODDLPRICE(\\\"04/20/2008\\\",\\\"06/15/2008\\\",\\\"12/24/2007\\\",3.75%,99.875,100,2)\":   \"5.0517841252892\",\n\t\t\"ODDLPRICE(\\\"04/20/2008\\\",\\\"06/15/2008\\\",\\\"12/24/2007\\\",3.75%,99.875,100,4,1)\": \"10.3667274303228\",\n\t\t// ODDLYIELD\n\t\t\"ODDLYIELD(\\\"04/20/2008\\\",\\\"06/15/2008\\\",\\\"12/24/2007\\\",3.75%,99.875,100,2)\":   \"0.0451922356291692\",\n\t\t\"ODDLYIELD(\\\"04/20/2008\\\",\\\"06/15/2008\\\",\\\"12/24/2007\\\",3.75%,99.875,100,4,1)\": \"0.0882287538349037\",\n\t\t// PDURATION\n\t\t\"PDURATION(0.04,10000,15000)\": \"10.3380350715076\",\n\t\t// PMT\n\t\t\"PMT(0,8,0,5000,1)\":       \"-625\",\n\t\t\"PMT(0.035/4,8,0,5000,1)\": \"-600.852027180466\",\n\t\t// PRICE\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"02/01/2020\\\",12%,10%,100,2)\":   \"110.655105178443\",\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"02/01/2020\\\",12%,10%,100,2,4)\": \"110.655105178443\",\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"03/31/2020\\\",12%,10%,100,2)\":   \"110.834483593216\",\n\t\t\"PRICE(\\\"01/01/2010\\\",\\\"06/30/2010\\\",0.5,1,1,1,4)\":     \"8.92419088847661\",\n\t\t// PPMT\n\t\t\"PPMT(0.05/12,2,60,50000)\":   \"-738.291800320824\",\n\t\t\"PPMT(0.035/4,2,8,0,5000,1)\": \"-606.109482418295\",\n\t\t// PRICEDISC\n\t\t\"PRICEDISC(\\\"04/01/2017\\\",\\\"03/31/2021\\\",2.5%,100)\":   \"90\",\n\t\t\"PRICEDISC(\\\"04/01/2017\\\",\\\"03/31/2021\\\",2.5%,100,3)\": \"90\",\n\t\t\"PRICEDISC(\\\"42826\\\",\\\"03/31/2021\\\",2.5%,100,3)\":      \"90\",\n\t\t// PRICEMAT\n\t\t\"PRICEMAT(\\\"04/01/2017\\\",\\\"03/31/2021\\\",\\\"01/01/2017\\\",4.5%,2.5%)\":   \"107.170454545455\",\n\t\t\"PRICEMAT(\\\"04/01/2017\\\",\\\"03/31/2021\\\",\\\"01/01/2017\\\",4.5%,2.5%,0)\": \"107.170454545455\",\n\t\t// PV\n\t\t\"PV(0,60,1000)\":         \"-60000\",\n\t\t\"PV(5%/12,60,1000)\":     \"-52990.7063239275\",\n\t\t\"PV(10%/4,16,2000,0,1)\": \"-26762.7554528811\",\n\t\t// RATE\n\t\t\"RATE(60,-1000,50000)\":       \"0.0061834131621292\",\n\t\t\"RATE(24,-800,0,20000,1)\":    \"0.00325084350160374\",\n\t\t\"RATE(48,-200,8000,3,1,0.5)\": \"0.0080412665831637\",\n\t\t// RECEIVED\n\t\t\"RECEIVED(\\\"04/01/2011\\\",\\\"03/31/2016\\\",1000,4.5%)\":   \"1290.32258064516\",\n\t\t\"RECEIVED(\\\"04/01/2011\\\",\\\"03/31/2016\\\",1000,4.5%,0)\": \"1290.32258064516\",\n\t\t// RRI\n\t\t\"RRI(10,10000,15000)\": \"0.0413797439924106\",\n\t\t// SLN\n\t\t\"SLN(10000,1000,5)\": \"1800\",\n\t\t// SYD\n\t\t\"SYD(10000,1000,5,1)\": \"3000\",\n\t\t\"SYD(10000,1000,5,2)\": \"2400\",\n\t\t// TBILLEQ\n\t\t\"TBILLEQ(\\\"01/01/2017\\\",\\\"06/30/2017\\\",2.5%)\": \"0.0256680731364276\",\n\t\t// TBILLPRICE\n\t\t\"TBILLPRICE(\\\"02/01/2017\\\",\\\"06/30/2017\\\",2.75%)\": \"98.8618055555556\",\n\t\t// TBILLYIELD\n\t\t\"TBILLYIELD(\\\"02/01/2017\\\",\\\"06/30/2017\\\",99)\": \"0.024405125076266\",\n\t\t// VDB\n\t\t\"VDB(10000,1000,5,0,1)\":           \"4000\",\n\t\t\"VDB(10000,1000,5,1,3)\":           \"3840\",\n\t\t\"VDB(10000,1000,5,3,5)\":           \"1160\",\n\t\t\"VDB(10000,1000,5,3.5,5,2)\":       \"709.6\",\n\t\t\"VDB(10000,1000,5,3,5,0.2,FALSE)\": \"3600\",\n\t\t\"VDB(10000,1000,5,3,5,0.2,TRUE)\":  \"693.633024\",\n\t\t\"VDB(24000,3000,10,0,0.875,2)\":    \"4200\",\n\t\t\"VDB(24000,3000,10,0.1,1)\":        \"4233.6\",\n\t\t\"VDB(24000,3000,10,0.1,1,1)\":      \"2138.4\",\n\t\t\"VDB(24000,3000,100,50,100,1)\":    \"10377.2944184652\",\n\t\t\"VDB(24000,3000,100,50,100,2)\":    \"5740.0723220908\",\n\t\t// YIELD\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2015\\\",10%,101,100,4)\":               \"0.0975631546829798\",\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2015\\\",10%,101,100,4,4)\":             \"0.0976269355643988\",\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2010\\\",0.5,1,1,1,4)\":                 \"1.91285866099894\",\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2010\\\",0,1,1,1,4)\":                   \"0\",\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"01/02/2020\\\",100,68.15518653988686,1,1,1)\": \"64\",\n\t\t// YIELDDISC\n\t\t\"YIELDDISC(\\\"01/01/2017\\\",\\\"06/30/2017\\\",97,100)\":   \"0.0622012325059031\",\n\t\t\"YIELDDISC(\\\"01/01/2017\\\",\\\"06/30/2017\\\",97,100,0)\": \"0.0622012325059031\",\n\t\t// YIELDMAT\n\t\t\"YIELDMAT(\\\"01/01/2017\\\",\\\"06/30/2018\\\",\\\"06/01/2014\\\",5.5%,101)\":   \"0.0419422478838651\",\n\t\t\"YIELDMAT(\\\"01/01/2017\\\",\\\"06/30/2018\\\",\\\"06/01/2014\\\",5.5%,101,0)\": \"0.0419422478838651\",\n\t\t// DISPIMG\n\t\t\"_xlfn.DISPIMG(\\\"ID_********************************\\\",1)\": \"ID_********************************\",\n\t}\n\tfor formula, expected := range mathCalc {\n\t\tf := prepareCalcData(cellData)\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tmathCalcError := map[string][]string{\n\t\t\"1/0\":        {\"\", \"#DIV/0!\"},\n\t\t\"1^\\\"text\\\"\": {\"\", \"strconv.ParseFloat: parsing \\\"text\\\": invalid syntax\"},\n\t\t\"\\\"text\\\"^1\": {\"\", \"strconv.ParseFloat: parsing \\\"text\\\": invalid syntax\"},\n\t\t\"1+\\\"text\\\"\": {\"\", \"strconv.ParseFloat: parsing \\\"text\\\": invalid syntax\"},\n\t\t\"\\\"text\\\"+1\": {\"\", \"strconv.ParseFloat: parsing \\\"text\\\": invalid syntax\"},\n\t\t\"1-\\\"text\\\"\": {\"\", \"strconv.ParseFloat: parsing \\\"text\\\": invalid syntax\"},\n\t\t\"\\\"text\\\"-1\": {\"\", \"strconv.ParseFloat: parsing \\\"text\\\": invalid syntax\"},\n\t\t\"1*\\\"text\\\"\": {\"\", \"strconv.ParseFloat: parsing \\\"text\\\": invalid syntax\"},\n\t\t\"\\\"text\\\"*1\": {\"\", \"strconv.ParseFloat: parsing \\\"text\\\": invalid syntax\"},\n\t\t\"1/\\\"text\\\"\": {\"\", \"strconv.ParseFloat: parsing \\\"text\\\": invalid syntax\"},\n\t\t\"\\\"text\\\"/1\": {\"\", \"strconv.ParseFloat: parsing \\\"text\\\": invalid syntax\"},\n\t\t// Engineering Functions\n\t\t// BESSELI\n\t\t\"BESSELI()\":       {\"#VALUE!\", \"BESSELI requires 2 numeric arguments\"},\n\t\t\"BESSELI(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BESSELI(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// BESSELJ\n\t\t\"BESSELJ()\":       {\"#VALUE!\", \"BESSELJ requires 2 numeric arguments\"},\n\t\t\"BESSELJ(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BESSELJ(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// BESSELK\n\t\t\"BESSELK()\":       {\"#VALUE!\", \"BESSELK requires 2 numeric arguments\"},\n\t\t\"BESSELK(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BESSELK(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BESSELK(-1,0)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"BESSELK(1,-1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t// BESSELY\n\t\t\"BESSELY()\":       {\"#VALUE!\", \"BESSELY requires 2 numeric arguments\"},\n\t\t\"BESSELY(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BESSELY(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BESSELY(-1,0)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"BESSELY(1,-1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t// BIN2DEC\n\t\t\"BIN2DEC()\":     {\"#VALUE!\", \"BIN2DEC requires 1 numeric argument\"},\n\t\t\"BIN2DEC(\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// BIN2HEX\n\t\t\"BIN2HEX()\":               {\"#VALUE!\", \"BIN2HEX requires at least 1 argument\"},\n\t\t\"BIN2HEX(1,1,1)\":          {\"#VALUE!\", \"BIN2HEX allows at most 2 arguments\"},\n\t\t\"BIN2HEX(\\\"\\\",1)\":         {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BIN2HEX(1,\\\"\\\")\":         {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BIN2HEX(12345678901,10)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"BIN2HEX(1,-1)\":           {\"#NUM!\", \"#NUM!\"},\n\t\t\"BIN2HEX(31,1)\":           {\"#NUM!\", \"#NUM!\"},\n\t\t// BIN2OCT\n\t\t\"BIN2OCT()\":                 {\"#VALUE!\", \"BIN2OCT requires at least 1 argument\"},\n\t\t\"BIN2OCT(1,1,1)\":            {\"#VALUE!\", \"BIN2OCT allows at most 2 arguments\"},\n\t\t\"BIN2OCT(\\\"\\\",1)\":           {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BIN2OCT(1,\\\"\\\")\":           {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BIN2OCT(-12345678901 ,10)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"BIN2OCT(1,-1)\":             {\"#NUM!\", \"#NUM!\"},\n\t\t\"BIN2OCT(8,1)\":              {\"#NUM!\", \"#NUM!\"},\n\t\t// BITAND\n\t\t\"BITAND()\":        {\"#VALUE!\", \"BITAND requires 2 numeric arguments\"},\n\t\t\"BITAND(-1,2)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITAND(2^48,2)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITAND(1,-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITAND(\\\"\\\",-1)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITAND(1,\\\"\\\")\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITAND(1,2^48)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// BITLSHIFT\n\t\t\"BITLSHIFT()\":        {\"#VALUE!\", \"BITLSHIFT requires 2 numeric arguments\"},\n\t\t\"BITLSHIFT(-1,2)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITLSHIFT(2^48,2)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITLSHIFT(1,-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITLSHIFT(\\\"\\\",-1)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITLSHIFT(1,\\\"\\\")\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITLSHIFT(1,2^48)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// BITOR\n\t\t\"BITOR()\":        {\"#VALUE!\", \"BITOR requires 2 numeric arguments\"},\n\t\t\"BITOR(-1,2)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITOR(2^48,2)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITOR(1,-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITOR(\\\"\\\",-1)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITOR(1,\\\"\\\")\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITOR(1,2^48)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// BITRSHIFT\n\t\t\"BITRSHIFT()\":        {\"#VALUE!\", \"BITRSHIFT requires 2 numeric arguments\"},\n\t\t\"BITRSHIFT(-1,2)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITRSHIFT(2^48,2)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITRSHIFT(1,-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITRSHIFT(\\\"\\\",-1)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITRSHIFT(1,\\\"\\\")\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITRSHIFT(1,2^48)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// BITXOR\n\t\t\"BITXOR()\":        {\"#VALUE!\", \"BITXOR requires 2 numeric arguments\"},\n\t\t\"BITXOR(-1,2)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITXOR(2^48,2)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITXOR(1,-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITXOR(\\\"\\\",-1)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITXOR(1,\\\"\\\")\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BITXOR(1,2^48)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// COMPLEX\n\t\t\"COMPLEX()\":              {\"#VALUE!\", \"COMPLEX requires at least 2 arguments\"},\n\t\t\"COMPLEX(10,-5,\\\"\\\")\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COMPLEX(\\\"\\\",0)\":        {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"COMPLEX(0,\\\"\\\")\":        {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"COMPLEX(10,-5,\\\"i\\\",0)\": {\"#VALUE!\", \"COMPLEX allows at most 3 arguments\"},\n\t\t// CONVERT\n\t\t\"CONVERT()\":                          {\"#VALUE!\", \"CONVERT requires 3 arguments\"},\n\t\t\"CONVERT(\\\"\\\",\\\"m\\\",\\\"yd\\\")\":         {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CONVERT(20.2,\\\"m\\\",\\\"C\\\")\":          {\"#N/A\", \"#N/A\"},\n\t\t\"CONVERT(20.2,\\\"\\\",\\\"C\\\")\":           {\"#N/A\", \"#N/A\"},\n\t\t\"CONVERT(100,\\\"dapt\\\",\\\"pt\\\")\":       {\"#N/A\", \"#N/A\"},\n\t\t\"CONVERT(1,\\\"ft\\\",\\\"day\\\")\":          {\"#N/A\", \"#N/A\"},\n\t\t\"CONVERT(234.56,\\\"kpt\\\",\\\"lt\\\")\":     {\"#N/A\", \"#N/A\"},\n\t\t\"CONVERT(234.56,\\\"lt\\\",\\\"kpt\\\")\":     {\"#N/A\", \"#N/A\"},\n\t\t\"CONVERT(234.56,\\\"kiqt\\\",\\\"pt\\\")\":    {\"#N/A\", \"#N/A\"},\n\t\t\"CONVERT(234.56,\\\"pt\\\",\\\"kiqt\\\")\":    {\"#N/A\", \"#N/A\"},\n\t\t\"CONVERT(12345.6,\\\"baton\\\",\\\"cwt\\\")\": {\"#N/A\", \"#N/A\"},\n\t\t\"CONVERT(12345.6,\\\"cwt\\\",\\\"baton\\\")\": {\"#N/A\", \"#N/A\"},\n\t\t\"CONVERT(234.56,\\\"xxxx\\\",\\\"m\\\")\":     {\"#N/A\", \"#N/A\"},\n\t\t\"CONVERT(234.56,\\\"m\\\",\\\"xxxx\\\")\":     {\"#N/A\", \"#N/A\"},\n\t\t// DEC2BIN\n\t\t\"DEC2BIN()\":        {\"#VALUE!\", \"DEC2BIN requires at least 1 argument\"},\n\t\t\"DEC2BIN(1,1,1)\":   {\"#VALUE!\", \"DEC2BIN allows at most 2 arguments\"},\n\t\t\"DEC2BIN(\\\"\\\",1)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DEC2BIN(1,\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DEC2BIN(-513,10)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"DEC2BIN(1,-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"DEC2BIN(2,1)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t// DEC2HEX\n\t\t\"DEC2HEX()\":                 {\"#VALUE!\", \"DEC2HEX requires at least 1 argument\"},\n\t\t\"DEC2HEX(1,1,1)\":            {\"#VALUE!\", \"DEC2HEX allows at most 2 arguments\"},\n\t\t\"DEC2HEX(\\\"\\\",1)\":           {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DEC2HEX(1,\\\"\\\")\":           {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DEC2HEX(-549755813888,10)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"DEC2HEX(1,-1)\":             {\"#NUM!\", \"#NUM!\"},\n\t\t\"DEC2HEX(31,1)\":             {\"#NUM!\", \"#NUM!\"},\n\t\t// DEC2OCT\n\t\t\"DEC2OCT()\":               {\"#VALUE!\", \"DEC2OCT requires at least 1 argument\"},\n\t\t\"DEC2OCT(1,1,1)\":          {\"#VALUE!\", \"DEC2OCT allows at most 2 arguments\"},\n\t\t\"DEC2OCT(\\\"\\\",1)\":         {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DEC2OCT(1,\\\"\\\")\":         {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DEC2OCT(-536870912 ,10)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"DEC2OCT(1,-1)\":           {\"#NUM!\", \"#NUM!\"},\n\t\t\"DEC2OCT(8,1)\":            {\"#NUM!\", \"#NUM!\"},\n\t\t// DELTA\n\t\t\"DELTA()\":       {\"#VALUE!\", \"DELTA requires at least 1 argument\"},\n\t\t\"DELTA(0,0,0)\":  {\"#VALUE!\", \"DELTA allows at most 2 arguments\"},\n\t\t\"DELTA(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DELTA(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// ERF\n\t\t\"ERF()\":       {\"#VALUE!\", \"ERF requires at least 1 argument\"},\n\t\t\"ERF(0,0,0)\":  {\"#VALUE!\", \"ERF allows at most 2 arguments\"},\n\t\t\"ERF(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ERF(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// ERF.PRECISE\n\t\t\"ERF.PRECISE()\":     {\"#VALUE!\", \"ERF.PRECISE requires 1 argument\"},\n\t\t\"ERF.PRECISE(\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// ERFC\n\t\t\"ERFC()\":     {\"#VALUE!\", \"ERFC requires 1 argument\"},\n\t\t\"ERFC(\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// ERFC.PRECISE\n\t\t\"ERFC.PRECISE()\":     {\"#VALUE!\", \"ERFC.PRECISE requires 1 argument\"},\n\t\t\"ERFC.PRECISE(\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// GESTEP\n\t\t\"GESTEP()\":       {\"#VALUE!\", \"GESTEP requires at least 1 argument\"},\n\t\t\"GESTEP(0,0,0)\":  {\"#VALUE!\", \"GESTEP allows at most 2 arguments\"},\n\t\t\"GESTEP(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GESTEP(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// HEX2BIN\n\t\t\"HEX2BIN()\":        {\"#VALUE!\", \"HEX2BIN requires at least 1 argument\"},\n\t\t\"HEX2BIN(1,1,1)\":   {\"#VALUE!\", \"HEX2BIN allows at most 2 arguments\"},\n\t\t\"HEX2BIN(\\\"X\\\",1)\": {\"#NUM!\", \"strconv.ParseInt: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"HEX2BIN(1,\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"HEX2BIN(-513,10)\": {\"#NUM!\", \"strconv.ParseInt: parsing \\\"-\\\": invalid syntax\"},\n\t\t\"HEX2BIN(1,-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"HEX2BIN(2,1)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t// HEX2DEC\n\t\t\"HEX2DEC()\":      {\"#VALUE!\", \"HEX2DEC requires 1 numeric argument\"},\n\t\t\"HEX2DEC(\\\"X\\\")\": {\"#NUM!\", \"strconv.ParseInt: parsing \\\"X\\\": invalid syntax\"},\n\t\t// HEX2OCT\n\t\t\"HEX2OCT()\":        {\"#VALUE!\", \"HEX2OCT requires at least 1 argument\"},\n\t\t\"HEX2OCT(1,1,1)\":   {\"#VALUE!\", \"HEX2OCT allows at most 2 arguments\"},\n\t\t\"HEX2OCT(\\\"X\\\",1)\": {\"#NUM!\", \"strconv.ParseInt: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"HEX2OCT(1,\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"HEX2OCT(-513,10)\": {\"#NUM!\", \"strconv.ParseInt: parsing \\\"-\\\": invalid syntax\"},\n\t\t\"HEX2OCT(1,-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// IMABS\n\t\t\"IMABS()\":     {\"#VALUE!\", \"IMABS requires 1 argument\"},\n\t\t\"IMABS(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMAGINARY\n\t\t\"IMAGINARY()\":     {\"#VALUE!\", \"IMAGINARY requires 1 argument\"},\n\t\t\"IMAGINARY(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMARGUMENT\n\t\t\"IMARGUMENT()\":     {\"#VALUE!\", \"IMARGUMENT requires 1 argument\"},\n\t\t\"IMARGUMENT(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMCONJUGATE\n\t\t\"IMCONJUGATE()\":     {\"#VALUE!\", \"IMCONJUGATE requires 1 argument\"},\n\t\t\"IMCONJUGATE(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMCOS\n\t\t\"IMCOS()\":     {\"#VALUE!\", \"IMCOS requires 1 argument\"},\n\t\t\"IMCOS(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMCOSH\n\t\t\"IMCOSH()\":     {\"#VALUE!\", \"IMCOSH requires 1 argument\"},\n\t\t\"IMCOSH(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMCOT\n\t\t\"IMCOT()\":     {\"#VALUE!\", \"IMCOT requires 1 argument\"},\n\t\t\"IMCOT(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMCSC\n\t\t\"IMCSC()\":     {\"#VALUE!\", \"IMCSC requires 1 argument\"},\n\t\t\"IMCSC(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IMCSC(0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// IMCSCH\n\t\t\"IMCSCH()\":     {\"#VALUE!\", \"IMCSCH requires 1 argument\"},\n\t\t\"IMCSCH(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IMCSCH(0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// IMDIV\n\t\t\"IMDIV()\":       {\"#VALUE!\", \"IMDIV requires 2 arguments\"},\n\t\t\"IMDIV(0,\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IMDIV(\\\"\\\",0)\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IMDIV(1,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// IMEXP\n\t\t\"IMEXP()\":     {\"#VALUE!\", \"IMEXP requires 1 argument\"},\n\t\t\"IMEXP(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMLN\n\t\t\"IMLN()\":     {\"#VALUE!\", \"IMLN requires 1 argument\"},\n\t\t\"IMLN(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IMLN(0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// IMLOG10\n\t\t\"IMLOG10()\":     {\"#VALUE!\", \"IMLOG10 requires 1 argument\"},\n\t\t\"IMLOG10(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IMLOG10(0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// IMLOG2\n\t\t\"IMLOG2()\":     {\"#VALUE!\", \"IMLOG2 requires 1 argument\"},\n\t\t\"IMLOG2(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IMLOG2(0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// IMPOWER\n\t\t\"IMPOWER()\":       {\"#VALUE!\", \"IMPOWER requires 2 arguments\"},\n\t\t\"IMPOWER(0,\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IMPOWER(\\\"\\\",0)\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IMPOWER(0,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"IMPOWER(0,-1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t// IMPRODUCT\n\t\t\"IMPRODUCT(\\\"x\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"x\\\": invalid syntax\"},\n\t\t\"IMPRODUCT(A1:D1)\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"Month\\\": invalid syntax\"},\n\t\t// IMREAL\n\t\t\"IMREAL()\":     {\"#VALUE!\", \"IMREAL requires 1 argument\"},\n\t\t\"IMREAL(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMSEC\n\t\t\"IMSEC()\":     {\"#VALUE!\", \"IMSEC requires 1 argument\"},\n\t\t\"IMSEC(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMSECH\n\t\t\"IMSECH()\":     {\"#VALUE!\", \"IMSECH requires 1 argument\"},\n\t\t\"IMSECH(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMSIN\n\t\t\"IMSIN()\":     {\"#VALUE!\", \"IMSIN requires 1 argument\"},\n\t\t\"IMSIN(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMSINH\n\t\t\"IMSINH()\":     {\"#VALUE!\", \"IMSINH requires 1 argument\"},\n\t\t\"IMSINH(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMSQRT\n\t\t\"IMSQRT()\":     {\"#VALUE!\", \"IMSQRT requires 1 argument\"},\n\t\t\"IMSQRT(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMSUB\n\t\t\"IMSUB()\":       {\"#VALUE!\", \"IMSUB requires 2 arguments\"},\n\t\t\"IMSUB(0,\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IMSUB(\\\"\\\",0)\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMSUM\n\t\t\"IMSUM()\":     {\"#VALUE!\", \"IMSUM requires at least 1 argument\"},\n\t\t\"IMSUM(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// IMTAN\n\t\t\"IMTAN()\":     {\"#VALUE!\", \"IMTAN requires 1 argument\"},\n\t\t\"IMTAN(\\\"\\\")\": {\"#NUM!\", \"strconv.ParseComplex: parsing \\\"\\\": invalid syntax\"},\n\t\t// OCT2BIN\n\t\t\"OCT2BIN()\":               {\"#VALUE!\", \"OCT2BIN requires at least 1 argument\"},\n\t\t\"OCT2BIN(1,1,1)\":          {\"#VALUE!\", \"OCT2BIN allows at most 2 arguments\"},\n\t\t\"OCT2BIN(\\\"\\\",1)\":         {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"OCT2BIN(1,\\\"\\\")\":         {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"OCT2BIN(-536870912 ,10)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"OCT2BIN(1,-1)\":           {\"#NUM!\", \"#NUM!\"},\n\t\t// OCT2DEC\n\t\t\"OCT2DEC()\":     {\"#VALUE!\", \"OCT2DEC requires 1 numeric argument\"},\n\t\t\"OCT2DEC(\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// OCT2HEX\n\t\t\"OCT2HEX()\":               {\"#VALUE!\", \"OCT2HEX requires at least 1 argument\"},\n\t\t\"OCT2HEX(1,1,1)\":          {\"#VALUE!\", \"OCT2HEX allows at most 2 arguments\"},\n\t\t\"OCT2HEX(\\\"\\\",1)\":         {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"OCT2HEX(1,\\\"\\\")\":         {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"OCT2HEX(-536870912 ,10)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"OCT2HEX(1,-1)\":           {\"#NUM!\", \"#NUM!\"},\n\t\t// Math and Trigonometric Functions\n\t\t// ABS\n\t\t\"ABS()\":      {\"#VALUE!\", \"ABS requires 1 numeric argument\"},\n\t\t\"ABS(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"ABS(~)\":     {\"#NAME?\", \"invalid reference\"},\n\t\t// ACOS\n\t\t\"ACOS()\":        {\"#VALUE!\", \"ACOS requires 1 numeric argument\"},\n\t\t\"ACOS(\\\"X\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"ACOS(ACOS(0))\": {\"#NUM!\", \"#NUM!\"},\n\t\t// ACOSH\n\t\t\"ACOSH()\":      {\"#VALUE!\", \"ACOSH requires 1 numeric argument\"},\n\t\t\"ACOSH(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// _xlfn.ACOT\n\t\t\"_xlfn.ACOT()\":      {\"#VALUE!\", \"ACOT requires 1 numeric argument\"},\n\t\t\"_xlfn.ACOT(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// _xlfn.ACOTH\n\t\t\"_xlfn.ACOTH()\":               {\"#VALUE!\", \"ACOTH requires 1 numeric argument\"},\n\t\t\"_xlfn.ACOTH(\\\"X\\\")\":          {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"_xlfn.ACOTH(_xlfn.ACOTH(2))\": {\"#NUM!\", \"#NUM!\"},\n\t\t// _xlfn.AGGREGATE\n\t\t\"_xlfn.AGGREGATE()\":             {\"#VALUE!\", \"AGGREGATE requires at least 3 arguments\"},\n\t\t\"_xlfn.AGGREGATE(\\\"\\\",0,A4:A5)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"_xlfn.AGGREGATE(1,\\\"\\\",A4:A5)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"_xlfn.AGGREGATE(0,A4:A5)\":      {\"#VALUE!\", \"AGGREGATE has invalid function_num\"},\n\t\t\"_xlfn.AGGREGATE(1,8,A4:A5)\":    {\"#VALUE!\", \"AGGREGATE has invalid options\"},\n\t\t\"_xlfn.AGGREGATE(1,0,A5:A6)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"_xlfn.AGGREGATE(13,0,A1:A6)\":   {\"#N/A\", \"#N/A\"},\n\t\t\"_xlfn.AGGREGATE(18,0,A1:A6,1)\": {\"#NUM!\", \"#NUM!\"},\n\t\t// _xlfn.ARABIC\n\t\t\"_xlfn.ARABIC()\": {\"#VALUE!\", \"ARABIC requires 1 numeric argument\"},\n\t\t\"_xlfn.ARABIC(\\\"\" + strings.Repeat(\"I\", 256) + \"\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// ASIN\n\t\t\"ASIN()\":      {\"#VALUE!\", \"ASIN requires 1 numeric argument\"},\n\t\t\"ASIN(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// ASINH\n\t\t\"ASINH()\":      {\"#VALUE!\", \"ASINH requires 1 numeric argument\"},\n\t\t\"ASINH(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// ATAN\n\t\t\"ATAN()\":      {\"#VALUE!\", \"ATAN requires 1 numeric argument\"},\n\t\t\"ATAN(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// ATANH\n\t\t\"ATANH()\":      {\"#VALUE!\", \"ATANH requires 1 numeric argument\"},\n\t\t\"ATANH(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// ATAN2\n\t\t\"ATAN2()\":        {\"#VALUE!\", \"ATAN2 requires 2 numeric arguments\"},\n\t\t\"ATAN2(\\\"X\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"ATAN2(0,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// BASE\n\t\t\"BASE()\":          {\"#VALUE!\", \"BASE requires at least 2 arguments\"},\n\t\t\"BASE(1,2,3,4)\":   {\"#VALUE!\", \"BASE allows at most 3 arguments\"},\n\t\t\"BASE(1,1)\":       {\"#VALUE!\", \"radix must be an integer >= 2 and <= 36\"},\n\t\t\"BASE(\\\"X\\\",2)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"BASE(1,\\\"X\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"BASE(1,2,\\\"X\\\")\": {\"#VALUE!\", \"strconv.Atoi: parsing \\\"X\\\": invalid syntax\"},\n\t\t// CEILING\n\t\t\"CEILING()\":        {\"#VALUE!\", \"CEILING requires at least 1 argument\"},\n\t\t\"CEILING(1,2,3)\":   {\"#VALUE!\", \"CEILING allows at most 2 arguments\"},\n\t\t\"CEILING(1,-1)\":    {\"#VALUE!\", \"negative sig to CEILING invalid\"},\n\t\t\"CEILING(\\\"X\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"CEILING(0,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// _xlfn.CEILING.MATH\n\t\t\"_xlfn.CEILING.MATH()\":          {\"#VALUE!\", \"CEILING.MATH requires at least 1 argument\"},\n\t\t\"_xlfn.CEILING.MATH(1,2,3,4)\":   {\"#VALUE!\", \"CEILING.MATH allows at most 3 arguments\"},\n\t\t\"_xlfn.CEILING.MATH(\\\"X\\\")\":     {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"_xlfn.CEILING.MATH(1,\\\"X\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"_xlfn.CEILING.MATH(1,2,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// _xlfn.CEILING.PRECISE\n\t\t\"_xlfn.CEILING.PRECISE()\":        {\"#VALUE!\", \"CEILING.PRECISE requires at least 1 argument\"},\n\t\t\"_xlfn.CEILING.PRECISE(1,2,3)\":   {\"#VALUE!\", \"CEILING.PRECISE allows at most 2 arguments\"},\n\t\t\"_xlfn.CEILING.PRECISE(\\\"X\\\",2)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"_xlfn.CEILING.PRECISE(1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// COMBIN\n\t\t\"COMBIN()\":         {\"#VALUE!\", \"COMBIN requires 2 argument\"},\n\t\t\"COMBIN(-1,1)\":     {\"#VALUE!\", \"COMBIN requires number >= number_chosen\"},\n\t\t\"COMBIN(\\\"X\\\",1)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"COMBIN(-1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// _xlfn.COMBINA\n\t\t\"_xlfn.COMBINA()\":         {\"#VALUE!\", \"COMBINA requires 2 argument\"},\n\t\t\"_xlfn.COMBINA(-1,1)\":     {\"#VALUE!\", \"COMBINA requires number > number_chosen\"},\n\t\t\"_xlfn.COMBINA(-1,-1)\":    {\"#VALUE!\", \"COMBIN requires number >= number_chosen\"},\n\t\t\"_xlfn.COMBINA(\\\"X\\\",1)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"_xlfn.COMBINA(-1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// COS\n\t\t\"COS()\":      {\"#VALUE!\", \"COS requires 1 numeric argument\"},\n\t\t\"COS(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// COSH\n\t\t\"COSH()\":      {\"#VALUE!\", \"COSH requires 1 numeric argument\"},\n\t\t\"COSH(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// _xlfn.COT\n\t\t\"COT()\":      {\"#VALUE!\", \"COT requires 1 numeric argument\"},\n\t\t\"COT(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"COT(0)\":     {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// _xlfn.COTH\n\t\t\"COTH()\":      {\"#VALUE!\", \"COTH requires 1 numeric argument\"},\n\t\t\"COTH(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"COTH(0)\":     {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// _xlfn.CSC\n\t\t\"_xlfn.CSC()\":      {\"#VALUE!\", \"CSC requires 1 numeric argument\"},\n\t\t\"_xlfn.CSC(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"_xlfn.CSC(0)\":     {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// _xlfn.CSCH\n\t\t\"_xlfn.CSCH()\":      {\"#VALUE!\", \"CSCH requires 1 numeric argument\"},\n\t\t\"_xlfn.CSCH(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"_xlfn.CSCH(0)\":     {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// _xlfn.DECIMAL\n\t\t\"_xlfn.DECIMAL()\":           {\"#VALUE!\", \"DECIMAL requires 2 numeric arguments\"},\n\t\t\"_xlfn.DECIMAL(\\\"X\\\",2)\":    {\"#VALUE!\", \"strconv.ParseInt: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"_xlfn.DECIMAL(2000,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// DEGREES\n\t\t\"DEGREES()\":      {\"#VALUE!\", \"DEGREES requires 1 numeric argument\"},\n\t\t\"DEGREES(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"DEGREES(0)\":     {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// EVEN\n\t\t\"EVEN()\":      {\"#VALUE!\", \"EVEN requires 1 numeric argument\"},\n\t\t\"EVEN(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// EXP\n\t\t\"EXP()\":      {\"#VALUE!\", \"EXP requires 1 numeric argument\"},\n\t\t\"EXP(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// FACT\n\t\t\"FACT()\":      {\"#VALUE!\", \"FACT requires 1 numeric argument\"},\n\t\t\"FACT(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"FACT(-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// FACTDOUBLE\n\t\t\"FACTDOUBLE()\":      {\"#VALUE!\", \"FACTDOUBLE requires 1 numeric argument\"},\n\t\t\"FACTDOUBLE(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"FACTDOUBLE(-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// FLOOR\n\t\t\"FLOOR()\":         {\"#VALUE!\", \"FLOOR requires 2 numeric arguments\"},\n\t\t\"FLOOR(\\\"X\\\",-1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"FLOOR(1,\\\"X\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"FLOOR(1,-1)\":     {\"#NUM!\", \"invalid arguments to FLOOR\"},\n\t\t// _xlfn.FLOOR.MATH\n\t\t\"_xlfn.FLOOR.MATH()\":          {\"#VALUE!\", \"FLOOR.MATH requires at least 1 argument\"},\n\t\t\"_xlfn.FLOOR.MATH(1,2,3,4)\":   {\"#VALUE!\", \"FLOOR.MATH allows at most 3 arguments\"},\n\t\t\"_xlfn.FLOOR.MATH(\\\"X\\\",2,3)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"_xlfn.FLOOR.MATH(1,\\\"X\\\",3)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"_xlfn.FLOOR.MATH(1,2,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// _xlfn.FLOOR.PRECISE\n\t\t\"_xlfn.FLOOR.PRECISE()\":        {\"#VALUE!\", \"FLOOR.PRECISE requires at least 1 argument\"},\n\t\t\"_xlfn.FLOOR.PRECISE(1,2,3)\":   {\"#VALUE!\", \"FLOOR.PRECISE allows at most 2 arguments\"},\n\t\t\"_xlfn.FLOOR.PRECISE(\\\"X\\\",2)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"_xlfn.FLOOR.PRECISE(1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// GCD\n\t\t\"GCD()\":      {\"#VALUE!\", \"GCD requires at least 1 argument\"},\n\t\t\"GCD(\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GCD(-1)\":    {\"#VALUE!\", \"GCD only accepts positive arguments\"},\n\t\t\"GCD(1,-1)\":  {\"#VALUE!\", \"GCD only accepts positive arguments\"},\n\t\t\"GCD(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// INT\n\t\t\"INT()\":      {\"#VALUE!\", \"INT requires 1 numeric argument\"},\n\t\t\"INT(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// ISO.CEILING\n\t\t\"ISO.CEILING()\":        {\"#VALUE!\", \"ISO.CEILING requires at least 1 argument\"},\n\t\t\"ISO.CEILING(1,2,3)\":   {\"#VALUE!\", \"ISO.CEILING allows at most 2 arguments\"},\n\t\t\"ISO.CEILING(\\\"X\\\",2)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"ISO.CEILING(1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// LCM\n\t\t\"LCM()\":      {\"#VALUE!\", \"LCM requires at least 1 argument\"},\n\t\t\"LCM(-1)\":    {\"#VALUE!\", \"LCM only accepts positive arguments\"},\n\t\t\"LCM(1,-1)\":  {\"#VALUE!\", \"LCM only accepts positive arguments\"},\n\t\t\"LCM(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// LN\n\t\t\"LN()\":      {\"#VALUE!\", \"LN requires 1 numeric argument\"},\n\t\t\"LN(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// LOG\n\t\t\"LOG()\":        {\"#VALUE!\", \"LOG requires at least 1 argument\"},\n\t\t\"LOG(1,2,3)\":   {\"#VALUE!\", \"LOG allows at most 2 arguments\"},\n\t\t\"LOG(\\\"X\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"LOG(1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"LOG(0,0)\":     {\"#NUM!\", \"#DIV/0!\"},\n\t\t\"LOG(1,0)\":     {\"#NUM!\", \"#DIV/0!\"},\n\t\t\"LOG(1,1)\":     {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// LOG10\n\t\t\"LOG10()\":      {\"#VALUE!\", \"LOG10 requires 1 numeric argument\"},\n\t\t\"LOG10(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// MDETERM\n\t\t\"MDETERM()\": {\"#VALUE!\", \"MDETERM requires 1 argument\"},\n\t\t// MINVERSE\n\t\t\"MINVERSE()\":      {\"#VALUE!\", \"MINVERSE requires 1 argument\"},\n\t\t\"MINVERSE(B3:C4)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MINVERSE(A1:C2)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MINVERSE(A4:A4)\": {\"#NUM!\", \"#NUM!\"},\n\t\t// MMULT\n\t\t\"MMULT()\":            {\"#VALUE!\", \"MMULT requires 2 argument\"},\n\t\t\"MMULT(A1:B2,B3:C4)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MMULT(B3:C4,A1:B2)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MMULT(A1:A2,B1:B2)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// MOD\n\t\t\"MOD()\":        {\"#VALUE!\", \"MOD requires 2 numeric arguments\"},\n\t\t\"MOD(6,0)\":     {\"#DIV/0!\", \"MOD divide by zero\"},\n\t\t\"MOD(\\\"X\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"MOD(6,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// MROUND\n\t\t\"MROUND()\":        {\"#VALUE!\", \"MROUND requires 2 numeric arguments\"},\n\t\t\"MROUND(1,0)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"MROUND(1,-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"MROUND(\\\"X\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"MROUND(1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// MULTINOMIAL\n\t\t\"MULTINOMIAL(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// _xlfn.MUNIT\n\t\t\"_xlfn.MUNIT()\":      {\"#VALUE!\", \"MUNIT requires 1 numeric argument\"},\n\t\t\"_xlfn.MUNIT(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"_xlfn.MUNIT(-1)\":    {\"#VALUE!\", \"\"},\n\t\t// ODD\n\t\t\"ODD()\":      {\"#VALUE!\", \"ODD requires 1 numeric argument\"},\n\t\t\"ODD(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// PI\n\t\t\"PI(1)\": {\"#VALUE!\", \"PI accepts no arguments\"},\n\t\t// POWER\n\t\t\"POWER(\\\"X\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"POWER(1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"POWER(0,0)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"POWER(0,-1)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"POWER(1)\":       {\"#VALUE!\", \"POWER requires 2 numeric arguments\"},\n\t\t// PRODUCT\n\t\t\"PRODUCT(\\\"X\\\")\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"PRODUCT(\\\"\\\",3,6)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// QUOTIENT\n\t\t\"QUOTIENT(\\\"X\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"QUOTIENT(1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"QUOTIENT(1,0)\":     {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"QUOTIENT(1)\":       {\"#VALUE!\", \"QUOTIENT requires 2 numeric arguments\"},\n\t\t// RADIANS\n\t\t\"RADIANS(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"RADIANS()\":      {\"#VALUE!\", \"RADIANS requires 1 numeric argument\"},\n\t\t// RAND\n\t\t\"RAND(1)\": {\"#VALUE!\", \"RAND accepts no arguments\"},\n\t\t// RANDBETWEEN\n\t\t\"RANDBETWEEN(\\\"X\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"RANDBETWEEN(1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"RANDBETWEEN()\":        {\"#VALUE!\", \"RANDBETWEEN requires 2 numeric arguments\"},\n\t\t\"RANDBETWEEN(2,1)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t// ROMAN\n\t\t\"ROMAN()\":       {\"#VALUE!\", \"ROMAN requires at least 1 argument\"},\n\t\t\"ROMAN(1,2,3)\":  {\"#VALUE!\", \"ROMAN allows at most 2 arguments\"},\n\t\t\"ROMAN(1,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ROMAN(\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ROMAN(\\\"\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// ROUND\n\t\t\"ROUND()\":        {\"#VALUE!\", \"ROUND requires 2 numeric arguments\"},\n\t\t\"ROUND(\\\"X\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"ROUND(1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// ROUNDDOWN\n\t\t\"ROUNDDOWN()\":        {\"#VALUE!\", \"ROUNDDOWN requires 2 numeric arguments\"},\n\t\t\"ROUNDDOWN(\\\"X\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"ROUNDDOWN(1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// ROUNDUP\n\t\t\"ROUNDUP()\":        {\"#VALUE!\", \"ROUNDUP requires 2 numeric arguments\"},\n\t\t\"ROUNDUP(\\\"X\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"ROUNDUP(1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// SEARCH\n\t\t\"SEARCH()\":          {\"#VALUE!\", \"SEARCH requires at least 2 arguments\"},\n\t\t\"SEARCH(1,A1,1,1)\":  {\"#VALUE!\", \"SEARCH allows at most 3 arguments\"},\n\t\t\"SEARCH(2,A1)\":      {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"SEARCH(1,A1,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// SEARCHB\n\t\t\"SEARCHB()\":                   {\"#VALUE!\", \"SEARCHB requires at least 2 arguments\"},\n\t\t\"SEARCHB(1,A1,1,1)\":           {\"#VALUE!\", \"SEARCHB allows at most 3 arguments\"},\n\t\t\"SEARCHB(2,A1)\":               {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"SEARCHB(\\\"?w\\\",\\\"你好world\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"SEARCHB(1,A1,\\\"\\\")\":          {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// SEC\n\t\t\"_xlfn.SEC()\":      {\"#VALUE!\", \"SEC requires 1 numeric argument\"},\n\t\t\"_xlfn.SEC(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// _xlfn.SECH\n\t\t\"_xlfn.SECH()\":      {\"#VALUE!\", \"SECH requires 1 numeric argument\"},\n\t\t\"_xlfn.SECH(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// SERIESSUM\n\t\t\"SERIESSUM()\":               {\"#VALUE!\", \"SERIESSUM requires 4 arguments\"},\n\t\t\"SERIESSUM(\\\"\\\",2,3,A1:A4)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"SERIESSUM(1,\\\"\\\",3,A1:A4)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"SERIESSUM(1,2,\\\"\\\",A1:A4)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"SERIESSUM(1,2,3,A1:D1)\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"Month\\\": invalid syntax\"},\n\t\t// SIGN\n\t\t\"SIGN()\":      {\"#VALUE!\", \"SIGN requires 1 numeric argument\"},\n\t\t\"SIGN(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// SIN\n\t\t\"SIN()\":      {\"#VALUE!\", \"SIN requires 1 numeric argument\"},\n\t\t\"SIN(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// SINH\n\t\t\"SINH()\":      {\"#VALUE!\", \"SINH requires 1 numeric argument\"},\n\t\t\"SINH(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// SQRT\n\t\t\"SQRT()\":      {\"#VALUE!\", \"SQRT requires 1 numeric argument\"},\n\t\t\"SQRT(\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"SQRT(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"SQRT(-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// SQRTPI\n\t\t\"SQRTPI()\":      {\"#VALUE!\", \"SQRTPI requires 1 numeric argument\"},\n\t\t\"SQRTPI(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// STDEV\n\t\t\"STDEV()\":      {\"#VALUE!\", \"STDEV requires at least 1 argument\"},\n\t\t\"STDEV(E2:E9)\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// STDEV.S\n\t\t\"STDEV.S()\": {\"#VALUE!\", \"STDEV.S requires at least 1 argument\"},\n\t\t// STDEVA\n\t\t\"STDEVA()\":      {\"#VALUE!\", \"STDEVA requires at least 1 argument\"},\n\t\t\"STDEVA(E2:E9)\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// POISSON.DIST\n\t\t\"POISSON.DIST()\": {\"#VALUE!\", \"POISSON.DIST requires 3 arguments\"},\n\t\t// POISSON\n\t\t\"POISSON()\":             {\"#VALUE!\", \"POISSON requires 3 arguments\"},\n\t\t\"POISSON(\\\"\\\",0,FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"POISSON(0,\\\"\\\",FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"POISSON(0,0,\\\"\\\")\":     {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"POISSON(0,-1,TRUE)\":    {\"#N/A\", \"#N/A\"},\n\t\t// PROB\n\t\t\"PROB()\":                   {\"#VALUE!\", \"PROB requires at least 3 arguments\"},\n\t\t\"PROB(A1:A2,B1:B2,1,1,1)\":  {\"#VALUE!\", \"PROB requires at most 4 arguments\"},\n\t\t\"PROB(A1:A2,B1:B2,\\\"\\\")\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"PROB(A1:A2,B1:B2,1,\\\"\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"PROB(A1,B1,1)\":            {\"#NUM!\", \"#NUM!\"},\n\t\t\"PROB(A1:A2,B1:B3,1)\":      {\"#N/A\", \"#N/A\"},\n\t\t\"PROB(A1:A2,B1:C2,1)\":      {\"#N/A\", \"#N/A\"},\n\t\t\"PROB(A1:A2,B1:B2,1)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t// SUBTOTAL\n\t\t\"SUBTOTAL()\":           {\"#VALUE!\", \"SUBTOTAL requires at least 2 arguments\"},\n\t\t\"SUBTOTAL(\\\"\\\",A4:A5)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"SUBTOTAL(0,A4:A5)\":    {\"#VALUE!\", \"SUBTOTAL has invalid function_num\"},\n\t\t\"SUBTOTAL(1,A5:A6)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// SUM\n\t\t\"SUM((\":             {\"\", ErrInvalidFormula.Error()},\n\t\t\"SUM(-)\":            {ErrInvalidFormula.Error(), ErrInvalidFormula.Error()},\n\t\t\"SUM(1+)\":           {ErrInvalidFormula.Error(), ErrInvalidFormula.Error()},\n\t\t\"SUM(1-)\":           {ErrInvalidFormula.Error(), ErrInvalidFormula.Error()},\n\t\t\"SUM(1*)\":           {ErrInvalidFormula.Error(), ErrInvalidFormula.Error()},\n\t\t\"SUM(1/)\":           {ErrInvalidFormula.Error(), ErrInvalidFormula.Error()},\n\t\t\"SUM(1*SUM(1/0))\":   {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"SUM(1*SUM(1/0)*1)\": {\"\", \"#DIV/0!\"},\n\t\t\"SUM(0:2)\":          {\"#NAME?\", \"invalid reference\"},\n\t\t\"SUM(1:1048577)\":    {\"#NAME?\", \"invalid reference\"},\n\t\t// SUMIF\n\t\t\"SUMIF()\": {\"#VALUE!\", \"SUMIF requires at least 2 arguments\"},\n\t\t// SUMSQ\n\t\t\"SUMSQ(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"SUMSQ(C1:D2)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"Month\\\": invalid syntax\"},\n\t\t// SUMPRODUCT\n\t\t\"SUMPRODUCT()\":            {\"#VALUE!\", \"SUMPRODUCT requires at least 1 argument\"},\n\t\t\"SUMPRODUCT(A1,B1:B2)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"SUMPRODUCT(A1,D1)\":       {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"SUMPRODUCT(A1:A3,D1:D3)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"SUMPRODUCT(A1:A2,B1:B3)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"SUMPRODUCT(\\\"\\\")\":        {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"SUMPRODUCT(A1,NA())\":     {\"#N/A\", \"#N/A\"},\n\t\t// SUMX2MY2\n\t\t\"SUMX2MY2()\":         {\"#VALUE!\", \"SUMX2MY2 requires 2 arguments\"},\n\t\t\"SUMX2MY2(A1,B1:B2)\": {\"#N/A\", \"#N/A\"},\n\t\t// SUMX2PY2\n\t\t\"SUMX2PY2()\":         {\"#VALUE!\", \"SUMX2PY2 requires 2 arguments\"},\n\t\t\"SUMX2PY2(A1,B1:B2)\": {\"#N/A\", \"#N/A\"},\n\t\t// SUMXMY2\n\t\t\"SUMXMY2()\":         {\"#VALUE!\", \"SUMXMY2 requires 2 arguments\"},\n\t\t\"SUMXMY2(A1,B1:B2)\": {\"#N/A\", \"#N/A\"},\n\t\t// TAN\n\t\t\"TAN()\":      {\"#VALUE!\", \"TAN requires 1 numeric argument\"},\n\t\t\"TAN(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// TANH\n\t\t\"TANH()\":      {\"#VALUE!\", \"TANH requires 1 numeric argument\"},\n\t\t\"TANH(\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// TRUNC\n\t\t\"TRUNC()\":        {\"#VALUE!\", \"TRUNC requires at least 1 argument\"},\n\t\t\"TRUNC(\\\"X\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t\"TRUNC(1,\\\"X\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"X\\\": invalid syntax\"},\n\t\t// Statistical Functions\n\t\t// AVEDEV\n\t\t\"AVEDEV()\":       {\"#VALUE!\", \"AVEDEV requires at least 1 argument\"},\n\t\t\"AVEDEV(\\\"\\\")\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"AVEDEV(1,\\\"\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// AVERAGE\n\t\t\"AVERAGE(H1)\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// AVERAGEA\n\t\t\"AVERAGEA(H1)\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// AVERAGEIF\n\t\t\"AVERAGEIF()\":                      {\"#VALUE!\", \"AVERAGEIF requires at least 2 arguments\"},\n\t\t\"AVERAGEIF(H1,\\\"\\\")\":               {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"AVERAGEIF(D1:D3,\\\"Month\\\",D1:D3)\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"AVERAGEIF(C1:C3,\\\"Month\\\",D1:D3)\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// BETA.DIST\n\t\t\"BETA.DIST()\":                     {\"#VALUE!\", \"BETA.DIST requires at least 4 arguments\"},\n\t\t\"BETA.DIST(0.4,4,5,TRUE,0,1,0)\":   {\"#VALUE!\", \"BETA.DIST requires at most 6 arguments\"},\n\t\t\"BETA.DIST(\\\"\\\",4,5,TRUE,0,1)\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETA.DIST(0.4,\\\"\\\",5,TRUE,0,1)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETA.DIST(0.4,4,\\\"\\\",TRUE,0,1)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETA.DIST(0.4,4,5,\\\"\\\",0,1)\":     {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETA.DIST(0.4,4,5,TRUE,\\\"\\\",1)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETA.DIST(0.4,4,5,TRUE,0,\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETA.DIST(0.4,0,5,TRUE,0,1)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETA.DIST(0.4,4,0,TRUE,0,0)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETA.DIST(0.4,4,5,TRUE,0.5,1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETA.DIST(0.4,4,5,TRUE,0,0.3)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETA.DIST(0.4,4,5,TRUE,0.4,0.4)\": {\"#NUM!\", \"#NUM!\"},\n\t\t// BETADIST\n\t\t\"BETADIST()\":                {\"#VALUE!\", \"BETADIST requires at least 3 arguments\"},\n\t\t\"BETADIST(0.4,4,5,0,1,0)\":   {\"#VALUE!\", \"BETADIST requires at most 5 arguments\"},\n\t\t\"BETADIST(\\\"\\\",4,5,0,1)\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETADIST(0.4,\\\"\\\",5,0,1)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETADIST(0.4,4,\\\"\\\",0,1)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETADIST(0.4,4,5,\\\"\\\",1)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETADIST(0.4,4,5,0,\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETADIST(2,4,5,3,1)\":       {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETADIST(2,4,5,0,1)\":       {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETADIST(0.4,0,5,0,1)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETADIST(0.4,4,0,0,1)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETADIST(0.4,4,5,0.4,0.4)\": {\"#NUM!\", \"#NUM!\"},\n\t\t// BETAINV\n\t\t\"BETAINV()\":               {\"#VALUE!\", \"BETAINV requires at least 3 arguments\"},\n\t\t\"BETAINV(0.2,4,5,0,1,0)\":  {\"#VALUE!\", \"BETAINV requires at most 5 arguments\"},\n\t\t\"BETAINV(\\\"\\\",4,5,0,1)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETAINV(0.2,\\\"\\\",5,0,1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETAINV(0.2,4,\\\"\\\",0,1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETAINV(0.2,4,5,\\\"\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETAINV(0.2,4,5,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETAINV(0,4,5,0,1)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETAINV(1,4,5,0,1)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETAINV(0.2,0,5,0,1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETAINV(0.2,4,0,0,1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETAINV(0.2,4,5,2,2)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// BETA.INV\n\t\t\"BETA.INV()\":               {\"#VALUE!\", \"BETA.INV requires at least 3 arguments\"},\n\t\t\"BETA.INV(0.2,4,5,0,1,0)\":  {\"#VALUE!\", \"BETA.INV requires at most 5 arguments\"},\n\t\t\"BETA.INV(\\\"\\\",4,5,0,1)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETA.INV(0.2,\\\"\\\",5,0,1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETA.INV(0.2,4,\\\"\\\",0,1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETA.INV(0.2,4,5,\\\"\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETA.INV(0.2,4,5,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BETA.INV(0,4,5,0,1)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETA.INV(1,4,5,0,1)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETA.INV(0.2,0,5,0,1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETA.INV(0.2,4,0,0,1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BETA.INV(0.2,4,5,2,2)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// BINOMDIST\n\t\t\"BINOMDIST()\":                   {\"#VALUE!\", \"BINOMDIST requires 4 arguments\"},\n\t\t\"BINOMDIST(\\\"\\\",100,0.5,FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOMDIST(10,\\\"\\\",0.5,FALSE)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOMDIST(10,100,\\\"\\\",FALSE)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOMDIST(10,100,0.5,\\\"\\\")\":    {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOMDIST(-1,100,0.5,FALSE)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOMDIST(110,100,0.5,FALSE)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOMDIST(10,100,-1,FALSE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOMDIST(10,100,2,FALSE)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t// BINOM.DIST\n\t\t\"BINOM.DIST()\":                   {\"#VALUE!\", \"BINOM.DIST requires 4 arguments\"},\n\t\t\"BINOM.DIST(\\\"\\\",100,0.5,FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOM.DIST(10,\\\"\\\",0.5,FALSE)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOM.DIST(10,100,\\\"\\\",FALSE)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOM.DIST(10,100,0.5,\\\"\\\")\":    {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOM.DIST(-1,100,0.5,FALSE)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.DIST(110,100,0.5,FALSE)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.DIST(10,100,-1,FALSE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.DIST(10,100,2,FALSE)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t// BINOM.DIST.RANGE\n\t\t\"BINOM.DIST.RANGE()\":                {\"#VALUE!\", \"BINOM.DIST.RANGE requires at least 3 arguments\"},\n\t\t\"BINOM.DIST.RANGE(100,0.5,0,40,0)\":  {\"#VALUE!\", \"BINOM.DIST.RANGE requires at most 4 arguments\"},\n\t\t\"BINOM.DIST.RANGE(\\\"\\\",0.5,0,40)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOM.DIST.RANGE(100,\\\"\\\",0,40)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOM.DIST.RANGE(100,0.5,\\\"\\\",40)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOM.DIST.RANGE(100,0.5,0,\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOM.DIST.RANGE(100,-1,0,40)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.DIST.RANGE(100,2,0,40)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.DIST.RANGE(100,0.5,-1,40)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.DIST.RANGE(100,0.5,110,40)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.DIST.RANGE(100,0.5,0,-1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.DIST.RANGE(100,0.5,0,110)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t// BINOM.INV\n\t\t\"BINOM.INV()\":             {\"#VALUE!\", \"BINOM.INV requires 3 numeric arguments\"},\n\t\t\"BINOM.INV(\\\"\\\",0.5,20%)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOM.INV(100,\\\"\\\",20%)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOM.INV(100,0.5,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"BINOM.INV(-1,0.5,20%)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.INV(100,-1,20%)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.INV(100,2,20%)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.INV(100,0.5,-1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.INV(100,0.5,2)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"BINOM.INV(1,1,20%)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t// CHIDIST\n\t\t\"CHIDIST()\":         {\"#VALUE!\", \"CHIDIST requires 2 numeric arguments\"},\n\t\t\"CHIDIST(\\\"\\\",3)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CHIDIST(0.5,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// CHIINV\n\t\t\"CHIINV()\":         {\"#VALUE!\", \"CHIINV requires 2 numeric arguments\"},\n\t\t\"CHIINV(\\\"\\\",1)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CHIINV(0.5,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CHIINV(0,1)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"CHIINV(2,1)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"CHIINV(0.5,0.5)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// CHISQ.DIST\n\t\t\"CHISQ.DIST()\":            {\"#VALUE!\", \"CHISQ.DIST requires 3 arguments\"},\n\t\t\"CHISQ.DIST(\\\"\\\",2,TRUE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CHISQ.DIST(3,\\\"\\\",TRUE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CHISQ.DIST(3,2,\\\"\\\")\":    {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CHISQ.DIST(-1,2,TRUE)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"CHISQ.DIST(3,0,TRUE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// CHISQ.DIST.RT\n\t\t\"CHISQ.DIST.RT()\":         {\"#VALUE!\", \"CHISQ.DIST.RT requires 2 numeric arguments\"},\n\t\t\"CHISQ.DIST.RT(\\\"\\\",3)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CHISQ.DIST.RT(0.5,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// CHISQ.INV\n\t\t\"CHISQ.INV()\":                {\"#VALUE!\", \"CHISQ.INV requires 2 numeric arguments\"},\n\t\t\"CHISQ.INV(\\\"\\\",1)\":          {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CHISQ.INV(0.5,\\\"\\\")\":        {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CHISQ.INV(-1,1)\":            {\"#NUM!\", \"#NUM!\"},\n\t\t\"CHISQ.INV(1,1)\":             {\"#NUM!\", \"#NUM!\"},\n\t\t\"CHISQ.INV(0.5,0.5)\":         {\"#NUM!\", \"#NUM!\"},\n\t\t\"CHISQ.INV(0.5,10000000001)\": {\"#NUM!\", \"#NUM!\"},\n\t\t// CHISQ.INV.RT\n\t\t\"CHISQ.INV.RT()\":         {\"#VALUE!\", \"CHISQ.INV.RT requires 2 numeric arguments\"},\n\t\t\"CHISQ.INV.RT(\\\"\\\",1)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CHISQ.INV.RT(0.5,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CHISQ.INV.RT(0,1)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"CHISQ.INV.RT(2,1)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"CHISQ.INV.RT(0.5,0.5)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// CONFIDENCE\n\t\t\"CONFIDENCE()\":               {\"#VALUE!\", \"CONFIDENCE requires 3 numeric arguments\"},\n\t\t\"CONFIDENCE(\\\"\\\",0.07,100)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CONFIDENCE(0.05,\\\"\\\",100)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CONFIDENCE(0.05,0.07,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CONFIDENCE(0,0.07,100)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"CONFIDENCE(1,0.07,100)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"CONFIDENCE(0.05,0,100)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"CONFIDENCE(0.05,0.07,0.5)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// CONFIDENCE.NORM\n\t\t\"CONFIDENCE.NORM()\":               {\"#VALUE!\", \"CONFIDENCE.NORM requires 3 numeric arguments\"},\n\t\t\"CONFIDENCE.NORM(\\\"\\\",0.07,100)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CONFIDENCE.NORM(0.05,\\\"\\\",100)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CONFIDENCE.NORM(0.05,0.07,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CONFIDENCE.NORM(0,0.07,100)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"CONFIDENCE.NORM(1,0.07,100)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"CONFIDENCE.NORM(0.05,0,100)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"CONFIDENCE.NORM(0.05,0.07,0.5)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// CORREL\n\t\t\"CORREL()\":            {\"#VALUE!\", \"CORREL requires 2 arguments\"},\n\t\t\"CORREL(A1:A3,B1:B5)\": {\"#N/A\", \"#N/A\"},\n\t\t\"CORREL(A1:A1,B1:B1)\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// CONFIDENCE.T\n\t\t\"CONFIDENCE.T()\":               {\"#VALUE!\", \"CONFIDENCE.T requires 3 arguments\"},\n\t\t\"CONFIDENCE.T(\\\"\\\",0.07,100)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CONFIDENCE.T(0.05,\\\"\\\",100)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CONFIDENCE.T(0.05,0.07,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CONFIDENCE.T(0,0.07,100)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"CONFIDENCE.T(1,0.07,100)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"CONFIDENCE.T(0.05,0,100)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"CONFIDENCE.T(0.05,0.07,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"CONFIDENCE.T(0.05,0.07,1)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// COUNTBLANK\n\t\t\"COUNTBLANK()\":    {\"#VALUE!\", \"COUNTBLANK requires 1 argument\"},\n\t\t\"COUNTBLANK(1,2)\": {\"#VALUE!\", \"COUNTBLANK requires 1 argument\"},\n\t\t// COUNTIF\n\t\t\"COUNTIF()\": {\"#VALUE!\", \"COUNTIF requires 2 arguments\"},\n\t\t// COUNTIFS\n\t\t\"COUNTIFS()\":              {\"#VALUE!\", \"COUNTIFS requires at least 2 arguments\"},\n\t\t\"COUNTIFS(A1:A9,2,D1:D9)\": {\"#N/A\", \"#N/A\"},\n\t\t// CRITBINOM\n\t\t\"CRITBINOM()\":             {\"#VALUE!\", \"CRITBINOM requires 3 numeric arguments\"},\n\t\t\"CRITBINOM(\\\"\\\",0.5,20%)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CRITBINOM(100,\\\"\\\",20%)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CRITBINOM(100,0.5,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CRITBINOM(-1,0.5,20%)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"CRITBINOM(100,-1,20%)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"CRITBINOM(100,2,20%)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"CRITBINOM(100,0.5,-1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"CRITBINOM(100,0.5,2)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"CRITBINOM(1,1,20%)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t// DEVSQ\n\t\t\"DEVSQ()\":      {\"#VALUE!\", \"DEVSQ requires at least 1 numeric argument\"},\n\t\t\"DEVSQ(D1:D2)\": {\"#N/A\", \"#N/A\"},\n\t\t// FISHER\n\t\t\"FISHER()\":         {\"#VALUE!\", \"FISHER requires 1 numeric argument\"},\n\t\t\"FISHER(2)\":        {\"#N/A\", \"#N/A\"},\n\t\t\"FISHER(\\\"2\\\")\":    {\"#N/A\", \"#N/A\"},\n\t\t\"FISHER(INT(-2)))\": {\"#N/A\", \"#N/A\"},\n\t\t\"FISHER(F1)\":       {\"#VALUE!\", \"FISHER requires 1 numeric argument\"},\n\t\t// FISHERINV\n\t\t\"FISHERINV()\":   {\"#VALUE!\", \"FISHERINV requires 1 numeric argument\"},\n\t\t\"FISHERINV(F1)\": {\"#VALUE!\", \"FISHERINV requires 1 numeric argument\"},\n\t\t// FORECAST\n\t\t\"FORECAST()\":                 {\"#VALUE!\", \"FORECAST requires 3 arguments\"},\n\t\t\"FORECAST(\\\"\\\",A1:A7,B1:B7)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FORECAST(1,A1:A2,B1:B1)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"FORECAST(1,A4,A4)\":          {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// FORECAST.LINEAR\n\t\t\"FORECAST.LINEAR()\":                 {\"#VALUE!\", \"FORECAST.LINEAR requires 3 arguments\"},\n\t\t\"FORECAST.LINEAR(\\\"\\\",A1:A7,B1:B7)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FORECAST.LINEAR(1,A1:A2,B1:B1)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"FORECAST.LINEAR(1,A4,A4)\":          {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// FREQUENCY\n\t\t\"FREQUENCY()\":           {\"#VALUE!\", \"FREQUENCY requires 2 arguments\"},\n\t\t\"FREQUENCY(NA(),A1:A3)\": {\"#N/A\", \"#N/A\"},\n\t\t\"FREQUENCY(A1:A3,NA())\": {\"#N/A\", \"#N/A\"},\n\t\t// GAMMA\n\t\t\"GAMMA()\":       {\"#VALUE!\", \"GAMMA requires 1 numeric argument\"},\n\t\t\"GAMMA(F1)\":     {\"#VALUE!\", \"GAMMA requires 1 numeric argument\"},\n\t\t\"GAMMA(0)\":      {\"#N/A\", \"#N/A\"},\n\t\t\"GAMMA(\\\"0\\\")\":  {\"#N/A\", \"#N/A\"},\n\t\t\"GAMMA(INT(0))\": {\"#N/A\", \"#N/A\"},\n\t\t// GAMMA.DIST\n\t\t\"GAMMA.DIST()\":               {\"#VALUE!\", \"GAMMA.DIST requires 4 arguments\"},\n\t\t\"GAMMA.DIST(\\\"\\\",3,2,FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMA.DIST(6,\\\"\\\",2,FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMA.DIST(6,3,\\\"\\\",FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMA.DIST(6,3,2,\\\"\\\")\":     {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMA.DIST(-1,3,2,FALSE)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"GAMMA.DIST(6,0,2,FALSE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"GAMMA.DIST(6,3,0,FALSE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// GAMMADIST\n\t\t\"GAMMADIST()\":               {\"#VALUE!\", \"GAMMADIST requires 4 arguments\"},\n\t\t\"GAMMADIST(\\\"\\\",3,2,FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMADIST(6,\\\"\\\",2,FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMADIST(6,3,\\\"\\\",FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMADIST(6,3,2,\\\"\\\")\":     {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMADIST(-1,3,2,FALSE)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"GAMMADIST(6,0,2,FALSE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"GAMMADIST(6,3,0,FALSE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// GAMMA.INV\n\t\t\"GAMMA.INV()\":           {\"#VALUE!\", \"GAMMA.INV requires 3 arguments\"},\n\t\t\"GAMMA.INV(\\\"\\\",3,2)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMA.INV(0.5,\\\"\\\",2)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMA.INV(0.5,3,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMA.INV(-1,3,2)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"GAMMA.INV(2,3,2)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"GAMMA.INV(0.5,0,2)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"GAMMA.INV(0.5,3,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// GAMMAINV\n\t\t\"GAMMAINV()\":           {\"#VALUE!\", \"GAMMAINV requires 3 arguments\"},\n\t\t\"GAMMAINV(\\\"\\\",3,2)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMAINV(0.5,\\\"\\\",2)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMAINV(0.5,3,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMAINV(-1,3,2)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"GAMMAINV(2,3,2)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"GAMMAINV(0.5,0,2)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"GAMMAINV(0.5,3,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// GAMMALN\n\t\t\"GAMMALN()\":       {\"#VALUE!\", \"GAMMALN requires 1 numeric argument\"},\n\t\t\"GAMMALN(F1)\":     {\"#VALUE!\", \"GAMMALN requires 1 numeric argument\"},\n\t\t\"GAMMALN(0)\":      {\"#N/A\", \"#N/A\"},\n\t\t\"GAMMALN(INT(0))\": {\"#N/A\", \"#N/A\"},\n\t\t// GAMMALN.PRECISE\n\t\t\"GAMMALN.PRECISE()\":     {\"#VALUE!\", \"GAMMALN.PRECISE requires 1 numeric argument\"},\n\t\t\"GAMMALN.PRECISE(\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GAMMALN.PRECISE(0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// GAUSS\n\t\t\"GAUSS()\":     {\"#VALUE!\", \"GAUSS requires 1 numeric argument\"},\n\t\t\"GAUSS(\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// GEOMEAN\n\t\t\"GEOMEAN()\":      {\"#VALUE!\", \"GEOMEAN requires at least 1 numeric argument\"},\n\t\t\"GEOMEAN(0)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"GEOMEAN(D1:D2)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"GEOMEAN(\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// HARMEAN\n\t\t\"HARMEAN()\":   {\"#VALUE!\", \"HARMEAN requires at least 1 argument\"},\n\t\t\"HARMEAN(-1)\": {\"#N/A\", \"#N/A\"},\n\t\t\"HARMEAN(0)\":  {\"#N/A\", \"#N/A\"},\n\t\t// HYPGEOM.DIST\n\t\t\"HYPGEOM.DIST()\":                  {\"#VALUE!\", \"HYPGEOM.DIST requires 5 arguments\"},\n\t\t\"HYPGEOM.DIST(\\\"\\\",4,4,12,FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"HYPGEOM.DIST(1,\\\"\\\",4,12,FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"HYPGEOM.DIST(1,4,\\\"\\\",12,FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"HYPGEOM.DIST(1,4,4,\\\"\\\",FALSE)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"HYPGEOM.DIST(1,4,4,12,\\\"\\\")\":     {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"HYPGEOM.DIST(-1,4,4,12,FALSE)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOM.DIST(2,1,4,12,FALSE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOM.DIST(2,4,1,12,FALSE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOM.DIST(2,2,2,1,FALSE)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOM.DIST(1,0,4,12,FALSE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOM.DIST(1,4,4,2,FALSE)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOM.DIST(1,4,0,12,FALSE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOM.DIST(1,4,4,0,FALSE)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t// HYPGEOMDIST\n\t\t\"HYPGEOMDIST()\":            {\"#VALUE!\", \"HYPGEOMDIST requires 4 numeric arguments\"},\n\t\t\"HYPGEOMDIST(\\\"\\\",4,4,12)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"HYPGEOMDIST(1,\\\"\\\",4,12)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"HYPGEOMDIST(1,4,\\\"\\\",12)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"HYPGEOMDIST(1,4,4,\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"HYPGEOMDIST(-1,4,4,12)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOMDIST(2,1,4,12)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOMDIST(2,4,1,12)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOMDIST(2,2,2,1)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOMDIST(1,0,4,12)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOMDIST(1,4,4,2)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOMDIST(1,4,0,12)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"HYPGEOMDIST(1,4,4,0)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t// INTERCEPT\n\t\t\"INTERCEPT()\":            {\"#VALUE!\", \"INTERCEPT requires 2 arguments\"},\n\t\t\"INTERCEPT(A1:A2,B1:B1)\": {\"#N/A\", \"#N/A\"},\n\t\t\"INTERCEPT(A4,A4)\":       {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// KURT\n\t\t\"KURT()\":          {\"#VALUE!\", \"KURT requires at least 1 argument\"},\n\t\t\"KURT(F1,INT(1))\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// EXPON.DIST\n\t\t\"EXPON.DIST()\":            {\"#VALUE!\", \"EXPON.DIST requires 3 arguments\"},\n\t\t\"EXPON.DIST(\\\"\\\",1,TRUE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EXPON.DIST(0,\\\"\\\",TRUE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EXPON.DIST(0,1,\\\"\\\")\":    {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EXPON.DIST(-1,1,TRUE)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"EXPON.DIST(1,0,TRUE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// EXPONDIST\n\t\t\"EXPONDIST()\":            {\"#VALUE!\", \"EXPONDIST requires 3 arguments\"},\n\t\t\"EXPONDIST(\\\"\\\",1,TRUE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EXPONDIST(0,\\\"\\\",TRUE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EXPONDIST(0,1,\\\"\\\")\":    {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EXPONDIST(-1,1,TRUE)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"EXPONDIST(1,0,TRUE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// FDIST\n\t\t\"FDIST()\":                {\"#VALUE!\", \"FDIST requires 3 arguments\"},\n\t\t\"FDIST(\\\"\\\",1,2)\":        {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FDIST(5,\\\"\\\",2)\":        {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FDIST(5,1,\\\"\\\")\":        {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FDIST(-1,1,2)\":          {\"#NUM!\", \"#NUM!\"},\n\t\t\"FDIST(5,0,2)\":           {\"#NUM!\", \"#NUM!\"},\n\t\t\"FDIST(5,10000000000,2)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"FDIST(5,1,0)\":           {\"#NUM!\", \"#NUM!\"},\n\t\t\"FDIST(5,1,10000000000)\": {\"#NUM!\", \"#NUM!\"},\n\t\t// F.DIST\n\t\t\"F.DIST()\":                     {\"#VALUE!\", \"F.DIST requires 4 arguments\"},\n\t\t\"F.DIST(\\\"\\\",2,5,TRUE)\":        {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.DIST(1,\\\"\\\",5,TRUE)\":        {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.DIST(1,2,\\\"\\\",TRUE)\":        {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.DIST(1,2,5,\\\"\\\")\":           {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.DIST(-1,1,2,TRUE)\":          {\"#NUM!\", \"#NUM!\"},\n\t\t\"F.DIST(5,0,2,TRUE)\":           {\"#NUM!\", \"#NUM!\"},\n\t\t\"F.DIST(5,10000000000,2,TRUE)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"F.DIST(5,1,0,TRUE)\":           {\"#NUM!\", \"#NUM!\"},\n\t\t\"F.DIST(5,1,10000000000,TRUE)\": {\"#NUM!\", \"#NUM!\"},\n\t\t// F.DIST.RT\n\t\t\"F.DIST.RT()\":                {\"#VALUE!\", \"F.DIST.RT requires 3 arguments\"},\n\t\t\"F.DIST.RT(\\\"\\\",1,2)\":        {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.DIST.RT(5,\\\"\\\",2)\":        {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.DIST.RT(5,1,\\\"\\\")\":        {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.DIST.RT(-1,1,2)\":          {\"#NUM!\", \"#NUM!\"},\n\t\t\"F.DIST.RT(5,0,2)\":           {\"#NUM!\", \"#NUM!\"},\n\t\t\"F.DIST.RT(5,10000000000,2)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"F.DIST.RT(5,1,0)\":           {\"#NUM!\", \"#NUM!\"},\n\t\t\"F.DIST.RT(5,1,10000000000)\": {\"#NUM!\", \"#NUM!\"},\n\t\t// F.INV\n\t\t\"F.INV()\":           {\"#VALUE!\", \"F.INV requires 3 arguments\"},\n\t\t\"F.INV(\\\"\\\",1,2)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.INV(0.2,\\\"\\\",2)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.INV(0.2,1,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.INV(0,1,2)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"F.INV(0.2,0.5,2)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"F.INV(0.2,1,0.5)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// FINV\n\t\t\"FINV()\":           {\"#VALUE!\", \"FINV requires 3 arguments\"},\n\t\t\"FINV(\\\"\\\",1,2)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FINV(0.2,\\\"\\\",2)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FINV(0.2,1,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FINV(0,1,2)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"FINV(0.2,0.5,2)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"FINV(0.2,1,0.5)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// F.INV.RT\n\t\t\"F.INV.RT()\":           {\"#VALUE!\", \"F.INV.RT requires 3 arguments\"},\n\t\t\"F.INV.RT(\\\"\\\",1,2)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.INV.RT(0.2,\\\"\\\",2)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.INV.RT(0.2,1,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"F.INV.RT(0,1,2)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"F.INV.RT(0.2,0.5,2)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"F.INV.RT(0.2,1,0.5)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// LOGINV\n\t\t\"LOGINV()\":             {\"#VALUE!\", \"LOGINV requires 3 arguments\"},\n\t\t\"LOGINV(\\\"\\\",2,0.2)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGINV(0.3,\\\"\\\",0.2)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGINV(0.3,2,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGINV(0,2,0.2)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"LOGINV(1,2,0.2)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"LOGINV(0.3,2,0)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t// LOGNORM.INV\n\t\t\"LOGNORM.INV()\":             {\"#VALUE!\", \"LOGNORM.INV requires 3 arguments\"},\n\t\t\"LOGNORM.INV(\\\"\\\",2,0.2)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGNORM.INV(0.3,\\\"\\\",0.2)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGNORM.INV(0.3,2,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGNORM.INV(0,2,0.2)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"LOGNORM.INV(1,2,0.2)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"LOGNORM.INV(0.3,2,0)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t// LOGNORM.DIST\n\t\t\"LOGNORM.DIST()\":                  {\"#VALUE!\", \"LOGNORM.DIST requires 4 arguments\"},\n\t\t\"LOGNORM.DIST(\\\"\\\",10,5,FALSE)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGNORM.DIST(0.5,\\\"\\\",5,FALSE)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGNORM.DIST(0.5,10,\\\"\\\",FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGNORM.DIST(0.5,10,5,\\\"\\\")\":     {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGNORM.DIST(0,10,5,FALSE)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"LOGNORM.DIST(0.5,10,0,FALSE)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// LOGNORMDIST\n\t\t\"LOGNORMDIST()\":           {\"#VALUE!\", \"LOGNORMDIST requires 3 arguments\"},\n\t\t\"LOGNORMDIST(\\\"\\\",10,5)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGNORMDIST(12,\\\"\\\",5)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGNORMDIST(12,10,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LOGNORMDIST(0,2,5)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"LOGNORMDIST(12,10,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// NEGBINOM.DIST\n\t\t\"NEGBINOM.DIST()\":                 {\"#VALUE!\", \"NEGBINOM.DIST requires 4 arguments\"},\n\t\t\"NEGBINOM.DIST(\\\"\\\",12,0.5,TRUE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NEGBINOM.DIST(6,\\\"\\\",0.5,TRUE)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NEGBINOM.DIST(6,12,\\\"\\\",TRUE)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NEGBINOM.DIST(6,12,0.5,\\\"\\\")\":    {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NEGBINOM.DIST(-1,12,0.5,TRUE)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"NEGBINOM.DIST(6,0,0.5,TRUE)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"NEGBINOM.DIST(6,12,-1,TRUE)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"NEGBINOM.DIST(6,12,2,TRUE)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t// NEGBINOMDIST\n\t\t\"NEGBINOMDIST()\":            {\"#VALUE!\", \"NEGBINOMDIST requires 3 arguments\"},\n\t\t\"NEGBINOMDIST(\\\"\\\",12,0.5)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NEGBINOMDIST(6,\\\"\\\",0.5)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NEGBINOMDIST(6,12,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NEGBINOMDIST(-1,12,0.5)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"NEGBINOMDIST(6,0,0.5)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"NEGBINOMDIST(6,12,-1)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"NEGBINOMDIST(6,12,2)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t// NORM.DIST\n\t\t\"NORM.DIST()\": {\"#VALUE!\", \"NORM.DIST requires 4 arguments\"},\n\t\t// NORMDIST\n\t\t\"NORMDIST()\":               {\"#VALUE!\", \"NORMDIST requires 4 arguments\"},\n\t\t\"NORMDIST(\\\"\\\",0,0,FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NORMDIST(0,\\\"\\\",0,FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NORMDIST(0,0,\\\"\\\",FALSE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NORMDIST(0,0,0,\\\"\\\")\":     {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NORMDIST(0,0,-1,TRUE)\":    {\"#N/A\", \"#N/A\"},\n\t\t// NORM.INV\n\t\t\"NORM.INV()\": {\"#VALUE!\", \"NORM.INV requires 3 arguments\"},\n\t\t// NORMINV\n\t\t\"NORMINV()\":         {\"#VALUE!\", \"NORMINV requires 3 arguments\"},\n\t\t\"NORMINV(\\\"\\\",0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NORMINV(0,\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NORMINV(0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NORMINV(0,0,-1)\":   {\"#N/A\", \"#N/A\"},\n\t\t\"NORMINV(-1,0,0)\":   {\"#N/A\", \"#N/A\"},\n\t\t\"NORMINV(0,0,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// NORM.S.DIST\n\t\t\"NORM.S.DIST()\": {\"#VALUE!\", \"NORM.S.DIST requires 2 numeric arguments\"},\n\t\t// NORMSDIST\n\t\t\"NORMSDIST()\": {\"#VALUE!\", \"NORMSDIST requires 1 numeric argument\"},\n\t\t// NORM.S.INV\n\t\t\"NORM.S.INV()\": {\"#VALUE!\", \"NORM.S.INV requires 1 numeric argument\"},\n\t\t// NORMSINV\n\t\t\"NORMSINV()\": {\"#VALUE!\", \"NORMSINV requires 1 numeric argument\"},\n\t\t// LARGE\n\t\t\"LARGE()\":           {\"#VALUE!\", \"LARGE requires 2 arguments\"},\n\t\t\"LARGE(A1:A5,0)\":    {\"#NUM!\", \"k should be > 0\"},\n\t\t\"LARGE(A1:A5,6)\":    {\"#NUM!\", \"k should be <= length of array\"},\n\t\t\"LARGE(A1:A5,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// MAX\n\t\t\"MAX()\":     {\"#VALUE!\", \"MAX requires at least 1 argument\"},\n\t\t\"MAX(NA())\": {\"#N/A\", \"#N/A\"},\n\t\t// MAXA\n\t\t\"MAXA()\":     {\"#VALUE!\", \"MAXA requires at least 1 argument\"},\n\t\t\"MAXA(NA())\": {\"#N/A\", \"#N/A\"},\n\t\t// MAXIFS\n\t\t\"MAXIFS()\":                         {\"#VALUE!\", \"MAXIFS requires at least 3 arguments\"},\n\t\t\"MAXIFS(F2:F4,A2:A4,\\\">0\\\",D2:D9)\": {\"#N/A\", \"#N/A\"},\n\t\t// MEDIAN\n\t\t\"MEDIAN()\":      {\"#VALUE!\", \"MEDIAN requires at least 1 argument\"},\n\t\t\"MEDIAN(\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"MEDIAN(D1:D2)\": {\"#NUM!\", \"#NUM!\"},\n\t\t// MIN\n\t\t\"MIN()\":     {\"#VALUE!\", \"MIN requires at least 1 argument\"},\n\t\t\"MIN(NA())\": {\"#N/A\", \"#N/A\"},\n\t\t// MINA\n\t\t\"MINA()\":     {\"#VALUE!\", \"MINA requires at least 1 argument\"},\n\t\t\"MINA(NA())\": {\"#N/A\", \"#N/A\"},\n\t\t// MINIFS\n\t\t\"MINIFS()\":                         {\"#VALUE!\", \"MINIFS requires at least 3 arguments\"},\n\t\t\"MINIFS(F2:F4,A2:A4,\\\"<0\\\",D2:D9)\": {\"#N/A\", \"#N/A\"},\n\t\t// PEARSON\n\t\t\"PEARSON()\":            {\"#VALUE!\", \"PEARSON requires 2 arguments\"},\n\t\t\"PEARSON(A1:A2,B1:B1)\": {\"#N/A\", \"#N/A\"},\n\t\t\"PEARSON(A4,A4)\":       {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// PERCENTILE.EXC\n\t\t\"PERCENTILE.EXC()\":           {\"#VALUE!\", \"PERCENTILE.EXC requires 2 arguments\"},\n\t\t\"PERCENTILE.EXC(A1:A4,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PERCENTILE.EXC(A1:A4,-1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"PERCENTILE.EXC(A1:A4,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"PERCENTILE.EXC(A1:A4,1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"PERCENTILE.EXC(NA(),0.5)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t// PERCENTILE.INC\n\t\t\"PERCENTILE.INC()\": {\"#VALUE!\", \"PERCENTILE.INC requires 2 arguments\"},\n\t\t// PERCENTILE\n\t\t\"PERCENTILE()\":       {\"#VALUE!\", \"PERCENTILE requires 2 arguments\"},\n\t\t\"PERCENTILE(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PERCENTILE(0,-1)\":   {\"#N/A\", \"#N/A\"},\n\t\t\"PERCENTILE(NA(),1)\": {\"#N/A\", \"#N/A\"},\n\t\t// PERCENTRANK.EXC\n\t\t\"PERCENTRANK.EXC()\":             {\"#VALUE!\", \"PERCENTRANK.EXC requires 2 or 3 arguments\"},\n\t\t\"PERCENTRANK.EXC(A1:B4,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PERCENTRANK.EXC(A1:B4,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PERCENTRANK.EXC(A1:B4,0,0)\":    {\"#NUM!\", \"PERCENTRANK.EXC arguments significance should be > 1\"},\n\t\t\"PERCENTRANK.EXC(A1:B4,6)\":      {\"#N/A\", \"#N/A\"},\n\t\t\"PERCENTRANK.EXC(NA(),1)\":       {\"#N/A\", \"#N/A\"},\n\t\t// PERCENTRANK.INC\n\t\t\"PERCENTRANK.INC()\":             {\"#VALUE!\", \"PERCENTRANK.INC requires 2 or 3 arguments\"},\n\t\t\"PERCENTRANK.INC(A1:B4,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PERCENTRANK.INC(A1:B4,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PERCENTRANK.INC(A1:B4,0,0)\":    {\"#NUM!\", \"PERCENTRANK.INC arguments significance should be > 1\"},\n\t\t\"PERCENTRANK.INC(A1:B4,6)\":      {\"#N/A\", \"#N/A\"},\n\t\t\"PERCENTRANK.INC(NA(),1)\":       {\"#N/A\", \"#N/A\"},\n\t\t// PERCENTRANK\n\t\t\"PERCENTRANK()\":             {\"#VALUE!\", \"PERCENTRANK requires 2 or 3 arguments\"},\n\t\t\"PERCENTRANK(A1:B4,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PERCENTRANK(A1:B4,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PERCENTRANK(A1:B4,0,0)\":    {\"#NUM!\", \"PERCENTRANK arguments significance should be > 1\"},\n\t\t\"PERCENTRANK(A1:B4,6)\":      {\"#N/A\", \"#N/A\"},\n\t\t\"PERCENTRANK(NA(),1)\":       {\"#N/A\", \"#N/A\"},\n\t\t// PERMUT\n\t\t\"PERMUT()\":       {\"#VALUE!\", \"PERMUT requires 2 numeric arguments\"},\n\t\t\"PERMUT(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PERMUT(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PERMUT(6,8)\":    {\"#N/A\", \"#N/A\"},\n\t\t// PERMUTATIONA\n\t\t\"PERMUTATIONA()\":       {\"#VALUE!\", \"PERMUTATIONA requires 2 numeric arguments\"},\n\t\t\"PERMUTATIONA(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PERMUTATIONA(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PERMUTATIONA(-1,0)\":   {\"#N/A\", \"#N/A\"},\n\t\t\"PERMUTATIONA(0,-1)\":   {\"#N/A\", \"#N/A\"},\n\t\t// PHI\n\t\t\"PHI()\":     {\"#VALUE!\", \"PHI requires 1 argument\"},\n\t\t\"PHI(\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// QUARTILE\n\t\t\"QUARTILE()\":           {\"#VALUE!\", \"QUARTILE requires 2 arguments\"},\n\t\t\"QUARTILE(A1:A4,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"QUARTILE(A1:A4,-1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"QUARTILE(A1:A4,5)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// QUARTILE.EXC\n\t\t\"QUARTILE.EXC()\":           {\"#VALUE!\", \"QUARTILE.EXC requires 2 arguments\"},\n\t\t\"QUARTILE.EXC(A1:A4,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"QUARTILE.EXC(A1:A4,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"QUARTILE.EXC(A1:A4,4)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// QUARTILE.INC\n\t\t\"QUARTILE.INC()\": {\"#VALUE!\", \"QUARTILE.INC requires 2 arguments\"},\n\t\t// RANK\n\t\t\"RANK()\":             {\"#VALUE!\", \"RANK requires at least 2 arguments\"},\n\t\t\"RANK(1,A1:B5,0,0)\":  {\"#VALUE!\", \"RANK requires at most 3 arguments\"},\n\t\t\"RANK(-1,A1:B5)\":     {\"#N/A\", \"#N/A\"},\n\t\t\"RANK(\\\"\\\",A1:B5)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"RANK(1,A1:B5,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// RANK.EQ\n\t\t\"RANK.EQ()\":             {\"#VALUE!\", \"RANK.EQ requires at least 2 arguments\"},\n\t\t\"RANK.EQ(1,A1:B5,0,0)\":  {\"#VALUE!\", \"RANK.EQ requires at most 3 arguments\"},\n\t\t\"RANK.EQ(-1,A1:B5)\":     {\"#N/A\", \"#N/A\"},\n\t\t\"RANK.EQ(\\\"\\\",A1:B5)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"RANK.EQ(1,A1:B5,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// RSQ\n\t\t\"RSQ()\":            {\"#VALUE!\", \"RSQ requires 2 arguments\"},\n\t\t\"RSQ(A1:A2,B1:B1)\": {\"#N/A\", \"#N/A\"},\n\t\t\"RSQ(A4,A4)\":       {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// SKEW\n\t\t\"SKEW()\":     {\"#VALUE!\", \"SKEW requires at least 1 argument\"},\n\t\t\"SKEW(\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"SKEW(0)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// SKEW.P\n\t\t\"SKEW.P()\":     {\"#VALUE!\", \"SKEW.P requires at least 1 argument\"},\n\t\t\"SKEW.P(\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"SKEW.P(0)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// SLOPE\n\t\t\"SLOPE()\":            {\"#VALUE!\", \"SLOPE requires 2 arguments\"},\n\t\t\"SLOPE(A1:A2,B1:B1)\": {\"#N/A\", \"#N/A\"},\n\t\t\"SLOPE(A4,A4)\":       {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// SMALL\n\t\t\"SMALL()\":           {\"#VALUE!\", \"SMALL requires 2 arguments\"},\n\t\t\"SMALL(A1:A5,0)\":    {\"#NUM!\", \"k should be > 0\"},\n\t\t\"SMALL(A1:A5,6)\":    {\"#NUM!\", \"k should be <= length of array\"},\n\t\t\"SMALL(A1:A5,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// STANDARDIZE\n\t\t\"STANDARDIZE()\":         {\"#VALUE!\", \"STANDARDIZE requires 3 arguments\"},\n\t\t\"STANDARDIZE(\\\"\\\",0,5)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"STANDARDIZE(0,\\\"\\\",5)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"STANDARDIZE(0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"STANDARDIZE(0,0,0)\":    {\"#N/A\", \"#N/A\"},\n\t\t// STDEVP\n\t\t\"STDEVP()\":     {\"#VALUE!\", \"STDEVP requires at least 1 argument\"},\n\t\t\"STDEVP(\\\"\\\")\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// STDEV.P\n\t\t\"STDEV.P()\":     {\"#VALUE!\", \"STDEV.P requires at least 1 argument\"},\n\t\t\"STDEV.P(\\\"\\\")\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// STDEVPA\n\t\t\"STDEVPA()\":     {\"#VALUE!\", \"STDEVPA requires at least 1 argument\"},\n\t\t\"STDEVPA(\\\"\\\")\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// T.DIST\n\t\t\"T.DIST()\":             {\"#VALUE!\", \"T.DIST requires 3 arguments\"},\n\t\t\"T.DIST(\\\"\\\",10,TRUE)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"T.DIST(1,\\\"\\\",TRUE)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"T.DIST(1,10,\\\"\\\")\":    {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"T.DIST(1,0,TRUE)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.DIST(1,-1,FALSE)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.DIST(1,0,FALSE)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// T.DIST.2T\n\t\t\"T.DIST.2T()\":        {\"#VALUE!\", \"T.DIST.2T requires 2 arguments\"},\n\t\t\"T.DIST.2T(\\\"\\\",10)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"T.DIST.2T(1,\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"T.DIST.2T(-1,10)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.DIST.2T(1,0)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t// T.DIST.RT\n\t\t\"T.DIST.RT()\":        {\"#VALUE!\", \"T.DIST.RT requires 2 arguments\"},\n\t\t\"T.DIST.RT(\\\"\\\",10)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"T.DIST.RT(1,\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"T.DIST.RT(1,0)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t// TDIST\n\t\t\"TDIST()\":          {\"#VALUE!\", \"TDIST requires 3 arguments\"},\n\t\t\"TDIST(\\\"\\\",10,1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TDIST(1,\\\"\\\",1)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TDIST(1,10,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TDIST(-1,10,1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"TDIST(1,0,1)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"TDIST(1,10,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// T.INV\n\t\t\"T.INV()\":          {\"#VALUE!\", \"T.INV requires 2 arguments\"},\n\t\t\"T.INV(\\\"\\\",10)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"T.INV(0.25,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"T.INV(0,10)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.INV(1,10)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.INV(0.25,0.5)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// T.INV.2T\n\t\t\"T.INV.2T()\":          {\"#VALUE!\", \"T.INV.2T requires 2 arguments\"},\n\t\t\"T.INV.2T(\\\"\\\",10)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"T.INV.2T(0.25,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"T.INV.2T(0,10)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.INV.2T(0.25,0.5)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// TINV\n\t\t\"TINV()\":          {\"#VALUE!\", \"TINV requires 2 arguments\"},\n\t\t\"TINV(\\\"\\\",10)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TINV(0.25,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TINV(0,10)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"TINV(0.25,0.5)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t// TRIMMEAN\n\t\t\"TRIMMEAN()\":        {\"#VALUE!\", \"TRIMMEAN requires 2 arguments\"},\n\t\t\"TRIMMEAN(A1,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TRIMMEAN(A1,1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"TRIMMEAN(A1,-1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t// VAR\n\t\t\"VAR()\": {\"#VALUE!\", \"VAR requires at least 1 argument\"},\n\t\t// VARA\n\t\t\"VARA()\": {\"#VALUE!\", \"VARA requires at least 1 argument\"},\n\t\t// VARP\n\t\t\"VARP()\":     {\"#VALUE!\", \"VARP requires at least 1 argument\"},\n\t\t\"VARP(\\\"\\\")\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// VAR.P\n\t\t\"VAR.P()\":     {\"#VALUE!\", \"VAR.P requires at least 1 argument\"},\n\t\t\"VAR.P(\\\"\\\")\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// VAR.S\n\t\t\"VAR.S()\": {\"#VALUE!\", \"VAR.S requires at least 1 argument\"},\n\t\t// VARPA\n\t\t\"VARPA()\": {\"#VALUE!\", \"VARPA requires at least 1 argument\"},\n\t\t// WEIBULL\n\t\t\"WEIBULL()\":               {\"#VALUE!\", \"WEIBULL requires 4 arguments\"},\n\t\t\"WEIBULL(\\\"\\\",1,1,FALSE)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WEIBULL(1,0,1,FALSE)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"WEIBULL(1,1,-1,FALSE)\":   {\"#N/A\", \"#N/A\"},\n\t\t// WEIBULL.DIST\n\t\t\"WEIBULL.DIST()\":               {\"#VALUE!\", \"WEIBULL.DIST requires 4 arguments\"},\n\t\t\"WEIBULL.DIST(\\\"\\\",1,1,FALSE)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WEIBULL.DIST(1,0,1,FALSE)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"WEIBULL.DIST(1,1,-1,FALSE)\":   {\"#N/A\", \"#N/A\"},\n\t\t// Z.TEST\n\t\t\"Z.TEST(A1)\":        {\"#VALUE!\", \"Z.TEST requires at least 2 arguments\"},\n\t\t\"Z.TEST(A1,0,0,0)\":  {\"#VALUE!\", \"Z.TEST accepts at most 3 arguments\"},\n\t\t\"Z.TEST(H1,0)\":      {\"#N/A\", \"#N/A\"},\n\t\t\"Z.TEST(A1,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"Z.TEST(A1,1)\":      {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"Z.TEST(A1,1,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// ZTEST\n\t\t\"ZTEST(A1)\":        {\"#VALUE!\", \"ZTEST requires at least 2 arguments\"},\n\t\t\"ZTEST(A1,0,0,0)\":  {\"#VALUE!\", \"ZTEST accepts at most 3 arguments\"},\n\t\t\"ZTEST(H1,0)\":      {\"#N/A\", \"#N/A\"},\n\t\t\"ZTEST(A1,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ZTEST(A1,1)\":      {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"ZTEST(A1,1,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// Information Functions\n\t\t// ERROR.TYPE\n\t\t\"ERROR.TYPE()\":  {\"#VALUE!\", \"ERROR.TYPE requires 1 argument\"},\n\t\t\"ERROR.TYPE(1)\": {\"#N/A\", \"#N/A\"},\n\t\t// ISBLANK\n\t\t\"ISBLANK(A1,A2)\": {\"#VALUE!\", \"ISBLANK requires 1 argument\"},\n\t\t// ISERR\n\t\t\"ISERR()\": {\"#VALUE!\", \"ISERR requires 1 argument\"},\n\t\t// ISERROR\n\t\t\"ISERROR()\": {\"#VALUE!\", \"ISERROR requires 1 argument\"},\n\t\t// ISEVEN\n\t\t\"ISEVEN()\":         {\"#VALUE!\", \"ISEVEN requires 1 argument\"},\n\t\t\"ISEVEN(\\\"text\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ISEVEN(A1:A2)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t// ISFORMULA\n\t\t\"ISFORMULA()\": {\"#VALUE!\", \"ISFORMULA requires 1 argument\"},\n\t\t// ISLOGICAL\n\t\t\"ISLOGICAL()\": {\"#VALUE!\", \"ISLOGICAL requires 1 argument\"},\n\t\t// ISNA\n\t\t\"ISNA()\": {\"#VALUE!\", \"ISNA requires 1 argument\"},\n\t\t// ISNONTEXT\n\t\t\"ISNONTEXT()\": {\"#VALUE!\", \"ISNONTEXT requires 1 argument\"},\n\t\t// ISNUMBER\n\t\t\"ISNUMBER()\": {\"#VALUE!\", \"ISNUMBER requires 1 argument\"},\n\t\t// ISODD\n\t\t\"ISODD()\":         {\"#VALUE!\", \"ISODD requires 1 argument\"},\n\t\t\"ISODD(\\\"text\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// ISREF\n\t\t\"ISREF()\": {\"#VALUE!\", \"ISREF requires 1 argument\"},\n\t\t// ISTEXT\n\t\t\"ISTEXT()\": {\"#VALUE!\", \"ISTEXT requires 1 argument\"},\n\t\t// N\n\t\t\"N()\":     {\"#VALUE!\", \"N requires 1 argument\"},\n\t\t\"N(NA())\": {\"#N/A\", \"#N/A\"},\n\t\t// NA\n\t\t\"NA()\":  {\"#N/A\", \"#N/A\"},\n\t\t\"NA(1)\": {\"#VALUE!\", \"NA accepts no arguments\"},\n\t\t// SHEET\n\t\t\"SHEET(\\\"\\\",\\\"\\\")\":  {\"#VALUE!\", \"SHEET accepts at most 1 argument\"},\n\t\t\"SHEET(\\\"Sheet2\\\")\": {\"#N/A\", \"#N/A\"},\n\t\t// SHEETS\n\t\t\"SHEETS(\\\"\\\",\\\"\\\")\":  {\"#VALUE!\", \"SHEETS accepts at most 1 argument\"},\n\t\t\"SHEETS(\\\"Sheet1\\\")\": {\"#N/A\", \"#N/A\"},\n\t\t// TYPE\n\t\t\"TYPE()\": {\"#VALUE!\", \"TYPE requires 1 argument\"},\n\t\t// T\n\t\t\"T()\":     {\"#VALUE!\", \"T requires 1 argument\"},\n\t\t\"T(NA())\": {\"#N/A\", \"#N/A\"},\n\t\t// Logical Functions\n\t\t// AND\n\t\t\"AND(\\\"text\\\")\":                          {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"AND(A1:B1)\":                             {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"AND(\\\"1\\\",\\\"TRUE\\\",\\\"FALSE\\\")\":          {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"AND()\":                                  {\"#VALUE!\", \"AND requires at least 1 argument\"},\n\t\t\"AND(1\" + strings.Repeat(\",1\", 30) + \")\": {\"#VALUE!\", \"AND accepts at most 30 arguments\"},\n\t\t// FALSE\n\t\t\"FALSE(A1)\": {\"#VALUE!\", \"FALSE takes no arguments\"},\n\t\t// IFERROR\n\t\t\"IFERROR()\": {\"#VALUE!\", \"IFERROR requires 2 arguments\"},\n\t\t// IFNA\n\t\t\"IFNA()\": {\"#VALUE!\", \"IFNA requires 2 arguments\"},\n\t\t// IFS\n\t\t\"IFS()\":            {\"#VALUE!\", \"IFS requires at least 2 arguments\"},\n\t\t\"IFS(FALSE,FALSE)\": {\"#N/A\", \"#N/A\"},\n\t\t// NOT\n\t\t\"NOT()\":      {\"#VALUE!\", \"NOT requires 1 argument\"},\n\t\t\"NOT(NOT())\": {\"#VALUE!\", \"NOT requires 1 argument\"},\n\t\t\"NOT(\\\"\\\")\":  {\"#VALUE!\", \"NOT expects 1 boolean or numeric argument\"},\n\t\t// OR\n\t\t\"OR(\\\"text\\\")\":                          {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"OR(\\\"1\\\",\\\"TRUE\\\",\\\"FALSE\\\")\":          {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"OR()\":                                  {\"#VALUE!\", \"OR requires at least 1 argument\"},\n\t\t\"OR(1\" + strings.Repeat(\",1\", 30) + \")\": {\"#VALUE!\", \"OR accepts at most 30 arguments\"},\n\t\t// SWITCH\n\t\t\"SWITCH()\":      {\"#VALUE!\", \"SWITCH requires at least 3 arguments\"},\n\t\t\"SWITCH(0,1,2)\": {\"#N/A\", \"#N/A\"},\n\t\t// TRUE\n\t\t\"TRUE(A1)\": {\"#VALUE!\", \"TRUE takes no arguments\"},\n\t\t// XOR\n\t\t\"XOR()\":              {\"#VALUE!\", \"XOR requires at least 1 argument\"},\n\t\t\"XOR(\\\"1\\\")\":         {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XOR(\\\"text\\\")\":      {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XOR(XOR(\\\"text\\\"))\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// Date and Time Functions\n\t\t// DATE\n\t\t\"DATE()\":                 {\"#VALUE!\", \"DATE requires 3 number arguments\"},\n\t\t\"DATE(\\\"text\\\",10,21)\":   {\"#VALUE!\", \"DATE requires 3 number arguments\"},\n\t\t\"DATE(2020,\\\"text\\\",21)\": {\"#VALUE!\", \"DATE requires 3 number arguments\"},\n\t\t\"DATE(2020,10,\\\"text\\\")\": {\"#VALUE!\", \"DATE requires 3 number arguments\"},\n\t\t// DATEDIF\n\t\t\"DATEDIF()\":                  {\"#VALUE!\", \"DATEDIF requires 3 number arguments\"},\n\t\t\"DATEDIF(\\\"\\\",\\\"\\\",\\\"\\\")\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DATEDIF(43891,43101,\\\"Y\\\")\": {\"#NUM!\", \"start_date > end_date\"},\n\t\t\"DATEDIF(43101,43891,\\\"x\\\")\": {\"#VALUE!\", \"DATEDIF has invalid unit\"},\n\t\t// DATEVALUE\n\t\t\"DATEVALUE()\":             {\"#VALUE!\", \"DATEVALUE requires 1 argument\"},\n\t\t\"DATEVALUE(\\\"01/01\\\")\":    {\"#VALUE!\", \"#VALUE!\"}, // valid in Excel, which uses years by the system date\n\t\t\"DATEVALUE(\\\"1900-0-0\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// DAY\n\t\t\"DAY()\":         {\"#VALUE!\", \"DAY requires exactly 1 argument\"},\n\t\t\"DAY(-1)\":       {\"#NUM!\", \"DAY only accepts positive argument\"},\n\t\t\"DAY(0,0)\":      {\"#VALUE!\", \"DAY requires exactly 1 argument\"},\n\t\t\"DAY(\\\"text\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 2020 9223372036854775808 AM\\\")\":                   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 2020 9223372036854775808:00 AM\\\")\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 2020 00:9223372036854775808 AM\\\")\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 2020 9223372036854775808:00.0 AM\\\")\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 2020 0:1\" + strings.Repeat(\"0\", 309) + \".0 AM\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 2020 9223372036854775808:00:00 AM\\\")\":             {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 2020 0:9223372036854775808:0 AM\\\")\":               {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 2020 0:0:1\" + strings.Repeat(\"0\", 309) + \" AM\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 2020 0:61:0 AM\\\")\":                                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 2020 0:00:60 AM\\\")\":                               {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 2020 24:00:00\\\")\":                                 {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 2020 00:00:10001\\\")\":                              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"9223372036854775808/25/2020\\\")\":                               {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"01/9223372036854775808/2020\\\")\":                               {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"01/25/9223372036854775808\\\")\":                                 {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"01/25/10000\\\")\":                                               {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"01/25/100\\\")\":                                                 {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 9223372036854775808, 2020\\\")\":                         {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 9223372036854775808\\\")\":                           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 10000\\\")\":                                         {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"January 25, 100\\\")\":                                           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"9223372036854775808-25-2020\\\")\":                               {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"01-9223372036854775808-2020\\\")\":                               {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"01-25-9223372036854775808\\\")\":                                 {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"1900-0-0\\\")\":                                                  {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"14-25-1900\\\")\":                                                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"3-January-9223372036854775808\\\")\":                             {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"9223372036854775808-January-1900\\\")\":                          {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAY(\\\"0-January-1900\\\")\":                                            {\"#VALUE!\", \"#VALUE!\"},\n\t\t// DAYS\n\t\t\"DAYS()\":       {\"#VALUE!\", \"DAYS requires 2 arguments\"},\n\t\t\"DAYS(\\\"\\\",0)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAYS(0,\\\"\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAYS(NA(),0)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAYS(0,NA())\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// DAYS360\n\t\t\"DAYS360(\\\"12/12/1999\\\")\":                           {\"#VALUE!\", \"DAYS360 requires at least 2 arguments\"},\n\t\t\"DAYS360(\\\"12/12/1999\\\", \\\"11/30/1999\\\",TRUE,\\\"\\\")\": {\"#VALUE!\", \"DAYS360 requires at most 3 arguments\"},\n\t\t\"DAYS360(\\\"12/12/1999\\\", \\\"11/30/1999\\\",\\\"\\\")\":      {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DAYS360(\\\"12/12/1999\\\", \\\"\\\")\":                     {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAYS360(\\\"\\\", \\\"11/30/1999\\\")\":                     {\"#VALUE!\", \"#VALUE!\"},\n\t\t// EDATE\n\t\t\"EDATE()\":                      {\"#VALUE!\", \"EDATE requires 2 arguments\"},\n\t\t\"EDATE(0,\\\"\\\")\":                {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EDATE(-1,0)\":                  {\"#NUM!\", \"#NUM!\"},\n\t\t\"EDATE(\\\"\\\",0)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"EDATE(\\\"January 25, 100\\\",0)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// EOMONTH\n\t\t\"EOMONTH()\":                      {\"#VALUE!\", \"EOMONTH requires 2 arguments\"},\n\t\t\"EOMONTH(0,\\\"\\\")\":                {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EOMONTH(-1,0)\":                  {\"#NUM!\", \"#NUM!\"},\n\t\t\"EOMONTH(\\\"\\\",0)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"EOMONTH(\\\"January 25, 100\\\",0)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// HOUR\n\t\t\"HOUR()\":             {\"#VALUE!\", \"HOUR requires exactly 1 argument\"},\n\t\t\"HOUR(-1)\":           {\"#NUM!\", \"HOUR only accepts positive argument\"},\n\t\t\"HOUR(\\\"\\\")\":         {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"HOUR(\\\"25:10:55\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// ISOWEEKNUM\n\t\t\"ISOWEEKNUM()\":                    {\"#VALUE!\", \"ISOWEEKNUM requires 1 argument\"},\n\t\t\"ISOWEEKNUM(\\\"\\\")\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ISOWEEKNUM(\\\"January 25, 100\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ISOWEEKNUM(-1)\":                  {\"#NUM!\", \"#NUM!\"},\n\t\t// MINUTE\n\t\t\"MINUTE()\":             {\"#VALUE!\", \"MINUTE requires exactly 1 argument\"},\n\t\t\"MINUTE(-1)\":           {\"#NUM!\", \"MINUTE only accepts positive argument\"},\n\t\t\"MINUTE(\\\"\\\")\":         {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MINUTE(\\\"13:60:55\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// MONTH\n\t\t\"MONTH()\":                    {\"#VALUE!\", \"MONTH requires exactly 1 argument\"},\n\t\t\"MONTH(0,0)\":                 {\"#VALUE!\", \"MONTH requires exactly 1 argument\"},\n\t\t\"MONTH(-1)\":                  {\"#NUM!\", \"MONTH only accepts positive argument\"},\n\t\t\"MONTH(\\\"text\\\")\":            {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MONTH(\\\"January 25, 100\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// YEAR\n\t\t\"YEAR()\":                    {\"#VALUE!\", \"YEAR requires exactly 1 argument\"},\n\t\t\"YEAR(0,0)\":                 {\"#VALUE!\", \"YEAR requires exactly 1 argument\"},\n\t\t\"YEAR(-1)\":                  {\"#NUM!\", \"YEAR only accepts positive argument\"},\n\t\t\"YEAR(\\\"text\\\")\":            {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"YEAR(\\\"January 25, 100\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// YEARFRAC\n\t\t\"YEARFRAC()\":                 {\"#VALUE!\", \"YEARFRAC requires 3 or 4 arguments\"},\n\t\t\"YEARFRAC(42005,42094,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t\"YEARFRAC(\\\"\\\",42094,5)\":     {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"YEARFRAC(42005,\\\"\\\",5)\":     {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"YEARFRAC(42005,42094,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// NOW\n\t\t\"NOW(A1)\": {\"#VALUE!\", \"NOW accepts no arguments\"},\n\t\t// SECOND\n\t\t\"SECOND()\":          {\"#VALUE!\", \"SECOND requires exactly 1 argument\"},\n\t\t\"SECOND(-1)\":        {\"#NUM!\", \"SECOND only accepts positive argument\"},\n\t\t\"SECOND(\\\"\\\")\":      {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"SECOND(\\\"25:55\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// TIME\n\t\t\"TIME()\":         {\"#VALUE!\", \"TIME requires 3 number arguments\"},\n\t\t\"TIME(\\\"\\\",0,0)\": {\"#VALUE!\", \"TIME requires 3 number arguments\"},\n\t\t\"TIME(0,0,-1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t// TIMEVALUE\n\t\t\"TIMEVALUE()\":          {\"#VALUE!\", \"TIMEVALUE requires exactly 1 argument\"},\n\t\t\"TIMEVALUE(1)\":         {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TIMEVALUE(-1)\":        {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TIMEVALUE(\\\"25:55\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// TODAY\n\t\t\"TODAY(A1)\": {\"#VALUE!\", \"TODAY accepts no arguments\"},\n\t\t// WEEKDAY\n\t\t\"WEEKDAY()\":                    {\"#VALUE!\", \"WEEKDAY requires at least 1 argument\"},\n\t\t\"WEEKDAY(0,1,0)\":               {\"#VALUE!\", \"WEEKDAY allows at most 2 arguments\"},\n\t\t\"WEEKDAY(0,\\\"\\\")\":              {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"WEEKDAY(\\\"\\\",1)\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WEEKDAY(0,0)\":                 {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WEEKDAY(\\\"January 25, 100\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WEEKDAY(-1,1)\":                {\"#NUM!\", \"#NUM!\"},\n\t\t// WEEKNUM\n\t\t\"WEEKNUM()\":                    {\"#VALUE!\", \"WEEKNUM requires at least 1 argument\"},\n\t\t\"WEEKNUM(0,1,0)\":               {\"#VALUE!\", \"WEEKNUM allows at most 2 arguments\"},\n\t\t\"WEEKNUM(0,\\\"\\\")\":              {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"WEEKNUM(\\\"\\\",1)\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WEEKNUM(\\\"January 25, 100\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WEEKNUM(0,0)\":                 {\"#NUM!\", \"#NUM!\"},\n\t\t\"WEEKNUM(-1,1)\":                {\"#NUM!\", \"#NUM!\"},\n\t\t// Text Functions\n\t\t// ARRAYTOTEXT\n\t\t\"ARRAYTOTEXT()\":        {\"#VALUE!\", \"ARRAYTOTEXT requires at least 1 argument\"},\n\t\t\"ARRAYTOTEXT(A1,0,0)\":  {\"#VALUE!\", \"ARRAYTOTEXT allows at most 2 arguments\"},\n\t\t\"ARRAYTOTEXT(A1,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ARRAYTOTEXT(A1,2)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t// BAHTTEXT\n\t\t\"BAHTTEXT()\":     {\"#VALUE!\", \"BAHTTEXT requires 1 numeric argument\"},\n\t\t\"BAHTTEXT(\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// CHAR\n\t\t\"CHAR()\":     {\"#VALUE!\", \"CHAR requires 1 argument\"},\n\t\t\"CHAR(-1)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"CHAR(256)\":  {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"CHAR(\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// CLEAN\n\t\t\"CLEAN()\":    {\"#VALUE!\", \"CLEAN requires 1 argument\"},\n\t\t\"CLEAN(1,2)\": {\"#VALUE!\", \"CLEAN requires 1 argument\"},\n\t\t// CODE\n\t\t\"CODE()\":    {\"#VALUE!\", \"CODE requires 1 argument\"},\n\t\t\"CODE(1,2)\": {\"#VALUE!\", \"CODE requires 1 argument\"},\n\t\t// CONCAT\n\t\t\"CONCAT(NA())\":  {\"#N/A\", \"#N/A\"},\n\t\t\"CONCAT(1,1/0)\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// CONCATENATE\n\t\t\"CONCATENATE(NA())\":  {\"#N/A\", \"#N/A\"},\n\t\t\"CONCATENATE(1,1/0)\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// DBCS\n\t\t\"DBCS(NA())\": {\"#N/A\", \"#N/A\"},\n\t\t\"DBCS()\":     {\"#VALUE!\", \"DBCS requires 1 argument\"},\n\t\t// EXACT\n\t\t\"EXACT()\":      {\"#VALUE!\", \"EXACT requires 2 arguments\"},\n\t\t\"EXACT(1,2,3)\": {\"#VALUE!\", \"EXACT requires 2 arguments\"},\n\t\t// FIXED\n\t\t\"FIXED()\":         {\"#VALUE!\", \"FIXED requires at least 1 argument\"},\n\t\t\"FIXED(0,1,2,3)\":  {\"#VALUE!\", \"FIXED allows at most 3 arguments\"},\n\t\t\"FIXED(\\\"\\\")\":     {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FIXED(0,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FIXED(0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t// FIND\n\t\t\"FIND()\":                 {\"#VALUE!\", \"FIND requires at least 2 arguments\"},\n\t\t\"FIND(1,2,3,4)\":          {\"#VALUE!\", \"FIND allows at most 3 arguments\"},\n\t\t\"FIND(\\\"x\\\",\\\"\\\")\":       {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"FIND(\\\"x\\\",\\\"x\\\",-1)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"FIND(\\\"x\\\",\\\"x\\\",\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// FINDB\n\t\t\"FINDB()\":                 {\"#VALUE!\", \"FINDB requires at least 2 arguments\"},\n\t\t\"FINDB(1,2,3,4)\":          {\"#VALUE!\", \"FINDB allows at most 3 arguments\"},\n\t\t\"FINDB(\\\"x\\\",\\\"\\\")\":       {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"FINDB(\\\"x\\\",\\\"x\\\",-1)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"FINDB(\\\"x\\\",\\\"x\\\",\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// LEFT\n\t\t\"LEFT()\":          {\"#VALUE!\", \"LEFT requires at least 1 argument\"},\n\t\t\"LEFT(\\\"\\\",2,3)\":  {\"#VALUE!\", \"LEFT allows at most 2 arguments\"},\n\t\t\"LEFT(\\\"\\\",\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LEFT(\\\"\\\",-1)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t// LEFTB\n\t\t\"LEFTB()\":          {\"#VALUE!\", \"LEFTB requires at least 1 argument\"},\n\t\t\"LEFTB(\\\"\\\",2,3)\":  {\"#VALUE!\", \"LEFTB allows at most 2 arguments\"},\n\t\t\"LEFTB(\\\"\\\",\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"LEFTB(\\\"\\\",-1)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t// LEN\n\t\t\"LEN()\": {\"#VALUE!\", \"LEN requires 1 string argument\"},\n\t\t// LENB\n\t\t\"LENB()\": {\"#VALUE!\", \"LENB requires 1 string argument\"},\n\t\t// LOWER\n\t\t\"LOWER()\":    {\"#VALUE!\", \"LOWER requires 1 argument\"},\n\t\t\"LOWER(1,2)\": {\"#VALUE!\", \"LOWER requires 1 argument\"},\n\t\t// MID\n\t\t\"MID()\":            {\"#VALUE!\", \"MID requires 3 arguments\"},\n\t\t\"MID(\\\"\\\",0,1)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MID(\\\"\\\",1,-1)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MID(\\\"\\\",\\\"\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"MID(\\\"\\\",1,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// MIDB\n\t\t\"MIDB()\":            {\"#VALUE!\", \"MIDB requires 3 arguments\"},\n\t\t\"MIDB(\\\"\\\",0,1)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MIDB(\\\"\\\",1,-1)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MIDB(\\\"\\\",\\\"\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"MIDB(\\\"\\\",1,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// PROPER\n\t\t\"PROPER()\":    {\"#VALUE!\", \"PROPER requires 1 argument\"},\n\t\t\"PROPER(1,2)\": {\"#VALUE!\", \"PROPER requires 1 argument\"},\n\t\t// REPLACE\n\t\t\"REPLACE()\":                           {\"#VALUE!\", \"REPLACE requires 4 arguments\"},\n\t\t\"REPLACE(\\\"text\\\",0,4,\\\"string\\\")\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"REPLACE(\\\"text\\\",\\\"\\\",0,\\\"string\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"REPLACE(\\\"text\\\",1,\\\"\\\",\\\"string\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// REPLACEB\n\t\t\"REPLACEB()\":                           {\"#VALUE!\", \"REPLACEB requires 4 arguments\"},\n\t\t\"REPLACEB(\\\"text\\\",0,4,\\\"string\\\")\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"REPLACEB(\\\"text\\\",\\\"\\\",0,\\\"string\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"REPLACEB(\\\"text\\\",1,\\\"\\\",\\\"string\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// REPT\n\t\t\"REPT()\":            {\"#VALUE!\", \"REPT requires 2 arguments\"},\n\t\t\"REPT(INT(0),2)\":    {\"#VALUE!\", \"REPT requires first argument to be a string\"},\n\t\t\"REPT(\\\"*\\\",\\\"*\\\")\": {\"#VALUE!\", \"REPT requires second argument to be a number\"},\n\t\t\"REPT(\\\"*\\\",-1)\":    {\"#VALUE!\", \"REPT requires second argument to be >= 0\"},\n\t\t// RIGHT\n\t\t\"RIGHT()\":          {\"#VALUE!\", \"RIGHT requires at least 1 argument\"},\n\t\t\"RIGHT(\\\"\\\",2,3)\":  {\"#VALUE!\", \"RIGHT allows at most 2 arguments\"},\n\t\t\"RIGHT(\\\"\\\",\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"RIGHT(\\\"\\\",-1)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t// RIGHTB\n\t\t\"RIGHTB()\":          {\"#VALUE!\", \"RIGHTB requires at least 1 argument\"},\n\t\t\"RIGHTB(\\\"\\\",2,3)\":  {\"#VALUE!\", \"RIGHTB allows at most 2 arguments\"},\n\t\t\"RIGHTB(\\\"\\\",\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"RIGHTB(\\\"\\\",-1)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t// SUBSTITUTE\n\t\t\"SUBSTITUTE()\":                    {\"#VALUE!\", \"SUBSTITUTE requires 3 or 4 arguments\"},\n\t\t\"SUBSTITUTE(\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"SUBSTITUTE(\\\"\\\",\\\"\\\",\\\"\\\",0)\":    {\"#VALUE!\", \"instance_num should be > 0\"},\n\t\t// TEXT\n\t\t\"TEXT()\":          {\"#VALUE!\", \"TEXT requires 2 arguments\"},\n\t\t\"TEXT(NA(),\\\"\\\")\": {\"#N/A\", \"#N/A\"},\n\t\t\"TEXT(0,NA())\":    {\"#N/A\", \"#N/A\"},\n\t\t// TEXTAFTER\n\t\t\"TEXTAFTER()\": {\"#VALUE!\", \"TEXTAFTER requires at least 2 arguments\"},\n\t\t\"TEXTAFTER(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",1,0,0,\\\"\\\",0)\": {\"#VALUE!\", \"TEXTAFTER accepts at most 6 arguments\"},\n\t\t\"TEXTAFTER(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",\\\"\\\")\":         {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TEXTAFTER(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",1,\\\"\\\")\":       {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TEXTAFTER(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",1,0,\\\"\\\")\":     {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TEXTAFTER(\\\"\\\",\\\"hood\\\")\":                                         {\"#N/A\", \"#N/A\"},\n\t\t\"TEXTAFTER(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",0)\":            {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TEXTAFTER(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",28)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t// TEXTBEFORE\n\t\t\"TEXTBEFORE()\": {\"#VALUE!\", \"TEXTBEFORE requires at least 2 arguments\"},\n\t\t\"TEXTBEFORE(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",1,0,0,\\\"\\\",0)\": {\"#VALUE!\", \"TEXTBEFORE accepts at most 6 arguments\"},\n\t\t\"TEXTBEFORE(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",\\\"\\\")\":         {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TEXTBEFORE(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",1,\\\"\\\")\":       {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TEXTBEFORE(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",1,0,\\\"\\\")\":     {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TEXTBEFORE(\\\"\\\",\\\"hood\\\")\":                                         {\"#N/A\", \"#N/A\"},\n\t\t\"TEXTBEFORE(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",0)\":            {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TEXTBEFORE(\\\"Red riding hood's, red hood\\\",\\\"hood\\\",28)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t// TEXTJOIN\n\t\t\"TEXTJOIN()\":               {\"#VALUE!\", \"TEXTJOIN requires at least 3 arguments\"},\n\t\t\"TEXTJOIN(\\\"\\\",\\\"\\\",1)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TEXTJOIN(\\\"\\\",TRUE,NA())\": {\"#N/A\", \"#N/A\"},\n\t\t\"TEXTJOIN(\\\"\\\",TRUE,\" + strings.Repeat(\"0,\", 250) + \",0)\": {\"#VALUE!\", \"TEXTJOIN accepts at most 252 arguments\"},\n\t\t\"TEXTJOIN(\\\",\\\",FALSE,REPT(\\\"*\\\",32768))\":                 {\"#VALUE!\", \"TEXTJOIN function exceeds 32767 characters\"},\n\t\t\"TEXTJOIN(\\\"\\\",FALSE,REPT(\\\"\\U0001F600\\\",16384))\":         {\"#VALUE!\", \"TEXTJOIN function exceeds 32767 characters\"},\n\t\t// TRIM\n\t\t\"TRIM()\":    {\"#VALUE!\", \"TRIM requires 1 argument\"},\n\t\t\"TRIM(1,2)\": {\"#VALUE!\", \"TRIM requires 1 argument\"},\n\t\t// UNICHAR\n\t\t\"UNICHAR()\":      {\"#VALUE!\", \"UNICHAR requires 1 argument\"},\n\t\t\"UNICHAR(\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"UNICHAR(55296)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"UNICHAR(0)\":     {\"#VALUE!\", \"#VALUE!\"},\n\t\t// UNICODE\n\t\t\"UNICODE()\":     {\"#VALUE!\", \"UNICODE requires 1 argument\"},\n\t\t\"UNICODE(\\\"\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// VALUE\n\t\t\"VALUE()\":     {\"#VALUE!\", \"VALUE requires 1 argument\"},\n\t\t\"VALUE(\\\"\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// VALUETOTEXT\n\t\t\"VALUETOTEXT()\":        {\"#VALUE!\", \"VALUETOTEXT requires at least 1 argument\"},\n\t\t\"VALUETOTEXT(A1,0,0)\":  {\"#VALUE!\", \"VALUETOTEXT allows at most 2 arguments\"},\n\t\t\"VALUETOTEXT(A1,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"VALUETOTEXT(A1,2)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t// UPPER\n\t\t\"UPPER()\":    {\"#VALUE!\", \"UPPER requires 1 argument\"},\n\t\t\"UPPER(1,2)\": {\"#VALUE!\", \"UPPER requires 1 argument\"},\n\t\t// Conditional Functions\n\t\t// IF\n\t\t\"IF()\":        {\"#VALUE!\", \"IF requires at least 1 argument\"},\n\t\t\"IF(0,1,2,3)\": {\"#VALUE!\", \"IF accepts at most 3 arguments\"},\n\t\t\"IF(D1,1,2)\":  {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"Month\\\": invalid syntax\"},\n\t\t// Excel Lookup and Reference Functions\n\t\t// ADDRESS\n\t\t\"ADDRESS()\":                        {\"#VALUE!\", \"ADDRESS requires at least 2 arguments\"},\n\t\t\"ADDRESS(1,1,1,TRUE,\\\"Sheet1\\\",0)\": {\"#VALUE!\", \"ADDRESS requires at most 5 arguments\"},\n\t\t\"ADDRESS(\\\"\\\",1,1,TRUE)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ADDRESS(1,\\\"\\\",1,TRUE)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ADDRESS(1,1,\\\"\\\",TRUE)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ADDRESS(1,1,1,\\\"\\\")\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ADDRESS(1,1,0,TRUE)\":              {\"#NUM!\", \"#NUM!\"},\n\t\t\"ADDRESS(1,16385,2,TRUE)\":          {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ADDRESS(1,16385,3,TRUE)\":          {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ADDRESS(1048577,1,1,TRUE)\":        {\"#VALUE!\", \"#VALUE!\"},\n\t\t// CHOOSE\n\t\t\"CHOOSE()\":                {\"#VALUE!\", \"CHOOSE requires 2 arguments\"},\n\t\t\"CHOOSE(\\\"index_num\\\",0)\": {\"#VALUE!\", \"CHOOSE requires first argument of type number\"},\n\t\t\"CHOOSE(2,0)\":             {\"#VALUE!\", \"index_num should be <= to the number of values\"},\n\t\t\"CHOOSE(1,NA())\":          {\"#N/A\", \"#N/A\"},\n\t\t// COLUMN\n\t\t\"COLUMN(1,2)\":                 {\"#VALUE!\", \"COLUMN requires at most 1 argument\"},\n\t\t\"COLUMN(\\\"\\\")\":                {\"#VALUE!\", \"invalid reference\"},\n\t\t\"COLUMN(Sheet1)\":              {\"#NAME?\", \"invalid reference\"},\n\t\t\"COLUMN(Sheet1!A1!B1)\":        {\"#NAME?\", \"invalid reference\"},\n\t\t\"COLUMN(Sheet1!A1:Sheet2!A2)\": {\"#NAME?\", \"invalid reference\"},\n\t\t\"COLUMN(Sheet1!A1:1A)\":        {\"#NAME?\", \"invalid reference\"},\n\t\t// COLUMNS\n\t\t\"COLUMNS()\":              {\"#VALUE!\", \"COLUMNS requires 1 argument\"},\n\t\t\"COLUMNS(1)\":             {\"#VALUE!\", \"invalid reference\"},\n\t\t\"COLUMNS(\\\"\\\")\":          {\"#VALUE!\", \"invalid reference\"},\n\t\t\"COLUMNS(Sheet1)\":        {\"#NAME?\", \"invalid reference\"},\n\t\t\"COLUMNS(Sheet1!A1!B1)\":  {\"#NAME?\", \"invalid reference\"},\n\t\t\"COLUMNS(Sheet1!Sheet1)\": {\"#NAME?\", \"invalid reference\"},\n\t\t// FORMULATEXT\n\t\t\"FORMULATEXT()\":  {\"#VALUE!\", \"FORMULATEXT requires 1 argument\"},\n\t\t\"FORMULATEXT(1)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// HLOOKUP\n\t\t\"HLOOKUP()\":                     {\"#VALUE!\", \"HLOOKUP requires at least 3 arguments\"},\n\t\t\"HLOOKUP(D2,D1,1,FALSE)\":        {\"#VALUE!\", \"HLOOKUP requires second argument of table array\"},\n\t\t\"HLOOKUP(D2,D:D,FALSE,FALSE)\":   {\"#VALUE!\", \"HLOOKUP requires numeric row argument\"},\n\t\t\"HLOOKUP(D2,D:D,1,FALSE,FALSE)\": {\"#VALUE!\", \"HLOOKUP requires at most 4 arguments\"},\n\t\t\"HLOOKUP(D2,D:D,1,2)\":           {\"#N/A\", \"HLOOKUP no result found\"},\n\t\t\"HLOOKUP(D2,D10:D10,1,FALSE)\":   {\"#N/A\", \"HLOOKUP no result found\"},\n\t\t\"HLOOKUP(D2,D2:D3,4,FALSE)\":     {\"#N/A\", \"HLOOKUP has invalid row index\"},\n\t\t\"HLOOKUP(D2,C:C,1,FALSE)\":       {\"#N/A\", \"HLOOKUP no result found\"},\n\t\t\"HLOOKUP(ISNUMBER(1),F3:F9,1)\":  {\"#N/A\", \"HLOOKUP no result found\"},\n\t\t\"HLOOKUP(INT(1),E2:E9,1)\":       {\"#N/A\", \"HLOOKUP no result found\"},\n\t\t\"HLOOKUP(MUNIT(2),MUNIT(3),1)\":  {\"#N/A\", \"HLOOKUP no result found\"},\n\t\t\"HLOOKUP(A1:B2,B2:B3,1)\":        {\"#N/A\", \"HLOOKUP no result found\"},\n\t\t// MATCH\n\t\t\"MATCH()\":              {\"#VALUE!\", \"MATCH requires 1 or 2 arguments\"},\n\t\t\"MATCH(0,A1:A1,0,0)\":   {\"#VALUE!\", \"MATCH requires 1 or 2 arguments\"},\n\t\t\"MATCH(0,A1:A1,\\\"x\\\")\": {\"#VALUE!\", \"MATCH requires numeric match_type argument\"},\n\t\t\"MATCH(0,A1)\":          {\"#N/A\", \"MATCH arguments lookup_array should be one-dimensional array\"},\n\t\t\"MATCH(0,A1:B2)\":       {\"#N/A\", \"MATCH arguments lookup_array should be one-dimensional array\"},\n\t\t\"MATCH(0,A1:B1)\":       {\"#N/A\", \"#N/A\"},\n\t\t// TRANSPOSE\n\t\t\"TRANSPOSE()\": {\"#VALUE!\", \"TRANSPOSE requires 1 argument\"},\n\t\t// HYPERLINK\n\t\t\"HYPERLINK()\": {\"#VALUE!\", \"HYPERLINK requires at least 1 argument\"},\n\t\t\"HYPERLINK(\\\"https://github.com/xuri/excelize\\\",\\\"Excelize\\\",\\\"\\\")\": {\"#VALUE!\", \"HYPERLINK allows at most 2 arguments\"},\n\t\t// VLOOKUP\n\t\t\"VLOOKUP()\":                     {\"#VALUE!\", \"VLOOKUP requires at least 3 arguments\"},\n\t\t\"VLOOKUP(D2,D1,1,FALSE)\":        {\"#VALUE!\", \"VLOOKUP requires second argument of table array\"},\n\t\t\"VLOOKUP(D2,D:D,FALSE,FALSE)\":   {\"#VALUE!\", \"VLOOKUP requires numeric col argument\"},\n\t\t\"VLOOKUP(D2,D:D,1,FALSE,FALSE)\": {\"#VALUE!\", \"VLOOKUP requires at most 4 arguments\"},\n\t\t\"VLOOKUP(D2,D10:D10,1,FALSE)\":   {\"#N/A\", \"VLOOKUP no result found\"},\n\t\t\"VLOOKUP(D2,D:D,2,FALSE)\":       {\"#N/A\", \"VLOOKUP has invalid column index\"},\n\t\t\"VLOOKUP(D2,C:C,1,FALSE)\":       {\"#N/A\", \"VLOOKUP no result found\"},\n\t\t\"VLOOKUP(ISNUMBER(1),F3:F9,1)\":  {\"#N/A\", \"VLOOKUP no result found\"},\n\t\t\"VLOOKUP(INT(1),E2:E9,1)\":       {\"#N/A\", \"VLOOKUP no result found\"},\n\t\t\"VLOOKUP(MUNIT(2),MUNIT(3),1)\":  {\"#N/A\", \"VLOOKUP no result found\"},\n\t\t\"VLOOKUP(1,G1:H2,1,FALSE)\":      {\"#N/A\", \"VLOOKUP no result found\"},\n\t\t// INDEX\n\t\t\"INDEX()\":          {\"#VALUE!\", \"INDEX requires 2 or 3 arguments\"},\n\t\t\"INDEX(A1,2)\":      {\"#REF!\", \"INDEX row_num out of range\"},\n\t\t\"INDEX(A1,0,2)\":    {\"#REF!\", \"INDEX col_num out of range\"},\n\t\t\"INDEX(A1:A1,2)\":   {\"#REF!\", \"INDEX row_num out of range\"},\n\t\t\"INDEX(A1:A1,0,2)\": {\"#REF!\", \"INDEX col_num out of range\"},\n\t\t\"INDEX(A1:B2,2,3)\": {\"#REF!\", \"INDEX col_num out of range\"},\n\t\t\"INDEX(A1:A2,0,0)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"INDEX(0,\\\"\\\")\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"INDEX(0,0,\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// INDIRECT\n\t\t\"INDIRECT()\":                     {\"#VALUE!\", \"INDIRECT requires 1 or 2 arguments\"},\n\t\t\"INDIRECT(\\\"E\\\"&1,TRUE,1)\":       {\"#VALUE!\", \"INDIRECT requires 1 or 2 arguments\"},\n\t\t\"INDIRECT(\\\"R1048577C1\\\",\\\"\\\")\":  {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"INDIRECT(\\\"E1048577\\\")\":         {\"#REF!\", \"#REF!\"},\n\t\t\"INDIRECT(\\\"R1048577C1\\\",FALSE)\": {\"#REF!\", \"#REF!\"},\n\t\t\"INDIRECT(\\\"R1C16385\\\",FALSE)\":   {\"#REF!\", \"#REF!\"},\n\t\t\"INDIRECT(\\\"\\\",FALSE)\":           {\"#REF!\", \"#REF!\"},\n\t\t\"INDIRECT(\\\"R C1\\\",FALSE)\":       {\"#REF!\", \"#REF!\"},\n\t\t\"INDIRECT(\\\"R1C \\\",FALSE)\":       {\"#REF!\", \"#REF!\"},\n\t\t\"INDIRECT(\\\"R1C1:R2C \\\",FALSE)\":  {\"#REF!\", \"#REF!\"},\n\t\t// LOOKUP\n\t\t\"LOOKUP()\":                     {\"#VALUE!\", \"LOOKUP requires at least 2 arguments\"},\n\t\t\"LOOKUP(D2,D1,D2)\":             {\"#VALUE!\", \"LOOKUP requires second argument of table array\"},\n\t\t\"LOOKUP(D2,D1,D2,FALSE)\":       {\"#VALUE!\", \"LOOKUP requires at most 3 arguments\"},\n\t\t\"LOOKUP(1,MUNIT(0))\":           {\"#VALUE!\", \"LOOKUP requires not empty range as second argument\"},\n\t\t\"LOOKUP(D1,MUNIT(1),MUNIT(1))\": {\"#N/A\", \"LOOKUP no result found\"},\n\t\t// ROW\n\t\t\"ROW(1,2)\":          {\"#VALUE!\", \"ROW requires at most 1 argument\"},\n\t\t\"ROW(\\\"\\\")\":         {\"#VALUE!\", \"invalid reference\"},\n\t\t\"ROW(Sheet1)\":       {\"#NAME?\", \"invalid reference\"},\n\t\t\"ROW(Sheet1!A1!B1)\": {\"#NAME?\", \"invalid reference\"},\n\t\t// ROWS\n\t\t\"ROWS()\":              {\"#VALUE!\", \"ROWS requires 1 argument\"},\n\t\t\"ROWS(1)\":             {\"#VALUE!\", \"invalid reference\"},\n\t\t\"ROWS(\\\"\\\")\":          {\"#VALUE!\", \"invalid reference\"},\n\t\t\"ROWS(Sheet1)\":        {\"#NAME?\", \"invalid reference\"},\n\t\t\"ROWS(Sheet1!A1!B1)\":  {\"#NAME?\", \"invalid reference\"},\n\t\t\"ROWS(Sheet1!Sheet1)\": {\"#NAME?\", \"invalid reference\"},\n\t\t// Web Functions\n\t\t// ENCODEURL\n\t\t\"ENCODEURL()\": {\"#VALUE!\", \"ENCODEURL requires 1 argument\"},\n\t\t// Financial Functions\n\t\t// ACCRINT\n\t\t\"ACCRINT()\": {\"#VALUE!\", \"ACCRINT requires at least 6 arguments\"},\n\t\t\"ACCRINT(\\\"01/01/2012\\\",\\\"04/01/2012\\\",\\\"12/31/2013\\\",8%,10000,4,1,FALSE,0)\":  {\"#VALUE!\", \"ACCRINT allows at most 8 arguments\"},\n\t\t\"ACCRINT(\\\"\\\",\\\"04/01/2012\\\",\\\"12/31/2013\\\",8%,10000,4,1,FALSE)\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ACCRINT(\\\"01/01/2012\\\",\\\"\\\",\\\"12/31/2013\\\",8%,10000,4,1,FALSE)\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ACCRINT(\\\"01/01/2012\\\",\\\"04/01/2012\\\",\\\"\\\",8%,10000,4,1,FALSE)\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ACCRINT(\\\"01/01/2012\\\",\\\"04/01/2012\\\",\\\"12/31/2013\\\",\\\"\\\",10000,4,1,FALSE)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"ACCRINT(\\\"01/01/2012\\\",\\\"04/01/2012\\\",\\\"12/31/2013\\\",8%,\\\"\\\",4,1,FALSE)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"ACCRINT(\\\"01/01/2012\\\",\\\"04/01/2012\\\",\\\"12/31/2013\\\",8%,10000,3)\":            {\"#NUM!\", \"#NUM!\"},\n\t\t\"ACCRINT(\\\"01/01/2012\\\",\\\"04/01/2012\\\",\\\"12/31/2013\\\",8%,10000,\\\"\\\",1,FALSE)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"ACCRINT(\\\"01/01/2012\\\",\\\"04/01/2012\\\",\\\"12/31/2013\\\",8%,10000,4,\\\"\\\",FALSE)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"ACCRINT(\\\"01/01/2012\\\",\\\"04/01/2012\\\",\\\"12/31/2013\\\",8%,10000,4,1,\\\"\\\")\":     {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ACCRINT(\\\"01/01/2012\\\",\\\"04/01/2012\\\",\\\"12/31/2013\\\",8%,10000,4,5,FALSE)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// ACCRINTM\n\t\t\"ACCRINTM()\":                                            {\"#VALUE!\", \"ACCRINTM requires 4 or 5 arguments\"},\n\t\t\"ACCRINTM(\\\"\\\",\\\"01/01/2012\\\",8%,10000)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ACCRINTM(\\\"01/01/2012\\\",\\\"\\\",8%,10000)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ACCRINTM(\\\"12/31/2012\\\",\\\"01/01/2012\\\",8%,10000)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"ACCRINTM(\\\"01/01/2012\\\",\\\"12/31/2012\\\",\\\"\\\",10000)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"ACCRINTM(\\\"01/01/2012\\\",\\\"12/31/2012\\\",8%,\\\"\\\",10000)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"ACCRINTM(\\\"01/01/2012\\\",\\\"12/31/2012\\\",8%,-1,10000)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"ACCRINTM(\\\"01/01/2012\\\",\\\"12/31/2012\\\",8%,10000,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"ACCRINTM(\\\"01/01/2012\\\",\\\"12/31/2012\\\",8%,10000,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// AMORDEGRC\n\t\t\"AMORDEGRC()\": {\"#VALUE!\", \"AMORDEGRC requires 6 or 7 arguments\"},\n\t\t\"AMORDEGRC(\\\"\\\",\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,20%)\":     {\"#VALUE!\", \"AMORDEGRC requires cost to be number argument\"},\n\t\t\"AMORDEGRC(-1,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,20%)\":       {\"#VALUE!\", \"AMORDEGRC requires cost >= 0\"},\n\t\t\"AMORDEGRC(150,\\\"\\\",\\\"09/30/2015\\\",20,1,20%)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"\\\",20,1,20%)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"AMORDEGRC(150,\\\"09/30/2015\\\",\\\"01/01/2015\\\",20,1,20%)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",\\\"\\\",1,20%)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",-1,1,20%)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,\\\"\\\",20%)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,-1,20%)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,\\\"\\\")\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,-1)\":       {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,20%,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,50%)\":      {\"#NUM!\", \"AMORDEGRC requires rate to be < 0.5\"},\n\t\t\"AMORDEGRC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,20%,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// AMORLINC\n\t\t\"AMORLINC()\": {\"#VALUE!\", \"AMORLINC requires 6 or 7 arguments\"},\n\t\t\"AMORLINC(\\\"\\\",\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,20%)\":     {\"#VALUE!\", \"AMORLINC requires cost to be number argument\"},\n\t\t\"AMORLINC(-1,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,20%)\":       {\"#VALUE!\", \"AMORLINC requires cost >= 0\"},\n\t\t\"AMORLINC(150,\\\"\\\",\\\"09/30/2015\\\",20,1,20%)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"\\\",20,1,20%)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"AMORLINC(150,\\\"09/30/2015\\\",\\\"01/01/2015\\\",20,1,20%)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",\\\"\\\",1,20%)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",-1,1,20%)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,\\\"\\\",20%)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,-1,20%)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,\\\"\\\")\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,-1)\":       {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,20%,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"AMORLINC(150,\\\"01/01/2015\\\",\\\"09/30/2015\\\",20,1,20%,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// COUPDAYBS\n\t\t\"COUPDAYBS()\":                                     {\"#VALUE!\", \"COUPDAYBS requires 3 or 4 arguments\"},\n\t\t\"COUPDAYBS(\\\"\\\",\\\"10/25/2012\\\",4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPDAYBS(\\\"01/01/2011\\\",\\\"\\\",4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPDAYBS(\\\"01/01/2011\\\",\\\"10/25/2012\\\",\\\"\\\")\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPDAYBS(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"COUPDAYBS(\\\"10/25/2012\\\",\\\"01/01/2011\\\",4)\":      {\"#NUM!\", \"COUPDAYBS requires maturity > settlement\"},\n\t\t// COUPDAYS\n\t\t\"COUPDAYS()\":                                     {\"#VALUE!\", \"COUPDAYS requires 3 or 4 arguments\"},\n\t\t\"COUPDAYS(\\\"\\\",\\\"10/25/2012\\\",4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPDAYS(\\\"01/01/2011\\\",\\\"\\\",4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPDAYS(\\\"01/01/2011\\\",\\\"10/25/2012\\\",\\\"\\\")\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPDAYS(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"COUPDAYS(\\\"10/25/2012\\\",\\\"01/01/2011\\\",4)\":      {\"#NUM!\", \"COUPDAYS requires maturity > settlement\"},\n\t\t// COUPDAYSNC\n\t\t\"COUPDAYSNC()\":                                     {\"#VALUE!\", \"COUPDAYSNC requires 3 or 4 arguments\"},\n\t\t\"COUPDAYSNC(\\\"\\\",\\\"10/25/2012\\\",4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPDAYSNC(\\\"01/01/2011\\\",\\\"\\\",4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPDAYSNC(\\\"01/01/2011\\\",\\\"10/25/2012\\\",\\\"\\\")\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPDAYSNC(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"COUPDAYSNC(\\\"10/25/2012\\\",\\\"01/01/2011\\\",4)\":      {\"#NUM!\", \"COUPDAYSNC requires maturity > settlement\"},\n\t\t// COUPNCD\n\t\t\"COUPNCD()\": {\"#VALUE!\", \"COUPNCD requires 3 or 4 arguments\"},\n\t\t\"COUPNCD(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,0,0)\":  {\"#VALUE!\", \"COUPNCD requires 3 or 4 arguments\"},\n\t\t\"COUPNCD(\\\"\\\",\\\"10/25/2012\\\",4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPNCD(\\\"01/01/2011\\\",\\\"\\\",4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPNCD(\\\"01/01/2011\\\",\\\"10/25/2012\\\",\\\"\\\")\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPNCD(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"COUPNCD(\\\"01/01/2011\\\",\\\"10/25/2012\\\",3)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"COUPNCD(\\\"10/25/2012\\\",\\\"01/01/2011\\\",4)\":      {\"#NUM!\", \"COUPNCD requires maturity > settlement\"},\n\t\t// COUPNUM\n\t\t\"COUPNUM()\": {\"#VALUE!\", \"COUPNUM requires 3 or 4 arguments\"},\n\t\t\"COUPNUM(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,0,0)\":  {\"#VALUE!\", \"COUPNUM requires 3 or 4 arguments\"},\n\t\t\"COUPNUM(\\\"\\\",\\\"10/25/2012\\\",4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPNUM(\\\"01/01/2011\\\",\\\"\\\",4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPNUM(\\\"01/01/2011\\\",\\\"10/25/2012\\\",\\\"\\\")\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPNUM(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"COUPNUM(\\\"01/01/2011\\\",\\\"10/25/2012\\\",3)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"COUPNUM(\\\"10/25/2012\\\",\\\"01/01/2011\\\",4)\":      {\"#NUM!\", \"COUPNUM requires maturity > settlement\"},\n\t\t// COUPPCD\n\t\t\"COUPPCD()\": {\"#VALUE!\", \"COUPPCD requires 3 or 4 arguments\"},\n\t\t\"COUPPCD(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,0,0)\":  {\"#VALUE!\", \"COUPPCD requires 3 or 4 arguments\"},\n\t\t\"COUPPCD(\\\"\\\",\\\"10/25/2012\\\",4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPPCD(\\\"01/01/2011\\\",\\\"\\\",4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPPCD(\\\"01/01/2011\\\",\\\"10/25/2012\\\",\\\"\\\")\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"COUPPCD(\\\"01/01/2011\\\",\\\"10/25/2012\\\",4,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"COUPPCD(\\\"01/01/2011\\\",\\\"10/25/2012\\\",3)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"COUPPCD(\\\"10/25/2012\\\",\\\"01/01/2011\\\",4)\":      {\"#NUM!\", \"COUPPCD requires maturity > settlement\"},\n\t\t// CUMIPMT\n\t\t\"CUMIPMT()\":               {\"#VALUE!\", \"CUMIPMT requires 6 arguments\"},\n\t\t\"CUMIPMT(0,0,0,0,0,2)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"CUMIPMT(0,0,0,-1,0,0)\":   {\"#N/A\", \"#N/A\"},\n\t\t\"CUMIPMT(0,0,0,1,0,0)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"CUMIPMT(\\\"\\\",0,0,0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CUMIPMT(0,\\\"\\\",0,0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CUMIPMT(0,0,\\\"\\\",0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CUMIPMT(0,0,0,\\\"\\\",0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CUMIPMT(0,0,0,0,\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CUMIPMT(0,0,0,0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// CUMPRINC\n\t\t\"CUMPRINC()\":               {\"#VALUE!\", \"CUMPRINC requires 6 arguments\"},\n\t\t\"CUMPRINC(0,0,0,0,0,2)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"CUMPRINC(0,0,0,-1,0,0)\":   {\"#N/A\", \"#N/A\"},\n\t\t\"CUMPRINC(0,0,0,1,0,0)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"CUMPRINC(\\\"\\\",0,0,0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CUMPRINC(0,\\\"\\\",0,0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CUMPRINC(0,0,\\\"\\\",0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CUMPRINC(0,0,0,\\\"\\\",0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CUMPRINC(0,0,0,0,\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"CUMPRINC(0,0,0,0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// DB\n\t\t\"DB()\":             {\"#VALUE!\", \"DB requires at least 4 arguments\"},\n\t\t\"DB(0,0,0,0,0,0)\":  {\"#VALUE!\", \"DB allows at most 5 arguments\"},\n\t\t\"DB(-1,0,0,0)\":     {\"#N/A\", \"#N/A\"},\n\t\t\"DB(\\\"\\\",0,0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DB(0,\\\"\\\",0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DB(0,0,\\\"\\\",0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DB(0,0,0,\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DB(0,0,0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// DDB\n\t\t\"DDB()\":             {\"#VALUE!\", \"DDB requires at least 4 arguments\"},\n\t\t\"DDB(0,0,0,0,0,0)\":  {\"#VALUE!\", \"DDB allows at most 5 arguments\"},\n\t\t\"DDB(-1,0,0,0)\":     {\"#N/A\", \"#N/A\"},\n\t\t\"DDB(\\\"\\\",0,0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DDB(0,\\\"\\\",0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DDB(0,0,\\\"\\\",0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DDB(0,0,0,\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DDB(0,0,0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// DISC\n\t\t\"DISC()\":                                          {\"#VALUE!\", \"DISC requires 4 or 5 arguments\"},\n\t\t\"DISC(\\\"\\\",\\\"03/31/2021\\\",95,100)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DISC(\\\"04/01/2016\\\",\\\"\\\",95,100)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",\\\"\\\",100)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,\\\"\\\")\":     {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,100,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"DISC(\\\"03/31/2021\\\",\\\"04/01/2016\\\",95,100)\":      {\"#NUM!\", \"DISC requires maturity > settlement\"},\n\t\t\"DISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",0,100)\":       {\"#NUM!\", \"DISC requires pr > 0\"},\n\t\t\"DISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,0)\":        {\"#NUM!\", \"DISC requires redemption > 0\"},\n\t\t\"DISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,100,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// DOLLAR\n\t\t\"DOLLAR()\":       {\"#VALUE!\", \"DOLLAR requires at least 1 argument\"},\n\t\t\"DOLLAR(0,0,0)\":  {\"#VALUE!\", \"DOLLAR requires 1 or 2 arguments\"},\n\t\t\"DOLLAR(\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DOLLAR(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DOLLAR(1,200)\":  {\"#VALUE!\", \"decimal value should be less than 128\"},\n\t\t// DOLLARDE\n\t\t\"DOLLARDE()\":       {\"#VALUE!\", \"DOLLARDE requires 2 arguments\"},\n\t\t\"DOLLARDE(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DOLLARDE(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DOLLARDE(0,-1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"DOLLARDE(0,0)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// DOLLARFR\n\t\t\"DOLLARFR()\":       {\"#VALUE!\", \"DOLLARFR requires 2 arguments\"},\n\t\t\"DOLLARFR(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DOLLARFR(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DOLLARFR(0,-1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"DOLLARFR(0,0)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t// DURATION\n\t\t\"DURATION()\":                                            {\"#VALUE!\", \"DURATION requires 5 or 6 arguments\"},\n\t\t\"DURATION(\\\"\\\",\\\"03/31/2025\\\",10%,8%,4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DURATION(\\\"04/01/2015\\\",\\\"\\\",10%,8%,4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DURATION(\\\"03/31/2025\\\",\\\"04/01/2015\\\",10%,8%,4)\":      {\"#NUM!\", \"DURATION requires maturity > settlement\"},\n\t\t\"DURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",-1,8%,4)\":       {\"#NUM!\", \"DURATION requires coupon >= 0\"},\n\t\t\"DURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,-1,4)\":      {\"#NUM!\", \"DURATION requires yld >= 0\"},\n\t\t\"DURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",\\\"\\\",8%,4)\":     {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,\\\"\\\",4)\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,8%,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"DURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,8%,3)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"DURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,8%,4,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"DURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,8%,4,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// EFFECT\n\t\t\"EFFECT()\":       {\"#VALUE!\", \"EFFECT requires 2 arguments\"},\n\t\t\"EFFECT(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EFFECT(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EFFECT(0,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"EFFECT(1,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// EUROCONVERT\n\t\t\"EUROCONVERT()\": {\"#VALUE!\", \"EUROCONVERT requires at least 3 arguments\"},\n\t\t\"EUROCONVERT(1.47,\\\"FRF\\\",\\\"DEM\\\",TRUE,3,1)\":  {\"#VALUE!\", \"EUROCONVERT allows at most 5 arguments\"},\n\t\t\"EUROCONVERT(\\\"\\\",\\\"FRF\\\",\\\"DEM\\\",TRUE,3)\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EUROCONVERT(1.47,\\\"FRF\\\",\\\"DEM\\\",\\\"\\\",3)\":    {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EUROCONVERT(1.47,\\\"FRF\\\",\\\"DEM\\\",TRUE,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"EUROCONVERT(1.47,\\\"\\\",\\\"DEM\\\")\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"EUROCONVERT(1.47,\\\"FRF\\\",\\\"\\\",TRUE,3)\":       {\"#VALUE!\", \"#VALUE!\"},\n\t\t// FV\n\t\t\"FV()\":              {\"#VALUE!\", \"FV requires at least 3 arguments\"},\n\t\t\"FV(0,0,0,0,0,0,0)\": {\"#VALUE!\", \"FV allows at most 5 arguments\"},\n\t\t\"FV(0,0,0,0,2)\":     {\"#N/A\", \"#N/A\"},\n\t\t\"FV(\\\"\\\",0,0,0,0)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FV(0,\\\"\\\",0,0,0)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FV(0,0,\\\"\\\",0,0)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FV(0,0,0,\\\"\\\",0)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FV(0,0,0,0,\\\"\\\")\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// FVSCHEDULE\n\t\t\"FVSCHEDULE()\":        {\"#VALUE!\", \"FVSCHEDULE requires 2 arguments\"},\n\t\t\"FVSCHEDULE(\\\"\\\",0)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"FVSCHEDULE(0,\\\"x\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"x\\\": invalid syntax\"},\n\t\t// INTRATE\n\t\t\"INTRATE()\":                                          {\"#VALUE!\", \"INTRATE requires 4 or 5 arguments\"},\n\t\t\"INTRATE(\\\"\\\",\\\"03/31/2021\\\",95,100)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"INTRATE(\\\"04/01/2016\\\",\\\"\\\",95,100)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"INTRATE(\\\"04/01/2016\\\",\\\"03/31/2021\\\",\\\"\\\",100)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"INTRATE(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,\\\"\\\")\":     {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"INTRATE(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,100,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"INTRATE(\\\"03/31/2021\\\",\\\"04/01/2016\\\",95,100)\":      {\"#NUM!\", \"INTRATE requires maturity > settlement\"},\n\t\t\"INTRATE(\\\"04/01/2016\\\",\\\"03/31/2021\\\",0,100)\":       {\"#NUM!\", \"INTRATE requires investment > 0\"},\n\t\t\"INTRATE(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,0)\":        {\"#NUM!\", \"INTRATE requires redemption > 0\"},\n\t\t\"INTRATE(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,100,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// IPMT\n\t\t\"IPMT()\":               {\"#VALUE!\", \"IPMT requires at least 4 arguments\"},\n\t\t\"IPMT(0,0,0,0,0,0,0)\":  {\"#VALUE!\", \"IPMT allows at most 6 arguments\"},\n\t\t\"IPMT(0,0,0,0,0,2)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"IPMT(0,-1,0,0,0,0)\":   {\"#N/A\", \"#N/A\"},\n\t\t\"IPMT(0,1,0,0,0,0)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"IPMT(\\\"\\\",0,0,0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IPMT(0,\\\"\\\",0,0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IPMT(0,0,\\\"\\\",0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IPMT(0,0,0,\\\"\\\",0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IPMT(0,0,0,0,\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IPMT(0,0,0,0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// ISPMT\n\t\t\"ISPMT()\":           {\"#VALUE!\", \"ISPMT requires 4 arguments\"},\n\t\t\"ISPMT(\\\"\\\",0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ISPMT(0,\\\"\\\",0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ISPMT(0,0,\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ISPMT(0,0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// MDURATION\n\t\t\"MDURATION()\": {\"#VALUE!\", \"MDURATION requires 5 or 6 arguments\"},\n\t\t\"MDURATION(\\\"\\\",\\\"03/31/2025\\\",10%,8%,4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MDURATION(\\\"04/01/2015\\\",\\\"\\\",10%,8%,4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MDURATION(\\\"03/31/2025\\\",\\\"04/01/2015\\\",10%,8%,4)\":      {\"#NUM!\", \"MDURATION requires maturity > settlement\"},\n\t\t\"MDURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",-1,8%,4)\":       {\"#NUM!\", \"MDURATION requires coupon >= 0\"},\n\t\t\"MDURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,-1,4)\":      {\"#NUM!\", \"MDURATION requires yld >= 0\"},\n\t\t\"MDURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",\\\"\\\",8%,4)\":     {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"MDURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,\\\"\\\",4)\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"MDURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,8%,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"MDURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,8%,3)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"MDURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,8%,4,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"MDURATION(\\\"04/01/2015\\\",\\\"03/31/2025\\\",10%,8%,4,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// NOMINAL\n\t\t\"NOMINAL()\":       {\"#VALUE!\", \"NOMINAL requires 2 arguments\"},\n\t\t\"NOMINAL(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NOMINAL(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NOMINAL(0,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"NOMINAL(1,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// NPER\n\t\t\"NPER()\":             {\"#VALUE!\", \"NPER requires at least 3 arguments\"},\n\t\t\"NPER(0,0,0,0,0,0)\":  {\"#VALUE!\", \"NPER allows at most 5 arguments\"},\n\t\t\"NPER(0,0,0)\":        {\"#NUM!\", \"#NUM!\"},\n\t\t\"NPER(0,0,0,0,2)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"NPER(\\\"\\\",0,0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NPER(0,\\\"\\\",0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NPER(0,0,\\\"\\\",0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NPER(0,0,0,\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"NPER(0,0,0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// NPV\n\t\t\"NPV()\":       {\"#VALUE!\", \"NPV requires at least 2 arguments\"},\n\t\t\"NPV(\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// ODDFPRICE\n\t\t\"ODDFPRICE()\": {\"#VALUE!\", \"ODDFPRICE requires 8 or 9 arguments\"},\n\t\t\"ODDFPRICE(\\\"\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",\\\"\\\",3.5%,100,2)\":      {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,\\\"\\\",100,2)\":      {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,\\\"\\\",2)\":     {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"02/01/2017\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2)\":      {\"#NUM!\", \"ODDFPRICE requires settlement > issue\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"02/01/2017\\\",5.5%,3.5%,100,2)\":      {\"#NUM!\", \"ODDFPRICE requires first_coupon > settlement\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"02/01/2017\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2)\":      {\"#NUM!\", \"ODDFPRICE requires maturity > first_coupon\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",-1,3.5%,100,2)\":        {\"#NUM!\", \"ODDFPRICE requires rate >= 0\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,-1,100,2)\":        {\"#NUM!\", \"ODDFPRICE requires yld >= 0\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,0,2)\":        {\"#NUM!\", \"ODDFPRICE requires redemption > 0\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,3)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/30/2017\\\",5.5%,3.5%,100,4)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"ODDFPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// ODDFYIELD\n\t\t\"ODDFYIELD()\": {\"#VALUE!\", \"ODDFYIELD requires 8 or 9 arguments\"},\n\t\t\"ODDFYIELD(\\\"\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",\\\"\\\",3.5%,100,2)\":      {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,\\\"\\\",100,2)\":      {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,\\\"\\\",2)\":     {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"02/01/2017\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2)\":      {\"#NUM!\", \"ODDFYIELD requires settlement > issue\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"02/01/2017\\\",5.5%,3.5%,100,2)\":      {\"#NUM!\", \"ODDFYIELD requires first_coupon > settlement\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"02/01/2017\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2)\":      {\"#NUM!\", \"ODDFYIELD requires maturity > first_coupon\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",-1,3.5%,100,2)\":        {\"#NUM!\", \"ODDFYIELD requires rate >= 0\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,0,100,2)\":         {\"#NUM!\", \"ODDFYIELD requires pr > 0\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,0,2)\":        {\"#NUM!\", \"ODDFYIELD requires redemption > 0\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,3)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/30/2017\\\",5.5%,3.5%,100,4)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"ODDFYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"03/31/2017\\\",5.5%,3.5%,100,2,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// ODDLPRICE\n\t\t\"ODDLPRICE()\": {\"#VALUE!\", \"ODDLPRICE requires 7 or 8 arguments\"},\n\t\t\"ODDLPRICE(\\\"\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDLPRICE(\\\"02/01/2017\\\",\\\"\\\",\\\"12/01/2016\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDLPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDLPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"\\\",3.5%,100,2)\":      {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDLPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,\\\"\\\",100,2)\":      {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDLPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,\\\"\\\",2)\":     {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDLPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,100,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDLPRICE(\\\"04/20/2008\\\",\\\"06/15/2008\\\",\\\"04/30/2008\\\",3.75%,99.875,100,2)\":   {\"#NUM!\", \"ODDLPRICE requires settlement > last_interest\"},\n\t\t\"ODDLPRICE(\\\"06/20/2008\\\",\\\"06/15/2008\\\",\\\"04/30/2008\\\",3.75%,99.875,100,2)\":   {\"#NUM!\", \"ODDLPRICE requires maturity > settlement\"},\n\t\t\"ODDLPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",-1,3.5%,100,2)\":        {\"#NUM!\", \"ODDLPRICE requires rate >= 0\"},\n\t\t\"ODDLPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,-1,100,2)\":        {\"#NUM!\", \"ODDLPRICE requires yld >= 0\"},\n\t\t\"ODDLPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,0,2)\":        {\"#NUM!\", \"ODDLPRICE requires redemption > 0\"},\n\t\t\"ODDLPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,100,2,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"ODDLPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,100,3)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"ODDLPRICE(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,100,2,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// ODDLYIELD\n\t\t\"ODDLYIELD()\": {\"#VALUE!\", \"ODDLYIELD requires 7 or 8 arguments\"},\n\t\t\"ODDLYIELD(\\\"\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDLYIELD(\\\"02/01/2017\\\",\\\"\\\",\\\"12/01/2016\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDLYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"\\\",5.5%,3.5%,100,2)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"ODDLYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",\\\"\\\",3.5%,100,2)\":      {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDLYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,\\\"\\\",100,2)\":      {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDLYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,\\\"\\\",2)\":     {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDLYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,100,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"ODDLYIELD(\\\"04/20/2008\\\",\\\"06/15/2008\\\",\\\"04/30/2008\\\",3.75%,99.875,100,2)\":   {\"#NUM!\", \"ODDLYIELD requires settlement > last_interest\"},\n\t\t\"ODDLYIELD(\\\"06/20/2008\\\",\\\"06/15/2008\\\",\\\"04/30/2008\\\",3.75%,99.875,100,2)\":   {\"#NUM!\", \"ODDLYIELD requires maturity > settlement\"},\n\t\t\"ODDLYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",-1,3.5%,100,2)\":        {\"#NUM!\", \"ODDLYIELD requires rate >= 0\"},\n\t\t\"ODDLYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,0,100,2)\":         {\"#NUM!\", \"ODDLYIELD requires pr > 0\"},\n\t\t\"ODDLYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,0,2)\":        {\"#NUM!\", \"ODDLYIELD requires redemption > 0\"},\n\t\t\"ODDLYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,100,2,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"ODDLYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,100,3)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"ODDLYIELD(\\\"02/01/2017\\\",\\\"03/31/2021\\\",\\\"12/01/2016\\\",5.5%,3.5%,100,2,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// PDURATION\n\t\t\"PDURATION()\":         {\"#VALUE!\", \"PDURATION requires 3 arguments\"},\n\t\t\"PDURATION(\\\"\\\",0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PDURATION(0,\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PDURATION(0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PDURATION(0,0,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t// PMT\n\t\t\"PMT()\":             {\"#VALUE!\", \"PMT requires at least 3 arguments\"},\n\t\t\"PMT(0,0,0,0,0,0)\":  {\"#VALUE!\", \"PMT allows at most 5 arguments\"},\n\t\t\"PMT(0,0,0,0,2)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"PMT(\\\"\\\",0,0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PMT(0,\\\"\\\",0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PMT(0,0,\\\"\\\",0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PMT(0,0,0,\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PMT(0,0,0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// PRICE\n\t\t\"PRICE()\": {\"#VALUE!\", \"PRICE requires 6 or 7 arguments\"},\n\t\t\"PRICE(\\\"\\\",\\\"02/01/2020\\\",12%,10%,100,2,4)\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"\\\",12%,10%,100,2,4)\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"02/01/2020\\\",\\\"\\\",10%,100,2,4)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"02/01/2020\\\",12%,\\\"\\\",100,2,4)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"02/01/2020\\\",12%,10%,\\\"\\\",2,4)\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"02/01/2020\\\",12%,10%,100,\\\"\\\",4)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"02/01/2020\\\",-1,10%,100,2,4)\":     {\"#NUM!\", \"PRICE requires rate >= 0\"},\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"02/01/2020\\\",12%,-1,100,2,4)\":     {\"#NUM!\", \"PRICE requires yld >= 0\"},\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"02/01/2020\\\",12%,10%,0,2,4)\":      {\"#NUM!\", \"PRICE requires redemption > 0\"},\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"02/01/2020\\\",12%,10%,100,2,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"02/01/2020\\\",12%,10%,100,3,4)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"PRICE(\\\"04/01/2012\\\",\\\"02/01/2020\\\",12%,10%,100,2,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// PPMT\n\t\t\"PPMT()\":               {\"#VALUE!\", \"PPMT requires at least 4 arguments\"},\n\t\t\"PPMT(0,0,0,0,0,0,0)\":  {\"#VALUE!\", \"PPMT allows at most 6 arguments\"},\n\t\t\"PPMT(0,0,0,0,0,2)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"PPMT(0,-1,0,0,0,0)\":   {\"#N/A\", \"#N/A\"},\n\t\t\"PPMT(0,1,0,0,0,0)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"PPMT(\\\"\\\",0,0,0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PPMT(0,\\\"\\\",0,0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PPMT(0,0,\\\"\\\",0,0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PPMT(0,0,0,\\\"\\\",0,0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PPMT(0,0,0,0,\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PPMT(0,0,0,0,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// PRICEDISC\n\t\t\"PRICEDISC()\":                                          {\"#VALUE!\", \"PRICEDISC requires 4 or 5 arguments\"},\n\t\t\"PRICEDISC(\\\"\\\",\\\"03/31/2021\\\",95,100)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"PRICEDISC(\\\"04/01/2016\\\",\\\"\\\",95,100)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"PRICEDISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",\\\"\\\",100)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"PRICEDISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,\\\"\\\")\":     {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"PRICEDISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,100,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"PRICEDISC(\\\"03/31/2021\\\",\\\"04/01/2016\\\",95,100)\":      {\"#NUM!\", \"PRICEDISC requires maturity > settlement\"},\n\t\t\"PRICEDISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",0,100)\":       {\"#NUM!\", \"PRICEDISC requires discount > 0\"},\n\t\t\"PRICEDISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,0)\":        {\"#NUM!\", \"PRICEDISC requires redemption > 0\"},\n\t\t\"PRICEDISC(\\\"04/01/2016\\\",\\\"03/31/2021\\\",95,100,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// PRICEMAT\n\t\t\"PRICEMAT()\": {\"#VALUE!\", \"PRICEMAT requires 5 or 6 arguments\"},\n\t\t\"PRICEMAT(\\\"\\\",\\\"03/31/2021\\\",\\\"01/01/2017\\\",4.5%,2.5%)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"PRICEMAT(\\\"04/01/2017\\\",\\\"\\\",\\\"01/01/2017\\\",4.5%,2.5%)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"PRICEMAT(\\\"04/01/2017\\\",\\\"03/31/2021\\\",\\\"\\\",4.5%,2.5%)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"PRICEMAT(\\\"04/01/2017\\\",\\\"03/31/2021\\\",\\\"01/01/2017\\\",\\\"\\\",2.5%)\":      {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PRICEMAT(\\\"04/01/2017\\\",\\\"03/31/2021\\\",\\\"01/01/2017\\\",4.5%,\\\"\\\")\":      {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PRICEMAT(\\\"04/01/2017\\\",\\\"03/31/2021\\\",\\\"01/01/2017\\\",4.5%,2.5%,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"PRICEMAT(\\\"03/31/2021\\\",\\\"04/01/2017\\\",\\\"01/01/2017\\\",4.5%,2.5%)\":      {\"#NUM!\", \"PRICEMAT requires maturity > settlement\"},\n\t\t\"PRICEMAT(\\\"01/01/2017\\\",\\\"03/31/2021\\\",\\\"04/01/2017\\\",4.5%,2.5%)\":      {\"#NUM!\", \"PRICEMAT requires settlement > issue\"},\n\t\t\"PRICEMAT(\\\"04/01/2017\\\",\\\"03/31/2021\\\",\\\"01/01/2017\\\",-1,2.5%)\":        {\"#NUM!\", \"PRICEMAT requires rate >= 0\"},\n\t\t\"PRICEMAT(\\\"04/01/2017\\\",\\\"03/31/2021\\\",\\\"01/01/2017\\\",4.5%,-1)\":        {\"#NUM!\", \"PRICEMAT requires yld >= 0\"},\n\t\t\"PRICEMAT(\\\"04/01/2017\\\",\\\"03/31/2021\\\",\\\"01/01/2017\\\",4.5%,2.5%,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// PV\n\t\t\"PV()\":                     {\"#VALUE!\", \"PV requires at least 3 arguments\"},\n\t\t\"PV(10%/4,16,2000,0,1,0)\":  {\"#VALUE!\", \"PV allows at most 5 arguments\"},\n\t\t\"PV(\\\"\\\",16,2000,0,1)\":     {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PV(10%/4,\\\"\\\",2000,0,1)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PV(10%/4,16,\\\"\\\",0,1)\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PV(10%/4,16,2000,\\\"\\\",1)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"PV(10%/4,16,2000,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// RATE\n\t\t\"RATE()\":                        {\"#VALUE!\", \"RATE requires at least 3 arguments\"},\n\t\t\"RATE(48,-200,8000,3,1,0.5,0)\":  {\"#VALUE!\", \"RATE allows at most 6 arguments\"},\n\t\t\"RATE(\\\"\\\",-200,8000,3,1,0.5)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"RATE(48,\\\"\\\",8000,3,1,0.5)\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"RATE(48,-200,\\\"\\\",3,1,0.5)\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"RATE(48,-200,8000,\\\"\\\",1,0.5)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"RATE(48,-200,8000,3,\\\"\\\",0.5)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"RATE(48,-200,8000,3,1,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t// RECEIVED\n\t\t\"RECEIVED()\": {\"#VALUE!\", \"RECEIVED requires at least 4 arguments\"},\n\t\t\"RECEIVED(\\\"04/01/2011\\\",\\\"03/31/2016\\\",1000,4.5%,1,0)\":  {\"#VALUE!\", \"RECEIVED allows at most 5 arguments\"},\n\t\t\"RECEIVED(\\\"\\\",\\\"03/31/2016\\\",1000,4.5%,1)\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"RECEIVED(\\\"04/01/2011\\\",\\\"\\\",1000,4.5%,1)\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"RECEIVED(\\\"04/01/2011\\\",\\\"03/31/2016\\\",\\\"\\\",4.5%,1)\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"RECEIVED(\\\"04/01/2011\\\",\\\"03/31/2016\\\",1000,\\\"\\\",1)\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"RECEIVED(\\\"04/01/2011\\\",\\\"03/31/2016\\\",1000,4.5%,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"RECEIVED(\\\"04/01/2011\\\",\\\"03/31/2016\\\",1000,0)\":         {\"#NUM!\", \"RECEIVED requires discount > 0\"},\n\t\t\"RECEIVED(\\\"04/01/2011\\\",\\\"03/31/2016\\\",1000,4.5%,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// RRI\n\t\t\"RRI()\":               {\"#VALUE!\", \"RRI requires 3 arguments\"},\n\t\t\"RRI(\\\"\\\",\\\"\\\",\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"RRI(0,10000,15000)\":  {\"#NUM!\", \"RRI requires nper argument to be > 0\"},\n\t\t\"RRI(10,0,15000)\":     {\"#NUM!\", \"RRI requires pv argument to be > 0\"},\n\t\t\"RRI(10,10000,-1)\":    {\"#NUM!\", \"RRI requires fv argument to be >= 0\"},\n\t\t// SLN\n\t\t\"SLN()\":               {\"#VALUE!\", \"SLN requires 3 arguments\"},\n\t\t\"SLN(\\\"\\\",\\\"\\\",\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"SLN(10000,1000,0)\":   {\"#NUM!\", \"SLN requires life argument to be > 0\"},\n\t\t// SYD\n\t\t\"SYD()\":                    {\"#VALUE!\", \"SYD requires 4 arguments\"},\n\t\t\"SYD(\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"SYD(10000,1000,0,1)\":      {\"#NUM!\", \"SYD requires life argument to be > 0\"},\n\t\t\"SYD(10000,1000,5,0)\":      {\"#NUM!\", \"SYD requires per argument to be > 0\"},\n\t\t\"SYD(10000,1000,1,5)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t// TBILLEQ\n\t\t\"TBILLEQ()\":                                   {\"#VALUE!\", \"TBILLEQ requires 3 arguments\"},\n\t\t\"TBILLEQ(\\\"\\\",\\\"06/30/2017\\\",2.5%)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TBILLEQ(\\\"01/01/2017\\\",\\\"\\\",2.5%)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TBILLEQ(\\\"01/01/2017\\\",\\\"06/30/2017\\\",\\\"\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TBILLEQ(\\\"01/01/2017\\\",\\\"06/30/2017\\\",0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"TBILLEQ(\\\"01/01/2017\\\",\\\"06/30/2018\\\",2.5%)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"TBILLEQ(\\\"06/30/2017\\\",\\\"01/01/2017\\\",2.5%)\": {\"#NUM!\", \"#NUM!\"},\n\t\t// TBILLPRICE\n\t\t\"TBILLPRICE()\":                                   {\"#VALUE!\", \"TBILLPRICE requires 3 arguments\"},\n\t\t\"TBILLPRICE(\\\"\\\",\\\"06/30/2017\\\",2.5%)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TBILLPRICE(\\\"01/01/2017\\\",\\\"\\\",2.5%)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TBILLPRICE(\\\"01/01/2017\\\",\\\"06/30/2017\\\",\\\"\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TBILLPRICE(\\\"01/01/2017\\\",\\\"06/30/2017\\\",0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"TBILLPRICE(\\\"01/01/2017\\\",\\\"06/30/2018\\\",2.5%)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"TBILLPRICE(\\\"06/30/2017\\\",\\\"01/01/2017\\\",2.5%)\": {\"#NUM!\", \"#NUM!\"},\n\t\t// TBILLYIELD\n\t\t\"TBILLYIELD()\":                                   {\"#VALUE!\", \"TBILLYIELD requires 3 arguments\"},\n\t\t\"TBILLYIELD(\\\"\\\",\\\"06/30/2017\\\",2.5%)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TBILLYIELD(\\\"01/01/2017\\\",\\\"\\\",2.5%)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TBILLYIELD(\\\"01/01/2017\\\",\\\"06/30/2017\\\",\\\"\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TBILLYIELD(\\\"01/01/2017\\\",\\\"06/30/2017\\\",0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"TBILLYIELD(\\\"01/01/2017\\\",\\\"06/30/2018\\\",2.5%)\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"TBILLYIELD(\\\"06/30/2017\\\",\\\"01/01/2017\\\",2.5%)\": {\"#NUM!\", \"#NUM!\"},\n\t\t// VDB\n\t\t\"VDB()\":                          {\"#VALUE!\", \"VDB requires 5 or 7 arguments\"},\n\t\t\"VDB(-1,1000,5,0,1)\":             {\"#NUM!\", \"VDB requires cost >= 0\"},\n\t\t\"VDB(10000,-1,5,0,1)\":            {\"#NUM!\", \"VDB requires salvage >= 0\"},\n\t\t\"VDB(10000,1000,0,0,1)\":          {\"#NUM!\", \"VDB requires life > 0\"},\n\t\t\"VDB(10000,1000,5,-1,1)\":         {\"#NUM!\", \"VDB requires start_period > 0\"},\n\t\t\"VDB(10000,1000,5,2,1)\":          {\"#NUM!\", \"VDB requires start_period <= end_period\"},\n\t\t\"VDB(10000,1000,5,0,6)\":          {\"#NUM!\", \"VDB requires end_period <= life\"},\n\t\t\"VDB(10000,1000,5,0,1,-0.2)\":     {\"#VALUE!\", \"VDB requires factor >= 0\"},\n\t\t\"VDB(\\\"\\\",1000,5,0,1)\":           {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"VDB(10000,\\\"\\\",5,0,1)\":          {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"VDB(10000,1000,\\\"\\\",0,1)\":       {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"VDB(10000,1000,5,\\\"\\\",1)\":       {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"VDB(10000,1000,5,0,\\\"\\\")\":       {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"VDB(10000,1000,5,0,1,\\\"\\\")\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"VDB(10000,1000,5,0,1,0.2,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t// YIELD\n\t\t\"YIELD()\": {\"#VALUE!\", \"YIELD requires 6 or 7 arguments\"},\n\t\t\"YIELD(\\\"\\\",\\\"06/30/2015\\\",10%,101,100,4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"\\\",10%,101,100,4)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2015\\\",\\\"\\\",101,100,4)\":     {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2015\\\",10%,\\\"\\\",100,4)\":     {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2015\\\",10%,101,\\\"\\\",4)\":     {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2015\\\",10%,101,100,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2015\\\",10%,101,100,4,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2015\\\",10%,101,100,3)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2015\\\",10%,101,100,4,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2015\\\",-1,101,100,4)\":       {\"#NUM!\", \"YIELD requires rate >= 0\"},\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2015\\\",10%,0,100,4)\":        {\"#NUM!\", \"YIELD requires pr > 0\"},\n\t\t\"YIELD(\\\"01/01/2010\\\",\\\"06/30/2015\\\",10%,101,-1,4)\":       {\"#NUM!\", \"YIELD requires redemption >= 0\"},\n\t\t// YIELDDISC\n\t\t\"YIELDDISC()\": {\"#VALUE!\", \"YIELDDISC requires 4 or 5 arguments\"},\n\t\t\"YIELDDISC(\\\"\\\",\\\"06/30/2017\\\",97,100,0)\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"YIELDDISC(\\\"01/01/2017\\\",\\\"\\\",97,100,0)\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"YIELDDISC(\\\"01/01/2017\\\",\\\"06/30/2017\\\",\\\"\\\",100,0)\":  {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"YIELDDISC(\\\"01/01/2017\\\",\\\"06/30/2017\\\",97,\\\"\\\",0)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"YIELDDISC(\\\"01/01/2017\\\",\\\"06/30/2017\\\",97,100,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"YIELDDISC(\\\"01/01/2017\\\",\\\"06/30/2017\\\",0,100)\":       {\"#NUM!\", \"YIELDDISC requires pr > 0\"},\n\t\t\"YIELDDISC(\\\"01/01/2017\\\",\\\"06/30/2017\\\",97,0)\":        {\"#NUM!\", \"YIELDDISC requires redemption > 0\"},\n\t\t\"YIELDDISC(\\\"01/01/2017\\\",\\\"06/30/2017\\\",97,100,5)\":    {\"#NUM!\", \"invalid basis\"},\n\t\t// YIELDMAT\n\t\t\"YIELDMAT()\": {\"#VALUE!\", \"YIELDMAT requires 5 or 6 arguments\"},\n\t\t\"YIELDMAT(\\\"\\\",\\\"06/30/2018\\\",\\\"06/01/2014\\\",5.5%,101,0)\":            {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"YIELDMAT(\\\"01/01/2017\\\",\\\"\\\",\\\"06/01/2014\\\",5.5%,101,0)\":            {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"YIELDMAT(\\\"01/01/2017\\\",\\\"06/30/2018\\\",\\\"\\\",5.5%,101,0)\":            {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"YIELDMAT(\\\"01/01/2017\\\",\\\"06/30/2018\\\",\\\"06/01/2014\\\",\\\"\\\",101,0)\":  {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"YIELDMAT(\\\"01/01/2017\\\",\\\"06/30/2018\\\",\\\"06/01/2014\\\",5,\\\"\\\",0)\":    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"YIELDMAT(\\\"01/01/2017\\\",\\\"06/30/2018\\\",\\\"06/01/2014\\\",5,5.5%,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"YIELDMAT(\\\"06/01/2014\\\",\\\"06/30/2018\\\",\\\"01/01/2017\\\",5.5%,101,0)\":  {\"#NUM!\", \"YIELDMAT requires settlement > issue\"},\n\t\t\"YIELDMAT(\\\"01/01/2017\\\",\\\"06/30/2018\\\",\\\"06/01/2014\\\",-1,101,0)\":    {\"#NUM!\", \"YIELDMAT requires rate >= 0\"},\n\t\t\"YIELDMAT(\\\"01/01/2017\\\",\\\"06/30/2018\\\",\\\"06/01/2014\\\",1,0,0)\":       {\"#NUM!\", \"YIELDMAT requires pr > 0\"},\n\t\t\"YIELDMAT(\\\"01/01/2017\\\",\\\"06/30/2018\\\",\\\"06/01/2014\\\",5.5%,101,5)\":  {\"#NUM!\", \"invalid basis\"},\n\t\t// DISPIMG\n\t\t\"_xlfn.DISPIMG()\": {\"#VALUE!\", \"DISPIMG requires 2 numeric arguments\"},\n\t}\n\tfor formula, expected := range mathCalcError {\n\t\tf := prepareCalcData(cellData)\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n\n\treferenceCalc := map[string]string{\n\t\t// MDETERM\n\t\t\"MDETERM(A1:B2)\": \"-3\",\n\t\t// PRODUCT\n\t\t\"PRODUCT(Sheet1!A1:Sheet1!A1:A2,A2)\": \"4\",\n\t\t// IMPRODUCT\n\t\t\"IMPRODUCT(Sheet1!A1:Sheet1!A1:A2,A2)\": \"4\",\n\t\t// SUM\n\t\t\"A1/A3\":                          \"0.333333333333333\",\n\t\t\"SUM(A1:A2)\":                     \"3\",\n\t\t\"SUM(Sheet1!A1:Sheet1!A2)\":       \"3\",\n\t\t\"SUM(Sheet1!A1,A2)\":              \"3\",\n\t\t\"(-2-SUM(-4+A2))*5\":              \"0\",\n\t\t\"SUM(Sheet1!A1:Sheet1!A1:A2,A2)\": \"5\",\n\t\t\"SUM(A1,A2,A3)*SUM(2,3)\":         \"30\",\n\t\t\"1+SUM(SUM(A1+A2/A3)*(2-3),2)\":   \"1.33333333333333\",\n\t\t\"A1/A2/SUM(A1:A2:B1)\":            \"0.0416666666666667\",\n\t\t\"A1/A2/SUM(A1:A2:B1)*A3\":         \"0.125\",\n\t\t\"SUM(B1:D1)\":                     \"4\",\n\t\t\"SUM(\\\"X\\\")\":                     \"0\",\n\t}\n\tfor formula, expected := range referenceCalc {\n\t\tf := prepareCalcData(cellData)\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\n\treferenceCalcError := map[string][]string{\n\t\t// MDETERM\n\t\t\"MDETERM(A1:B3)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t// SUM\n\t\t\"1+SUM(SUM(A1+A2/A4)*(2-3),2)\": {\"#VALUE!\", \"#DIV/0!\"},\n\t}\n\tfor formula, expected := range referenceCalcError {\n\t\tf := prepareCalcData(cellData)\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n\n\tvolatileFuncs := []string{\n\t\t\"NOW()\",\n\t\t\"RAND()\",\n\t\t\"RANDBETWEEN(1,2)\",\n\t\t\"TODAY()\",\n\t}\n\tfor _, formula := range volatileFuncs {\n\t\tf := prepareCalcData(cellData)\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\t_, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err)\n\t}\n\n\t// Test get calculated cell value on not formula cell\n\tf := prepareCalcData(cellData)\n\tresult, err := f.CalcCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"1\", result)\n\t// Test get calculated cell value on not exists worksheet\n\tf = prepareCalcData(cellData)\n\t_, err = f.CalcCellValue(\"SheetN\", \"A1\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get calculated cell value with invalid sheet name\n\t_, err = f.CalcCellValue(\"Sheet:1\", \"A1\")\n\tassert.Equal(t, ErrSheetNameInvalid, err)\n\t// Test get calculated cell value with not support formula\n\tf = prepareCalcData(cellData)\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", \"UNSUPPORT(A1)\"))\n\t_, err = f.CalcCellValue(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, \"not support UNSUPPORT function\")\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestCalcCellValue.xlsx\")))\n}\n\nfunc TestCalcWithDefinedName(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"A1_as_string\", \"B1_as_string\", 123, nil},\n\t}\n\tf := prepareCalcData(cellData)\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{Name: \"defined_name1\", RefersTo: \"Sheet1!A1\", Scope: \"Workbook\"}))\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{Name: \"defined_name1\", RefersTo: \"Sheet1!B1\", Scope: \"Sheet1\"}))\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{Name: \"defined_name2\", RefersTo: \"Sheet1!C1\", Scope: \"Workbook\"}))\n\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D1\", \"defined_name1\"))\n\tresult, err := f.CalcCellValue(\"Sheet1\", \"D1\")\n\tassert.NoError(t, err)\n\t// DefinedName with scope WorkSheet takes precedence over DefinedName with scope Workbook, so we should get B1 value\n\tassert.Equal(t, \"B1_as_string\", result, \"defined_name1\")\n\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D1\", \"CONCATENATE(\\\"<\\\",defined_name1,\\\">\\\")\"))\n\tresult, err = f.CalcCellValue(\"Sheet1\", \"D1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"<B1_as_string>\", result, \"defined_name1\")\n\n\t// comparing numeric values\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D1\", \"123=defined_name2\"))\n\tresult, err = f.CalcCellValue(\"Sheet1\", \"D1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"TRUE\", result, \"123=defined_name2\")\n\n\t// comparing text values\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D1\", \"\\\"B1_as_string\\\"=defined_name1\"))\n\tresult, err = f.CalcCellValue(\"Sheet1\", \"D1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"TRUE\", result, \"\\\"B1_as_string\\\"=defined_name1\")\n\n\t// comparing text values\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D1\", \"IF(\\\"B1_as_string\\\"=defined_name1,\\\"YES\\\",\\\"NO\\\")\"))\n\tresult, err = f.CalcCellValue(\"Sheet1\", \"D1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"YES\", result, \"IF(\\\"B1_as_string\\\"=defined_name1,\\\"YES\\\",\\\"NO\\\")\")\n\n\tt.Run(\"for_sheet_name_with_space\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tdefer func() {\n\t\t\tassert.NoError(t, f.Close())\n\t\t}()\n\t\tassert.NoError(t, f.SetSheetName(\"Sheet1\", \"Sheet 1\"))\n\t\tcells := []string{\"A1\", \"A2\", \"A3\", \"A4\"}\n\t\tnames := []string{\"val1\", \"val2\", \"val3\", \"val4\"}\n\t\tfor idx, v := range []interface{}{100, 20, 30, 5} {\n\t\t\tassert.NoError(t, f.SetCellValue(\"Sheet 1\", cells[idx], v))\n\t\t}\n\t\tfor idx, cell := range cells {\n\t\t\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\t\t\tName: names[idx], RefersTo: \"'Sheet 1'!\" + cell,\n\t\t\t}))\n\t\t}\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet 1\", \"B1\", \"=val1-val2-val3-val4\"))\n\t\tresult, err := f.CalcCellValue(\"Sheet 1\", \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"45\", result)\n\t})\n}\n\nfunc TestCalcISBLANK(t *testing.T) {\n\targsList := list.New()\n\targsList.PushBack(formulaArg{\n\t\tType: ArgUnknown,\n\t})\n\tfn := formulaFuncs{}\n\tresult := fn.ISBLANK(argsList)\n\tassert.Equal(t, \"TRUE\", result.Value())\n\tassert.Empty(t, result.Error)\n}\n\nfunc TestCalcAND(t *testing.T) {\n\targsList := list.New()\n\targsList.PushBack(formulaArg{\n\t\tType: ArgUnknown,\n\t})\n\tfn := formulaFuncs{}\n\tresult := fn.AND(argsList)\n\tassert.Equal(t, result.String, \"\")\n\tassert.Empty(t, result.Error)\n}\n\nfunc TestCalcOR(t *testing.T) {\n\targsList := list.New()\n\targsList.PushBack(formulaArg{\n\t\tType: ArgUnknown,\n\t})\n\tfn := formulaFuncs{}\n\tresult := fn.OR(argsList)\n\tassert.Equal(t, result.Value(), \"FALSE\")\n\tassert.Empty(t, result.Error)\n}\n\nfunc TestCalcDet(t *testing.T) {\n\tassert.Equal(t, det([][]float64{\n\t\t{1, 2, 3, 4},\n\t\t{2, 3, 4, 5},\n\t\t{3, 4, 5, 6},\n\t\t{4, 5, 6, 7},\n\t}), float64(0))\n}\n\nfunc TestCalcToBool(t *testing.T) {\n\tb := newBoolFormulaArg(true).ToBool()\n\tassert.Equal(t, b.Boolean, true)\n\tassert.Equal(t, b.Number, 1.0)\n}\n\nfunc TestCalcToList(t *testing.T) {\n\tassert.Equal(t, []formulaArg(nil), newEmptyFormulaArg().ToList())\n\tformulaList := []formulaArg{newEmptyFormulaArg()}\n\tassert.Equal(t, formulaList, newListFormulaArg(formulaList).ToList())\n}\n\nfunc TestCalcCompareFormulaArg(t *testing.T) {\n\tassert.Equal(t, compareFormulaArg(newEmptyFormulaArg(), newEmptyFormulaArg(), newNumberFormulaArg(matchModeMaxLess), false), criteriaEq)\n\tlhs := newListFormulaArg([]formulaArg{newEmptyFormulaArg()})\n\trhs := newListFormulaArg([]formulaArg{newEmptyFormulaArg(), newEmptyFormulaArg()})\n\tassert.Equal(t, compareFormulaArg(lhs, rhs, newNumberFormulaArg(matchModeMaxLess), false), criteriaL)\n\tassert.Equal(t, compareFormulaArg(rhs, lhs, newNumberFormulaArg(matchModeMaxLess), false), criteriaG)\n\n\tlhs = newListFormulaArg([]formulaArg{newBoolFormulaArg(true)})\n\trhs = newListFormulaArg([]formulaArg{newBoolFormulaArg(true)})\n\tassert.Equal(t, compareFormulaArg(lhs, rhs, newNumberFormulaArg(matchModeMaxLess), false), criteriaEq)\n\n\tlhs = newListFormulaArg([]formulaArg{newNumberFormulaArg(1)})\n\trhs = newListFormulaArg([]formulaArg{newNumberFormulaArg(0)})\n\tassert.Equal(t, compareFormulaArg(lhs, rhs, newNumberFormulaArg(matchModeMaxLess), false), criteriaG)\n\n\tassert.Equal(t, compareFormulaArg(formulaArg{Type: ArgUnknown}, formulaArg{Type: ArgUnknown}, newNumberFormulaArg(matchModeMaxLess), false), criteriaErr)\n}\n\nfunc TestCalcCompareFormulaArgMatrix(t *testing.T) {\n\tlhs := newMatrixFormulaArg([][]formulaArg{{newEmptyFormulaArg()}})\n\trhs := newMatrixFormulaArg([][]formulaArg{{newEmptyFormulaArg(), newEmptyFormulaArg()}})\n\tassert.Equal(t, compareFormulaArgMatrix(lhs, rhs, newNumberFormulaArg(matchModeMaxLess), false), criteriaL)\n\n\tlhs = newMatrixFormulaArg([][]formulaArg{{newEmptyFormulaArg(), newEmptyFormulaArg()}})\n\trhs = newMatrixFormulaArg([][]formulaArg{{newEmptyFormulaArg()}})\n\tassert.Equal(t, compareFormulaArgMatrix(lhs, rhs, newNumberFormulaArg(matchModeMaxLess), false), criteriaG)\n\n\tlhs = newMatrixFormulaArg([][]formulaArg{{newNumberFormulaArg(1)}})\n\trhs = newMatrixFormulaArg([][]formulaArg{{newNumberFormulaArg(0)}})\n\tassert.Equal(t, compareFormulaArgMatrix(lhs, rhs, newNumberFormulaArg(matchModeMaxLess), false), criteriaG)\n}\n\nfunc TestCalcANCHORARRAY(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 1))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A2\", 2))\n\tformulaType, ref := STCellFormulaTypeArray, \"B1:B2\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"A1:A2\",\n\t\tFormulaOpts{Ref: &ref, Type: &formulaType}))\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"SUM(_xlfn.ANCHORARRAY($B$1))\"))\n\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"3\", result)\n\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"SUM(_xlfn.ANCHORARRAY(\\\"\\\",\\\"\\\"))\"))\n\tresult, err = f.CalcCellValue(\"Sheet1\", \"C1\")\n\tassert.EqualError(t, err, \"ANCHORARRAY requires 1 numeric argument\")\n\tassert.Equal(t, \"#VALUE!\", result)\n\n\tfn := &formulaFuncs{f: f, sheet: \"SheetN\"}\n\targsList := list.New()\n\targsList.PushBack(newStringFormulaArg(\"$B$1\"))\n\tformulaArg := fn.ANCHORARRAY(argsList)\n\tassert.Equal(t, \"sheet SheetN does not exist\", formulaArg.Value())\n\n\tfn.sheet = \"Sheet1\"\n\targsList = argsList.Init()\n\targ := newStringFormulaArg(\"$A$1\")\n\targ.cellRefs = list.New()\n\targ.cellRefs.PushBack(cellRef{Row: 1, Col: 1})\n\targsList.PushBack(arg)\n\tformulaArg = fn.ANCHORARRAY(argsList)\n\tassert.Equal(t, ArgEmpty, formulaArg.Type)\n\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetData.Row[0].C[0].F = &xlsxF{}\n\tformulaArg = fn.ANCHORARRAY(argsList)\n\tassert.Equal(t, ArgError, formulaArg.Type)\n\tassert.Equal(t, ErrParameterInvalid.Error(), formulaArg.Value())\n\n\targsList = argsList.Init()\n\targ = newStringFormulaArg(\"$B$1\")\n\targ.cellRefs = list.New()\n\targ.cellRefs.PushBack(cellRef{Row: 1, Col: 1, Sheet: \"SheetN\"})\n\targsList.PushBack(arg)\n\tws.(*xlsxWorksheet).SheetData.Row[0].C[0].F = &xlsxF{Ref: \"A1:A1\"}\n\tformulaArg = fn.ANCHORARRAY(argsList)\n\tassert.Equal(t, ArgError, formulaArg.Type)\n\tassert.Equal(t, \"sheet SheetN does not exist\", formulaArg.Value())\n}\n\nfunc TestCalcArrayFormula(t *testing.T) {\n\tt.Run(\"matrix_multiplication\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A1\", &[]int{1, 2}))\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A2\", &[]int{3, 4}))\n\t\tformulaType, ref := STCellFormulaTypeArray, \"C1:C2\"\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"A1:A2*B1:B2\",\n\t\t\tFormulaOpts{Ref: &ref, Type: &formulaType}))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"2\", result)\n\t\tresult, err = f.CalcCellValue(\"Sheet1\", \"C2\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"12\", result)\n\t})\n\tt.Run(\"matrix_multiplication_with_defined_name\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A1\", &[]int{1, 2}))\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A2\", &[]int{3, 4}))\n\t\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\t\tName:     \"matrix\",\n\t\t\tRefersTo: \"Sheet1!$A$1:$A$2\",\n\t\t}))\n\t\tformulaType, ref := STCellFormulaTypeArray, \"C1:C2\"\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"matrix*B1:B2+\\\"1\\\"\",\n\t\t\tFormulaOpts{Ref: &ref, Type: &formulaType}))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"3\", result)\n\t\tresult, err = f.CalcCellValue(\"Sheet1\", \"C2\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"13\", result)\n\t})\n\tt.Run(\"columm_multiplication\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A1\", &[]int{1, 2}))\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A2\", &[]int{3, 4}))\n\t\tformulaType, ref := STCellFormulaTypeArray, \"C1:C1048576\"\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"A:A*B:B\",\n\t\t\tFormulaOpts{Ref: &ref, Type: &formulaType}))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"2\", result)\n\t\tresult, err = f.CalcCellValue(\"Sheet1\", \"C2\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"12\", result)\n\t})\n\tt.Run(\"row_multiplication\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A1\", &[]int{1, 2}))\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A2\", &[]int{3, 4}))\n\t\tformulaType, ref := STCellFormulaTypeArray, \"A3:XFD3\"\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A3\", \"1:1*2:2\",\n\t\t\tFormulaOpts{Ref: &ref, Type: &formulaType}))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"A3\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"3\", result)\n\t\tresult, err = f.CalcCellValue(\"Sheet1\", \"B3\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"8\", result)\n\t})\n}\n\nfunc TestCalcTRANSPOSE(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"a\", \"d\"},\n\t\t{\"b\", \"e\"},\n\t\t{\"c\", \"f\"},\n\t}\n\tformula := \"=TRANSPOSE(A1:A3)\"\n\tf := prepareCalcData(cellData)\n\tformulaType, ref := STCellFormulaTypeArray, \"D1:F2\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D1\", formula,\n\t\tFormulaOpts{Ref: &ref, Type: &formulaType}))\n\t_, err := f.CalcCellValue(\"Sheet1\", \"D1\")\n\tassert.NoError(t, err, formula)\n}\n\nfunc TestCalcVLOOKUP(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{nil, nil, nil, nil, nil, nil},\n\t\t{nil, \"Score\", \"Grade\", nil, nil, nil},\n\t\t{nil, 0, \"F\", nil, \"Score\", 85},\n\t\t{nil, 60, \"D\", nil, \"Grade\"},\n\t\t{nil, 70, \"C\", nil, nil, nil},\n\t\t{nil, 80, \"b\", nil, nil, nil},\n\t\t{nil, 90, \"A\", nil, nil, nil},\n\t\t{nil, 85, \"B\", nil, nil, nil},\n\t\t{nil, nil, nil, nil, nil, nil},\n\t}\n\tf := prepareCalcData(cellData)\n\tcalc := map[string]string{\n\t\t\"VLOOKUP(F3,B3:C8,2)\":       \"b\",\n\t\t\"VLOOKUP(F3,B3:C8,2,TRUE)\":  \"b\",\n\t\t\"VLOOKUP(F3,B3:C8,2,FALSE)\": \"B\",\n\t}\n\tfor formula, expected := range calc {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"F4\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"F4\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"VLOOKUP(INT(1),C3:C3,1,FALSE)\": {\"#N/A\", \"VLOOKUP no result found\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"F4\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"F4\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n\targsList := list.New()\n\targsList.PushBack(newStringFormulaArg(\"\"))\n\targsList.PushBack(newMatrixFormulaArg([][]formulaArg{{newNumberFormulaArg(1)}}))\n\targsList.PushBack(newNumberFormulaArg(1))\n\targsList.PushBack(newStringFormulaArg(\"\"))\n\t_, _, _, _, err := checkHVLookupArgs(\"VLOOKUP\", argsList)\n\tassert.Equal(t, ArgError, err.Type)\n}\n\nfunc TestCalcBoolean(t *testing.T) {\n\tcellData := [][]interface{}{{0.5, \"TRUE\", -0.5, \"FALSE\", true}}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"AVERAGEA(A1:C1)\":  \"0.333333333333333\",\n\t\t\"MAX(0.5,B1)\":      \"0.5\",\n\t\t\"MAX(A1:B1)\":       \"0.5\",\n\t\t\"MAXA(A1:B1)\":      \"0.5\",\n\t\t\"MAXA(A1:E1)\":      \"1\",\n\t\t\"MAXA(0.5,B1)\":     \"1\",\n\t\t\"MIN(-0.5,D1)\":     \"-0.5\",\n\t\t\"MIN(C1:D1)\":       \"-0.5\",\n\t\t\"MINA(C1:D1)\":      \"-0.5\",\n\t\t\"MINA(-0.5,D1)\":    \"-0.5\",\n\t\t\"STDEV(A1:C1)\":     \"0.707106781186548\",\n\t\t\"STDEV(A1,B1,C1)\":  \"0.707106781186548\",\n\t\t\"STDEVA(A1:C1,B1)\": \"0.707106781186548\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B10\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"B10\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestCalcMAXMIN(t *testing.T) {\n\tcellData := [][]interface{}{{\"1\"}, {\"2\"}, {true}}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"MAX(A1:A3)\":  \"0\",\n\t\t\"MAXA(A1:A3)\": \"1\",\n\t\t\"MIN(A1:A3)\":  \"0\",\n\t\t\"MINA(A1:A3)\": \"1\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestCalcAVERAGEIF(t *testing.T) {\n\tf := prepareCalcData([][]interface{}{\n\t\t{\"Monday\", 500},\n\t\t{\"Tuesday\", 50},\n\t\t{\"Thursday\", 100},\n\t\t{\"Friday\", 100},\n\t\t{\"Thursday\", 200},\n\t\t{5, 300},\n\t\t{2, 200},\n\t\t{3, 100},\n\t\t{4, 50},\n\t\t{5, 100},\n\t\t{1, 50},\n\t\t{true, 200},\n\t\t{true, 250},\n\t\t{false, 50},\n\t})\n\tfor formula, expected := range map[string]string{\n\t\t\"AVERAGEIF(A1:A14,\\\"Thursday\\\",B1:B14)\": \"150\",\n\t\t\"AVERAGEIF(A1:A14,5,B1:B14)\":            \"200\",\n\t\t\"AVERAGEIF(A1:A14,\\\">2\\\",B1:B14)\":       \"137.5\",\n\t\t\"AVERAGEIF(A1:A14,TRUE,B1:B14)\":         \"225\",\n\t\t\"AVERAGEIF(A1:A14,\\\"<>TRUE\\\",B1:B14)\":   \"150\",\n\t} {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestCalcCOVAR(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"array1\", \"array2\"},\n\t\t{2, 22.9},\n\t\t{7, 33.49},\n\t\t{8, 34.5},\n\t\t{3, 27.61},\n\t\t{4, 19.5},\n\t\t{1, 10.11},\n\t\t{6, 37.9},\n\t\t{5, 31.08},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"COVAR(A1:A9,B1:B9)\":        \"16.633125\",\n\t\t\"COVAR(A2:A9,B2:B9)\":        \"16.633125\",\n\t\t\"COVARIANCE.P(A1:A9,B1:B9)\": \"16.633125\",\n\t\t\"COVARIANCE.P(A2:A9,B2:B9)\": \"16.633125\",\n\t\t\"COVARIANCE.S(A1:A9,B1:B9)\": \"19.0092857142857\",\n\t\t\"COVARIANCE.S(A2:A9,B2:B9)\": \"19.0092857142857\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"COVAR()\":                   {\"#VALUE!\", \"COVAR requires 2 arguments\"},\n\t\t\"COVAR(A2:A9,B3:B3)\":        {\"#N/A\", \"#N/A\"},\n\t\t\"COVARIANCE.P()\":            {\"#VALUE!\", \"COVARIANCE.P requires 2 arguments\"},\n\t\t\"COVARIANCE.P(A2:A9,B3:B3)\": {\"#N/A\", \"#N/A\"},\n\t\t\"COVARIANCE.S()\":            {\"#VALUE!\", \"COVARIANCE.S requires 2 arguments\"},\n\t\t\"COVARIANCE.S(A2:A9,B3:B3)\": {\"#N/A\", \"#N/A\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcUniqueExactlyOnce(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"Customer name\"},\n\t\t{\"Fife, Grant\"},\n\t\t{\"Pruitt, Barbara\"},\n\t\t{\"Horn, Frances\"},\n\t\t{\"Barrett, Alicia\"},\n\t\t{\"Barrett, Alicia\"},\n\t\t{\"Larson, Lynn\"},\n\t\t{\"Pruitt, Barbara\"},\n\t\t{\"Snook, Anthony\"},\n\t\t{\"Snook, Anthony\"},\n\t\t{\"Horn, Frances\"},\n\t\t{\"Brown, Charity\"},\n\t}\n\tf := prepareCalcData(cellData)\n\n\tformulaList := map[string]string{\n\t\t\"TEXTJOIN(\\\":\\\", TRUE, UNIQUE(A2:A12))\":             \"Fife, Grant:Pruitt, Barbara:Horn, Frances:Barrett, Alicia:Larson, Lynn:Snook, Anthony:Brown, Charity\",\n\t\t\"TEXTJOIN(\\\":\\\", TRUE, UNIQUE(A2:A12,FALSE,TRUE))\":  \"Fife, Grant:Larson, Lynn:Brown, Charity\",\n\t\t\"TEXTJOIN(\\\":\\\", TRUE, UNIQUE(A2:A12,FALSE,FALSE))\": \"Fife, Grant:Pruitt, Barbara:Horn, Frances:Barrett, Alicia:Larson, Lynn:Snook, Anthony:Brown, Charity\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestCalcUniqueMultiColumn(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"Player name\", \"Gender\", \"Nickname\"},\n\t\t{\"Tom\", \"M\", \"Tom\"},\n\t\t{\"Fred\", \"M\", \"Fred\"},\n\t\t{\"Amy\", \"F\", \"Amy\"},\n\t\t{\"John\", \"M\", \"John\"},\n\t\t{\"Malicia\", \"F\", \"Malicia\"},\n\t\t{\"Fred\", \"M\", \"Fred\"},\n\t}\n\tf := prepareCalcData(cellData)\n\n\tformulaList := map[string]string{\n\t\t\"TEXTJOIN(\\\":\\\", TRUE, UNIQUE(A2:C7))\":            \"Tom:M:Tom:Fred:M:Fred:Amy:F:Amy:John:M:John:Malicia:F:Malicia\",\n\t\t\"TEXTJOIN(\\\":\\\", TRUE, UNIQUE(A2:C7,TRUE))\":       \"Tom:M:Fred:M:Amy:F:John:M:Malicia:F:Fred:M\",\n\t\t\"TEXTJOIN(\\\":\\\", TRUE, UNIQUE(A2:C7,TRUE, TRUE))\": \"M:M:F:M:F:M\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestCalcUniqueErrors(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"Player name\", \"Gender\", \"Nickname\"},\n\t\t{\"Tom\", \"M\", \"Tom\"},\n\t\t{\"Fred\", \"M\", \"Fred\"},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"TEXTJOIN(\\\":\\\", TRUE, UNIQUE())\":                       \"#VALUE!\",\n\t\t\"TEXTJOIN(\\\":\\\", TRUE, UNIQUE(1, 2, 3, 4))\":             \"#VALUE!\",\n\t\t\"TEXTJOIN(\\\":\\\", TRUE, UNIQUE(A2:A3, \\\"Hello\\\"))\":       \"#VALUE!\",\n\t\t\"TEXTJOIN(\\\":\\\", TRUE, UNIQUE(A2:A3, TRUE, \\\"Hello\\\"))\": \"#VALUE!\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Error(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestTransposeFormulaArgsMatrix(t *testing.T) {\n\tassert.Empty(t, transposeFormulaArgsMatrix([][]formulaArg{}))\n}\n\nfunc TestGetFormulaUniqueArgs(t *testing.T) {\n\targsList := list.New()\n\temptyArg := newEmptyFormulaArg()\n\targsList.PushBack(emptyArg)\n\n\t_, err := getFormulaUniqueArgs(argsList)\n\tassert.Equal(t, \"missing first argument to UNIQUE\", err.Error)\n\n\targsList = list.New()\n\targsList.PushBack(newListFormulaArg([]formulaArg{newErrorFormulaArg(formulaErrorNAME, formulaErrorNAME)}))\n\t_, err = getFormulaUniqueArgs(argsList)\n\tassert.Equal(t, formulaErrorNAME, err.Error)\n}\n\nfunc TestCalcDatabase(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"Tree\", \"Height\", \"Age\", \"Yield\", \"Profit\", \"Height\"},\n\t\t{nil, \">1000%\", nil, nil, nil, \"<16\"},\n\t\t{},\n\t\t{\"Tree\", \"Height\", \"Age\", \"Yield\", \"Profit\"},\n\t\t{\"Apple\", 18, 20, 14, 105},\n\t\t{\"Pear\", 12, 12, 10, 96},\n\t\t{\"Cherry\", 13, 14, 9, 105},\n\t\t{\"Apple\", 14, nil, 10, 75},\n\t\t{\"Pear\", 9, 8, 8, 77},\n\t\t{\"Apple\", 12, 11, 6, 45},\n\t}\n\tf := prepareCalcData(cellData)\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A2\", \"\\\"=Apple\\\"\"))\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A3\", \"\\\"=Pear\\\"\"))\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C8\", \"NA()\"))\n\tformulaList := map[string]string{\n\t\t\"DAVERAGE(A4:E10,\\\"Profit\\\",A1:F3)\": \"73.25\",\n\t\t\"DCOUNT(A4:E10,\\\"Age\\\",A1:F2)\":      \"1\",\n\t\t\"DCOUNT(A4:E10,,A1:F2)\":             \"2\",\n\t\t\"DCOUNT(A4:E10,\\\"Profit\\\",A1:F2)\":   \"2\",\n\t\t\"DCOUNT(A4:E10,\\\"Tree\\\",A1:F2)\":     \"0\",\n\t\t\"DCOUNT(A4:E10,\\\"Age\\\",A2:F3)\":      \"0\",\n\t\t\"DCOUNTA(A4:E10,\\\"Age\\\",A1:F2)\":     \"1\",\n\t\t\"DCOUNTA(A4:E10,,A1:F2)\":            \"2\",\n\t\t\"DCOUNTA(A4:E10,\\\"Profit\\\",A1:F2)\":  \"2\",\n\t\t\"DCOUNTA(A4:E10,\\\"Tree\\\",A1:F2)\":    \"2\",\n\t\t\"DCOUNTA(A4:E10,\\\"Age\\\",A2:F3)\":     \"0\",\n\t\t\"DGET(A4:E6,\\\"Profit\\\",A1:F3)\":      \"96\",\n\t\t\"DMAX(A4:E10,\\\"Tree\\\",A1:F3)\":       \"0\",\n\t\t\"DMAX(A4:E10,\\\"Profit\\\",A1:F3)\":     \"96\",\n\t\t\"DMIN(A4:E10,\\\"Tree\\\",A1:F3)\":       \"0\",\n\t\t\"DMIN(A4:E10,\\\"Profit\\\",A1:F3)\":     \"45\",\n\t\t\"DPRODUCT(A4:E10,\\\"Profit\\\",A1:F3)\": \"24948000\",\n\t\t\"DSTDEV(A4:E10,\\\"Profit\\\",A1:F3)\":   \"21.077238908358\",\n\t\t\"DSTDEVP(A4:E10,\\\"Profit\\\",A1:F3)\":  \"18.2534243362718\",\n\t\t\"DSUM(A4:E10,\\\"Profit\\\",A1:F3)\":     \"293\",\n\t\t\"DVAR(A4:E10,\\\"Profit\\\",A1:F3)\":     \"444.25\",\n\t\t\"DVARP(A4:E10,\\\"Profit\\\",A1:F3)\":    \"333.1875\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A11\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"A11\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"DAVERAGE()\":                         {\"#VALUE!\", \"DAVERAGE requires 3 arguments\"},\n\t\t\"DAVERAGE(A4:E10,\\\"x\\\",A1:F3)\":       {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DAVERAGE(A4:E10,\\\"Tree\\\",A1:F3)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"DCOUNT()\":                           {\"#VALUE!\", \"DCOUNT requires at least 2 arguments\"},\n\t\t\"DCOUNT(A4:E10,\\\"Age\\\",A1:F2,\\\"\\\")\":  {\"#VALUE!\", \"DCOUNT allows at most 3 arguments\"},\n\t\t\"DCOUNT(A4,\\\"Age\\\",A1:F2)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DCOUNT(A4:E10,NA(),A1:F2)\":          {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DCOUNT(A4:E4,,A1:F2)\":               {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DCOUNT(A4:E10,\\\"x\\\",A2:F3)\":         {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DCOUNTA()\":                          {\"#VALUE!\", \"DCOUNTA requires at least 2 arguments\"},\n\t\t\"DCOUNTA(A4:E10,\\\"Age\\\",A1:F2,\\\"\\\")\": {\"#VALUE!\", \"DCOUNTA allows at most 3 arguments\"},\n\t\t\"DCOUNTA(A4,\\\"Age\\\",A1:F2)\":          {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DCOUNTA(A4:E10,NA(),A1:F2)\":         {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DCOUNTA(A4:E4,,A1:F2)\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DCOUNTA(A4:E10,\\\"x\\\",A2:F3)\":        {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DGET()\":                             {\"#VALUE!\", \"DGET requires 3 arguments\"},\n\t\t\"DGET(A1,\\\"Profit\\\",A1)\":             {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DGET(A4:E5,\\\"Profit\\\",A1:F3)\":       {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DGET(A4:E10,\\\"Profit\\\",A1:F3)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"DMAX()\":                             {\"#VALUE!\", \"DMAX requires 3 arguments\"},\n\t\t\"DMAX(A4:E10,\\\"x\\\",A1:F3)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DMIN()\":                             {\"#VALUE!\", \"DMIN requires 3 arguments\"},\n\t\t\"DMIN(A4:E10,\\\"x\\\",A1:F3)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DPRODUCT()\":                         {\"#VALUE!\", \"DPRODUCT requires 3 arguments\"},\n\t\t\"DPRODUCT(A4:E10,\\\"x\\\",A1:F3)\":       {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DSTDEV()\":                           {\"#VALUE!\", \"DSTDEV requires 3 arguments\"},\n\t\t\"DSTDEV(A4:E10,\\\"x\\\",A1:F3)\":         {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DSTDEVP()\":                          {\"#VALUE!\", \"DSTDEVP requires 3 arguments\"},\n\t\t\"DSTDEVP(A4:E10,\\\"x\\\",A1:F3)\":        {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DSUM()\":                             {\"#VALUE!\", \"DSUM requires 3 arguments\"},\n\t\t\"DSUM(A4:E10,\\\"x\\\",A1:F3)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DVAR()\":                             {\"#VALUE!\", \"DVAR requires 3 arguments\"},\n\t\t\"DVAR(A4:E10,\\\"x\\\",A1:F3)\":           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"DVARP()\":                            {\"#VALUE!\", \"DVARP requires 3 arguments\"},\n\t\t\"DVARP(A4:E10,\\\"x\\\",A1:F3)\":          {\"#VALUE!\", \"#VALUE!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A11\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"A11\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcDBCS(t *testing.T) {\n\tf := NewFile(Options{CultureInfo: CultureNameZhCN})\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", \"DBCS(\\\"`~·!@#$¥%…^&*()_-+=[]{}\\\\|;:'\\\"\\\"<,>.?/01234567890 abc ABC \\uff65\\uff9e\\uff9f \\uff74\\uff78\\uff7e\\uff99\\\")\"))\n\tresult, err := f.CalcCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"\\uff40\\uff5e\\u00b7\\uff01\\uff20\\uff03\\uff04\\u00a5\\uff05\\u2026\\uff3e\\uff06\\uff0a\\uff08\\uff09\\uff3f\\uff0d\\uff0b\\uff1d\\uff3b\\uff3d\\uff5b\\uff5d\\uff3c\\uff5c\\uff1b\\uff1a\\uff07\\uff02\\uff1c\\uff0c\\uff1e\\uff0e\\uff1f\\uff0f\\uff10\\uff11\\uff12\\uff13\\uff14\\uff15\\uff16\\uff17\\uff18\\uff19\\uff10\\u3000\\uff41\\uff42\\uff43\\u3000\\uff21\\uff22\\uff23\\u3000\\uff65\\uff9e\\uff9f\\u3000\\uff74\\uff78\\uff7e\\uff99\", result)\n}\n\nfunc TestCalcFORMULATEXT(t *testing.T) {\n\tf, formulaText := NewFile(), \"=SUM(B1:C1)\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", formulaText))\n\tfor _, formula := range []string{\"FORMULATEXT(A1)\", \"FORMULATEXT(A:A)\", \"FORMULATEXT(A1:B1)\"} {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D1\", formula), formula)\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"D1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, formulaText, result, formula)\n\t}\n}\n\nfunc TestCalcGROWTHandTREND(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"known_x's\", \"known_y's\", 0, -1},\n\t\t{1, 10, 1},\n\t\t{2, 20, 1},\n\t\t{3, 40},\n\t\t{4, 80},\n\t\t{},\n\t\t{\"new_x's\", \"new_y's\"},\n\t\t{5},\n\t\t{6},\n\t\t{7},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"GROWTH(A2:B2)\":                    \"1\",\n\t\t\"GROWTH(B2:B5,A2:A5,A8:A10)\":       \"160\",\n\t\t\"GROWTH(B2:B5,A2:A5,A8:A10,FALSE)\": \"467.842838114059\",\n\t\t\"GROWTH(A4:A5,A2:B3,A8:A10,FALSE)\": \"\",\n\t\t\"GROWTH(A3:A5,A2:B4,A2:B3)\":        \"2\",\n\t\t\"GROWTH(A4:A5,A2:B3)\":              \"\",\n\t\t\"GROWTH(A2:B2,A2:B3)\":              \"\",\n\t\t\"GROWTH(A2:B2,A2:B3,A2:B3,FALSE)\":  \"1.28402541668774\",\n\t\t\"GROWTH(A2:B2,A4:B5,A4:B5,FALSE)\":  \"1\",\n\t\t\"GROWTH(A3:C3,A2:C3,A2:B3)\":        \"2\",\n\t\t\"TREND(A2:B2)\":                     \"1\",\n\t\t\"TREND(B2:B5,A2:A5,A8:A10)\":        \"95\",\n\t\t\"TREND(B2:B5,A2:A5,A8:A10,FALSE)\":  \"81.6666666666667\",\n\t\t\"TREND(A4:A5,A2:B3,A8:A10,FALSE)\":  \"\",\n\t\t\"TREND(A4:A5,A2:B3,A2:B3,FALSE)\":   \"1.5\",\n\t\t\"TREND(A3:A5,A2:B4,A2:B3)\":         \"2\",\n\t\t\"TREND(A4:A5,A2:B3)\":               \"\",\n\t\t\"TREND(A2:B2,A2:B3)\":               \"\",\n\t\t\"TREND(A2:B2,A2:B3,A2:B3,FALSE)\":   \"1\",\n\t\t\"TREND(A2:B2,A4:B5,A4:B5,FALSE)\":   \"1\",\n\t\t\"TREND(A3:C3,A2:C3,A2:B3)\":         \"2\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"GROWTH()\":                          {\"#VALUE!\", \"GROWTH requires at least 1 argument\"},\n\t\t\"GROWTH(B2:B5,A2:A5,A8:A10,TRUE,0)\": {\"#VALUE!\", \"GROWTH allows at most 4 arguments\"},\n\t\t\"GROWTH(A1:B1,A2:A5,A8:A10,TRUE)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"GROWTH(B2:B5,A1:B1,A8:A10,TRUE)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"GROWTH(B2:B5,A2:A5,A1:B1,TRUE)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"GROWTH(B2:B5,A2:A5,A8:A10,\\\"\\\")\":   {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"GROWTH(A2:B3,A4:B4)\":               {\"#REF!\", \"#REF!\"},\n\t\t\"GROWTH(A4:B4,A2:A2)\":               {\"#REF!\", \"#REF!\"},\n\t\t\"GROWTH(A2:A2,A4:A5)\":               {\"#REF!\", \"#REF!\"},\n\t\t\"GROWTH(C1:C1,A2:A3)\":               {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"GROWTH(D1:D1,A2:A3)\":               {\"#NUM!\", \"#NUM!\"},\n\t\t\"GROWTH(A2:A3,C1:C1)\":               {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TREND()\":                           {\"#VALUE!\", \"TREND requires at least 1 argument\"},\n\t\t\"TREND(B2:B5,A2:A5,A8:A10,TRUE,0)\":  {\"#VALUE!\", \"TREND allows at most 4 arguments\"},\n\t\t\"TREND(A1:B1,A2:A5,A8:A10,TRUE)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TREND(B2:B5,A1:B1,A8:A10,TRUE)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TREND(B2:B5,A2:A5,A1:B1,TRUE)\":     {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TREND(B2:B5,A2:A5,A8:A10,\\\"\\\")\":    {\"#VALUE!\", \"strconv.ParseBool: parsing \\\"\\\": invalid syntax\"},\n\t\t\"TREND(A2:B3,A4:B4)\":                {\"#REF!\", \"#REF!\"},\n\t\t\"TREND(A4:B4,A2:A2)\":                {\"#REF!\", \"#REF!\"},\n\t\t\"TREND(A2:A2,A4:A5)\":                {\"#REF!\", \"#REF!\"},\n\t\t\"TREND(C1:C1,A2:A3)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TREND(D1:D1,A2:A3)\":                {\"#REF!\", \"#REF!\"},\n\t\t\"TREND(A2:A3,C1:C1)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TREND(C1:C1,C1:C1)\":                {\"#VALUE!\", \"#VALUE!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcHLOOKUP(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"Example Result Table\"},\n\t\t{nil, \"A\", \"B\", \"C\", \"E\", \"F\"},\n\t\t{\"Math\", .58, .9, .67, .76, .8},\n\t\t{\"French\", .61, .71, .59, .59, .76},\n\t\t{\"Physics\", .75, .45, .39, .52, .69},\n\t\t{\"Biology\", .39, .55, .77, .61, .45},\n\t\t{},\n\t\t{\"Individual Student Score\"},\n\t\t{\"Student:\", \"Biology Score:\"},\n\t\t{\"E\"},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"HLOOKUP(A10,A2:F6,5,FALSE)\":  \"0.61\",\n\t\t\"HLOOKUP(D3,D3:D3,1,TRUE)\":    \"0.67\",\n\t\t\"HLOOKUP(F3,D3:F3,1,TRUE)\":    \"0.8\",\n\t\t\"HLOOKUP(A5,A2:F2,1,TRUE)\":    \"F\",\n\t\t\"HLOOKUP(\\\"D\\\",A2:F2,1,TRUE)\": \"C\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B10\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"B10\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"HLOOKUP(INT(1),A3:A3,1,FALSE)\": {\"#N/A\", \"HLOOKUP no result found\"},\n\t\t\"HLOOKUP(4,A1:E1048576,2,TRUE)\": {\"#N/A\", \"HLOOKUP no result found\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B10\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"B10\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcCHITESTandCHISQdotTEST(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{nil, \"Observed Frequencies\", nil, nil, \"Expected Frequencies\"},\n\t\t{nil, \"men\", \"women\", nil, nil, \"men\", \"women\"},\n\t\t{\"answer a\", 33, 39, nil, \"answer a\", 26.25, 31.5},\n\t\t{\"answer b\", 62, 62, nil, \"answer b\", 57.75, 61.95},\n\t\t{\"answer c\", 10, 4, nil, \"answer c\", 21, 11.55},\n\t\t{nil, -1, 0},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"CHITEST(B3:C5,F3:G5)\":    \"0.000699102758787672\",\n\t\t\"CHITEST(B3:C3,F3:G3)\":    \"0.0605802098655177\",\n\t\t\"CHITEST(B3:B4,F3:F4)\":    \"0.152357748933542\",\n\t\t\"CHITEST(B4:B6,F3:F5)\":    \"7.07076951440726E-25\",\n\t\t\"CHISQ.TEST(B3:C5,F3:G5)\": \"0.000699102758787672\",\n\t\t\"CHISQ.TEST(B3:C3,F3:G3)\": \"0.0605802098655177\",\n\t\t\"CHISQ.TEST(B3:B4,F3:F4)\": \"0.152357748933542\",\n\t\t\"CHISQ.TEST(B4:B6,F3:F5)\": \"7.07076951440726E-25\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"I1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"I1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"CHITEST()\":                  {\"#VALUE!\", \"CHITEST requires 2 arguments\"},\n\t\t\"CHITEST(MUNIT(0),MUNIT(0))\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"CHITEST(B3:C5,F3:F4)\":       {\"#N/A\", \"#N/A\"},\n\t\t\"CHITEST(B3:B3,F3:F3)\":       {\"#N/A\", \"#N/A\"},\n\t\t\"CHITEST(F3:F5,B4:B6)\":       {\"#NUM!\", \"#NUM!\"},\n\t\t\"CHITEST(F3:F5,C4:C6)\":       {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"CHISQ.TEST()\":               {\"#VALUE!\", \"CHISQ.TEST requires 2 arguments\"},\n\t\t\"CHISQ.TEST(B3:C5,F3:F4)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"CHISQ.TEST(B3:B3,F3:F3)\":    {\"#N/A\", \"#N/A\"},\n\t\t\"CHISQ.TEST(F3:F5,B4:B6)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"CHISQ.TEST(F3:F5,C4:C6)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"I1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"I1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcFTEST(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"Group 1\", \"Group 2\"},\n\t\t{3.5, 9.2},\n\t\t{4.7, 8.2},\n\t\t{6.2, 7.3},\n\t\t{4.9, 6.1},\n\t\t{3.8, 5.4},\n\t\t{5.5, 7.8},\n\t\t{7.1, 5.9},\n\t\t{6.7, 8.4},\n\t\t{3.9, 7.7},\n\t\t{4.6, 6.6},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"FTEST(A2:A11,B2:B11)\":  \"0.95403555939413\",\n\t\t\"F.TEST(A2:A11,B2:B11)\": \"0.95403555939413\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"FTEST()\":               {\"#VALUE!\", \"FTEST requires 2 arguments\"},\n\t\t\"FTEST(A2:A2,B2:B2)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"FTEST(A12:A14,B2:B4)\":  {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"FTEST(A2:A4,B2:B2)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"FTEST(A2:A4,B12:B14)\":  {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"F.TEST()\":              {\"#VALUE!\", \"F.TEST requires 2 arguments\"},\n\t\t\"F.TEST(A2:A2,B2:B2)\":   {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"F.TEST(A12:A14,B2:B4)\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"F.TEST(A2:A4,B2:B2)\":   {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"F.TEST(A2:A4,B12:B14)\": {\"#DIV/0!\", \"#DIV/0!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcIRR(t *testing.T) {\n\tcellData := [][]interface{}{{-1}, {0.2}, {0.24}, {0.288}, {0.3456}, {0.4147}}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"IRR(A1:A4)\":      \"-0.136189509034157\",\n\t\t\"IRR(A1:A6)\":      \"0.130575760006905\",\n\t\t\"IRR(A1:A4,-0.1)\": \"-0.136189514994621\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"IRR()\":       {\"#VALUE!\", \"IRR requires at least 1 argument\"},\n\t\t\"IRR(0,0,0)\":  {\"#VALUE!\", \"IRR allows at most 2 arguments\"},\n\t\t\"IRR(0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"IRR(A2:A3)\":  {\"#NUM!\", \"#NUM!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"B1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcMAXMINIFS(t *testing.T) {\n\tf := NewFile()\n\tfor cell, row := range map[string][]interface{}{\n\t\t\"A1\": {1, -math.MaxFloat64 - 1},\n\t\t\"A2\": {2, -math.MaxFloat64 - 2},\n\t\t\"A3\": {3, math.MaxFloat64 + 1},\n\t\t\"A4\": {4, math.MaxFloat64 + 2},\n\t} {\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", cell, &row))\n\t}\n\tformulaList := map[string]string{\n\t\t\"MAX(B1:B2)\":                 \"0\",\n\t\t\"MAXIFS(B1:B2,A1:A2,\\\">0\\\")\": \"0\",\n\t\t\"MIN(B3:B4)\":                 \"0\",\n\t\t\"MINIFS(B3:B4,A3:A4,\\\"<0\\\")\": \"0\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestCalcMIRR(t *testing.T) {\n\tcellData := [][]interface{}{{-100}, {18}, {22.5}, {28}, {35.5}, {45}}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"MIRR(A1:A6,0.055,0.05)\": \"0.1000268752662\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"MIRR()\":             {\"#VALUE!\", \"MIRR requires 3 arguments\"},\n\t\t\"MIRR(A1:A5,\\\"\\\",0)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"MIRR(A1:A5,0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"MIRR(B1:B5,0,0)\":    {\"#DIV/0!\", \"#DIV/0!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"B1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcSUMIFSAndAVERAGEIFS(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"Quarter\", \"Area\", \"Sales Rep.\", \"Sales\"},\n\t\t{1, \"North\", \"Jeff\", 223000},\n\t\t{1, \"North\", \"Chris\", 125000},\n\t\t{1, \"South\", \"Carol\", 456000},\n\t\t{2, \"North\", \"Jeff\", 322000},\n\t\t{2, \"North\", \"Chris\", 340000},\n\t\t{2, \"South\", \"Carol\", 198000},\n\t\t{3, \"North\", \"Jeff\", 310000},\n\t\t{3, \"North\", \"Chris\", 250000},\n\t\t{3, \"South\", \"Carol\", 460000},\n\t\t{4, \"North\", \"Jeff\", 261000},\n\t\t{4, \"North\", \"Chris\", 389000},\n\t\t{4, \"South\", \"Carol\", 305000},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"AVERAGEIFS(D2:D13,A2:A13,1,B2:B13,\\\"North\\\")\":                \"174000\",\n\t\t\"AVERAGEIFS(D2:D13,A2:A13,\\\">2\\\",C2:C13,\\\"Jeff\\\")\":            \"285500\",\n\t\t\"SUMIFS(D2:D13,A2:A13,1,B2:B13,\\\"North\\\")\":                    \"348000\",\n\t\t\"SUMIFS(D2:D13,A2:A13,\\\">2\\\",C2:C13,\\\"Jeff\\\")\":                \"571000\",\n\t\t\"SUMIFS(D2:D13,A2:A13,1,D2:D13,125000)\":                       \"125000\",\n\t\t\"SUMIFS(D2:D13,A2:A13,1,D2:D13,\\\">100000\\\",C2:C13,\\\"Chris\\\")\": \"125000\",\n\t\t\"SUMIFS(D2:D13,A2:A13,1,D2:D13,\\\"<40000\\\",C2:C13,\\\"Chris\\\")\":  \"0\",\n\t\t\"SUMIFS(D2:D13,A2:A13,1,A2:A13,2)\":                            \"0\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"E1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"E1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"AVERAGEIFS()\":                                  {\"#VALUE!\", \"AVERAGEIFS requires at least 3 arguments\"},\n\t\t\"AVERAGEIFS(H1,\\\"\\\")\":                           {\"#VALUE!\", \"AVERAGEIFS requires at least 3 arguments\"},\n\t\t\"AVERAGEIFS(H1,\\\"\\\",TRUE,1)\":                    {\"#N/A\", \"#N/A\"},\n\t\t\"AVERAGEIFS(H1,\\\"\\\",TRUE)\":                      {\"#DIV/0!\", \"AVERAGEIF divide by zero\"},\n\t\t\"AVERAGEIFS(D2:D13,A2:A13,1,A2:A13,2)\":          {\"#DIV/0!\", \"AVERAGEIF divide by zero\"},\n\t\t\"SUMIFS()\":                                      {\"#VALUE!\", \"SUMIFS requires at least 3 arguments\"},\n\t\t\"SUMIFS(D2:D13,A2:A13,1,B2:B13)\":                {\"#N/A\", \"#N/A\"},\n\t\t\"SUMIFS(D20:D23,A2:A13,\\\">2\\\",C2:C13,\\\"Jeff\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"E1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"E1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcSUMIFExactMatch(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"Category\", \"Amount\"},\n\t\t{\"text\", 100},\n\t\t{\"***_***_text\", 200},\n\t\t{\"text\", 150},\n\t\t{\"***_text_***\", 300},\n\t\t{\"TEXT\", 50},\n\t\t{\"other\", 400},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t`SUMIF(A2:A7,\"text\",B2:B7)`:           \"300\",\n\t\t`COUNTIF(A2:A7,\"text\")`:               \"3\",\n\t\t`SUMIF(A2:A7,\".\",B2:B7)`:              \"0\",\n\t\t`SUMIF(A2:A7,\".*\",B2:B7)`:             \"0\",\n\t\t`SUMIF(A2:A7,\".?\",B2:B7)`:             \"0\",\n\t\t`SUMIF(A2:A7,\"*text*\",B2:B7)`:         \"800\",\n\t\t`SUMIF(A2:A7,\"text*\",B2:B7)`:          \"300\",\n\t\t`SUMIF(A2:A7,\"*text\",B2:B7)`:          \"500\",\n\t\t`SUMIF(A2:A7,\"*\",B2:B7)`:              \"1200\",\n\t\t`SUMIF(A2:A7,\"othe?\",B2:B7)`:          \"400\",\n\t\t`SUMIF(A2:A7,\"~**\",B2:B7)`:            \"500\",\n\t\t`COUNTIF(A2:A7,\"*text*\")`:             \"5\",\n\t\t`COUNTIF(A2:A7,\"~*\")`:                 \"0\",\n\t\t`COUNTIF(A2:A7,\"~~\")`:                 \"0\",\n\t\t`COUNTIF(A2:A7,\"~?\")`:                 \"0\",\n\t\t`COUNTIF(A2:A7,\"*~**\")`:               \"2\",\n\t\t`COUNTIF(A2:A7,\"~*~*~*_~*~*~*_text\")`: \"1\",\n\t\t`COUNTIF(A2:A7,\"~*~*~*_text_~*~*~*\")`: \"1\",\n\t\t`COUNTIF(A2:A7,\"~a\")`:                 \"0\",\n\t\t`COUNTIF(A2:A7,\"<>text\")`:             \"3\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcellData = [][]interface{}{\n\t\t{\"Category\", \"Amount\"},\n\t\t{\"text\", 10},\n\t\t{\"***text\", 20},\n\t\t{\"text ***\", 30},\n\t\t{\"TEXT\", 40},\n\t\t{5, 50},\n\t\t{5.5, 60},\n\t}\n\tf = prepareCalcData(cellData)\n\tfor formula, expected := range map[string]string{\n\t\t`SUMIF(A2:A7,\"text\",B2:B7)`:  \"50\",\n\t\t`SUMIF(A2:A7,\"*text\",B2:B7)`: \"70\",\n\t\t`SUMIF(A2:A7,\"text*\",B2:B7)`: \"80\",\n\t\t`SUMIF(A2:A7,5,B2:B7)`:       \"50\",\n\t\t`COUNTIF(A2:A7,\"tex?\")`:      \"2\",\n\t} {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestCalcXIRR(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{-100.00, 42370},\n\t\t{20.00, 42461},\n\t\t{40.00, 42644},\n\t\t{25.00, 42767},\n\t\t{8.00, 42795},\n\t\t{15.00, 42887},\n\t\t{-1e-10, 42979},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"XIRR(A1:A4,B1:B4)\":     \"-0.196743861298328\",\n\t\t\"XIRR(A1:A6,B1:B6,0.5)\": \"0.0944390744445204\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"XIRR()\":                 {\"#VALUE!\", \"XIRR requires 2 or 3 arguments\"},\n\t\t\"XIRR(A1:A4,B1:B4,-1)\":   {\"#VALUE!\", \"XIRR requires guess > -1\"},\n\t\t\"XIRR(\\\"\\\",B1:B4)\":       {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XIRR(A1:A4,\\\"\\\")\":       {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XIRR(A1:A4,B1:B4,\\\"\\\")\": {\"#NUM!\", \"#NUM!\"},\n\t\t\"XIRR(A2:A6,B2:B6)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"XIRR(A2:A7,B2:B7)\":      {\"#NUM!\", \"#NUM!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcXLOOKUP(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{},\n\t\t{nil, nil, \"Quarter\", \"Gross Profit\", \"Net profit\", \"Profit %\"},\n\t\t{nil, nil, \"Qtr1\", nil, 19342, 29.30},\n\t\t{},\n\t\t{nil, \"Income Statement\", \"Qtr1\", \"Qtr2\", \"Qtr3\", \"Qtr4\", \"Total\"},\n\t\t{nil, \"Total sales\", 50000, 78200, 89500, 91250, 308.95},\n\t\t{nil, \"Cost of sales\", -25000, -42050, -59450, -60450, -186950},\n\t\t{nil, \"Gross Profit\", 25000, 36150, 30050, 30800, 122000},\n\t\t{},\n\t\t{nil, \"Depreciation\", -899, -791, -202, -412, -2304},\n\t\t{nil, \"Interest\", -513, -853, -150, -956, -2472},\n\t\t{nil, \"Earnings before Tax\", 23588, 34506, 29698, 29432, 117224},\n\t\t{},\n\t\t{nil, \"Tax\", -4246, -6211, -5346, -5298, 21100},\n\t\t{},\n\t\t{nil, \"Net profit\", 19342, 28295, 24352, 24134, 96124},\n\t\t{nil, \"Profit %\", 0.293, 0.278, 0.234, 0.276, 0.269},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"SUM(XLOOKUP($C3,$C5:$C5,$C6:$C17,NA(),0,2))\":        \"87272.293\",\n\t\t\"SUM(XLOOKUP($C3,$C5:$C5,$C6:$G6,NA(),0,-2))\":        \"309258.95\",\n\t\t\"SUM(XLOOKUP($C3,$C5:$C5,$C6:$C17,NA(),0,-2))\":       \"87272.293\",\n\t\t\"SUM(XLOOKUP($C3,$C5:$G5,$C6:$G17,NA(),0,2))\":        \"87272.293\",\n\t\t\"SUM(XLOOKUP(D2,$B6:$B17,$C6:$G17,NA(),0,2))\":        \"244000\",\n\t\t\"XLOOKUP(D2,$B6:$B17,C6:C17)\":                        \"25000\",\n\t\t\"XLOOKUP(D2,$B6:$B17,XLOOKUP($C3,$C5:$G5,$C6:$G17))\": \"25000\",\n\t\t\"XLOOKUP(\\\"*p*\\\",B2:B9,C2:C9,NA(),2)\":                \"25000\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D3\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"D3\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"XLOOKUP()\": {\"#VALUE!\", \"XLOOKUP requires at least 3 arguments\"},\n\t\t\"XLOOKUP($C3,$C5:$C5,$C6:$C17,NA(),0,2,1)\":  {\"#VALUE!\", \"XLOOKUP allows at most 6 arguments\"},\n\t\t\"XLOOKUP($C3,$C5,$C6,NA(),0,2)\":             {\"#N/A\", \"#N/A\"},\n\t\t\"XLOOKUP($C3,$C4:$D5,$C6:$C17,NA(),0,2)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XLOOKUP($C3,$C5:$C5,$C6:$G17,NA(),0,-2)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XLOOKUP($C3,$C5:$G5,$C6:$F7,NA(),0,2)\":     {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XLOOKUP(D2,$B6:$B17,$C6:$G16,NA(),0,2)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XLOOKUP(D2,$B6:$B17,$C6:$G17,NA(),3,2)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XLOOKUP(D2,$B6:$B17,$C6:$G17,NA(),0,0)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XLOOKUP(D2,$B6:$B17,$C6:$G17,NA(),\\\"\\\",2)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"XLOOKUP(D2,$B6:$B17,$C6:$G17,NA(),0,\\\"\\\")\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D3\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"D3\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n\n\tcellData = [][]interface{}{\n\t\t{\"Salesperson\", \"Item\", \"Amont\"},\n\t\t{\"B\", \"Apples\", 30, 25, 15, 50, 45, 18},\n\t\t{\"L\", \"Oranges\", 25, \"D3\", \"E3\"},\n\t\t{\"C\", \"Grapes\", 15},\n\t\t{\"L\", \"Lemons\", 50},\n\t\t{\"L\", \"Oranges\", 45},\n\t\t{\"C\", \"Peaches\", 18},\n\t\t{\"B\", \"Pears\", 40},\n\t\t{\"B\", \"Apples\", 55},\n\t}\n\tf = prepareCalcData(cellData)\n\tformulaList = map[string]string{\n\t\t// Test match mode with partial match (wildcards)\n\t\t\"XLOOKUP(\\\"*p*\\\",B2:B9,C2:C9,NA(),2)\": \"30\",\n\t\t// Test match mode with approximate match in vertical (next larger item)\n\t\t\"XLOOKUP(32,B2:B9,C2:C9,NA(),1)\": \"30\",\n\t\t// Test match mode with approximate match in horizontal (next larger item)\n\t\t\"XLOOKUP(30,C2:F2,C3:F3,NA(),1)\": \"25\",\n\t\t// Test match mode with approximate match in vertical (next smaller item)\n\t\t\"XLOOKUP(40,C2:C9,B2:B9,NA(),-1)\": \"Pears\",\n\t\t// Test match mode with approximate match in horizontal (next smaller item)\n\t\t\"XLOOKUP(29,C2:F2,C3:F3,NA(),-1)\": \"D3\",\n\t\t// Test search mode\n\t\t\"XLOOKUP(\\\"L\\\",A2:A9,C2:C9,NA(),0,1)\":  \"25\",\n\t\t\"XLOOKUP(\\\"L\\\",A2:A9,C2:C9,NA(),0,-1)\": \"45\",\n\t\t\"XLOOKUP(\\\"L\\\",A2:A9,C2:C9,NA(),0,2)\":  \"50\",\n\t\t\"XLOOKUP(\\\"L\\\",A2:A9,C2:C9,NA(),0,-2)\": \"45\",\n\t\t// Test match mode and search mode\n\t\t\"XLOOKUP(29,C2:H2,C3:H3,NA(),-1,-1)\": \"D3\",\n\t\t\"XLOOKUP(29,C2:H2,C3:H3,NA(),-1,1)\":  \"D3\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D4\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"D4\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError = map[string][]string{\n\t\t// Test match mode with exact match\n\t\t\"XLOOKUP(\\\"*p*\\\",B2:B9,C2:C9,NA(),0)\": {\"#N/A\", \"#N/A\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D3\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"D3\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcXNPV(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{nil, 0.05},\n\t\t{42370, -10000, nil},\n\t\t{42401, 2000},\n\t\t{42491, 2400},\n\t\t{42552, 2900},\n\t\t{42675, 3500},\n\t\t{42736, 4100},\n\t\t{},\n\t\t{42401},\n\t\t{42370},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"XNPV(B1,B2:B7,A2:A7)\": \"4447.93800944052\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"XNPV()\":                 {\"#VALUE!\", \"XNPV requires 3 arguments\"},\n\t\t\"XNPV(\\\"\\\",B2:B7,A2:A7)\": {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"XNPV(0,B2:B7,A2:A7)\":    {\"#VALUE!\", \"XNPV requires rate > 0\"},\n\t\t\"XNPV(B1,\\\"\\\",A2:A7)\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XNPV(B1,B2:B7,\\\"\\\")\":    {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XNPV(B1,B2:B7,C2:C7)\":   {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"XNPV(B1,B2,A2)\":         {\"#NUM!\", \"#NUM!\"},\n\t\t\"XNPV(B1,B2:B3,A2:A5)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"XNPV(B1,B2:B3,A9:A10)\":  {\"#VALUE!\", \"#VALUE!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcMATCH(t *testing.T) {\n\tf := NewFile()\n\tfor cell, row := range map[string][]interface{}{\n\t\t\"A1\": {\"cccc\", 7, 4, 16},\n\t\t\"A2\": {\"dddd\", 2, 6, 11},\n\t\t\"A3\": {\"aaaa\", 4, 7, 10},\n\t\t\"A4\": {\"bbbb\", 1, 10, 7},\n\t\t\"A5\": {\"eeee\", 8, 11, 6},\n\t\t\"A6\": {nil, 11, 16, 4},\n\t} {\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", cell, &row))\n\t}\n\tformulaList := map[string]string{\n\t\t\"MATCH(\\\"aaaa\\\",A1:A6,0)\": \"3\",\n\t\t\"MATCH(\\\"*b\\\",A1:A5,0)\":   \"4\",\n\t\t\"MATCH(\\\"?eee\\\",A1:A5,0)\": \"5\",\n\t\t\"MATCH(\\\"?*?e\\\",A1:A5,0)\": \"5\",\n\t\t\"MATCH(\\\"aaaa\\\",A1:A6,1)\": \"3\",\n\t\t\"MATCH(10,B1:B6)\":         \"5\",\n\t\t\"MATCH(8,C1:C6,1)\":        \"3\",\n\t\t\"MATCH(6,B1:B6,-1)\":       \"1\",\n\t\t\"MATCH(10,D1:D6,-1)\":      \"3\",\n\t\t\"MATCH(-10,D1:D6,-1)\":     \"6\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"E1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"E1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string]string{\n\t\t\"MATCH(3,C1:C6,1)\":  \"#N/A\",\n\t\t\"MATCH(5,C1:C6,-1)\": \"#N/A\",\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"E1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"E1\")\n\t\tassert.EqualError(t, err, expected, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tassert.Equal(t, newErrorFormulaArg(formulaErrorNA, formulaErrorNA), calcMatch(2, nil, []formulaArg{}))\n}\n\nfunc TestCalcISFORMULA(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"ISFORMULA(A1)\"))\n\tfor _, formula := range []string{\"NA()\", \"SUM(A1:A3)\"} {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"B1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, \"TRUE\", result, formula)\n\t}\n}\n\nfunc TestCalcMODE(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{1, 1},\n\t\t{1, 1},\n\t\t{2, 2},\n\t\t{2, 2},\n\t\t{3, 2},\n\t\t{3},\n\t\t{3},\n\t\t{4},\n\t\t{4},\n\t\t{4},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"MODE(A1:A10)\":      \"3\",\n\t\t\"MODE(B1:B6)\":       \"2\",\n\t\t\"MODE.MULT(A1:A10)\": \"3\",\n\t\t\"MODE.SNGL(A1:A10)\": \"3\",\n\t\t\"MODE.SNGL(B1:B6)\":  \"2\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"MODE()\":            {\"#VALUE!\", \"MODE requires at least 1 argument\"},\n\t\t\"MODE(0,\\\"\\\")\":      {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MODE(D1:D3)\":       {\"#N/A\", \"#N/A\"},\n\t\t\"MODE.MULT()\":       {\"#VALUE!\", \"MODE.MULT requires at least 1 argument\"},\n\t\t\"MODE.MULT(0,\\\"\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MODE.MULT(D1:D3)\":  {\"#N/A\", \"#N/A\"},\n\t\t\"MODE.SNGL()\":       {\"#VALUE!\", \"MODE.SNGL requires at least 1 argument\"},\n\t\t\"MODE.SNGL(0,\\\"\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"MODE.SNGL(D1:D3)\":  {\"#N/A\", \"#N/A\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcPEARSON(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"x\", \"y\"},\n\t\t{1, 10.11},\n\t\t{2, 22.9},\n\t\t{2, 27.61},\n\t\t{3, 27.61},\n\t\t{4, 11.15},\n\t\t{5, 31.08},\n\t\t{6, 37.9},\n\t\t{7, 33.49},\n\t\t{8, 21.05},\n\t\t{9, 27.01},\n\t\t{10, 45.78},\n\t\t{11, 31.32},\n\t\t{12, 50.57},\n\t\t{13, 45.48},\n\t\t{14, 40.94},\n\t\t{15, 53.76},\n\t\t{16, 36.18},\n\t\t{17, 49.77},\n\t\t{18, 55.66},\n\t\t{19, 63.83},\n\t\t{20, 63.6},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"PEARSON(A2:A22,B2:B22)\": \"0.864129542184994\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestCalcPROB(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"x\", \"probability\"},\n\t\t{0, 0.1},\n\t\t{1, 0.15},\n\t\t{2, 0.17},\n\t\t{3, 0.22},\n\t\t{4, 0.21},\n\t\t{5, 0.09},\n\t\t{6, 0.05},\n\t\t{7, 0.01},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"PROB(A2:A9,B2:B9,3)\":    \"0.22\",\n\t\t\"PROB(A2:A9,B2:B9,3,5)\":  \"0.52\",\n\t\t\"PROB(A2:A9,B2:B9,8,10)\": \"0\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A2\", \"NA()\"))\n\tcalcError := map[string][]string{\n\t\t\"PROB(A2:A9,B2:B9,3)\": {\"#NUM!\", \"#NUM!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcRSQ(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"known_y's\", \"known_x's\"},\n\t\t{2, 22.9},\n\t\t{7, 33.49},\n\t\t{8, 34.5},\n\t\t{3, 27.61},\n\t\t{4, 19.5},\n\t\t{1, 10.11},\n\t\t{6, 37.9},\n\t\t{5, 31.08},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"RSQ(A2:A9,B2:B9)\": \"0.711666290486784\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestCalcSLOP(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"known_x's\", \"known_y's\"},\n\t\t{1, 3},\n\t\t{2, 7},\n\t\t{3, 17},\n\t\t{4, 20},\n\t\t{5, 20},\n\t\t{6, 27},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"SLOPE(A2:A7,B2:B7)\": \"0.200826446280992\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestCalcSHEET(t *testing.T) {\n\tf := NewFile()\n\t_, err := f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tformulaList := map[string]string{\n\t\t\"SHEET(\\\"Sheet2\\\")\":   \"2\",\n\t\t\"SHEET(Sheet2!A1)\":    \"2\",\n\t\t\"SHEET(Sheet2!A1:A2)\": \"2\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"A1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestCalcSHEETS(t *testing.T) {\n\tf := NewFile()\n\t_, err := f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tformulaList := map[string]string{\n\t\t\"SHEETS(Sheet1!A1:B1)\":        \"1\",\n\t\t\"SHEETS(Sheet1!A1:Sheet1!B1)\": \"1\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"A1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestCalcSTEY(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"known_x's\", \"known_y's\"},\n\t\t{1, 3},\n\t\t{2, 7.9},\n\t\t{3, 8},\n\t\t{4, 9.2},\n\t\t{4.5, 12},\n\t\t{5, 10.5},\n\t\t{6, 15},\n\t\t{7, 15.5},\n\t\t{8, 17},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"STEYX(B2:B11,A2:A11)\": \"1.20118634668221\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"STEYX()\":             {\"#VALUE!\", \"STEYX requires 2 arguments\"},\n\t\t\"STEYX(B2:B11,A1:A9)\": {\"#N/A\", \"#N/A\"},\n\t\t\"STEYX(B2,A2)\":        {\"#DIV/0!\", \"#DIV/0!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcTTEST(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{4, 8, nil, 1, 1},\n\t\t{5, 3, nil, 1, 1},\n\t\t{2, 7},\n\t\t{5, 3},\n\t\t{8, 5},\n\t\t{9, 2},\n\t\t{3, 2},\n\t\t{2, 7},\n\t\t{3, 9},\n\t\t{8, 4},\n\t\t{9, 4},\n\t\t{5, 7},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"TTEST(A1:A12,B1:B12,1,1)\":  \"0.44907068944428\",\n\t\t\"TTEST(A1:A12,B1:B12,1,2)\":  \"0.436717306029283\",\n\t\t\"TTEST(A1:A12,B1:B12,1,3)\":  \"0.436722015384755\",\n\t\t\"TTEST(A1:A12,B1:B12,2,1)\":  \"0.898141378888559\",\n\t\t\"TTEST(A1:A12,B1:B12,2,2)\":  \"0.873434612058567\",\n\t\t\"TTEST(A1:A12,B1:B12,2,3)\":  \"0.873444030769511\",\n\t\t\"T.TEST(A1:A12,B1:B12,1,1)\": \"0.44907068944428\",\n\t\t\"T.TEST(A1:A12,B1:B12,1,2)\": \"0.436717306029283\",\n\t\t\"T.TEST(A1:A12,B1:B12,1,3)\": \"0.436722015384755\",\n\t\t\"T.TEST(A1:A12,B1:B12,2,1)\": \"0.898141378888559\",\n\t\t\"T.TEST(A1:A12,B1:B12,2,2)\": \"0.873434612058567\",\n\t\t\"T.TEST(A1:A12,B1:B12,2,3)\": \"0.873444030769511\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"TTEST()\":                      {\"#VALUE!\", \"TTEST requires 4 arguments\"},\n\t\t\"TTEST(\\\"\\\",B1:B12,1,1)\":       {\"#NUM!\", \"#NUM!\"},\n\t\t\"TTEST(A1:A12,\\\"\\\",1,1)\":       {\"#NUM!\", \"#NUM!\"},\n\t\t\"TTEST(A1:A12,B1:B12,\\\"\\\",1)\":  {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TTEST(A1:A12,B1:B12,1,\\\"\\\")\":  {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"TTEST(A1:A12,B1:B12,0,1)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"TTEST(A1:A12,B1:B12,1,0)\":     {\"#NUM!\", \"#NUM!\"},\n\t\t\"TTEST(A1:A2,B1:B1,1,1)\":       {\"#N/A\", \"#N/A\"},\n\t\t\"TTEST(A13:A14,B13:B14,1,1)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"TTEST(A12:A13,B12:B13,1,1)\":   {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"TTEST(A13:A14,B13:B14,1,2)\":   {\"#NUM!\", \"#NUM!\"},\n\t\t\"TTEST(D1:D4,E1:E4,1,3)\":       {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.TEST()\":                     {\"#VALUE!\", \"T.TEST requires 4 arguments\"},\n\t\t\"T.TEST(\\\"\\\",B1:B12,1,1)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.TEST(A1:A12,\\\"\\\",1,1)\":      {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.TEST(A1:A12,B1:B12,\\\"\\\",1)\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"T.TEST(A1:A12,B1:B12,1,\\\"\\\")\": {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"T.TEST(A1:A12,B1:B12,0,1)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.TEST(A1:A12,B1:B12,1,0)\":    {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.TEST(A1:A2,B1:B1,1,1)\":      {\"#N/A\", \"#N/A\"},\n\t\t\"T.TEST(A13:A14,B13:B14,1,1)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.TEST(A12:A13,B12:B13,1,1)\":  {\"#DIV/0!\", \"#DIV/0!\"},\n\t\t\"T.TEST(A13:A14,B13:B14,1,2)\":  {\"#NUM!\", \"#NUM!\"},\n\t\t\"T.TEST(D1:D4,E1:E4,1,3)\":      {\"#NUM!\", \"#NUM!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcNETWORKDAYSandWORKDAY(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"05/01/2019\", 43586, \"text1\"},\n\t\t{\"09/13/2019\", 43721, \"text2\"},\n\t\t{\"10/01/2019\", 43739},\n\t\t{\"12/25/2019\", 43824},\n\t\t{\"01/01/2020\", 43831},\n\t\t{\"01/01/2020\", 43831},\n\t\t{\"01/24/2020\", 43854},\n\t\t{\"04/04/2020\", 43925},\n\t\t{\"05/01/2020\", 43952},\n\t\t{\"06/25/2020\", 44007},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"NETWORKDAYS(\\\"01/01/2020\\\",\\\"09/12/2020\\\")\":               \"183\",\n\t\t\"NETWORKDAYS(\\\"01/01/2020\\\",\\\"09/12/2020\\\",2)\":             \"183\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\")\":          \"183\",\n\t\t\"NETWORKDAYS.INTL(\\\"09/12/2020\\\",\\\"01/01/2020\\\")\":          \"-183\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",1)\":        \"183\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",2)\":        \"184\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",3)\":        \"184\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",4)\":        \"183\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",5)\":        \"182\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",6)\":        \"182\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",7)\":        \"182\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",11)\":       \"220\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",12)\":       \"220\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",13)\":       \"220\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",14)\":       \"219\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",15)\":       \"219\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",16)\":       \"219\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",17)\":       \"219\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",1,A1:A12)\": \"178\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",1,B1:B12)\": \"178\",\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",1,C1:C2)\":  \"183\",\n\t\t\"WORKDAY(\\\"12/01/2015\\\",25)\":                               \"42374\",\n\t\t\"WORKDAY(\\\"01/01/2020\\\",123,B1:B12)\":                       \"44006\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",0)\":                           \"42339\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25)\":                          \"42374\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",-25)\":                         \"42304\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,1)\":                        \"42374\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,2)\":                        \"42374\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,3)\":                        \"42372\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,4)\":                        \"42373\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,5)\":                        \"42374\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,6)\":                        \"42374\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,7)\":                        \"42374\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,11)\":                       \"42368\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,12)\":                       \"42368\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,13)\":                       \"42368\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,14)\":                       \"42369\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,15)\":                       \"42368\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,16)\":                       \"42368\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,17)\":                       \"42368\",\n\t\t\"WORKDAY.INTL(\\\"12/01/2015\\\",25,\\\"0001100\\\")\":              \"42374\",\n\t\t\"WORKDAY.INTL(\\\"01/01/2020\\\",-123,4)\":                      \"43659\",\n\t\t\"WORKDAY.INTL(\\\"01/01/2020\\\",123,4,44010)\":                 \"44002\",\n\t\t\"WORKDAY.INTL(\\\"01/01/2020\\\",-123,4,43640)\":                \"43659\",\n\t\t\"WORKDAY.INTL(\\\"01/01/2020\\\",-123,4,43660)\":                \"43658\",\n\t\t\"WORKDAY.INTL(\\\"01/01/2020\\\",-123,7,43660)\":                \"43657\",\n\t\t\"WORKDAY.INTL(\\\"01/01/2020\\\",123,4,A1:A12)\":                \"44008\",\n\t\t\"WORKDAY.INTL(\\\"01/01/2020\\\",123,4,B1:B12)\":                \"44008\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"NETWORKDAYS()\": {\"#VALUE!\", \"NETWORKDAYS requires at least 2 arguments\"},\n\t\t\"NETWORKDAYS(\\\"01/01/2020\\\",\\\"09/12/2020\\\",2,\\\"\\\")\":             {\"#VALUE!\", \"NETWORKDAYS requires at most 3 arguments\"},\n\t\t\"NETWORKDAYS(\\\"\\\",\\\"09/12/2020\\\",2)\":                            {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"NETWORKDAYS(\\\"01/01/2020\\\",\\\"\\\",2)\":                            {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"NETWORKDAYS.INTL()\":                                            {\"#VALUE!\", \"NETWORKDAYS.INTL requires at least 2 arguments\"},\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",4,A1:A12,\\\"\\\")\": {\"#VALUE!\", \"NETWORKDAYS.INTL requires at most 4 arguments\"},\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"January 25, 100\\\",4)\":        {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"NETWORKDAYS.INTL(\\\"\\\",123,4,B1:B12)\":                           {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",123,\\\"000000x\\\")\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",123,\\\"0000002\\\")\":              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"NETWORKDAYS.INTL(\\\"January 25, 100\\\",123)\":                     {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"NETWORKDAYS.INTL(\\\"01/01/2020\\\",\\\"09/12/2020\\\",8)\":             {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"NETWORKDAYS.INTL(-1,123)\":                                      {\"#NUM!\", \"#NUM!\"},\n\t\t\"WORKDAY()\":                                                     {\"#VALUE!\", \"WORKDAY requires at least 2 arguments\"},\n\t\t\"WORKDAY(\\\"01/01/2020\\\",123,A1:A12,\\\"\\\")\":                       {\"#VALUE!\", \"WORKDAY requires at most 3 arguments\"},\n\t\t\"WORKDAY(\\\"01/01/2020\\\",\\\"\\\",B1:B12)\":                           {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"WORKDAY(\\\"\\\",123,B1:B12)\":                                      {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WORKDAY(\\\"January 25, 100\\\",123)\":                              {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WORKDAY(-1,123)\":                                               {\"#NUM!\", \"#NUM!\"},\n\t\t\"WORKDAY.INTL()\":                                                {\"#VALUE!\", \"WORKDAY.INTL requires at least 2 arguments\"},\n\t\t\"WORKDAY.INTL(\\\"01/01/2020\\\",123,4,A1:A12,\\\"\\\")\":                {\"#VALUE!\", \"WORKDAY.INTL requires at most 4 arguments\"},\n\t\t\"WORKDAY.INTL(\\\"01/01/2020\\\",\\\"\\\",4,B1:B12)\":                    {\"#VALUE!\", \"strconv.ParseFloat: parsing \\\"\\\": invalid syntax\"},\n\t\t\"WORKDAY.INTL(\\\"\\\",123,4,B1:B12)\":                               {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WORKDAY.INTL(\\\"01/01/2020\\\",123,\\\"\\\",B1:B12)\":                  {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WORKDAY.INTL(\\\"01/01/2020\\\",123,\\\"000000x\\\")\":                  {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WORKDAY.INTL(\\\"01/01/2020\\\",123,\\\"0000002\\\")\":                  {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WORKDAY.INTL(\\\"January 25, 100\\\",123)\":                         {\"#VALUE!\", \"#VALUE!\"},\n\t\t\"WORKDAY.INTL(-1,123)\":                                          {\"#NUM!\", \"#NUM!\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\t\tassert.Equal(t, expected[0], result, formula)\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t}\n}\n\nfunc TestCalcZTEST(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A1\", &[]int{4, 5, 2, 5, 8, 9, 3, 2, 3, 8, 9, 5}))\n\tformulaList := map[string]string{\n\t\t\"Z.TEST(A1:L1,5)\":   \"0.371103278558538\",\n\t\t\"Z.TEST(A1:L1,6)\":   \"0.838129187019751\",\n\t\t\"Z.TEST(A1:L1,5,1)\": \"0.193238115385616\",\n\t\t\"ZTEST(A1:L1,5)\":    \"0.371103278558538\",\n\t\t\"ZTEST(A1:L1,6)\":    \"0.838129187019751\",\n\t\t\"ZTEST(A1:L1,5,1)\":  \"0.193238115385616\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"M1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"M1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestStrToDate(t *testing.T) {\n\t_, _, _, _, err := strToDate(\"\")\n\tassert.Equal(t, formulaErrorVALUE, err.Error)\n}\n\nfunc TestGetYearDays(t *testing.T) {\n\tfor _, data := range [][]int{{2021, 0, 360}, {2000, 1, 366}, {2021, 1, 365}, {2000, 3, 365}} {\n\t\tassert.Equal(t, data[2], getYearDays(data[0], data[1]))\n\t}\n}\n\nfunc TestCalcGetBetaHelperContFrac(t *testing.T) {\n\tassert.Equal(t, 1.0, getBetaHelperContFrac(1, 0, 1))\n}\n\nfunc TestCalcGetBetaDistPDF(t *testing.T) {\n\tassert.Equal(t, 0.0, getBetaDistPDF(0.5, 2000, 3))\n\tassert.Equal(t, 0.0, getBetaDistPDF(0, 1, 0))\n}\n\nfunc TestCalcD1mach(t *testing.T) {\n\tassert.Equal(t, 0.0, d1mach(6))\n}\n\nfunc TestCalcChebyshevInit(t *testing.T) {\n\tassert.Equal(t, 0, chebyshevInit(0, 0, nil))\n\tassert.Equal(t, 0, chebyshevInit(1, 0, []float64{0}))\n}\n\nfunc TestCalcChebyshevEval(t *testing.T) {\n\tassert.True(t, math.IsNaN(chebyshevEval(0, 0, nil)))\n}\n\nfunc TestCalcLgammacor(t *testing.T) {\n\tassert.True(t, math.IsNaN(lgammacor(9)))\n\tassert.Equal(t, 4.930380657631324e-32, lgammacor(3.7451940309632633e+306))\n\tassert.Equal(t, 8.333333333333334e-10, lgammacor(10e+07))\n}\n\nfunc TestCalcLgammaerr(t *testing.T) {\n\tassert.True(t, math.IsNaN(logrelerr(-2)))\n}\n\nfunc TestCalcLogBeta(t *testing.T) {\n\tassert.True(t, math.IsNaN(logBeta(-1, -1)))\n\tassert.Equal(t, math.MaxFloat64, logBeta(0, 0))\n}\n\nfunc TestCalcBetainvProbIterator(t *testing.T) {\n\tassert.Equal(t, 1.0, betainvProbIterator(1, 1, 1, 1, 1, 1, 1, 1, 1))\n}\n\nfunc TestCalcRangeResolver(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", \"SUM(Sheet1!B:B)\"))\n\tcellRefs := list.New()\n\tcellRanges := list.New()\n\t// Test extract value from ranges on invalid ranges\n\tcellRanges.PushBack(cellRange{\n\t\tFrom: cellRef{Col: 1, Row: 1, Sheet: \"SheetN\"},\n\t\tTo:   cellRef{Col: 1, Row: TotalRows, Sheet: \"SheetN\"},\n\t})\n\t_, err := f.rangeResolver(&calcContext{}, cellRefs, cellRanges)\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\n\tws, err := f.workSheetReader(\"Sheet1\")\n\tws.SheetData.Row = make([]xlsxRow, TotalRows+1)\n\tws.SheetData.Row[TotalRows].C = make([]xlsxC, 3)\n\tassert.NoError(t, err)\n\tcellRanges.Init()\n\tcellRanges.PushBack(cellRange{\n\t\tFrom: cellRef{Col: 3, Row: TotalRows, Sheet: \"Sheet1\"},\n\t\tTo:   cellRef{Col: 3, Row: TotalRows + 1, Sheet: \"Sheet1\"},\n\t})\n\t_, err = f.rangeResolver(&calcContext{}, cellRefs, cellRanges)\n\tassert.Equal(t, ErrMaxRows, err)\n\n\t// Test extract value from references with invalid references\n\tcellRanges.Init()\n\tcellRefs.PushBack(cellRef{Col: 1, Row: 1, Sheet: \"SheetN\"})\n\t_, err = f.rangeResolver(&calcContext{}, cellRefs, cellRanges)\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\n\tcellRefs.Init()\n\tcellRefs.PushBack(cellRef{Col: 1, Row: TotalRows + 1, Sheet: \"SheetN\"})\n\t_, err = f.rangeResolver(&calcContext{}, cellRefs, cellRanges)\n\tassert.Equal(t, ErrMaxRows, err)\n\tt.Run(\"for_range_resolver_error\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", \"test\"))\n\t\tcellRefs := list.New()\n\t\tcellRanges := list.New()\n\t\tcellRanges.PushBack(cellRange{\n\t\t\tFrom: cellRef{Col: 1, Row: 1, Sheet: \"Sheet1\"},\n\t\t\tTo:   cellRef{Col: 1, Row: 1, Sheet: \"Sheet1\"},\n\t\t})\n\t\tf.SharedStrings = nil\n\t\tf.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)\n\t\t_, err := f.rangeResolver(&calcContext{}, cellRefs, cellRanges)\n\t\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t})\n}\n\nfunc TestCalcBahttextAppendDigit(t *testing.T) {\n\tassert.Empty(t, bahttextAppendDigit(\"\", -1))\n}\n\nfunc TestNestedFunctionsWithOperators(t *testing.T) {\n\tf := NewFile()\n\tformulaList := map[string]string{\n\t\t\"LEN(\\\"KEEP\\\")\":                                                   \"4\",\n\t\t\"LEN(\\\"REMOVEKEEP\\\") - LEN(\\\"REMOVE\\\")\":                           \"4\",\n\t\t\"RIGHT(\\\"REMOVEKEEP\\\", 4)\":                                        \"KEEP\",\n\t\t\"RIGHT(\\\"REMOVEKEEP\\\", 10 - 6))\":                                  \"KEEP\",\n\t\t\"RIGHT(\\\"REMOVEKEEP\\\", LEN(\\\"REMOVEKEEP\\\") - 6)\":                  \"KEEP\",\n\t\t\"RIGHT(\\\"REMOVEKEEP\\\", LEN(\\\"REMOVEKEEP\\\") - LEN(\\\"REMOV\\\") - 1)\": \"KEEP\",\n\t\t\"RIGHT(\\\"REMOVEKEEP\\\", 10 - LEN(\\\"REMOVE\\\"))\":                     \"KEEP\",\n\t\t\"RIGHT(\\\"REMOVEKEEP\\\", LEN(\\\"REMOVEKEEP\\\") - LEN(\\\"REMOVE\\\"))\":    \"KEEP\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"E1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"E1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n}\n\nfunc TestFormulaRawCellValueOption(t *testing.T) {\n\tf := NewFile()\n\trawTest := []struct {\n\t\tvalue    string\n\t\traw      bool\n\t\texpected string\n\t}{\n\t\t{\"=VALUE(\\\"1.0E-07\\\")\", false, \"0.00\"},\n\t\t{\"=VALUE(\\\"1.0E-07\\\")\", true, \"0.0000001\"},\n\t\t{\"=\\\"text\\\"\", false, \"$text\"},\n\t\t{\"=\\\"text\\\"\", true, \"text\"},\n\t\t{\"=\\\"10e3\\\"\", false, \"$10e3\"},\n\t\t{\"=\\\"10e3\\\"\", true, \"10e3\"},\n\t\t{\"=\\\"10\\\" & \\\"e3\\\"\", false, \"$10e3\"},\n\t\t{\"=\\\"10\\\" & \\\"e3\\\"\", true, \"10e3\"},\n\t\t{\"=10e3\", false, \"10000.00\"},\n\t\t{\"=10e3\", true, \"10000\"},\n\t\t{\"=\\\"1111111111111111\\\"\", false, \"$1111111111111111\"},\n\t\t{\"=\\\"1111111111111111\\\"\", true, \"1111111111111111\"},\n\t\t{\"=1111111111111111\", false, \"1111111111111110.00\"},\n\t\t{\"=1111111111111111\", true, \"1.11111111111111E+15\"},\n\t\t{\"=1444.00000000003\", false, \"1444.00\"},\n\t\t{\"=1444.00000000003\", true, \"1444.00000000003\"},\n\t\t{\"=1444.000000000003\", false, \"1444.00\"},\n\t\t{\"=1444.000000000003\", true, \"1444\"},\n\t\t{\"=ROUND(1444.00000000000003,2)\", false, \"1444.00\"},\n\t\t{\"=ROUND(1444.00000000000003,2)\", true, \"1444\"},\n\t}\n\texp := \"0.00;0.00;;$@\"\n\tstyleID, err := f.NewStyle(&Style{CustomNumFmt: &exp})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A1\", \"A1\", styleID))\n\tfor _, test := range rawTest {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", test.value))\n\t\tval, err := f.CalcCellValue(\"Sheet1\", \"A1\", Options{RawCellValue: test.raw})\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, test.expected, val)\n\t}\n}\n\nfunc TestFormulaArgToToken(t *testing.T) {\n\tassert.Equal(t,\n\t\tefp.Token{\n\t\t\tTType:    efp.TokenTypeOperand,\n\t\t\tTSubType: efp.TokenSubTypeLogical,\n\t\t\tTValue:   \"TRUE\",\n\t\t},\n\t\tformulaArgToToken(newBoolFormulaArg(true)),\n\t)\n}\n\nfunc TestPrepareTrendGrowth(t *testing.T) {\n\tassert.Equal(t, [][]float64(nil), prepareTrendGrowthMtxX([][]float64{{0, 0}, {0, 0}}))\n\tassert.Equal(t, [][]float64(nil), prepareTrendGrowthMtxY(false, [][]float64{{0, 0}, {0, 0}}))\n\tinfo, err := prepareTrendGrowth(false, [][]float64{{0, 0}, {0, 0}}, [][]float64{{0, 0}, {0, 0}})\n\tassert.Nil(t, info)\n\tassert.Equal(t, newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM), err)\n}\n\nfunc TestCalcColRowQRDecomposition(t *testing.T) {\n\tassert.False(t, calcRowQRDecomposition([][]float64{{0, 0}, {0, 0}}, []float64{0, 0}, 1, 0))\n\tassert.False(t, calcColQRDecomposition([][]float64{{0, 0}, {0, 0}}, []float64{0, 0}, 1, 0))\n}\n\nfunc TestCalcCellResolver(t *testing.T) {\n\tf := NewFile()\n\t// Test reference a cell multiple times in a formula\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", \"VALUE1\"))\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A2\", \"A1\"))\n\tfor formula, expected := range map[string]string{\n\t\t\"CONCATENATE(A1,\\\"_\\\",A1)\": \"VALUE1_VALUE1\",\n\t\t\"CONCATENATE(A1,\\\"_\\\",A2)\": \"VALUE1_VALUE1\",\n\t\t\"CONCATENATE(A2,\\\"_\\\",A2)\": \"VALUE1_VALUE1\",\n\t} {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A3\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"A3\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\t// Test calculates formula that contains a nested argument function which returns a numeric result\n\tf = NewFile()\n\tfor _, cell := range []string{\"A1\", \"B2\", \"B3\", \"B4\"} {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", cell, \"ABC\"))\n\t}\n\tfor cell, formula := range map[string]string{\n\t\t\"A2\": \"IF(B2<>\\\"\\\",MAX(A1:A1)+1,\\\"\\\")\",\n\t\t\"A3\": \"IF(B3<>\\\"\\\",MAX(A1:A2)+1,\\\"\\\")\",\n\t\t\"A4\": \"IF(B4<>\\\"\\\",MAX(A1:A3)+1,\\\"\\\")\",\n\t} {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", cell, formula))\n\t}\n\tfor cell, expected := range map[string]string{\"A2\": \"1\", \"A3\": \"2\", \"A4\": \"3\"} {\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", cell)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, result)\n\t}\n\t// Test calculates formula that reference date and error type cells\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"C1\", \"20200208T080910.123\"))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"C2\", \"2020-07-10 15:00:00.000\"))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"C3\", formulaErrorDIV))\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetData.Row[0].C[2].T = \"d\"\n\tws.(*xlsxWorksheet).SheetData.Row[0].C[2].V = \"20200208T080910.123\"\n\tws.(*xlsxWorksheet).SheetData.Row[1].C[2].T = \"d\"\n\tws.(*xlsxWorksheet).SheetData.Row[1].C[2].V = \"2020-07-10 15:00:00.000\"\n\tws.(*xlsxWorksheet).SheetData.Row[2].C[2].T = \"e\"\n\tws.(*xlsxWorksheet).SheetData.Row[2].C[2].V = formulaErrorDIV\n\tfor _, tbl := range [][]string{\n\t\t{\"D1\", \"SUM(C1,1)\", \"43870.3397004977\"},\n\t\t{\"D2\", \"LEN(C2)\", \"23\"},\n\t\t{\"D3\", \"IFERROR(C3,TRUE)\", \"TRUE\"},\n\t} {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", tbl[0], tbl[1]))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", tbl[0])\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, tbl[2], result)\n\t}\n\t// Test calculates formula that reference invalid cell\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"E1\", \"E1\"))\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"F1\", \"LEN(E1)\"))\n\tf.SharedStrings = nil\n\tf.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)\n\t_, err := f.CalcCellValue(\"Sheet1\", \"F1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestEvalInfixExp(t *testing.T) {\n\tf := NewFile()\n\targ, err := f.evalInfixExp(nil, \"Sheet1\", \"A1\", []efp.Token{\n\t\t{TSubType: efp.TokenSubTypeRange, TValue: \"1A\"},\n\t})\n\tassert.Equal(t, arg, newEmptyFormulaArg())\n\tassert.Equal(t, formulaErrorNAME, err.Error())\n}\n\nfunc TestParseToken(t *testing.T) {\n\tf := NewFile()\n\tassert.Equal(t, formulaErrorNAME, f.parseToken(nil, \"Sheet1\",\n\t\tefp.Token{TSubType: efp.TokenSubTypeRange, TValue: \"1A\"}, nil, nil,\n\t).Error())\n}\n\nfunc TestCalcCellValueCache(t *testing.T) {\n\tt.Run(\"for_calc_call_value_with_cache\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 40))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A2\", 50))\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A3\", \"A1+A2\"))\n\n\t\tresult1, err := f.CalcCellValue(\"Sheet1\", \"A3\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"90\", result1)\n\n\t\tresult2, err := f.CalcCellValue(\"Sheet1\", \"A3\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, result1, result2, \"cached result should be consistent\")\n\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 60))\n\n\t\tresult3, err := f.CalcCellValue(\"Sheet1\", \"A3\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"110\", result3)\n\t\tassert.NotEqual(t, result1, result3, \"result should be updated after cache clear\")\n\t})\n\tt.Run(\"for_calc_call_value_with_multiple_dependent_cells\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 10))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A2\", 10))\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A3\", \"A1+A2\"))\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A4\", \"A3*3\"))\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A5\", \"A3+A4\"))\n\n\t\tresult3, err := f.CalcCellValue(\"Sheet1\", \"A3\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"20\", result3)\n\n\t\tresult4, err := f.CalcCellValue(\"Sheet1\", \"A4\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"60\", result4)\n\n\t\tresult5, err := f.CalcCellValue(\"Sheet1\", \"A5\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"80\", result5)\n\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 20))\n\n\t\tnewResult3, err := f.CalcCellValue(\"Sheet1\", \"A3\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"30\", newResult3)\n\t\tassert.NotEqual(t, result3, newResult3, \"A3 should be updated\")\n\n\t\tnewResult5, err := f.CalcCellValue(\"Sheet1\", \"A5\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"120\", newResult5)\n\t\tassert.NotEqual(t, result5, newResult5, \"A5 should be updated\")\n\t})\n\tt.Run(\"for_clear_calculation_cache\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 10))\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A2\", \"A1*2\"))\n\n\t\tresult1, err := f.CalcCellValue(\"Sheet1\", \"A2\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"20\", result1)\n\n\t\tresult2, err := f.CalcCellValue(\"Sheet1\", \"A2\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, result1, result2, \"results should be consistent from cache\")\n\n\t\tcases := []struct {\n\t\t\tname string\n\t\t\tfn   func() error\n\t\t}{\n\t\t\t{\"SetCellValue\", func() error { return f.SetCellValue(\"Sheet1\", \"B1\", 100) }},\n\t\t\t{\"SetCellInt\", func() error { return f.SetCellInt(\"Sheet1\", \"B2\", 200) }},\n\t\t\t{\"SetCellUint\", func() error { return f.SetCellUint(\"Sheet1\", \"B3\", 300) }},\n\t\t\t{\"SetCellFloat\", func() error { return f.SetCellFloat(\"Sheet1\", \"B4\", 3.14, 2, 64) }},\n\t\t\t{\"SetCellStr\", func() error { return f.SetCellStr(\"Sheet1\", \"B5\", \"test\") }},\n\t\t\t{\"SetCellBool\", func() error { return f.SetCellBool(\"Sheet1\", \"B6\", true) }},\n\t\t\t{\"SetCellDefault\", func() error { return f.SetCellDefault(\"Sheet1\", \"B7\", \"default\") }},\n\t\t\t{\"SetCellFormula\", func() error { return f.SetCellFormula(\"Sheet1\", \"B8\", \"=1+1\") }},\n\t\t\t{\"SetCellHyperLink\", func() error {\n\t\t\t\treturn f.SetCellHyperLink(\"Sheet1\", \"B9\", \"https://github.com/xuri/excelize\", \"External\")\n\t\t\t}},\n\t\t\t{\"SetCellRichText\", func() error {\n\t\t\t\truns := []RichTextRun{{Text: \"Rich\", Font: &Font{Bold: true}}}\n\t\t\t\treturn f.SetCellRichText(\"Sheet1\", \"B10\", runs)\n\t\t\t}},\n\t\t\t{\"SetSheetRow\", func() error { return f.SetSheetRow(\"Sheet1\", \"C1\", &[]interface{}{1, 2, 3}) }},\n\t\t\t{\"SetSheetCol\", func() error { return f.SetSheetCol(\"Sheet1\", \"D1\", &[]interface{}{4, 5, 6}) }},\n\t\t}\n\t\tfor _, tc := range cases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t_, err := f.CalcCellValue(\"Sheet1\", \"A2\")\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tassert.NoError(t, tc.fn())\n\t\t\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"A2\")\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tassert.Equal(t, \"20\", result, \"calculation should still work after cache clear\")\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestCalcLookupCol(t *testing.T) {\n\tresult := lookupCol(formulaArg{\n\t\tType: ArgMatrix,\n\t\tMatrix: [][]formulaArg{\n\t\t\t{newNumberFormulaArg(1), newNumberFormulaArg(2)},\n\t\t\t{},\n\t\t\t{newNumberFormulaArg(3), newNumberFormulaArg(4)},\n\t\t},\n\t}, 0)\n\tassert.Equal(t, 3, len(result))\n\tassert.Equal(t, \"1\", result[0].Value())\n\tassert.Equal(t, \"\", result[1].Value())\n\tassert.Equal(t, \"3\", result[2].Value())\n}\n\nfunc TestCalcLookupLinearSearch(t *testing.T) {\n\tlookupValue := newStringFormulaArg(\"test\")\n\tlookupArray := formulaArg{\n\t\tType: ArgString,\n\t\tMatrix: [][]formulaArg{\n\t\t\t{newStringFormulaArg(\"test\")},\n\t\t},\n\t}\n\tmatchMode := newNumberFormulaArg(0)\n\tsearchMode := newNumberFormulaArg(1)\n\tidx, wasExact := lookupLinearSearch(false, lookupValue, lookupArray, matchMode, searchMode)\n\tassert.Equal(t, 0, idx)\n\tassert.True(t, wasExact)\n}\n\nfunc TestCalcMatchMatrix(t *testing.T) {\n\tassert.Equal(t, formulaArg{Type: ArgNumber, Number: 2},\n\t\tcalcMatchMatrix(true, 1, &formulaCriteria{\n\t\t\tType:      criteriaEq,\n\t\t\tCondition: newStringFormulaArg(\"B\"),\n\t\t}, [][]formulaArg{\n\t\t\t{newStringFormulaArg(\"A\")},\n\t\t\t{newStringFormulaArg(\"B\")},\n\t\t\t{newStringFormulaArg(\"C\")},\n\t\t}),\n\t)\n}\n\nfunc TestCalcSORTBY(t *testing.T) {\n\tcellData := [][]interface{}{\n\t\t{\"Name\", \"Region\", \"Sales\", \"Quarter\"},\n\t\t{\"Alice\", \"North\", 1500, 1},\n\t\t{\"Bob\", \"South\", 2000, 2},\n\t\t{\"Alice\", \"North\", 1200, 2},\n\t\t{\"Charlie\", \"East\", 1800, 1},\n\t\t{\"Bob\", \"South\", 2200, 1},\n\t\t{\"David\", \"West\", 1000, 2},\n\t\t{\"Alice\", \"North\", 1700, 3},\n\t\t{\"Test\", \"Mixed\", \"Text\", 4},\n\t\t{nil, \"Empty\", 500, 4},\n\t}\n\tf := prepareCalcData(cellData)\n\tformulaList := map[string]string{\n\t\t\"TEXTJOIN(\\\",\\\", TRUE, SORTBY(A2:A8, C2:C8))\":                         \"David,Alice,Alice,Alice,Charlie,Bob,Bob\",\n\t\t\"TEXTJOIN(\\\",\\\", TRUE, SORTBY(A2:A8, C2:C8, -1))\":                     \"Bob,Bob,Charlie,Alice,Alice,Alice,David\",\n\t\t\"TEXTJOIN(\\\",\\\", TRUE, SORTBY(A2:A8, B2:B8, 1, C2:C8, -1))\":           \"Charlie,Alice,Alice,Alice,Bob,Bob,David\",\n\t\t\"TEXTJOIN(\\\",\\\", TRUE, SORTBY(A2:A8, D2:D8, 1, B2:B8, 1, C2:C8, -1))\": \"Charlie,Alice,Bob,Alice,Bob,David,Alice\",\n\t\t\"TEXTJOIN(\\\";\\\", TRUE, SORTBY(A2:C4, C2:C4))\":                         \"Alice;North;1200;Alice;North;1500;Bob;South;2000\",\n\t\t\"TEXTJOIN(\\\",\\\", TRUE, SORTBY(C2:C8, A2:B8))\":                         \"1500,1200,1700,2000,2200,1800,1000\",\n\t\t\"TEXTJOIN(\\\",\\\", TRUE, SORTBY(A2:A10, C2:C10))\":                       \"David,Alice,Alice,Alice,Charlie,Bob,Bob,Test\",\n\t\t\"TEXTJOIN(\\\",\\\", TRUE, SORTBY(D2:D8, D2:D8))\":                         \"1,1,1,2,2,2,3\",\n\t\t\"TEXTJOIN(\\\",\\\", TRUE, SORTBY(B2:B8, B2:B8))\":                         \"East,North,North,North,South,South,West\",\n\t}\n\tfor formula, expected := range formulaList {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"E1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"E1\")\n\t\tassert.NoError(t, err, formula)\n\t\tassert.Equal(t, expected, result, formula)\n\t}\n\tcalcError := map[string][]string{\n\t\t\"SORTBY(A2:A7)\": {\"#VALUE!\", \"SORTBY requires at least 2 arguments\"},\n\t\t\"SORTBY(A2:A7, C2:C7, 1, D2:D7, 1, B2:B7, 1, E2:E7, 1)\": {\"#VALUE!\", \"SORTBY takes at most 7 arguments, received 9\"},\n\t\t\"SORTBY(A2:A7, C2:C7, 1, D2:D7)\":                        {\"#VALUE!\", \"SORTBY requires 2, 3, 5, or 7 arguments, received 4\"},\n\t\t\"SORTBY(A2:A7, C2:C7, 1, D2:D7, 1, B2:B7)\":              {\"#VALUE!\", \"SORTBY requires 2, 3, 5, or 7 arguments, received 6\"},\n\t\t\"SORTBY(A2:A7, C2:C5)\":                                  {\"#VALUE!\", \"by_array dimensions (4 rows) do not match array dimensions (6 rows)\"},\n\t\t\"SORTBY(A2:A7, C2:C7, 2)\":                               {\"#VALUE!\", \"sort_order must be 1 or -1, received 2\"},\n\t\t\"SORTBY(A2:A7, C2:C7, 0)\":                               {\"#VALUE!\", \"sort_order must be 1 or -1, received 0\"},\n\t}\n\tfor formula, expected := range calcError {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"F1\", formula))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", \"F1\")\n\t\tassert.EqualError(t, err, expected[1], formula)\n\t\tassert.Equal(t, expected[0], result, formula)\n\t}\n\n\tf = prepareCalcData([][]interface{}{\n\t\t{\"Name\", \"Score\"},\n\t\t{\"Alice\", 100},\n\t})\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"TEXTJOIN(\\\",\\\", TRUE, SORTBY(A2:A2, B2:B2))\"))\n\tresult, err := f.CalcCellValue(\"Sheet1\", \"C1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Alice\", result, \"Single row should work\")\n\n\tf = prepareCalcData([][]interface{}{\n\t\t{\"Name\", \"Score\"},\n\t\t{\"Alice\", 100},\n\t\t{\"Bob\", 100},\n\t\t{\"Charlie\", 100},\n\t})\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"TEXTJOIN(\\\",\\\", TRUE, SORTBY(A2:A4, B2:B4))\"))\n\tresult, err = f.CalcCellValue(\"Sheet1\", \"C1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Alice,Bob,Charlie\", result, \"Equal values maintain original order\")\n\n\tf = prepareCalcData([][]interface{}{\n\t\t{\"Name\", \"Group\", \"Score\"},\n\t\t{\"Charlie\", \"A\", 90},\n\t\t{\"Alice\", \"A\", 95},\n\t\t{\"Bob\", \"A\", 85},\n\t})\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D1\", \"TEXTJOIN(\\\",\\\", TRUE, SORTBY(A2:A4, B2:B4, 1, C2:C4, 1))\"))\n\tresult, err = f.CalcCellValue(\"Sheet1\", \"D1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Bob,Charlie,Alice\", result, \"Second key determines order when first is equal\")\n\n\tf = prepareCalcData([][]interface{}{\n\t\t{\"Name\", \"Score\"},\n\t\t{\"Alice\", 100},\n\t})\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"=SORTBY(A2:A2,{})\"))\n\tresult, err = f.CalcCellValue(\"Sheet1\", \"C1\")\n\tassert.Error(t, err)\n\tassert.Equal(t, formulaErrorVALUE, result)\n\n\tf = prepareCalcData([][]interface{}{\n\t\t{\"Name\", \"Score\"},\n\t\t{\"Alice\", 100},\n\t})\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"=SORTBY({},B2:B2)\"))\n\tresult, err = f.CalcCellValue(\"Sheet1\", \"C1\")\n\tassert.Error(t, err)\n\tassert.Equal(t, formulaErrorVALUE, result)\n\n\tf = prepareCalcData([][]interface{}{\n\t\t{1, 2, 3},\n\t\t{4, 5, 6},\n\t\t{7, 8, 9},\n\t})\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D2\", \"=SORTBY(NA(),B1:B2)\"))\n\tresult, err = f.CalcCellValue(\"Sheet1\", \"D2\")\n\tassert.Equal(t, formulaErrorNA, result)\n\tassert.Equal(t, formulaErrorNA, err.Error())\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D4\", \"=SORTBY(A1:A2,NA())\"))\n\t_, err = f.CalcCellValue(\"Sheet1\", \"D4\")\n\tassert.Equal(t, formulaErrorNA, result)\n\tassert.Equal(t, formulaErrorNA, err.Error())\n\n\targsList := list.New()\n\targsList.PushBack(newStringFormulaArg(\"text\"))\n\tnextByArray, errArg := parseSortOrderArg(argsList.Front(), &sortbyKey{}, 0, 3)\n\tassert.Equal(t, \"strconv.ParseFloat: parsing \\\"text\\\": invalid syntax\", errArg.Error)\n\tassert.NotNil(t, nextByArray)\n\tnextByArray, errArg = parseSortOrderArg(argsList.Front(), &sortbyKey{}, 0, 1)\n\tassert.Nil(t, errArg)\n\tassert.NotNil(t, nextByArray)\n}\n\nfunc TestCalcTrendGrowthMultipleRegressionPart2(t *testing.T) {\n\tcalcTrendGrowthMultipleRegressionPart2(true, false,\n\t\t[][]float64{{1}, {2}, {3}},\n\t\t[][]float64{},\n\t\t[][]float64{},\n\t\t[][]float64{{0}},\n\t\t2.0, 0, 0, 3)\n\tcalcTrendGrowthMultipleRegressionPart2(true, false,\n\t\t[][]float64{{1}, {2}, {3}},\n\t\t[][]float64{{0}, {0}, {0}},\n\t\t[][]float64{},\n\t\t[][]float64{{0}},\n\t\t2.0, 0, 1, 3)\n}\n\nfunc TestCalcTrendGrowthRegression(t *testing.T) {\n\tmtx := [][]float64{}\n\tcalcTrendGrowthRegression(false, false, 0, 0, 0, 0, 0, mtx, mtx, mtx, mtx)\n}\n\nfunc TestCalcImplicitIntersect(t *testing.T) {\n\tf := NewFile()\n\tfor cell, value := range map[string]interface{}{\n\t\t\"A1\": -5, \"A2\": 10, \"A3\": -3, \"A4\": \"text\", \"A5\": 7, \"D1\": 1, \"D2\": 0, \"D3\": 1,\n\t} {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", cell, value))\n\t}\n\tfor cell, expected := range map[string]string{\n\t\t\"B1\": \"5\", \"B2\": \"10\", \"B3\": \"3\", \"B5\": \"7\",\n\t} {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", cell, \"ABS(A1:A5)\"))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", cell)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, result)\n\t}\n\tfor cell, expected := range map[string]string{\n\t\t\"C1\": \"TRUE\", \"C2\": \"TRUE\", \"C3\": \"TRUE\", \"C4\": \"FALSE\", \"C5\": \"TRUE\",\n\t} {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", cell, \"ISNUMBER(A1:A5)\"))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", cell)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, result)\n\t}\n\tfor cell, expected := range map[string]string{\n\t\t\"E1\": \"yes\", \"E2\": \"no\", \"E3\": \"yes\",\n\t} {\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", cell, \"IF(D1:D3,\\\"yes\\\",\\\"no\\\")\"))\n\t\tresult, err := f.CalcCellValue(\"Sheet1\", cell)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, result)\n\t}\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B10\", \"ABS(A1:A5)\"))\n\t_, err := f.CalcCellValue(\"Sheet1\", \"B10\")\n\tassert.NoError(t, err)\n\tresult, err := formulaCriteriaEval(newStringFormulaArg(\"text\"), &formulaCriteria{\n\t\tType:      criteriaRegexp,\n\t\tCondition: newStringFormulaArg(\"text\"),\n\t})\n\tassert.NoError(t, err)\n\tassert.True(t, result)\n\tfn := formulaFuncs{}\n\tassert.Equal(t, ArgMatrix, fn.implicitIntersect(\n\t\tnewMatrixFormulaArg([][]formulaArg{{newNumberFormulaArg(1)}})).Type,\n\t)\n}\n"
  },
  {
    "path": "calcchain.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"io\"\n)\n\n// calcChainReader provides a function to get the pointer to the structure\n// after deserialization of xl/calcChain.xml.\nfunc (f *File) calcChainReader() (*xlsxCalcChain, error) {\n\tif f.CalcChain == nil {\n\t\tf.CalcChain = new(xlsxCalcChain)\n\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathCalcChain)))).\n\t\t\tDecode(f.CalcChain); err != nil && err != io.EOF {\n\t\t\treturn f.CalcChain, err\n\t\t}\n\t}\n\treturn f.CalcChain, nil\n}\n\n// calcChainWriter provides a function to save xl/calcChain.xml after\n// serialize structure.\nfunc (f *File) calcChainWriter() {\n\tif f.CalcChain != nil && f.CalcChain.C != nil {\n\t\toutput, _ := xml.Marshal(f.CalcChain)\n\t\tf.saveFileList(defaultXMLPathCalcChain, output)\n\t}\n}\n\n// deleteCalcChain provides a function to remove cell reference on the\n// calculation chain.\nfunc (f *File) deleteCalcChain(index int, cell string) error {\n\tcalc, err := f.calcChainReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif calc != nil {\n\t\tcalc.C = xlsxCalcChainCollection(calc.C).Filter(func(c xlsxCalcChainC) bool {\n\t\t\treturn (c.I != index || c.R != cell) && (c.I != index || cell != \"\") && (c.I != 0 || c.R != cell)\n\t\t})\n\t}\n\tif len(calc.C) == 0 {\n\t\tf.CalcChain = nil\n\t\tf.Pkg.Delete(defaultXMLPathCalcChain)\n\t\tcontent, err := f.contentTypesReader()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcontent.mu.Lock()\n\t\tdefer content.mu.Unlock()\n\t\tfor k, v := range content.Overrides {\n\t\t\tif v.PartName == \"/xl/calcChain.xml\" {\n\t\t\t\tcontent.Overrides = append(content.Overrides[:k], content.Overrides[k+1:]...)\n\t\t\t}\n\t\t}\n\t}\n\treturn err\n}\n\ntype xlsxCalcChainCollection []xlsxCalcChainC\n\n// Filter provides a function to filter calculation chain.\nfunc (c xlsxCalcChainCollection) Filter(fn func(v xlsxCalcChainC) bool) []xlsxCalcChainC {\n\tvar results []xlsxCalcChainC\n\tfor _, v := range c {\n\t\tif fn(v) {\n\t\t\tresults = append(results, v)\n\t\t}\n\t}\n\treturn results\n}\n\n// volatileDepsReader provides a function to get the pointer to the structure\n// after deserialization of xl/volatileDependencies.xml.\nfunc (f *File) volatileDepsReader() (*xlsxVolTypes, error) {\n\tif f.VolatileDeps == nil {\n\t\tvolatileDeps, ok := f.Pkg.Load(defaultXMLPathVolatileDeps)\n\t\tif !ok {\n\t\t\treturn f.VolatileDeps, nil\n\t\t}\n\t\tf.VolatileDeps = new(xlsxVolTypes)\n\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(volatileDeps.([]byte)))).\n\t\t\tDecode(f.VolatileDeps); err != nil && err != io.EOF {\n\t\t\treturn f.VolatileDeps, err\n\t\t}\n\t}\n\treturn f.VolatileDeps, nil\n}\n\n// volatileDepsWriter provides a function to save xl/volatileDependencies.xml\n// after serialize structure.\nfunc (f *File) volatileDepsWriter() {\n\tif f.VolatileDeps != nil {\n\t\toutput, _ := xml.Marshal(f.VolatileDeps)\n\t\tf.saveFileList(defaultXMLPathVolatileDeps, output)\n\t}\n}\n\n// deleteVolTopicRef provides a function to remove cell reference on the\n// volatile dependencies topic.\nfunc (vt *xlsxVolTypes) deleteVolTopicRef(i1, i2, i3, i4 int) {\n\tfor i := range vt.VolType[i1].Main[i2].Tp[i3].Tr {\n\t\tif i == i4 {\n\t\t\tvt.VolType[i1].Main[i2].Tp[i3].Tr = append(vt.VolType[i1].Main[i2].Tp[i3].Tr[:i], vt.VolType[i1].Main[i2].Tp[i3].Tr[i+1:]...)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "calcchain_test.go",
    "content": "package excelize\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCalcChainReader(t *testing.T) {\n\tf := NewFile()\n\t// Test read calculation chain with unsupported charset\n\tf.CalcChain = nil\n\tf.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)\n\t_, err := f.calcChainReader()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestDeleteCalcChain(t *testing.T) {\n\tf := NewFile()\n\tf.CalcChain = &xlsxCalcChain{C: []xlsxCalcChainC{}}\n\tf.ContentTypes.Overrides = append(f.ContentTypes.Overrides, xlsxOverride{\n\t\tPartName: \"/xl/calcChain.xml\",\n\t})\n\tassert.NoError(t, f.deleteCalcChain(1, \"A1\"))\n\n\tf.CalcChain = nil\n\tf.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.deleteCalcChain(1, \"A1\"), \"XML syntax error on line 1: invalid UTF-8\")\n\n\tf.CalcChain = nil\n\tf.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetCellFormula(\"Sheet1\", \"A1\", \"\"), \"XML syntax error on line 1: invalid UTF-8\")\n\n\tformulaType, ref := STCellFormulaTypeShared, \"C1:C5\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"A1+B1\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\n\t// Test delete calculation chain with unsupported charset calculation chain\n\tf.CalcChain = nil\n\tf.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetCellValue(\"Sheet1\", \"C1\", true), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test delete calculation chain with unsupported charset content types\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.deleteCalcChain(1, \"A1\"), \"XML syntax error on line 1: invalid UTF-8\")\n}\n"
  },
  {
    "path": "cell.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/xuri/efp\"\n)\n\n// CellType is the type of cell value type.\ntype CellType byte\n\n// Cell value types enumeration.\nconst (\n\tCellTypeUnset CellType = iota\n\tCellTypeBool\n\tCellTypeDate\n\tCellTypeError\n\tCellTypeFormula\n\tCellTypeInlineString\n\tCellTypeNumber\n\tCellTypeSharedString\n)\n\nconst (\n\t// STCellFormulaTypeArray defined the formula is an array formula.\n\tSTCellFormulaTypeArray = \"array\"\n\t// STCellFormulaTypeDataTable defined the formula is a data table formula.\n\tSTCellFormulaTypeDataTable = \"dataTable\"\n\t// STCellFormulaTypeNormal defined the formula is a regular cell formula.\n\tSTCellFormulaTypeNormal = \"normal\"\n\t// STCellFormulaTypeShared defined the formula is part of a shared formula.\n\tSTCellFormulaTypeShared = \"shared\"\n)\n\n// cellTypes mapping the cell's data type and enumeration.\nvar cellTypes = map[string]CellType{\n\t\"b\":         CellTypeBool,\n\t\"d\":         CellTypeDate,\n\t\"n\":         CellTypeNumber,\n\t\"e\":         CellTypeError,\n\t\"s\":         CellTypeSharedString,\n\t\"str\":       CellTypeFormula,\n\t\"inlineStr\": CellTypeInlineString,\n}\n\n// GetCellValue provides a function to get formatted value from cell by given\n// worksheet name and cell reference in spreadsheet. The return value is\n// converted to the 'string' data type. This function is concurrency safe. If\n// the cell format can be applied to the value of a cell, the applied value\n// will be returned, otherwise the original value will be returned. All cells'\n// values will be the same in a merged range.\nfunc (f *File) GetCellValue(sheet, cell string, opts ...Options) (string, error) {\n\treturn f.getCellStringFunc(sheet, cell, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {\n\t\tsst, err := f.sharedStringsReader()\n\t\tif err != nil {\n\t\t\treturn \"\", true, err\n\t\t}\n\t\tval, err := c.getValueFrom(f, sst, f.getOptions(opts...).RawCellValue)\n\t\treturn val, true, err\n\t})\n}\n\n// GetCellType provides a function to get the cell's data type by given\n// worksheet name and cell reference in spreadsheet file.\nfunc (f *File) GetCellType(sheet, cell string) (CellType, error) {\n\tvar (\n\t\terr         error\n\t\tcellTypeStr string\n\t\tcellType    CellType\n\t)\n\tif cellTypeStr, err = f.getCellStringFunc(sheet, cell, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {\n\t\treturn c.T, true, nil\n\t}); err != nil {\n\t\treturn CellTypeUnset, err\n\t}\n\tcellType = cellTypes[cellTypeStr]\n\treturn cellType, err\n}\n\n// SetCellValue provides a function to set the value of a cell. This function\n// is concurrency safe. The specified coordinates should not be in the first\n// row of the table, a complex number can be set with string text. The\n// following shows the supported data types:\n//\n//\tint\n//\tint8\n//\tint16\n//\tint32\n//\tint64\n//\tuint\n//\tuint8\n//\tuint16\n//\tuint32\n//\tuint64\n//\tfloat32\n//\tfloat64\n//\tstring\n//\t[]byte\n//\ttime.Duration\n//\ttime.Time\n//\tbool\n//\tnil\n//\n// Note that default date format is m/d/yy h:mm of time.Time type value. You\n// can set numbers format by the SetCellStyle function. If you need to set the\n// specialized date in Excel like January 0, 1900 or February 29, 1900, these\n// times can not representation in Go language time.Time data type. Please set\n// the cell value as number 0 or 60, then create and bind the date-time number\n// format style for the cell.\nfunc (f *File) SetCellValue(sheet, cell string, value interface{}) error {\n\tvar err error\n\tswitch v := value.(type) {\n\tcase int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:\n\t\terr = f.setCellIntFunc(sheet, cell, v)\n\tcase float32:\n\t\terr = f.SetCellFloat(sheet, cell, float64(v), -1, 32)\n\tcase float64:\n\t\terr = f.SetCellFloat(sheet, cell, v, -1, 64)\n\tcase string:\n\t\terr = f.SetCellStr(sheet, cell, v)\n\tcase []byte:\n\t\terr = f.SetCellStr(sheet, cell, string(v))\n\tcase time.Duration:\n\t\t_, d := setCellDuration(v)\n\t\terr = f.SetCellDefault(sheet, cell, d)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr = f.setDefaultTimeStyle(sheet, cell, getDurationNumFmt(v))\n\tcase time.Time:\n\t\terr = f.setCellTimeFunc(sheet, cell, v)\n\tcase bool:\n\t\terr = f.SetCellBool(sheet, cell, v)\n\tcase nil:\n\t\terr = f.SetCellDefault(sheet, cell, \"\")\n\tdefault:\n\t\terr = f.SetCellStr(sheet, cell, fmt.Sprint(value))\n\t}\n\treturn err\n}\n\n// String extracts characters from a string item.\nfunc (x xlsxSI) String() string {\n\tvar value strings.Builder\n\tif x.T != nil {\n\t\tvalue.WriteString(x.T.Val)\n\t}\n\tfor _, s := range x.R {\n\t\tif s.T != nil {\n\t\t\tvalue.WriteString(s.T.Val)\n\t\t}\n\t}\n\tif value.Len() == 0 {\n\t\treturn \"\"\n\t}\n\treturn bstrUnmarshal(value.String())\n}\n\n// hasValue determine if cell non-blank value.\nfunc (c *xlsxC) hasValue() bool {\n\treturn c.S != 0 || c.V != \"\" || c.F != nil || c.T != \"\"\n}\n\n// removeFormula delete formula for the cell.\nfunc (f *File) removeFormula(c *xlsxC, ws *xlsxWorksheet, sheet string) error {\n\tf.clearCalcCache()\n\tif c.F != nil && c.Vm == nil {\n\t\tsheetID := f.getSheetID(sheet)\n\t\tif err := f.deleteCalcChain(sheetID, c.R); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif c.F.T == STCellFormulaTypeShared && c.F.Ref != \"\" {\n\t\t\tsi := c.F.Si\n\t\t\tfor r, row := range ws.SheetData.Row {\n\t\t\t\tfor col, cell := range row.C {\n\t\t\t\t\tif cell.F != nil && cell.F.Si != nil && *cell.F.Si == *si {\n\t\t\t\t\t\tws.SheetData.Row[r].C[col].F = nil\n\t\t\t\t\t\tws.formulaSI.Delete(si)\n\t\t\t\t\t\t_ = f.deleteCalcChain(sheetID, cell.R)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tc.F = nil\n\t}\n\treturn nil\n}\n\n// setCellIntFunc is a wrapper of SetCellInt.\nfunc (f *File) setCellIntFunc(sheet, cell string, value interface{}) error {\n\tvar err error\n\tswitch v := value.(type) {\n\tcase int:\n\t\terr = f.SetCellInt(sheet, cell, int64(v))\n\tcase int8:\n\t\terr = f.SetCellInt(sheet, cell, int64(v))\n\tcase int16:\n\t\terr = f.SetCellInt(sheet, cell, int64(v))\n\tcase int32:\n\t\terr = f.SetCellInt(sheet, cell, int64(v))\n\tcase int64:\n\t\terr = f.SetCellInt(sheet, cell, v)\n\tcase uint:\n\t\terr = f.SetCellUint(sheet, cell, uint64(v))\n\tcase uint8:\n\t\terr = f.SetCellUint(sheet, cell, uint64(v))\n\tcase uint16:\n\t\terr = f.SetCellUint(sheet, cell, uint64(v))\n\tcase uint32:\n\t\terr = f.SetCellUint(sheet, cell, uint64(v))\n\tcase uint64:\n\t\terr = f.SetCellUint(sheet, cell, v)\n\t}\n\treturn err\n}\n\n// setCellTimeFunc provides a method to process time type of value for\n// SetCellValue.\nfunc (f *File) setCellTimeFunc(sheet, cell string, value time.Time) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tc, col, row, err := ws.prepareCell(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.mu.Lock()\n\tc.S = ws.prepareCellStyle(col, row, c.S)\n\tws.mu.Unlock()\n\tvar date1904, isNum bool\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif wb != nil && wb.WorkbookPr != nil {\n\t\tdate1904 = wb.WorkbookPr.Date1904\n\t}\n\tif isNum, err = c.setCellTime(value, date1904); err != nil {\n\t\treturn err\n\t}\n\tif isNum {\n\t\t_ = f.setDefaultTimeStyle(sheet, cell, getTimeNumFmt(value))\n\t}\n\treturn err\n}\n\n// setCellTime prepares cell type and Excel time by given Go time.Time type\n// timestamp.\nfunc (c *xlsxC) setCellTime(value time.Time, date1904 bool) (isNum bool, err error) {\n\tvar excelTime float64\n\t_, offset := value.In(value.Location()).Zone()\n\tvalue = value.Add(time.Duration(offset) * time.Second)\n\tif excelTime, err = timeToExcelTime(value, date1904); err != nil {\n\t\treturn\n\t}\n\tisNum = excelTime > 0\n\tif isNum {\n\t\tc.setCellDefault(strconv.FormatFloat(excelTime, 'f', -1, 64))\n\t} else {\n\t\tc.setCellDefault(value.Format(time.RFC3339Nano))\n\t}\n\treturn\n}\n\n// setCellDuration prepares cell type and value by given Go time.Duration type\n// time duration.\nfunc setCellDuration(value time.Duration) (t string, v string) {\n\tv = strconv.FormatFloat(value.Seconds()/86400, 'f', -1, 32)\n\treturn\n}\n\n// SetCellInt provides a function to set int type value of a cell by given\n// worksheet name, cell reference and cell value.\nfunc (f *File) SetCellInt(sheet, cell string, value int64) error {\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tc, col, row, err := ws.prepareCell(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.S = ws.prepareCellStyle(col, row, c.S)\n\tc.T, c.V = setCellInt(value)\n\tc.IS = nil\n\treturn f.removeFormula(c, ws, sheet)\n}\n\n// setCellInt prepares cell type and string type cell value by a given integer.\nfunc setCellInt(value int64) (t string, v string) {\n\tv = strconv.FormatInt(value, 10)\n\treturn\n}\n\n// SetCellUint provides a function to set uint type value of a cell by given\n// worksheet name, cell reference and cell value.\nfunc (f *File) SetCellUint(sheet, cell string, value uint64) error {\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tc, col, row, err := ws.prepareCell(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.S = ws.prepareCellStyle(col, row, c.S)\n\tc.T, c.V = setCellUint(value)\n\tc.IS = nil\n\treturn f.removeFormula(c, ws, sheet)\n}\n\n// setCellUint prepares cell type and string type cell value by a given unsigned\n// integer.\nfunc setCellUint(value uint64) (t string, v string) {\n\tv = strconv.FormatUint(value, 10)\n\treturn\n}\n\n// SetCellBool provides a function to set bool type value of a cell by given\n// worksheet name, cell reference and cell value.\nfunc (f *File) SetCellBool(sheet, cell string, value bool) error {\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tc, col, row, err := ws.prepareCell(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.S = ws.prepareCellStyle(col, row, c.S)\n\tc.T, c.V = setCellBool(value)\n\tc.IS = nil\n\treturn f.removeFormula(c, ws, sheet)\n}\n\n// setCellBool prepares cell type and string type cell value by a given boolean\n// value.\nfunc setCellBool(value bool) (t string, v string) {\n\tt = \"b\"\n\tif value {\n\t\tv = \"1\"\n\t} else {\n\t\tv = \"0\"\n\t}\n\treturn\n}\n\n// SetCellFloat sets a floating point value into a cell. The precision\n// parameter specifies how many places after the decimal will be shown\n// while -1 is a special value that will use as many decimal places as\n// necessary to represent the number. bitSize is 32 or 64 depending on if a\n// float32 or float64 was originally used for the value. For Example:\n//\n//\tvar x float32 = 1.325\n//\tf.SetCellFloat(\"Sheet1\", \"A1\", float64(x), 2, 32)\nfunc (f *File) SetCellFloat(sheet, cell string, value float64, precision, bitSize int) error {\n\tif math.IsNaN(value) || math.IsInf(value, 0) {\n\t\treturn f.SetCellStr(sheet, cell, fmt.Sprint(value))\n\t}\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tc, col, row, err := ws.prepareCell(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.S = ws.prepareCellStyle(col, row, c.S)\n\tc.setCellFloat(value, precision, bitSize)\n\treturn f.removeFormula(c, ws, sheet)\n}\n\n// setCellFloat prepares cell type and string type cell value by a given float\n// value.\nfunc (c *xlsxC) setCellFloat(value float64, precision, bitSize int) {\n\tif math.IsNaN(value) || math.IsInf(value, 0) {\n\t\tc.setInlineStr(fmt.Sprint(value))\n\t\treturn\n\t}\n\tc.T, c.V = \"\", strconv.FormatFloat(value, 'f', precision, bitSize)\n\tc.IS = nil\n}\n\n// SetCellStr provides a function to set string type value of a cell. Total\n// number of characters that a cell can contain 32767 characters.\nfunc (f *File) SetCellStr(sheet, cell, value string) error {\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tc, col, row, err := ws.prepareCell(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.S = ws.prepareCellStyle(col, row, c.S)\n\tif c.T, c.V, err = f.setCellString(value); err != nil {\n\t\treturn err\n\t}\n\tc.IS = nil\n\treturn f.removeFormula(c, ws, sheet)\n}\n\n// setCellString provides a function to set string type to shared string table.\nfunc (f *File) setCellString(value string) (t, v string, err error) {\n\tif countUTF16String(value) > TotalCellChars {\n\t\tvalue = truncateUTF16Units(value, TotalCellChars)\n\t}\n\tt = \"s\"\n\tvar si int\n\tif si, err = f.setSharedString(value); err != nil {\n\t\treturn\n\t}\n\tv = strconv.Itoa(si)\n\treturn\n}\n\n// sharedStringsLoader load shared string table from system temporary file to\n// memory, and reset shared string table for reader.\nfunc (f *File) sharedStringsLoader() (err error) {\n\tf.mu.Lock()\n\tdefer f.mu.Unlock()\n\tif path, ok := f.tempFiles.Load(defaultXMLPathSharedStrings); ok {\n\t\tf.Pkg.Store(defaultXMLPathSharedStrings, f.readBytes(defaultXMLPathSharedStrings))\n\t\tf.tempFiles.Delete(defaultXMLPathSharedStrings)\n\t\tif err = os.Remove(path.(string)); err != nil {\n\t\t\treturn\n\t\t}\n\t\tf.SharedStrings = nil\n\t}\n\tif f.sharedStringTemp != nil {\n\t\tif err := f.sharedStringTemp.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tf.tempFiles.Delete(defaultTempFileSST)\n\t\tf.sharedStringItem, err = nil, os.Remove(f.sharedStringTemp.Name())\n\t\tf.sharedStringTemp = nil\n\t}\n\treturn\n}\n\n// setSharedString provides a function to add string to the share string table.\nfunc (f *File) setSharedString(val string) (int, error) {\n\tif err := f.sharedStringsLoader(); err != nil {\n\t\treturn 0, err\n\t}\n\tsst, err := f.sharedStringsReader()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tf.mu.Lock()\n\tdefer f.mu.Unlock()\n\tif i, ok := f.sharedStringsMap[val]; ok {\n\t\treturn i, nil\n\t}\n\tsst.mu.Lock()\n\tdefer sst.mu.Unlock()\n\tt := xlsxT{Val: val}\n\tval, t.Space = trimCellValue(val, false)\n\tsst.SI = append(sst.SI, xlsxSI{T: &t})\n\tsst.Count = len(sst.SI)\n\tsst.UniqueCount = sst.Count\n\tf.sharedStringsMap[val] = sst.UniqueCount - 1\n\treturn sst.UniqueCount - 1, nil\n}\n\n// trimCellValue provides a function to set string type to cell.\nfunc trimCellValue(value string, escape bool) (v string, ns xml.Attr) {\n\tif countUTF16String(value) > TotalCellChars {\n\t\tvalue = truncateUTF16Units(value, TotalCellChars)\n\t}\n\tif value != \"\" {\n\t\tprefix, suffix := value[0], value[len(value)-1]\n\t\tfor _, ascii := range []byte{9, 10, 13, 32} {\n\t\t\tif prefix == ascii || suffix == ascii {\n\t\t\t\tns = xml.Attr{\n\t\t\t\t\tName:  xml.Name{Space: NameSpaceXML, Local: \"space\"},\n\t\t\t\t\tValue: \"preserve\",\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif escape {\n\t\t\tvar buf strings.Builder\n\t\t\t_ = xml.EscapeText(&buf, []byte(value))\n\t\t\tvalue = strings.ReplaceAll(buf.String(), \"&#xA;\", \"\\n\")\n\t\t}\n\t}\n\tv = bstrMarshal(value)\n\treturn\n}\n\n// setCellValue set cell data type and value for (inline) rich string cell or\n// formula cell.\nfunc (c *xlsxC) setCellValue(val string) {\n\tif c.F != nil {\n\t\tc.setStr(val)\n\t\treturn\n\t}\n\tc.setInlineStr(val)\n}\n\n// setInlineStr set cell data type and value which containing an (inline) rich\n// string.\nfunc (c *xlsxC) setInlineStr(val string) {\n\tc.T, c.V, c.IS = \"inlineStr\", \"\", &xlsxSI{T: &xlsxT{}}\n\tc.IS.T.Val, c.IS.T.Space = trimCellValue(val, true)\n}\n\n// setStr set cell data type and value which containing a formula string.\nfunc (c *xlsxC) setStr(val string) {\n\tc.T, c.IS = \"str\", nil\n\tc.V, c.XMLSpace = trimCellValue(val, false)\n}\n\n// getCellBool parse cell value which containing a boolean.\nfunc (c *xlsxC) getCellBool(f *File, raw bool) (string, error) {\n\tif !raw {\n\t\tif c.V == \"1\" {\n\t\t\treturn \"TRUE\", nil\n\t\t}\n\t\tif c.V == \"0\" {\n\t\t\treturn \"FALSE\", nil\n\t\t}\n\t}\n\treturn f.formattedValue(c, raw, CellTypeBool)\n}\n\n// setCellDefault prepares cell type and string type cell value by a given\n// string.\nfunc (c *xlsxC) setCellDefault(value string) {\n\tif ok, _, _ := isNumeric(value); !ok {\n\t\tif value != \"\" {\n\t\t\tc.setInlineStr(value)\n\t\t\tc.IS.T.Val = value\n\t\t\treturn\n\t\t}\n\t\tc.T, c.V, c.IS = value, value, nil\n\t\treturn\n\t}\n\tc.T, c.V = \"\", value\n}\n\n// getCellDate parse cell value which contains a date in the ISO 8601 format.\nfunc (c *xlsxC) getCellDate(f *File, raw bool) (string, error) {\n\tif !raw {\n\t\tlayout := \"20060102T150405.999\"\n\t\tif strings.HasSuffix(c.V, \"Z\") {\n\t\t\tlayout = \"20060102T150405Z\"\n\t\t\tif strings.Contains(c.V, \"-\") {\n\t\t\t\tlayout = \"2006-01-02T15:04:05Z\"\n\t\t\t}\n\t\t} else if strings.Contains(c.V, \"-\") {\n\t\t\tlayout = \"2006-01-02 15:04:05Z\"\n\t\t}\n\t\tif timestamp, err := time.Parse(layout, strings.ReplaceAll(c.V, \",\", \".\")); err == nil {\n\t\t\texcelTime, _ := timeToExcelTime(timestamp, false)\n\t\t\tc.V = strconv.FormatFloat(excelTime, 'G', 15, 64)\n\t\t}\n\t}\n\treturn f.formattedValue(c, raw, CellTypeDate)\n}\n\n// getValueFrom return a value from a column/row cell, this function is\n// intended to be used with for range on rows an argument with the spreadsheet\n// opened file.\nfunc (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) {\n\tswitch c.T {\n\tcase \"b\":\n\t\treturn c.getCellBool(f, raw)\n\tcase \"d\":\n\t\treturn c.getCellDate(f, raw)\n\tcase \"s\":\n\t\tif c.V != \"\" {\n\t\t\txlsxSI, _ := strconv.Atoi(strings.TrimSpace(c.V))\n\t\t\tif _, ok := f.tempFiles.Load(defaultXMLPathSharedStrings); ok {\n\t\t\t\treturn f.formattedValue(&xlsxC{S: c.S, V: f.getFromStringItem(xlsxSI)}, raw, CellTypeSharedString)\n\t\t\t}\n\t\t\td.mu.Lock()\n\t\t\tdefer d.mu.Unlock()\n\t\t\tif len(d.SI) > xlsxSI {\n\t\t\t\treturn f.formattedValue(&xlsxC{S: c.S, V: d.SI[xlsxSI].String()}, raw, CellTypeSharedString)\n\t\t\t}\n\t\t}\n\t\treturn f.formattedValue(c, raw, CellTypeSharedString)\n\tcase \"str\":\n\t\treturn c.V, nil\n\tcase \"inlineStr\":\n\t\tif c.IS != nil {\n\t\t\treturn f.formattedValue(&xlsxC{S: c.S, V: c.IS.String()}, raw, CellTypeInlineString)\n\t\t}\n\t\treturn f.formattedValue(c, raw, CellTypeInlineString)\n\tdefault:\n\t\tif isNum, precision, decimal := isNumeric(c.V); isNum && !raw {\n\t\t\tif precision > 15 {\n\t\t\t\tc.V = strconv.FormatFloat(decimal, 'G', 15, 64)\n\t\t\t} else {\n\t\t\t\tc.V = strconv.FormatFloat(decimal, 'f', -1, 64)\n\t\t\t}\n\t\t}\n\t\treturn f.formattedValue(c, raw, CellTypeNumber)\n\t}\n}\n\n// SetCellDefault provides a function to set string type value of a cell as\n// default format without escaping the cell.\nfunc (f *File) SetCellDefault(sheet, cell, value string) error {\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tc, col, row, err := ws.prepareCell(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.S = ws.prepareCellStyle(col, row, c.S)\n\tc.setCellDefault(value)\n\treturn f.removeFormula(c, ws, sheet)\n}\n\n// GetCellFormula provides a function to get formula from cell by given\n// worksheet name and cell reference in spreadsheet.\nfunc (f *File) GetCellFormula(sheet, cell string) (string, error) {\n\treturn f.getCellFormula(sheet, cell, false)\n}\n\n// getCellFormula provides a function to get transformed formula from cell by\n// given worksheet name and cell reference in spreadsheet.\nfunc (f *File) getCellFormula(sheet, cell string, transformed bool) (string, error) {\n\treturn f.getCellStringFunc(sheet, cell, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {\n\t\tif transformed && !f.formulaChecked {\n\t\t\tif err := f.setArrayFormulaCells(); err != nil {\n\t\t\t\treturn \"\", false, err\n\t\t\t}\n\t\t\tf.formulaChecked = true\n\t\t}\n\t\tif transformed && c.f != \"\" {\n\t\t\treturn c.f, true, nil\n\t\t}\n\t\tif c.F == nil {\n\t\t\treturn \"\", false, nil\n\t\t}\n\t\tif c.F.T == STCellFormulaTypeShared && c.F.Si != nil {\n\t\t\tformula, err := getSharedFormula(x, *c.F.Si, c.R)\n\t\t\treturn formula, true, err\n\t\t}\n\t\treturn c.F.Content, true, nil\n\t})\n}\n\n// FormulaOpts can be passed to SetCellFormula to use other formula types.\ntype FormulaOpts struct {\n\tType *string // Formula type\n\tRef  *string // Shared formula ref\n}\n\n// SetCellFormula provides a function to set formula on the cell is taken\n// according to the given worksheet name and cell formula settings. The result\n// of the formula cell can be calculated when the worksheet is opened by the\n// Office Excel application or can be using the \"CalcCellValue\" function also\n// can get the calculated cell value. If the Excel application doesn't\n// calculate the formula automatically when the workbook has been opened,\n// please call \"UpdateLinkedValue\" after setting the cell formula functions.\n//\n// Example 1, set normal formula \"SUM(A1,B1)\" for the cell \"A3\" on \"Sheet1\":\n//\n//\terr := f.SetCellFormula(\"Sheet1\", \"A3\", \"SUM(A1,B1)\")\n//\n// Example 2, set one-dimensional vertical constant array (column array) formula\n// \"1,2,3\" for the cell \"A3\" on \"Sheet1\":\n//\n//\terr := f.SetCellFormula(\"Sheet1\", \"A3\", \"{1;2;3}\")\n//\n// Example 3, set one-dimensional horizontal constant array (row array)\n// formula '\"a\",\"b\",\"c\"' for the cell \"A3\" on \"Sheet1\":\n//\n//\terr := f.SetCellFormula(\"Sheet1\", \"A3\", \"{\\\"a\\\",\\\"b\\\",\\\"c\\\"}\")\n//\n// Example 4, set two-dimensional constant array formula '{1,2,\"a\",\"b\"}' for\n// the cell \"A3\" on \"Sheet1\":\n//\n//\tformulaType, ref := excelize.STCellFormulaTypeArray, \"A3:A3\"\n//\terr := f.SetCellFormula(\"Sheet1\", \"A3\", \"{1,2;\\\"a\\\",\\\"b\\\"}\",\n//\t    excelize.FormulaOpts{Ref: &ref, Type: &formulaType})\n//\n// Example 5, set range array formula \"A1:A2\" for the cell \"A3\" on \"Sheet1\":\n//\n//\tformulaType, ref := excelize.STCellFormulaTypeArray, \"A3:A3\"\n//\terr := f.SetCellFormula(\"Sheet1\", \"A3\", \"A1:A2\",\n//\t       excelize.FormulaOpts{Ref: &ref, Type: &formulaType})\n//\n// Example 6, set shared formula \"A1+B1\" for the cell \"C1:C5\"\n// on \"Sheet1\", \"C1\" is the master cell:\n//\n//\tformulaType, ref := excelize.STCellFormulaTypeShared, \"C1:C5\"\n//\terr := f.SetCellFormula(\"Sheet1\", \"C1\", \"A1+B1\",\n//\t    excelize.FormulaOpts{Ref: &ref, Type: &formulaType})\n//\n// Example 7, set table formula \"SUM(Table1[[A]:[B]])\" for the cell \"C2\"\n// on \"Sheet1\":\n//\n//\tpackage main\n//\n//\timport (\n//\t    \"fmt\"\n//\n//\t    \"github.com/xuri/excelize/v2\"\n//\t)\n//\n//\tfunc main() {\n//\t    f := excelize.NewFile()\n//\t    defer func() {\n//\t        if err := f.Close(); err != nil {\n//\t            fmt.Println(err)\n//\t        }\n//\t    }()\n//\t    for idx, row := range [][]interface{}{{\"A\", \"B\", \"C\"}, {1, 2}} {\n//\t        if err := f.SetSheetRow(\"Sheet1\", fmt.Sprintf(\"A%d\", idx+1), &row); err != nil {\n//\t            fmt.Println(err)\n//\t            return\n//\t        }\n//\t    }\n//\t    if err := f.AddTable(\"Sheet1\", &excelize.Table{\n//\t        Range: \"A1:C2\", Name: \"Table1\", StyleName: \"TableStyleMedium2\",\n//\t    }); err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    formulaType := excelize.STCellFormulaTypeDataTable\n//\t    if err := f.SetCellFormula(\"Sheet1\", \"C2\", \"SUM(Table1[[A]:[B]])\",\n//\t        excelize.FormulaOpts{Type: &formulaType}); err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    if err := f.SaveAs(\"Book1.xlsx\"); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t}\nfunc (f *File) SetCellFormula(sheet, cell, formula string, opts ...FormulaOpts) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tc, _, _, err := ws.prepareCell(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.clearCalcCache()\n\tif formula == \"\" {\n\t\tws.deleteSharedFormula(c)\n\t\tc.F = nil\n\t\treturn f.deleteCalcChain(f.getSheetID(sheet), cell)\n\t}\n\n\tif c.F != nil {\n\t\tc.F.Content = formula\n\t} else {\n\t\tc.F = &xlsxF{Content: formula}\n\t}\n\n\tfor _, opt := range opts {\n\t\tif opt.Type != nil {\n\t\t\tif *opt.Type == STCellFormulaTypeDataTable {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tc.F.T = *opt.Type\n\t\t\tif c.F.T == STCellFormulaTypeArray && opt.Ref != nil {\n\t\t\t\tif err = ws.setArrayFormula(sheet, &xlsxF{Ref: *opt.Ref, Content: formula}, f.GetDefinedName()); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tif c.F.T == STCellFormulaTypeShared {\n\t\t\t\tws.deleteSharedFormula(c)\n\t\t\t\tif err = ws.setSharedFormula(cell, *opt.Ref); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif opt.Ref != nil {\n\t\t\tc.F.Ref = *opt.Ref\n\t\t}\n\t}\n\tc.T, c.IS = \"str\", nil\n\treturn err\n}\n\n// setArrayFormula transform the array formula in an array formula range to the\n// normal formula and set cells in this range to the formula as the normal\n// formula.\nfunc (ws *xlsxWorksheet) setArrayFormula(sheet string, formula *xlsxF, definedNames []DefinedName) error {\n\tif len(strings.Split(formula.Ref, \":\")) < 2 {\n\t\treturn nil\n\t}\n\tcoordinates, err := rangeRefToCoordinates(formula.Ref)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_ = sortCoordinates(coordinates)\n\ttokens, arrayFormulaOperandTokens, err := getArrayFormulaTokens(sheet, formula.Content, definedNames)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttopLeftCol, topLeftRow := coordinates[0], coordinates[1]\n\tfor c := coordinates[0]; c <= coordinates[2]; c++ {\n\t\tfor r := coordinates[1]; r <= coordinates[3]; r++ {\n\t\t\tcolOffset, rowOffset := c-topLeftCol, r-topLeftRow\n\t\t\tfor i, af := range arrayFormulaOperandTokens {\n\t\t\t\tcolNum, rowNum := af.topLeftCol+colOffset, af.topLeftRow+rowOffset\n\t\t\t\tif colNum <= af.bottomRightCol && rowNum <= af.bottomRightRow {\n\t\t\t\t\tarrayFormulaOperandTokens[i].targetCellRef, _ = CoordinatesToCellName(colNum, rowNum)\n\t\t\t\t}\n\t\t\t}\n\t\t\tws.prepareSheetXML(c, r)\n\t\t\tif cell := &ws.SheetData.Row[r-1].C[c-1]; cell.f == \"\" {\n\t\t\t\tcell.f = transformArrayFormula(tokens, arrayFormulaOperandTokens)\n\t\t\t}\n\t\t}\n\t}\n\treturn err\n}\n\n// setArrayFormulaCells transform the array formula in all worksheets to the\n// normal formula and set cells in the array formula reference range to the\n// formula as the normal formula.\nfunc (f *File) setArrayFormulaCells() error {\n\tdefinedNames := f.GetDefinedName()\n\tfor _, sheetN := range f.GetSheetList() {\n\t\tws, err := f.workSheetReader(sheetN)\n\t\tif err != nil {\n\t\t\tif err.Error() == newNotWorksheetError(sheetN).Error() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tfor _, row := range ws.SheetData.Row {\n\t\t\tfor _, cell := range row.C {\n\t\t\t\tif cell.F != nil && cell.F.T == STCellFormulaTypeArray {\n\t\t\t\t\tif err = ws.setArrayFormula(sheetN, cell.F, definedNames); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// setSharedFormula set shared formula for the cells.\nfunc (ws *xlsxWorksheet) setSharedFormula(cell, ref string) error {\n\tcoordinates, err := rangeRefToCoordinates(ref)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_ = sortCoordinates(coordinates)\n\tsi := ws.countSharedFormula()\n\tfor col := coordinates[0]; col <= coordinates[2]; col++ {\n\t\tfor rol := coordinates[1]; rol <= coordinates[3]; rol++ {\n\t\t\tws.prepareSheetXML(col, rol)\n\t\t\tc := &ws.SheetData.Row[rol-1].C[col-1]\n\t\t\tif c.F == nil {\n\t\t\t\tc.F = &xlsxF{}\n\t\t\t}\n\t\t\tc.F.T = STCellFormulaTypeShared\n\t\t\tif c.R == cell {\n\t\t\t\tif c.F.Ref != \"\" {\n\t\t\t\t\tsi = *c.F.Si\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tc.F.Si = &si\n\t\t}\n\t}\n\treturn err\n}\n\n// countSharedFormula count shared formula in the given worksheet.\nfunc (ws *xlsxWorksheet) countSharedFormula() (count int) {\n\tfor _, row := range ws.SheetData.Row {\n\t\tfor _, cell := range row.C {\n\t\t\tif cell.F != nil && cell.F.Si != nil && *cell.F.Si+1 > count {\n\t\t\t\tcount = *cell.F.Si + 1\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// deleteSharedFormula delete shared formula cell from worksheet shared formula\n// index cache and remove all shared cells formula which refer to the cell which\n// containing the formula.\nfunc (ws *xlsxWorksheet) deleteSharedFormula(c *xlsxC) {\n\tif c.F != nil && c.F.Si != nil && c.F.Ref != \"\" {\n\t\tsi := *c.F.Si\n\t\tws.formulaSI.Delete(si)\n\t\tfor r, row := range ws.SheetData.Row {\n\t\t\tfor c, cell := range row.C {\n\t\t\t\tif cell.F != nil && cell.F.Si != nil && *cell.F.Si == si && cell.F.Ref == \"\" {\n\t\t\t\t\tws.SheetData.Row[r].C[c].F = nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// GetCellHyperLink gets a cell hyperlink based on the given worksheet name and\n// cell reference. If the cell has a hyperlink, it will return 'true' and\n// the link address, otherwise it will return 'false' and an empty link\n// address.\n//\n// For example, get a hyperlink to a 'H6' cell on a worksheet named 'Sheet1':\n//\n//\tlink, target, err := f.GetCellHyperLink(\"Sheet1\", \"H6\")\nfunc (f *File) GetCellHyperLink(sheet, cell string) (bool, string, error) {\n\t// Check for correct cell name\n\tif _, _, err := SplitCellName(cell); err != nil {\n\t\treturn false, \"\", err\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn false, \"\", err\n\t}\n\tif ws.Hyperlinks != nil {\n\t\tfor _, link := range ws.Hyperlinks.Hyperlink {\n\t\t\tok, err := f.checkCellInRangeRef(cell, link.Ref)\n\t\t\tif err != nil {\n\t\t\t\treturn false, \"\", err\n\t\t\t}\n\t\t\tif link.Ref == cell || ok {\n\t\t\t\tif link.RID != \"\" {\n\t\t\t\t\treturn true, f.getSheetRelationshipsTargetByID(sheet, link.RID), err\n\t\t\t\t}\n\t\t\t\treturn true, link.Location, err\n\t\t\t}\n\t\t}\n\t}\n\treturn false, \"\", err\n}\n\n// GetHyperLinkCells returns cell references which contain hyperlinks in a\n// given worksheet name and link type. The optional parameter 'linkType' use for\n// specific link type, the optional values are \"External\" for website links,\n// \"Location\" for moving to one of cell in this workbook, \"None\" for no links.\n// If linkType is empty, it will return all hyperlinks in the worksheet.\nfunc (f *File) GetHyperLinkCells(sheet, linkType string) ([]string, error) {\n\tvar rangeRef []string\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn rangeRef, err\n\t}\n\tif ws.Hyperlinks != nil {\n\t\tfor _, link := range ws.Hyperlinks.Hyperlink {\n\t\t\tswitch linkType {\n\t\t\tcase \"External\":\n\t\t\t\tif link.RID != \"\" {\n\t\t\t\t\trangeRef = append(rangeRef, link.Ref)\n\t\t\t\t}\n\t\t\tcase \"Location\":\n\t\t\t\tif link.Location != \"\" {\n\t\t\t\t\trangeRef = append(rangeRef, link.Ref)\n\t\t\t\t}\n\t\t\tcase \"None\":\n\t\t\t\treturn rangeRef, err\n\t\t\tcase \"\":\n\t\t\t\trangeRef = append(rangeRef, link.Ref)\n\t\t\tdefault:\n\t\t\t\treturn rangeRef, newInvalidLinkTypeError(linkType)\n\t\t\t}\n\t\t}\n\t}\n\treturn rangeRef, err\n}\n\n// HyperlinkOpts can be passed to SetCellHyperlink to set optional hyperlink\n// attributes (e.g. display value)\ntype HyperlinkOpts struct {\n\tDisplay *string\n\tTooltip *string\n}\n\n// removeHyperLink remove hyperlink for worksheet and delete relationships for\n// the worksheet by given sheet name and cell reference. Note that if the cell\n// in a range reference, the whole hyperlinks will be deleted.\nfunc (f *File) removeHyperLink(ws *xlsxWorksheet, sheet, cell string) error {\n\tfor idx := 0; idx < len(ws.Hyperlinks.Hyperlink); idx++ {\n\t\tlink := ws.Hyperlinks.Hyperlink[idx]\n\t\tok, err := f.checkCellInRangeRef(cell, link.Ref)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif link.Ref == cell || ok {\n\t\t\tws.Hyperlinks.Hyperlink = append(ws.Hyperlinks.Hyperlink[:idx], ws.Hyperlinks.Hyperlink[idx+1:]...)\n\t\t\tidx--\n\t\t\tf.deleteSheetRelationships(sheet, link.RID)\n\t\t}\n\t}\n\tif len(ws.Hyperlinks.Hyperlink) == 0 {\n\t\tws.Hyperlinks = nil\n\t}\n\treturn nil\n}\n\n// SetCellHyperLink provides a function to set cell hyperlink by given\n// worksheet name and link URL address. LinkType defines three types of\n// hyperlink \"External\" for website or \"Location\" for moving to one of cell in\n// this workbook or \"None\" for remove hyperlink. Maximum limit hyperlinks in a\n// worksheet is 65530. This function is only used to set the hyperlink of the\n// cell and doesn't affect the value of the cell. If you need to set the value\n// of the cell, please use the other functions such as `SetCellStyle` or\n// `SetSheetRow`. The below is example for external link.\n//\n//\tdisplay, tooltip := \"https://github.com/xuri/excelize\", \"Excelize on GitHub\"\n//\tif err := f.SetCellHyperLink(\"Sheet1\", \"A3\",\n//\t    display, \"External\", excelize.HyperlinkOpts{\n//\t        Display: &display,\n//\t        Tooltip: &tooltip,\n//\t    }); err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\t// Set underline and font color style for the cell.\n//\tstyle, err := f.NewStyle(&excelize.Style{\n//\t    Font: &excelize.Font{Color: \"1265BE\", Underline: \"single\"},\n//\t})\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\terr = f.SetCellStyle(\"Sheet1\", \"A3\", \"A3\", style)\n//\n// This is another example for \"Location\":\n//\n//\terr := f.SetCellHyperLink(\"Sheet1\", \"A3\", \"Sheet1!A40\", \"Location\")\nfunc (f *File) SetCellHyperLink(sheet, cell, link, linkType string, opts ...HyperlinkOpts) error {\n\t// Check for correct cell name\n\tif _, _, err := SplitCellName(cell); err != nil {\n\t\treturn err\n\t}\n\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif cell, err = ws.mergeCellsParser(cell); err != nil {\n\t\treturn err\n\t}\n\n\tvar linkData xlsxHyperlink\n\tidx := -1\n\tif ws.Hyperlinks == nil {\n\t\tws.Hyperlinks = new(xlsxHyperlinks)\n\t}\n\tfor i, hyperlink := range ws.Hyperlinks.Hyperlink {\n\t\tif hyperlink.Ref == cell {\n\t\t\tidx = i\n\t\t\tlinkData = hyperlink\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif len(ws.Hyperlinks.Hyperlink) > TotalSheetHyperlinks {\n\t\treturn ErrTotalSheetHyperlinks\n\t}\n\n\tswitch linkType {\n\tcase \"External\":\n\t\tsheetPath, _ := f.getSheetXMLPath(sheet)\n\t\tsheetRels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(sheetPath, \"xl/worksheets/\") + \".rels\"\n\t\trID := f.setRels(linkData.RID, sheetRels, SourceRelationshipHyperLink, link, linkType)\n\t\tlinkData = xlsxHyperlink{\n\t\t\tRef: cell,\n\t\t}\n\t\tlinkData.RID = \"rId\" + strconv.Itoa(rID)\n\t\tf.addSheetNameSpace(sheet, SourceRelationship)\n\tcase \"Location\":\n\t\tlinkData = xlsxHyperlink{\n\t\t\tRef:      cell,\n\t\t\tLocation: link,\n\t\t}\n\tcase \"None\":\n\t\treturn f.removeHyperLink(ws, sheet, cell)\n\tdefault:\n\t\treturn newInvalidLinkTypeError(linkType)\n\t}\n\n\tfor _, o := range opts {\n\t\tif o.Display != nil {\n\t\t\tlinkData.Display = *o.Display\n\t\t}\n\t\tif o.Tooltip != nil {\n\t\t\tlinkData.Tooltip = *o.Tooltip\n\t\t}\n\t}\n\tif idx == -1 {\n\t\tws.Hyperlinks.Hyperlink = append(ws.Hyperlinks.Hyperlink, linkData)\n\t\treturn err\n\t}\n\tws.Hyperlinks.Hyperlink[idx] = linkData\n\treturn err\n}\n\n// getCellRichText returns rich text of cell by given string item.\nfunc getCellRichText(si *xlsxSI) (runs []RichTextRun) {\n\tif si.T != nil {\n\t\truns = append(runs, RichTextRun{Text: si.T.Val})\n\t}\n\tfor _, v := range si.R {\n\t\trun := RichTextRun{\n\t\t\tText: v.T.Val,\n\t\t}\n\t\tif v.RPr != nil {\n\t\t\trun.Font = v.RPr.getFont()\n\t\t}\n\t\truns = append(runs, run)\n\t}\n\treturn\n}\n\n// GetCellRichText provides a function to get rich text of cell by given\n// worksheet and cell reference.\nfunc (f *File) GetCellRichText(sheet, cell string) (runs []RichTextRun, err error) {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn\n\t}\n\tc, _, _, err := ws.prepareCell(cell)\n\tif err != nil {\n\t\treturn\n\t}\n\tif c.T == \"inlineStr\" && c.IS != nil {\n\t\truns = getCellRichText(c.IS)\n\t\treturn\n\t}\n\tif c.T != \"s\" || c.V == \"\" {\n\t\treturn\n\t}\n\tsiIdx, err := strconv.Atoi(c.V)\n\tif err != nil {\n\t\treturn\n\t}\n\tsst, err := f.sharedStringsReader()\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(sst.SI) <= siIdx || siIdx < 0 {\n\t\treturn\n\t}\n\truns = getCellRichText(&sst.SI[siIdx])\n\treturn\n}\n\n// newRpr create run properties for the rich text by given font format.\nfunc (fnt *Font) newRpr() *xlsxRPr {\n\tfont := fnt.newFont()\n\treturn &xlsxRPr{\n\t\tRFont:     font.Name,\n\t\tCharset:   font.Charset,\n\t\tFamily:    font.Family,\n\t\tB:         font.B,\n\t\tI:         font.I,\n\t\tStrike:    font.Strike,\n\t\tOutline:   font.Outline,\n\t\tShadow:    font.Shadow,\n\t\tCondense:  font.Condense,\n\t\tExtend:    font.Extend,\n\t\tColor:     font.Color,\n\t\tSz:        font.Sz,\n\t\tU:         font.U,\n\t\tVertAlign: font.VertAlign,\n\t\tScheme:    font.Scheme,\n\t}\n}\n\n// getFont create font format by given internal font properties.\nfunc (rPr *xlsxRPr) getFont() *Font {\n\treturn extractFont(&xlsxFont{\n\t\tName:      rPr.RFont,\n\t\tCharset:   rPr.Charset,\n\t\tFamily:    rPr.Family,\n\t\tB:         rPr.B,\n\t\tI:         rPr.I,\n\t\tStrike:    rPr.Strike,\n\t\tOutline:   rPr.Outline,\n\t\tShadow:    rPr.Shadow,\n\t\tCondense:  rPr.Condense,\n\t\tExtend:    rPr.Extend,\n\t\tColor:     rPr.Color,\n\t\tSz:        rPr.Sz,\n\t\tU:         rPr.U,\n\t\tVertAlign: rPr.VertAlign,\n\t\tScheme:    rPr.Scheme,\n\t})\n}\n\n// setRichText provides a function to set rich text of a cell.\nfunc setRichText(runs []RichTextRun) ([]xlsxR, error) {\n\tvar (\n\t\ttextRuns       []xlsxR\n\t\ttotalCellChars int\n\t)\n\tfor _, textRun := range runs {\n\t\ttotalCellChars += countUTF16String(textRun.Text)\n\t\tif totalCellChars > TotalCellChars {\n\t\t\treturn textRuns, ErrCellCharsLength\n\t\t}\n\t\trun := xlsxR{T: &xlsxT{}}\n\t\trun.T.Val, run.T.Space = trimCellValue(textRun.Text, false)\n\t\tfnt := textRun.Font\n\t\tif fnt != nil {\n\t\t\trun.RPr = fnt.newRpr()\n\t\t}\n\t\ttextRuns = append(textRuns, run)\n\t}\n\treturn textRuns, nil\n}\n\n// SetCellRichText provides a function to set cell with rich text by given\n// worksheet name, cell reference and rich text runs. For example, set rich text\n// on the A1 cell of the worksheet named Sheet1:\n//\n//\tpackage main\n//\n//\timport (\n//\t    \"fmt\"\n//\n//\t    \"github.com/xuri/excelize/v2\"\n//\t)\n//\n//\tfunc main() {\n//\t    f := excelize.NewFile()\n//\t    defer func() {\n//\t        if err := f.Close(); err != nil {\n//\t            fmt.Println(err)\n//\t        }\n//\t    }()\n//\t    if err := f.SetRowHeight(\"Sheet1\", 1, 35); err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    if err := f.SetColWidth(\"Sheet1\", \"A\", \"A\", 44); err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    if err := f.SetCellRichText(\"Sheet1\", \"A1\", []excelize.RichTextRun{\n//\t        {\n//\t            Text: \"bold\",\n//\t            Font: &excelize.Font{\n//\t                Bold:   true,\n//\t                Color:  \"2354E8\",\n//\t                Family: \"Times New Roman\",\n//\t            },\n//\t        },\n//\t        {\n//\t            Text: \" and \",\n//\t            Font: &excelize.Font{\n//\t                Family: \"Times New Roman\",\n//\t            },\n//\t        },\n//\t        {\n//\t            Text: \"italic \",\n//\t            Font: &excelize.Font{\n//\t                Bold:   true,\n//\t                Color:  \"E83723\",\n//\t                Italic: true,\n//\t                Family: \"Times New Roman\",\n//\t            },\n//\t        },\n//\t        {\n//\t            Text: \"text with color and font-family,\",\n//\t            Font: &excelize.Font{\n//\t                Bold:   true,\n//\t                Color:  \"2354E8\",\n//\t                Family: \"Times New Roman\",\n//\t            },\n//\t        },\n//\t        {\n//\t            Text: \"\\r\\nlarge text with \",\n//\t            Font: &excelize.Font{\n//\t                Size:  14,\n//\t                Color: \"AD23E8\",\n//\t            },\n//\t        },\n//\t        {\n//\t            Text: \"strike\",\n//\t            Font: &excelize.Font{\n//\t                Color:  \"E89923\",\n//\t                Strike: true,\n//\t            },\n//\t        },\n//\t        {\n//\t            Text: \" superscript\",\n//\t            Font: &excelize.Font{\n//\t                Color:     \"DBC21F\",\n//\t                VertAlign: \"superscript\",\n//\t            },\n//\t        },\n//\t        {\n//\t            Text: \" and \",\n//\t            Font: &excelize.Font{\n//\t                Size:      14,\n//\t                Color:     \"AD23E8\",\n//\t                VertAlign: \"baseline\",\n//\t            },\n//\t        },\n//\t        {\n//\t            Text: \"underline\",\n//\t            Font: &excelize.Font{\n//\t                Color:     \"23E833\",\n//\t                Underline: \"single\",\n//\t            },\n//\t        },\n//\t        {\n//\t            Text: \" subscript.\",\n//\t            Font: &excelize.Font{\n//\t                Color:     \"017505\",\n//\t                VertAlign: \"subscript\",\n//\t            },\n//\t        },\n//\t    }); err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    style, err := f.NewStyle(&excelize.Style{\n//\t        Alignment: &excelize.Alignment{\n//\t            WrapText: true,\n//\t        },\n//\t    })\n//\t    if err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    if err := f.SetCellStyle(\"Sheet1\", \"A1\", \"A1\", style); err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    if err := f.SaveAs(\"Book1.xlsx\"); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t}\nfunc (f *File) SetCellRichText(sheet, cell string, runs []RichTextRun) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tc, col, row, err := ws.prepareCell(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := f.sharedStringsLoader(); err != nil {\n\t\treturn err\n\t}\n\tc.S = ws.prepareCellStyle(col, row, c.S)\n\tsi := xlsxSI{}\n\tsst, err := f.sharedStringsReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif si.R, err = setRichText(runs); err != nil {\n\t\treturn err\n\t}\n\tf.clearCalcCache()\n\tfor idx, strItem := range sst.SI {\n\t\tif reflect.DeepEqual(strItem, si) {\n\t\t\tc.T, c.V = \"s\", strconv.Itoa(idx)\n\t\t\treturn err\n\t\t}\n\t}\n\tsst.SI = append(sst.SI, si)\n\tsst.Count++\n\tsst.UniqueCount++\n\tc.T, c.V = \"s\", strconv.Itoa(len(sst.SI)-1)\n\treturn err\n}\n\n// SetSheetRow writes an array to row by given worksheet name, starting\n// cell reference and a pointer to array type 'slice'. This function is\n// concurrency safe. For example, writes an array to row 6 start with the cell\n// B6 on Sheet1:\n//\n//\terr := f.SetSheetRow(\"Sheet1\", \"B6\", &[]interface{}{\"1\", nil, 2})\nfunc (f *File) SetSheetRow(sheet, cell string, slice interface{}) error {\n\treturn f.setSheetCells(sheet, cell, slice, rows)\n}\n\n// SetSheetCol writes an array to column by given worksheet name, starting\n// cell reference and a pointer to array type 'slice'. For example, writes an\n// array to column B start with the cell B6 on Sheet1:\n//\n//\terr := f.SetSheetCol(\"Sheet1\", \"B6\", &[]interface{}{\"1\", nil, 2})\nfunc (f *File) SetSheetCol(sheet, cell string, slice interface{}) error {\n\treturn f.setSheetCells(sheet, cell, slice, columns)\n}\n\n// setSheetCells provides a function to set worksheet cells value.\nfunc (f *File) setSheetCells(sheet, cell string, slice interface{}, dir adjustDirection) error {\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Make sure 'slice' is a Ptr to Slice\n\tv := reflect.ValueOf(slice)\n\tif v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Slice {\n\t\treturn ErrParameterInvalid\n\t}\n\tv = v.Elem()\n\tfor i := 0; i < v.Len(); i++ {\n\t\tvar cell string\n\t\tvar err error\n\t\tif dir == rows {\n\t\t\tcell, err = CoordinatesToCellName(col+i, row)\n\t\t} else {\n\t\t\tcell, err = CoordinatesToCellName(col, row+i)\n\t\t}\n\t\t// Error should never happen here. But keep checking to early detect regressions\n\t\t// if it will be introduced in the future.\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := f.SetCellValue(sheet, cell, v.Index(i).Interface()); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn err\n}\n\n// getCellInfo does common preparation for all set cell value functions.\nfunc (ws *xlsxWorksheet) prepareCell(cell string) (*xlsxC, int, int, error) {\n\tvar err error\n\tcell, err = ws.mergeCellsParser(cell)\n\tif err != nil {\n\t\treturn nil, 0, 0, err\n\t}\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn nil, 0, 0, err\n\t}\n\n\tws.prepareSheetXML(col, row)\n\treturn &ws.SheetData.Row[row-1].C[col-1], col, row, err\n}\n\n// getCellStringFunc does common value extraction workflow for all get cell\n// value function. Passed function implements specific part of required\n// logic.\nfunc (f *File) getCellStringFunc(sheet, cell string, fn func(x *xlsxWorksheet, c *xlsxC) (string, bool, error)) (string, error) {\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn \"\", err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tcell, err = ws.mergeCellsParser(cell)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\t_, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tlastRowNum := 0\n\tif l := len(ws.SheetData.Row); l > 0 {\n\t\tlastRowNum = ws.SheetData.Row[l-1].R\n\t}\n\n\t// keep in mind: row starts from 1\n\tif row > lastRowNum {\n\t\treturn \"\", nil\n\t}\n\n\tidx, found := sort.Find(len(ws.SheetData.Row), func(i int) int {\n\t\tif ws.SheetData.Row[i].R == row {\n\t\t\treturn 0\n\t\t}\n\t\tif ws.SheetData.Row[i].R > row {\n\t\t\treturn -1\n\t\t}\n\t\treturn 1\n\t})\n\tif !found {\n\t\treturn \"\", nil\n\t}\n\trowData := ws.SheetData.Row[idx]\n\tfor colIdx := range rowData.C {\n\t\tcolData := &rowData.C[colIdx]\n\t\tif cell == colData.R {\n\t\t\tval, ok, err := fn(ws, colData)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\tif ok {\n\t\t\t\treturn val, nil\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\treturn \"\", nil\n}\n\n// formattedValue provides a function to returns a value after formatted. If\n// it is possible to apply a format to the cell value, it will do so, if not\n// then an error will be returned, along with the raw value of the cell.\nfunc (f *File) formattedValue(c *xlsxC, raw bool, cellType CellType) (string, error) {\n\tif raw || c.S == 0 {\n\t\treturn c.V, nil\n\t}\n\tstyleSheet, err := f.stylesReader()\n\tif err != nil {\n\t\treturn c.V, err\n\t}\n\tif styleSheet.CellXfs == nil {\n\t\treturn c.V, err\n\t}\n\tif c.S >= len(styleSheet.CellXfs.Xf) || c.S < 0 {\n\t\treturn c.V, err\n\t}\n\tvar numFmtID int\n\tif styleSheet.CellXfs.Xf[c.S].NumFmtID != nil {\n\t\tnumFmtID = *styleSheet.CellXfs.Xf[c.S].NumFmtID\n\t}\n\tdate1904 := false\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn c.V, err\n\t}\n\tif wb != nil && wb.WorkbookPr != nil {\n\t\tdate1904 = wb.WorkbookPr.Date1904\n\t}\n\tif fmtCode, ok := styleSheet.getCustomNumFmtCode(numFmtID); ok {\n\t\treturn format(c.V, fmtCode, date1904, cellType, f.options), err\n\t}\n\tif fmtCode, ok := f.getBuiltInNumFmtCode(numFmtID); ok {\n\t\treturn f.applyBuiltInNumFmt(c, fmtCode, numFmtID, date1904, cellType), err\n\t}\n\treturn c.V, err\n}\n\n// getCustomNumFmtCode provides a function to returns custom number format code.\nfunc (ss *xlsxStyleSheet) getCustomNumFmtCode(numFmtID int) (string, bool) {\n\tif ss.NumFmts == nil {\n\t\treturn \"\", false\n\t}\n\tfor _, xlsxFmt := range ss.NumFmts.NumFmt {\n\t\tif xlsxFmt.NumFmtID == numFmtID {\n\t\t\tif xlsxFmt.FormatCode16 != \"\" {\n\t\t\t\treturn xlsxFmt.FormatCode16, true\n\t\t\t}\n\t\t\treturn xlsxFmt.FormatCode, true\n\t\t}\n\t}\n\treturn \"\", false\n}\n\n// prepareCellStyle provides a function to prepare style index of cell in\n// worksheet by given column index and style index.\nfunc (ws *xlsxWorksheet) prepareCellStyle(col, row, style int) int {\n\tif style != 0 {\n\t\treturn style\n\t}\n\tif row <= len(ws.SheetData.Row) {\n\t\tif styleID := ws.SheetData.Row[row-1].S; styleID != 0 {\n\t\t\treturn styleID\n\t\t}\n\t}\n\tif ws.Cols != nil {\n\t\tfor _, c := range ws.Cols.Col {\n\t\t\tif c.Min <= col && col <= c.Max && c.Style != 0 {\n\t\t\t\treturn c.Style\n\t\t\t}\n\t\t}\n\t}\n\treturn style\n}\n\n// mergeCellsParser provides a function to check merged cells in worksheet by\n// given cell reference.\nfunc (ws *xlsxWorksheet) mergeCellsParser(cell string) (string, error) {\n\tcell = strings.ToUpper(cell)\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn cell, err\n\t}\n\tif ws.MergeCells != nil {\n\t\tfor i := 0; i < len(ws.MergeCells.Cells); i++ {\n\t\t\tif ws.MergeCells.Cells[i] == nil {\n\t\t\t\tws.MergeCells.Cells = append(ws.MergeCells.Cells[:i], ws.MergeCells.Cells[i+1:]...)\n\t\t\t\ti--\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif ref := ws.MergeCells.Cells[i].Ref; len(ws.MergeCells.Cells[i].rect) == 0 && ref != \"\" {\n\t\t\t\tif strings.Count(ref, \":\") != 1 {\n\t\t\t\t\tref += \":\" + ref\n\t\t\t\t}\n\t\t\t\trect, err := rangeRefToCoordinates(ref)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn cell, err\n\t\t\t\t}\n\t\t\t\t_ = sortCoordinates(rect)\n\t\t\t\tws.MergeCells.Cells[i].rect = rect\n\t\t\t}\n\t\t\tif cellInRange([]int{col, row}, ws.MergeCells.Cells[i].rect) {\n\t\t\t\tcell = strings.Split(ws.MergeCells.Cells[i].Ref, \":\")[0]\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn cell, nil\n}\n\n// checkCellInRangeRef provides a function to determine if a given cell reference\n// in a range.\nfunc (f *File) checkCellInRangeRef(cell, rangeRef string) (bool, error) {\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tif rng := strings.Split(rangeRef, \":\"); len(rng) != 2 {\n\t\treturn false, err\n\t}\n\tcoordinates, err := rangeRefToCoordinates(rangeRef)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn cellInRange([]int{col, row}, coordinates), err\n}\n\n// cellInRange provides a function to determine if a given range is within a\n// range.\nfunc cellInRange(cell, ref []int) bool {\n\treturn cell[0] >= ref[0] && cell[0] <= ref[2] && cell[1] >= ref[1] && cell[1] <= ref[3]\n}\n\n// isOverlap find if the given two rectangles overlap or not.\nfunc isOverlap(rect1, rect2 []int) bool {\n\treturn cellInRange([]int{rect1[0], rect1[1]}, rect2) ||\n\t\tcellInRange([]int{rect1[2], rect1[1]}, rect2) ||\n\t\tcellInRange([]int{rect1[0], rect1[3]}, rect2) ||\n\t\tcellInRange([]int{rect1[2], rect1[3]}, rect2) ||\n\t\tcellInRange([]int{rect2[0], rect2[1]}, rect1) ||\n\t\tcellInRange([]int{rect2[2], rect2[1]}, rect1) ||\n\t\tcellInRange([]int{rect2[0], rect2[3]}, rect1) ||\n\t\tcellInRange([]int{rect2[2], rect2[3]}, rect1)\n}\n\n// convertSharedFormula creates a non shared formula from the shared formula\n// counterpart by given cell reference which not containing the formula.\nfunc (c *xlsxC) convertSharedFormula(cell string) (string, error) {\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tsharedCol, sharedRow, err := CellNameToCoordinates(c.R)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdCol, dRow := col-sharedCol, row-sharedRow\n\tps := efp.ExcelParser()\n\ttokens := ps.Parse(c.F.Content)\n\tfor i := range tokens {\n\t\ttoken := tokens[i]\n\t\tif token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeRange {\n\t\t\ttokens[i].TValue = shiftCell(token.TValue, dCol, dRow)\n\t\t}\n\t}\n\treturn ps.Render(), nil\n}\n\n// getSharedFormula find a cell contains the same formula as another cell,\n// the \"shared\" value can be used for the t attribute and the si attribute can\n// be used to refer to the cell containing the formula. Two formulas are\n// considered to be the same when their respective representations in\n// R1C1-reference notation, are the same.\n//\n// Note that this function not validate ref tag to check the cell whether in\n// allow range reference, and always return origin shared formula.\nfunc getSharedFormula(ws *xlsxWorksheet, si int, cell string) (string, error) {\n\tval, ok := ws.formulaSI.Load(si)\n\n\tif ok {\n\t\treturn val.(*xlsxC).convertSharedFormula(cell)\n\t}\n\tfor row := range ws.SheetData.Row {\n\t\tr := &ws.SheetData.Row[row]\n\t\tfor column := range r.C {\n\t\t\tc := &r.C[column]\n\t\t\tif c.F != nil && c.F.Ref != \"\" && c.F.T == STCellFormulaTypeShared && c.F.Si != nil && *c.F.Si == si {\n\t\t\t\tws.formulaSI.Store(si, c)\n\t\t\t\treturn c.convertSharedFormula(cell)\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\", nil\n}\n\n// shiftCell returns the cell shifted according to dCol and dRow taking into\n// consideration absolute references with dollar sign ($)\nfunc shiftCell(val string, dCol, dRow int) string {\n\tparts := strings.Split(val, \":\")\n\tfor j := 0; j < len(parts); j++ {\n\t\tcell := parts[j]\n\t\ttrimmedCellName := strings.ReplaceAll(cell, \"$\", \"\")\n\t\tc, r, err := CellNameToCoordinates(trimmedCellName)\n\t\tif err == nil {\n\t\t\tabsCol := strings.Index(cell, \"$\") == 0\n\t\t\tabsRow := strings.LastIndex(cell, \"$\") > 0\n\t\t\tif !absCol && !absRow {\n\t\t\t\tparts[j], _ = CoordinatesToCellName(c+dCol, r+dRow)\n\t\t\t}\n\t\t\tif !absCol && absRow {\n\t\t\t\tcolName, _ := ColumnNumberToName(c + dCol)\n\t\t\t\tparts[j] = colName + \"$\" + strconv.Itoa(r)\n\t\t\t}\n\t\t\tif absCol && !absRow {\n\t\t\t\tcolName, _ := ColumnNumberToName(c)\n\t\t\t\tparts[j] = \"$\" + colName + strconv.Itoa(r+dRow)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\t// Cell reference is a column name\n\t\tc, err = ColumnNameToNumber(trimmedCellName)\n\t\tif err == nil && !strings.HasPrefix(cell, \"$\") {\n\t\t\tparts[j], _ = ColumnNumberToName(c + dCol)\n\t\t\tcontinue\n\t\t}\n\t\t// Cell reference is a row number\n\t\tr, err = strconv.Atoi(trimmedCellName)\n\t\tif err == nil && !strings.HasPrefix(cell, \"$\") {\n\t\t\tparts[j] = strconv.Itoa(r + dRow)\n\t\t}\n\t}\n\treturn strings.Join(parts, \":\")\n}\n"
  },
  {
    "path": "cell_test.go",
    "content": "package excelize\n\nimport (\n\t\"fmt\"\n\t_ \"image/jpeg\"\n\t\"math\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestConcurrency(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\twg := new(sync.WaitGroup)\n\tfor i := 1; i <= 5; i++ {\n\t\twg.Add(1)\n\t\tgo func(val int, t *testing.T) {\n\t\t\t// Concurrency set cell value\n\t\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"A%d\", val), val))\n\t\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"B%d\", val), strconv.Itoa(val)))\n\t\t\t// Concurrency get cell value\n\t\t\t_, err := f.GetCellValue(\"Sheet1\", fmt.Sprintf(\"A%d\", val))\n\t\t\tassert.NoError(t, err)\n\t\t\t// Concurrency set rows\n\t\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"B6\", &[]interface{}{\n\t\t\t\t\" Hello\",\n\t\t\t\t[]byte(\"World\"), 42, int8(1<<8/2 - 1), int16(1<<16/2 - 1), int32(1<<32/2 - 1),\n\t\t\t\tint64(1<<32/2 - 1), float32(42.65418), -42.65418, float32(42), float64(42),\n\t\t\t\tuint(1<<32 - 1), uint8(1<<8 - 1), uint16(1<<16 - 1), uint32(1<<32 - 1),\n\t\t\t\tuint64(1<<32 - 1), true, complex64(5 + 10i),\n\t\t\t}))\n\t\t\t// Concurrency create style\n\t\t\tstyle, err := f.NewStyle(&Style{Font: &Font{Color: \"1265BE\", Underline: \"single\"}})\n\t\t\tassert.NoError(t, err)\n\t\t\t// Concurrency set cell style\n\t\t\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A3\", \"A3\", style))\n\t\t\t// Concurrency get cell style\n\t\t\t_, err = f.GetCellStyle(\"Sheet1\", \"A3\")\n\t\t\tassert.NoError(t, err)\n\t\t\t// Concurrency add picture\n\t\t\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"F21\", filepath.Join(\"test\", \"images\", \"excel.jpg\"),\n\t\t\t\t&GraphicOptions{\n\t\t\t\t\tOffsetX:       10,\n\t\t\t\t\tOffsetY:       10,\n\t\t\t\t\tHyperlink:     \"https://github.com/xuri/excelize\",\n\t\t\t\t\tHyperlinkType: \"External\",\n\t\t\t\t\tPositioning:   \"oneCell\",\n\t\t\t\t},\n\t\t\t))\n\t\t\t// Concurrency get cell picture\n\t\t\tpics, err := f.GetPictures(\"Sheet1\", \"A1\")\n\t\t\tassert.Len(t, pics, 0)\n\t\t\tassert.NoError(t, err)\n\t\t\t// Concurrency iterate rows\n\t\t\trows, err := f.Rows(\"Sheet1\")\n\t\t\tassert.NoError(t, err)\n\t\t\tfor rows.Next() {\n\t\t\t\t_, err := rows.Columns()\n\t\t\t\tassert.NoError(t, err)\n\t\t\t}\n\t\t\t// Concurrency iterate columns\n\t\t\tcols, err := f.Cols(\"Sheet1\")\n\t\t\tassert.NoError(t, err)\n\t\t\tfor cols.Next() {\n\t\t\t\t_, err := cols.Rows()\n\t\t\t\tassert.NoError(t, err)\n\t\t\t}\n\t\t\t// Concurrency set columns style\n\t\t\tassert.NoError(t, f.SetColStyle(\"Sheet1\", \"C:E\", style))\n\t\t\t// Concurrency get columns style\n\t\t\tstyleID, err := f.GetColStyle(\"Sheet1\", \"D\")\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, style, styleID)\n\t\t\t// Concurrency set columns width\n\t\t\tassert.NoError(t, f.SetColWidth(\"Sheet1\", \"A\", \"B\", 10))\n\t\t\t// Concurrency get columns width\n\t\t\twidth, err := f.GetColWidth(\"Sheet1\", \"A\")\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, 10.0, width)\n\t\t\t// Concurrency set columns visible\n\t\t\tassert.NoError(t, f.SetColVisible(\"Sheet1\", \"A:B\", true))\n\t\t\t// Concurrency get columns visible\n\t\t\tvisible, err := f.GetColVisible(\"Sheet1\", \"A\")\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, true, visible)\n\t\t\t// Concurrency add data validation\n\t\t\tdv := NewDataValidation(true)\n\t\t\tdv.Sqref = fmt.Sprintf(\"A%d:B%d\", val, val)\n\t\t\tassert.NoError(t, dv.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan))\n\t\t\tdv.SetInput(fmt.Sprintf(\"title:%d\", val), strconv.Itoa(val))\n\t\t\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\t\t\t// Concurrency delete data validation with reference sequence\n\t\t\tassert.NoError(t, f.DeleteDataValidation(\"Sheet1\", dv.Sqref))\n\t\t\twg.Done()\n\t\t}(i, t)\n\t}\n\twg.Wait()\n\tval, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tassert.Equal(t, \"1\", val)\n\t// Test the length of data validation\n\tdataValidations, err := f.GetDataValidations(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, dataValidations, 0)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestConcurrency.xlsx\")))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestCheckCellInRangeRef(t *testing.T) {\n\tf := NewFile()\n\texpectedTrueCellInRangeRefList := [][2]string{\n\t\t{\"c2\", \"A1:AAZ32\"},\n\t\t{\"B9\", \"A1:B9\"},\n\t\t{\"C2\", \"C2:C2\"},\n\t}\n\n\tfor _, expectedTrueCellInRangeRef := range expectedTrueCellInRangeRefList {\n\t\tcell := expectedTrueCellInRangeRef[0]\n\t\treference := expectedTrueCellInRangeRef[1]\n\t\tok, err := f.checkCellInRangeRef(cell, reference)\n\t\tassert.NoError(t, err)\n\t\tassert.Truef(t, ok,\n\t\t\t\"Expected cell %v to be in range reference %v, got false\\n\", cell, reference)\n\t}\n\n\texpectedFalseCellInRangeRefList := [][2]string{\n\t\t{\"c2\", \"A4:AAZ32\"},\n\t\t{\"C4\", \"D6:A1\"}, // weird case, but you never know\n\t\t{\"AEF42\", \"BZ40:AEF41\"},\n\t}\n\n\tfor _, expectedFalseCellInRangeRef := range expectedFalseCellInRangeRefList {\n\t\tcell := expectedFalseCellInRangeRef[0]\n\t\treference := expectedFalseCellInRangeRef[1]\n\t\tok, err := f.checkCellInRangeRef(cell, reference)\n\t\tassert.NoError(t, err)\n\t\tassert.Falsef(t, ok,\n\t\t\t\"Expected cell %v not to be inside of range reference %v, but got true\\n\", cell, reference)\n\t}\n\n\tok, err := f.checkCellInRangeRef(\"A1\", \"A:B\")\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), err)\n\tassert.False(t, ok)\n\n\tok, err = f.checkCellInRangeRef(\"AA0\", \"Z0:AB1\")\n\tassert.Equal(t, newCellNameToCoordinatesError(\"AA0\", newInvalidCellNameError(\"AA0\")), err)\n\tassert.False(t, ok)\n}\n\nfunc TestSetCellFloat(t *testing.T) {\n\tsheet := \"Sheet1\"\n\tt.Run(\"with no decimal\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetCellFloat(sheet, \"A1\", 123.0, -1, 64))\n\t\tassert.NoError(t, f.SetCellFloat(sheet, \"A2\", 123.0, 1, 64))\n\t\tval, err := f.GetCellValue(sheet, \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"123\", val, \"A1 should be 123\")\n\t\tval, err = f.GetCellValue(sheet, \"A2\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"123\", val, \"A2 should be 123\")\n\t})\n\n\tt.Run(\"with a decimal and precision limit\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetCellFloat(sheet, \"A1\", 123.42, 1, 64))\n\t\tval, err := f.GetCellValue(sheet, \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"123.4\", val, \"A1 should be 123.4\")\n\t})\n\n\tt.Run(\"with a decimal and no limit\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetCellFloat(sheet, \"A1\", 123.42, -1, 64))\n\t\tval, err := f.GetCellValue(sheet, \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"123.42\", val, \"A1 should be 123.42\")\n\t})\n\tf := NewFile()\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.SetCellFloat(sheet, \"A\", 123.42, -1, 64))\n\t// Test set cell float data type value with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.SetCellFloat(\"Sheet:1\", \"A1\", 123.42, -1, 64))\n}\n\nfunc TestSetCellUint(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", uint8(math.MaxUint8)))\n\tresult, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.Equal(t, \"255\", result)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", uint(math.MaxUint16)))\n\tresult, err = f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.Equal(t, \"65535\", result)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", uint(math.MaxUint32)))\n\tresult, err = f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.Equal(t, \"4294967295\", result)\n\tassert.NoError(t, err)\n\t// Test uint cell value not exists worksheet\n\tassert.EqualError(t, f.SetCellUint(\"SheetN\", \"A1\", 1), \"sheet SheetN does not exist\")\n\t// Test uint cell value with illegal cell reference\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.SetCellUint(\"Sheet1\", \"A\", 1))\n}\n\nfunc TestSetCellValuesMultiByte(t *testing.T) {\n\tf := NewFile()\n\trow := []interface{}{\n\t\t// Test set cell value with multi byte characters value\n\t\tstrings.Repeat(\"\\u4E00\", TotalCellChars+1),\n\t\t// Test set cell value with XML escape characters\n\t\tstrings.Repeat(\"<>\", TotalCellChars/2),\n\t\tstrings.Repeat(\">\", TotalCellChars-1),\n\t\tstrings.Repeat(\">\", TotalCellChars+1),\n\t}\n\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A1\", &row))\n\t// Test set cell value with XML escape characters in stream writer\n\t_, err := f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tstreamWriter, err := f.NewStreamWriter(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", row))\n\tassert.NoError(t, streamWriter.Flush())\n\tfor _, sheetName := range []string{\"Sheet1\", \"Sheet2\"} {\n\t\tfor cell, expected := range map[string]int{\n\t\t\t\"A1\": TotalCellChars,\n\t\t\t\"B1\": TotalCellChars - 1,\n\t\t\t\"C1\": TotalCellChars - 1,\n\t\t\t\"D1\": TotalCellChars,\n\t\t} {\n\t\t\tresult, err := f.GetCellValue(sheetName, cell)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Len(t, []rune(result), expected)\n\t\t}\n\t}\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellValuesMultiByte.xlsx\")))\n}\n\nfunc TestSetCellValue(t *testing.T) {\n\tf := NewFile()\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.SetCellValue(\"Sheet1\", \"A\", time.Now().UTC()))\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.SetCellValue(\"Sheet1\", \"A\", time.Duration(1e13)))\n\t// Test set cell value with column and row style inherit\n\tstyle1, err := f.NewStyle(&Style{NumFmt: 2})\n\tassert.NoError(t, err)\n\tstyle2, err := f.NewStyle(&Style{NumFmt: 9})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetColStyle(\"Sheet1\", \"B\", style1))\n\tassert.NoError(t, f.SetRowStyle(\"Sheet1\", 1, 1, style2))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B1\", 0.5))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B2\", 0.5))\n\tB1, err := f.GetCellValue(\"Sheet1\", \"B1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"50%\", B1)\n\tB2, err := f.GetCellValue(\"Sheet1\", \"B2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"0.50\", B2)\n\n\t// Test set cell value with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.SetCellValue(\"Sheet:1\", \"A1\", \"A1\"))\n\t// Test set cell value with unsupported charset shared strings table\n\tf.SharedStrings = nil\n\tf.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetCellValue(\"Sheet1\", \"A1\", \"A1\"), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test set cell value with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetCellValue(\"Sheet1\", \"A1\", time.Now().UTC()), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test set cell value with the shared string table's count not equal with unique count\n\tf = NewFile()\n\tf.SharedStrings = nil\n\tf.Pkg.Store(defaultXMLPathSharedStrings, []byte(fmt.Sprintf(`<sst xmlns=\"%s\" count=\"2\" uniqueCount=\"1\"><si><t>a</t></si><si><t>a</t></si></sst>`, NameSpaceSpreadSheet.Value)))\n\tf.Sheet.Store(\"xl/worksheets/sheet1.xml\", &xlsxWorksheet{\n\t\tSheetData: xlsxSheetData{Row: []xlsxRow{\n\t\t\t{R: 1, C: []xlsxC{{R: \"A1\", T: \"str\", V: \"1\"}}},\n\t\t}},\n\t})\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", \"b\"))\n\tval, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"b\", val)\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B1\", \"b\"))\n\tval, err = f.GetCellValue(\"Sheet1\", \"B1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"b\", val)\n\n\tf = NewFile()\n\t// Test set cell value with an IEEE 754 \"not-a-number\" value or infinity\n\tfor num, expected := range map[float64]string{\n\t\tmath.NaN():   \"NaN\",\n\t\tmath.Inf(0):  \"+Inf\",\n\t\tmath.Inf(-1): \"-Inf\",\n\t} {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", num))\n\t\tval, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, val)\n\t}\n\t// Test set cell value with time duration\n\tfor val, expected := range map[time.Duration]string{\n\t\ttime.Hour*21 + time.Minute*51 + time.Second*44: \"21:51:44\",\n\t\ttime.Hour*21 + time.Minute*50:                  \"21:50\",\n\t\ttime.Hour*24 + time.Minute*51 + time.Second*44: \"24:51:44\",\n\t\ttime.Hour*24 + time.Minute*50:                  \"24:50:00\",\n\t} {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", val))\n\t\tval, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, val)\n\t}\n\t// Test set cell value with time\n\tfor val, expected := range map[time.Time]string{\n\t\ttime.Date(2024, time.October, 1, 0, 0, 0, 0, time.UTC):   \"Oct-24\",\n\t\ttime.Date(2024, time.October, 10, 0, 0, 0, 0, time.UTC):  \"10-10-24\",\n\t\ttime.Date(2024, time.October, 10, 12, 0, 0, 0, time.UTC): \"10/10/24 12:00\",\n\t} {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", val))\n\t\tval, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, val)\n\t}\n}\n\nfunc TestSetCellValues(t *testing.T) {\n\tf := NewFile()\n\terr := f.SetCellValue(\"Sheet1\", \"A1\", time.Date(2010, time.December, 31, 0, 0, 0, 0, time.UTC))\n\tassert.NoError(t, err)\n\n\tv, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, v, \"12-31-10\")\n\n\t// Test date value lower than min date supported by Excel\n\terr = f.SetCellValue(\"Sheet1\", \"A1\", time.Date(1600, time.December, 31, 0, 0, 0, 0, time.UTC))\n\tassert.NoError(t, err)\n\n\tv, err = f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, v, \"1600-12-31T00:00:00Z\")\n}\n\nfunc TestSetCellBool(t *testing.T) {\n\tf := NewFile()\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.SetCellBool(\"Sheet1\", \"A\", true))\n\t// Test set cell boolean data type value with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.SetCellBool(\"Sheet:1\", \"A1\", true))\n}\n\nfunc TestSetCellTime(t *testing.T) {\n\tdate, err := time.Parse(time.RFC3339Nano, \"2009-11-10T23:00:00Z\")\n\tassert.NoError(t, err)\n\tfor location, expected := range map[string]string{\n\t\t\"America/New_York\": \"40127.75\",\n\t\t\"Asia/Shanghai\":    \"40128.291666666664\",\n\t\t\"Europe/London\":    \"40127.958333333336\",\n\t\t\"UTC\":              \"40127.958333333336\",\n\t} {\n\t\ttimezone, err := time.LoadLocation(location)\n\t\tassert.NoError(t, err)\n\t\tc := &xlsxC{}\n\t\tisNum, err := c.setCellTime(date.In(timezone), false)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, true, isNum)\n\t\tassert.Equal(t, expected, c.V)\n\t}\n}\n\nfunc TestGetCellValue(t *testing.T) {\n\t// Test get cell value without r attribute of the row\n\tf := NewFile()\n\tsheetData := `<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><sheetData>%s</sheetData></worksheet>`\n\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(fmt.Sprintf(sheetData, `<row r=\"3\"><c t=\"inlineStr\"><is><t>A3</t></is></c></row><row><c t=\"inlineStr\"><is><t>A4</t></is></c><c t=\"inlineStr\"><is><t>B4</t></is></c></row><row r=\"7\"><c t=\"inlineStr\"><is><t>A7</t></is></c><c t=\"inlineStr\"><is><t>B7</t></is></c></row><row><c t=\"inlineStr\"><is><t>A8</t></is></c><c t=\"inlineStr\"><is><t>B8</t></is></c></row>`)))\n\tf.checked = sync.Map{}\n\tcells := []string{\"A3\", \"A4\", \"B4\", \"A7\", \"B7\"}\n\trows, err := f.GetRows(\"Sheet1\")\n\tassert.Equal(t, [][]string{nil, nil, {\"A3\"}, {\"A4\", \"B4\"}, nil, nil, {\"A7\", \"B7\"}, {\"A8\", \"B8\"}}, rows)\n\tassert.NoError(t, err)\n\tfor _, cell := range cells {\n\t\tvalue, err := f.GetCellValue(\"Sheet1\", cell)\n\t\tassert.Equal(t, cell, value)\n\t\tassert.NoError(t, err)\n\t}\n\tcols, err := f.GetCols(\"Sheet1\")\n\tassert.Equal(t, [][]string{{\"\", \"\", \"A3\", \"A4\", \"\", \"\", \"A7\", \"A8\"}, {\"\", \"\", \"\", \"B4\", \"\", \"\", \"B7\", \"B8\"}}, cols)\n\tassert.NoError(t, err)\n\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(fmt.Sprintf(sheetData, `<row r=\"2\"><c r=\"A2\" t=\"inlineStr\"><is><t>A2</t></is></c></row><row r=\"2\"><c r=\"B2\" t=\"inlineStr\"><is><t>B2</t></is></c></row>`)))\n\tf.checked = sync.Map{}\n\tcell, err := f.GetCellValue(\"Sheet1\", \"A2\")\n\tassert.Equal(t, \"A2\", cell)\n\tassert.NoError(t, err)\n\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(fmt.Sprintf(sheetData, `<row r=\"2\"><c r=\"A2\" t=\"inlineStr\"><is><t>A2</t></is></c></row><row r=\"2\"><c r=\"B2\" t=\"inlineStr\"><is><t>B2</t></is></c></row>`)))\n\tf.checked = sync.Map{}\n\trows, err = f.GetRows(\"Sheet1\")\n\tassert.Equal(t, [][]string{nil, {\"A2\", \"B2\"}}, rows)\n\tassert.NoError(t, err)\n\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(fmt.Sprintf(sheetData, `<row r=\"1\"><c r=\"A1\" t=\"inlineStr\"><is><t>A1</t></is></c></row><row r=\"1\"><c r=\"B1\" t=\"inlineStr\"><is><t>B1</t></is></c></row>`)))\n\tf.checked = sync.Map{}\n\trows, err = f.GetRows(\"Sheet1\")\n\tassert.Equal(t, [][]string{{\"A1\", \"B1\"}}, rows)\n\tassert.NoError(t, err)\n\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(fmt.Sprintf(sheetData, `<row><c t=\"inlineStr\"><is><t>A3</t></is></c></row><row><c t=\"inlineStr\"><is><t>A4</t></is></c><c t=\"inlineStr\"><is><t>B4</t></is></c></row><row r=\"7\"><c t=\"inlineStr\"><is><t>A7</t></is></c><c t=\"inlineStr\"><is><t>B7</t></is></c></row><row><c t=\"inlineStr\"><is><t>A8</t></is></c><c t=\"inlineStr\"><is><t>B8</t></is></c></row>`)))\n\tf.checked = sync.Map{}\n\trows, err = f.GetRows(\"Sheet1\")\n\tassert.Equal(t, [][]string{{\"A3\"}, {\"A4\", \"B4\"}, nil, nil, nil, nil, {\"A7\", \"B7\"}, {\"A8\", \"B8\"}}, rows)\n\tassert.NoError(t, err)\n\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(fmt.Sprintf(sheetData, `<row r=\"0\"><c r=\"H6\" t=\"inlineStr\"><is><t>H6</t></is></c><c r=\"A1\" t=\"inlineStr\"><is><t>r0A6</t></is></c><c r=\"F4\" t=\"inlineStr\"><is><t>F4</t></is></c></row><row><c r=\"A1\" t=\"inlineStr\"><is><t>A6</t></is></c><c r=\"B1\" t=\"inlineStr\"><is><t>B6</t></is></c><c r=\"C1\" t=\"inlineStr\"><is><t>C6</t></is></c></row><row r=\"3\"><c r=\"A3\"><v>100</v></c><c r=\"B3\" t=\"inlineStr\"><is><t>B3</t></is></c></row>`)))\n\tf.checked = sync.Map{}\n\tcell, err = f.GetCellValue(\"Sheet1\", \"H6\")\n\tassert.Equal(t, \"H6\", cell)\n\tassert.NoError(t, err)\n\trows, err = f.GetRows(\"Sheet1\")\n\tassert.Equal(t, [][]string{\n\t\t{\"A6\", \"B6\", \"C6\"},\n\t\tnil,\n\t\t{\"100\", \"B3\"},\n\t\t{\"\", \"\", \"\", \"\", \"\", \"F4\"},\n\t\tnil,\n\t\t{\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"H6\"},\n\t}, rows)\n\tassert.NoError(t, err)\n\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(fmt.Sprintf(sheetData, `<row><c r=\"A1\" t=\"inlineStr\"><is><t>A1</t></is></c></row><row></row><row><c r=\"A3\" t=\"inlineStr\"><is><t>A3</t></is></c></row>`)))\n\tf.checked = sync.Map{}\n\trows, err = f.GetRows(\"Sheet1\")\n\tassert.Equal(t, [][]string{{\"A1\"}, nil, {\"A3\"}}, rows)\n\tassert.NoError(t, err)\n\tcell, err = f.GetCellValue(\"Sheet1\", \"A3\")\n\tassert.Equal(t, \"A3\", cell)\n\tassert.NoError(t, err)\n\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(fmt.Sprintf(sheetData, `\n\t<row r=\"1\"><c r=\"A1\"><v>2422.3000000000002</v></c></row>\n\t<row r=\"2\"><c r=\"A2\"><v>2422.3000000000002</v></c></row>\n\t<row r=\"3\"><c r=\"A3\"><v>12.4</v></c></row>\n\t<row r=\"4\"><c r=\"A4\"><v>964</v></c></row>\n\t<row r=\"5\"><c r=\"A5\"><v>1101.5999999999999</v></c></row>\n\t<row r=\"6\"><c r=\"A6\"><v>275.39999999999998</v></c></row>\n\t<row r=\"7\"><c r=\"A7\"><v>68.900000000000006</v></c></row>\n\t<row r=\"8\"><c r=\"A8\"><v>44385.208333333336</v></c></row>\n\t<row r=\"9\"><c r=\"A9\"><v>5.0999999999999996</v></c></row>\n\t<row r=\"10\"><c r=\"A10\"><v>5.1100000000000003</v></c></row>\n\t<row r=\"11\"><c r=\"A11\"><v>5.0999999999999996</v></c></row>\n\t<row r=\"12\"><c r=\"A12\"><v>5.1109999999999998</v></c></row>\n\t<row r=\"13\"><c r=\"A13\"><v>5.1111000000000004</v></c></row>\n\t<row r=\"14\"><c r=\"A14\"><v>2422.012345678</v></c></row>\n\t<row r=\"15\"><c r=\"A15\"><v>2422.0123456789</v></c></row>\n\t<row r=\"16\"><c r=\"A16\"><v>12.012345678901</v></c></row>\n\t<row r=\"17\"><c r=\"A17\"><v>964</v></c></row>\n\t<row r=\"18\"><c r=\"A18\"><v>1101.5999999999999</v></c></row>\n\t<row r=\"19\"><c r=\"A19\"><v>275.39999999999998</v></c></row>\n\t<row r=\"20\"><c r=\"A20\"><v>68.900000000000006</v></c></row>\n\t<row r=\"21\"><c r=\"A21\"><v>8.8880000000000001E-2</v></c></row>\n\t<row r=\"22\"><c r=\"A22\"><v>4.0000000000000003e-5</v></c></row>\n\t<row r=\"23\"><c r=\"A23\"><v>2422.3000000000002</v></c></row>\n\t<row r=\"24\"><c r=\"A24\"><v>1101.5999999999999</v></c></row>\n\t<row r=\"25\"><c r=\"A25\"><v>275.39999999999998</v></c></row>\n\t<row r=\"26\"><c r=\"A26\"><v>68.900000000000006</v></c></row>\n\t<row r=\"27\"><c r=\"A27\"><v>1.1000000000000001</v></c></row>\n\t<row r=\"28\"><c r=\"A28\" t=\"inlineStr\"><is><t>1234567890123_4</t></is></c></row>\n\t<row r=\"29\"><c r=\"A29\" t=\"inlineStr\"><is><t>123456789_0123_4</t></is></c></row>\n\t<row r=\"30\"><c r=\"A30\"><v>+0.0000000000000000002399999999999992E-4</v></c></row>\n\t<row r=\"31\"><c r=\"A31\"><v>7.2399999999999992E-2</v></c></row>\n\t<row r=\"32\"><c r=\"A32\" t=\"d\"><v>20200208T080910.123</v></c></row>\n\t<row r=\"33\"><c r=\"A33\" t=\"d\"><v>20200208T080910,123</v></c></row>\n\t<row r=\"34\"><c r=\"A34\" t=\"d\"><v>20221022T150529Z</v></c></row>\n\t<row r=\"35\"><c r=\"A35\" t=\"d\"><v>2022-10-22T15:05:29Z</v></c></row>\n\t<row r=\"36\"><c r=\"A36\" t=\"d\"><v>2020-07-10 15:00:00.000</v></c></row>`)))\n\tf.checked = sync.Map{}\n\trows, err = f.GetCols(\"Sheet1\")\n\tassert.Equal(t, []string{\n\t\t\"2422.3\",\n\t\t\"2422.3\",\n\t\t\"12.4\",\n\t\t\"964\",\n\t\t\"1101.6\",\n\t\t\"275.4\",\n\t\t\"68.9\",\n\t\t\"44385.2083333333\",\n\t\t\"5.1\",\n\t\t\"5.11\",\n\t\t\"5.1\",\n\t\t\"5.111\",\n\t\t\"5.1111\",\n\t\t\"2422.012345678\",\n\t\t\"2422.0123456789\",\n\t\t\"12.012345678901\",\n\t\t\"964\",\n\t\t\"1101.6\",\n\t\t\"275.4\",\n\t\t\"68.9\",\n\t\t\"0.08888\",\n\t\t\"0.00004\",\n\t\t\"2422.3\",\n\t\t\"1101.6\",\n\t\t\"275.4\",\n\t\t\"68.9\",\n\t\t\"1.1\",\n\t\t\"1234567890123_4\",\n\t\t\"123456789_0123_4\",\n\t\t\"2.39999999999999E-23\",\n\t\t\"0.0724\",\n\t\t\"43869.3397004977\",\n\t\t\"43869.3397004977\",\n\t\t\"44856.6288078704\",\n\t\t\"44856.6288078704\",\n\t\t\"2020-07-10 15:00:00.000\",\n\t}, rows[0])\n\tassert.NoError(t, err)\n\n\t// Test get cell value with unsupported charset shared strings table\n\tf.SharedStrings = nil\n\tf.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)\n\t_, value := f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, value, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get cell value with invalid sheet name\n\t_, err = f.GetCellValue(\"Sheet:1\", \"A1\")\n\tassert.Equal(t, ErrSheetNameInvalid, err)\n}\n\nfunc TestGetCellType(t *testing.T) {\n\tf := NewFile()\n\tcellType, err := f.GetCellType(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, CellTypeUnset, cellType)\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", \"A1\"))\n\tcellType, err = f.GetCellType(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, CellTypeSharedString, cellType)\n\t_, err = f.GetCellType(\"Sheet1\", \"A\")\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), err)\n\t// Test get cell type with invalid sheet name\n\t_, err = f.GetCellType(\"Sheet:1\", \"A1\")\n\tassert.Equal(t, ErrSheetNameInvalid, err)\n}\n\nfunc TestGetValueFrom(t *testing.T) {\n\tf := NewFile()\n\tc := xlsxC{T: \"s\"}\n\tsst, err := f.sharedStringsReader()\n\tassert.NoError(t, err)\n\tvalue, err := c.getValueFrom(f, sst, false)\n\tassert.NoError(t, err)\n\tassert.Empty(t, value)\n\n\tc = xlsxC{T: \"s\", V: \" 1 \"}\n\tvalue, err = c.getValueFrom(f, &xlsxSST{Count: 1, SI: []xlsxSI{{}, {T: &xlsxT{Val: \"s\"}}}}, false)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"s\", value)\n}\n\nfunc TestGetCellFormula(t *testing.T) {\n\t// Test get cell formula on not exist worksheet\n\tf := NewFile()\n\t_, err := f.GetCellFormula(\"SheetN\", \"A1\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\n\t// Test get cell formula with invalid sheet name\n\t_, err = f.GetCellFormula(\"Sheet:1\", \"A1\")\n\tassert.Equal(t, ErrSheetNameInvalid, err)\n\n\t// Test get cell formula on no formula cell\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", true))\n\t_, err = f.GetCellFormula(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\n\t// Test get cell shared formula\n\tf = NewFile()\n\tsheetData := `<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><sheetData><row r=\"1\"><c r=\"A1\"><v>1</v></c><c r=\"B1\"><f>2*A1</f></c></row><row r=\"2\"><c r=\"A2\"><v>2</v></c><c r=\"B2\"><f t=\"shared\" ref=\"B2:B7\" si=\"0\">%s</f></c></row><row r=\"3\"><c r=\"A3\"><v>3</v></c><c r=\"B3\"><f t=\"shared\" si=\"0\"/></c></row><row r=\"4\"><c r=\"A4\"><v>4</v></c><c r=\"B4\"><f t=\"shared\" si=\"0\"/></c></row><row r=\"5\"><c r=\"A5\"><v>5</v></c><c r=\"B5\"><f t=\"shared\" si=\"0\"/></c></row><row r=\"6\"><c r=\"A6\"><v>6</v></c><c r=\"B6\"><f t=\"shared\" si=\"0\"/></c></row><row r=\"7\"><c r=\"A7\"><v>7</v></c><c r=\"B7\"><f t=\"shared\" si=\"0\"/></c></row></sheetData></worksheet>`\n\n\tfor sharedFormula, expected := range map[string]string{\n\t\t`2*A2`:                 `2*A3`,\n\t\t`2*A1A`:                `2*A1A`,\n\t\t`2*$A$2+LEN(\"\")`:       `2*$A$2+LEN(\"\")`,\n\t\t`SUMIF(A:A,$B11, 5:5)`: `SUMIF(A:A,$B12,6:6)`,\n\t\t`SUMIF(A:A,B$11, 5:5)`: `SUMIF(A:A,B$11,6:6)`,\n\t} {\n\t\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\t\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(fmt.Sprintf(sheetData, sharedFormula)))\n\t\tformula, err := f.GetCellFormula(\"Sheet1\", \"B3\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, formula)\n\t\t// Test get shared formula form cache\n\t\tformula, err = f.GetCellFormula(\"Sheet1\", \"B3\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, formula)\n\t}\n\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(`<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><sheetData><row r=\"2\"><c r=\"B2\"><f t=\"shared\" si=\"0\"></f></c></row></sheetData></worksheet>`))\n\tformula, err := f.GetCellFormula(\"Sheet1\", \"B2\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, formula)\n\n\t// Test get array formula with invalid cell range reference\n\tf = NewFile()\n\tassert.NoError(t, f.AddChartSheet(\"Chart1\", &Chart{Type: Line}))\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tformulaType, ref := STCellFormulaTypeArray, \"B1:B2\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet2\", \"B1\", \"A1:B2\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet3.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetData.Row[0].C[1].F.Ref = \":\"\n\t_, err = f.getCellFormula(\"Sheet2\", \"A1\", true)\n\tassert.Equal(t, newCellNameToCoordinatesError(\"\", newInvalidCellNameError(\"\")), err)\n\n\t// Test set formula for the cells in array formula range with unsupported charset\n\tf = NewFile()\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.setArrayFormulaCells(), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test get shared formula after updated refer cell formula, the shared\n\t// formula cell reference range covered the previous.\n\tf = NewFile()\n\tformulaType, ref = STCellFormulaTypeShared, \"C2:C6\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C2\", \"A2+B2\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"C2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A2+B2\", formula)\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"C6\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A6+B6\", formula)\n\n\tformulaType, ref = STCellFormulaTypeShared, \"C2:C8\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C2\", \"A2*B2\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"C2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A2*B2\", formula)\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"C8\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A8*B8\", formula)\n\tassert.NoError(t, f.Close())\n\n\t// Test get shared formula after updated refer cell formula, the shared\n\t// formula cell reference range not over the previous.\n\tf = NewFile()\n\tformulaType, ref = STCellFormulaTypeShared, \"C2:C6\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C2\", \"A2+B2\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"C2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A2+B2\", formula)\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"C6\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A6+B6\", formula)\n\n\tformulaType, ref = STCellFormulaTypeShared, \"C2:C4\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C2\", \"A2*B2\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"C2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A2*B2\", formula)\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"C6\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, formula)\n\n\t// Test get shared formula after remove refer cell formula\n\tf = NewFile()\n\tformulaType, ref = STCellFormulaTypeShared, \"C2:C6\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C2\", \"A2+B2\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C2\", \"\"))\n\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"C2\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, formula)\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"C6\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, formula)\n\n\tformulaType, ref = STCellFormulaTypeShared, \"C2:C8\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C2\", \"A2*B2\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"C2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A2*B2\", formula)\n\tformula, err = f.GetCellFormula(\"Sheet1\", \"C8\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A8*B8\", formula)\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestConvertSharedFormula(t *testing.T) {\n\tc := xlsxC{R: \"A\"}\n\t_, err := c.convertSharedFormula(\"A\")\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), err)\n\t_, err = c.convertSharedFormula(\"A1\")\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), err)\n}\n\nfunc ExampleFile_SetCellFloat() {\n\tf := NewFile()\n\tdefer func() {\n\t\tif err := f.Close(); err != nil {\n\t\t\tfmt.Println(err)\n\t\t}\n\t}()\n\tx := 3.14159265\n\tif err := f.SetCellFloat(\"Sheet1\", \"A1\", x, 2, 64); err != nil {\n\t\tfmt.Println(err)\n\t}\n\tval, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tfmt.Println(val)\n\t// Output: 3.14\n}\n\nfunc BenchmarkSetCellValue(b *testing.B) {\n\tvalues := []string{\"First\", \"Second\", \"Third\", \"Fourth\", \"Fifth\", \"Sixth\"}\n\tcols := []string{\"A\", \"B\", \"C\", \"D\", \"E\", \"F\"}\n\tf := NewFile()\n\tb.ResetTimer()\n\tfor i := 1; i <= b.N; i++ {\n\t\tfor j := 0; j < len(values); j++ {\n\t\t\tif err := f.SetCellValue(\"Sheet1\", cols[j]+strconv.Itoa(i), values[j]); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestOverflowNumericCell(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"OverflowNumericCell.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tval, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\t// GOARCH=amd64 - all ok; GOARCH=386 - actual: \"-2147483648\"\n\tassert.Equal(t, \"8595602512225\", val, \"A1 should be 8595602512225\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestSetCellFormula(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B19\", \"SUM(Sheet2!D2,Sheet2!D11)\"))\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C19\", \"SUM(Sheet2!D2,Sheet2!D9)\"))\n\n\t// Test set cell formula with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.SetCellFormula(\"Sheet:1\", \"A1\", \"SUM(1,2)\"))\n\n\t// Test set cell formula with illegal rows number\n\tassert.Equal(t, newCellNameToCoordinatesError(\"C\", newInvalidCellNameError(\"C\")), f.SetCellFormula(\"Sheet1\", \"C\", \"SUM(Sheet2!D2,Sheet2!D9)\"))\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellFormula1.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\tf, err = OpenFile(filepath.Join(\"test\", \"CalcChain.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\t// Test remove cell formula\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", \"\"))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellFormula2.xlsx\")))\n\t// Test remove all cell formula\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"B1\", \"\"))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellFormula3.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\t// Test set shared formula for the cells\n\tf = NewFile()\n\tfor r := 1; r <= 5; r++ {\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", fmt.Sprintf(\"A%d\", r), &[]interface{}{r, r + 1}))\n\t}\n\tformulaType, ref := STCellFormulaTypeShared, \"C1:C5\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"A1+B1\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\tsharedFormulaSpreadsheet := filepath.Join(\"test\", \"TestSetCellFormula4.xlsx\")\n\tassert.NoError(t, f.SaveAs(sharedFormulaSpreadsheet))\n\n\tf, err = OpenFile(sharedFormulaSpreadsheet)\n\tassert.NoError(t, err)\n\tref = \"D1:D5\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"D1\", \"A1+C1\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\tref = \"\"\n\tassert.Equal(t, ErrParameterInvalid, f.SetCellFormula(\"Sheet1\", \"D1\", \"A1+C1\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellFormula5.xlsx\")))\n\n\t// Test set table formula for the cells\n\tf = NewFile()\n\tfor idx, row := range [][]interface{}{{\"A\", \"B\", \"C\"}, {1, 2}} {\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", fmt.Sprintf(\"A%d\", idx+1), &row))\n\t}\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{Range: \"A1:C2\", Name: \"Table1\", StyleName: \"TableStyleMedium2\"}))\n\tformulaType = STCellFormulaTypeDataTable\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C2\", \"SUM(Table1[[A]:[B]])\", FormulaOpts{Type: &formulaType}))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellFormula6.xlsx\")))\n\n\t// Test set array formula with invalid cell range reference\n\tformulaType, ref = STCellFormulaTypeArray, \":\"\n\tassert.Equal(t, newCellNameToCoordinatesError(\"\", newInvalidCellNameError(\"\")), f.SetCellFormula(\"Sheet1\", \"B1\", \"A1:A2\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n\n\t// Test set array formula with invalid cell reference\n\tformulaType, ref = STCellFormulaTypeArray, \"A1:A2\"\n\tassert.Equal(t, ErrColumnNumber, f.SetCellFormula(\"Sheet1\", \"A1\", \"SUM(XFE1:XFE2)\", FormulaOpts{Ref: &ref, Type: &formulaType}))\n}\n\nfunc TestGetCellRichText(t *testing.T) {\n\tf, theme := NewFile(), 1\n\n\trunsSource := []RichTextRun{\n\t\t{\n\t\t\tText: \"a\\n\",\n\t\t},\n\t\t{\n\t\t\tText: \"b\",\n\t\t\tFont: &Font{\n\t\t\t\tUnderline:  \"single\",\n\t\t\t\tColor:      \"ff0000\",\n\t\t\t\tColorTheme: &theme,\n\t\t\t\tColorTint:  0.5,\n\t\t\t\tBold:       true,\n\t\t\t\tItalic:     true,\n\t\t\t\tFamily:     \"Times New Roman\",\n\t\t\t\tSize:       100,\n\t\t\t\tStrike:     true,\n\t\t\t},\n\t\t},\n\t}\n\tassert.NoError(t, f.SetCellRichText(\"Sheet1\", \"A1\", runsSource))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A2\", false))\n\n\truns, err := f.GetCellRichText(\"Sheet1\", \"A2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []RichTextRun(nil), runs)\n\n\truns, err = f.GetCellRichText(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\n\tassert.Equal(t, runsSource[0].Text, runs[0].Text)\n\tassert.Nil(t, runs[0].Font)\n\tassert.NotNil(t, runs[1].Font)\n\n\trunsSource[1].Font.Color = strings.ToUpper(runsSource[1].Font.Color)\n\tassert.True(t, reflect.DeepEqual(runsSource[1].Font, runs[1].Font), \"should get the same font\")\n\n\t// Test get cell rich text with inlineStr\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetData.Row[0].C[0] = xlsxC{\n\t\tT: \"inlineStr\",\n\t\tIS: &xlsxSI{\n\t\t\tT: &xlsxT{Val: \"A\"},\n\t\t\tR: []xlsxR{{T: &xlsxT{Val: \"1\"}}},\n\t\t},\n\t}\n\truns, err = f.GetCellRichText(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []RichTextRun{{Text: \"A\"}, {Text: \"1\"}}, runs)\n\n\t// Test get cell rich text when string item index overflow\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetData.Row[0].C[0] = xlsxC{V: \"2\", IS: &xlsxSI{}}\n\truns, err = f.GetCellRichText(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, 0, len(runs))\n\t// Test get cell rich text when string item index is negative\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetData.Row[0].C[0] = xlsxC{T: \"s\", V: \"-1\", IS: &xlsxSI{}}\n\truns, err = f.GetCellRichText(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, 0, len(runs))\n\t// Test get cell rich text when string item index is invalid\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetData.Row[0].C[0] = xlsxC{T: \"s\", V: \"A\", IS: &xlsxSI{}}\n\truns, err = f.GetCellRichText(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, \"strconv.Atoi: parsing \\\"A\\\": invalid syntax\")\n\tassert.Equal(t, 0, len(runs))\n\t// Test get cell rich text on invalid string item index\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetData.Row[0].C[0] = xlsxC{V: \"x\"}\n\truns, err = f.GetCellRichText(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, 0, len(runs))\n\t// Test set cell rich text on not exists worksheet\n\t_, err = f.GetCellRichText(\"SheetN\", \"A1\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test set cell rich text with illegal cell reference\n\t_, err = f.GetCellRichText(\"Sheet1\", \"A\")\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), err)\n\t// Test set rich text color theme without tint\n\tassert.NoError(t, f.SetCellRichText(\"Sheet1\", \"A1\", []RichTextRun{{Font: &Font{ColorTheme: &theme}}}))\n\t// Test set rich text color tint without theme\n\tassert.NoError(t, f.SetCellRichText(\"Sheet1\", \"A1\", []RichTextRun{{Font: &Font{ColorTint: 0.5}}}))\n\n\t// Test set cell rich text with unsupported charset shared strings table\n\tf.SharedStrings = nil\n\tf.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetCellRichText(\"Sheet1\", \"A1\", runsSource), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get cell rich text with unsupported charset shared strings table\n\tf.SharedStrings = nil\n\tf.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)\n\t_, err = f.GetCellRichText(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get cell rich text with invalid sheet name\n\t_, err = f.GetCellRichText(\"Sheet:1\", \"A1\")\n\tassert.Equal(t, ErrSheetNameInvalid, err)\n}\n\nfunc TestSetCellRichText(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetRowHeight(\"Sheet1\", 1, 35))\n\trichTextRun := []RichTextRun{\n\t\t{\n\t\t\tText: \"bold\",\n\t\t\tFont: &Font{\n\t\t\t\tBold:         true,\n\t\t\t\tColor:        \"2354E8\",\n\t\t\t\tColorIndexed: 0,\n\t\t\t\tFamily:       \"Times New Roman\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tText: \" and \",\n\t\t\tFont: &Font{\n\t\t\t\tFamily: \"Times New Roman\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tText: \"italic \",\n\t\t\tFont: &Font{\n\t\t\t\tBold:   true,\n\t\t\t\tColor:  \"E83723\",\n\t\t\t\tItalic: true,\n\t\t\t\tFamily: \"Times New Roman\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tText: \"text with color and font-family, \",\n\t\t\tFont: &Font{\n\t\t\t\tBold:   true,\n\t\t\t\tColor:  \"2354E8\",\n\t\t\t\tFamily: \"Times New Roman\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tText: \"\\r\\nlarge text with \",\n\t\t\tFont: &Font{\n\t\t\t\tSize:  14,\n\t\t\t\tColor: \"AD23E8\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tText: \"strike\",\n\t\t\tFont: &Font{\n\t\t\t\tColor:  \"E89923\",\n\t\t\t\tStrike: true,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tText: \" superscript\",\n\t\t\tFont: &Font{\n\t\t\t\tColor:     \"DBC21F\",\n\t\t\t\tVertAlign: \"superscript\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tText: \" and \",\n\t\t\tFont: &Font{\n\t\t\t\tSize:      14,\n\t\t\t\tColor:     \"AD23E8\",\n\t\t\t\tVertAlign: \"baseline\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tText: \"underline\",\n\t\t\tFont: &Font{\n\t\t\t\tColor:     \"23E833\",\n\t\t\t\tUnderline: \"single\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tText: \" subscript.\",\n\t\t\tFont: &Font{\n\t\t\t\tColor:     \"017505\",\n\t\t\t\tVertAlign: \"subscript\",\n\t\t\t},\n\t\t},\n\t}\n\tassert.NoError(t, f.SetCellRichText(\"Sheet1\", \"A1\", richTextRun))\n\tassert.NoError(t, f.SetCellRichText(\"Sheet1\", \"A2\", richTextRun))\n\tassert.NoError(t, f.SetCellRichText(\"Sheet1\", \"A3\", []RichTextRun{{Text: strings.Repeat(\"\\u4e00\", TotalCellChars)}}))\n\tstyle, err := f.NewStyle(&Style{\n\t\tAlignment: &Alignment{\n\t\t\tWrapText: true,\n\t\t\tVertical: \"center\",\n\t\t},\n\t})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A1\", \"A1\", style))\n\tassert.NoError(t, f.AutoFitColWidth(\"Sheet1\", \"A\"))\n\n\truns, err := f.GetCellRichText(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, richTextRun, runs)\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellRichText.xlsx\")))\n\t// Test set cell rich text on not exists worksheet\n\tassert.EqualError(t, f.SetCellRichText(\"SheetN\", \"A1\", richTextRun), \"sheet SheetN does not exist\")\n\t// Test set cell rich text with invalid sheet name\n\tassert.EqualError(t, f.SetCellRichText(\"Sheet:1\", \"A1\", richTextRun), ErrSheetNameInvalid.Error())\n\t// Test set cell rich text with illegal cell reference\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.SetCellRichText(\"Sheet1\", \"A\", richTextRun))\n\trichTextRun = []RichTextRun{{Text: strings.Repeat(\"s\", TotalCellChars+1)}}\n\t// Test set cell rich text with characters over the maximum limit\n\tassert.EqualError(t, f.SetCellRichText(\"Sheet1\", \"A1\", richTextRun), ErrCellCharsLength.Error())\n}\n\nfunc TestFormattedValue(t *testing.T) {\n\tf := NewFile()\n\tresult, err := f.formattedValue(&xlsxC{S: 0, V: \"43528\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"43528\", result)\n\n\t// S is too large\n\tresult, err = f.formattedValue(&xlsxC{S: 15, V: \"43528\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"43528\", result)\n\n\t// S is too small\n\tresult, err = f.formattedValue(&xlsxC{S: -15, V: \"43528\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"43528\", result)\n\n\tresult, err = f.formattedValue(&xlsxC{S: 1, V: \"43528\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"43528\", result)\n\tcustomNumFmt := \"[$-409]MM/DD/YYYY\"\n\t_, err = f.NewStyle(&Style{\n\t\tCustomNumFmt: &customNumFmt,\n\t})\n\tassert.NoError(t, err)\n\tresult, err = f.formattedValue(&xlsxC{S: 1, V: \"43528\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"03/04/2019\", result)\n\n\t// Test format value with no built-in number format ID\n\tnumFmtID := 5\n\tf.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{\n\t\tNumFmtID: &numFmtID,\n\t})\n\tresult, err = f.formattedValue(&xlsxC{S: 2, V: \"43528\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"43528\", result)\n\n\t// Test format value with invalid number format ID\n\tf.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{\n\t\tNumFmtID: nil,\n\t})\n\tresult, err = f.formattedValue(&xlsxC{S: 3, V: \"43528\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"43528\", result)\n\n\t// Test format value with empty number format\n\tf.Styles.NumFmts = nil\n\tf.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{\n\t\tNumFmtID: &numFmtID,\n\t})\n\tresult, err = f.formattedValue(&xlsxC{S: 1, V: \"43528\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"43528\", result)\n\n\t// Test format numeric value with shared string data type\n\tf.Styles.NumFmts, numFmtID = nil, 11\n\tf.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{\n\t\tNumFmtID: &numFmtID,\n\t})\n\tresult, err = f.formattedValue(&xlsxC{S: 5, V: \"43528\"}, false, CellTypeSharedString)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"43528\", result)\n\n\t// Test format decimal value with build-in number format ID\n\tstyleID, err := f.NewStyle(&Style{\n\t\tNumFmt: 1,\n\t})\n\tassert.NoError(t, err)\n\tresult, err = f.formattedValue(&xlsxC{S: styleID, V: \"310.56\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"311\", result)\n\n\tassert.Equal(t, \"0_0\", format(\"0_0\", \"\", false, CellTypeNumber, nil))\n\n\t// Test format value with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\t_, err = f.formattedValue(&xlsxC{S: 1, V: \"43528\"}, false, CellTypeNumber)\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test format value with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\t_, err = f.formattedValue(&xlsxC{S: 1, V: \"43528\"}, false, CellTypeNumber)\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\n\tassert.Equal(t, \"text\", format(\"text\", \"0\", false, CellTypeNumber, nil))\n}\n\nfunc TestFormattedValueNilXfs(t *testing.T) {\n\t// Set the CellXfs to nil and verify that the formattedValue function does not crash\n\tf := NewFile()\n\tf.Styles.CellXfs = nil\n\tresult, err := f.formattedValue(&xlsxC{S: 3, V: \"43528\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"43528\", result)\n}\n\nfunc TestFormattedValueNilNumFmts(t *testing.T) {\n\t// Set the NumFmts value to nil and verify that the formattedValue function does not crash\n\tf := NewFile()\n\tf.Styles.NumFmts = nil\n\tresult, err := f.formattedValue(&xlsxC{S: 3, V: \"43528\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"43528\", result)\n}\n\nfunc TestFormattedValueNilWorkbook(t *testing.T) {\n\t// Set the Workbook value to nil and verify that the formattedValue function does not crash\n\tf := NewFile()\n\tf.WorkBook = nil\n\tresult, err := f.formattedValue(&xlsxC{S: 3, V: \"43528\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"43528\", result)\n}\n\nfunc TestFormattedValueNilWorkbookPr(t *testing.T) {\n\t// Set the WorkBook.WorkbookPr value to nil and verify that the formattedValue function does not\n\t// crash.\n\tf := NewFile()\n\tf.WorkBook.WorkbookPr = nil\n\tresult, err := f.formattedValue(&xlsxC{S: 3, V: \"43528\"}, false, CellTypeNumber)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"43528\", result)\n}\n\nfunc TestGetCustomNumFmtCode(t *testing.T) {\n\texpected := \"[$-ja-JP-x-gannen,80]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\"\n\tstyleSheet := &xlsxStyleSheet{NumFmts: &xlsxNumFmts{NumFmt: []*xlsxNumFmt{\n\t\t{NumFmtID: 164, FormatCode16: expected},\n\t}}}\n\tnumFmtCode, ok := styleSheet.getCustomNumFmtCode(164)\n\tassert.Equal(t, expected, numFmtCode)\n\tassert.True(t, ok)\n}\n\nfunc TestSharedStringsError(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"), Options{UnzipXMLSizeLimit: 128})\n\tassert.NoError(t, err)\n\ttempFile, ok := f.tempFiles.Load(defaultXMLPathSharedStrings)\n\tassert.True(t, ok)\n\tf.tempFiles.Store(defaultXMLPathSharedStrings, \"\")\n\tassert.Equal(t, \"1\", f.getFromStringItem(1))\n\t// Test get from string item with invalid offset range\n\tf.sharedStringItem = [][]uint{{0}}\n\tassert.Equal(t, \"0\", f.getFromStringItem(0))\n\t// Cleanup undelete temporary files\n\tassert.NoError(t, os.Remove(tempFile.(string)))\n\t// Test reload the file error on set cell value and rich text. The error message was different between macOS and Windows\n\terr = f.SetCellValue(\"Sheet1\", \"A19\", \"A19\")\n\tassert.Error(t, err)\n\n\tf.tempFiles.Store(defaultXMLPathSharedStrings, \"\")\n\terr = f.SetCellRichText(\"Sheet1\", \"A19\", []RichTextRun{})\n\tassert.Error(t, err)\n\tassert.NoError(t, f.Close())\n\n\tf, err = OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"), Options{UnzipXMLSizeLimit: 128})\n\tassert.NoError(t, err)\n\trows, err := f.Rows(\"Sheet1\")\n\tassert.NoError(t, err)\n\tconst maxUint16 = 1<<16 - 1\n\tcurrentRow := 0\n\tfor rows.Next() {\n\t\tcurrentRow++\n\t\tif currentRow == 19 {\n\t\t\t_, err := rows.Columns()\n\t\t\tassert.NoError(t, err)\n\t\t\t// Test get cell value from string item with invalid offset\n\t\t\tf.sharedStringItem[1] = []uint{maxUint16 - 1, maxUint16}\n\t\t\tassert.Equal(t, \"1\", f.getFromStringItem(1))\n\t\t\tbreak\n\t\t}\n\t}\n\tassert.NoError(t, rows.Close())\n\t// Test shared string item temporary files has been closed before close the workbook\n\tassert.NoError(t, f.sharedStringTemp.Close())\n\tassert.Error(t, f.Close())\n\t// Cleanup undelete temporary files\n\tf.tempFiles.Range(func(k, v interface{}) bool {\n\t\treturn assert.NoError(t, os.Remove(v.(string)))\n\t})\n\n\tf, err = OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"), Options{UnzipXMLSizeLimit: 128})\n\tassert.NoError(t, err)\n\trows, err = f.Rows(\"Sheet1\")\n\tassert.NoError(t, err)\n\tcurrentRow = 0\n\tfor rows.Next() {\n\t\tcurrentRow++\n\t\tif currentRow == 19 {\n\t\t\t_, err := rows.Columns()\n\t\t\tassert.NoError(t, err)\n\t\t\tbreak\n\t\t}\n\t}\n\tassert.NoError(t, rows.Close())\n\tassert.NoError(t, f.sharedStringTemp.Close())\n\t// Test shared string item temporary files has been closed before set the cell value\n\tassert.Error(t, f.SetCellValue(\"Sheet1\", \"A1\", \"A1\"))\n\tassert.Error(t, f.Close())\n\t// Cleanup undelete temporary files\n\tf.tempFiles.Range(func(k, v interface{}) bool {\n\t\treturn assert.NoError(t, os.Remove(v.(string)))\n\t})\n}\n\nfunc TestSetCellIntFunc(t *testing.T) {\n\tcases := []struct {\n\t\tval    interface{}\n\t\ttarget string\n\t}{\n\t\t{val: 128, target: \"128\"},\n\t\t{val: int8(-128), target: \"-128\"},\n\t\t{val: int16(-32768), target: \"-32768\"},\n\t\t{val: int32(-2147483648), target: \"-2147483648\"},\n\t\t{val: int64(-9223372036854775808), target: \"-9223372036854775808\"},\n\t\t{val: uint(128), target: \"128\"},\n\t\t{val: uint8(255), target: \"255\"},\n\t\t{val: uint16(65535), target: \"65535\"},\n\t\t{val: uint32(4294967295), target: \"4294967295\"},\n\t\t{val: uint64(18446744073709551615), target: \"18446744073709551615\"},\n\t}\n\tfor _, c := range cases {\n\t\tcell := &xlsxC{}\n\t\tsetCellIntFunc(cell, c.val)\n\t\tassert.Equal(t, c.target, cell.V)\n\t}\n}\n\nfunc TestSIString(t *testing.T) {\n\tassert.Empty(t, xlsxSI{}.String())\n}\n\nfunc TestGetCellStringFunc(t *testing.T) {\n\tf := NewFile()\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetData.Row = []xlsxRow{{R: 2}}\n\tval, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.Empty(t, val)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Close())\n}\n"
  },
  {
    "path": "chart.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// ChartType is the type of supported chart types.\ntype ChartType byte\n\n// This section defines the currently supported chart types enumeration.\nconst (\n\tArea ChartType = iota\n\tAreaStacked\n\tAreaPercentStacked\n\tArea3D\n\tArea3DStacked\n\tArea3DPercentStacked\n\tBar\n\tBarStacked\n\tBarPercentStacked\n\tBar3DClustered\n\tBar3DStacked\n\tBar3DPercentStacked\n\tBar3DConeClustered\n\tBar3DConeStacked\n\tBar3DConePercentStacked\n\tBar3DPyramidClustered\n\tBar3DPyramidStacked\n\tBar3DPyramidPercentStacked\n\tBar3DCylinderClustered\n\tBar3DCylinderStacked\n\tBar3DCylinderPercentStacked\n\tCol\n\tColStacked\n\tColPercentStacked\n\tCol3D\n\tCol3DClustered\n\tCol3DStacked\n\tCol3DPercentStacked\n\tCol3DCone\n\tCol3DConeClustered\n\tCol3DConeStacked\n\tCol3DConePercentStacked\n\tCol3DPyramid\n\tCol3DPyramidClustered\n\tCol3DPyramidStacked\n\tCol3DPyramidPercentStacked\n\tCol3DCylinder\n\tCol3DCylinderClustered\n\tCol3DCylinderStacked\n\tCol3DCylinderPercentStacked\n\tDoughnut\n\tLine\n\tLine3D\n\tPie\n\tPie3D\n\tPieOfPie\n\tBarOfPie\n\tRadar\n\tScatter\n\tSurface3D\n\tWireframeSurface3D\n\tContour\n\tWireframeContour\n\tBubble\n\tBubble3D\n\tStockHighLowClose\n\tStockOpenHighLowClose\n)\n\n// ChartDashType is the type of supported chart dash types.\ntype ChartDashType byte\n\n// This section defines the currently supported chart dash types enumeration.\nconst (\n\tChartDashUnset ChartDashType = iota\n\tChartDashSolid\n\tChartDashDot\n\tChartDashDash\n\tChartDashLgDash\n\tChartDashSashDot\n\tChartDashLgDashDot\n\tChartDashLgDashDotDot\n\tChartDashSysDash\n\tChartDashSysDot\n\tChartDashSysDashDot\n\tChartDashSysDashDotDot\n)\n\n// ChartLineType is the type of supported chart line types.\ntype ChartLineType byte\n\n// This section defines the currently supported chart line types enumeration.\nconst (\n\tChartLineUnset ChartLineType = iota\n\tChartLineSolid\n\tChartLineNone\n\tChartLineAutomatic\n)\n\n// ChartTickLabelPositionType is the type of supported chart tick label position\n// types.\ntype ChartTickLabelPositionType byte\n\n// This section defines the supported chart tick label position types\n// enumeration.\nconst (\n\tChartTickLabelNextToAxis ChartTickLabelPositionType = iota\n\tChartTickLabelHigh\n\tChartTickLabelLow\n\tChartTickLabelNone\n)\n\n// This section defines the default value of chart properties.\nvar (\n\tchartView3DRotX = map[ChartType]int{\n\t\tArea:                        0,\n\t\tAreaStacked:                 0,\n\t\tAreaPercentStacked:          0,\n\t\tArea3D:                      15,\n\t\tArea3DStacked:               15,\n\t\tArea3DPercentStacked:        15,\n\t\tBar:                         0,\n\t\tBarStacked:                  0,\n\t\tBarPercentStacked:           0,\n\t\tBar3DClustered:              15,\n\t\tBar3DStacked:                15,\n\t\tBar3DPercentStacked:         15,\n\t\tBar3DConeClustered:          15,\n\t\tBar3DConeStacked:            15,\n\t\tBar3DConePercentStacked:     15,\n\t\tBar3DPyramidClustered:       15,\n\t\tBar3DPyramidStacked:         15,\n\t\tBar3DPyramidPercentStacked:  15,\n\t\tBar3DCylinderClustered:      15,\n\t\tBar3DCylinderStacked:        15,\n\t\tBar3DCylinderPercentStacked: 15,\n\t\tCol:                         0,\n\t\tColStacked:                  0,\n\t\tColPercentStacked:           0,\n\t\tCol3D:                       15,\n\t\tCol3DClustered:              15,\n\t\tCol3DStacked:                15,\n\t\tCol3DPercentStacked:         15,\n\t\tCol3DCone:                   15,\n\t\tCol3DConeClustered:          15,\n\t\tCol3DConeStacked:            15,\n\t\tCol3DConePercentStacked:     15,\n\t\tCol3DPyramid:                15,\n\t\tCol3DPyramidClustered:       15,\n\t\tCol3DPyramidStacked:         15,\n\t\tCol3DPyramidPercentStacked:  15,\n\t\tCol3DCylinder:               15,\n\t\tCol3DCylinderClustered:      15,\n\t\tCol3DCylinderStacked:        15,\n\t\tCol3DCylinderPercentStacked: 15,\n\t\tDoughnut:                    0,\n\t\tLine:                        0,\n\t\tLine3D:                      20,\n\t\tPie:                         0,\n\t\tPie3D:                       30,\n\t\tPieOfPie:                    0,\n\t\tBarOfPie:                    0,\n\t\tRadar:                       0,\n\t\tScatter:                     0,\n\t\tSurface3D:                   15,\n\t\tWireframeSurface3D:          15,\n\t\tContour:                     90,\n\t\tWireframeContour:            90,\n\t}\n\tchartView3DRotY = map[ChartType]int{\n\t\tArea:                        0,\n\t\tAreaStacked:                 0,\n\t\tAreaPercentStacked:          0,\n\t\tArea3D:                      20,\n\t\tArea3DStacked:               20,\n\t\tArea3DPercentStacked:        20,\n\t\tBar:                         0,\n\t\tBarStacked:                  0,\n\t\tBarPercentStacked:           0,\n\t\tBar3DClustered:              20,\n\t\tBar3DStacked:                20,\n\t\tBar3DPercentStacked:         20,\n\t\tBar3DConeClustered:          20,\n\t\tBar3DConeStacked:            20,\n\t\tBar3DConePercentStacked:     20,\n\t\tBar3DPyramidClustered:       20,\n\t\tBar3DPyramidStacked:         20,\n\t\tBar3DPyramidPercentStacked:  20,\n\t\tBar3DCylinderClustered:      20,\n\t\tBar3DCylinderStacked:        20,\n\t\tBar3DCylinderPercentStacked: 20,\n\t\tCol:                         0,\n\t\tColStacked:                  0,\n\t\tColPercentStacked:           0,\n\t\tCol3D:                       20,\n\t\tCol3DClustered:              20,\n\t\tCol3DStacked:                20,\n\t\tCol3DPercentStacked:         20,\n\t\tCol3DCone:                   20,\n\t\tCol3DConeClustered:          20,\n\t\tCol3DConeStacked:            20,\n\t\tCol3DConePercentStacked:     20,\n\t\tCol3DPyramid:                20,\n\t\tCol3DPyramidClustered:       20,\n\t\tCol3DPyramidStacked:         20,\n\t\tCol3DPyramidPercentStacked:  20,\n\t\tCol3DCylinder:               20,\n\t\tCol3DCylinderClustered:      20,\n\t\tCol3DCylinderStacked:        20,\n\t\tCol3DCylinderPercentStacked: 20,\n\t\tDoughnut:                    0,\n\t\tLine:                        0,\n\t\tLine3D:                      15,\n\t\tPie:                         0,\n\t\tPie3D:                       0,\n\t\tPieOfPie:                    0,\n\t\tBarOfPie:                    0,\n\t\tRadar:                       0,\n\t\tScatter:                     0,\n\t\tSurface3D:                   20,\n\t\tWireframeSurface3D:          20,\n\t\tContour:                     0,\n\t\tWireframeContour:            0,\n\t}\n\tplotAreaChartOverlap = map[ChartType]int{\n\t\tBarStacked:        100,\n\t\tBarPercentStacked: 100,\n\t\tColStacked:        100,\n\t\tColPercentStacked: 100,\n\t}\n\tchartView3DPerspective = map[ChartType]int{\n\t\tLine3D:           30,\n\t\tContour:          0,\n\t\tWireframeContour: 0,\n\t}\n\tchartView3DRAngAx = map[ChartType]int{\n\t\tArea:                        0,\n\t\tAreaStacked:                 0,\n\t\tAreaPercentStacked:          0,\n\t\tArea3D:                      1,\n\t\tArea3DStacked:               1,\n\t\tArea3DPercentStacked:        1,\n\t\tBar:                         0,\n\t\tBarStacked:                  0,\n\t\tBarPercentStacked:           0,\n\t\tBar3DClustered:              1,\n\t\tBar3DStacked:                1,\n\t\tBar3DPercentStacked:         1,\n\t\tBar3DConeClustered:          1,\n\t\tBar3DConeStacked:            1,\n\t\tBar3DConePercentStacked:     1,\n\t\tBar3DPyramidClustered:       1,\n\t\tBar3DPyramidStacked:         1,\n\t\tBar3DPyramidPercentStacked:  1,\n\t\tBar3DCylinderClustered:      1,\n\t\tBar3DCylinderStacked:        1,\n\t\tBar3DCylinderPercentStacked: 1,\n\t\tCol:                         0,\n\t\tColStacked:                  0,\n\t\tColPercentStacked:           0,\n\t\tCol3D:                       1,\n\t\tCol3DClustered:              1,\n\t\tCol3DStacked:                1,\n\t\tCol3DPercentStacked:         1,\n\t\tCol3DCone:                   1,\n\t\tCol3DConeClustered:          1,\n\t\tCol3DConeStacked:            1,\n\t\tCol3DConePercentStacked:     1,\n\t\tCol3DPyramid:                1,\n\t\tCol3DPyramidClustered:       1,\n\t\tCol3DPyramidStacked:         1,\n\t\tCol3DPyramidPercentStacked:  1,\n\t\tCol3DCylinder:               1,\n\t\tCol3DCylinderClustered:      1,\n\t\tCol3DCylinderStacked:        1,\n\t\tCol3DCylinderPercentStacked: 1,\n\t\tDoughnut:                    0,\n\t\tLine:                        0,\n\t\tLine3D:                      0,\n\t\tPie:                         0,\n\t\tPie3D:                       0,\n\t\tPieOfPie:                    0,\n\t\tBarOfPie:                    0,\n\t\tRadar:                       0,\n\t\tScatter:                     0,\n\t\tSurface3D:                   0,\n\t\tWireframeSurface3D:          0,\n\t\tContour:                     0,\n\t\tBubble:                      0,\n\t\tBubble3D:                    0,\n\t}\n\tchartLegendPosition = map[string]string{\n\t\t\"bottom\":    \"b\",\n\t\t\"left\":      \"l\",\n\t\t\"right\":     \"r\",\n\t\t\"top\":       \"t\",\n\t\t\"top_right\": \"tr\",\n\t}\n\tchartValAxNumFmtFormatCode = map[ChartType]string{\n\t\tArea:                        \"General\",\n\t\tAreaStacked:                 \"General\",\n\t\tAreaPercentStacked:          \"0%\",\n\t\tArea3D:                      \"General\",\n\t\tArea3DStacked:               \"General\",\n\t\tArea3DPercentStacked:        \"0%\",\n\t\tBar:                         \"General\",\n\t\tBarStacked:                  \"General\",\n\t\tBarPercentStacked:           \"0%\",\n\t\tBar3DClustered:              \"General\",\n\t\tBar3DStacked:                \"General\",\n\t\tBar3DPercentStacked:         \"0%\",\n\t\tBar3DConeClustered:          \"General\",\n\t\tBar3DConeStacked:            \"General\",\n\t\tBar3DConePercentStacked:     \"0%\",\n\t\tBar3DPyramidClustered:       \"General\",\n\t\tBar3DPyramidStacked:         \"General\",\n\t\tBar3DPyramidPercentStacked:  \"0%\",\n\t\tBar3DCylinderClustered:      \"General\",\n\t\tBar3DCylinderStacked:        \"General\",\n\t\tBar3DCylinderPercentStacked: \"0%\",\n\t\tCol:                         \"General\",\n\t\tColStacked:                  \"General\",\n\t\tColPercentStacked:           \"0%\",\n\t\tCol3D:                       \"General\",\n\t\tCol3DClustered:              \"General\",\n\t\tCol3DStacked:                \"General\",\n\t\tCol3DPercentStacked:         \"0%\",\n\t\tCol3DCone:                   \"General\",\n\t\tCol3DConeClustered:          \"General\",\n\t\tCol3DConeStacked:            \"General\",\n\t\tCol3DConePercentStacked:     \"0%\",\n\t\tCol3DPyramid:                \"General\",\n\t\tCol3DPyramidClustered:       \"General\",\n\t\tCol3DPyramidStacked:         \"General\",\n\t\tCol3DPyramidPercentStacked:  \"0%\",\n\t\tCol3DCylinder:               \"General\",\n\t\tCol3DCylinderClustered:      \"General\",\n\t\tCol3DCylinderStacked:        \"General\",\n\t\tCol3DCylinderPercentStacked: \"0%\",\n\t\tDoughnut:                    \"General\",\n\t\tLine:                        \"General\",\n\t\tLine3D:                      \"General\",\n\t\tPie:                         \"General\",\n\t\tPie3D:                       \"General\",\n\t\tPieOfPie:                    \"General\",\n\t\tBarOfPie:                    \"General\",\n\t\tRadar:                       \"General\",\n\t\tScatter:                     \"General\",\n\t\tSurface3D:                   \"General\",\n\t\tWireframeSurface3D:          \"General\",\n\t\tContour:                     \"General\",\n\t\tWireframeContour:            \"General\",\n\t\tBubble:                      \"General\",\n\t\tBubble3D:                    \"General\",\n\t\tStockHighLowClose:           \"General\",\n\t\tStockOpenHighLowClose:       \"General\",\n\t}\n\tchartValAxCrossBetween = map[ChartType]string{\n\t\tArea:                        \"midCat\",\n\t\tAreaStacked:                 \"midCat\",\n\t\tAreaPercentStacked:          \"midCat\",\n\t\tArea3D:                      \"midCat\",\n\t\tArea3DStacked:               \"midCat\",\n\t\tArea3DPercentStacked:        \"midCat\",\n\t\tBar:                         \"between\",\n\t\tBarStacked:                  \"between\",\n\t\tBarPercentStacked:           \"between\",\n\t\tBar3DClustered:              \"between\",\n\t\tBar3DStacked:                \"between\",\n\t\tBar3DPercentStacked:         \"between\",\n\t\tBar3DConeClustered:          \"between\",\n\t\tBar3DConeStacked:            \"between\",\n\t\tBar3DConePercentStacked:     \"between\",\n\t\tBar3DPyramidClustered:       \"between\",\n\t\tBar3DPyramidStacked:         \"between\",\n\t\tBar3DPyramidPercentStacked:  \"between\",\n\t\tBar3DCylinderClustered:      \"between\",\n\t\tBar3DCylinderStacked:        \"between\",\n\t\tBar3DCylinderPercentStacked: \"between\",\n\t\tCol:                         \"between\",\n\t\tColStacked:                  \"between\",\n\t\tColPercentStacked:           \"between\",\n\t\tCol3D:                       \"between\",\n\t\tCol3DClustered:              \"between\",\n\t\tCol3DStacked:                \"between\",\n\t\tCol3DPercentStacked:         \"between\",\n\t\tCol3DCone:                   \"between\",\n\t\tCol3DConeClustered:          \"between\",\n\t\tCol3DConeStacked:            \"between\",\n\t\tCol3DConePercentStacked:     \"between\",\n\t\tCol3DPyramid:                \"between\",\n\t\tCol3DPyramidClustered:       \"between\",\n\t\tCol3DPyramidStacked:         \"between\",\n\t\tCol3DPyramidPercentStacked:  \"between\",\n\t\tCol3DCylinder:               \"between\",\n\t\tCol3DCylinderClustered:      \"between\",\n\t\tCol3DCylinderStacked:        \"between\",\n\t\tCol3DCylinderPercentStacked: \"between\",\n\t\tDoughnut:                    \"between\",\n\t\tLine:                        \"between\",\n\t\tLine3D:                      \"between\",\n\t\tPie:                         \"between\",\n\t\tPie3D:                       \"between\",\n\t\tPieOfPie:                    \"between\",\n\t\tBarOfPie:                    \"between\",\n\t\tRadar:                       \"between\",\n\t\tScatter:                     \"between\",\n\t\tSurface3D:                   \"midCat\",\n\t\tWireframeSurface3D:          \"midCat\",\n\t\tContour:                     \"midCat\",\n\t\tWireframeContour:            \"midCat\",\n\t\tBubble:                      \"midCat\",\n\t\tBubble3D:                    \"midCat\",\n\t\tStockHighLowClose:           \"between\",\n\t\tStockOpenHighLowClose:       \"between\",\n\t}\n\tplotAreaChartGrouping = map[ChartType]string{\n\t\tArea:                        \"standard\",\n\t\tAreaStacked:                 \"stacked\",\n\t\tAreaPercentStacked:          \"percentStacked\",\n\t\tArea3D:                      \"standard\",\n\t\tArea3DStacked:               \"stacked\",\n\t\tArea3DPercentStacked:        \"percentStacked\",\n\t\tBar:                         \"clustered\",\n\t\tBarStacked:                  \"stacked\",\n\t\tBarPercentStacked:           \"percentStacked\",\n\t\tBar3DClustered:              \"clustered\",\n\t\tBar3DStacked:                \"stacked\",\n\t\tBar3DPercentStacked:         \"percentStacked\",\n\t\tBar3DConeClustered:          \"clustered\",\n\t\tBar3DConeStacked:            \"stacked\",\n\t\tBar3DConePercentStacked:     \"percentStacked\",\n\t\tBar3DPyramidClustered:       \"clustered\",\n\t\tBar3DPyramidStacked:         \"stacked\",\n\t\tBar3DPyramidPercentStacked:  \"percentStacked\",\n\t\tBar3DCylinderClustered:      \"clustered\",\n\t\tBar3DCylinderStacked:        \"stacked\",\n\t\tBar3DCylinderPercentStacked: \"percentStacked\",\n\t\tCol:                         \"clustered\",\n\t\tColStacked:                  \"stacked\",\n\t\tColPercentStacked:           \"percentStacked\",\n\t\tCol3D:                       \"standard\",\n\t\tCol3DClustered:              \"clustered\",\n\t\tCol3DStacked:                \"stacked\",\n\t\tCol3DPercentStacked:         \"percentStacked\",\n\t\tCol3DCone:                   \"standard\",\n\t\tCol3DConeClustered:          \"clustered\",\n\t\tCol3DConeStacked:            \"stacked\",\n\t\tCol3DConePercentStacked:     \"percentStacked\",\n\t\tCol3DPyramid:                \"standard\",\n\t\tCol3DPyramidClustered:       \"clustered\",\n\t\tCol3DPyramidStacked:         \"stacked\",\n\t\tCol3DPyramidPercentStacked:  \"percentStacked\",\n\t\tCol3DCylinder:               \"standard\",\n\t\tCol3DCylinderClustered:      \"clustered\",\n\t\tCol3DCylinderStacked:        \"stacked\",\n\t\tCol3DCylinderPercentStacked: \"percentStacked\",\n\t\tLine:                        \"standard\",\n\t\tLine3D:                      \"standard\",\n\t}\n\tplotAreaChartBarDir = map[ChartType]string{\n\t\tBar:                         \"bar\",\n\t\tBarStacked:                  \"bar\",\n\t\tBarPercentStacked:           \"bar\",\n\t\tBar3DClustered:              \"bar\",\n\t\tBar3DStacked:                \"bar\",\n\t\tBar3DPercentStacked:         \"bar\",\n\t\tBar3DConeClustered:          \"bar\",\n\t\tBar3DConeStacked:            \"bar\",\n\t\tBar3DConePercentStacked:     \"bar\",\n\t\tBar3DPyramidClustered:       \"bar\",\n\t\tBar3DPyramidStacked:         \"bar\",\n\t\tBar3DPyramidPercentStacked:  \"bar\",\n\t\tBar3DCylinderClustered:      \"bar\",\n\t\tBar3DCylinderStacked:        \"bar\",\n\t\tBar3DCylinderPercentStacked: \"bar\",\n\t\tCol:                         \"col\",\n\t\tColStacked:                  \"col\",\n\t\tColPercentStacked:           \"col\",\n\t\tCol3D:                       \"col\",\n\t\tCol3DClustered:              \"col\",\n\t\tCol3DStacked:                \"col\",\n\t\tCol3DPercentStacked:         \"col\",\n\t\tCol3DCone:                   \"col\",\n\t\tCol3DConeStacked:            \"col\",\n\t\tCol3DConeClustered:          \"col\",\n\t\tCol3DConePercentStacked:     \"col\",\n\t\tCol3DPyramid:                \"col\",\n\t\tCol3DPyramidClustered:       \"col\",\n\t\tCol3DPyramidStacked:         \"col\",\n\t\tCol3DPyramidPercentStacked:  \"col\",\n\t\tCol3DCylinder:               \"col\",\n\t\tCol3DCylinderClustered:      \"col\",\n\t\tCol3DCylinderStacked:        \"col\",\n\t\tCol3DCylinderPercentStacked: \"col\",\n\t\tLine:                        \"standard\",\n\t\tLine3D:                      \"standard\",\n\t}\n\tbarColChartTypes = []ChartType{\n\t\tBar,\n\t\tBarStacked,\n\t\tBarPercentStacked,\n\t\tBar3DClustered,\n\t\tBar3DStacked,\n\t\tBar3DPercentStacked,\n\t\tBar3DConeClustered,\n\t\tBar3DConeStacked,\n\t\tBar3DConePercentStacked,\n\t\tBar3DPyramidClustered,\n\t\tBar3DPyramidStacked,\n\t\tBar3DPyramidPercentStacked,\n\t\tBar3DCylinderClustered,\n\t\tBar3DCylinderStacked,\n\t\tBar3DCylinderPercentStacked,\n\t\tCol,\n\t\tColStacked,\n\t\tColPercentStacked,\n\t\tCol3D,\n\t\tCol3DClustered,\n\t\tCol3DStacked,\n\t\tCol3DPercentStacked,\n\t\tCol3DCone,\n\t\tCol3DConeStacked,\n\t\tCol3DConeClustered,\n\t\tCol3DConePercentStacked,\n\t\tCol3DPyramid,\n\t\tCol3DPyramidClustered,\n\t\tCol3DPyramidStacked,\n\t\tCol3DPyramidPercentStacked,\n\t\tCol3DCylinder,\n\t\tCol3DCylinderClustered,\n\t\tCol3DCylinderStacked,\n\t\tCol3DCylinderPercentStacked,\n\t}\n\torientation = map[bool]string{\n\t\ttrue:  \"maxMin\",\n\t\tfalse: \"minMax\",\n\t}\n\tcatAxPos = map[bool]string{\n\t\ttrue:  \"t\",\n\t\tfalse: \"b\",\n\t}\n\tvalAxPos = map[bool]string{\n\t\ttrue:  \"r\",\n\t\tfalse: \"l\",\n\t}\n\ttickLblPosVal = map[ChartTickLabelPositionType]string{\n\t\tChartTickLabelNextToAxis: \"nextTo\",\n\t\tChartTickLabelHigh:       \"high\",\n\t\tChartTickLabelLow:        \"low\",\n\t\tChartTickLabelNone:       \"none\",\n\t}\n\ttickLblPosNone = map[ChartType]string{\n\t\tContour:          \"none\",\n\t\tWireframeContour: \"none\",\n\t}\n)\n\n// parseChartOptions provides a function to parse the format settings of the\n// chart with default value.\nfunc parseChartOptions(opts *Chart) (*Chart, error) {\n\tif opts == nil {\n\t\treturn nil, ErrParameterInvalid\n\t}\n\tif opts.Dimension.Width == 0 {\n\t\topts.Dimension.Width = defaultChartDimensionWidth\n\t}\n\tif opts.Dimension.Height == 0 {\n\t\topts.Dimension.Height = defaultChartDimensionHeight\n\t}\n\tif opts.Legend.Position == \"\" {\n\t\topts.Legend.Position = defaultChartLegendPosition\n\t}\n\topts.parseTitle()\n\tif opts.Fill.Transparency < 0 || 100 < opts.Fill.Transparency {\n\t\treturn opts, ErrTransparency\n\t}\n\tif opts.VaryColors == nil {\n\t\topts.VaryColors = boolPtr(true)\n\t}\n\tif opts.Border.Width == 0 {\n\t\topts.Border.Width = 0.75\n\t}\n\tif opts.ShowBlanksAs == \"\" {\n\t\topts.ShowBlanksAs = defaultChartShowBlanksAs\n\t}\n\tformat := opts.Format\n\tgraphicOptions, err := format.parseGraphicOptions(nil)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\topts.Format = *graphicOptions\n\treturn opts, opts.parseSeries()\n}\n\n// parseSeries check the series settings of the chart.\nfunc (opts *Chart) parseSeries() error {\n\tfor _, series := range opts.Series {\n\t\tif series.Fill.Transparency < 0 || 100 < series.Fill.Transparency {\n\t\t\treturn ErrTransparency\n\t\t}\n\t}\n\treturn nil\n}\n\n// parseTitle parse the title settings of the chart with default value.\nfunc (opts *Chart) parseTitle() {\n\tfor i := range opts.Title {\n\t\tif opts.Title[i].Font == nil {\n\t\t\topts.Title[i].Font = &Font{}\n\t\t}\n\t\tif opts.Title[i].Font.Color == \"\" {\n\t\t\topts.Title[i].Font.Color = \"595959\"\n\t\t}\n\t\tif opts.Title[i].Font.Size == 0 {\n\t\t\topts.Title[i].Font.Size = 14\n\t\t}\n\t}\n}\n\n// AddChart provides the method to add chart in a sheet by given chart format\n// set (such as offset, scale, aspect ratio setting and print settings) and\n// properties set. For example, create 3D clustered column chart with data\n// Sheet1!$E$1:$L$15:\n//\n//\tpackage main\n//\n//\timport (\n//\t    \"fmt\"\n//\n//\t    \"github.com/xuri/excelize/v2\"\n//\t)\n//\n//\tfunc main() {\n//\t    f := excelize.NewFile()\n//\t    defer func() {\n//\t        if err := f.Close(); err != nil {\n//\t            fmt.Println(err)\n//\t        }\n//\t    }()\n//\t    for idx, row := range [][]interface{}{\n//\t        {nil, \"Apple\", \"Orange\", \"Pear\"}, {\"Small\", 2, 3, 3},\n//\t        {\"Normal\", 5, 2, 4}, {\"Large\", 6, 7, 8},\n//\t    } {\n//\t        cell, err := excelize.CoordinatesToCellName(1, idx+1)\n//\t        if err != nil {\n//\t            fmt.Println(err)\n//\t            return\n//\t        }\n//\t        f.SetSheetRow(\"Sheet1\", cell, &row)\n//\t    }\n//\t    if err := f.AddChart(\"Sheet1\", \"E1\", &excelize.Chart{\n//\t        Type: excelize.Col3DClustered,\n//\t        Series: []excelize.ChartSeries{\n//\t            {\n//\t                Name:       \"Sheet1!$A$2\",\n//\t                Categories: \"Sheet1!$B$1:$D$1\",\n//\t                Values:     \"Sheet1!$B$2:$D$2\",\n//\t            },\n//\t            {\n//\t                Name:       \"Sheet1!$A$3\",\n//\t                Categories: \"Sheet1!$B$1:$D$1\",\n//\t                Values:     \"Sheet1!$B$3:$D$3\",\n//\t            },\n//\t            {\n//\t                Name:       \"Sheet1!$A$4\",\n//\t                Categories: \"Sheet1!$B$1:$D$1\",\n//\t                Values:     \"Sheet1!$B$4:$D$4\",\n//\t            },\n//\t        },\n//\t        Title: []excelize.RichTextRun{\n//\t            {\n//\t                Text: \"Fruit 3D Clustered Column Chart\",\n//\t            },\n//\t        },\n//\t        Legend: excelize.ChartLegend{\n//\t            ShowLegendKey: false,\n//\t        },\n//\t        PlotArea: excelize.ChartPlotArea{\n//\t            ShowBubbleSize:  true,\n//\t            ShowCatName:     false,\n//\t            ShowLeaderLines: false,\n//\t            ShowPercent:     true,\n//\t            ShowSerName:     true,\n//\t            ShowVal:         true,\n//\t        },\n//\t    }); err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    // Save spreadsheet by the given path.\n//\t    if err := f.SaveAs(\"Book1.xlsx\"); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t}\n//\n// The following shows the type of chart supported by excelize:\n//\n//\t ID | Enumeration                 | Chart\n//\t----+-----------------------------+------------------------------\n//\t 0  | Area                        | 2D area chart\n//\t 1  | AreaStacked                 | 2D stacked area chart\n//\t 2  | AreaPercentStacked          | 2D 100% stacked area chart\n//\t 3  | Area3D                      | 3D area chart\n//\t 4  | Area3DStacked               | 3D stacked area chart\n//\t 5  | Area3DPercentStacked        | 3D 100% stacked area chart\n//\t 6  | Bar                         | 2D clustered bar chart\n//\t 7  | BarStacked                  | 2D stacked bar chart\n//\t 8  | BarPercentStacked           | 2D 100% stacked bar chart\n//\t 9  | Bar3DClustered              | 3D clustered bar chart\n//\t 10 | Bar3DStacked                | 3D stacked bar chart\n//\t 11 | Bar3DPercentStacked         | 3D 100% stacked bar chart\n//\t 12 | Bar3DConeClustered          | 3D cone clustered bar chart\n//\t 13 | Bar3DConeStacked            | 3D cone stacked bar chart\n//\t 14 | Bar3DConePercentStacked     | 3D cone percent bar chart\n//\t 15 | Bar3DPyramidClustered       | 3D pyramid clustered bar chart\n//\t 16 | Bar3DPyramidStacked         | 3D pyramid stacked bar chart\n//\t 17 | Bar3DPyramidPercentStacked  | 3D pyramid percent stacked bar chart\n//\t 18 | Bar3DCylinderClustered      | 3D cylinder clustered bar chart\n//\t 19 | Bar3DCylinderStacked        | 3D cylinder stacked bar chart\n//\t 20 | Bar3DCylinderPercentStacked | 3D cylinder percent stacked bar chart\n//\t 21 | Col                         | 2D clustered column chart\n//\t 22 | ColStacked                  | 2D stacked column chart\n//\t 23 | ColPercentStacked           | 2D 100% stacked column chart\n//\t 24 | Col3DClustered              | 3D clustered column chart\n//\t 25 | Col3D                       | 3D column chart\n//\t 26 | Col3DStacked                | 3D stacked column chart\n//\t 27 | Col3DPercentStacked         | 3D 100% stacked column chart\n//\t 28 | Col3DCone                   | 3D cone column chart\n//\t 29 | Col3DConeClustered          | 3D cone clustered column chart\n//\t 30 | Col3DConeStacked            | 3D cone stacked column chart\n//\t 31 | Col3DConePercentStacked     | 3D cone percent stacked column chart\n//\t 32 | Col3DPyramid                | 3D pyramid column chart\n//\t 33 | Col3DPyramidClustered       | 3D pyramid clustered column chart\n//\t 34 | Col3DPyramidStacked         | 3D pyramid stacked column chart\n//\t 35 | Col3DPyramidPercentStacked  | 3D pyramid percent stacked column chart\n//\t 36 | Col3DCylinder               | 3D cylinder column chart\n//\t 37 | Col3DCylinderClustered      | 3D cylinder clustered column chart\n//\t 38 | Col3DCylinderStacked        | 3D cylinder stacked column chart\n//\t 39 | Col3DCylinderPercentStacked | 3D cylinder percent stacked column chart\n//\t 40 | Doughnut                    | doughnut chart\n//\t 41 | Line                        | line chart\n//\t 42 | Line3D                      | 3D line chart\n//\t 43 | Pie                         | pie chart\n//\t 44 | Pie3D                       | 3D pie chart\n//\t 45 | PieOfPie                    | pie of pie chart\n//\t 46 | BarOfPie                    | bar of pie chart\n//\t 47 | Radar                       | radar chart\n//\t 48 | Scatter                     | scatter chart\n//\t 49 | Surface3D                   | 3D surface chart\n//\t 50 | WireframeSurface3D          | 3D wireframe surface chart\n//\t 51 | Contour                     | contour chart\n//\t 52 | WireframeContour            | wireframe contour chart\n//\t 53 | Bubble                      | bubble chart\n//\t 54 | Bubble3D                    | 3D bubble chart\n//\t 55 | StockHighLowClose           | High-Low-Close stock chart\n//\t 56 | StockOpenHighLowClose       | Open-High-Low-Close stock chart\n//\n// In Excel a chart series is a collection of information that defines which\n// data is plotted such as values, axis labels and formatting.\n//\n// The series options that can be set are:\n//\n//\tName\n//\tCategories\n//\tValues\n//\tFill\n//\tLegend\n//\tLine\n//\tMarker\n//\tDataLabel\n//\tDataLabelPosition\n//\tDataPoint\n//\n// Name: Set the name for the series. The name is displayed in the chart legend\n// and in the formula bar. The 'Name' property is optional and if it isn't\n// supplied it will default to Series 1..n. The name can also be a formula such\n// as Sheet1!$A$1\n//\n// Categories: This sets the chart category labels. The category is more or less\n// the same as the X axis. In most chart types the 'Categories' property is\n// optional and the chart will just assume a sequential series from 1..n.\n//\n// Values: This is the most important property of a series and is the only\n// mandatory option for every chart object. This option links the chart with\n// the worksheet data that it displays.\n//\n// Sizes: This sets the bubble size in a data series. The 'Sizes' property is\n// optional and the default value was same with 'Values'.\n//\n// Fill: This set the format for the data series fill. The 'Fill' property is\n// optional.\n//\n// Legend: This set the font of legend text for a data series. The 'Legend'\n// property is optional.\n//\n// Line: This sets the line format of the line chart. The 'Line' property is\n// optional and if it isn't supplied it will default style. The options that\n// can be set are width and color. The range of width is 0.25pt - 999pt. If the\n// value of width is outside the range, the default width of the line is 2pt.\n//\n// Marker: This sets the marker of the line chart and scatter chart. The range\n// of optional field 'Size' is 2-72 (default value is 5). The enumeration value\n// of optional field 'Symbol' are (default value is 'auto'):\n//\n//\tcircle\n//\tdash\n//\tdiamond\n//\tdot\n//\tnone\n//\tpicture\n//\tplus\n//\tsquare\n//\tstar\n//\ttriangle\n//\tx\n//\tauto\n//\n// DataLabel: This sets the format of the chart series data label.\n//\n// DataLabelPosition: This sets the position of the chart series data label.\n//\n// DataPoint: This sets the format for individual data points in a doughnut, pie\n// or 3D pie chart series. The 'DataPoint' property is optional.\n//\n// Set properties of the chart legend. The options that can be set are:\n//\n//\tPosition\n//\tShowLegendKey\n//\tFont\n//\n// Position: Set the position of the chart legend. The default legend position\n// is bottom. The available positions are:\n//\n//\tnone\n//\ttop\n//\tbottom\n//\tleft\n//\tright\n//\ttop_right\n//\n// ShowLegendKey: Set the legend keys shall be shown in data labels. The default\n// value is false.\n//\n// Font: Set the font properties of the chart legend text. The properties that\n// can be set are the same as the font object that is used for cell formatting.\n// The font family, size, color, bold, italic, underline, and strike properties\n// can be set.\n//\n// Set properties of the chart title. The properties that can be set are:\n//\n//\tTitle\n//\n// Title: Set the name (title) for the chart. The name is displayed above the\n// chart. The name can also be a formula such as Sheet1!$A$1 or a list with a\n// sheet name. The name property is optional. The default is to have no chart\n// title.\n//\n// Specifies how blank cells are plotted on the chart by 'ShowBlanksAs'. The\n// default value is gap. The options that can be set are:\n//\n//\tgap\n//\tspan\n//\tzero\n//\n// gap: Specifies that blank values shall be left as a gap.\n//\n// span: Specifies that blank values shall be spanned with a line.\n//\n// zero: Specifies that blank values shall be treated as zero.\n//\n// Specifies that each data marker in the series has a different color by\n// 'VaryColors'. The default value is true.\n//\n// Set chart offset, scale, aspect ratio setting and print settings by 'Format',\n// same as function 'AddPicture'.\n//\n// Set the position of the chart plot area by 'PlotArea'. The properties that\n// can be set are:\n//\n//\tSecondPlotValues\n//\tShowBubbleSize\n//\tShowCatName\n//\tShowDataTable\n//\tShowDataTableKeys\n//\tShowLeaderLines\n//\tShowPercent\n//\tShowSerName\n//\tShowVal\n//\tFill\n//\tUpBars\n//\tDownBars\n//\tNumFmt\n//\n// SecondPlotValues: Specifies the values in second plot for the 'PieOfPie' and\n// 'BarOfPie' chart.\n//\n// ShowBubbleSize: Specifies the bubble size shall be shown in a data label. The\n// 'ShowBubbleSize' property is optional. The default value is false.\n//\n// ShowCatName: Specifies that the category name shall be shown in the data\n// label. The 'ShowCatName' property is optional. The default value is true.\n//\n// ShowDataTable: Used for add data table under chart, depending on the chart\n// type, only available for area, bar, column and line series type charts. The\n// 'ShowDataTable' property is optional. The default value is false.\n//\n// ShowDataTableKeys: Used for add legend key in data table, only works on\n// 'ShowDataTable' is enabled. The 'ShowDataTableKeys' property is optional.\n// The default value is false.\n//\n// ShowLeaderLines: Specifies leader lines shall be shown for data labels. The\n// 'ShowLeaderLines' property is optional. The default value is false.\n//\n// ShowPercent: Specifies that the percentage shall be shown in a data label.\n// The 'ShowPercent' property is optional. The default value is false.\n//\n// ShowSerName: Specifies that the series name shall be shown in a data label.\n// The 'ShowSerName' property is optional. The default value is false.\n//\n// ShowVal: Specifies that the value shall be shown in a data label.\n// The 'ShowVal' property is optional. The default value is false.\n//\n// Fill: Set fill color of the chart.\n//\n// UpBars: Specifies the format for stock chart up bars. The 'UpBars' property\n// is optional.\n//\n// DownBars: Specifies the format for stock chart down bars. The 'DownBars'\n// property is optional.\n//\n// NumFmt: Specifies that if linked to source and set custom number format code\n// for data labels. The 'NumFmt' property is optional. The default format code\n// is 'General'.\n//\n// Set the primary horizontal and vertical axis options by 'XAxis' and 'YAxis'.\n// The properties of 'XAxis' that can be set are:\n//\n//\tNone\n//\tDropLines\n//\tHighLowLines\n//\tMajorGridLines\n//\tMinorGridLines\n//\tTickLabelSkip\n//\tReverseOrder\n//\tMaximum\n//\tMinimum\n//\tAlignment\n//\tFont\n//\tNumFmt\n//\tTitle\n//\n// The properties of 'YAxis' that can be set are:\n//\n//\tNone\n//\tMajorGridLines\n//\tMinorGridLines\n//\tMajorUnit\n//\tSecondary\n//\tReverseOrder\n//\tMaximum\n//\tMinimum\n//\tAlignment\n//\tFont\n//\tLogBase\n//\tNumFmt\n//\tTitle\n//\n// None: Disable axes.\n//\n// DropLines: Specifies drop lines for the 2D and 3D area and line charts. Drop\n// lines are vertical lines that connect data points in a chart down to the\n// horizontal (category) axis. They are often used in Line or Area charts to\n// make it easier to see the exact category position of each point. The\n// 'DropLines' property is optional. The default value is false.\n//\n// HighLowLines: Specifies high low lines for the 2D line chart. High low lines\n// displayed by default in stock charts. They extend from the highest value to\n// the lowest value in each category. The 'HighLowLines' property is optional.\n// The default value is false.\n//\n// MajorGridLines: Specifies major grid lines.\n//\n// MinorGridLines: Specifies minor grid lines.\n//\n// MajorUnit: Specifies the distance between major ticks. Shall contain a\n// positive floating-point number. The 'MajorUnit' property is optional. The\n// default value is auto.\n//\n// Secondary: Specifies the current series vertical axis as the secondary axis,\n// this only works for the second and later chart in the combo chart. The\n// default value is false.\n//\n// TickLabelSkip: Specifies how many tick labels to skip between label that is\n// drawn. The 'TickLabelSkip' property is optional. The default value is auto.\n//\n// ReverseOrder: Specifies that the categories or values on reverse order\n// (orientation of the chart). The 'ReverseOrder' property is optional. The\n// default value is false.\n//\n// Maximum: Specifies that the fixed maximum, 0 is auto. The 'Maximum' property\n// is optional. The default value is auto.\n//\n// Minimum: Specifies that the fixed minimum, 0 is auto. The 'Minimum' property\n// is optional. The default value is auto.\n//\n// Alignment: Specifies that the alignment of the horizontal and vertical axis.\n// The properties of alignment that can be set are:\n//\n//\tTextRotation\n//\tVertical\n//\n// The value of 'TextRotation' that can be set from -90 to 90.\n//\n// The value of 'Vertical' that can be set are:\n//\n//\thorz\n//\tvert\n//\tvert270\n//\twordArtVert\n//\teaVert\n//\tmongolianVert\n//\twordArtVertRtl\n//\n// Font: Specifies that the font of the horizontal and vertical axis. The\n// properties of font that can be set are:\n//\n//\tBold\n//\tItalic\n//\tUnderline\n//\tFamily\n//\tSize\n//\tStrike\n//\tColor\n//\tVertAlign\n//\n// LogBase: Specifies logarithmic scale base number of the vertical axis.\n//\n// NumFmt: Specifies that if linked to source and set custom number format code\n// for axis. The 'NumFmt' property is optional. The default format code is\n// 'General'.\n//\n// Title: Specifies that the primary horizontal or vertical axis title and\n// resize chart. The 'Title' property is optional.\n//\n// Set chart size by 'Dimension' property. The 'Dimension' property is optional.\n// The default width is 480, and height is 260.\n//\n// Set chart legend for all data series by 'Legend' property. The 'Legend'\n// property is optional.\n//\n// Set the bubble size in all data series for the bubble chart or 3D bubble\n// chart by 'BubbleSizes' property. The 'BubbleSizes' property is optional. The\n// default width is 100, and the value should be great than 0 and less or equal\n// than 300.\n//\n// Set the doughnut hole size in all data series for the doughnut chart by\n// 'HoleSize' property. The 'HoleSize' property is optional. The default width\n// is 75, and the value should be great than 0 and less or equal than 90.\n//\n// Set the gap with of the column and bar series chart by 'GapWidth' property.\n// The 'GapWidth' property is optional. The default width is 150, and the value\n// should be great or equal than 0 and less or equal than 500.\n//\n// Set series overlap of the column and bar series chart by 'Overlap' property.\n// The 'Overlap' property is optional. The default width is 0, and the value\n// should be great or equal than -100 and less or equal than 100.\n//\n// combo: Specifies the create a chart that combines two or more chart types in\n// a single chart. For example, create a clustered column - line chart with\n// data Sheet1!$E$1:$L$15:\n//\n//\tpackage main\n//\n//\timport (\n//\t    \"fmt\"\n//\n//\t    \"github.com/xuri/excelize/v2\"\n//\t)\n//\n//\tfunc main() {\n//\t    f := excelize.NewFile()\n//\t    defer func() {\n//\t        if err := f.Close(); err != nil {\n//\t            fmt.Println(err)\n//\t        }\n//\t    }()\n//\t    for idx, row := range [][]interface{}{\n//\t        {nil, \"Apple\", \"Orange\", \"Pear\"}, {\"Small\", 2, 3, 3},\n//\t        {\"Normal\", 5, 2, 4}, {\"Large\", 6, 7, 8},\n//\t    } {\n//\t        cell, err := excelize.CoordinatesToCellName(1, idx+1)\n//\t        if err != nil {\n//\t            fmt.Println(err)\n//\t            return\n//\t        }\n//\t        f.SetSheetRow(\"Sheet1\", cell, &row)\n//\t    }\n//\t    enable, disable := true, false\n//\t    if err := f.AddChart(\"Sheet1\", \"E1\", &excelize.Chart{\n//\t        Type: excelize.Col,\n//\t        Series: []excelize.ChartSeries{\n//\t            {\n//\t                Name:       \"Sheet1!$A$2\",\n//\t                Categories: \"Sheet1!$B$1:$D$1\",\n//\t                Values:     \"Sheet1!$B$2:$D$2\",\n//\t            },\n//\t        },\n//\t        Format: excelize.GraphicOptions{\n//\t            ScaleX:          1,\n//\t            ScaleY:          1,\n//\t            OffsetX:         15,\n//\t            OffsetY:         10,\n//\t            PrintObject:     &enable,\n//\t            LockAspectRatio: false,\n//\t            Locked:          &disable,\n//\t        },\n//\t        Title: []excelize.RichTextRun{\n//\t            {\n//\t                Text: \"Clustered Column - Line Chart\",\n//\t            },\n//\t        },\n//\t        Legend: excelize.ChartLegend{\n//\t            Position:      \"left\",\n//\t            ShowLegendKey: false,\n//\t        },\n//\t        PlotArea: excelize.ChartPlotArea{\n//\t            ShowCatName:     false,\n//\t            ShowLeaderLines: false,\n//\t            ShowPercent:     true,\n//\t            ShowSerName:     true,\n//\t            ShowVal:         true,\n//\t        },\n//\t    }, &excelize.Chart{\n//\t        Type: excelize.Line,\n//\t        Series: []excelize.ChartSeries{\n//\t            {\n//\t                Name:       \"Sheet1!$A$4\",\n//\t                Categories: \"Sheet1!$B$1:$D$1\",\n//\t                Values:     \"Sheet1!$B$4:$D$4\",\n//\t                Marker: excelize.ChartMarker{\n//\t                    Symbol: \"none\", Size: 10,\n//\t                },\n//\t            },\n//\t        },\n//\t        Format: excelize.GraphicOptions{\n//\t            ScaleX:          1,\n//\t            ScaleY:          1,\n//\t            OffsetX:         15,\n//\t            OffsetY:         10,\n//\t            PrintObject:     &enable,\n//\t            LockAspectRatio: false,\n//\t            Locked:          &disable,\n//\t        },\n//\t        Legend: excelize.ChartLegend{\n//\t            Position:      \"right\",\n//\t            ShowLegendKey: false,\n//\t        },\n//\t        PlotArea: excelize.ChartPlotArea{\n//\t            ShowCatName:     false,\n//\t            ShowLeaderLines: false,\n//\t            ShowPercent:     true,\n//\t            ShowSerName:     true,\n//\t            ShowVal:         true,\n//\t        },\n//\t    }); err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    // Save spreadsheet by the given path.\n//\t    if err := f.SaveAs(\"Book1.xlsx\"); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t}\nfunc (f *File) AddChart(sheet, cell string, chart *Chart, combo ...*Chart) error {\n\t// Read worksheet data\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\topts, comboCharts, err := f.getChartOptions(chart, combo)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.\n\tdrawingID := f.countDrawings() + 1\n\tchartID := f.countCharts() + 1\n\tdrawingXML := \"xl/drawings/drawing\" + strconv.Itoa(drawingID) + \".xml\"\n\tdrawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML)\n\tdrawingRels := \"xl/drawings/_rels/drawing\" + strconv.Itoa(drawingID) + \".xml.rels\"\n\tdrawingRID := f.addRels(drawingRels, SourceRelationshipChart, \"../charts/chart\"+strconv.Itoa(chartID)+\".xml\", \"\")\n\terr = f.addDrawingChart(sheet, drawingXML, cell, int(opts.Dimension.Width), int(opts.Dimension.Height), drawingRID, &opts.Format)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.addChart(opts, comboCharts)\n\tif err = f.addContentTypePart(chartID, \"chart\"); err != nil {\n\t\treturn err\n\t}\n\t_ = f.addContentTypePart(drawingID, \"drawings\")\n\tf.addSheetNameSpace(sheet, SourceRelationship)\n\treturn err\n}\n\n// AddChartSheet provides the method to create a chartsheet by given chart\n// format set (such as offset, scale, aspect ratio setting and print settings)\n// and properties set. In Excel a chartsheet is a worksheet that only contains\n// a chart.\nfunc (f *File) AddChartSheet(sheet string, chart *Chart, combo ...*Chart) error {\n\t// Check if the worksheet already exists\n\tidx, err := f.GetSheetIndex(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif idx != -1 {\n\t\treturn ErrExistsSheet\n\t}\n\topts, comboCharts, err := f.getChartOptions(chart, combo)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcs := xlsxChartsheet{\n\t\tSheetViews: &xlsxChartsheetViews{\n\t\t\tSheetView: []*xlsxChartsheetView{{ZoomScaleAttr: 100, ZoomToFitAttr: true}},\n\t\t},\n\t}\n\tf.SheetCount++\n\twb, _ := f.workbookReader()\n\tsheetID := 0\n\tfor _, v := range wb.Sheets.Sheet {\n\t\tif v.SheetID > sheetID {\n\t\t\tsheetID = v.SheetID\n\t\t}\n\t}\n\tsheetID++\n\tpath := \"xl/chartsheets/sheet\" + strconv.Itoa(sheetID) + \".xml\"\n\tf.sheetMap[sheet] = path\n\tf.Sheet.Store(path, nil)\n\tdrawingID := f.countDrawings() + 1\n\tchartID := f.countCharts() + 1\n\tdrawingXML := \"xl/drawings/drawing\" + strconv.Itoa(drawingID) + \".xml\"\n\tf.prepareChartSheetDrawing(&cs, drawingID, sheet)\n\tdrawingRels := \"xl/drawings/_rels/drawing\" + strconv.Itoa(drawingID) + \".xml.rels\"\n\tdrawingRID := f.addRels(drawingRels, SourceRelationshipChart, \"../charts/chart\"+strconv.Itoa(chartID)+\".xml\", \"\")\n\tif err = f.addSheetDrawingChart(drawingXML, drawingRID, &opts.Format); err != nil {\n\t\treturn err\n\t}\n\tf.addChart(opts, comboCharts)\n\tif err = f.addContentTypePart(chartID, \"chart\"); err != nil {\n\t\treturn err\n\t}\n\t_ = f.addContentTypePart(sheetID, \"chartsheet\")\n\t_ = f.addContentTypePart(drawingID, \"drawings\")\n\t// Update workbook.xml.rels\n\trID := f.addRels(f.getWorkbookRelsPath(), SourceRelationshipChartsheet, fmt.Sprintf(\"/xl/chartsheets/sheet%d.xml\", sheetID), \"\")\n\t// Update workbook.xml\n\tf.setWorkbook(sheet, sheetID, rID)\n\tchartsheet, _ := xml.Marshal(cs)\n\tf.addSheetNameSpace(sheet, NameSpaceSpreadSheet)\n\tf.saveFileList(path, replaceRelationshipsBytes(f.replaceNameSpaceBytes(path, chartsheet)))\n\treturn err\n}\n\n// getChartOptions provides a function to check format set of the chart and\n// create chart format.\nfunc (f *File) getChartOptions(opts *Chart, combo []*Chart) (*Chart, []*Chart, error) {\n\tvar comboCharts []*Chart\n\toptions, err := parseChartOptions(opts)\n\tif err != nil {\n\t\treturn options, comboCharts, err\n\t}\n\tfor _, comboFormat := range combo {\n\t\tcomboChart, err := parseChartOptions(comboFormat)\n\t\tif err != nil {\n\t\t\treturn options, comboCharts, err\n\t\t}\n\t\tif _, ok := chartValAxNumFmtFormatCode[comboChart.Type]; !ok {\n\t\t\treturn options, comboCharts, newUnsupportedChartType(comboChart.Type)\n\t\t}\n\t\tcomboCharts = append(comboCharts, comboChart)\n\t}\n\tif _, ok := chartValAxNumFmtFormatCode[options.Type]; !ok {\n\t\treturn options, comboCharts, newUnsupportedChartType(options.Type)\n\t}\n\treturn options, comboCharts, err\n}\n\n// DeleteChart provides a function to delete chart in spreadsheet by given\n// worksheet name and cell reference.\nfunc (f *File) DeleteChart(sheet, cell string) error {\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcol--\n\trow--\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif ws.Drawing == nil {\n\t\treturn err\n\t}\n\tdrawingXML := strings.ReplaceAll(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), \"..\", \"xl\")\n\t_, err = f.deleteDrawing(col, row, drawingXML, \"Chart\")\n\treturn err\n}\n\n// countCharts provides a function to get chart files count storage in the\n// folder xl/charts.\nfunc (f *File) countCharts() int {\n\tcount := 0\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/charts/chart\") {\n\t\t\tcount++\n\t\t}\n\t\treturn true\n\t})\n\treturn count\n}\n\n// ptToEMUs provides a function to convert pt to EMUs, 1 pt = 12700 EMUs. The\n// range of pt is 0.25pt - 999pt. If the value of pt is outside the range, the\n// default EMUs will be returned.\nfunc (f *File) ptToEMUs(pt float64) int {\n\tif 0.25 > pt || pt > 999 {\n\t\treturn 25400\n\t}\n\treturn int(12700 * pt)\n}\n"
  },
  {
    "path": "chart_test.go",
    "content": "package excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestChartSize(t *testing.T) {\n\tf := NewFile()\n\tsheet1 := f.GetSheetName(0)\n\n\tcategories := map[string]string{\n\t\t\"A2\": \"Small\",\n\t\t\"A3\": \"Normal\",\n\t\t\"A4\": \"Large\",\n\t\t\"B1\": \"Apple\",\n\t\t\"C1\": \"Orange\",\n\t\t\"D1\": \"Pear\",\n\t}\n\tfor cell, v := range categories {\n\t\tassert.NoError(t, f.SetCellValue(sheet1, cell, v))\n\t}\n\n\tvalues := map[string]int{\n\t\t\"B2\": 2,\n\t\t\"C2\": 3,\n\t\t\"D2\": 3,\n\t\t\"B3\": 5,\n\t\t\"C3\": 2,\n\t\t\"D3\": 4,\n\t\t\"B4\": 6,\n\t\t\"C4\": 7,\n\t\t\"D4\": 8,\n\t}\n\tfor cell, v := range values {\n\t\tassert.NoError(t, f.SetCellValue(sheet1, cell, v))\n\t}\n\n\tassert.NoError(t, f.AddChart(\"Sheet1\", \"E4\", &Chart{\n\t\tType: Col3DClustered,\n\t\tDimension: ChartDimension{\n\t\t\tWidth:  640,\n\t\t\tHeight: 480,\n\t\t},\n\t\tSeries: []ChartSeries{\n\t\t\t{Name: \"Sheet1!$A$2\", Categories: \"Sheet1!$B$1:$D$1\", Values: \"Sheet1!$B$2:$D$2\"},\n\t\t\t{Name: \"Sheet1!$A$3\", Categories: \"Sheet1!$B$1:$D$1\", Values: \"Sheet1!$B$3:$D$3\"},\n\t\t\t{Name: \"Sheet1!$A$4\", Categories: \"Sheet1!$B$1:$D$1\", Values: \"Sheet1!$B$4:$D$4\"},\n\t\t},\n\t\tTitle: []RichTextRun{{Text: \"3D Clustered Column Chart\"}},\n\t}))\n\n\tvar buffer bytes.Buffer\n\n\t// Save spreadsheet by the given path.\n\tassert.NoError(t, f.Write(&buffer))\n\n\tnewFile, err := OpenReader(&buffer)\n\tassert.NoError(t, err)\n\n\tchartsNum := newFile.countCharts()\n\tif !assert.Equal(t, 1, chartsNum, \"Expected 1 chart, actual %d\", chartsNum) {\n\t\tt.FailNow()\n\t}\n\n\tvar (\n\t\tworkdir decodeWsDr\n\t\tanchor  decodeCellAnchor\n\t)\n\n\tcontent, ok := newFile.Pkg.Load(\"xl/drawings/drawing1.xml\")\n\tassert.True(t, ok, \"Can't open the chart\")\n\n\terr = xml.Unmarshal(content.([]byte), &workdir)\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\n\terr = xml.Unmarshal([]byte(\"<decodeCellAnchor>\"+\n\t\tworkdir.TwoCellAnchor[0].Content+\"</decodeCellAnchor>\"), &anchor)\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\n\tif !assert.Equal(t, 4, anchor.From.Col, \"Expected 'from' column 4\") ||\n\t\t!assert.Equal(t, 3, anchor.From.Row, \"Expected 'from' row 3\") {\n\n\t\tt.FailNow()\n\t}\n\n\tassert.Equal(t, 14, anchor.To.Col, \"Expected 'to' column 14\")\n\tassert.Equal(t, 29, anchor.To.Row, \"Expected 'to' row 29\")\n}\n\nfunc TestAddDrawingChart(t *testing.T) {\n\tf := NewFile()\n\tassert.EqualError(t, f.addDrawingChart(\"SheetN\", \"\", \"\", 0, 0, 0, nil), newCellNameToCoordinatesError(\"\", newInvalidCellNameError(\"\")).Error())\n\n\tpath := \"xl/drawings/drawing1.xml\"\n\tf.Pkg.Store(path, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.addDrawingChart(\"Sheet1\", path, \"A1\", 0, 0, 0, &GraphicOptions{PrintObject: boolPtr(true), Locked: boolPtr(false)}), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestAddSheetDrawingChart(t *testing.T) {\n\tf := NewFile()\n\tpath := \"xl/drawings/drawing1.xml\"\n\tf.Pkg.Store(path, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.addSheetDrawingChart(path, 0, &GraphicOptions{PrintObject: boolPtr(true), Locked: boolPtr(false)}), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestDeleteDrawing(t *testing.T) {\n\tf := NewFile()\n\tpath := \"xl/drawings/drawing1.xml\"\n\tf.Pkg.Store(path, MacintoshCyrillicCharset)\n\t_, err := f.deleteDrawing(0, 0, path, \"Chart\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tf, err = OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\tf.Drawings.Store(path, &xlsxWsDr{OneCellAnchor: []*xdrCellAnchor{{\n\t\tGraphicFrame: string(MacintoshCyrillicCharset),\n\t}}})\n\t_, err = f.deleteDrawing(0, 0, path, \"Chart\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tf.Drawings.Store(path, &xlsxWsDr{TwoCellAnchor: []*xdrCellAnchor{{\n\t\tGraphicFrame: string(MacintoshCyrillicCharset),\n\t}}})\n\t_, err = f.deleteDrawing(0, 0, path, \"Chart\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestAddChart(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\n\tcategories := map[string]string{\"A30\": \"SS\", \"A31\": \"S\", \"A32\": \"M\", \"A33\": \"L\", \"A34\": \"LL\", \"A35\": \"XL\", \"A36\": \"XXL\", \"A37\": \"XXXL\", \"B29\": \"Apple\", \"C29\": \"Orange\", \"D29\": \"Pear\"}\n\tvalues := map[string]int{\"B30\": 1, \"C30\": 1, \"D30\": 1, \"B31\": 2, \"C31\": 2, \"D31\": 2, \"B32\": 3, \"C32\": 3, \"D32\": 3, \"B33\": 4, \"C33\": 4, \"D33\": 4, \"B34\": 5, \"C34\": 5, \"D34\": 5, \"B35\": 6, \"C35\": 6, \"D35\": 6, \"B36\": 7, \"C36\": 7, \"D36\": 7, \"B37\": 8, \"C37\": 8, \"D37\": 8}\n\tfor k, v := range categories {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", k, v))\n\t}\n\tfor k, v := range values {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", k, v))\n\t}\n\tassert.EqualError(t, f.AddChart(\"Sheet1\", \"P1\", nil), ErrParameterInvalid.Error())\n\n\t// Test add chart on not exists worksheet\n\tassert.EqualError(t, f.AddChart(\"SheetN\", \"P1\", nil), \"sheet SheetN does not exist\")\n\t// Test add chart with invalid positioning types\n\tassert.Equal(t, f.AddChart(\"Sheet1\", \"P1\", &Chart{\n\t\tFormat: GraphicOptions{Positioning: \"x\"},\n\t}), newInvalidOptionalValue(\"Positioning\", \"x\", supportedPositioning))\n\tmaximum, minimum, zero := 7.5, 0.5, .0\n\tseries := []ChartSeries{\n\t\t{Name: \"Sheet1!$A$30\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$30:$D$30\"},\n\t\t{Name: \"Sheet1!$A$31\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$31:$D$31\"},\n\t\t{Name: \"Sheet1!$A$32\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$32:$D$32\"},\n\t\t{Name: \"Sheet1!$A$33\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$33:$D$33\"},\n\t\t{Name: \"Sheet1!$A$34\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$34:$D$34\"},\n\t\t{Name: \"Sheet1!$A$35\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$35:$D$35\"},\n\t\t{Name: \"Sheet1!$A$36\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$36:$D$36\"},\n\t\t{\n\t\t\tName: \"Sheet1!$A$37\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$37:$D$37\",\n\t\t\tMarker: ChartMarker{\n\t\t\t\tFill: Fill{Type: \"pattern\", Color: []string{\"FFFFFF\"}, Pattern: 1},\n\t\t\t\tBorder: ChartLine{\n\t\t\t\t\tType:  ChartLineSolid,\n\t\t\t\t\tDash:  ChartDashDot,\n\t\t\t\t\tFill:  Fill{Type: \"pattern\", Color: []string{\"#FFFF00\"}, Pattern: 1},\n\t\t\t\t\tWidth: 1,\n\t\t\t\t},\n\t\t\t},\n\t\t\tLegend: ChartLegend{Font: &Font{Family: \"Arial\", Size: 11, Strike: true, Color: \"777777\"}},\n\t\t},\n\t}\n\tseries2 := []ChartSeries{\n\t\t{\n\t\t\tName: \"Sheet1!$A$30\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$30:$D$30\",\n\t\t\tFill:   Fill{Type: \"pattern\", Color: []string{\"000000\"}, Pattern: 1, Transparency: 60},\n\t\t\tMarker: ChartMarker{Symbol: \"none\", Size: 10},\n\t\t},\n\t\t{Name: \"Sheet1!$A$31\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$31:$D$31\"},\n\t\t{Name: \"Sheet1!$A$32\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$32:$D$32\"},\n\t\t{Name: \"Sheet1!$A$33\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$33:$D$33\"},\n\t\t{Name: \"Sheet1!$A$34\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$34:$D$34\"},\n\t\t{Name: \"Sheet1!$A$35\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$35:$D$35\"},\n\t\t{Name: \"Sheet1!$A$36\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$36:$D$36\"},\n\t\t{Name: \"Sheet1!$A$37\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$37:$D$37\", Line: ChartLine{Dash: ChartDashDash, Width: 0.25}},\n\t}\n\tseries3 := []ChartSeries{{Name: \"Sheet1!$A$30\", Categories: \"Sheet1!$A$30:$D$37\", Values: \"Sheet1!$B$30:$B$37\"}}\n\tseries4 := []ChartSeries{{Name: \"Sheet1!$A$30\", Categories: \"Sheet1!$A$30:$D$37\", Values: \"Sheet1!$B$30:$B$37\", DataPoint: []ChartDataPoint{\n\t\t{Index: 0, Fill: Fill{Type: \"pattern\", Color: []string{\"003F5C\"}, Pattern: 1}},\n\t\t{Index: 1, Fill: Fill{Type: \"pattern\", Color: []string{\"58508D\"}, Pattern: 1}},\n\t\t{Index: 2, Fill: Fill{Type: \"pattern\", Color: []string{\"BC5090\"}, Pattern: 1}},\n\t\t{Index: 3, Fill: Fill{Type: \"pattern\", Color: []string{\"FF6361\"}, Pattern: 1}},\n\t}}}\n\tseries5 := []ChartSeries{\n\t\t{Name: \"Sheet1!$A$30\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$30:$D$30\", Sizes: \"Sheet1!$B$30:$D$30\", DataLabelPosition: ChartDataLabelsPositionAbove},\n\t\t{Name: \"Sheet1!$A$31\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$31:$D$31\", Sizes: \"Sheet1!$B$31:$D$31\", DataLabelPosition: ChartDataLabelsPositionLeft},\n\t\t{Name: \"Sheet1!$A$32\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$32:$D$32\", Sizes: \"Sheet1!$B$32:$D$32\", DataLabelPosition: ChartDataLabelsPositionBestFit},\n\t\t{Name: \"Sheet1!$A$33\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$33:$D$33\", Sizes: \"Sheet1!$B$33:$D$33\", DataLabelPosition: ChartDataLabelsPositionCenter},\n\t\t{Name: \"Sheet1!$A$34\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$34:$D$34\", Sizes: \"Sheet1!$B$34:$D$34\", DataLabelPosition: ChartDataLabelsPositionInsideBase},\n\t\t{Name: \"Sheet1!$A$35\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$35:$D$35\", Sizes: \"Sheet1!$B$35:$D$35\", DataLabelPosition: ChartDataLabelsPositionInsideEnd},\n\t\t{Name: \"Sheet1!$A$36\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$36:$D$36\", Sizes: \"Sheet1!$B$36:$D$36\", DataLabelPosition: ChartDataLabelsPositionOutsideEnd},\n\t\t{Name: \"Sheet1!$A$37\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$37:$D$37\", Sizes: \"Sheet1!$B$37:$D$37\", DataLabelPosition: ChartDataLabelsPositionRight},\n\t}\n\tformat := GraphicOptions{\n\t\tScaleX:          defaultDrawingScale,\n\t\tScaleY:          defaultDrawingScale,\n\t\tOffsetX:         15,\n\t\tOffsetY:         10,\n\t\tPrintObject:     boolPtr(true),\n\t\tLockAspectRatio: false,\n\t\tLocked:          boolPtr(false),\n\t}\n\tlegend := ChartLegend{Position: \"left\", ShowLegendKey: false}\n\tplotArea := ChartPlotArea{\n\t\tSecondPlotValues: 3,\n\t\tShowBubbleSize:   true,\n\t\tShowCatName:      true,\n\t\tShowLeaderLines:  false,\n\t\tShowPercent:      true,\n\t\tShowSerName:      true,\n\t\tShowVal:          true,\n\t\tFill:             Fill{Type: \"pattern\", Pattern: 1},\n\t}\n\tfor _, c := range []struct {\n\t\tsheetName, cell string\n\t\topts            *Chart\n\t}{\n\t\t{sheetName: \"Sheet1\", cell: \"P1\", opts: &Chart{Type: Col, Series: series, Format: format, Legend: ChartLegend{Position: \"none\", ShowLegendKey: true}, Title: []RichTextRun{{Text: \"2D Column Chart\", Font: &Font{Size: 11, Family: \"Calibri\"}}}, PlotArea: plotArea, Border: ChartLine{Type: ChartLineNone}, ShowBlanksAs: \"zero\", XAxis: ChartAxis{Font: Font{Bold: true, Italic: true, Underline: \"dbl\", Family: \"Times New Roman\", Size: 15, Strike: true, Color: \"000000\"}, Title: []RichTextRun{{Text: \"Primary Horizontal Axis Title\"}}}, YAxis: ChartAxis{Font: Font{Bold: false, Italic: false, Underline: \"sng\", Color: \"777777\"}, Title: []RichTextRun{{Text: \"Primary Vertical Axis Title\", Font: &Font{Color: \"777777\", Bold: true, Italic: true, Size: 12}}}}}},\n\t\t{sheetName: \"Sheet1\", cell: \"X1\", opts: &Chart{Type: ColStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"2D Stacked Column Chart\"}}, PlotArea: plotArea, Fill: Fill{Type: \"pattern\", Pattern: 1}, Border: ChartLine{Type: ChartLineAutomatic}, ShowBlanksAs: \"zero\", GapWidth: uintPtr(10), Overlap: intPtr(100)}},\n\t\t{sheetName: \"Sheet1\", cell: \"P16\", opts: &Chart{Type: ColPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"100% Stacked Column Chart\"}}, PlotArea: plotArea, Fill: Fill{Type: \"pattern\", Color: []string{\"EEEEEE\"}, Pattern: 1}, Border: ChartLine{Type: ChartLineSolid, Width: 2}, ShowBlanksAs: \"zero\", XAxis: ChartAxis{Alignment: Alignment{Vertical: \"wordArtVertRtl\", TextRotation: 0}}}},\n\t\t{sheetName: \"Sheet1\", cell: \"X16\", opts: &Chart{Type: Col3DClustered, Series: series, Format: format, Legend: ChartLegend{Position: \"bottom\", ShowLegendKey: false, Font: &Font{Size: 10}}, Title: []RichTextRun{{Text: \"3D Clustered Column Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"P30\", opts: &Chart{Type: Col3DStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Stacked Column Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{Alignment: Alignment{Vertical: \"vert\", TextRotation: 0}}}},\n\t\t{sheetName: \"Sheet1\", cell: \"X30\", opts: &Chart{Type: Col3DPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D 100% Stacked Column Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"X45\", opts: &Chart{Type: Radar, Series: series, Format: format, Legend: ChartLegend{Position: \"top_right\", ShowLegendKey: false}, Title: []RichTextRun{{Text: \"Radar Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"span\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"AF1\", opts: &Chart{Type: Col3DConeStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Cone Stacked Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"AF16\", opts: &Chart{Type: Col3DConeClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Cone Clustered Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"AF30\", opts: &Chart{Type: Col3DConePercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Cone Percent Stacked Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"AF45\", opts: &Chart{Type: Col3DCone, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Cone Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"AN1\", opts: &Chart{Type: Col3DPyramidStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Pyramid Percent Stacked Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"AN16\", opts: &Chart{Type: Col3DPyramidClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Pyramid Clustered Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"AN30\", opts: &Chart{Type: Col3DPyramidPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Pyramid Percent Stacked Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"AN45\", opts: &Chart{Type: Col3DPyramid, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Pyramid Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"AV1\", opts: &Chart{Type: Col3DCylinderStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Cylinder Stacked Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"AV16\", opts: &Chart{Type: Col3DCylinderClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Cylinder Clustered Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"AV30\", opts: &Chart{Type: Col3DCylinderPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Cylinder Percent Stacked Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"AV45\", opts: &Chart{Type: Col3DCylinder, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Cylinder Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet1\", cell: \"P45\", opts: &Chart{Type: Col3D, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Column Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{Alignment: Alignment{Vertical: \"vert270\", TextRotation: 0}}}},\n\t\t{sheetName: \"Sheet2\", cell: \"P1\", opts: &Chart{Type: Line3D, Series: series2, Format: format, Legend: ChartLegend{Position: \"top\", ShowLegendKey: false}, Title: []RichTextRun{{Text: \"3D Line Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{DropLines: true, MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1, NumFmt: ChartNumFmt{CustomNumFmt: \"General\"}}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1, NumFmt: ChartNumFmt{CustomNumFmt: \"General\"}}}},\n\t\t{sheetName: \"Sheet2\", cell: \"X1\", opts: &Chart{Type: Scatter, Series: series, Format: format, Legend: ChartLegend{Position: \"bottom\", ShowLegendKey: false}, Title: []RichTextRun{{Text: \"Scatter Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"P16\", opts: &Chart{Type: Doughnut, Series: series3, Format: format, Legend: ChartLegend{Position: \"right\", ShowLegendKey: false}, Title: []RichTextRun{{Text: \"Doughnut Chart\"}}, PlotArea: ChartPlotArea{ShowBubbleSize: false, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: false, ShowVal: false}, ShowBlanksAs: \"zero\", HoleSize: 30}},\n\t\t{sheetName: \"Sheet2\", cell: \"X16\", opts: &Chart{Type: Line, Series: series2, Format: format, Legend: ChartLegend{Position: \"top\", ShowLegendKey: false}, Title: []RichTextRun{{Text: \"Line Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{DropLines: true, HighLowLines: true, MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1, TickLabelPosition: ChartTickLabelLow}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1}}},\n\t\t{sheetName: \"Sheet2\", cell: \"P32\", opts: &Chart{Type: Pie3D, Series: series3, Format: format, Legend: ChartLegend{Position: \"bottom\", ShowLegendKey: false}, Title: []RichTextRun{{Text: \"3D Column Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"X32\", opts: &Chart{Type: Pie, Series: series4, Format: format, Legend: ChartLegend{Position: \"bottom\", ShowLegendKey: false}, Title: []RichTextRun{{Text: \"Pie Chart\"}}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: false, ShowVal: false, NumFmt: ChartNumFmt{CustomNumFmt: \"0.00%;0;;\"}}, ShowBlanksAs: \"gap\"}},\n\t\t// bar series chart\n\t\t{sheetName: \"Sheet2\", cell: \"P48\", opts: &Chart{Type: Bar, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"2D Clustered Bar Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"X48\", opts: &Chart{Type: BarStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"2D Stacked Bar Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"P64\", opts: &Chart{Type: BarPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"2D Stacked 100% Bar Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"X64\", opts: &Chart{Type: Bar3DClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Clustered Bar Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"P80\", opts: &Chart{Type: Bar3DStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Stacked Bar Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", YAxis: ChartAxis{Maximum: &maximum, Minimum: &minimum}}},\n\t\t{sheetName: \"Sheet2\", cell: \"X80\", opts: &Chart{Type: Bar3DPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D 100% Stacked Bar Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{ReverseOrder: true, Secondary: true, Minimum: &zero}, YAxis: ChartAxis{ReverseOrder: true, Minimum: &zero}}},\n\t\t// area series chart\n\t\t{sheetName: \"Sheet2\", cell: \"AF1\", opts: &Chart{Type: Area, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"2D Area Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{DropLines: true}}},\n\t\t{sheetName: \"Sheet2\", cell: \"AN1\", opts: &Chart{Type: AreaStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"2D Stacked Area Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{DropLines: true}}},\n\t\t{sheetName: \"Sheet2\", cell: \"AF16\", opts: &Chart{Type: AreaPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"2D 100% Stacked Area Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{DropLines: true}}},\n\t\t{sheetName: \"Sheet2\", cell: \"AN16\", opts: &Chart{Type: Area3D, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Area Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{DropLines: true}}},\n\t\t{sheetName: \"Sheet2\", cell: \"AF32\", opts: &Chart{Type: Area3DStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Stacked Area Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{DropLines: true}}},\n\t\t{sheetName: \"Sheet2\", cell: \"AN32\", opts: &Chart{Type: Area3DPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D 100% Stacked Area Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{DropLines: true}}},\n\t\t// cylinder series chart\n\t\t{sheetName: \"Sheet2\", cell: \"AF48\", opts: &Chart{Type: Bar3DCylinderStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Bar Cylinder Stacked Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"AF64\", opts: &Chart{Type: Bar3DCylinderClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Bar Cylinder Clustered Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"AF80\", opts: &Chart{Type: Bar3DCylinderPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Bar Cylinder Percent Stacked Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t// cone series chart\n\t\t{sheetName: \"Sheet2\", cell: \"AN48\", opts: &Chart{Type: Bar3DConeStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Bar Cone Stacked Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"AN64\", opts: &Chart{Type: Bar3DConeClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Bar Cone Clustered Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"AN80\", opts: &Chart{Type: Bar3DConePercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Bar Cone Percent Stacked Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"AV48\", opts: &Chart{Type: Bar3DPyramidStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Bar Pyramid Stacked Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"AV64\", opts: &Chart{Type: Bar3DPyramidClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Bar Pyramid Clustered Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"AV80\", opts: &Chart{Type: Bar3DPyramidPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Bar Pyramid Percent Stacked Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t// surface series chart\n\t\t{sheetName: \"Sheet2\", cell: \"AV1\", opts: &Chart{Type: Surface3D, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Surface Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", YAxis: ChartAxis{MajorGridLines: true}}},\n\t\t{sheetName: \"Sheet2\", cell: \"AV16\", opts: &Chart{Type: WireframeSurface3D, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"3D Wireframe Surface Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", YAxis: ChartAxis{MajorGridLines: true}}},\n\t\t{sheetName: \"Sheet2\", cell: \"AV32\", opts: &Chart{Type: Contour, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"Contour Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t{sheetName: \"Sheet2\", cell: \"BD1\", opts: &Chart{Type: WireframeContour, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"Wireframe Contour Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}},\n\t\t// bubble chart\n\t\t{sheetName: \"Sheet2\", cell: \"BD16\", opts: &Chart{Type: Bubble, Series: series5, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"Bubble Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", BubbleSize: 75}},\n\t\t{sheetName: \"Sheet2\", cell: \"BD32\", opts: &Chart{Type: Bubble3D, Series: series5, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"Bubble 3D Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},\n\t\t// pie of pie chart\n\t\t{sheetName: \"Sheet2\", cell: \"BD48\", opts: &Chart{Type: PieOfPie, Series: series3, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"Pie of Pie Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},\n\t\t// bar of pie chart\n\t\t{sheetName: \"Sheet2\", cell: \"BD64\", opts: &Chart{Type: BarOfPie, Series: series3, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"Bar of Pie Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},\n\t} {\n\t\tassert.NoError(t, f.AddChart(c.sheetName, c.cell, c.opts))\n\t}\n\t// combo chart\n\t_, err = f.NewSheet(\"Combo Charts\")\n\tassert.NoError(t, err)\n\tclusteredColumnCombo := [][]interface{}{\n\t\t{\"A1\", Line, \"Clustered Column - Line Chart\"},\n\t\t{\"I1\", Doughnut, \"Clustered Column - Doughnut Chart\"},\n\t}\n\tfor _, props := range clusteredColumnCombo {\n\t\tassert.NoError(t, f.AddChart(\"Combo Charts\", props[0].(string), &Chart{Type: Col, Series: series[:4], Format: format, Legend: legend, Title: []RichTextRun{{Text: props[2].(string)}}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowDataTable: true, ShowDataTableKeys: true, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}, &Chart{Type: props[1].(ChartType), Series: series[4:], Format: format, Legend: legend, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}, YAxis: ChartAxis{Secondary: true}}))\n\t}\n\tstackedAreaCombo := map[string][]interface{}{\n\t\t\"A16\": {Line, \"Stacked Area - Line Chart\"},\n\t\t\"I16\": {Doughnut, \"Stacked Area - Doughnut Chart\"},\n\t}\n\tfor axis, props := range stackedAreaCombo {\n\t\tassert.NoError(t, f.AddChart(\"Combo Charts\", axis, &Chart{Type: AreaStacked, Series: series[:4], Format: format, Legend: legend, Title: []RichTextRun{{Text: props[1].(string)}}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}, &Chart{Type: props[0].(ChartType), Series: series[4:], Format: format, Legend: legend, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}))\n\t}\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddChart.xlsx\")))\n\t// Test with invalid sheet name\n\tassert.EqualError(t, f.AddChart(\"Sheet:1\", \"A1\", &Chart{Type: Col, Series: series[:1]}), ErrSheetNameInvalid.Error())\n\t// Test with illegal cell reference\n\tassert.EqualError(t, f.AddChart(\"Sheet2\", \"A\", &Chart{Type: Col, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"2D Column Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\t// Test with unsupported chart type\n\tassert.EqualError(t, f.AddChart(\"Sheet2\", \"BD32\", &Chart{Type: 0x39, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"Bubble 3D Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}), newUnsupportedChartType(0x39).Error())\n\t// Test add combo chart with invalid format set\n\tassert.EqualError(t, f.AddChart(\"Sheet2\", \"BD32\", &Chart{Type: Col, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"2D Column Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}, nil), ErrParameterInvalid.Error())\n\t// Test add combo chart with unsupported chart type\n\tassert.EqualError(t, f.AddChart(\"Sheet2\", \"BD64\", &Chart{Type: BarOfPie, Series: []ChartSeries{{Name: \"Sheet1!$A$30\", Categories: \"Sheet1!$A$30:$D$37\", Values: \"Sheet1!$B$30:$B$37\"}}, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"Bar of Pie Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}, &Chart{Type: 0x39, Series: []ChartSeries{{Name: \"Sheet1!$A$30\", Categories: \"Sheet1!$A$30:$D$37\", Values: \"Sheet1!$B$30:$B$37\"}}, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"Bar of Pie Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}), newUnsupportedChartType(0x39).Error())\n\t// Test add chart with series transparency value exceeds limit\n\tassert.Equal(t, ErrTransparency, f.AddChart(\"Sheet1\", \"BD64\", &Chart{Type: Col, Series: []ChartSeries{{Name: \"Sheet1!$A$30\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$30:$D$30\", Fill: Fill{Transparency: 110}}}}))\n\t// Test add chart with transparency value exceeds limit\n\tassert.Equal(t, ErrTransparency, f.AddChart(\"Sheet1\", \"BD64\", &Chart{Type: Col, Series: []ChartSeries{{Name: \"Sheet1!$A$30\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$30:$D$30\"}}, Fill: Fill{Transparency: -1}}))\n\tassert.NoError(t, f.Close())\n\n\t// Test add chart with unsupported charset content types.\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddChart(\"Sheet1\", \"P1\", &Chart{Type: Col, Series: []ChartSeries{{Name: \"Sheet1!$A$30\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$30:$D$30\"}}, Title: []RichTextRun{{Text: \"2D Column Chart\"}}}), \"XML syntax error on line 1: invalid UTF-8\")\n\n\tt.Run(\"for_create_stock_chart\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tfor i, row := range [][]interface{}{\n\t\t\t{\"Date\", \"Volume\", \"Open\", \"High\", \"Low\", \"Close\"},\n\t\t\t{45593, 14864000, 431.66, 431.94, 426.3, 426.59},\n\t\t\t{45590, 16899100, 426.76, 432.52, 426.57, 428.15},\n\t\t\t{45589, 13581600, 425.33, 425.98, 422.4, 424.73},\n\t\t\t{45588, 19654400, 430.86, 431.08, 422.53, 424.6},\n\t\t\t{45587, 25482200, 418.49, 430.58, 418.04, 427.51},\n\t\t\t{45586, 14206100, 416.12, 418.96, 413.75, 418.78},\n\t\t\t{45583, 17145300, 417.14, 419.65, 416.26, 418.16},\n\t\t\t{45582, 14820000, 422.36, 422.5, 415.59, 416.72},\n\t\t\t{45581, 15508900, 415.17, 416.36, 410.48, 416.12},\n\t\t\t{45580, 18900200, 422.18, 422.48, 415.26, 418.74},\n\t\t\t{45579, 16653100, 417.77, 424.04, 417.52, 419.14},\n\t\t\t{45576, 14144900, 416.14, 417.13, 413.25, 416.32},\n\t\t\t{45575, 13848400, 415.23, 417.35, 413.15, 415.84},\n\t\t\t{45574, 14974300, 415.86, 420.38, 414.3, 417.46},\n\t\t\t{45573, 19229300, 410.9, 415.66, 408.17, 414.71},\n\t\t\t{45572, 20919800, 416, 417.11, 409, 409.54},\n\t\t\t{45569, 19169700, 418.24, 419.75, 414.97, 416.06},\n\t\t\t{45568, 13686400, 417.63, 419.55, 414.29, 416.54},\n\t\t\t{45567, 16582300, 422.58, 422.82, 416.71, 417.13},\n\t\t\t{45566, 19092900, 428.45, 428.48, 418.81, 420.69},\n\t\t\t{45565, 16807300, 428.21, 430.42, 425.37, 430.3},\n\t\t} {\n\t\t\tcell, err := CoordinatesToCellName(1, i+1)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", cell, &row))\n\t\t}\n\t\tstyle, err := f.NewStyle(&Style{NumFmt: 15})\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.SetColStyle(\"Sheet1\", \"A\", style))\n\n\t\tassert.NoError(t, f.AddChart(\"Sheet1\", \"G1\", &Chart{\n\t\t\tType: StockHighLowClose,\n\t\t\tSeries: []ChartSeries{\n\t\t\t\t{Name: \"Sheet1!$D$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$D$2:$D$22\"},\n\t\t\t\t{Name: \"Sheet1!$E$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$E$2:$E$22\"},\n\t\t\t\t{Name: \"Sheet1!$F$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$F$2:$F$22\"},\n\t\t\t},\n\t\t\tLegend: ChartLegend{Position: \"none\"},\n\t\t\tTitle:  []RichTextRun{{Text: \"High-Low-Close Stock Chart\"}},\n\t\t\tXAxis:  ChartAxis{NumFmt: ChartNumFmt{CustomNumFmt: \"d-mmm-yy\"}},\n\t\t}))\n\t\tassert.NoError(t, f.AddChart(\"Sheet1\", \"G16\", &Chart{\n\t\t\tType: StockOpenHighLowClose,\n\t\t\tSeries: []ChartSeries{\n\t\t\t\t{Name: \"Sheet1!$C$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$C$2:$C$22\"},\n\t\t\t\t{Name: \"Sheet1!$D$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$D$2:$D$22\"},\n\t\t\t\t{Name: \"Sheet1!$E$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$E$2:$E$22\"},\n\t\t\t\t{Name: \"Sheet1!$F$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$F$2:$F$22\"},\n\t\t\t},\n\t\t\tLegend: ChartLegend{Position: \"none\"},\n\t\t\tTitle:  []RichTextRun{{Text: \"Open-High-Low-Close Stock Chart\"}},\n\t\t\tXAxis:  ChartAxis{NumFmt: ChartNumFmt{CustomNumFmt: \"d-mmm-yy\"}},\n\t\t\tPlotArea: ChartPlotArea{\n\t\t\t\tUpBars: ChartUpDownBar{\n\t\t\t\t\tBorder: ChartLine{Type: ChartLineNone},\n\t\t\t\t\tFill:   Fill{Type: \"pattern\", Color: []string{\"00B050\"}, Pattern: 1},\n\t\t\t\t},\n\t\t\t\tDownBars: ChartUpDownBar{\n\t\t\t\t\tBorder: ChartLine{Type: ChartLineNone},\n\t\t\t\t\tFill:   Fill{Type: \"pattern\", Color: []string{\"FF0000\"}, Pattern: 1},\n\t\t\t\t},\n\t\t\t},\n\t\t}))\n\t\tassert.NoError(t, f.AddChart(\"Sheet1\", \"O1\", &Chart{\n\t\t\tType: Col,\n\t\t\tSeries: []ChartSeries{\n\t\t\t\t{Name: \"Sheet1!$B$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$B$2:$B$22\"},\n\t\t\t},\n\t\t\tVaryColors: boolPtr(false),\n\t\t\tXAxis:      ChartAxis{NumFmt: ChartNumFmt{CustomNumFmt: \"d-mmm-yy\"}},\n\t\t\tYAxis:      ChartAxis{NumFmt: ChartNumFmt{CustomNumFmt: \"#,##0\"}},\n\t\t\tTitle:      []RichTextRun{{Text: \"Volume-High-Low-Close Stock Chart\"}},\n\t\t}, &Chart{\n\t\t\tType: StockHighLowClose,\n\t\t\tSeries: []ChartSeries{\n\t\t\t\t{Name: \"Sheet1!$D$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$D$2:$D$22\"},\n\t\t\t\t{Name: \"Sheet1!$E$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$E$2:$E$22\"},\n\t\t\t\t{Name: \"Sheet1!$F$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$F$2:$F$22\"},\n\t\t\t},\n\t\t\tYAxis: ChartAxis{Secondary: true},\n\t\t}))\n\t\tassert.NoError(t, f.AddChart(\"Sheet1\", \"O16\", &Chart{\n\t\t\tType: Col,\n\t\t\tSeries: []ChartSeries{\n\t\t\t\t{Name: \"Sheet1!$B$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$B$2:$B$22\"},\n\t\t\t},\n\t\t\tVaryColors: boolPtr(false),\n\t\t\tXAxis:      ChartAxis{NumFmt: ChartNumFmt{CustomNumFmt: \"d-mmm-yy\"}},\n\t\t\tYAxis:      ChartAxis{NumFmt: ChartNumFmt{CustomNumFmt: \"#,##0\"}},\n\t\t\tTitle:      []RichTextRun{{Text: \"Volume-Open-High-Low-Close Stock Chart\"}},\n\t\t}, &Chart{\n\t\t\tType: StockOpenHighLowClose,\n\t\t\tSeries: []ChartSeries{\n\t\t\t\t{Name: \"Sheet1!$C$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$C$2:$C$22\"},\n\t\t\t\t{Name: \"Sheet1!$D$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$D$2:$D$22\"},\n\t\t\t\t{Name: \"Sheet1!$E$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$E$2:$E$22\"},\n\t\t\t\t{Name: \"Sheet1!$F$1\", Categories: \"Sheet1!$A$2:$A$22\", Values: \"Sheet1!$F$2:$F$22\"},\n\t\t\t},\n\t\t\tYAxis: ChartAxis{Secondary: true},\n\t\t}))\n\t\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddChartStock.xlsx\")))\n\t})\n}\n\nfunc TestAddChartSheet(t *testing.T) {\n\tcategories := map[string]string{\"A2\": \"Small\", \"A3\": \"Normal\", \"A4\": \"Large\", \"B1\": \"Apple\", \"C1\": \"Orange\", \"D1\": \"Pear\"}\n\tvalues := map[string]int{\"B2\": 2, \"C2\": 3, \"D2\": 3, \"B3\": 5, \"C3\": 2, \"D3\": 4, \"B4\": 6, \"C4\": 7, \"D4\": 8}\n\tf := NewFile()\n\tfor k, v := range categories {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", k, v))\n\t}\n\tfor k, v := range values {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", k, v))\n\t}\n\tseries := []ChartSeries{\n\t\t{Name: \"Sheet1!$A$2\", Categories: \"Sheet1!$B$1:$D$1\", Values: \"Sheet1!$B$2:$D$2\"},\n\t\t{Name: \"Sheet1!$A$3\", Categories: \"Sheet1!$B$1:$D$1\", Values: \"Sheet1!$B$3:$D$3\"},\n\t\t{Name: \"Sheet1!$A$4\", Categories: \"Sheet1!$B$1:$D$1\", Values: \"Sheet1!$B$4:$D$4\"},\n\t}\n\tassert.NoError(t, f.AddChartSheet(\"Chart1\", &Chart{Type: Col3DClustered, Series: series, Title: []RichTextRun{{Text: \"Fruit 3D Clustered Column Chart\"}}}))\n\t// Test set the chartsheet as active sheet\n\tvar sheetIdx int\n\tfor idx, sheetName := range f.GetSheetList() {\n\t\tif sheetName != \"Chart1\" {\n\t\t\tcontinue\n\t\t}\n\t\tsheetIdx = idx\n\t}\n\tf.SetActiveSheet(sheetIdx)\n\n\t// Test cell value on chartsheet\n\tassert.EqualError(t, f.SetCellValue(\"Chart1\", \"A1\", true), \"sheet Chart1 is not a worksheet\")\n\t// Test add chartsheet on already existing name sheet\n\n\tassert.EqualError(t, f.AddChartSheet(\"Sheet1\", &Chart{Type: Col3DClustered, Series: series, Title: []RichTextRun{{Text: \"Fruit 3D Clustered Column Chart\"}}}), ErrExistsSheet.Error())\n\t// Test add chartsheet with invalid sheet name\n\tassert.EqualError(t, f.AddChartSheet(\"Sheet:1\", nil, &Chart{Type: Col3DClustered, Series: series, Title: []RichTextRun{{Text: \"Fruit 3D Clustered Column Chart\"}}}), ErrSheetNameInvalid.Error())\n\t// Test with unsupported chart type\n\tassert.EqualError(t, f.AddChartSheet(\"Chart2\", &Chart{Type: 0x39, Series: series, Title: []RichTextRun{{Text: \"Fruit 3D Clustered Column Chart\"}}}), newUnsupportedChartType(0x39).Error())\n\n\tassert.NoError(t, f.UpdateLinkedValue())\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddChartSheet.xlsx\")))\n\t// Test add chart sheet with unsupported charset content types\n\tf = NewFile()\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddChartSheet(\"Chart4\", &Chart{Type: Col, Series: []ChartSeries{{Name: \"Sheet1!$A$30\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$30:$D$30\"}}, Title: []RichTextRun{{Text: \"2D Column Chart\"}}}), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestDeleteChart(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.DeleteChart(\"Sheet1\", \"A1\"))\n\tseries := []ChartSeries{\n\t\t{Name: \"Sheet1!$A$30\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$30:$D$30\"},\n\t\t{Name: \"Sheet1!$A$31\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$31:$D$31\"},\n\t\t{Name: \"Sheet1!$A$32\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$32:$D$32\"},\n\t\t{Name: \"Sheet1!$A$33\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$33:$D$33\"},\n\t\t{Name: \"Sheet1!$A$34\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$34:$D$34\"},\n\t\t{Name: \"Sheet1!$A$35\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$35:$D$35\"},\n\t\t{Name: \"Sheet1!$A$36\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$36:$D$36\"},\n\t\t{Name: \"Sheet1!$A$37\", Categories: \"Sheet1!$B$29:$D$29\", Values: \"Sheet1!$B$37:$D$37\"},\n\t}\n\tformat := GraphicOptions{\n\t\tName:            \"Chart 3\",\n\t\tAltText:         \"chart\",\n\t\tScaleX:          defaultDrawingScale,\n\t\tScaleY:          defaultDrawingScale,\n\t\tOffsetX:         15,\n\t\tOffsetY:         10,\n\t\tPrintObject:     boolPtr(true),\n\t\tLockAspectRatio: false,\n\t\tLocked:          boolPtr(false),\n\t}\n\tlegend := ChartLegend{Position: \"left\", ShowLegendKey: false}\n\tplotArea := ChartPlotArea{\n\t\tShowBubbleSize:  true,\n\t\tShowCatName:     true,\n\t\tShowLeaderLines: false,\n\t\tShowPercent:     true,\n\t\tShowSerName:     true,\n\t\tShowVal:         true,\n\t}\n\tassert.NoError(t, f.AddChart(\"Sheet1\", \"P1\", &Chart{Type: Col, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: \"2D Column Chart\"}}, PlotArea: plotArea, ShowBlanksAs: \"zero\"}))\n\tassert.NoError(t, f.DeleteChart(\"Sheet1\", \"P1\"))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestDeleteChart.xlsx\")))\n\t// Test delete chart with invalid sheet name\n\tassert.EqualError(t, f.DeleteChart(\"Sheet:1\", \"P1\"), ErrSheetNameInvalid.Error())\n\t// Test delete chart on not exists worksheet\n\tassert.EqualError(t, f.DeleteChart(\"SheetN\", \"A1\"), \"sheet SheetN does not exist\")\n\t// Test delete chart with invalid coordinates\n\tassert.EqualError(t, f.DeleteChart(\"Sheet1\", \"\"), newCellNameToCoordinatesError(\"\", newInvalidCellNameError(\"\")).Error())\n\t// Test delete chart on no chart worksheet\n\tassert.NoError(t, NewFile().DeleteChart(\"Sheet1\", \"A1\"))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestChartWithLogarithmicBase(t *testing.T) {\n\t// Create test workbook with data\n\tf := NewFile()\n\tsheet1 := f.GetSheetName(0)\n\tcategories := map[string]float64{\n\t\t\"A1\":  1,\n\t\t\"A2\":  2,\n\t\t\"A3\":  3,\n\t\t\"A4\":  4,\n\t\t\"A5\":  5,\n\t\t\"A6\":  6,\n\t\t\"A7\":  7,\n\t\t\"A8\":  8,\n\t\t\"A9\":  9,\n\t\t\"A10\": 10,\n\t\t\"B1\":  0.1,\n\t\t\"B2\":  1,\n\t\t\"B3\":  2,\n\t\t\"B4\":  3,\n\t\t\"B5\":  20,\n\t\t\"B6\":  30,\n\t\t\"B7\":  100,\n\t\t\"B8\":  500,\n\t\t\"B9\":  700,\n\t\t\"B10\": 5000,\n\t}\n\tfor cell, v := range categories {\n\t\tassert.NoError(t, f.SetCellValue(sheet1, cell, v))\n\t}\n\tseries := []ChartSeries{{Name: \"value\", Categories: \"Sheet1!$A$1:$A$19\", Values: \"Sheet1!$B$1:$B$10\"}}\n\tdimension := []uint{640, 480, 320, 240}\n\tfor _, c := range []struct {\n\t\tcell string\n\t\topts *Chart\n\t}{\n\t\t{cell: \"C1\", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[0], Height: dimension[1]}, Series: series, Title: []RichTextRun{{Text: \"Line chart without log scaling\"}}}},\n\t\t{cell: \"M1\", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[0], Height: dimension[1]}, Series: series, Title: []RichTextRun{{Text: \"Line chart with log 10.5 scaling\"}}, YAxis: ChartAxis{LogBase: 10.5}}},\n\t\t{cell: \"A25\", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[2], Height: dimension[3]}, Series: series, Title: []RichTextRun{{Text: \"Line chart with log 1.9 scaling\"}}, YAxis: ChartAxis{LogBase: 1.9}}},\n\t\t{cell: \"F25\", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[2], Height: dimension[3]}, Series: series, Title: []RichTextRun{{Text: \"Line chart with log 2 scaling\"}}, YAxis: ChartAxis{LogBase: 2}}},\n\t\t{cell: \"K25\", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[2], Height: dimension[3]}, Series: series, Title: []RichTextRun{{Text: \"Line chart with log 1000.1 scaling\"}}, YAxis: ChartAxis{LogBase: 1000.1}}},\n\t\t{cell: \"P25\", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[2], Height: dimension[3]}, Series: series, Title: []RichTextRun{{Text: \"Line chart with log 1000 scaling\"}}, YAxis: ChartAxis{LogBase: 1000}}},\n\t} {\n\t\t// Add two chart, one without and one with log scaling\n\t\tassert.NoError(t, f.AddChart(sheet1, c.cell, c.opts))\n\t}\n\n\t// Export workbook for human confirmation\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestChartWithLogarithmicBase10.xlsx\")))\n\n\t// Write the workbook to a buffer\n\tvar buffer bytes.Buffer\n\tassert.NoError(t, f.Write(&buffer))\n\n\t// Read back the workbook from the buffer\n\tnewFile, err := OpenReader(&buffer)\n\tassert.NoError(t, err)\n\n\t// Check the number of charts\n\texpectedChartsCount := 6\n\tchartsNum := newFile.countCharts()\n\tif !assert.Equal(t, expectedChartsCount, chartsNum,\n\t\t\"Expected %d charts, actual %d\", expectedChartsCount, chartsNum) {\n\t\tt.FailNow()\n\t}\n\n\tchartSpaces := make([]xlsxChartSpace, expectedChartsCount)\n\ttype xmlChartContent []byte\n\txmlCharts := make([]xmlChartContent, expectedChartsCount)\n\texpectedChartsLogBase := []float64{0, 10.5, 0, 2, 0, 1000}\n\tvar (\n\t\tdrawingML interface{}\n\t\tok        bool\n\t)\n\tfor i := 0; i < expectedChartsCount; i++ {\n\t\tchartPath := fmt.Sprintf(\"xl/charts/chart%d.xml\", i+1)\n\t\tif drawingML, ok = newFile.Pkg.Load(chartPath); ok {\n\t\t\txmlCharts[i] = drawingML.([]byte)\n\t\t}\n\t\tassert.True(t, ok, \"Can't open the %s\", chartPath)\n\n\t\terr = xml.Unmarshal(xmlCharts[i], &chartSpaces[i])\n\t\tif !assert.NoError(t, err) {\n\t\t\tt.FailNow()\n\t\t}\n\n\t\tchartLogBasePtr := chartSpaces[i].Chart.PlotArea.ValAx[0].Scaling.LogBase\n\t\tif expectedChartsLogBase[i] == 0 {\n\t\t\tif !assert.Nil(t, chartLogBasePtr, \"LogBase is not nil\") {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t} else {\n\t\t\tif !assert.NotNil(t, chartLogBasePtr, \"LogBase is nil\") {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t\tif !assert.Equal(t, expectedChartsLogBase[i], *(chartLogBasePtr.Val),\n\t\t\t\t\"Expected log base to %f, actual %f\", expectedChartsLogBase[i], *(chartLogBasePtr.Val)) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "col.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/tiendc/go-deepcopy\"\n\t\"golang.org/x/text/width\"\n)\n\n// Cols defines an iterator to a sheet\ntype Cols struct {\n\terr                                    error\n\tcurCol, totalCols, totalRows, stashCol int\n\trawCellValue                           bool\n\tsheet                                  string\n\tf                                      *File\n\tsheetXML                               []byte\n\tsst                                    *xlsxSST\n}\n\n// GetCols gets the value of all cells by columns on the worksheet based on the\n// given worksheet name, returned as a two-dimensional array, where the value\n// of the cell is converted to the `string` type. If the cell format can be\n// applied to the value of the cell, the applied value will be used, otherwise\n// the original value will be used.\n//\n// For example, get and traverse the value of all cells by columns on a\n// worksheet named\n// 'Sheet1':\n//\n//\tcols, err := f.GetCols(\"Sheet1\")\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\tfor _, col := range cols {\n//\t    for _, rowCell := range col {\n//\t        fmt.Print(rowCell, \"\\t\")\n//\t    }\n//\t    fmt.Println()\n//\t}\nfunc (f *File) GetCols(sheet string, opts ...Options) ([][]string, error) {\n\tcols, err := f.Cols(sheet)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresults := make([][]string, 0, 64)\n\tfor cols.Next() {\n\t\tcol, _ := cols.Rows(opts...)\n\t\tresults = append(results, col)\n\t}\n\treturn results, nil\n}\n\n// Next will return true if the next column is found.\nfunc (cols *Cols) Next() bool {\n\tcols.curCol++\n\treturn cols.curCol <= cols.totalCols\n}\n\n// Error will return an error when the error occurs.\nfunc (cols *Cols) Error() error {\n\treturn cols.err\n}\n\n// Rows return the current column's row values.\nfunc (cols *Cols) Rows(opts ...Options) ([]string, error) {\n\tvar rowIterator rowXMLIterator\n\tif cols.stashCol >= cols.curCol {\n\t\treturn rowIterator.cells, rowIterator.err\n\t}\n\tcols.rawCellValue = cols.f.getOptions(opts...).RawCellValue\n\tif cols.sst, rowIterator.err = cols.f.sharedStringsReader(); rowIterator.err != nil {\n\t\treturn rowIterator.cells, rowIterator.err\n\t}\n\tdecoder := cols.f.xmlNewDecoder(bytes.NewReader(cols.sheetXML))\n\tfor {\n\t\ttoken, _ := decoder.Token()\n\t\tif token == nil {\n\t\t\tbreak\n\t\t}\n\t\tswitch xmlElement := token.(type) {\n\t\tcase xml.StartElement:\n\t\t\trowIterator.inElement = xmlElement.Name.Local\n\t\t\tif rowIterator.inElement == \"row\" {\n\t\t\t\trowIterator.cellCol = 0\n\t\t\t\trowIterator.cellRow++\n\t\t\t\tattrR, _ := attrValToInt(\"r\", xmlElement.Attr)\n\t\t\t\tif attrR != 0 {\n\t\t\t\t\trowIterator.cellRow = attrR\n\t\t\t\t}\n\t\t\t}\n\t\t\tif cols.rowXMLHandler(&rowIterator, &xmlElement, decoder); rowIterator.err != nil {\n\t\t\t\treturn rowIterator.cells, rowIterator.err\n\t\t\t}\n\t\tcase xml.EndElement:\n\t\t\tif xmlElement.Name.Local == \"sheetData\" {\n\t\t\t\treturn rowIterator.cells, rowIterator.err\n\t\t\t}\n\t\t}\n\t}\n\treturn rowIterator.cells, rowIterator.err\n}\n\n// columnXMLIterator defined runtime use field for the worksheet column SAX parser.\ntype columnXMLIterator struct {\n\terr                  error\n\tcols                 Cols\n\tcellCol, curRow, row int\n}\n\n// columnXMLHandler parse the column XML element of the worksheet.\nfunc columnXMLHandler(colIterator *columnXMLIterator, xmlElement *xml.StartElement) {\n\tcolIterator.err = nil\n\tinElement := xmlElement.Name.Local\n\tif inElement == \"row\" {\n\t\tcolIterator.row++\n\t\tfor _, attr := range xmlElement.Attr {\n\t\t\tif attr.Name.Local == \"r\" {\n\t\t\t\tif colIterator.curRow, colIterator.err = strconv.Atoi(attr.Value); colIterator.err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tcolIterator.row = colIterator.curRow\n\t\t\t}\n\t\t}\n\t\tcolIterator.cols.totalRows = colIterator.row\n\t\tcolIterator.cellCol = 0\n\t}\n\tif inElement == \"c\" {\n\t\tcolIterator.cellCol++\n\t\tfor _, attr := range xmlElement.Attr {\n\t\t\tif attr.Name.Local == \"r\" {\n\t\t\t\tif colIterator.cellCol, _, colIterator.err = CellNameToCoordinates(attr.Value); colIterator.err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif colIterator.cellCol > colIterator.cols.totalCols {\n\t\t\tcolIterator.cols.totalCols = colIterator.cellCol\n\t\t}\n\t}\n}\n\n// rowXMLHandler parse the row XML element of the worksheet.\nfunc (cols *Cols) rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.StartElement, decoder *xml.Decoder) {\n\tif rowIterator.inElement == \"c\" {\n\t\trowIterator.cellCol++\n\t\tfor _, attr := range xmlElement.Attr {\n\t\t\tif attr.Name.Local == \"r\" {\n\t\t\t\tif rowIterator.cellCol, rowIterator.cellRow, rowIterator.err = CellNameToCoordinates(attr.Value); rowIterator.err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tblank := rowIterator.cellRow - len(rowIterator.cells)\n\t\tfor i := 1; i < blank; i++ {\n\t\t\trowIterator.cells = append(rowIterator.cells, \"\")\n\t\t}\n\t\tif rowIterator.cellCol == cols.curCol {\n\t\t\tcolCell := xlsxC{}\n\t\t\t_ = decoder.DecodeElement(&colCell, xmlElement)\n\t\t\tval, _ := colCell.getValueFrom(cols.f, cols.sst, cols.rawCellValue)\n\t\t\trowIterator.cells = append(rowIterator.cells, val)\n\t\t}\n\t}\n}\n\n// Cols returns a columns iterator, used for streaming reading data for a\n// worksheet with a large data. This function is concurrency safe. For\n// example:\n//\n//\tcols, err := f.Cols(\"Sheet1\")\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\tfor cols.Next() {\n//\t    col, err := cols.Rows()\n//\t    if err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t    for _, rowCell := range col {\n//\t        fmt.Print(rowCell, \"\\t\")\n//\t    }\n//\t    fmt.Println()\n//\t}\nfunc (f *File) Cols(sheet string) (*Cols, error) {\n\tif err := checkSheetName(sheet); err != nil {\n\t\treturn nil, err\n\t}\n\tname, ok := f.getSheetXMLPath(sheet)\n\tif !ok {\n\t\treturn nil, ErrSheetNotExist{sheet}\n\t}\n\tif worksheet, ok := f.Sheet.Load(name); ok && worksheet != nil {\n\t\tws := worksheet.(*xlsxWorksheet)\n\t\tws.mu.Lock()\n\t\tdefer ws.mu.Unlock()\n\t\toutput, _ := xml.Marshal(ws)\n\t\tf.saveFileList(name, f.replaceNameSpaceBytes(name, output))\n\t}\n\tvar colIterator columnXMLIterator\n\tcolIterator.cols.sheetXML = f.readBytes(name)\n\tdecoder := f.xmlNewDecoder(bytes.NewReader(colIterator.cols.sheetXML))\n\tfor {\n\t\ttoken, _ := decoder.Token()\n\t\tif token == nil {\n\t\t\tbreak\n\t\t}\n\t\tswitch xmlElement := token.(type) {\n\t\tcase xml.StartElement:\n\t\t\tcolumnXMLHandler(&colIterator, &xmlElement)\n\t\t\tif colIterator.err != nil {\n\t\t\t\treturn &colIterator.cols, colIterator.err\n\t\t\t}\n\t\tcase xml.EndElement:\n\t\t\tif xmlElement.Name.Local == \"sheetData\" {\n\t\t\t\tcolIterator.cols.f = f\n\t\t\t\tcolIterator.cols.sheet = sheet\n\t\t\t\treturn &colIterator.cols, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn &colIterator.cols, nil\n}\n\n// GetColVisible provides a function to get visible of a single column by given\n// worksheet name and column name. This function is concurrency safe. For\n// example, get visible state of column D in Sheet1:\n//\n//\tvisible, err := f.GetColVisible(\"Sheet1\", \"D\")\nfunc (f *File) GetColVisible(sheet, col string) (bool, error) {\n\tcolNum, err := ColumnNameToNumber(col)\n\tif err != nil {\n\t\treturn true, err\n\t}\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn false, err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tif ws.Cols == nil {\n\t\treturn true, err\n\t}\n\tvisible := true\n\tfor c := range ws.Cols.Col {\n\t\tcolData := &ws.Cols.Col[c]\n\t\tif colData.Min <= colNum && colNum <= colData.Max {\n\t\t\tvisible = !colData.Hidden\n\t\t}\n\t}\n\treturn visible, err\n}\n\n// SetColVisible provides a function to set visible columns by given worksheet\n// name, columns range and visibility. This function is concurrency safe.\n//\n// For example hide column D on Sheet1:\n//\n//\terr := f.SetColVisible(\"Sheet1\", \"D\", false)\n//\n// Hide the columns from D to F (included):\n//\n//\terr := f.SetColVisible(\"Sheet1\", \"D:F\", false)\nfunc (f *File) SetColVisible(sheet, columns string, visible bool) error {\n\tminVal, maxVal, err := f.parseColRange(columns)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tws.setColVisible(minVal, maxVal, visible)\n\treturn err\n}\n\n// setColVisible provides a function to set the visibility of a single column\n// or multiple columns by given column number.\nfunc (ws *xlsxWorksheet) setColVisible(minVal, maxVal int, visible bool) {\n\tcolData := xlsxCol{\n\t\tMin:         minVal,\n\t\tMax:         maxVal,\n\t\tWidth:       float64Ptr(defaultColWidth),\n\t\tHidden:      !visible,\n\t\tCustomWidth: true,\n\t}\n\tif ws.Cols == nil {\n\t\tcols := xlsxCols{}\n\t\tcols.Col = append(cols.Col, colData)\n\t\tws.Cols = &cols\n\t\treturn\n\t}\n\tws.Cols.Col = flatCols(colData, ws.Cols.Col, func(fc, c xlsxCol) xlsxCol {\n\t\tfc.BestFit = c.BestFit\n\t\tfc.Collapsed = c.Collapsed\n\t\tfc.CustomWidth = c.CustomWidth\n\t\tfc.OutlineLevel = c.OutlineLevel\n\t\tfc.Phonetic = c.Phonetic\n\t\tfc.Style = c.Style\n\t\tfc.Width = c.Width\n\t\treturn fc\n\t})\n}\n\n// GetColOutlineLevel provides a function to get outline level of a single\n// column by given worksheet name and column name. For example, get outline\n// level of column D in Sheet1:\n//\n//\tlevel, err := f.GetColOutlineLevel(\"Sheet1\", \"D\")\nfunc (f *File) GetColOutlineLevel(sheet, col string) (uint8, error) {\n\tlevel := uint8(0)\n\tcolNum, err := ColumnNameToNumber(col)\n\tif err != nil {\n\t\treturn level, err\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif ws.Cols == nil {\n\t\treturn level, err\n\t}\n\tfor c := range ws.Cols.Col {\n\t\tcolData := &ws.Cols.Col[c]\n\t\tif colData.Min <= colNum && colNum <= colData.Max {\n\t\t\tlevel = colData.OutlineLevel\n\t\t}\n\t}\n\treturn level, err\n}\n\n// parseColRange parse and convert column range with column name to the column number.\nfunc (f *File) parseColRange(columns string) (minVal, maxVal int, err error) {\n\tcolsTab := strings.Split(columns, \":\")\n\tminVal, err = ColumnNameToNumber(colsTab[0])\n\tif err != nil {\n\t\treturn\n\t}\n\tmaxVal = minVal\n\tif len(colsTab) == 2 {\n\t\tif maxVal, err = ColumnNameToNumber(colsTab[1]); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif maxVal < minVal {\n\t\tminVal, maxVal = maxVal, minVal\n\t}\n\treturn\n}\n\n// SetColOutlineLevel provides a function to set outline level of a single\n// column by given worksheet name and column name. The value of parameter\n// 'level' is 1-7. For example, set outline level of column D in Sheet1 to 2:\n//\n//\terr := f.SetColOutlineLevel(\"Sheet1\", \"D\", 2)\nfunc (f *File) SetColOutlineLevel(sheet, col string, level uint8) error {\n\tif level > 7 || level < 1 {\n\t\treturn ErrOutlineLevel\n\t}\n\tcolNum, err := ColumnNameToNumber(col)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.setColOutlineLevel(colNum, level)\n\treturn err\n}\n\n// setColOutlineLevel provides a function to set the outline level of a single\n// column by given column number.\nfunc (ws *xlsxWorksheet) setColOutlineLevel(colNum int, level uint8) {\n\tcolData := xlsxCol{\n\t\tMin:          colNum,\n\t\tMax:          colNum,\n\t\tOutlineLevel: level,\n\t\tCustomWidth:  true,\n\t}\n\tif ws.Cols == nil {\n\t\tcols := xlsxCols{}\n\t\tcols.Col = append(cols.Col, colData)\n\t\tws.Cols = &cols\n\t\treturn\n\t}\n\tws.Cols.Col = flatCols(colData, ws.Cols.Col, func(fc, c xlsxCol) xlsxCol {\n\t\tfc.BestFit = c.BestFit\n\t\tfc.Collapsed = c.Collapsed\n\t\tfc.CustomWidth = c.CustomWidth\n\t\tfc.Hidden = c.Hidden\n\t\tfc.Phonetic = c.Phonetic\n\t\tfc.Style = c.Style\n\t\tfc.Width = c.Width\n\t\treturn fc\n\t})\n}\n\n// SetColStyle provides a function to set style of columns by given worksheet\n// name, columns range and style ID. This function is concurrency safe. Note\n// that this will overwrite the existing styles for the columns, it won't\n// append or merge style with existing styles.\n//\n// For example set style of column H on Sheet1:\n//\n//\terr = f.SetColStyle(\"Sheet1\", \"H\", style)\n//\n// Set style of columns C:F on Sheet1:\n//\n//\terr = f.SetColStyle(\"Sheet1\", \"C:F\", style)\nfunc (f *File) SetColStyle(sheet, columns string, styleID int) error {\n\tminVal, maxVal, err := f.parseColRange(columns)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.mu.Lock()\n\ts, err := f.stylesReader()\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn err\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn err\n\t}\n\tf.mu.Unlock()\n\ts.mu.Lock()\n\tif styleID < 0 || s.CellXfs == nil || len(s.CellXfs.Xf) <= styleID {\n\t\ts.mu.Unlock()\n\t\treturn newInvalidStyleID(styleID)\n\t}\n\ts.mu.Unlock()\n\tws.mu.Lock()\n\tws.setColStyle(minVal, maxVal, styleID)\n\tws.mu.Unlock()\n\tif rows := len(ws.SheetData.Row); rows > 0 {\n\t\tfor col := minVal; col <= maxVal; col++ {\n\t\t\tfrom, _ := CoordinatesToCellName(col, 1)\n\t\t\tto, _ := CoordinatesToCellName(col, rows)\n\t\t\terr = f.SetCellStyle(sheet, from, to, styleID)\n\t\t}\n\t}\n\treturn err\n}\n\n// setColStyle provides a function to set the style of a single column or\n// multiple columns.\nfunc (ws *xlsxWorksheet) setColStyle(minVal, maxVal, styleID int) {\n\tif ws.Cols == nil {\n\t\tws.Cols = &xlsxCols{}\n\t}\n\twidth := defaultColWidth\n\tif ws.SheetFormatPr != nil && ws.SheetFormatPr.DefaultColWidth > 0 {\n\t\twidth = ws.SheetFormatPr.DefaultColWidth\n\t}\n\tws.Cols.Col = flatCols(xlsxCol{\n\t\tMin:   minVal,\n\t\tMax:   maxVal,\n\t\tWidth: float64Ptr(width),\n\t\tStyle: styleID,\n\t}, ws.Cols.Col, func(fc, c xlsxCol) xlsxCol {\n\t\tfc.BestFit = c.BestFit\n\t\tfc.Collapsed = c.Collapsed\n\t\tfc.CustomWidth = c.CustomWidth\n\t\tfc.Hidden = c.Hidden\n\t\tfc.OutlineLevel = c.OutlineLevel\n\t\tfc.Phonetic = c.Phonetic\n\t\tfc.Width = c.Width\n\t\treturn fc\n\t})\n}\n\n// SetColWidth provides a function to set the width of a single column or\n// multiple columns. This function is concurrency safe. For example:\n//\n//\terr := f.SetColWidth(\"Sheet1\", \"A\", \"H\", 20)\nfunc (f *File) SetColWidth(sheet, startCol, endCol string, width float64) error {\n\tminVal, maxVal, err := f.parseColRange(startCol + \":\" + endCol)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif width > MaxColumnWidth {\n\t\treturn ErrColumnWidth\n\t}\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tws.setColWidth(minVal, maxVal, width)\n\treturn err\n}\n\n// setColWidth provides a function to set the width of a single column or\n// multiple columns.\nfunc (ws *xlsxWorksheet) setColWidth(minVal, maxVal int, width float64) {\n\tcol := xlsxCol{\n\t\tMin:         minVal,\n\t\tMax:         maxVal,\n\t\tWidth:       float64Ptr(width),\n\t\tCustomWidth: true,\n\t}\n\tif ws.Cols == nil {\n\t\tcols := xlsxCols{}\n\t\tcols.Col = append(cols.Col, col)\n\t\tws.Cols = &cols\n\t\treturn\n\t}\n\tws.Cols.Col = flatCols(col, ws.Cols.Col, func(fc, c xlsxCol) xlsxCol {\n\t\tfc.BestFit = c.BestFit\n\t\tfc.Collapsed = c.Collapsed\n\t\tfc.Hidden = c.Hidden\n\t\tfc.OutlineLevel = c.OutlineLevel\n\t\tfc.Phonetic = c.Phonetic\n\t\tfc.Style = c.Style\n\t\treturn fc\n\t})\n}\n\n// flatCols provides a method for the column's operation functions to flatten\n// and check the worksheet columns.\nfunc flatCols(col xlsxCol, cols []xlsxCol, replacer func(fc, c xlsxCol) xlsxCol) []xlsxCol {\n\tvar fc []xlsxCol\n\tfor i := col.Min; i <= col.Max; i++ {\n\t\tvar c xlsxCol\n\t\t_ = deepcopy.Copy(&c, col)\n\t\tc.Min, c.Max = i, i\n\t\tfc = append(fc, c)\n\t}\n\tinFlat := func(colID int, cols []xlsxCol) (int, bool) {\n\t\tfor idx, c := range cols {\n\t\t\tif c.Max == colID && c.Min == colID {\n\t\t\t\treturn idx, true\n\t\t\t}\n\t\t}\n\t\treturn -1, false\n\t}\n\tfor _, column := range cols {\n\t\tfor i := column.Min; i <= column.Max; i++ {\n\t\t\tif idx, ok := inFlat(i, fc); ok {\n\t\t\t\tfc[idx] = replacer(fc[idx], column)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar c xlsxCol\n\t\t\t_ = deepcopy.Copy(&c, column)\n\t\t\tc.Min, c.Max = i, i\n\t\t\tfc = append(fc, c)\n\t\t}\n\t}\n\treturn fc\n}\n\n// positionObjectPixels calculate the vertices that define the position of a\n// graphical object within the worksheet in pixels.\n//\n//\t      +------------+------------+\n//\t      |     A      |      B     |\n//\t+-----+------------+------------+\n//\t|     |(x1,y1)     |            |\n//\t|  1  |(A1)._______|______      |\n//\t|     |    |              |     |\n//\t|     |    |              |     |\n//\t+-----+----|    OBJECT    |-----+\n//\t|     |    |              |     |\n//\t|  2  |    |______________.     |\n//\t|     |            |        (B2)|\n//\t|     |            |     (x2,y2)|\n//\t+-----+------------+------------+\n//\n// Example of an object that covers some range reference from cell A1 to B2.\n//\n// Based on the width and height of the object we need to calculate 8 vars:\n//\n//\tcolStart, rowStart, colEnd, rowEnd, x1, y1, x2, y2.\n//\n// We also calculate the absolute x and y position of the top left vertex of\n// the object. This is required for images.\n//\n// The width and height of the cells that the object occupies can be\n// variable and have to be taken into account.\n//\n// The values of col_start and row_start are passed in from the calling\n// function. The values of col_end and row_end are calculated by\n// subtracting the width and height of the object from the width and\n// height of the underlying cells.\n//\n//\tcolStart        # Col containing upper left corner of object.\n//\tx1              # Distance to left side of object.\n//\n//\trowStart        # Row containing top left corner of object.\n//\ty1              # Distance to top of object.\n//\n//\tcolEnd          # Col containing lower right corner of object.\n//\tx2              # Distance to right side of object.\n//\n//\trowEnd          # Row containing bottom right corner of object.\n//\ty2              # Distance to bottom of object.\n//\n//\twidth           # Width of object frame.\n//\theight          # Height of object frame.\nfunc (f *File) positionObjectPixels(sheet string, col, row, width, height int, opts *GraphicOptions) (int, int, int, int, int, int, int, int) {\n\tcolIdx, rowIdx := col-1, row-1\n\t// Initialized end cell to the same as the start cell.\n\tcolEnd, rowEnd := colIdx, rowIdx\n\tx1, y1, x2, y2 := opts.OffsetX, opts.OffsetY, width, height\n\tif opts.Positioning != \"oneCell\" {\n\t\t// Using a twoCellAnchor, the maximum possible offset is limited by the\n\t\t// \"from\" cell dimensions. If these were to be exceeded the \"toPoint\" would\n\t\t// be calculated incorrectly, since the requested \"fromPoint\" is not possible\n\n\t\tx1 = min(x1, f.getColWidth(sheet, col))\n\t\ty1 = min(y1, f.getRowHeight(sheet, row))\n\n\t\tx2 += x1\n\t\ty2 += y1\n\t\t// Subtract the underlying cell widths to find end cell of the object.\n\t\tfor x2 >= f.getColWidth(sheet, colEnd+1) {\n\t\t\tcolEnd++\n\t\t\tx2 -= f.getColWidth(sheet, colEnd)\n\t\t}\n\n\t\t// Subtract the underlying cell heights to find end cell of the object.\n\t\tfor y2 >= f.getRowHeight(sheet, rowEnd+1) {\n\t\t\trowEnd++\n\t\t\ty2 -= f.getRowHeight(sheet, rowEnd)\n\t\t}\n\t}\n\t// The end vertices are whatever is left from the width and height.\n\treturn colIdx, rowIdx, colEnd, rowEnd, x1, y1, x2, y2\n}\n\n// getColWidth provides a function to get column width in pixels by given\n// sheet name and column number.\nfunc (f *File) getColWidth(sheet string, col int) int {\n\tws, _ := f.workSheetReader(sheet)\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tif ws.Cols != nil {\n\t\twidth := -1.0\n\t\tfor _, v := range ws.Cols.Col {\n\t\t\tif v.Min <= col && col <= v.Max && v.Width != nil {\n\t\t\t\twidth = *v.Width\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif width != -1.0 {\n\t\t\treturn int(convertColWidthToPixels(width))\n\t\t}\n\t}\n\tif ws.SheetFormatPr != nil {\n\t\tif ws.SheetFormatPr.DefaultColWidth > 0 {\n\t\t\treturn int(convertColWidthToPixels(ws.SheetFormatPr.DefaultColWidth))\n\t\t}\n\t\tif ws.SheetFormatPr.BaseColWidth > 0 {\n\t\t\treturn int(convertColWidthToPixels(float64(ws.SheetFormatPr.BaseColWidth))) + 5\n\t\t}\n\t}\n\t// Optimization for when the column widths haven't changed.\n\treturn int(defaultColWidthPixels)\n}\n\n// GetColStyle provides a function to get column style ID by given worksheet\n// name and column name. This function is concurrency safe.\nfunc (f *File) GetColStyle(sheet, col string) (int, error) {\n\tvar styleID int\n\tcolNum, err := ColumnNameToNumber(col)\n\tif err != nil {\n\t\treturn styleID, err\n\t}\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn styleID, err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tif ws.Cols != nil {\n\t\tfor _, v := range ws.Cols.Col {\n\t\t\tif v.Min <= colNum && colNum <= v.Max {\n\t\t\t\tstyleID = v.Style\n\t\t\t}\n\t\t}\n\t}\n\treturn styleID, err\n}\n\n// GetColWidth provides a function to get column width by given worksheet name\n// and column name. This function is concurrency safe.\nfunc (f *File) GetColWidth(sheet, col string) (float64, error) {\n\tcolNum, err := ColumnNameToNumber(col)\n\tif err != nil {\n\t\treturn defaultColWidth, err\n\t}\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn defaultColWidth, err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tif ws.Cols != nil {\n\t\tvar width float64\n\t\tfor _, v := range ws.Cols.Col {\n\t\t\tif v.Min <= colNum && colNum <= v.Max && v.Width != nil {\n\t\t\t\twidth = *v.Width\n\t\t\t}\n\t\t}\n\t\tif width != 0 {\n\t\t\treturn width, err\n\t\t}\n\t}\n\tif ws.SheetFormatPr != nil {\n\t\tif ws.SheetFormatPr.DefaultColWidth > 0 {\n\t\t\treturn ws.SheetFormatPr.DefaultColWidth, err\n\t\t}\n\t\tif ws.SheetFormatPr.BaseColWidth > 0 {\n\t\t\treturn float64(ws.SheetFormatPr.BaseColWidth), err\n\t\t}\n\t}\n\t// Optimization for when the column widths haven't changed.\n\treturn defaultColWidth, err\n}\n\n// InsertCols provides a function to insert new columns before the given column\n// name and number of columns. For example, create two columns before column\n// C in Sheet1:\n//\n//\terr := f.InsertCols(\"Sheet1\", \"C\", 2)\n//\n// Use this method with caution, which will affect changes in references such\n// as formulas, charts, and so on. If there is any referenced value of the\n// worksheet, it will cause a file error when you open it. The excelize only\n// partially updates these references currently.\nfunc (f *File) InsertCols(sheet, col string, n int) error {\n\tnum, err := ColumnNameToNumber(col)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif n < 1 || n > MaxColumns {\n\t\treturn ErrColumnNumber\n\t}\n\treturn f.adjustHelper(sheet, columns, num, n)\n}\n\n// RemoveCol provides a function to remove single column by given worksheet\n// name and column index. For example, remove column C in Sheet1:\n//\n//\terr := f.RemoveCol(\"Sheet1\", \"C\")\n//\n// Use this method with caution, which will affect changes in references such\n// as formulas, charts, and so on. If there is any referenced value of the\n// worksheet, it will cause a file error when you open it. The excelize only\n// partially updates these references currently.\nfunc (f *File) RemoveCol(sheet, col string) error {\n\tnum, err := ColumnNameToNumber(col)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.formulaSI.Clear()\n\tfor rowIdx := range ws.SheetData.Row {\n\t\trowData := &ws.SheetData.Row[rowIdx]\n\t\tfor colIdx := range rowData.C {\n\t\t\tcolName, _, _ := SplitCellName(rowData.C[colIdx].R)\n\t\t\tif colName == col {\n\t\t\t\trowData.C = append(rowData.C[:colIdx], rowData.C[colIdx+1:]...)[:len(rowData.C)-1]\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn f.adjustHelper(sheet, columns, num, -1)\n}\n\n// convertColWidthToPixels provides function to convert the width of a cell\n// from user's units to pixels. Excel rounds the column width to the nearest\n// pixel. If the width hasn't been set by the user we use the default value.\n// If the column is hidden it has a value of zero.\nfunc convertColWidthToPixels(width float64) float64 {\n\tvar pixels float64\n\tvar maxDigitWidth float64 = 8\n\tif width == 0 {\n\t\treturn pixels\n\t}\n\tpixels = (width*maxDigitWidth + 0.5)\n\treturn float64(int(pixels))\n}\n\n// calcTextWidth calculates the column width needed to display a text based on\n// the font family, font size, font weight and italic format.\nfunc (fnt *Font) calcTextWidth(text string) float64 {\n\tvar lowerUnits, upperUnits, wideUnits float64\n\tfor _, r := range text {\n\t\tswitch width.LookupRune(r).Kind() {\n\t\tcase width.EastAsianWide, width.EastAsianFullwidth:\n\t\t\twideUnits += 2\n\t\tdefault:\n\t\t\tif unicode.IsUpper(r) {\n\t\t\t\tupperUnits++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlowerUnits++\n\t\t}\n\t}\n\tsz := fnt.Size\n\tif sz <= 0 {\n\t\tsz = defaultFontSize\n\t}\n\tlowerFactor, upperFactor, wideFactor := 1.0, 1.0, 1.0\n\tif fw, ok := supportedFontWidthFactors[strings.ToLower(fnt.Family)]; ok {\n\t\tlowerFactor, upperFactor, wideFactor = fw[0], fw[1], fw[2]\n\t}\n\tw := (lowerUnits*lowerFactor + upperUnits*upperFactor + wideUnits*wideFactor) * (sz / defaultFontSize)\n\tif fnt.Bold {\n\t\tw *= 1.05\n\t}\n\tif fnt.Italic {\n\t\tw *= 1.05\n\t}\n\tif inStrSlice(supportedVertAlignTypes, fnt.VertAlign, true) != -1 {\n\t\tw *= 0.6\n\t}\n\treturn w\n}\n\n// calcRichTextWidth calculates the column width needed to display a rich text\n// based on the font format.\nfunc (fnt *Font) calcRichTextWidth(runs []RichTextRun) float64 {\n\tvar w, width float64\n\tfor _, run := range runs {\n\t\tif run.Font != nil {\n\t\t\tfnt = run.Font\n\t\t}\n\t\tif i := strings.IndexAny(run.Text, \"\\r\\n\"); i >= 0 {\n\t\t\tfirst := run.Text[:i]\n\t\t\trest := run.Text[i+1:]\n\t\t\tif w += fnt.calcTextWidth(first); w > width {\n\t\t\t\twidth = w\n\t\t\t}\n\t\t\tw = fnt.calcTextWidth(rest)\n\t\t\tcontinue\n\t\t}\n\t\tw += fnt.calcTextWidth(run.Text)\n\t}\n\tif w > width {\n\t\twidth = w\n\t}\n\treturn width\n}\n\n// autoFitColWidth provides a function to auto fit columns width according to\n// their text content with default font size and font.\nfunc (f *File) autoFitColWidth(sheet string, col, rows int, defaultFnt *Font) (float64, error) {\n\tvar width float64\n\tfor row := 1; row <= rows; row++ {\n\t\tcell, err := CoordinatesToCellName(col, row)\n\t\tif err != nil {\n\t\t\treturn width, err\n\t\t}\n\t\tval, err := f.CalcCellValue(sheet, cell)\n\t\tif err != nil && inStrSlice([]string{\n\t\t\tformulaErrorDIV,\n\t\t\tformulaErrorNAME,\n\t\t\tformulaErrorNA,\n\t\t\tformulaErrorNUM,\n\t\t\tformulaErrorVALUE,\n\t\t\tformulaErrorREF,\n\t\t\tformulaErrorNULL,\n\t\t\tformulaErrorSPILL,\n\t\t\tformulaErrorCALC,\n\t\t\tformulaErrorGETTINGDATA,\n\t\t}, err.Error(), true) != -1 {\n\t\t\tval = err.Error()\n\t\t}\n\t\tif val == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tstyleID, _ := f.GetCellStyle(sheet, cell)\n\t\tfnt := defaultFnt\n\t\tstyle, err := f.GetStyle(styleID)\n\t\tif err != nil {\n\t\t\treturn width, err\n\t\t}\n\t\tif style != nil && style.Font != nil {\n\t\t\tfnt = style.Font\n\t\t}\n\t\tif cellType, _ := f.GetCellType(sheet, cell); cellType == CellTypeInlineString || cellType == CellTypeSharedString {\n\t\t\truns, _ := f.GetCellRichText(sheet, cell)\n\t\t\tif w := fnt.calcRichTextWidth(runs); w > width {\n\t\t\t\twidth = w\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif w := fnt.calcTextWidth(val); w > width {\n\t\t\twidth = w\n\t\t}\n\t}\n\treturn width, nil\n}\n\n// AutoFitColWidth provides a function to auto fit columns width according to\n// their text content with font format. If the selected range contains hidden\n// columns and those columns have content, this function will unhide the hidden\n// columns. Not that this function calculates the width of the text\n// approximately based on the font format, currently does not support merged\n// cells. the actual width may be different when you open the workbook in Office\n// applications. This process can be relatively slow on large worksheets, so\n// this should normally only be called once per column, at the end of your\n// processing.\n//\n// For example, auto fit column width for column D on Sheet1:\n//\n//\terr := f.AutoFitColWidth(\"Sheet1\", \"D\")\n//\n// Auto fit column width for columns D to F on Sheet1:\n//\n//\terr := f.AutoFitColWidth(\"Sheet1\", \"D:F\")\nfunc (f *File) AutoFitColWidth(sheet, columns string) error {\n\tminVal, maxVal, err := f.parseColRange(columns)\n\tif err != nil {\n\t\treturn err\n\t}\n\trows, err := f.GetRows(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefaultFnt := &Font{}\n\tfont, err := f.readDefaultFont()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif font != nil {\n\t\tif font.Sz != nil && font.Sz.Val != nil {\n\t\t\tdefaultFnt.Size = *font.Sz.Val\n\t\t}\n\t\tif font.Name != nil && font.Name.Val != nil {\n\t\t\tdefaultFnt.Family = *font.Name.Val\n\t\t}\n\t}\n\tws, _ := f.workSheetReader(sheet)\n\tfor col := minVal; col <= maxVal; col++ {\n\t\tif width, _ := f.autoFitColWidth(sheet, col, len(rows), defaultFnt); width > 0 {\n\t\t\twidth += 2\n\t\t\tif width > MaxColumnWidth {\n\t\t\t\twidth = MaxColumnWidth\n\t\t\t}\n\t\t\tws.setColVisible(col, col, true)\n\t\t\tws.setColWidth(col, col, width)\n\t\t}\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "col_test.go",
    "content": "package excelize\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestCols(t *testing.T) {\n\tconst sheet2 = \"Sheet2\"\n\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\n\tcols, err := f.Cols(sheet2)\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\n\tvar collectedRows [][]string\n\tfor cols.Next() {\n\t\trows, err := cols.Rows()\n\t\tassert.NoError(t, err)\n\t\tcollectedRows = append(collectedRows, trimSliceSpace(rows))\n\t}\n\tif !assert.NoError(t, cols.Error()) {\n\t\tt.FailNow()\n\t}\n\n\treturnedColumns, err := f.GetCols(sheet2)\n\tassert.NoError(t, err)\n\tfor i := range returnedColumns {\n\t\treturnedColumns[i] = trimSliceSpace(returnedColumns[i])\n\t}\n\tif !assert.Equal(t, collectedRows, returnedColumns) {\n\t\tt.FailNow()\n\t}\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tcells := []string{\"C2\", \"C3\", \"C4\"}\n\tfor _, cell := range cells {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", cell, 1))\n\t}\n\t_, err = f.Rows(\"Sheet1\")\n\tassert.NoError(t, err)\n\n\tf.Sheet.Store(\"xl/worksheets/sheet1.xml\", &xlsxWorksheet{\n\t\tDimension: &xlsxDimension{\n\t\t\tRef: \"C2:C4\",\n\t\t},\n\t})\n\t_, err = f.Rows(\"Sheet1\")\n\tassert.NoError(t, err)\n\n\t// Test columns iterator with invalid sheet name\n\t_, err = f.Cols(\"Sheet:1\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\t// Test get columns cells with invalid sheet name\n\t_, err = f.GetCols(\"Sheet:1\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\t// Test columns iterator with unsupported charset shared strings table\n\tf.SharedStrings = nil\n\tf.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)\n\tcols, err = f.Cols(\"Sheet1\")\n\tassert.NoError(t, err)\n\tcols.Next()\n\t_, err = cols.Rows()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\n\tf = NewFile()\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(`<worksheet><sheetData><row r=\"A\"><c r=\"2\" t=\"inlineStr\"><is><t>B</t></is></c></row></sheetData></worksheet>`))\n\tf.checked = sync.Map{}\n\t_, err = f.Cols(\"Sheet1\")\n\tassert.EqualError(t, err, `strconv.Atoi: parsing \"A\": invalid syntax`)\n\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(`<worksheet><sheetData><row r=\"2\"><c r=\"A\" t=\"inlineStr\"><is><t>B</t></is></c></row></sheetData></worksheet>`))\n\t_, err = f.Cols(\"Sheet1\")\n\tassert.EqualError(t, err, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n}\n\nfunc TestColumnsIterator(t *testing.T) {\n\tsheetName, colCount, expectedNumCol := \"Sheet2\", 0, 9\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\trequire.NoError(t, err)\n\n\tcols, err := f.Cols(sheetName)\n\trequire.NoError(t, err)\n\n\tfor cols.Next() {\n\t\tcolCount++\n\t\trequire.True(t, colCount <= expectedNumCol, \"colCount is greater than expected\")\n\t}\n\tassert.Equal(t, expectedNumCol, colCount)\n\tassert.NoError(t, f.Close())\n\n\tf, sheetName, colCount, expectedNumCol = NewFile(), \"Sheet1\", 0, 4\n\tcells := []string{\"C2\", \"C3\", \"C4\", \"D2\", \"D3\", \"D4\"}\n\tfor _, cell := range cells {\n\t\tassert.NoError(t, f.SetCellValue(sheetName, cell, 1))\n\t}\n\tcols, err = f.Cols(sheetName)\n\trequire.NoError(t, err)\n\n\tfor cols.Next() {\n\t\tcolCount++\n\t\trequire.True(t, colCount <= 4, \"colCount is greater than expected\")\n\t}\n\tassert.Equal(t, expectedNumCol, colCount)\n}\n\nfunc TestColsError(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\t_, err = f.Cols(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestGetColsError(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\t_, err = f.GetCols(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(fmt.Sprintf(`<worksheet xmlns=\"%s\"><sheetData><row r=\"A\"><c r=\"2\" t=\"inlineStr\"><is><t>B</t></is></c></row></sheetData></worksheet>`, NameSpaceSpreadSheet.Value)))\n\tf.checked = sync.Map{}\n\t_, err = f.GetCols(\"Sheet1\")\n\tassert.EqualError(t, err, `strconv.Atoi: parsing \"A\": invalid syntax`)\n\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(fmt.Sprintf(`<worksheet xmlns=\"%s\"><sheetData><row r=\"2\"><c r=\"A\" t=\"inlineStr\"><is><t>B</t></is></c></row></sheetData></worksheet>`, NameSpaceSpreadSheet.Value)))\n\t_, err = f.GetCols(\"Sheet1\")\n\tassert.EqualError(t, err, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\n\tf = NewFile()\n\tcols, err := f.Cols(\"Sheet1\")\n\tassert.NoError(t, err)\n\tcols.totalRows = 2\n\tcols.totalCols = 2\n\tcols.curCol = 1\n\tcols.sheetXML = []byte(fmt.Sprintf(`<worksheet xmlns=\"%s\"><sheetData><row r=\"1\"><c r=\"A\" t=\"inlineStr\"><is><t>A</t></is></c></row></sheetData></worksheet>`, NameSpaceSpreadSheet.Value))\n\t_, err = cols.Rows()\n\tassert.EqualError(t, err, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", nil)\n\tf.Sheet.Store(\"xl/worksheets/sheet1.xml\", nil)\n\t_, err = f.Cols(\"Sheet1\")\n\tassert.NoError(t, err)\n}\n\nfunc TestColsRows(t *testing.T) {\n\tf := NewFile()\n\n\t_, err := f.Cols(\"Sheet1\")\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 1))\n\tf.Sheet.Store(\"xl/worksheets/sheet1.xml\", &xlsxWorksheet{\n\t\tDimension: &xlsxDimension{\n\t\t\tRef: \"A1:A1\",\n\t\t},\n\t})\n\n\tf = NewFile()\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", nil)\n\t_, err = f.Cols(\"Sheet1\")\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tf = NewFile()\n\tcols, err := f.Cols(\"Sheet1\")\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\t_, err = cols.Rows()\n\tassert.NoError(t, err)\n\tcols.stashCol, cols.curCol = 0, 1\n\t// Test if token is nil\n\tcols.sheetXML = nil\n\t_, err = cols.Rows()\n\tassert.NoError(t, err)\n}\n\nfunc TestColumnVisibility(t *testing.T) {\n\tt.Run(\"TestBook1\", func(t *testing.T) {\n\t\tf, err := prepareTestBook1()\n\t\tassert.NoError(t, err)\n\n\t\t// Hide/display a column with SetColVisible\n\t\tassert.NoError(t, f.SetColVisible(\"Sheet1\", \"F\", false))\n\t\tassert.NoError(t, f.SetColVisible(\"Sheet1\", \"F\", true))\n\t\tvisible, err := f.GetColVisible(\"Sheet1\", \"F\")\n\t\tassert.Equal(t, true, visible)\n\t\tassert.NoError(t, err)\n\n\t\t// Test hiding a few columns SetColVisible(...false)...\n\t\tassert.NoError(t, f.SetColVisible(\"Sheet1\", \"F:V\", false))\n\t\tvisible, err = f.GetColVisible(\"Sheet1\", \"F\")\n\t\tassert.Equal(t, false, visible)\n\t\tassert.NoError(t, err)\n\t\tvisible, err = f.GetColVisible(\"Sheet1\", \"U\")\n\t\tassert.Equal(t, false, visible)\n\t\tassert.NoError(t, err)\n\t\tvisible, err = f.GetColVisible(\"Sheet1\", \"V\")\n\t\tassert.Equal(t, false, visible)\n\t\tassert.NoError(t, err)\n\t\t// ...and displaying them back SetColVisible(...true)\n\t\tassert.NoError(t, f.SetColVisible(\"Sheet1\", \"V:F\", true))\n\t\tvisible, err = f.GetColVisible(\"Sheet1\", \"F\")\n\t\tassert.Equal(t, true, visible)\n\t\tassert.NoError(t, err)\n\t\tvisible, err = f.GetColVisible(\"Sheet1\", \"U\")\n\t\tassert.Equal(t, true, visible)\n\t\tassert.NoError(t, err)\n\t\tvisible, err = f.GetColVisible(\"Sheet1\", \"G\")\n\t\tassert.Equal(t, true, visible)\n\t\tassert.NoError(t, err)\n\n\t\t// Test get column visible on not exists worksheet\n\t\t_, err = f.GetColVisible(\"SheetN\", \"F\")\n\t\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t\t// Test get column visible with invalid sheet name\n\t\t_, err = f.GetColVisible(\"Sheet:1\", \"F\")\n\t\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\t\t// Test get column visible with illegal cell reference\n\t\t_, err = f.GetColVisible(\"Sheet1\", \"*\")\n\t\tassert.EqualError(t, err, newInvalidColumnNameError(\"*\").Error())\n\t\tassert.EqualError(t, f.SetColVisible(\"Sheet1\", \"*\", false), newInvalidColumnNameError(\"*\").Error())\n\t\t// Test set column visible with invalid sheet name\n\t\tassert.EqualError(t, f.SetColVisible(\"Sheet:1\", \"A\", false), ErrSheetNameInvalid.Error())\n\n\t\t_, err = f.NewSheet(\"Sheet3\")\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.SetColVisible(\"Sheet3\", \"E\", false))\n\t\tassert.EqualError(t, f.SetColVisible(\"Sheet1\", \"A:-1\", true), newInvalidColumnNameError(\"-1\").Error())\n\t\tassert.EqualError(t, f.SetColVisible(\"SheetN\", \"E\", false), \"sheet SheetN does not exist\")\n\t\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestColumnVisibility.xlsx\")))\n\t})\n\n\tt.Run(\"TestBook3\", func(t *testing.T) {\n\t\tf, err := prepareTestBook3()\n\t\tassert.NoError(t, err)\n\t\tvisible, err := f.GetColVisible(\"Sheet1\", \"B\")\n\t\tassert.Equal(t, true, visible)\n\t\tassert.NoError(t, err)\n\t})\n}\n\nfunc TestOutlineLevel(t *testing.T) {\n\tf := NewFile()\n\tlevel, err := f.GetColOutlineLevel(\"Sheet1\", \"D\")\n\tassert.Equal(t, uint8(0), level)\n\tassert.NoError(t, err)\n\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetColOutlineLevel(\"Sheet1\", \"D\", 4))\n\n\tlevel, err = f.GetColOutlineLevel(\"Sheet1\", \"D\")\n\tassert.Equal(t, uint8(4), level)\n\tassert.NoError(t, err)\n\n\tlevel, err = f.GetColOutlineLevel(\"SheetN\", \"A\")\n\tassert.Equal(t, uint8(0), level)\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\n\t// Test column outline level with invalid sheet name\n\t_, err = f.GetColOutlineLevel(\"Sheet:1\", \"A\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\n\tassert.NoError(t, f.SetColWidth(\"Sheet2\", \"A\", \"D\", 13))\n\tassert.EqualError(t, f.SetColWidth(\"Sheet2\", \"A\", \"D\", MaxColumnWidth+1), ErrColumnWidth.Error())\n\t// Test set column width with invalid sheet name\n\tassert.EqualError(t, f.SetColWidth(\"Sheet:1\", \"A\", \"D\", 13), ErrSheetNameInvalid.Error())\n\n\tassert.NoError(t, f.SetColOutlineLevel(\"Sheet2\", \"B\", 2))\n\tassert.NoError(t, f.SetRowOutlineLevel(\"Sheet1\", 2, 7))\n\tassert.EqualError(t, f.SetColOutlineLevel(\"Sheet1\", \"D\", 8), ErrOutlineLevel.Error())\n\tassert.EqualError(t, f.SetRowOutlineLevel(\"Sheet1\", 2, 8), ErrOutlineLevel.Error())\n\t// Test set row outline level on not exists worksheet\n\tassert.EqualError(t, f.SetRowOutlineLevel(\"SheetN\", 1, 4), \"sheet SheetN does not exist\")\n\t// Test set row outline level with invalid sheet name\n\tassert.EqualError(t, f.SetRowOutlineLevel(\"Sheet:1\", 1, 4), ErrSheetNameInvalid.Error())\n\t// Test get row outline level on not exists worksheet\n\t_, err = f.GetRowOutlineLevel(\"SheetN\", 1)\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get row outline level with invalid sheet name\n\t_, err = f.GetRowOutlineLevel(\"Sheet:1\", 1)\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\t// Test set and get column outline level with illegal cell reference\n\tassert.EqualError(t, f.SetColOutlineLevel(\"Sheet1\", \"*\", 1), newInvalidColumnNameError(\"*\").Error())\n\t_, err = f.GetColOutlineLevel(\"Sheet1\", \"*\")\n\tassert.EqualError(t, err, newInvalidColumnNameError(\"*\").Error())\n\n\t// Test set column outline level on not exists worksheet\n\tassert.EqualError(t, f.SetColOutlineLevel(\"SheetN\", \"E\", 2), \"sheet SheetN does not exist\")\n\n\tassert.EqualError(t, f.SetRowOutlineLevel(\"Sheet1\", 0, 1), newInvalidRowNumberError(0).Error())\n\tlevel, err = f.GetRowOutlineLevel(\"Sheet1\", 2)\n\tassert.NoError(t, err)\n\tassert.Equal(t, uint8(7), level)\n\n\t_, err = f.GetRowOutlineLevel(\"Sheet1\", 0)\n\tassert.EqualError(t, err, newInvalidRowNumberError(0).Error())\n\n\tlevel, err = f.GetRowOutlineLevel(\"Sheet1\", 10)\n\tassert.NoError(t, err)\n\tassert.Equal(t, uint8(0), level)\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestOutlineLevel.xlsx\")))\n\n\tf, err = OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetColOutlineLevel(\"Sheet2\", \"B\", 2))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestSetColStyle(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B2\", \"Hello\"))\n\n\tstyleID, err := f.NewStyle(&Style{Fill: Fill{Type: \"pattern\", Color: []string{\"94D3A2\"}, Pattern: 1}})\n\tassert.NoError(t, err)\n\t// Test set column style on not exists worksheet\n\tassert.EqualError(t, f.SetColStyle(\"SheetN\", \"E\", styleID), \"sheet SheetN does not exist\")\n\t// Test set column style with illegal column name\n\tassert.EqualError(t, f.SetColStyle(\"Sheet1\", \"*\", styleID), newInvalidColumnNameError(\"*\").Error())\n\tassert.EqualError(t, f.SetColStyle(\"Sheet1\", \"A:*\", styleID), newInvalidColumnNameError(\"*\").Error())\n\t// Test set column style with invalid style ID\n\tassert.EqualError(t, f.SetColStyle(\"Sheet1\", \"B\", -1), newInvalidStyleID(-1).Error())\n\t// Test set column style with not exists style ID\n\tassert.EqualError(t, f.SetColStyle(\"Sheet1\", \"B\", 10), newInvalidStyleID(10).Error())\n\t// Test set column style with invalid sheet name\n\tassert.EqualError(t, f.SetColStyle(\"Sheet:1\", \"A\", 0), ErrSheetNameInvalid.Error())\n\n\tassert.NoError(t, f.SetColStyle(\"Sheet1\", \"B\", styleID))\n\tstyle, err := f.GetColStyle(\"Sheet1\", \"B\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, styleID, style)\n\n\t// Test set column style with already exists column with style\n\tassert.NoError(t, f.SetColStyle(\"Sheet1\", \"B\", styleID))\n\tassert.NoError(t, f.SetColStyle(\"Sheet1\", \"D:C\", styleID))\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetData.Row[1].C[2].S = 0\n\tcellStyleID, err := f.GetCellStyle(\"Sheet1\", \"C2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, styleID, cellStyleID)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetColStyle.xlsx\")))\n\t// Test set column style with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetColStyle(\"Sheet1\", \"C:F\", styleID), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test set column style with worksheet properties columns default width settings\n\tf = NewFile()\n\tassert.NoError(t, f.SetSheetProps(\"Sheet1\", &SheetPropsOptions{DefaultColWidth: float64Ptr(20)}))\n\tstyle, err = f.NewStyle(&Style{Alignment: &Alignment{Vertical: \"center\"}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetColStyle(\"Sheet1\", \"A:Z\", style))\n\twidth, err := f.GetColWidth(\"Sheet1\", \"B\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, 20.0, width)\n}\n\nfunc TestColWidth(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetColWidth(\"Sheet1\", \"B\", \"A\", 12))\n\tassert.NoError(t, f.SetColWidth(\"Sheet1\", \"A\", \"B\", 12))\n\twidth, err := f.GetColWidth(\"Sheet1\", \"A\")\n\tassert.Equal(t, float64(12), width)\n\tassert.NoError(t, err)\n\twidth, err = f.GetColWidth(\"Sheet1\", \"C\")\n\tassert.Equal(t, defaultColWidth, width)\n\tassert.NoError(t, err)\n\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetFormatPr = &xlsxSheetFormatPr{DefaultColWidth: 10}\n\tws.(*xlsxWorksheet).Cols = nil\n\twidth, err = f.GetColWidth(\"Sheet1\", \"A\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, 10.0, width)\n\tassert.Equal(t, 80, f.getColWidth(\"Sheet1\", 1))\n\n\t// Test set and get column width with illegal cell reference\n\twidth, err = f.GetColWidth(\"Sheet1\", \"*\")\n\tassert.Equal(t, defaultColWidth, width)\n\tassert.EqualError(t, err, newInvalidColumnNameError(\"*\").Error())\n\tassert.EqualError(t, f.SetColWidth(\"Sheet1\", \"*\", \"B\", 1), newInvalidColumnNameError(\"*\").Error())\n\tassert.EqualError(t, f.SetColWidth(\"Sheet1\", \"A\", \"*\", 1), newInvalidColumnNameError(\"*\").Error())\n\n\t// Test set column width on not exists worksheet\n\tassert.EqualError(t, f.SetColWidth(\"SheetN\", \"B\", \"A\", 12), \"sheet SheetN does not exist\")\n\t// Test get column width on not exists worksheet\n\t_, err = f.GetColWidth(\"SheetN\", \"A\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get column width invalid sheet name\n\t_, err = f.GetColWidth(\"Sheet:1\", \"A\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestColWidth.xlsx\")))\n\tconvertRowHeightToPixels(0)\n}\n\nfunc TestGetColStyle(t *testing.T) {\n\tf := NewFile()\n\tstyleID, err := f.GetColStyle(\"Sheet1\", \"A\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, styleID, 0)\n\n\t// Test get column style on not exists worksheet\n\t_, err = f.GetColStyle(\"SheetN\", \"A\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get column style with illegal column name\n\t_, err = f.GetColStyle(\"Sheet1\", \"*\")\n\tassert.EqualError(t, err, newInvalidColumnNameError(\"*\").Error())\n\t// Test get column style with invalid sheet name\n\t_, err = f.GetColStyle(\"Sheet:1\", \"A\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n}\n\nfunc TestInsertCols(t *testing.T) {\n\tf := NewFile()\n\tsheet1 := f.GetSheetName(0)\n\n\tassert.NoError(t, fillCells(f, sheet1, 10, 10))\n\n\tassert.NoError(t, f.SetCellHyperLink(sheet1, \"A5\", \"https://github.com/xuri/excelize\", \"External\"))\n\tassert.NoError(t, f.MergeCell(sheet1, \"A1\", \"C3\"))\n\n\tassert.NoError(t, f.AutoFilter(sheet1, \"A2:B2\", []AutoFilterOptions{{Column: \"B\", Expression: \"x != blanks\"}}))\n\tassert.NoError(t, f.InsertCols(sheet1, \"A\", 1))\n\n\t// Test insert column with illegal cell reference\n\tassert.EqualError(t, f.InsertCols(sheet1, \"*\", 1), newInvalidColumnNameError(\"*\").Error())\n\t// Test insert column with invalid sheet name\n\tassert.EqualError(t, f.InsertCols(\"Sheet:1\", \"A\", 1), ErrSheetNameInvalid.Error())\n\tassert.EqualError(t, f.InsertCols(sheet1, \"A\", 0), ErrColumnNumber.Error())\n\tassert.EqualError(t, f.InsertCols(sheet1, \"A\", MaxColumns), ErrColumnNumber.Error())\n\tassert.EqualError(t, f.InsertCols(sheet1, \"A\", MaxColumns-10), ErrColumnNumber.Error())\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestInsertCols.xlsx\")))\n}\n\nfunc TestRemoveCol(t *testing.T) {\n\tf := NewFile()\n\tsheet1 := f.GetSheetName(0)\n\n\tassert.NoError(t, fillCells(f, sheet1, 10, 15))\n\n\tassert.NoError(t, f.SetCellHyperLink(sheet1, \"A5\", \"https://github.com/xuri/excelize\", \"External\"))\n\tassert.NoError(t, f.SetCellHyperLink(sheet1, \"C5\", \"https://github.com\", \"External\"))\n\n\tassert.NoError(t, f.MergeCell(sheet1, \"A1\", \"B1\"))\n\tassert.NoError(t, f.MergeCell(sheet1, \"A2\", \"B2\"))\n\n\tassert.NoError(t, f.RemoveCol(sheet1, \"A\"))\n\tassert.NoError(t, f.RemoveCol(sheet1, \"A\"))\n\n\t// Test remove column with illegal cell reference\n\tassert.EqualError(t, f.RemoveCol(\"Sheet1\", \"*\"), newInvalidColumnNameError(\"*\").Error())\n\t// Test remove column on not exists worksheet\n\tassert.EqualError(t, f.RemoveCol(\"SheetN\", \"B\"), \"sheet SheetN does not exist\")\n\t// Test remove column  with invalid sheet name\n\tassert.EqualError(t, f.RemoveCol(\"Sheet:1\", \"A\"), ErrSheetNameInvalid.Error())\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestRemoveCol.xlsx\")))\n}\n\nfunc TestConvertColWidthToPixels(t *testing.T) {\n\tassert.Equal(t, -7.0, convertColWidthToPixels(-1))\n}\n\nfunc TestAutoFitColWidth(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetColVisible(\"Sheet1\", \"A:C\", false))\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", \"SUBSTITUTE(\\\"1 \\\",\\\" \\\",\\\"\\\")\"))\n\tassert.NoError(t, f.AutoFitColWidth(\"Sheet1\", \"A:B\"))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"C1\", 1234567890))\n\tassert.NoError(t, f.AutoFitColWidth(\"Sheet1\", \"C\"))\n\tvisible, err := f.GetColVisible(\"Sheet1\", \"A\")\n\tassert.NoError(t, err)\n\tassert.True(t, visible)\n\tvisible, err = f.GetColVisible(\"Sheet1\", \"B\")\n\tassert.NoError(t, err)\n\tassert.False(t, visible)\n\tvisible, err = f.GetColVisible(\"Sheet1\", \"C\")\n\tassert.NoError(t, err)\n\tassert.True(t, visible)\n\tstyleID, err := f.NewStyle(&Style{Font: &Font{Family: \"Microsoft YaHei\", Bold: true, Italic: true, Charset: intPtr(134)}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetColStyle(\"Sheet1\", \"D\", styleID))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"D1\", 1234567890))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"D2\", \"文本1234567890\"))\n\tassert.NoError(t, f.AutoFitColWidth(\"Sheet1\", \"D\"))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"E1\", \"text\"))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"E2\", \"文本********\"))\n\tassert.NoError(t, f.AutoFitColWidth(\"Sheet1\", \"E\"))\n\tstyleID, err = f.NewStyle(&Style{Font: &Font{Size: 50}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"F1\", \"F1\", styleID))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"F1\", \"Text\"))\n\tassert.NoError(t, f.AutoFitColWidth(\"Sheet1\", \"F\"))\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"G1\", \"1/0\"))\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"G2\", \"1^\\\"text\\\"\"))\n\tassert.NoError(t, f.AutoFitColWidth(\"Sheet1\", \"G:G\"))\n\n\tnames := make([]string, 0, len(supportedFontWidthFactors))\n\tfor name := range supportedFontWidthFactors {\n\t\tnames = append(names, name)\n\t}\n\tsort.Strings(names)\n\tfor idx, name := range names {\n\t\tcol, err := ColumnNumberToName(idx + 8)\n\t\tassert.NoError(t, err)\n\t\tstyleID, err := f.NewStyle(&Style{Font: &Font{Family: name}})\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", col+\"1\", name))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", col+\"2\", strings.ToUpper(name)))\n\t\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", col+\"1\", col+\"2\", styleID))\n\t}\n\tassert.NoError(t, f.AutoFitColWidth(\"Sheet1\", \"H:KP\"))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"KQ1\", strings.Repeat(\"s\", TotalCellChars)))\n\tassert.NoError(t, f.AutoFitColWidth(\"Sheet1\", \"KQ\"))\n\tassert.NoError(t, err)\n\twidth, err := f.GetColWidth(\"Sheet1\", \"KQ\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, float64(MaxColumnWidth), width)\n\n\tassert.Equal(t, f.AutoFitColWidth(\"Sheet1\", \"\"), newInvalidColumnNameError(\"\"))\n\tassert.Equal(t, f.AutoFitColWidth(\"SheetN\", \"A\"), ErrSheetNotExist{\"SheetN\"})\n\t_, err = f.autoFitColWidth(\"Sheet1\", TotalRows, 1, &Font{})\n\tassert.Equal(t, ErrColumnNumber, err)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAutoFitColWidth.xlsx\")))\n\n\tf = NewFile()\n\t// Test auto fit column width with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AutoFitColWidth(\"Sheet1\", \"A\"), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test auto fit column width with invalid cell style ID\n\tf.Sheet.Store(\"xl/worksheets/sheet1.xml\", &xlsxWorksheet{\n\t\tSheetData: xlsxSheetData{Row: []xlsxRow{\n\t\t\t{R: 1, C: []xlsxC{{R: \"A1\", S: 1, V: \"Test\"}}},\n\t\t}},\n\t})\n\t_, err = f.autoFitColWidth(\"Sheet1\", 1, 1, &Font{})\n\tassert.Equal(t, err, newInvalidStyleID(1))\n}\n"
  },
  {
    "path": "crypt.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/md5\"\n\t\"crypto/rand\"\n\t\"crypto/sha1\"\n\t\"crypto/sha256\"\n\t\"crypto/sha512\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"encoding/xml\"\n\t\"hash\"\n\t\"math\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/richardlehane/mscfb\"\n\t\"golang.org/x/crypto/md4\"\n\t\"golang.org/x/crypto/ripemd160\"\n\t\"golang.org/x/text/encoding/unicode\"\n)\n\nvar (\n\tblockKey                    = []byte{0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6} // Block keys used for encryption\n\toleIdentifier               = []byte{0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1}\n\theaderCLSID                 = make([]byte, 16)\n\tdifSect                     = -4\n\tendOfChain                  = -2\n\tfatSect                     = -3\n\titerCount                   = 50000\n\tpackageEncryptionChunkSize  = 4096\n\tpackageOffset               = 8 // First 8 bytes are the size of the stream\n\tsheetProtectionSpinCount    = 1e5\n\tworkbookProtectionSpinCount = 1e5\n)\n\n// Encryption specifies the encryption structure, streams, and storages are\n// required when encrypting ECMA-376 documents.\ntype Encryption struct {\n\tXMLName       xml.Name      `xml:\"encryption\"`\n\tKeyData       KeyData       `xml:\"keyData\"`\n\tDataIntegrity DataIntegrity `xml:\"dataIntegrity\"`\n\tKeyEncryptors KeyEncryptors `xml:\"keyEncryptors\"`\n}\n\n// KeyData specifies the cryptographic attributes used to encrypt the data.\ntype KeyData struct {\n\tSaltSize        int    `xml:\"saltSize,attr\"`\n\tBlockSize       int    `xml:\"blockSize,attr\"`\n\tKeyBits         int    `xml:\"keyBits,attr\"`\n\tHashSize        int    `xml:\"hashSize,attr\"`\n\tCipherAlgorithm string `xml:\"cipherAlgorithm,attr\"`\n\tCipherChaining  string `xml:\"cipherChaining,attr\"`\n\tHashAlgorithm   string `xml:\"hashAlgorithm,attr\"`\n\tSaltValue       string `xml:\"saltValue,attr\"`\n}\n\n// DataIntegrity specifies the encrypted copies of the salt and hash values\n// used to help ensure that the integrity of the encrypted data has not been\n// compromised.\ntype DataIntegrity struct {\n\tEncryptedHmacKey   string `xml:\"encryptedHmacKey,attr\"`\n\tEncryptedHmacValue string `xml:\"encryptedHmacValue,attr\"`\n}\n\n// KeyEncryptors specifies the key encryptors used to encrypt the data.\ntype KeyEncryptors struct {\n\tKeyEncryptor []KeyEncryptor `xml:\"keyEncryptor\"`\n}\n\n// KeyEncryptor specifies that the schema used by this encryptor is the schema\n// specified for password-based encryptors.\ntype KeyEncryptor struct {\n\tXMLName      xml.Name     `xml:\"keyEncryptor\"`\n\tURI          string       `xml:\"uri,attr\"`\n\tEncryptedKey EncryptedKey `xml:\"encryptedKey\"`\n}\n\n// EncryptedKey used to generate the encrypting key.\ntype EncryptedKey struct {\n\tXMLName                    xml.Name `xml:\"http://schemas.microsoft.com/office/2006/keyEncryptor/password encryptedKey\"`\n\tSpinCount                  int      `xml:\"spinCount,attr\"`\n\tEncryptedVerifierHashInput string   `xml:\"encryptedVerifierHashInput,attr\"`\n\tEncryptedVerifierHashValue string   `xml:\"encryptedVerifierHashValue,attr\"`\n\tEncryptedKeyValue          string   `xml:\"encryptedKeyValue,attr\"`\n\tKeyData\n}\n\n// StandardEncryptionHeader structure is used by ECMA-376 document encryption\n// [ECMA-376] and Office binary document RC4 CryptoAPI encryption, to specify\n// encryption properties for an encrypted stream.\ntype StandardEncryptionHeader struct {\n\tFlags        uint32\n\tSizeExtra    uint32\n\tAlgID        uint32\n\tAlgIDHash    uint32\n\tKeySize      uint32\n\tProviderType uint32\n\tReserved1    uint32\n\tReserved2    uint32\n\tCspName      string\n}\n\n// StandardEncryptionVerifier structure is used by Office Binary Document RC4\n// CryptoAPI Encryption and ECMA-376 Document Encryption. Every usage of this\n// structure MUST specify the hashing algorithm and encryption algorithm used\n// in the EncryptionVerifier structure.\ntype StandardEncryptionVerifier struct {\n\tSaltSize              uint32\n\tSalt                  []byte\n\tEncryptedVerifier     []byte\n\tVerifierHashSize      uint32\n\tEncryptedVerifierHash []byte\n}\n\n// encryptionInfo structure is used for standard encryption with SHA1\n// cryptographic algorithm.\ntype encryption struct {\n\tBlockSize, SaltSize                                                                  int\n\tEncryptedKeyValue, EncryptedVerifierHashInput, EncryptedVerifierHashValue, SaltValue []byte\n\tKeyBits                                                                              uint32\n}\n\n// Decrypt API decrypts the CFB file format with ECMA-376 agile encryption and\n// standard encryption. Support cryptographic algorithm: MD4, MD5, RIPEMD-160,\n// SHA1, SHA256, SHA384 and SHA512 currently.\nfunc Decrypt(raw []byte, opts *Options) (packageBuf []byte, err error) {\n\tdoc, err := mscfb.New(bytes.NewReader(raw))\n\tif err != nil {\n\t\treturn\n\t}\n\tvar encryptionInfoBuf, encryptedPackageBuf []byte\n\tif encryptionInfoBuf, encryptedPackageBuf, err = extractPart(doc); err != nil {\n\t\treturn\n\t}\n\tmechanism, err := encryptionMechanism(encryptionInfoBuf)\n\tif err != nil || mechanism == \"extensible\" {\n\t\treturn\n\t}\n\tif mechanism == \"agile\" {\n\t\treturn agileDecrypt(encryptionInfoBuf, encryptedPackageBuf, opts)\n\t}\n\treturn standardDecrypt(encryptionInfoBuf, encryptedPackageBuf, opts)\n}\n\n// Encrypt API encrypt data with the password.\nfunc Encrypt(raw []byte, opts *Options) ([]byte, error) {\n\tencryptor := encryption{\n\t\tEncryptedVerifierHashInput: make([]byte, 16),\n\t\tEncryptedVerifierHashValue: make([]byte, 32),\n\t\tSaltValue:                  make([]byte, 16),\n\t\tBlockSize:                  16,\n\t\tKeyBits:                    128,\n\t\tSaltSize:                   16,\n\t}\n\t// Key Encryption\n\tencryptionInfoBuffer, err := encryptor.standardKeyEncryption(opts.Password)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Package Encryption\n\tencryptedPackage := make([]byte, 8)\n\tbinary.LittleEndian.PutUint64(encryptedPackage, uint64(len(raw)))\n\tencryptedPackage = append(encryptedPackage, encryptor.encrypt(raw)...)\n\t// Create a new CFB\n\tcompoundFile := &cfb{\n\t\tpaths:   []string{\"Root Entry/\"},\n\t\tsectors: []sector{{name: \"Root Entry\", typeID: 5}},\n\t}\n\tcompoundFile.put(\"EncryptionInfo\", encryptionInfoBuffer)\n\tcompoundFile.put(\"EncryptedPackage\", encryptedPackage)\n\treturn compoundFile.write(), nil\n}\n\n// extractPart extract data from storage by specified part name.\nfunc extractPart(doc *mscfb.Reader) ([]byte, []byte, error) {\n\tvar encryptionInfoBuf, encryptedPackageBuf []byte\n\tfor entry, err := doc.Next(); err == nil; entry, err = doc.Next() {\n\t\tswitch entry.Name {\n\t\tcase \"EncryptionInfo\":\n\t\t\tbuf := make([]byte, entry.Size)\n\t\t\tif _, err := doc.Read(buf); err != nil {\n\t\t\t\treturn encryptionInfoBuf, encryptedPackageBuf, err\n\t\t\t}\n\t\t\tencryptionInfoBuf = buf\n\t\tcase \"EncryptedPackage\":\n\t\t\tbuf := make([]byte, entry.Size)\n\t\t\tif _, err := doc.Read(buf); err != nil {\n\t\t\t\treturn encryptionInfoBuf, encryptedPackageBuf, err\n\t\t\t}\n\t\t\tencryptedPackageBuf = buf\n\t\t}\n\t}\n\treturn encryptionInfoBuf, encryptedPackageBuf, nil\n}\n\n// encryptionMechanism parse password-protected documents created mechanism.\nfunc encryptionMechanism(buffer []byte) (mechanism string, err error) {\n\tif len(buffer) < 4 {\n\t\terr = ErrUnknownEncryptMechanism\n\t\treturn\n\t}\n\tversionMajor, versionMinor := binary.LittleEndian.Uint16(buffer[:2]), binary.LittleEndian.Uint16(buffer[2:4])\n\tif versionMajor == 4 && versionMinor == 4 {\n\t\tmechanism = \"agile\"\n\t\treturn\n\t} else if (2 <= versionMajor && versionMajor <= 4) && versionMinor == 2 {\n\t\tmechanism = \"standard\"\n\t\treturn\n\t} else if (versionMajor == 3 || versionMajor == 4) && versionMinor == 3 {\n\t\tmechanism = \"extensible\"\n\t}\n\terr = ErrUnsupportedEncryptMechanism\n\treturn\n}\n\n// ECMA-376 Standard Encryption\n\n// standardDecrypt decrypt the CFB file format with ECMA-376 standard encryption.\nfunc standardDecrypt(encryptionInfoBuf, encryptedPackageBuf []byte, opts *Options) ([]byte, error) {\n\tencryptionHeaderSize := binary.LittleEndian.Uint32(encryptionInfoBuf[8:12])\n\tblock := encryptionInfoBuf[12 : 12+encryptionHeaderSize]\n\theader := StandardEncryptionHeader{\n\t\tFlags:        binary.LittleEndian.Uint32(block[:4]),\n\t\tSizeExtra:    binary.LittleEndian.Uint32(block[4:8]),\n\t\tAlgID:        binary.LittleEndian.Uint32(block[8:12]),\n\t\tAlgIDHash:    binary.LittleEndian.Uint32(block[12:16]),\n\t\tKeySize:      binary.LittleEndian.Uint32(block[16:20]),\n\t\tProviderType: binary.LittleEndian.Uint32(block[20:24]),\n\t\tReserved1:    binary.LittleEndian.Uint32(block[24:28]),\n\t\tReserved2:    binary.LittleEndian.Uint32(block[28:32]),\n\t\tCspName:      string(block[32:]),\n\t}\n\tblock = encryptionInfoBuf[12+encryptionHeaderSize:]\n\talgIDMap := map[uint32]string{\n\t\t0x0000660E: \"AES-128\",\n\t\t0x0000660F: \"AES-192\",\n\t\t0x00006610: \"AES-256\",\n\t}\n\talgorithm := \"AES\"\n\t_, ok := algIDMap[header.AlgID]\n\tif !ok {\n\t\talgorithm = \"RC4\"\n\t}\n\tverifier := standardEncryptionVerifier(algorithm, block)\n\tsecretKey, err := standardConvertPasswdToKey(header, verifier, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// decrypted data\n\tx := encryptedPackageBuf[8:]\n\tblob, err := aes.NewCipher(secretKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdecrypted := make([]byte, len(x))\n\tsize := 16\n\tfor bs, be := 0, size; bs < len(x); bs, be = bs+size, be+size {\n\t\tblob.Decrypt(decrypted[bs:be], x[bs:be])\n\t}\n\treturn decrypted, err\n}\n\n// standardEncryptionVerifier extract ECMA-376 standard encryption verifier.\nfunc standardEncryptionVerifier(algorithm string, blob []byte) StandardEncryptionVerifier {\n\tverifier := StandardEncryptionVerifier{\n\t\tSaltSize:          binary.LittleEndian.Uint32(blob[:4]),\n\t\tSalt:              blob[4:20],\n\t\tEncryptedVerifier: blob[20:36],\n\t\tVerifierHashSize:  binary.LittleEndian.Uint32(blob[36:40]),\n\t}\n\tswitch algorithm {\n\tcase \"RC4\":\n\t\tverifier.EncryptedVerifierHash = blob[40:60]\n\tcase \"AES\":\n\t\tverifier.EncryptedVerifierHash = blob[40:72]\n\t}\n\treturn verifier\n}\n\n// standardConvertPasswdToKey generate intermediate key from given password.\nfunc standardConvertPasswdToKey(header StandardEncryptionHeader, verifier StandardEncryptionVerifier, opts *Options) ([]byte, error) {\n\tencoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()\n\tpasswordBuffer, err := encoder.Bytes([]byte(opts.Password))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tkey := hashing(\"sha1\", verifier.Salt, passwordBuffer)\n\tfor i := 0; i < iterCount; i++ {\n\t\titerator := createUInt32LEBuffer(i, 4)\n\t\tkey = hashing(\"sha1\", iterator, key)\n\t}\n\tvar block int\n\thFinal := hashing(\"sha1\", key, createUInt32LEBuffer(block, 4))\n\tcbRequiredKeyLength := int(header.KeySize) / 8\n\tcbHash := sha1.Size\n\tbuf1 := bytes.Repeat([]byte{0x36}, 64)\n\tbuf1 = append(standardXORBytes(hFinal, buf1[:cbHash]), buf1[cbHash:]...)\n\tx1 := hashing(\"sha1\", buf1)\n\tbuf2 := bytes.Repeat([]byte{0x5c}, 64)\n\tbuf2 = append(standardXORBytes(hFinal, buf2[:cbHash]), buf2[cbHash:]...)\n\tx2 := hashing(\"sha1\", buf2)\n\tx3 := append(x1, x2...)\n\tkeyDerived := x3[:cbRequiredKeyLength]\n\treturn keyDerived, err\n}\n\n// standardXORBytes perform XOR operations for two bytes slice.\nfunc standardXORBytes(a, b []byte) []byte {\n\tr := make([][2]byte, len(a))\n\tfor i, e := range a {\n\t\tr[i] = [2]byte{e, b[i]}\n\t}\n\tbuf := make([]byte, len(a))\n\tfor p, q := range r {\n\t\tbuf[p] = q[0] ^ q[1]\n\t}\n\treturn buf\n}\n\n// encrypt provides a function to encrypt given value with AES cryptographic\n// algorithm.\nfunc (e *encryption) encrypt(input []byte) []byte {\n\tinputBytes := len(input)\n\tif pad := inputBytes % e.BlockSize; pad != 0 {\n\t\tinputBytes += e.BlockSize - pad\n\t}\n\tvar output, chunk []byte\n\tencryptedChunk := make([]byte, e.BlockSize)\n\tfor i := 0; i < inputBytes; i += e.BlockSize {\n\t\tif i+e.BlockSize <= len(input) {\n\t\t\tchunk = input[i : i+e.BlockSize]\n\t\t} else {\n\t\t\tchunk = input[i:]\n\t\t}\n\t\tchunk = append(chunk, make([]byte, e.BlockSize-len(chunk))...)\n\t\tc, _ := aes.NewCipher(e.EncryptedKeyValue)\n\t\tc.Encrypt(encryptedChunk, chunk)\n\t\toutput = append(output, encryptedChunk...)\n\t}\n\treturn output\n}\n\n// standardKeyEncryption encrypt convert the password to an encryption key.\nfunc (e *encryption) standardKeyEncryption(password string) ([]byte, error) {\n\tif countUTF16String(password) == 0 || countUTF16String(password) > MaxFieldLength {\n\t\treturn nil, ErrPasswordLengthInvalid\n\t}\n\tvar storage cfb\n\tstorage.writeUint16(0x0003)\n\tstorage.writeUint16(0x0002)\n\tstorage.writeUint32(0x24)\n\tstorage.writeUint32(0xA4)\n\tstorage.writeUint32(0x24)\n\tstorage.writeUint32(0x00)\n\tstorage.writeUint32(0x660E)\n\tstorage.writeUint32(0x8004)\n\tstorage.writeUint32(0x80)\n\tstorage.writeUint32(0x18)\n\tstorage.writeUint64(0x00)\n\tproviderName := \"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)\"\n\tstorage.writeStrings(providerName)\n\tstorage.writeUint16(0x00)\n\tstorage.writeUint32(0x10)\n\tkeyDataSaltValue, _ := randomBytes(16)\n\tverifierHashInput, _ := randomBytes(16)\n\te.SaltValue = keyDataSaltValue\n\te.EncryptedKeyValue, _ = standardConvertPasswdToKey(\n\t\tStandardEncryptionHeader{KeySize: e.KeyBits},\n\t\tStandardEncryptionVerifier{Salt: e.SaltValue},\n\t\t&Options{Password: password})\n\tverifierHashInputKey := hashing(\"sha1\", verifierHashInput)\n\te.EncryptedVerifierHashInput = e.encrypt(verifierHashInput)\n\te.EncryptedVerifierHashValue = e.encrypt(verifierHashInputKey)\n\tstorage.writeBytes(e.SaltValue)\n\tstorage.writeBytes(e.EncryptedVerifierHashInput)\n\tstorage.writeUint32(0x14)\n\tstorage.writeBytes(e.EncryptedVerifierHashValue)\n\tstorage.position = 0\n\treturn storage.stream, nil\n}\n\n// ECMA-376 Agile Encryption\n\n// agileDecrypt decrypt the CFB file format with ECMA-376 agile encryption.\n// Support cryptographic algorithm: MD4, MD5, RIPEMD-160, SHA1, SHA256,\n// SHA384 and SHA512.\nfunc agileDecrypt(encryptionInfoBuf, encryptedPackageBuf []byte, opts *Options) (packageBuf []byte, err error) {\n\tvar encryptionInfo Encryption\n\tif encryptionInfo, err = parseEncryptionInfo(encryptionInfoBuf[8:]); err != nil {\n\t\treturn\n\t}\n\t// Convert the password into an encryption key.\n\tkey, err := convertPasswdToKey(opts.Password, blockKey, encryptionInfo)\n\tif err != nil {\n\t\treturn\n\t}\n\t// Use the key to decrypt the package key.\n\tencryptedKey := encryptionInfo.KeyEncryptors.KeyEncryptor[0].EncryptedKey\n\tsaltValue, err := base64.StdEncoding.DecodeString(encryptedKey.SaltValue)\n\tif err != nil {\n\t\treturn\n\t}\n\tencryptedKeyValue, err := base64.StdEncoding.DecodeString(encryptedKey.EncryptedKeyValue)\n\tif err != nil {\n\t\treturn\n\t}\n\tpackageKey, _ := decrypt(key, saltValue, encryptedKeyValue)\n\t// Use the package key to decrypt the package.\n\treturn decryptPackage(packageKey, encryptedPackageBuf, encryptionInfo)\n}\n\n// convertPasswdToKey convert the password into an encryption key.\nfunc convertPasswdToKey(passwd string, blockKey []byte, encryption Encryption) (key []byte, err error) {\n\tvar b bytes.Buffer\n\tsaltValue, err := base64.StdEncoding.DecodeString(encryption.KeyEncryptors.KeyEncryptor[0].EncryptedKey.SaltValue)\n\tif err != nil {\n\t\treturn\n\t}\n\tb.Write(saltValue)\n\tencoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()\n\tpasswordBuffer, err := encoder.Bytes([]byte(passwd))\n\tif err != nil {\n\t\treturn\n\t}\n\tb.Write(passwordBuffer)\n\t// Generate the initial hash.\n\tkey = hashing(encryption.KeyData.HashAlgorithm, b.Bytes())\n\t// Now regenerate until spin count.\n\tfor i := 0; i < encryption.KeyEncryptors.KeyEncryptor[0].EncryptedKey.SpinCount; i++ {\n\t\titerator := createUInt32LEBuffer(i, 4)\n\t\tkey = hashing(encryption.KeyData.HashAlgorithm, iterator, key)\n\t}\n\t// Now generate the final hash.\n\tkey = hashing(encryption.KeyData.HashAlgorithm, key, blockKey)\n\t// Truncate or pad as needed to get to length of keyBits.\n\tkeyBytes := encryption.KeyEncryptors.KeyEncryptor[0].EncryptedKey.KeyBits / 8\n\tif len(key) < keyBytes {\n\t\ttmp := make([]byte, 0x36)\n\t\tkey = append(key, tmp...)\n\t} else if len(key) > keyBytes {\n\t\tkey = key[:keyBytes]\n\t}\n\treturn\n}\n\n// hashing data by specified hash algorithm.\nfunc hashing(hashAlgorithm string, buffer ...[]byte) (key []byte) {\n\thashMap := map[string]hash.Hash{\n\t\t\"md4\":        md4.New(),\n\t\t\"md5\":        md5.New(),\n\t\t\"ripemd-160\": ripemd160.New(),\n\t\t\"sha1\":       sha1.New(),\n\t\t\"sha256\":     sha256.New(),\n\t\t\"sha384\":     sha512.New384(),\n\t\t\"sha512\":     sha512.New(),\n\t}\n\thandler, ok := hashMap[strings.ToLower(hashAlgorithm)]\n\tif !ok {\n\t\treturn key\n\t}\n\tfor _, buf := range buffer {\n\t\t_, _ = handler.Write(buf)\n\t}\n\tkey = handler.Sum(nil)\n\treturn key\n}\n\n// createUInt32LEBuffer create buffer with little endian 32-bit unsigned\n// integer.\nfunc createUInt32LEBuffer(value int, bufferSize int) []byte {\n\tbuf := make([]byte, bufferSize)\n\tbinary.LittleEndian.PutUint32(buf, uint32(value))\n\treturn buf\n}\n\n// parseEncryptionInfo parse the encryption info XML into an object.\nfunc parseEncryptionInfo(encryptionInfo []byte) (encryption Encryption, err error) {\n\terr = xml.Unmarshal(encryptionInfo, &encryption)\n\treturn\n}\n\n// decrypt provides a function to decrypt input by given cipher algorithm,\n// cipher chaining, key and initialization vector.\nfunc decrypt(key, iv, input []byte) (packageKey []byte, err error) {\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn input, err\n\t}\n\tcipher.NewCBCDecrypter(block, iv).CryptBlocks(input, input)\n\treturn input, nil\n}\n\n// decryptPackage decrypt package by given packageKey and encryption\n// info.\nfunc decryptPackage(packageKey, input []byte, encryption Encryption) (outputChunks []byte, err error) {\n\tencryptedKey, offset := encryption.KeyData, packageOffset\n\tvar i, start, end int\n\tvar iv, outputChunk []byte\n\tfor end < len(input) {\n\t\tstart = end\n\t\tend = start + packageEncryptionChunkSize\n\n\t\tif end > len(input) {\n\t\t\tend = len(input)\n\t\t}\n\t\t// Grab the next chunk\n\t\tvar inputChunk []byte\n\t\tif (end + offset) < len(input) {\n\t\t\tinputChunk = input[start+offset : end+offset]\n\t\t} else {\n\t\t\tinputChunk = input[start+offset : end]\n\t\t}\n\n\t\t// Pad the chunk if it is not an integer multiple of the block size\n\t\tremainder := len(inputChunk) % encryptedKey.BlockSize\n\t\tif remainder != 0 {\n\t\t\tinputChunk = append(inputChunk, make([]byte, encryptedKey.BlockSize-remainder)...)\n\t\t}\n\t\t// Create the initialization vector\n\t\tiv, err = createIV(i, encryption)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t// Decrypt the chunk and add it to the array\n\t\toutputChunk, err = decrypt(packageKey, iv, inputChunk)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\toutputChunks = append(outputChunks, outputChunk...)\n\t\ti++\n\t}\n\treturn\n}\n\n// createIV create an initialization vector (IV).\nfunc createIV(blockKey interface{}, encryption Encryption) ([]byte, error) {\n\tencryptedKey := encryption.KeyData\n\t// Create the block key from the current index\n\tvar blockKeyBuf []byte\n\tif reflect.TypeOf(blockKey).Kind() == reflect.Int {\n\t\tblockKeyBuf = createUInt32LEBuffer(blockKey.(int), 4)\n\t} else {\n\t\tblockKeyBuf = blockKey.([]byte)\n\t}\n\tsaltValue, err := base64.StdEncoding.DecodeString(encryptedKey.SaltValue)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Create the initialization vector by hashing the salt with the block key.\n\t// Truncate or pad as needed to meet the block size.\n\tiv := hashing(encryptedKey.HashAlgorithm, append(saltValue, blockKeyBuf...))\n\tif len(iv) < encryptedKey.BlockSize {\n\t\ttmp := make([]byte, 0x36)\n\t\tiv = append(iv, tmp...)\n\t} else if len(iv) > encryptedKey.BlockSize {\n\t\tiv = iv[:encryptedKey.BlockSize]\n\t}\n\treturn iv, nil\n}\n\n// randomBytes returns securely generated random bytes. It will return an\n// error if the system's secure random number generator fails to function\n// correctly, in which case the caller should not continue.\nfunc randomBytes(n int) ([]byte, error) {\n\tb := make([]byte, n)\n\t_, err := rand.Read(b)\n\treturn b, err\n}\n\n// ISO Write Protection Method\n\n// genISOPasswdHash implements the ISO password hashing algorithm by given\n// plaintext password, name of the cryptographic hash algorithm, salt value\n// and spin count.\nfunc genISOPasswdHash(passwd, hashAlgorithm, salt string, spinCount int) (hashValue, saltValue string, err error) {\n\tif countUTF16String(passwd) < 1 || countUTF16String(passwd) > MaxFieldLength {\n\t\terr = ErrPasswordLengthInvalid\n\t\treturn\n\t}\n\talgorithmName, ok := map[string]string{\n\t\t\"MD4\":     \"md4\",\n\t\t\"MD5\":     \"md5\",\n\t\t\"SHA-1\":   \"sha1\",\n\t\t\"SHA-256\": \"sha256\",\n\t\t\"SHA-384\": \"sha384\",\n\t\t\"SHA-512\": \"sha512\",\n\t}[hashAlgorithm]\n\tif !ok {\n\t\terr = ErrUnsupportedHashAlgorithm\n\t\treturn\n\t}\n\tvar b bytes.Buffer\n\ts, _ := randomBytes(16)\n\tif salt != \"\" {\n\t\tif s, err = base64.StdEncoding.DecodeString(salt); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tb.Write(s)\n\tencoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()\n\tpasswordBuffer, _ := encoder.Bytes([]byte(passwd))\n\tb.Write(passwordBuffer)\n\t// Generate the initial hash.\n\tkey := hashing(algorithmName, b.Bytes())\n\t// Now regenerate until spin count.\n\tfor i := 0; i < spinCount; i++ {\n\t\titerator := createUInt32LEBuffer(i, 4)\n\t\tkey = hashing(algorithmName, key, iterator)\n\t}\n\thashValue, saltValue = base64.StdEncoding.EncodeToString(key), base64.StdEncoding.EncodeToString(s)\n\treturn\n}\n\n// Compound File Binary Implements\n\n// cfb structure is used for the compound file binary (CFB) file format writer.\ntype cfb struct {\n\tstream   []byte\n\tposition int\n\tpaths    []string\n\tsectors  []sector\n}\n\n// sector structure used for FAT, directory, miniFAT, and miniStream sectors.\ntype sector struct {\n\tclsID, content                             []byte\n\tname                                       string\n\tC, L, R, color, size, start, state, typeID int\n}\n\n// writeBytes write bytes in the stream by a given value with an offset.\nfunc (c *cfb) writeBytes(value []byte) {\n\tpos := c.position\n\tfor i := 0; i < len(value); i++ {\n\t\tfor j := len(c.stream); j <= i+pos; j++ {\n\t\t\tc.stream = append(c.stream, 0)\n\t\t}\n\t\tc.stream[i+pos] = value[i]\n\t}\n\tc.position = pos + len(value)\n}\n\n// writeUint16 write an uint16 data type bytes in the stream by a given value\n// with an offset.\nfunc (c *cfb) writeUint16(value int) {\n\tbuf := make([]byte, 2)\n\tbinary.LittleEndian.PutUint16(buf, uint16(value))\n\tc.writeBytes(buf)\n}\n\n// writeUint32 write an uint32 data type bytes in the stream by a given value\n// with an offset.\nfunc (c *cfb) writeUint32(value int) {\n\tbuf := make([]byte, 4)\n\tbinary.LittleEndian.PutUint32(buf, uint32(value))\n\tc.writeBytes(buf)\n}\n\n// writeUint64 write an uint64 data type bytes in the stream by a given value\n// with an offset.\nfunc (c *cfb) writeUint64(value int) {\n\tbuf := make([]byte, 8)\n\tbinary.LittleEndian.PutUint64(buf, uint64(value))\n\tc.writeBytes(buf)\n}\n\n// writeStrings write strings in the stream by a given value with an offset.\nfunc (c *cfb) writeStrings(value string) {\n\tencoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()\n\tbuffer, err := encoder.Bytes([]byte(value))\n\tif err != nil {\n\t\treturn\n\t}\n\tc.writeBytes(buffer)\n}\n\n// put provides a function to add an entry to compound file by given entry name\n// and raw bytes.\nfunc (c *cfb) put(name string, content []byte) {\n\tpath := c.paths[0]\n\tif len(path) <= len(name) && name[:len(path)] == path {\n\t\tpath = name\n\t} else {\n\t\tif len(path) > 0 && string(path[len(path)-1]) != \"/\" {\n\t\t\tpath += \"/\"\n\t\t}\n\t\tpath = strings.ReplaceAll(path+name, \"//\", \"/\")\n\t}\n\tfile := sector{name: path, typeID: 2, content: content, size: len(content)}\n\tc.sectors = append(c.sectors, file)\n\tc.paths = append(c.paths, path)\n}\n\n// compare provides a function to compare object path, each set of sibling\n// objects in one level of the containment hierarchy (all child objects under\n// a storage object) is represented as a red-black tree. The parent object of\n// this set of siblings will have a pointer to the top of this tree.\nfunc (c *cfb) compare(left, right string) int {\n\tL, R, i, j := strings.Split(left, \"/\"), strings.Split(right, \"/\"), 0, 0\n\tfor Z := int(math.Min(float64(len(L)), float64(len(R)))); i < Z; i++ {\n\t\tif j = len(L[i]) - len(R[i]); j != 0 {\n\t\t\treturn j\n\t\t}\n\t\tif L[i] != R[i] {\n\t\t\tif L[i] < R[i] {\n\t\t\t\treturn -1\n\t\t\t}\n\t\t\treturn 1\n\t\t}\n\t}\n\treturn len(L) - len(R)\n}\n\n// prepare provides a function to prepare object before write stream.\nfunc (c *cfb) prepare() {\n\ttype object struct {\n\t\tpath   string\n\t\tsector sector\n\t}\n\tvar objects []object\n\tfor i := 0; i < len(c.paths); i++ {\n\t\tif c.sectors[i].typeID == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tobjects = append(objects, object{path: c.paths[i], sector: c.sectors[i]})\n\t}\n\tsort.Slice(objects, func(i, j int) bool {\n\t\treturn c.compare(objects[i].path, objects[j].path) == 0\n\t})\n\tc.paths, c.sectors = []string{}, []sector{}\n\tfor i := 0; i < len(objects); i++ {\n\t\tc.paths = append(c.paths, objects[i].path)\n\t\tc.sectors = append(c.sectors, objects[i].sector)\n\t}\n\tfor i := 0; i < len(objects); i++ {\n\t\tsector, path := &c.sectors[i], c.paths[i]\n\t\tsector.name, sector.color = filepath.Base(path), 1\n\t\tsector.L, sector.R, sector.C = -1, -1, -1\n\t\tsector.size, sector.start = len(sector.content), 0\n\t\tif len(sector.clsID) == 0 {\n\t\t\tsector.clsID = headerCLSID\n\t\t}\n\t\tif i == 0 {\n\t\t\tsector.C = -1\n\t\t\tif len(objects) > 1 {\n\t\t\t\tsector.C = 1\n\t\t\t}\n\t\t\tsector.size, sector.typeID = 0, 5\n\t\t} else {\n\t\t\tif len(c.paths) > i+1 && filepath.Dir(c.paths[i+1]) == filepath.Dir(path) {\n\t\t\t\tsector.R = i + 1\n\t\t\t}\n\t\t\tsector.typeID = 2\n\t\t}\n\t}\n}\n\n// locate provides a function to locate sectors location and size of the\n// compound file.\nfunc (c *cfb) locate() []int {\n\tvar miniStreamSectorSize, FATSectorSize int\n\tfor i := 0; i < len(c.sectors); i++ {\n\t\tsector := c.sectors[i]\n\t\tif len(sector.content) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tsize := len(sector.content)\n\t\tif size > 0 {\n\t\t\tif size < 0x1000 {\n\t\t\t\tminiStreamSectorSize += (size + 0x3F) >> 6\n\t\t\t} else {\n\t\t\t\tFATSectorSize += (size + 0x01FF) >> 9\n\t\t\t}\n\t\t}\n\t}\n\tdirectorySectors := (len(c.paths) + 3) >> 2\n\tminiStreamSectors := (miniStreamSectorSize + 7) >> 3\n\tminiFATSectors := (miniStreamSectorSize + 0x7F) >> 7\n\tsectors := miniStreamSectors + FATSectorSize + directorySectors + miniFATSectors\n\tFATSectors := (sectors + 0x7F) >> 7\n\tDIFATSectors := 0\n\tif FATSectors > 109 {\n\t\tDIFATSectors = int(math.Ceil((float64(FATSectors) - 109) / 0x7F))\n\t}\n\tfor ((sectors + FATSectors + DIFATSectors + 0x7F) >> 7) > FATSectors {\n\t\tFATSectors++\n\t\tif FATSectors <= 109 {\n\t\t\tDIFATSectors = 0\n\t\t} else {\n\t\t\tDIFATSectors = int(math.Ceil((float64(FATSectors) - 109) / 0x7F))\n\t\t}\n\t}\n\tlocation := []int{1, DIFATSectors, FATSectors, miniFATSectors, directorySectors, FATSectorSize, miniStreamSectorSize, 0}\n\tc.sectors[0].size = miniStreamSectorSize << 6\n\tc.sectors[0].start = location[0] + location[1] + location[2] + location[3] + location[4] + location[5]\n\tlocation[7] = c.sectors[0].start + ((location[6] + 7) >> 3)\n\treturn location\n}\n\n// writeMSAT provides a function to write compound file master sector allocation\n// table.\nfunc (c *cfb) writeMSAT(location []int) {\n\tvar i, offset int\n\tfor i = 0; i < 109; i++ {\n\t\tif i < location[2] {\n\t\t\tc.writeUint32(location[1] + i)\n\t\t} else {\n\t\t\tc.writeUint32(-1)\n\t\t}\n\t}\n\tif location[1] != 0 {\n\t\tfor offset = 0; offset < location[1]; offset++ {\n\t\t\tfor ; i < 236+offset*127; i++ {\n\t\t\t\tif i < location[2] {\n\t\t\t\t\tc.writeUint32(location[1] + i)\n\t\t\t\t} else {\n\t\t\t\t\tc.writeUint32(-1)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif offset == location[1]-1 {\n\t\t\t\tc.writeUint32(endOfChain)\n\t\t\t} else {\n\t\t\t\tc.writeUint32(offset + 1)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// writeDirectoryEntry provides a function to write compound file directory\n// entries. The directory entry array is an array of directory entries that\n// are grouped into a directory sector. Each storage object or stream object\n// within a compound file is represented by a single directory entry. The\n// space for the directory sectors that are holding the array is allocated\n// from the FAT.\nfunc (c *cfb) writeDirectoryEntry(location []int) {\n\tvar sector sector\n\tvar j, sectorSize int\n\tfor i := 0; i < location[4]<<2; i++ {\n\t\tvar path string\n\t\tif i < len(c.paths) {\n\t\t\tpath = c.paths[i]\n\t\t}\n\t\tif i >= len(c.paths) || len(path) == 0 {\n\t\t\tfor j = 0; j < 17; j++ {\n\t\t\t\tc.writeUint32(0)\n\t\t\t}\n\t\t\tfor j = 0; j < 3; j++ {\n\t\t\t\tc.writeUint32(-1)\n\t\t\t}\n\t\t\tfor j = 0; j < 12; j++ {\n\t\t\t\tc.writeUint32(0)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tsector = c.sectors[i]\n\t\tif i == 0 {\n\t\t\tif sector.size > 0 {\n\t\t\t\tsector.start = sector.start - 1\n\t\t\t} else {\n\t\t\t\tsector.start = endOfChain\n\t\t\t}\n\t\t}\n\t\tname := sector.name\n\t\tsectorSize = 2 * (len(name) + 1)\n\t\tc.writeStrings(name)\n\t\tc.position += 64 - 2*(len(name))\n\t\tc.writeUint16(sectorSize)\n\t\tc.writeBytes([]byte(string(rune(sector.typeID))))\n\t\tc.writeBytes([]byte(string(rune(sector.color))))\n\t\tc.writeUint32(sector.L)\n\t\tc.writeUint32(sector.R)\n\t\tc.writeUint32(sector.C)\n\t\tif len(sector.clsID) == 0 {\n\t\t\tfor j = 0; j < 4; j++ {\n\t\t\t\tc.writeUint32(0)\n\t\t\t}\n\t\t} else {\n\t\t\tc.writeBytes(sector.clsID)\n\t\t}\n\t\tc.writeUint32(sector.state)\n\t\tc.writeUint32(0)\n\t\tc.writeUint32(0)\n\t\tc.writeUint32(0)\n\t\tc.writeUint32(0)\n\t\tc.writeUint32(sector.start)\n\t\tc.writeUint32(sector.size)\n\t\tc.writeUint32(0)\n\t}\n}\n\n// writeSectorChains provides a function to write compound file sector chains.\nfunc (c *cfb) writeSectorChains(location []int) sector {\n\tvar i, j, offset, sectorSize int\n\twriteSectorChain := func(head, offset int) int {\n\t\tfor offset += head; i < offset-1; i++ {\n\t\t\tc.writeUint32(i + 1)\n\t\t}\n\t\tif head != 0 {\n\t\t\ti++\n\t\t\tc.writeUint32(endOfChain)\n\t\t}\n\t\treturn offset\n\t}\n\tfor offset += location[1]; i < offset; i++ {\n\t\tc.writeUint32(difSect)\n\t}\n\tfor offset += location[2]; i < offset; i++ {\n\t\tc.writeUint32(fatSect)\n\t}\n\toffset = writeSectorChain(location[3], offset)\n\toffset = writeSectorChain(location[4], offset)\n\tsector := c.sectors[0]\n\tfor ; j < len(c.sectors); j++ {\n\t\tif sector = c.sectors[j]; len(sector.content) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif sectorSize = len(sector.content); sectorSize < 0x1000 {\n\t\t\tcontinue\n\t\t}\n\t\tc.sectors[j].start = offset\n\t\toffset = writeSectorChain((sectorSize+0x01FF)>>9, offset)\n\t}\n\twriteSectorChain((location[6]+7)>>3, offset)\n\tfor c.position&0x1FF != 0 {\n\t\tc.writeUint32(endOfChain)\n\t}\n\ti, offset = 0, 0\n\tfor j = 0; j < len(c.sectors); j++ {\n\t\tif sector = c.sectors[j]; len(sector.content) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif sectorSize = len(sector.content); sectorSize == 0 || sectorSize >= 0x1000 {\n\t\t\tcontinue\n\t\t}\n\t\tsector.start = offset\n\t\toffset = writeSectorChain((sectorSize+0x3F)>>6, offset)\n\t}\n\tfor c.position&0x1FF != 0 {\n\t\tc.writeUint32(endOfChain)\n\t}\n\treturn sector\n}\n\n// write provides a function to create compound file package stream.\nfunc (c *cfb) write() []byte {\n\tc.prepare()\n\tlocation := c.locate()\n\tc.stream = make([]byte, location[7]<<9)\n\tvar i, j int\n\tfor i = 0; i < 8; i++ {\n\t\tc.writeBytes([]byte{oleIdentifier[i]})\n\t}\n\tc.writeBytes(make([]byte, 16))\n\tc.writeUint16(0x003E)\n\tc.writeUint16(0x0003)\n\tc.writeUint16(0xFFFE)\n\tc.writeUint16(0x0009)\n\tc.writeUint16(0x0006)\n\tc.writeBytes(make([]byte, 10))\n\tc.writeUint32(location[2])\n\tc.writeUint32(location[0] + location[1] + location[2] + location[3] - 1)\n\tc.writeUint32(0)\n\tc.writeUint32(1 << 12)\n\tif location[3] != 0 {\n\t\tc.writeUint32(location[0] + location[1] + location[2] - 1)\n\t} else {\n\t\tc.writeUint32(endOfChain)\n\t}\n\tc.writeUint32(location[3])\n\tif location[1] != 0 {\n\t\tc.writeUint32(location[0] - 1)\n\t} else {\n\t\tc.writeUint32(endOfChain)\n\t}\n\tc.writeUint32(location[1])\n\tc.writeMSAT(location)\n\tsector := c.writeSectorChains(location)\n\tc.writeDirectoryEntry(location)\n\tfor i = 1; i < len(c.sectors); i++ {\n\t\tsector = c.sectors[i]\n\t\tif sector.size >= 0x1000 {\n\t\t\tc.position = (sector.start + 1) << 9\n\t\t\tfor j = 0; j < sector.size; j++ {\n\t\t\t\tc.writeBytes([]byte{sector.content[j]})\n\t\t\t}\n\t\t\tfor ; j&0x1FF != 0; j++ {\n\t\t\t\tc.writeBytes([]byte{0})\n\t\t\t}\n\t\t}\n\t}\n\tfor i = 1; i < len(c.sectors); i++ {\n\t\tsector = c.sectors[i]\n\t\tif sector.size > 0 && sector.size < 0x1000 {\n\t\t\tfor j = 0; j < sector.size; j++ {\n\t\t\t\tc.writeBytes([]byte{sector.content[j]})\n\t\t\t}\n\t\t\tfor ; j&0x3F != 0; j++ {\n\t\t\t\tc.writeBytes([]byte{0})\n\t\t\t}\n\t\t}\n\t}\n\tfor c.position < len(c.stream) {\n\t\tc.writeBytes([]byte{0})\n\t}\n\treturn c.stream\n}\n"
  },
  {
    "path": "crypt_test.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/richardlehane/mscfb\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestEncrypt(t *testing.T) {\n\t// Test decrypt spreadsheet with incorrect password\n\t_, err := OpenFile(filepath.Join(\"test\", \"encryptSHA1.xlsx\"), Options{Password: \"passwd\"})\n\tassert.EqualError(t, err, ErrWorkbookPassword.Error())\n\t// Test decrypt spreadsheet with password\n\tf, err := OpenFile(filepath.Join(\"test\", \"encryptSHA1.xlsx\"), Options{Password: \"password\"})\n\tassert.NoError(t, err)\n\tcell, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"SECRET\", cell)\n\tassert.NoError(t, f.Close())\n\t// Test decrypt spreadsheet with unsupported encrypt mechanism\n\traw, err := os.ReadFile(filepath.Join(\"test\", \"encryptAES.xlsx\"))\n\tassert.NoError(t, err)\n\traw[2050] = 3\n\t_, err = Decrypt(raw, &Options{Password: \"password\"})\n\tassert.Equal(t, ErrUnsupportedEncryptMechanism, err)\n\n\t// Test encrypt spreadsheet with invalid password\n\tassert.EqualError(t, f.SaveAs(filepath.Join(\"test\", \"Encryption.xlsx\"), Options{Password: strings.Repeat(\"*\", MaxFieldLength+1)}), ErrPasswordLengthInvalid.Error())\n\t// Test encrypt spreadsheet with new password\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"Encryption.xlsx\"), Options{Password: \"passwd\"}))\n\tassert.NoError(t, f.Close())\n\tf, err = OpenFile(filepath.Join(\"test\", \"Encryption.xlsx\"), Options{Password: \"passwd\"})\n\tassert.NoError(t, err)\n\tcell, err = f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"SECRET\", cell)\n\t// Test remove password by save workbook with options\n\tassert.NoError(t, f.Save(Options{Password: \"\"}))\n\tassert.NoError(t, f.Close())\n\n\tdoc, err := mscfb.New(bytes.NewReader(raw))\n\tassert.NoError(t, err)\n\tencryptionInfoBuf, encryptedPackageBuf, err := extractPart(doc)\n\tassert.NoError(t, err)\n\tbinary.LittleEndian.PutUint64(encryptionInfoBuf[20:32], uint64(0))\n\t_, err = standardDecrypt(encryptionInfoBuf, encryptedPackageBuf, &Options{Password: \"password\"})\n\tassert.NoError(t, err)\n\t_, err = decrypt(nil, nil, nil)\n\tassert.EqualError(t, err, \"crypto/aes: invalid key size 0\")\n\t_, err = agileDecrypt(encryptionInfoBuf, MacintoshCyrillicCharset, &Options{Password: \"password\"})\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid character entity &0 (no semicolon)\")\n\t_, err = convertPasswdToKey(\"password\", nil, Encryption{\n\t\tKeyEncryptors: KeyEncryptors{KeyEncryptor: []KeyEncryptor{\n\t\t\t{EncryptedKey: EncryptedKey{KeyData: KeyData{SaltValue: \"==\"}}},\n\t\t}},\n\t})\n\tassert.EqualError(t, err, \"illegal base64 data at input byte 0\")\n\t_, err = createIV([]byte{0}, Encryption{KeyData: KeyData{SaltValue: \"==\"}})\n\tassert.EqualError(t, err, \"illegal base64 data at input byte 0\")\n\t// Test error handling for EncryptionInfo parse failure\n\tcompoundFile := &cfb{\n\t\tpaths:   []string{\"Root Entry/\"},\n\t\tsectors: []sector{{name: \"Root Entry\", typeID: 5}},\n\t}\n\tcompoundFile.put(\"EncryptionInfo\", []byte{})\n\t_, err = OpenReader(bytes.NewReader(compoundFile.write()))\n\tassert.Equal(t, ErrWorkbookFileFormat, err)\n\t// Test error handling for EncryptedPackage parse failure\n\tcompoundFile = &cfb{\n\t\tpaths:   []string{\"Root Entry/\"},\n\t\tsectors: []sector{{name: \"Root Entry\", typeID: 5}},\n\t}\n\tencryptionInfo := make([]byte, 100)\n\tbinary.LittleEndian.PutUint16(encryptionInfo[:2], 4)\n\tbinary.LittleEndian.PutUint16(encryptionInfo[2:4], 4)\n\tcompoundFile.put(\"EncryptionInfo\", encryptionInfo)\n\tcompoundFile.put(\"EncryptedPackage\", []byte{})\n\t_, err = OpenReader(bytes.NewReader(compoundFile.write()))\n\tassert.Equal(t, ErrWorkbookFileFormat, err)\n\t// Test createIV when iv length is less than block size\n\t_, err = createIV(0, Encryption{\n\t\tKeyData: KeyData{\n\t\t\tHashAlgorithm: \"md5\",\n\t\t\tBlockSize:     32,\n\t\t\tSaltValue:     base64.StdEncoding.EncodeToString([]byte(\"\")),\n\t\t},\n\t})\n\tassert.NoError(t, err)\n\t// Test decryptPackage error with padding\n\tinput := make([]byte, 18)\n\tbinary.LittleEndian.PutUint64(input[:8], 10)\n\tfor i := 8; i < 18; i++ {\n\t\tinput[i] = byte(i)\n\t}\n\t_, err = decryptPackage(make([]byte, 32), input, Encryption{\n\t\tKeyData: KeyData{\n\t\t\tHashAlgorithm: \"sha256\",\n\t\t\tBlockSize:     16,\n\t\t\tSaltValue:     base64.StdEncoding.EncodeToString([]byte(\"\")),\n\t\t},\n\t})\n\tassert.NoError(t, err)\n\t// Test IV creation error with invalid salt\n\tinput = make([]byte, 4104)\n\tbinary.LittleEndian.PutUint64(input[:8], 4096)\n\t_, err = decryptPackage(make([]byte, 32), input, Encryption{\n\t\tKeyData: KeyData{\n\t\t\tHashAlgorithm: \"sha256\",\n\t\t\tBlockSize:     16,\n\t\t\tSaltValue:     \"==\",\n\t\t},\n\t})\n\tassert.Error(t, err)\n\t// Test decrypt error with invalid key\n\t_, err = decryptPackage([]byte{}, input, Encryption{\n\t\tKeyData: KeyData{\n\t\t\tHashAlgorithm: \"sha256\",\n\t\t\tBlockSize:     16,\n\t\t\tSaltValue:     base64.StdEncoding.EncodeToString([]byte(\"\")),\n\t\t},\n\t})\n\tassert.Error(t, err)\n\t// Test put with path that is a prefix of name\n\tcompoundFile = &cfb{\n\t\tpaths:   []string{\"Root Entry/\"},\n\t\tsectors: []sector{{name: \"Root Entry\", typeID: 5}},\n\t}\n\tcompoundFile.put(\"Root Entry/Test\", []byte(\"\"))\n\tcompoundFile = &cfb{\n\t\tpaths:   []string{\"Root\"},\n\t\tsectors: []sector{{name: \"Root\", typeID: 5}},\n\t}\n\tcompoundFile.put(\"Test\", []byte(\"\"))\n\t// Test compare function with different scenarios\n\tcompoundFile = &cfb{}\n\tassert.Equal(t, -1, compoundFile.compare(\"Root/A\", \"Root/B\"))\n\tassert.Equal(t, 1, compoundFile.compare(\"Root/B\", \"Root/A\"))\n\tassert.Equal(t, -1, compoundFile.compare(\"Root\", \"Root/Child\"))\n\t// Test prepare with typeID == 0 sector\n\tcompoundFile = &cfb{\n\t\tpaths: []string{\"Root Entry/\", \"Skip/\", \"Valid/\"},\n\t\tsectors: []sector{\n\t\t\t{name: \"Root Entry\", typeID: 5},\n\t\t\t{name: \"Skip\", typeID: 0},\n\t\t\t{name: \"Valid\", typeID: 2},\n\t\t},\n\t}\n\tcompoundFile.prepare()\n\t// Test locate with FATSectors incrementing but staying <= 109\n\tcompoundFile = &cfb{\n\t\tpaths:   []string{\"Root Entry/\"},\n\t\tsectors: []sector{{name: \"Root Entry\", typeID: 5, content: []byte{}}},\n\t}\n\tnumFiles := 1533\n\tfor i := range numFiles {\n\t\tname := strings.Repeat(\"F\", i%50+1)\n\t\tcompoundFile.paths = append(compoundFile.paths, name+\"/\")\n\t\tcompoundFile.sectors = append(compoundFile.sectors, sector{\n\t\t\tname:    name,\n\t\t\ttypeID:  2,\n\t\t\tcontent: make([]byte, 4096),\n\t\t})\n\t}\n\tcompoundFile.locate()\n\t// Test writeDirectoryEntry with empty clsID\n\tcompoundFile = &cfb{\n\t\tpaths: []string{\"Root Entry/\", \"File1/\"},\n\t\tsectors: []sector{\n\t\t\t{name: \"Root Entry\", typeID: 5, content: []byte{}, clsID: headerCLSID},\n\t\t\t{name: \"File1\", typeID: 2, content: []byte(\"test\"), clsID: []byte{}},\n\t\t},\n\t}\n\tcompoundFile.stream = make([]byte, 10000)\n\tcompoundFile.writeDirectoryEntry([]int{1, 0, 1, 0, 1, 0, 0, 0})\n}\n\nfunc TestEncryptionMechanism(t *testing.T) {\n\tmechanism, err := encryptionMechanism([]byte{3, 0, 3, 0})\n\tassert.Equal(t, mechanism, \"extensible\")\n\tassert.EqualError(t, err, ErrUnsupportedEncryptMechanism.Error())\n\t_, err = encryptionMechanism([]byte{})\n\tassert.EqualError(t, err, ErrUnknownEncryptMechanism.Error())\n}\n\nfunc TestHashing(t *testing.T) {\n\tassert.Equal(t, hashing(\"unsupportedHashAlgorithm\", []byte{}), []byte(nil))\n}\n\nfunc TestGenISOPasswdHash(t *testing.T) {\n\tfor hashAlgorithm, expected := range map[string][]string{\n\t\t\"MD4\":     {\"2lZQZUubVHLm/t6KsuHX4w==\", \"TTHjJdU70B/6Zq83XGhHVA==\"},\n\t\t\"MD5\":     {\"HWbqyd4dKKCjk1fEhk2kuQ==\", \"8ADyorkumWCayIukRhlVKQ==\"},\n\t\t\"SHA-1\":   {\"XErQIV3Ol+nhXkyCxrLTEQm+mSc=\", \"I3nDtyf59ASaNX1l6KpFnA==\"},\n\t\t\"SHA-256\": {\"7oqMFyfED+mPrzRIBQ+KpKT4SClMHEPOZldliP15xAA=\", \"ru1R/w3P3Jna2Qo+EE8QiA==\"},\n\t\t\"SHA-384\": {\"nMODLlxsC8vr0btcq0kp/jksg5FaI3az5Sjo1yZk+/x4bFzsuIvpDKUhJGAk/fzo\", \"Zjq9/jHlgOY6MzFDSlVNZg==\"},\n\t\t\"SHA-512\": {\"YZ6jrGOFQgVKK3rDK/0SHGGgxEmFJglQIIRamZc2PkxVtUBp54fQn96+jVXEOqo6dtCSanqksXGcm/h3KaiR4Q==\", \"p5s/bybHBPtusI7EydTIrg==\"},\n\t} {\n\t\thashValue, saltValue, err := genISOPasswdHash(\"password\", hashAlgorithm, expected[1], int(sheetProtectionSpinCount))\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected[0], hashValue)\n\t\tassert.Equal(t, expected[1], saltValue)\n\t}\n}\n"
  },
  {
    "path": "datavalidation.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"slices\"\n\t\"strings\"\n)\n\n// DataValidationType defined the type of data validation.\ntype DataValidationType byte\n\n// Data validation types.\nconst (\n\t_ DataValidationType = iota\n\tDataValidationTypeNone\n\tDataValidationTypeCustom\n\tDataValidationTypeDate\n\tDataValidationTypeDecimal\n\tDataValidationTypeList\n\tDataValidationTypeTextLength\n\tDataValidationTypeTime\n\tDataValidationTypeWhole\n)\n\n// DataValidationErrorStyle defined the style of data validation error alert.\ntype DataValidationErrorStyle byte\n\n// Data validation error styles.\nconst (\n\t_ DataValidationErrorStyle = iota\n\tDataValidationErrorStyleStop\n\tDataValidationErrorStyleWarning\n\tDataValidationErrorStyleInformation\n)\n\n// Data validation error styles.\nconst (\n\tstyleStop        = \"stop\"\n\tstyleWarning     = \"warning\"\n\tstyleInformation = \"information\"\n)\n\n// DataValidationOperator operator enum.\ntype DataValidationOperator byte\n\n// Data validation operators.\nconst (\n\t_ DataValidationOperator = iota\n\tDataValidationOperatorBetween\n\tDataValidationOperatorEqual\n\tDataValidationOperatorGreaterThan\n\tDataValidationOperatorGreaterThanOrEqual\n\tDataValidationOperatorLessThan\n\tDataValidationOperatorLessThanOrEqual\n\tDataValidationOperatorNotBetween\n\tDataValidationOperatorNotEqual\n)\n\nvar (\n\t// formulaEscaper mimics the Excel escaping rules for data validation,\n\t// which converts `\"` to `\"\"` instead of `&quot;`.\n\tformulaEscaper = strings.NewReplacer(\n\t\t`&`, `&amp;`,\n\t\t`<`, `&lt;`,\n\t\t`>`, `&gt;`,\n\t)\n\tformulaUnescaper = strings.NewReplacer(\n\t\t`&amp;`, `&`,\n\t\t`&lt;`, `<`,\n\t\t`&gt;`, `>`,\n\t)\n\t// dataValidationTypeMap defined supported data validation types.\n\tdataValidationTypeMap = map[DataValidationType]string{\n\t\tDataValidationTypeNone:       \"none\",\n\t\tDataValidationTypeCustom:     \"custom\",\n\t\tDataValidationTypeDate:       \"date\",\n\t\tDataValidationTypeDecimal:    \"decimal\",\n\t\tDataValidationTypeList:       \"list\",\n\t\tDataValidationTypeTextLength: \"textLength\",\n\t\tDataValidationTypeTime:       \"time\",\n\t\tDataValidationTypeWhole:      \"whole\",\n\t}\n\t// dataValidationOperatorMap defined supported data validation operators.\n\tdataValidationOperatorMap = map[DataValidationOperator]string{\n\t\tDataValidationOperatorBetween:            \"between\",\n\t\tDataValidationOperatorEqual:              \"equal\",\n\t\tDataValidationOperatorGreaterThan:        \"greaterThan\",\n\t\tDataValidationOperatorGreaterThanOrEqual: \"greaterThanOrEqual\",\n\t\tDataValidationOperatorLessThan:           \"lessThan\",\n\t\tDataValidationOperatorLessThanOrEqual:    \"lessThanOrEqual\",\n\t\tDataValidationOperatorNotBetween:         \"notBetween\",\n\t\tDataValidationOperatorNotEqual:           \"notEqual\",\n\t}\n)\n\n// NewDataValidation return data validation struct.\nfunc NewDataValidation(allowBlank bool) *DataValidation {\n\treturn &DataValidation{\n\t\tAllowBlank:       allowBlank,\n\t\tShowErrorMessage: false,\n\t\tShowInputMessage: false,\n\t}\n}\n\n// SetError set error notice.\nfunc (dv *DataValidation) SetError(style DataValidationErrorStyle, title, msg string) {\n\tdv.Error = &msg\n\tdv.ErrorTitle = &title\n\tstrStyle := styleStop\n\tswitch style {\n\tcase DataValidationErrorStyleStop:\n\t\tstrStyle = styleStop\n\tcase DataValidationErrorStyleWarning:\n\t\tstrStyle = styleWarning\n\tcase DataValidationErrorStyleInformation:\n\t\tstrStyle = styleInformation\n\n\t}\n\tdv.ShowErrorMessage = true\n\tdv.ErrorStyle = &strStyle\n}\n\n// SetInput set prompt notice.\nfunc (dv *DataValidation) SetInput(title, msg string) {\n\tdv.ShowInputMessage = true\n\tdv.PromptTitle = &title\n\tdv.Prompt = &msg\n}\n\n// SetDropList data validation list. If you type the items into the data\n// validation dialog box (a delimited list), the limit is 255 characters,\n// including the separators. If your data validation list source formula is\n// over the maximum length limit, please set the allowed values in the\n// worksheet cells, and use the SetSqrefDropList function to set the reference\n// for their cells.\nfunc (dv *DataValidation) SetDropList(keys []string) error {\n\tformula := strings.Join(keys, \",\")\n\tif MaxFieldLength < countUTF16String(formula) {\n\t\treturn ErrDataValidationFormulaLength\n\t}\n\tdv.Type = dataValidationTypeMap[DataValidationTypeList]\n\tif strings.HasPrefix(formula, \"=\") {\n\t\tdv.Formula1 = formulaEscaper.Replace(formula)\n\t\treturn nil\n\t}\n\tdv.Formula1 = fmt.Sprintf(`\"%s\"`, strings.NewReplacer(`\"`, `\"\"`).Replace(formulaEscaper.Replace(formula)))\n\treturn nil\n}\n\n// SetRange provides function to set data validation range in drop list, only\n// accepts int, float64, string or []string data type formula argument.\nfunc (dv *DataValidation) SetRange(f1, f2 interface{}, t DataValidationType, o DataValidationOperator) error {\n\tgenFormula := func(val interface{}) (string, error) {\n\t\tvar formula string\n\t\tswitch v := val.(type) {\n\t\tcase int:\n\t\t\tformula = fmt.Sprintf(\"%d\", v)\n\t\tcase float64:\n\t\t\tif math.Abs(v) > math.MaxFloat32 {\n\t\t\t\treturn formula, ErrDataValidationRange\n\t\t\t}\n\t\t\tformula = fmt.Sprintf(\"%.17g\", v)\n\t\tcase string:\n\t\t\tformula = v\n\t\tdefault:\n\t\t\treturn formula, ErrParameterInvalid\n\t\t}\n\t\treturn formula, nil\n\t}\n\tformula1, err := genFormula(f1)\n\tif err != nil {\n\t\treturn err\n\t}\n\tformula2, err := genFormula(f2)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdv.Formula1, dv.Formula2 = formula1, formula2\n\tdv.Type = dataValidationTypeMap[t]\n\tdv.Operator = dataValidationOperatorMap[o]\n\treturn err\n}\n\n// SetSqrefDropList provides set data validation on a range with source\n// reference range of the worksheet by given data validation object and\n// worksheet name. The data validation object can be created by\n// NewDataValidation function. There are limits to the number of items that\n// will show in a data validation drop down list: The list can show up to show\n// 32768 items from a list on the worksheet. If you need more items than that,\n// you could create a dependent drop down list, broken down by category. For\n// example, set data validation on Sheet1!A7:B8 with validation criteria source\n// Sheet1!E1:E3 settings, create in-cell dropdown by allowing list source:\n//\n//\tdv := excelize.NewDataValidation(true)\n//\tdv.Sqref = \"A7:B8\"\n//\tdv.SetSqrefDropList(\"$E$1:$E$3\")\n//\terr := f.AddDataValidation(\"Sheet1\", dv)\nfunc (dv *DataValidation) SetSqrefDropList(sqref string) {\n\tdv.Formula1 = sqref\n\tdv.Type = dataValidationTypeMap[DataValidationTypeList]\n}\n\n// SetSqref provides function to set data validation range in drop list.\nfunc (dv *DataValidation) SetSqref(sqref string) {\n\tif dv.Sqref == \"\" {\n\t\tdv.Sqref = sqref\n\t\treturn\n\t}\n\tdv.Sqref = fmt.Sprintf(\"%s %s\", dv.Sqref, sqref)\n}\n\n// AddDataValidation provides set data validation on a range of the worksheet\n// by given data validation object and worksheet name. This function is\n// concurrency safe. The data validation object can be created by\n// NewDataValidation function.\n//\n// Example 1, set data validation on Sheet1!A1:B2 with validation criteria\n// settings, show error alert after invalid data is entered with \"Stop\" style\n// and custom title \"error body\":\n//\n//\tdv := excelize.NewDataValidation(true)\n//\tdv.Sqref = \"A1:B2\"\n//\tdv.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorBetween)\n//\tdv.SetError(excelize.DataValidationErrorStyleStop, \"error title\", \"error body\")\n//\terr := f.AddDataValidation(\"Sheet1\", dv)\n//\n// Example 2, set data validation on Sheet1!A3:B4 with validation criteria\n// settings, and show input message when cell is selected:\n//\n//\tdv = excelize.NewDataValidation(true)\n//\tdv.Sqref = \"A3:B4\"\n//\tdv.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorGreaterThan)\n//\tdv.SetInput(\"input title\", \"input body\")\n//\terr = f.AddDataValidation(\"Sheet1\", dv)\n//\n// Example 3, set data validation on Sheet1!A5:B6 with validation criteria\n// settings, create in-cell dropdown by allowing list source:\n//\n//\tdv = excelize.NewDataValidation(true)\n//\tdv.Sqref = \"A5:B6\"\n//\tdv.SetDropList([]string{\"1\", \"2\", \"3\"})\n//\terr = f.AddDataValidation(\"Sheet1\", dv)\nfunc (f *File) AddDataValidation(sheet string, dv *DataValidation) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tif nil == ws.DataValidations {\n\t\tws.DataValidations = new(xlsxDataValidations)\n\t}\n\tdataValidation := &xlsxDataValidation{\n\t\tAllowBlank:       dv.AllowBlank,\n\t\tError:            dv.Error,\n\t\tErrorStyle:       dv.ErrorStyle,\n\t\tErrorTitle:       dv.ErrorTitle,\n\t\tOperator:         dv.Operator,\n\t\tPrompt:           dv.Prompt,\n\t\tPromptTitle:      dv.PromptTitle,\n\t\tShowDropDown:     dv.ShowDropDown,\n\t\tShowErrorMessage: dv.ShowErrorMessage,\n\t\tShowInputMessage: dv.ShowInputMessage,\n\t\tSqref:            dv.Sqref,\n\t\tType:             dv.Type,\n\t}\n\tif dv.Formula1 != \"\" {\n\t\tdataValidation.Formula1 = &xlsxInnerXML{Content: dv.Formula1}\n\t}\n\tif dv.Formula2 != \"\" {\n\t\tdataValidation.Formula2 = &xlsxInnerXML{Content: dv.Formula2}\n\t}\n\tws.DataValidations.DataValidation = append(ws.DataValidations.DataValidation, dataValidation)\n\tws.DataValidations.Count = len(ws.DataValidations.DataValidation)\n\treturn err\n}\n\n// GetDataValidations returns data validations list by given worksheet name.\nfunc (f *File) GetDataValidations(sheet string) ([]*DataValidation, error) {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar (\n\t\tdataValidations       []*DataValidation\n\t\tdecodeExtLst          = new(decodeExtLst)\n\t\tdecodeDataValidations *xlsxDataValidations\n\t\text                   *xlsxExt\n\t)\n\tif ws.DataValidations != nil {\n\t\tdataValidations = append(dataValidations, getDataValidations(ws.DataValidations)...)\n\t}\n\tif ws.ExtLst != nil {\n\t\tif err = f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + ws.ExtLst.Ext + \"</extLst>\")).\n\t\t\tDecode(decodeExtLst); err != nil && err != io.EOF {\n\t\t\treturn dataValidations, err\n\t\t}\n\t\tfor _, ext = range decodeExtLst.Ext {\n\t\t\tif ext.URI == ExtURIDataValidations {\n\t\t\t\tdecodeDataValidations = new(xlsxDataValidations)\n\t\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(decodeDataValidations)\n\t\t\t\tdataValidations = append(dataValidations, getDataValidations(decodeDataValidations)...)\n\t\t\t}\n\t\t}\n\t}\n\treturn dataValidations, err\n}\n\n// getDataValidations returns data validations list by given worksheet data\n// validations.\nfunc getDataValidations(dvs *xlsxDataValidations) []*DataValidation {\n\tif dvs == nil {\n\t\treturn nil\n\t}\n\tvar dataValidations []*DataValidation\n\tfor _, dv := range dvs.DataValidation {\n\t\tif dv == nil {\n\t\t\tcontinue\n\t\t}\n\t\tdataValidation := &DataValidation{\n\t\t\tAllowBlank:       dv.AllowBlank,\n\t\t\tError:            dv.Error,\n\t\t\tErrorStyle:       dv.ErrorStyle,\n\t\t\tErrorTitle:       dv.ErrorTitle,\n\t\t\tOperator:         dv.Operator,\n\t\t\tPrompt:           dv.Prompt,\n\t\t\tPromptTitle:      dv.PromptTitle,\n\t\t\tShowDropDown:     dv.ShowDropDown,\n\t\t\tShowErrorMessage: dv.ShowErrorMessage,\n\t\t\tShowInputMessage: dv.ShowInputMessage,\n\t\t\tSqref:            dv.Sqref,\n\t\t\tType:             dv.Type,\n\t\t}\n\t\tif dv.Formula1 != nil {\n\t\t\tdataValidation.Formula1 = unescapeDataValidationFormula(dv.Formula1.Content)\n\t\t}\n\t\tif dv.Formula2 != nil {\n\t\t\tdataValidation.Formula2 = unescapeDataValidationFormula(dv.Formula2.Content)\n\t\t}\n\t\tif dv.XMSqref != \"\" {\n\t\t\tdataValidation.Sqref = dv.XMSqref\n\t\t\tdataValidation.Formula1 = strings.TrimSuffix(strings.TrimPrefix(dataValidation.Formula1, \"<xm:f>\"), \"</xm:f>\")\n\t\t\tdataValidation.Formula2 = strings.TrimSuffix(strings.TrimPrefix(dataValidation.Formula2, \"<xm:f>\"), \"</xm:f>\")\n\t\t}\n\t\tdataValidations = append(dataValidations, dataValidation)\n\t}\n\treturn dataValidations\n}\n\n// DeleteDataValidation delete data validation by given worksheet name and\n// reference sequence. This function is concurrency safe. All data validations\n// in the worksheet will be deleted if not specify reference sequence parameter.\n//\n// Example 1, delete data validation on Sheet1!A1:B2:\n//\n//\terr := f.DeleteDataValidation(\"Sheet1\", \"A1:B2\")\n//\n// Example 2, delete data validations on Sheet1 with multiple cell ranges\n// A1:B2 and C1:C3 with reference sequence slice:\n//\n//\terr := f.DeleteDataValidation(\"Sheet1\", []string{\"A1:B2\", \"C1:C3\"}...)\n//\n// Example 3, delete data validations on Sheet1 with multiple cell ranges\n// A1:B2 and C1:C3 with blank separated reference sequence string, the result\n// same as example 2:\n//\n//\terr := f.DeleteDataValidation(\"Sheet1\", \"A1:B2 C1:C3\")\n//\n// Example 4, delete all data validations on Sheet1:\n//\n//\terr := f.DeleteDataValidation(\"Sheet1\")\nfunc (f *File) DeleteDataValidation(sheet string, sqref ...string) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tif ws.DataValidations == nil && ws.ExtLst == nil {\n\t\treturn nil\n\t}\n\tif sqref == nil {\n\t\tws.DataValidations = nil\n\t\treturn nil\n\t}\n\tdelCells, err := flatSqref(strings.Join(sqref, \" \"))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif ws.DataValidations != nil {\n\t\tif err = f.deleteDataValidation(ws, delCells); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif ws.ExtLst != nil {\n\t\treturn f.deleteX14DataValidation(ws, delCells)\n\t}\n\treturn nil\n}\n\n// deleteDataValidation deletes data validation by given worksheet and cell\n// reference list.\nfunc (f *File) deleteDataValidation(ws *xlsxWorksheet, delCells map[int][][]int) error {\n\tdv := ws.DataValidations\n\tvar err error\n\tfor i := 0; i < len(dv.DataValidation); i++ {\n\t\tif dv.DataValidation[i].Sqref, err = deleteCellsFromSqref(dv.DataValidation[i].Sqref, delCells); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif len(dv.DataValidation[i].Sqref) == 0 {\n\t\t\tdv.DataValidation = append(dv.DataValidation[:i], dv.DataValidation[i+1:]...)\n\t\t\ti--\n\t\t}\n\t}\n\tdv.Count = len(dv.DataValidation)\n\tif dv.Count == 0 {\n\t\tws.DataValidations = nil\n\t}\n\treturn nil\n}\n\n// deleteX14DataValidation deletes data validation in the extLst element by\n// given worksheet and cell reference list.\nfunc (f *File) deleteX14DataValidation(ws *xlsxWorksheet, delCells map[int][][]int) error {\n\tvar (\n\t\terr                   error\n\t\tdecodeExtLst          = new(decodeExtLst)\n\t\tdecodeDataValidations *xlsxDataValidations\n\t\tx14DataValidations    *xlsxX14DataValidations\n\t)\n\tif err := f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + ws.ExtLst.Ext + \"</extLst>\")).\n\t\tDecode(decodeExtLst); err != nil && err != io.EOF {\n\t\treturn err\n\t}\n\tfor i, ext := range decodeExtLst.Ext {\n\t\tif ext.URI == ExtURIDataValidations {\n\t\t\tdecodeDataValidations = new(xlsxDataValidations)\n\t\t\tx14DataValidations = new(xlsxX14DataValidations)\n\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(decodeDataValidations)\n\t\t\tx14DataValidations.XMLNSXM = NameSpaceSpreadSheetExcel2006Main.Value\n\t\t\tx14DataValidations.DisablePrompts = decodeDataValidations.DisablePrompts\n\t\t\tx14DataValidations.XWindow = decodeDataValidations.XWindow\n\t\t\tx14DataValidations.YWindow = decodeDataValidations.YWindow\n\t\t\tfor _, dv := range decodeDataValidations.DataValidation {\n\t\t\t\tif dv.XMSqref, err = deleteCellsFromSqref(dv.XMSqref, delCells); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif len(dv.XMSqref) == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tx14DataValidations.DataValidation = append(x14DataValidations.DataValidation, &xlsxX14DataValidation{\n\t\t\t\t\tAllowBlank:       dv.AllowBlank,\n\t\t\t\t\tError:            dv.Error,\n\t\t\t\t\tErrorStyle:       dv.ErrorStyle,\n\t\t\t\t\tErrorTitle:       dv.ErrorTitle,\n\t\t\t\t\tOperator:         dv.Operator,\n\t\t\t\t\tPrompt:           dv.Prompt,\n\t\t\t\t\tPromptTitle:      dv.PromptTitle,\n\t\t\t\t\tShowDropDown:     dv.ShowDropDown,\n\t\t\t\t\tShowErrorMessage: dv.ShowErrorMessage,\n\t\t\t\t\tShowInputMessage: dv.ShowInputMessage,\n\t\t\t\t\tSqref:            dv.Sqref,\n\t\t\t\t\tXMSqref:          dv.XMSqref,\n\t\t\t\t\tType:             dv.Type,\n\t\t\t\t\tFormula1:         dv.Formula1,\n\t\t\t\t\tFormula2:         dv.Formula2,\n\t\t\t\t})\n\t\t\t}\n\t\t\tx14DataValidations.Count = len(x14DataValidations.DataValidation)\n\t\t\tx14DataValidationsBytes, _ := xml.Marshal(x14DataValidations)\n\t\t\tdecodeExtLst.Ext[i] = &xlsxExt{\n\t\t\t\txmlns: []xml.Attr{{Name: xml.Name{Local: \"xmlns:\" + NameSpaceSpreadSheetX14.Name.Local}, Value: NameSpaceSpreadSheetX14.Value}},\n\t\t\t\tURI:   ExtURIDataValidations, Content: string(x14DataValidationsBytes),\n\t\t\t}\n\t\t\tif x14DataValidations.Count == 0 {\n\t\t\t\tdecodeExtLst.Ext = slices.Delete(decodeExtLst.Ext, i, i+1)\n\t\t\t}\n\t\t}\n\t}\n\textLstBytes, err := xml.Marshal(decodeExtLst)\n\tws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), \"<extLst>\"), \"</extLst>\")}\n\treturn err\n}\n\n// squashSqref generates cell reference sequence by given cells coordinates list.\nfunc squashSqref(cells [][]int) []string {\n\tif len(cells) == 1 {\n\t\tcell, _ := CoordinatesToCellName(cells[0][0], cells[0][1])\n\t\treturn []string{cell}\n\t} else if len(cells) == 0 {\n\t\treturn []string{}\n\t}\n\tvar refs []string\n\tl, r := 0, 0\n\tfor i := 1; i < len(cells); i++ {\n\t\tif cells[i][0] == cells[r][0] && cells[i][1]-cells[r][1] > 1 {\n\t\t\tref, _ := coordinatesToRangeRef(append(cells[l], cells[r]...))\n\t\t\tif l == r {\n\t\t\t\tref, _ = CoordinatesToCellName(cells[l][0], cells[l][1])\n\t\t\t}\n\t\t\trefs = append(refs, ref)\n\t\t\tl, r = i, i\n\t\t} else {\n\t\t\tr++\n\t\t}\n\t}\n\tref, _ := coordinatesToRangeRef(append(cells[l], cells[r]...))\n\tif l == r {\n\t\tref, _ = CoordinatesToCellName(cells[l][0], cells[l][1])\n\t}\n\treturn append(refs, ref)\n}\n\n// deleteCellsFromSqref deletes cells from given sqref by delCells map and\n// returns the updated sqref.\nfunc deleteCellsFromSqref(sqref string, delCells map[int][][]int) (string, error) {\n\tvar (\n\t\tref        string\n\t\tapplySqref []string\n\t)\n\tcolCells, err := flatSqref(sqref)\n\tif err != nil {\n\t\treturn ref, err\n\t}\n\tfor col, cells := range delCells {\n\t\tfor _, cell := range cells {\n\t\t\tidx := inCoordinates(colCells[col], cell)\n\t\t\tif idx != -1 {\n\t\t\t\tcolCells[col] = append(colCells[col][:idx], colCells[col][idx+1:]...)\n\t\t\t}\n\t\t}\n\t}\n\tfor _, col := range colCells {\n\t\tapplySqref = append(applySqref, squashSqref(col)...)\n\t}\n\treturn strings.Join(applySqref, \" \"), err\n}\n\n// isFormulaDataValidation returns whether the data validation rule is a formula.\nfunc (dv *xlsxInnerXML) isFormula() bool {\n\treturn dv != nil && (!strings.HasPrefix(dv.Content, \"&quot;\") || !strings.HasSuffix(dv.Content, \"&quot;\"))\n}\n\n// unescapeDataValidationFormula returns unescaped data validation formula.\nfunc unescapeDataValidationFormula(val string) string {\n\tif strings.HasPrefix(val, \"\\\"\") { // Text detection\n\t\treturn strings.NewReplacer(`\"\"`, `\"`).Replace(formulaUnescaper.Replace(val))\n\t}\n\treturn formulaUnescaper.Replace(val)\n}\n"
  },
  {
    "path": "datavalidation_test.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestDataValidation(t *testing.T) {\n\tresultFile := filepath.Join(\"test\", \"TestDataValidation.xlsx\")\n\n\tf := NewFile()\n\n\tdv := NewDataValidation(true)\n\tdv.Sqref = \"A1:B2\"\n\tassert.NoError(t, dv.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorBetween))\n\tdv.SetError(DataValidationErrorStyleStop, \"error title\", \"error body\")\n\tdv.SetError(DataValidationErrorStyleWarning, \"error title\", \"error body\")\n\tdv.SetError(DataValidationErrorStyleInformation, \"error title\", \"error body\")\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\n\tdataValidations, err := f.GetDataValidations(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, dataValidations, 1)\n\n\tassert.NoError(t, f.SaveAs(resultFile))\n\n\tdv = NewDataValidation(true)\n\tdv.Sqref = \"A3:B4\"\n\tassert.NoError(t, dv.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan))\n\tdv.SetInput(\"input title\", \"input body\")\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\n\tdataValidations, err = f.GetDataValidations(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, dataValidations, 2)\n\n\tassert.NoError(t, f.SaveAs(resultFile))\n\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetSheetRow(\"Sheet2\", \"A2\", &[]interface{}{\"B2\", 1}))\n\tassert.NoError(t, f.SetSheetRow(\"Sheet2\", \"A3\", &[]interface{}{\"B3\", 3}))\n\tdv = NewDataValidation(true)\n\tdv.Sqref = \"A1:B1\"\n\tassert.NoError(t, dv.SetRange(\"INDIRECT($A$2)\", \"INDIRECT($A$3)\", DataValidationTypeWhole, DataValidationOperatorBetween))\n\tdv.SetError(DataValidationErrorStyleStop, \"error title\", \"error body\")\n\tassert.NoError(t, f.AddDataValidation(\"Sheet2\", dv))\n\tdataValidations, err = f.GetDataValidations(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, dataValidations, 2)\n\tdataValidations, err = f.GetDataValidations(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.Len(t, dataValidations, 1)\n\n\tdv = NewDataValidation(true)\n\tdv.Sqref = \"A5:B6\"\n\tfor _, listValid := range [][]string{\n\t\t{\"1\", \"2\", \"3\"},\n\t\t{\"=A1\"},\n\t\t{strings.Repeat(\"&\", MaxFieldLength)},\n\t\t{strings.Repeat(\"\\u4E00\", MaxFieldLength)},\n\t\t{strings.Repeat(\"\\U0001F600\", 100), strings.Repeat(\"\\u4E01\", 50), \"<&>\"},\n\t\t{`A<`, `B>`, `C\"`, \"D\\t\", `E'`, `F`},\n\t} {\n\t\tdv.Formula1 = \"\"\n\t\tassert.NoError(t, dv.SetDropList(listValid),\n\t\t\t\"SetDropList failed for valid input %v\", listValid)\n\t\tassert.NotEmpty(t, dv.Formula1,\n\t\t\t\"Formula1 should not be empty for valid input %v\", listValid)\n\t}\n\tassert.Equal(t, `\"A&lt;,B&gt;,C\"\",D\t,E',F\"`, dv.Formula1)\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\n\tdataValidations, err = f.GetDataValidations(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, dataValidations, 3)\n\n\t// Test get data validation on no exists worksheet\n\t_, err = f.GetDataValidations(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get data validation with invalid sheet name\n\t_, err = f.GetDataValidations(\"Sheet:1\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\n\tassert.NoError(t, f.SaveAs(resultFile))\n\n\t// Test get data validation on a worksheet without data validation settings\n\tf = NewFile()\n\tdataValidations, err = f.GetDataValidations(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []*DataValidation(nil), dataValidations)\n\n\t// Test get data validations which storage in the extension lists\n\tf = NewFile()\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: fmt.Sprintf(`<ext uri=\"%s\" xmlns:x14=\"%s\"><x14:dataValidations><x14:dataValidation type=\"list\" allowBlank=\"1\"><x14:formula1><xm:f>Sheet1!$B$1:$B$5</xm:f></x14:formula1><xm:sqref>A7:B8</xm:sqref></x14:dataValidation></x14:dataValidations></ext>`, ExtURIDataValidations, NameSpaceSpreadSheetX14.Value)}\n\tdataValidations, err = f.GetDataValidations(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []*DataValidation{\n\t\t{\n\t\t\tAllowBlank: true,\n\t\t\tType:       \"list\",\n\t\t\tFormula1:   \"Sheet1!$B$1:$B$5\",\n\t\t\tSqref:      \"A7:B8\",\n\t\t},\n\t}, dataValidations)\n\n\t// Test get data validations with invalid extension list characters\n\tws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: fmt.Sprintf(`<ext uri=\"%s\" xmlns:x14=\"%s\"><x14:dataValidations></x14:dataValidation></x14:dataValidations></ext>`, ExtURIDataValidations, NameSpaceSpreadSheetX14.Value)}\n\t_, err = f.GetDataValidations(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: element <dataValidations> closed by </dataValidation>\")\n\n\t// Test get validations without validations\n\tassert.Nil(t, getDataValidations(nil))\n\tassert.Nil(t, getDataValidations(&xlsxDataValidations{DataValidation: []*xlsxDataValidation{nil}}))\n}\n\nfunc TestDataValidationError(t *testing.T) {\n\tresultFile := filepath.Join(\"test\", \"TestDataValidationError.xlsx\")\n\n\tf := NewFile()\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", \"E1\", \"E1\"))\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", \"E2\", \"E2\"))\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", \"E3\", \"E3\"))\n\n\tdv := NewDataValidation(true)\n\tdv.SetSqref(\"A7:B8\")\n\tdv.SetSqref(\"A7:B8\")\n\tdv.SetSqrefDropList(\"$E$1:$E$3\")\n\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\n\tdv = NewDataValidation(true)\n\terr := dv.SetDropList(make([]string, 258))\n\tif dv.Formula1 != \"\" {\n\t\tt.Errorf(\"data validation error. Formula1 must be empty!\")\n\t\treturn\n\t}\n\tassert.EqualError(t, err, ErrDataValidationFormulaLength.Error())\n\tassert.EqualError(t, dv.SetRange(nil, 20, DataValidationTypeWhole, DataValidationOperatorBetween), ErrParameterInvalid.Error())\n\tassert.EqualError(t, dv.SetRange(10, nil, DataValidationTypeWhole, DataValidationOperatorBetween), ErrParameterInvalid.Error())\n\tassert.NoError(t, dv.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan))\n\tdv.SetSqref(\"A9:B10\")\n\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\n\t// Test width invalid data validation formula\n\tprevFormula1 := dv.Formula1\n\tfor _, keys := range [][]string{\n\t\tmake([]string, 257),\n\t\t{strings.Repeat(\"s\", 256)},\n\t\t{strings.Repeat(\"\\u4E00\", 256)},\n\t\t{strings.Repeat(\"\\U0001F600\", 128)},\n\t\t{strings.Repeat(\"\\U0001F600\", 127), \"s\"},\n\t} {\n\t\terr = dv.SetDropList(keys)\n\t\tassert.Equal(t, prevFormula1, dv.Formula1,\n\t\t\t\"Formula1 should be unchanged for invalid input %v\", keys)\n\t\tassert.EqualError(t, err, ErrDataValidationFormulaLength.Error())\n\t}\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\tassert.NoError(t, dv.SetRange(\n\t\t-math.MaxFloat32, math.MaxFloat32,\n\t\tDataValidationTypeWhole, DataValidationOperatorGreaterThan))\n\tassert.EqualError(t, dv.SetRange(\n\t\t-math.MaxFloat64, math.MaxFloat32,\n\t\tDataValidationTypeWhole, DataValidationOperatorGreaterThan), ErrDataValidationRange.Error())\n\tassert.EqualError(t, dv.SetRange(\n\t\tmath.SmallestNonzeroFloat64, math.MaxFloat64,\n\t\tDataValidationTypeWhole, DataValidationOperatorGreaterThan), ErrDataValidationRange.Error())\n\tassert.NoError(t, f.SaveAs(resultFile))\n\n\t// Test add data validation on no exists worksheet\n\tf = NewFile()\n\tassert.EqualError(t, f.AddDataValidation(\"SheetN\", nil), \"sheet SheetN does not exist\")\n\n\t// Test add data validation with invalid sheet name\n\tf = NewFile()\n\tassert.EqualError(t, f.AddDataValidation(\"Sheet:1\", nil), ErrSheetNameInvalid.Error())\n}\n\nfunc TestDeleteDataValidation(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.DeleteDataValidation(\"Sheet1\", \"A1:B2\"))\n\n\tdv := NewDataValidation(true)\n\tdv.Sqref = \"A1:B2\"\n\tassert.NoError(t, dv.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorBetween))\n\tdv.SetInput(\"input title\", \"input body\")\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\tassert.NoError(t, f.DeleteDataValidation(\"Sheet1\", \"A1:B2\"))\n\n\tdv.Sqref = \"A1\"\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\tassert.NoError(t, f.DeleteDataValidation(\"Sheet1\", \"B1\"))\n\tassert.NoError(t, f.DeleteDataValidation(\"Sheet1\", \"A1\"))\n\n\tdv.Sqref = \"C2:C5\"\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\tassert.NoError(t, f.DeleteDataValidation(\"Sheet1\", \"C4\"))\n\n\tdv = NewDataValidation(true)\n\tdv.Sqref = \"D2:D2 D3 D4\"\n\tassert.NoError(t, dv.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorBetween))\n\tdv.SetInput(\"input title\", \"input body\")\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\tassert.NoError(t, f.DeleteDataValidation(\"Sheet1\", \"D3\"))\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestDeleteDataValidation.xlsx\")))\n\n\tdv.Sqref = \"A\"\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\tassert.EqualError(t, f.DeleteDataValidation(\"Sheet1\", \"A1\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\n\tassert.EqualError(t, f.DeleteDataValidation(\"Sheet1\", \"A1:A\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).DataValidations.DataValidation[0].Sqref = \"A1:A\"\n\tassert.EqualError(t, f.DeleteDataValidation(\"Sheet1\", \"A1:B2\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\n\t// Test delete data validation on no exists worksheet\n\tassert.EqualError(t, f.DeleteDataValidation(\"SheetN\", \"A1:B2\"), \"sheet SheetN does not exist\")\n\t// Test delete all data validation with invalid sheet name\n\tassert.EqualError(t, f.DeleteDataValidation(\"Sheet:1\"), ErrSheetNameInvalid.Error())\n\t// Test delete all data validations in the worksheet\n\tassert.NoError(t, f.DeleteDataValidation(\"Sheet1\"))\n\tassert.Nil(t, ws.(*xlsxWorksheet).DataValidations)\n\n\tt.Run(\"with_data_validation_in_extLst\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\t\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", fmt.Appendf(nil,\n\t\t\t`<worksheet xmlns=\"%s\"><sheetData/><extLst><ext xmlns:x14=\"%s\" uri=\"%s\"><x14:dataValidations xmlns:xm=\"%s\" count=\"2\"><x14:dataValidation allowBlank=\"true\" showErrorMessage=\"true\" showInputMessage=\"true\" sqref=\"\" type=\"list\"><xm:sqref>A1:A2</xm:sqref><x14:formula1><xm:f>Sheet1!$A$2:$A$4</xm:f></x14:formula1></x14:dataValidation><x14:dataValidation allowBlank=\"true\" showErrorMessage=\"true\" showInputMessage=\"true\" sqref=\"\" type=\"list\"><xm:sqref>B1:B2</xm:sqref><x14:formula1><xm:f>Sheet1!$B$2:$B$3</xm:f></x14:formula1></x14:dataValidation></x14:dataValidations></ext></extLst></worksheet>`,\n\t\t\tNameSpaceSpreadSheet.Value, NameSpaceSpreadSheetExcel2006Main.Value,\n\t\t\tExtURIDataValidations, NameSpaceSpreadSheetExcel2006Main.Value))\n\t\tf.checked = sync.Map{}\n\t\tassert.NoError(t, f.DeleteDataValidation(\"Sheet1\", \"A1:A2\"))\n\t\tdvs, err := f.GetDataValidations(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, dvs, 1)\n\t\tassert.Equal(t, \"B1:B2\", dvs[0].Sqref)\n\n\t\tassert.NoError(t, f.DeleteDataValidation(\"Sheet1\", \"B1:B2\"))\n\t\tdvs, err = f.GetDataValidations(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Empty(t, dvs)\n\t})\n\n\tt.Run(\"with_invalid_data_validation_in_extLst\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.EqualError(t, f.deleteX14DataValidation(&xlsxWorksheet{\n\t\t\tExtLst: &xlsxExtLst{Ext: \"<extLst><x14:dataValidations></x14:dataValidation></x14:dataValidations></ext></extLst>\"},\n\t\t}, nil), \"XML syntax error on line 1: element <dataValidations> closed by </dataValidation>\")\n\t})\n\n\tt.Run(\"with_unordered_sqref\", func(t *testing.T) {\n\t\t// Test deleting data validation when sqref has unordered ranges\n\t\tf := NewFile()\n\t\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\t\tassert.True(t, ok)\n\n\t\tws.(*xlsxWorksheet).DataValidations = &xlsxDataValidations{\n\t\t\tCount: 1,\n\t\t\tDataValidation: []*xlsxDataValidation{{\n\t\t\t\tAllowBlank:       true,\n\t\t\t\tShowInputMessage: true,\n\t\t\t\tShowErrorMessage: true,\n\t\t\t\tType:             \"whole\",\n\t\t\t\tOperator:         \"between\",\n\t\t\t\tSqref:            \"A5:A10 A15:A20 A3:A4\",\n\t\t\t\tFormula1:         &xlsxInnerXML{Content: \"1\"},\n\t\t\t\tFormula2:         &xlsxInnerXML{Content: \"100\"},\n\t\t\t}},\n\t\t}\n\n\t\tassert.NoError(t, f.DeleteDataValidation(\"Sheet1\", \"A7\"))\n\n\t\tdvs, err := f.GetDataValidations(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, dvs, 1)\n\t\tassert.Equal(t, \"A3:A6 A8:A10 A15:A20\", dvs[0].Sqref)\n\t})\n\n\tt.Run(\"with_unordered_sqref_in_extLst\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\t\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", fmt.Appendf(nil,\n\t\t\t`<worksheet xmlns=\"%s\"><sheetData/><extLst><ext xmlns:x14=\"%s\" uri=\"%s\"><x14:dataValidations xmlns:xm=\"%s\" count=\"1\"><x14:dataValidation><xm:sqref>A5:A10 A15:A20 A3:A4</xm:sqref><x14:formula1></x14:formula1></x14:dataValidation></x14:dataValidations></ext></extLst></worksheet>`,\n\t\t\tNameSpaceSpreadSheet.Value, NameSpaceSpreadSheetExcel2006Main.Value,\n\t\t\tExtURIDataValidations, NameSpaceSpreadSheetExcel2006Main.Value))\n\t\tf.checked = sync.Map{}\n\t\tassert.NoError(t, f.DeleteDataValidation(\"Sheet1\", \"A7\"))\n\t\tdvs, err := f.GetDataValidations(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, dvs, 1)\n\t\tassert.Equal(t, \"A3:A6 A8:A10 A15:A20\", dvs[0].Sqref)\n\t\t// Test deleting data validation with invalid sqref\n\t\tf = NewFile()\n\t\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\t\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", fmt.Appendf(nil,\n\t\t\t`<worksheet xmlns=\"%s\"><sheetData/><extLst><ext xmlns:x14=\"%s\" uri=\"%s\"><x14:dataValidations xmlns:xm=\"%s\" count=\"1\"><x14:dataValidation><xm:sqref>A</xm:sqref><x14:formula1></x14:formula1></x14:dataValidation></x14:dataValidations></ext></extLst></worksheet>`,\n\t\t\tNameSpaceSpreadSheet.Value, NameSpaceSpreadSheetExcel2006Main.Value,\n\t\t\tExtURIDataValidations, NameSpaceSpreadSheetExcel2006Main.Value))\n\t\tf.checked = sync.Map{}\n\t\tassert.Equal(t, f.DeleteDataValidation(\"Sheet1\", \"A7\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")))\n\t})\n}\n"
  },
  {
    "path": "date.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\nconst (\n\tnanosInADay    = float64((24 * time.Hour) / time.Nanosecond)\n\tdayNanoseconds = 24 * time.Hour\n\tmaxDuration    = 290 * 364 * dayNanoseconds\n\troundEpsilon   = 1e-9\n)\n\nvar (\n\tdaysInMonth           = []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}\n\texcel1900Epoc         = time.Date(1899, time.December, 30, 0, 0, 0, 0, time.UTC)\n\texcel1904Epoc         = time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC)\n\texcelMinTime1900      = time.Date(1899, time.December, 31, 0, 0, 0, 0, time.UTC)\n\texcelBuggyPeriodStart = time.Date(1900, time.March, 1, 0, 0, 0, 0, time.UTC).Add(-time.Nanosecond)\n)\n\n// timeToExcelTime provides a function to convert time to Excel time.\nfunc timeToExcelTime(t time.Time, date1904 bool) (float64, error) {\n\tdate := excelMinTime1900\n\tif date1904 {\n\t\tdate = excel1904Epoc\n\t}\n\tif t.Before(date) {\n\t\treturn 0, nil\n\t}\n\ttt, diff, result := t, t.Sub(date), 0.0\n\tfor diff >= maxDuration {\n\t\tresult += float64(maxDuration / dayNanoseconds)\n\t\ttt = tt.Add(-maxDuration)\n\t\tdiff = tt.Sub(date)\n\t}\n\n\trem := diff % dayNanoseconds\n\tresult += float64(diff-rem)/float64(dayNanoseconds) + float64(rem)/float64(dayNanoseconds)\n\n\t// Excel dates after 28th February 1900 are actually one day out.\n\t// Excel behaves as though the date 29th February 1900 existed, which it didn't.\n\t// Microsoft intentionally included this bug in Excel so that it would remain compatible with the spreadsheet\n\t// program that had the majority market share at the time; Lotus 1-2-3.\n\t// https://www.myonlinetraininghub.com/excel-date-and-time\n\tif !date1904 && t.After(excelBuggyPeriodStart) {\n\t\tresult++\n\t}\n\treturn result, nil\n}\n\n// shiftJulianToNoon provides a function to process julian date to noon.\nfunc shiftJulianToNoon(julianDays, julianFraction float64) (float64, float64) {\n\tswitch {\n\tcase -0.5 < julianFraction && julianFraction < 0.5:\n\t\tjulianFraction += 0.5\n\tcase julianFraction >= 0.5:\n\t\tjulianDays++\n\t\tjulianFraction -= 0.5\n\tcase julianFraction <= -0.5:\n\t\tjulianDays--\n\t\tjulianFraction += 1.5\n\t}\n\treturn julianDays, julianFraction\n}\n\n// fractionOfADay provides a function to return the integer values for hour,\n// minutes, seconds and nanoseconds that comprised a given fraction of a day.\n// values would round to 1 us.\nfunc fractionOfADay(fraction float64) (hours, minutes, seconds, nanoseconds int) {\n\tconst (\n\t\tc1us  = 1e3\n\t\tc1s   = 1e9\n\t\tc1day = 24 * 60 * 60 * c1s\n\t)\n\n\tfrac := int64(c1day*fraction + c1us/2)\n\tnanoseconds = int((frac%c1s)/c1us) * c1us\n\tfrac /= c1s\n\tseconds = int(frac % 60)\n\tfrac /= 60\n\tminutes = int(frac % 60)\n\thours = int(frac / 60)\n\treturn\n}\n\n// julianDateToGregorianTime provides a function to convert julian date to\n// gregorian time.\nfunc julianDateToGregorianTime(part1, part2 float64) time.Time {\n\tpart1I, part1F := math.Modf(part1)\n\tpart2I, part2F := math.Modf(part2)\n\tjulianDays := part1I + part2I\n\tjulianFraction := part1F + part2F\n\tjulianDays, julianFraction = shiftJulianToNoon(julianDays, julianFraction)\n\tday, month, year := doTheFliegelAndVanFlandernAlgorithm(int(julianDays))\n\thours, minutes, seconds, nanoseconds := fractionOfADay(julianFraction)\n\treturn time.Date(year, time.Month(month), day, hours, minutes, seconds, nanoseconds, time.UTC)\n}\n\n// doTheFliegelAndVanFlandernAlgorithm; By this point generations of\n// programmers have repeated the algorithm sent to the editor of\n// \"Communications of the ACM\" in 1968 (published in CACM, volume 11, number\n// 10, October 1968, p.657). None of those programmers seems to have found it\n// necessary to explain the constants or variable names set out by Henry F.\n// Fliegel and Thomas C. Van Flandern. Maybe one day I'll buy that journal and\n// expand an explanation here - that day is not today.\nfunc doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {\n\tl := jd + 68569\n\tn := (4 * l) / 146097\n\tl = l - (146097*n+3)/4\n\ti := (4000 * (l + 1)) / 1461001\n\tl = l - (1461*i)/4 + 31\n\tj := (80 * l) / 2447\n\td := l - (2447*j)/80\n\tl = j / 11\n\tm := j + 2 - (12 * l)\n\ty := 100*(n-49) + i + l\n\treturn d, m, y\n}\n\n// timeFromExcelTime provides a function to convert an excelTime\n// representation (stored as a floating point number) to a time.Time.\nfunc timeFromExcelTime(excelTime float64, date1904 bool) time.Time {\n\tvar date time.Time\n\twholeDaysPart := int(excelTime)\n\t// Excel uses Julian dates prior to March 1st 1900, and Gregorian\n\t// thereafter.\n\tif wholeDaysPart <= 61 {\n\t\tconst OFFSET1900 = 15018.0\n\t\tconst OFFSET1904 = 16480.0\n\t\tconst MJD0 float64 = 2400000.5\n\t\tvar date time.Time\n\t\tif date1904 {\n\t\t\tdate = julianDateToGregorianTime(MJD0, excelTime+OFFSET1904)\n\t\t} else {\n\t\t\tdate = julianDateToGregorianTime(MJD0, excelTime+OFFSET1900)\n\t\t}\n\t\treturn date\n\t}\n\tfloatPart := excelTime - float64(wholeDaysPart) + roundEpsilon\n\tif date1904 {\n\t\tdate = excel1904Epoc\n\t} else {\n\t\tdate = excel1900Epoc\n\t}\n\tdurationPart := time.Duration(nanosInADay * floatPart)\n\tdate = date.AddDate(0, 0, wholeDaysPart).Add(durationPart)\n\tif date.Nanosecond()/1e6 > 500 {\n\t\treturn date.Round(time.Second)\n\t}\n\treturn date.Truncate(time.Second)\n}\n\n// ExcelDateToTime converts a float-based Excel date representation to a time.Time.\nfunc ExcelDateToTime(excelDate float64, use1904Format bool) (time.Time, error) {\n\tif excelDate < 0 {\n\t\treturn time.Time{}, newInvalidExcelDateError(excelDate)\n\t}\n\treturn timeFromExcelTime(excelDate, use1904Format), nil\n}\n\n// isLeapYear determine if leap year for a given year.\nfunc isLeapYear(y int) bool {\n\tif y == y/400*400 {\n\t\treturn true\n\t}\n\tif y == y/100*100 {\n\t\treturn false\n\t}\n\treturn y == y/4*4\n}\n\n// getDaysInMonth provides a function to get the days by a given year and\n// month number.\nfunc getDaysInMonth(y, m int) int {\n\tif m == 2 && isLeapYear(y) {\n\t\treturn 29\n\t}\n\treturn daysInMonth[m-1]\n}\n\n// validateDate provides a function to validate if a valid date by a given\n// year, month, and day number.\nfunc validateDate(y, m, d int) bool {\n\tif m < 1 || m > 12 {\n\t\treturn false\n\t}\n\tif d < 1 {\n\t\treturn false\n\t}\n\treturn d <= getDaysInMonth(y, m)\n}\n\n// formatYear converts the given year number into a 4-digit format.\nfunc formatYear(y int) int {\n\tif y < 1900 {\n\t\tif y < 30 {\n\t\t\ty += 2000\n\t\t} else {\n\t\t\ty += 1900\n\t\t}\n\t}\n\treturn y\n}\n\n// getDurationNumFmt returns most simplify numbers format code for time\n// duration type cell value by given worksheet name, cell reference and number.\nfunc getDurationNumFmt(d time.Duration) int {\n\tif d >= time.Hour*24 {\n\t\treturn 46\n\t}\n\t// Whole minutes\n\tif d.Minutes() == float64(int(d.Minutes())) {\n\t\treturn 20\n\t}\n\treturn 21\n}\n\n// getTimeNumFmt returns most simplify numbers format code for time type cell\n// value by given worksheet name, cell reference and number.\nfunc getTimeNumFmt(t time.Time) int {\n\tnextMonth := t.AddDate(0, 1, 0)\n\t// Whole months\n\tif t.Day() == 1 && nextMonth.Day() == 1 {\n\t\treturn 17\n\t}\n\t// Whole days\n\tif t.Hour() == 0 && t.Minute() == 0 && t.Second() == 0 && t.Nanosecond() == 0 {\n\t\treturn 14\n\t}\n\treturn 22\n}\n"
  },
  {
    "path": "date_test.go",
    "content": "package excelize\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype dateTest struct {\n\tExcelValue float64\n\tGoValue    time.Time\n}\n\nvar trueExpectedDateList = []dateTest{\n\t{0.0000000000000000, time.Date(1899, time.December, 30, 0, 0, 0, 0, time.UTC)},\n\t{25569.000000000000, time.Unix(0, 0).UTC()},\n\n\t// Expected values extracted from real spreadsheet\n\t{1.0000000000000000, time.Date(1900, time.January, 1, 0, 0, 0, 0, time.UTC)},\n\t{1.0000115740740740, time.Date(1900, time.January, 1, 0, 0, 1, 0, time.UTC)},\n\t{1.0006944444444446, time.Date(1900, time.January, 1, 0, 1, 0, 0, time.UTC)},\n\t{1.0416666666666667, time.Date(1900, time.January, 1, 1, 0, 0, 0, time.UTC)},\n\t{2.0000000000000000, time.Date(1900, time.January, 2, 0, 0, 0, 0, time.UTC)},\n\t{43269.000000000000, time.Date(2018, time.June, 18, 0, 0, 0, 0, time.UTC)},\n\t{43542.611111111109, time.Date(2019, time.March, 18, 14, 40, 0, 0, time.UTC)},\n\t{401769.00000000000, time.Date(3000, time.January, 1, 0, 0, 0, 0, time.UTC)},\n}\n\nvar excelTimeInputList = []dateTest{\n\t{0.0, time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)},\n\t{60.0, time.Date(1900, 2, 28, 0, 0, 0, 0, time.UTC)},\n\t{61.0, time.Date(1900, 3, 1, 0, 0, 0, 0, time.UTC)},\n\t{41275.0, time.Date(2013, 1, 1, 0, 0, 0, 0, time.UTC)},\n\t{44450.3333333333, time.Date(2021, time.September, 11, 8, 0, 0, 0, time.UTC)},\n\t{401769.0, time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC)},\n}\n\nfunc TestTimeToExcelTime(t *testing.T) {\n\tfor i, test := range trueExpectedDateList {\n\t\tt.Run(fmt.Sprintf(\"TestData%d\", i+1), func(t *testing.T) {\n\t\t\texcelTime, err := timeToExcelTime(test.GoValue, false)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equalf(t, test.ExcelValue, excelTime,\n\t\t\t\t\"Time: %s\", test.GoValue.String())\n\t\t})\n\t}\n}\n\nfunc TestTimeToExcelTime_Timezone(t *testing.T) {\n\tlocation, err := time.LoadLocation(\"America/Los_Angeles\")\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tfor i, test := range trueExpectedDateList {\n\t\tt.Run(fmt.Sprintf(\"TestData%d\", i+1), func(t *testing.T) {\n\t\t\t_, err := timeToExcelTime(test.GoValue.In(location), false)\n\t\t\tassert.NoError(t, err)\n\t\t})\n\t}\n}\n\nfunc TestTimeFromExcelTime(t *testing.T) {\n\tfor i, test := range excelTimeInputList {\n\t\tt.Run(fmt.Sprintf(\"TestData%d\", i+1), func(t *testing.T) {\n\t\t\tassert.Equal(t, test.GoValue, timeFromExcelTime(test.ExcelValue, false))\n\t\t})\n\t}\n\tfor hour := 0; hour < 24; hour++ {\n\t\tfor minVal := 0; minVal < 60; minVal++ {\n\t\t\tfor sec := 0; sec < 60; sec++ {\n\t\t\t\tdate := time.Date(2021, time.December, 30, hour, minVal, sec, 0, time.UTC)\n\t\t\t\t// Test use 1900 date system\n\t\t\t\texcel1900Time, err := timeToExcelTime(date, false)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tdate1900Out := timeFromExcelTime(excel1900Time, false)\n\t\t\t\tassert.EqualValues(t, hour, date1900Out.Hour())\n\t\t\t\tassert.EqualValues(t, minVal, date1900Out.Minute())\n\t\t\t\tassert.EqualValues(t, sec, date1900Out.Second())\n\t\t\t\t// Test use 1904 date system\n\t\t\t\texcel1904Time, err := timeToExcelTime(date, true)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tdate1904Out := timeFromExcelTime(excel1904Time, true)\n\t\t\t\tassert.EqualValues(t, hour, date1904Out.Hour())\n\t\t\t\tassert.EqualValues(t, minVal, date1904Out.Minute())\n\t\t\t\tassert.EqualValues(t, sec, date1904Out.Second())\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestTimeFromExcelTime_1904(t *testing.T) {\n\tjulianDays, julianFraction := shiftJulianToNoon(1, -0.6)\n\tassert.Equal(t, julianDays, 0.0)\n\tassert.Equal(t, julianFraction, 0.9)\n\tjulianDays, julianFraction = shiftJulianToNoon(1, 0.1)\n\tassert.Equal(t, julianDays, 1.0)\n\tassert.Equal(t, julianFraction, 0.6)\n\tassert.Equal(t, timeFromExcelTime(61, true), time.Date(1904, time.March, 2, 0, 0, 0, 0, time.UTC))\n\tassert.Equal(t, timeFromExcelTime(62, true), time.Date(1904, time.March, 3, 0, 0, 0, 0, time.UTC))\n}\n\nfunc TestExcelDateToTime(t *testing.T) {\n\t// Check normal case\n\tfor i, test := range excelTimeInputList {\n\t\tt.Run(fmt.Sprintf(\"TestData%d\", i+1), func(t *testing.T) {\n\t\t\ttimeValue, err := ExcelDateToTime(test.ExcelValue, false)\n\t\t\tassert.Equal(t, test.GoValue, timeValue)\n\t\t\tassert.NoError(t, err)\n\t\t})\n\t}\n\t// Check error case\n\t_, err := ExcelDateToTime(-1, false)\n\tassert.EqualError(t, err, newInvalidExcelDateError(-1).Error())\n}\n"
  },
  {
    "path": "docProps.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"io\"\n\t\"reflect\"\n\t\"slices\"\n\t\"time\"\n)\n\n// SetAppProps provides a function to set document application properties. The\n// properties that can be set are:\n//\n//\t Property          | Description\n//\t-------------------+--------------------------------------------------------------------------\n//\t Application       | The name of the application that created this document.\n//\t                   |\n//\t ScaleCrop         | Indicates the display mode of the document thumbnail. Set this element\n//\t                   | to 'true' to enable scaling of the document thumbnail to the display. Set\n//\t                   | this element to 'false' to enable cropping of the document thumbnail to\n//\t                   | show only sections that will fit the display.\n//\t                   |\n//\t DocSecurity       | Security level of a document as a numeric value. Document security is\n//\t                   | defined as:\n//\t                   | 1 - Document is password protected.\n//\t                   | 2 - Document is recommended to be opened as read-only.\n//\t                   | 3 - Document is enforced to be opened as read-only.\n//\t                   | 4 - Document is locked for annotation.\n//\t                   |\n//\t Company           | The name of a company associated with the document.\n//\t                   |\n//\t LinksUpToDate     | Indicates whether hyperlinks in a document are up-to-date. Set this\n//\t                   | element to 'true' to indicate that hyperlinks are updated. Set this\n//\t                   | element to 'false' to indicate that hyperlinks are outdated.\n//\t                   |\n//\t HyperlinksChanged | Specifies that one or more hyperlinks in this part were updated\n//\t                   | exclusively in this part by a producer. The next producer to open this\n//\t                   | document shall update the hyperlink relationships with the new\n//\t                   | hyperlinks specified in this part.\n//\t                   |\n//\t AppVersion        | Specifies the version of the application which produced this document.\n//\t                   | The content of this element shall be of the form XX.YYYY where X and Y\n//\t                   | represent numerical values, or the document shall be considered\n//\t                   | non-conformant.\n//\n// For example:\n//\n//\terr := f.SetAppProps(&excelize.AppProperties{\n//\t    Application:       \"Microsoft Excel\",\n//\t    ScaleCrop:         true,\n//\t    DocSecurity:       3,\n//\t    Company:           \"Company Name\",\n//\t    LinksUpToDate:     true,\n//\t    HyperlinksChanged: true,\n//\t    AppVersion:        \"16.0000\",\n//\t})\nfunc (f *File) SetAppProps(appProperties *AppProperties) error {\n\tapp := new(xlsxProperties)\n\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsApp)))).\n\t\tDecode(app); err != nil && err != io.EOF {\n\t\treturn err\n\t}\n\tsetNoPtrFieldsVal([]string{\"Application\", \"ScaleCrop\", \"DocSecurity\", \"Company\", \"LinksUpToDate\", \"HyperlinksChanged\", \"AppVersion\"},\n\t\treflect.ValueOf(*appProperties), reflect.ValueOf(app).Elem())\n\tapp.Vt = NameSpaceDocumentPropertiesVariantTypes.Value\n\toutput, err := xml.Marshal(app)\n\tf.saveFileList(defaultXMLPathDocPropsApp, output)\n\treturn err\n}\n\n// GetAppProps provides a function to get document application properties.\nfunc (f *File) GetAppProps() (ret *AppProperties, err error) {\n\tapp := new(xlsxProperties)\n\tif err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsApp)))).\n\t\tDecode(app); err != nil && err != io.EOF {\n\t\treturn\n\t}\n\tret, err = &AppProperties{\n\t\tApplication:       app.Application,\n\t\tScaleCrop:         app.ScaleCrop,\n\t\tDocSecurity:       app.DocSecurity,\n\t\tCompany:           app.Company,\n\t\tLinksUpToDate:     app.LinksUpToDate,\n\t\tHyperlinksChanged: app.HyperlinksChanged,\n\t\tAppVersion:        app.AppVersion,\n\t}, nil\n\treturn\n}\n\n// SetDocProps provides a function to set document core properties. The\n// properties that can be set are:\n//\n//\t Property       | Description\n//\t----------------+-----------------------------------------------------------\n//\t Title          | The name given to the resource.\n//\t                |\n//\t Subject        | The topic of the content of the resource.\n//\t                |\n//\t Creator        | An entity primarily responsible for making the content of\n//\t                | the resource.\n//\t                |\n//\t Keywords       | A delimited set of keywords to support searching and\n//\t                | indexing. This is typically a list of terms that are not\n//\t                | available elsewhere in the properties.\n//\t                |\n//\t Description    | An explanation of the content of the resource.\n//\t                |\n//\t LastModifiedBy | The user who performed the last modification. The\n//\t                | identification is environment-specific.\n//\t                |\n//\t Language       | The language of the intellectual content of the resource.\n//\t                |\n//\t Identifier     | An unambiguous reference to the resource within a given\n//\t                | context.\n//\t                |\n//\t Revision       | The topic of the content of the resource.\n//\t                |\n//\t ContentStatus  | The status of the content. For example: Values might\n//\t                | include \"Draft\", \"Reviewed\" and \"Final\"\n//\t                |\n//\t Category       | A categorization of the content of this package.\n//\t                |\n//\t Version        | The version number. This value is set by the user or by\n//\t                | the application.\n//\t                |\n//\t Created        | The created time of the content of the resource which\n//\t                | represent in ISO 8601 UTC format, for example\n//\t                | \"2019-06-04T22:00:10Z\".\n//\t                |\n//\t Modified       | The modified time of the content of the resource which\n//\t                | represent in ISO 8601 UTC format, for example\n//\t                | \"2019-06-04T22:00:10Z\".\n//\t                |\n//\n// For example:\n//\n//\terr := f.SetDocProps(&excelize.DocProperties{\n//\t    Category:       \"category\",\n//\t    ContentStatus:  \"Draft\",\n//\t    Created:        \"2019-06-04T22:00:10Z\",\n//\t    Creator:        \"Go Excelize\",\n//\t    Description:    \"This file created by Go Excelize\",\n//\t    Identifier:     \"xlsx\",\n//\t    Keywords:       \"Spreadsheet\",\n//\t    LastModifiedBy: \"Go Author\",\n//\t    Modified:       \"2019-06-04T22:00:10Z\",\n//\t    Revision:       \"0\",\n//\t    Subject:        \"Test Subject\",\n//\t    Title:          \"Test Title\",\n//\t    Language:       \"en-US\",\n//\t    Version:        \"1.0.0\",\n//\t})\nfunc (f *File) SetDocProps(docProperties *DocProperties) error {\n\tcore := new(decodeCoreProperties)\n\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCore)))).\n\t\tDecode(core); err != nil && err != io.EOF {\n\t\treturn err\n\t}\n\tnewProps := &xlsxCoreProperties{\n\t\tDc:             NameSpaceDublinCore,\n\t\tDcterms:        NameSpaceDublinCoreTerms,\n\t\tDcmitype:       NameSpaceDublinCoreMetadataInitiative,\n\t\tXSI:            NameSpaceXMLSchemaInstance,\n\t\tTitle:          core.Title,\n\t\tSubject:        core.Subject,\n\t\tCreator:        core.Creator,\n\t\tKeywords:       core.Keywords,\n\t\tDescription:    core.Description,\n\t\tLastModifiedBy: core.LastModifiedBy,\n\t\tLanguage:       core.Language,\n\t\tIdentifier:     core.Identifier,\n\t\tRevision:       core.Revision,\n\t\tContentStatus:  core.ContentStatus,\n\t\tCategory:       core.Category,\n\t\tVersion:        core.Version,\n\t}\n\tif core.Created != nil {\n\t\tnewProps.Created = &xlsxDcTerms{Type: core.Created.Type, Text: core.Created.Text}\n\t}\n\tif core.Modified != nil {\n\t\tnewProps.Modified = &xlsxDcTerms{Type: core.Modified.Type, Text: core.Modified.Text}\n\t}\n\tsetNoPtrFieldsVal([]string{\n\t\t\"Category\", \"ContentStatus\", \"Creator\", \"Description\", \"Identifier\", \"Keywords\",\n\t\t\"LastModifiedBy\", \"Revision\", \"Subject\", \"Title\", \"Language\", \"Version\",\n\t}, reflect.ValueOf(*docProperties), reflect.ValueOf(newProps).Elem())\n\tif docProperties.Created != \"\" {\n\t\tnewProps.Created = &xlsxDcTerms{Type: \"dcterms:W3CDTF\", Text: docProperties.Created}\n\t}\n\tif docProperties.Modified != \"\" {\n\t\tnewProps.Modified = &xlsxDcTerms{Type: \"dcterms:W3CDTF\", Text: docProperties.Modified}\n\t}\n\toutput, err := xml.Marshal(newProps)\n\tf.saveFileList(defaultXMLPathDocPropsCore, output)\n\n\treturn err\n}\n\n// GetDocProps provides a function to get document core properties.\nfunc (f *File) GetDocProps() (ret *DocProperties, err error) {\n\tcore := new(decodeCoreProperties)\n\n\tif err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCore)))).\n\t\tDecode(core); err != nil && err != io.EOF {\n\t\treturn\n\t}\n\tret, err = &DocProperties{\n\t\tCategory:       core.Category,\n\t\tContentStatus:  core.ContentStatus,\n\t\tCreator:        core.Creator,\n\t\tDescription:    core.Description,\n\t\tIdentifier:     core.Identifier,\n\t\tKeywords:       core.Keywords,\n\t\tLastModifiedBy: core.LastModifiedBy,\n\t\tRevision:       core.Revision,\n\t\tSubject:        core.Subject,\n\t\tTitle:          core.Title,\n\t\tLanguage:       core.Language,\n\t\tVersion:        core.Version,\n\t}, nil\n\tif core.Created != nil {\n\t\tret.Created = core.Created.Text\n\t}\n\tif core.Modified != nil {\n\t\tret.Modified = core.Modified.Text\n\t}\n\treturn\n}\n\n// SetCustomProps provides a function to set custom file properties by given\n// property name and value. If the property name already exists, it will be\n// updated, otherwise a new property will be added. The value can be of type\n// int32, float64, bool, string, time.Time or nil. The property will be delete\n// if the value is nil. The function returns an error if the property value is\n// not of the correct type.\nfunc (f *File) SetCustomProps(prop CustomProperty) error {\n\tif prop.Name == \"\" {\n\t\treturn ErrParameterInvalid\n\t}\n\tprops := new(decodeCustomProperties)\n\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCustom)))).\n\t\tDecode(props); err != nil && err != io.EOF {\n\t\treturn err\n\t}\n\tcustomProps := xlsxCustomProperties{Vt: NameSpaceDocumentPropertiesVariantTypes.Value}\n\tidx, pID := -1, 1\n\tfor i := range props.Property {\n\t\tp := new(xlsxProperty)\n\t\tsetPtrFields(reflect.ValueOf(&props.Property[i]).Elem(), reflect.ValueOf(p).Elem())\n\t\tif pID < props.Property[i].PID {\n\t\t\tpID = props.Property[i].PID\n\t\t}\n\t\tif props.Property[i].Name == prop.Name {\n\t\t\tidx = i\n\t\t}\n\t\tcustomProps.Property = append(customProps.Property, *p)\n\t}\n\tif idx != -1 && prop.Value == nil {\n\t\tcustomProps.Property = slices.Delete(customProps.Property, idx, idx+1)\n\t}\n\tif prop.Value != nil {\n\t\tproperty := xlsxProperty{Name: prop.Name, FmtID: EXtURICustomPropertyFmtID}\n\t\tif err := property.setCustomProps(prop.Value); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif idx != -1 {\n\t\t\tproperty.PID = customProps.Property[idx].PID\n\t\t\tcustomProps.Property[idx] = property\n\t\t} else {\n\t\t\tproperty.PID = pID + 1\n\t\t\tcustomProps.Property = append(customProps.Property, property)\n\t\t}\n\t}\n\t_ = f.addRels(defaultXMLPathRels, SourceRelationshipCustomProperties, defaultXMLPathDocPropsCustom, \"\")\n\tif err := f.addContentTypePart(0, \"customProperties\"); err != nil {\n\t\treturn err\n\t}\n\toutput, err := xml.Marshal(customProps)\n\tf.saveFileList(defaultXMLPathDocPropsCustom, output)\n\treturn err\n}\n\n// setCustomProps sets the custom property value based on its type.\nfunc (prop *xlsxProperty) setCustomProps(value interface{}) error {\n\tswitch v := value.(type) {\n\tcase int32:\n\t\tprop.I4 = &v\n\tcase float64:\n\t\tprop.R8 = float64Ptr(v)\n\tcase bool:\n\t\tprop.Bool = boolPtr(v)\n\tcase string:\n\t\tprop.Lpwstr = stringPtr(value.(string))\n\tcase time.Time:\n\t\tprop.FileTime = stringPtr(value.(time.Time).Format(time.RFC3339))\n\tdefault:\n\t\treturn ErrParameterInvalid\n\t}\n\treturn nil\n}\n\n// GetCustomProps provides a function to get custom file properties.\nfunc (f *File) GetCustomProps() ([]CustomProperty, error) {\n\tvar customProps []CustomProperty\n\tprops := new(decodeCustomProperties)\n\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCustom)))).\n\t\tDecode(props); err != nil && err != io.EOF {\n\t\treturn customProps, err\n\t}\n\tfor _, p := range props.Property {\n\t\tprop := CustomProperty{Name: p.Name}\n\t\tvar err error\n\t\tif prop.Value, err = p.getCustomProps(); err != nil {\n\t\t\treturn customProps, err\n\t\t}\n\t\tcustomProps = append(customProps, prop)\n\t}\n\treturn customProps, nil\n}\n\n// getCustomProps gets the custom property value based on its type.\nfunc (p *decodeProperty) getCustomProps() (interface{}, error) {\n\ts := reflect.ValueOf(p).Elem()\n\tfor i := range s.NumField() {\n\t\tif 11 <= i && i <= 20 && !s.Field(i).IsNil() {\n\t\t\treturn int32(s.Field(i).Elem().Int()), nil // Field vt:i1 to vt:uint\n\t\t}\n\t\tif 21 <= i && i <= 22 && !s.Field(i).IsNil() {\n\t\t\treturn s.Field(i).Elem().Float(), nil // Field vt:r4 to vt:r8\n\t\t}\n\t\tif p.Bool != nil {\n\t\t\treturn *p.Bool, nil\n\t\t}\n\t\tif p.Lpwstr != nil {\n\t\t\treturn *p.Lpwstr, nil\n\t\t}\n\t\tif p.FileTime != nil {\n\t\t\treturn time.Parse(time.RFC3339, *p.FileTime)\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "docProps_test.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar MacintoshCyrillicCharset = []byte{0x8F, 0xF0, 0xE8, 0xE2, 0xE5, 0xF2, 0x20, 0xEC, 0xE8, 0xF0}\n\nfunc TestSetAppProps(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tassert.NoError(t, f.SetAppProps(&AppProperties{\n\t\tApplication:       \"Microsoft Excel\",\n\t\tScaleCrop:         true,\n\t\tDocSecurity:       3,\n\t\tCompany:           \"Company Name\",\n\t\tLinksUpToDate:     true,\n\t\tHyperlinksChanged: true,\n\t\tAppVersion:        \"16.0000\",\n\t}))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetAppProps.xlsx\")))\n\tf.Pkg.Store(defaultXMLPathDocPropsApp, nil)\n\tassert.NoError(t, f.SetAppProps(&AppProperties{}))\n\tassert.NoError(t, f.Close())\n\n\t// Test unsupported charset\n\tf = NewFile()\n\tf.Pkg.Store(defaultXMLPathDocPropsApp, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetAppProps(&AppProperties{}), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestGetAppProps(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tprops, err := f.GetAppProps()\n\tassert.NoError(t, err)\n\tassert.Equal(t, props.Application, \"Microsoft Macintosh Excel\")\n\tf.Pkg.Store(defaultXMLPathDocPropsApp, nil)\n\t_, err = f.GetAppProps()\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Close())\n\n\t// Test get application properties with unsupported charset\n\tf = NewFile()\n\tf.Pkg.Store(defaultXMLPathDocPropsApp, MacintoshCyrillicCharset)\n\t_, err = f.GetAppProps()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestSetDocProps(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tassert.NoError(t, f.SetDocProps(&DocProperties{\n\t\tCategory:       \"category\",\n\t\tContentStatus:  \"Draft\",\n\t\tCreated:        \"2019-06-04T22:00:10Z\",\n\t\tCreator:        \"Go Excelize\",\n\t\tDescription:    \"This file created by Go Excelize\",\n\t\tIdentifier:     \"xlsx\",\n\t\tKeywords:       \"Spreadsheet\",\n\t\tLastModifiedBy: \"Go Author\",\n\t\tModified:       \"2019-06-04T22:00:10Z\",\n\t\tRevision:       \"0\",\n\t\tSubject:        \"Test Subject\",\n\t\tTitle:          \"Test Title\",\n\t\tLanguage:       \"en-US\",\n\t\tVersion:        \"1.0.0\",\n\t}))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetDocProps.xlsx\")))\n\tf.Pkg.Store(defaultXMLPathDocPropsCore, nil)\n\tassert.NoError(t, f.SetDocProps(&DocProperties{}))\n\tassert.NoError(t, f.Close())\n\n\t// Test unsupported charset\n\tf = NewFile()\n\tf.Pkg.Store(defaultXMLPathDocPropsCore, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetDocProps(&DocProperties{}), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestGetDocProps(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tprops, err := f.GetDocProps()\n\tassert.NoError(t, err)\n\tassert.Equal(t, props.Creator, \"Microsoft Office User\")\n\tf.Pkg.Store(defaultXMLPathDocPropsCore, nil)\n\t_, err = f.GetDocProps()\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Close())\n\n\t// Test get workbook properties with unsupported charset\n\tf = NewFile()\n\tf.Pkg.Store(defaultXMLPathDocPropsCore, MacintoshCyrillicCharset)\n\t_, err = f.GetDocProps()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestCustomProps(t *testing.T) {\n\tf := NewFile()\n\texpected := []CustomProperty{\n\t\t{Name: \"Text Prop\", Value: \"text\"},\n\t\t{Name: \"Boolean Prop 1\", Value: true},\n\t\t{Name: \"Boolean Prop 2\", Value: false},\n\t\t{Name: \"Number Prop 1\", Value: -123.456},\n\t\t{Name: \"Number Prop 2\", Value: int32(1)},\n\t\t{Name: \"Date Prop\", Value: time.Date(2021, time.September, 11, 0, 0, 0, 0, time.UTC)},\n\t}\n\tfor _, prop := range expected {\n\t\tassert.NoError(t, f.SetCustomProps(prop))\n\t}\n\tprops, err := f.GetCustomProps()\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected, props)\n\n\t// Test delete custom property\n\tassert.NoError(t, f.SetCustomProps(CustomProperty{Name: \"Boolean Prop 1\", Value: nil}))\n\tprops, err = f.GetCustomProps()\n\tassert.NoError(t, err)\n\texpected = slices.Delete(expected, 1, 2)\n\tassert.Equal(t, expected, props)\n\n\t// Test change custom property value data type\n\tassert.NoError(t, f.SetCustomProps(CustomProperty{Name: \"Boolean Prop 2\", Value: \"true\"}))\n\tprops, err = f.GetCustomProps()\n\tassert.NoError(t, err)\n\tassert.Equal(t, props[1].Value, \"true\")\n\n\t// Test set custom property with unsupported value data type\n\tassert.Equal(t, ErrParameterInvalid, f.SetCustomProps(CustomProperty{Name: \"Boolean Prop 2\", Value: 1}))\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCustomProps.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\t// Test set custom property without property name\n\tf = NewFile()\n\tassert.Equal(t, ErrParameterInvalid, f.SetCustomProps(CustomProperty{}))\n\n\t// Test set custom property with unsupported charset\n\tf.Pkg.Store(defaultXMLPathDocPropsCustom, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetCustomProps(CustomProperty{Name: \"Prop\"}), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test get custom property with unsupported charset\n\t_, err = f.GetCustomProps()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test set custom property with unsupported charset content types\n\tf = NewFile()\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetCustomProps(CustomProperty{Name: \"Prop\"}), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test get custom property with unsupported charset\n\tf.Pkg.Store(defaultXMLPathDocPropsCustom, []byte(fmt.Sprintf(`<Properties xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/custom-properties\" xmlns:vt=\"%s\"><property fmtid=\"%s\" pid=\"2\" name=\"Prop\"><vt:filetime>x</vt:filetime></property></Properties>`, NameSpaceDocumentPropertiesVariantTypes, EXtURICustomPropertyFmtID)))\n\t_, err = f.GetCustomProps()\n\tassert.EqualError(t, err, \"parsing time \\\"x\\\" as \\\"2006-01-02T15:04:05Z07:00\\\": cannot parse \\\"x\\\" as \\\"2006\\\"\")\n\n\t// Test get custom property with unsupported value data type\n\tf.Pkg.Store(defaultXMLPathDocPropsCustom, []byte(fmt.Sprintf(`<Properties xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/custom-properties\" xmlns:vt=\"%s\"><property fmtid=\"%s\" pid=\"2\" name=\"Prop\"><vt:cy></vt:cy></property></Properties>`, NameSpaceDocumentPropertiesVariantTypes, EXtURICustomPropertyFmtID)))\n\tprops, err = f.GetCustomProps()\n\tassert.Equal(t, []CustomProperty{{Name: \"Prop\"}}, props)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Close())\n}\n"
  },
  {
    "path": "drawing.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"io\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// prepareDrawing provides a function to prepare drawing ID and XML by given\n// drawingID, worksheet name and default drawingXML.\nfunc (f *File) prepareDrawing(ws *xlsxWorksheet, drawingID int, sheet, drawingXML string) (int, string) {\n\tsheetRelationshipsDrawingXML := \"../drawings/drawing\" + strconv.Itoa(drawingID) + \".xml\"\n\tif ws.Drawing != nil {\n\t\t// The worksheet already has a picture or chart relationships, use the\n\t\t// relationships drawing ../drawings/drawing%d.xml or /xl/drawings/drawing%d.xml.\n\t\tsheetRelationshipsDrawingXML = strings.ReplaceAll(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), \"/xl/drawings/\", \"../drawings/\")\n\t\tdrawingID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingXML, \"../drawings/drawing\"), \".xml\"))\n\t\tdrawingXML = strings.ReplaceAll(sheetRelationshipsDrawingXML, \"..\", \"xl\")\n\t} else {\n\t\t// Add first picture for given sheet.\n\t\tsheetXMLPath, _ := f.getSheetXMLPath(sheet)\n\t\tsheetRels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(sheetXMLPath, \"xl/worksheets/\") + \".rels\"\n\t\trID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, \"\")\n\t\tf.addSheetDrawing(sheet, rID)\n\t}\n\treturn drawingID, drawingXML\n}\n\n// prepareChartSheetDrawing provides a function to prepare drawing ID and XML\n// by given drawingID, worksheet name and default drawingXML.\nfunc (f *File) prepareChartSheetDrawing(cs *xlsxChartsheet, drawingID int, sheet string) {\n\tsheetRelationshipsDrawingXML := \"../drawings/drawing\" + strconv.Itoa(drawingID) + \".xml\"\n\t// Only allow one chart in a chartsheet.\n\tsheetXMLPath, _ := f.getSheetXMLPath(sheet)\n\tsheetRels := \"xl/chartsheets/_rels/\" + strings.TrimPrefix(sheetXMLPath, \"xl/chartsheets/\") + \".rels\"\n\trID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, \"\")\n\tf.addSheetNameSpace(sheet, SourceRelationship)\n\tcs.Drawing = &xlsxDrawing{\n\t\tRID: \"rId\" + strconv.Itoa(rID),\n\t}\n}\n\n// addChart provides a function to create chart as xl/charts/chart%d.xml by\n// given format sets.\nfunc (f *File) addChart(opts *Chart, comboCharts []*Chart) {\n\tcount := f.countCharts()\n\txlsxChartSpace := xlsxChartSpace{\n\t\tXMLNSa:         NameSpaceDrawingML.Value,\n\t\tDate1904:       &attrValBool{Val: boolPtr(false)},\n\t\tLang:           &attrValString{Val: stringPtr(\"en-US\")},\n\t\tRoundedCorners: &attrValBool{Val: boolPtr(false)},\n\t\tChart: cChart{\n\t\t\tTitle: f.drawPlotAreaTitles(opts.Title, \"\"),\n\t\t\tView3D: &cView3D{\n\t\t\t\tRotX:        &attrValInt{Val: intPtr(chartView3DRotX[opts.Type])},\n\t\t\t\tRotY:        &attrValInt{Val: intPtr(chartView3DRotY[opts.Type])},\n\t\t\t\tPerspective: &attrValInt{Val: intPtr(chartView3DPerspective[opts.Type])},\n\t\t\t\tRAngAx:      &attrValInt{Val: intPtr(chartView3DRAngAx[opts.Type])},\n\t\t\t},\n\t\t\tFloor: &cThicknessSpPr{\n\t\t\t\tThickness: &attrValInt{Val: intPtr(0)},\n\t\t\t},\n\t\t\tSideWall: &cThicknessSpPr{\n\t\t\t\tThickness: &attrValInt{Val: intPtr(0)},\n\t\t\t},\n\t\t\tBackWall: &cThicknessSpPr{\n\t\t\t\tThickness: &attrValInt{Val: intPtr(0)},\n\t\t\t},\n\t\t\tPlotArea:         &cPlotArea{},\n\t\t\tPlotVisOnly:      &attrValBool{Val: boolPtr(false)},\n\t\t\tDispBlanksAs:     &attrValString{Val: stringPtr(opts.ShowBlanksAs)},\n\t\t\tShowDLblsOverMax: &attrValBool{Val: boolPtr(false)},\n\t\t},\n\t\tSpPr: &cSpPr{\n\t\t\tSolidFill: &aSolidFill{\n\t\t\t\tSchemeClr: &aSchemeClr{Val: \"bg1\"},\n\t\t\t},\n\t\t\tLn: f.drawChartLn(&opts.Border),\n\t\t},\n\t\tPrintSettings: &cPrintSettings{\n\t\t\tPageMargins: &cPageMargins{\n\t\t\t\tB:      0.75,\n\t\t\t\tL:      0.7,\n\t\t\t\tR:      0.7,\n\t\t\t\tT:      0.7,\n\t\t\t\tHeader: 0.3,\n\t\t\t\tFooter: 0.3,\n\t\t\t},\n\t\t},\n\t}\n\txlsxChartSpace.SpPr = f.drawShapeFill(opts.Fill, xlsxChartSpace.SpPr)\n\tplotAreaFunc := map[ChartType]func(pa *cPlotArea, opts *Chart) *cPlotArea{\n\t\tArea:                        f.drawBaseChart,\n\t\tAreaStacked:                 f.drawBaseChart,\n\t\tAreaPercentStacked:          f.drawBaseChart,\n\t\tArea3D:                      f.drawBaseChart,\n\t\tArea3DStacked:               f.drawBaseChart,\n\t\tArea3DPercentStacked:        f.drawBaseChart,\n\t\tBar:                         f.drawBaseChart,\n\t\tBarStacked:                  f.drawBaseChart,\n\t\tBarPercentStacked:           f.drawBaseChart,\n\t\tBar3DClustered:              f.drawBaseChart,\n\t\tBar3DStacked:                f.drawBaseChart,\n\t\tBar3DPercentStacked:         f.drawBaseChart,\n\t\tBar3DConeClustered:          f.drawBaseChart,\n\t\tBar3DConeStacked:            f.drawBaseChart,\n\t\tBar3DConePercentStacked:     f.drawBaseChart,\n\t\tBar3DPyramidClustered:       f.drawBaseChart,\n\t\tBar3DPyramidStacked:         f.drawBaseChart,\n\t\tBar3DPyramidPercentStacked:  f.drawBaseChart,\n\t\tBar3DCylinderClustered:      f.drawBaseChart,\n\t\tBar3DCylinderStacked:        f.drawBaseChart,\n\t\tBar3DCylinderPercentStacked: f.drawBaseChart,\n\t\tCol:                         f.drawBaseChart,\n\t\tColStacked:                  f.drawBaseChart,\n\t\tColPercentStacked:           f.drawBaseChart,\n\t\tCol3D:                       f.drawBaseChart,\n\t\tCol3DClustered:              f.drawBaseChart,\n\t\tCol3DStacked:                f.drawBaseChart,\n\t\tCol3DPercentStacked:         f.drawBaseChart,\n\t\tCol3DCone:                   f.drawBaseChart,\n\t\tCol3DConeClustered:          f.drawBaseChart,\n\t\tCol3DConeStacked:            f.drawBaseChart,\n\t\tCol3DConePercentStacked:     f.drawBaseChart,\n\t\tCol3DPyramid:                f.drawBaseChart,\n\t\tCol3DPyramidClustered:       f.drawBaseChart,\n\t\tCol3DPyramidStacked:         f.drawBaseChart,\n\t\tCol3DPyramidPercentStacked:  f.drawBaseChart,\n\t\tCol3DCylinder:               f.drawBaseChart,\n\t\tCol3DCylinderClustered:      f.drawBaseChart,\n\t\tCol3DCylinderStacked:        f.drawBaseChart,\n\t\tCol3DCylinderPercentStacked: f.drawBaseChart,\n\t\tDoughnut:                    f.drawDoughnutChart,\n\t\tLine:                        f.drawLineChart,\n\t\tLine3D:                      f.drawLine3DChart,\n\t\tPie:                         f.drawPieChart,\n\t\tPie3D:                       f.drawPie3DChart,\n\t\tPieOfPie:                    f.drawPieOfPieChart,\n\t\tBarOfPie:                    f.drawBarOfPieChart,\n\t\tRadar:                       f.drawRadarChart,\n\t\tScatter:                     f.drawScatterChart,\n\t\tSurface3D:                   f.drawSurface3DChart,\n\t\tWireframeSurface3D:          f.drawSurface3DChart,\n\t\tContour:                     f.drawSurfaceChart,\n\t\tWireframeContour:            f.drawSurfaceChart,\n\t\tBubble:                      f.drawBubbleChart,\n\t\tBubble3D:                    f.drawBubbleChart,\n\t\tStockHighLowClose:           f.drawStockChart,\n\t\tStockOpenHighLowClose:       f.drawStockChart,\n\t}\n\txlsxChartSpace.Chart.drawChartLegend(opts)\n\txlsxChartSpace.Chart.PlotArea.SpPr = f.drawShapeFill(opts.PlotArea.Fill, xlsxChartSpace.Chart.PlotArea.SpPr)\n\txlsxChartSpace.Chart.PlotArea.DTable = f.drawPlotAreaDTable(opts)\n\taddChart := func(c, p *cPlotArea) {\n\t\timmutable, mutable := reflect.ValueOf(c).Elem(), reflect.ValueOf(p).Elem()\n\t\tfor i := 0; i < mutable.NumField(); i++ {\n\t\t\tfield := mutable.Field(i)\n\t\t\tif field.IsNil() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfld := immutable.FieldByName(mutable.Type().Field(i).Name)\n\t\t\tif field.Kind() == reflect.Slice && i < 17 { // All []*cCharts type fields\n\t\t\t\tfld.Set(reflect.Append(fld, field.Index(0)))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfld.Set(field)\n\t\t}\n\t}\n\taddChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[opts.Type](xlsxChartSpace.Chart.PlotArea, opts))\n\torder := len(opts.Series)\n\tfor idx := range comboCharts {\n\t\tcomboCharts[idx].order = order\n\t\taddChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[comboCharts[idx].Type](xlsxChartSpace.Chart.PlotArea, comboCharts[idx]))\n\t\torder += len(comboCharts[idx].Series)\n\t}\n\t// If the dateAx field exists, valAx field should be nil.\n\tif xlsxChartSpace.Chart.PlotArea != nil && xlsxChartSpace.Chart.PlotArea.DateAx != nil {\n\t\txlsxChartSpace.Chart.PlotArea.CatAx = nil\n\t}\n\tchart, _ := xml.Marshal(xlsxChartSpace)\n\tmedia := \"xl/charts/chart\" + strconv.Itoa(count+1) + \".xml\"\n\tf.saveFileList(media, chart)\n}\n\n// drawBaseChart provides a function to draw the c:plotArea element for bar,\n// and column series charts by given format sets.\nfunc (f *File) drawBaseChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\tc := []*cCharts{\n\t\t{\n\t\t\tBarDir: &attrValString{\n\t\t\t\tVal: stringPtr(\"col\"),\n\t\t\t},\n\t\t\tGrouping: &attrValString{\n\t\t\t\tVal: stringPtr(plotAreaChartGrouping[opts.Type]),\n\t\t\t},\n\t\t\tVaryColors: &attrValBool{\n\t\t\t\tVal: opts.VaryColors,\n\t\t\t},\n\t\t\tSer:      f.drawChartSeries(opts),\n\t\t\tShape:    f.drawChartShape(opts),\n\t\t\tDLbls:    f.drawChartDLbls(opts),\n\t\t\tGapWidth: f.drawChartGapWidth(opts),\n\t\t\tAxID:     f.genAxID(opts),\n\t\t\tOverlap:  f.drawChartOverlap(opts),\n\t\t},\n\t}\n\tvar ok bool\n\tif *c[0].BarDir.Val, ok = plotAreaChartBarDir[opts.Type]; !ok {\n\t\tc[0].BarDir = nil\n\t\tif opts.XAxis.DropLines {\n\t\t\tc[0].DropLines = &cChartLines{}\n\t\t}\n\t}\n\tcatAx := f.drawPlotAreaCatAx(pa, opts)\n\tvalAx := f.drawPlotAreaValAx(pa, opts)\n\tcharts := map[ChartType]*cPlotArea{\n\t\tArea: {\n\t\t\tAreaChart: c,\n\t\t\tCatAx:     catAx,\n\t\t\tValAx:     valAx,\n\t\t},\n\t\tAreaStacked: {\n\t\t\tAreaChart: c,\n\t\t\tCatAx:     catAx,\n\t\t\tValAx:     valAx,\n\t\t},\n\t\tAreaPercentStacked: {\n\t\t\tAreaChart: c,\n\t\t\tCatAx:     catAx,\n\t\t\tValAx:     valAx,\n\t\t},\n\t\tArea3D: {\n\t\t\tArea3DChart: c,\n\t\t\tCatAx:       catAx,\n\t\t\tValAx:       valAx,\n\t\t},\n\t\tArea3DStacked: {\n\t\t\tArea3DChart: c,\n\t\t\tCatAx:       catAx,\n\t\t\tValAx:       valAx,\n\t\t},\n\t\tArea3DPercentStacked: {\n\t\t\tArea3DChart: c,\n\t\t\tCatAx:       catAx,\n\t\t\tValAx:       valAx,\n\t\t},\n\t\tBar: {\n\t\t\tBarChart: c,\n\t\t\tCatAx:    catAx,\n\t\t\tValAx:    valAx,\n\t\t},\n\t\tBarStacked: {\n\t\t\tBarChart: c,\n\t\t\tCatAx:    catAx,\n\t\t\tValAx:    valAx,\n\t\t},\n\t\tBarPercentStacked: {\n\t\t\tBarChart: c,\n\t\t\tCatAx:    catAx,\n\t\t\tValAx:    valAx,\n\t\t},\n\t\tBar3DClustered: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tBar3DStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tBar3DPercentStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tBar3DConeClustered: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tBar3DConeStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tBar3DConePercentStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tBar3DPyramidClustered: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tBar3DPyramidStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tBar3DPyramidPercentStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tBar3DCylinderClustered: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tBar3DCylinderStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tBar3DCylinderPercentStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol: {\n\t\t\tBarChart: c,\n\t\t\tCatAx:    catAx,\n\t\t\tValAx:    valAx,\n\t\t},\n\t\tColStacked: {\n\t\t\tBarChart: c,\n\t\t\tCatAx:    catAx,\n\t\t\tValAx:    valAx,\n\t\t},\n\t\tColPercentStacked: {\n\t\t\tBarChart: c,\n\t\t\tCatAx:    catAx,\n\t\t\tValAx:    valAx,\n\t\t},\n\t\tCol3D: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DClustered: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DPercentStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DCone: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DConeClustered: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DConeStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DConePercentStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DPyramid: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DPyramidClustered: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DPyramidStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DPyramidPercentStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DCylinder: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DCylinderClustered: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DCylinderStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tCol3DCylinderPercentStacked: {\n\t\t\tBar3DChart: c,\n\t\t\tCatAx:      catAx,\n\t\t\tValAx:      valAx,\n\t\t},\n\t\tBubble: {\n\t\t\tBubbleChart: c,\n\t\t\tCatAx:       catAx,\n\t\t\tValAx:       valAx,\n\t\t},\n\t\tBubble3D: {\n\t\t\tBubbleChart: c,\n\t\t\tCatAx:       catAx,\n\t\t\tValAx:       valAx,\n\t\t},\n\t}\n\treturn charts[opts.Type]\n}\n\n// drawDoughnutChart provides a function to draw the c:plotArea element for\n// doughnut chart by given format sets.\nfunc (f *File) drawDoughnutChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\tholeSize := 75\n\tif opts.HoleSize > 0 && opts.HoleSize <= 90 {\n\t\tholeSize = opts.HoleSize\n\t}\n\n\treturn &cPlotArea{\n\t\tDoughnutChart: []*cCharts{\n\t\t\t{\n\t\t\t\tVaryColors: &attrValBool{\n\t\t\t\t\tVal: opts.VaryColors,\n\t\t\t\t},\n\t\t\t\tSer:      f.drawChartSeries(opts),\n\t\t\t\tHoleSize: &attrValInt{Val: intPtr(holeSize)},\n\t\t\t},\n\t\t},\n\t}\n}\n\n// drawLineChart provides a function to draw the c:plotArea element for line\n// chart by given format sets.\nfunc (f *File) drawLineChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\tplotArea := &cPlotArea{\n\t\tLineChart: []*cCharts{\n\t\t\t{\n\t\t\t\tGrouping: &attrValString{\n\t\t\t\t\tVal: stringPtr(plotAreaChartGrouping[opts.Type]),\n\t\t\t\t},\n\t\t\t\tVaryColors: &attrValBool{\n\t\t\t\t\tVal: boolPtr(false),\n\t\t\t\t},\n\t\t\t\tSer:   f.drawChartSeries(opts),\n\t\t\t\tDLbls: f.drawChartDLbls(opts),\n\t\t\t\tAxID:  f.genAxID(opts),\n\t\t\t},\n\t\t},\n\t\tCatAx: f.drawPlotAreaCatAx(pa, opts),\n\t\tValAx: f.drawPlotAreaValAx(pa, opts),\n\t}\n\tif opts.XAxis.DropLines {\n\t\tplotArea.LineChart[0].DropLines = &cChartLines{}\n\t}\n\tif opts.XAxis.HighLowLines {\n\t\tplotArea.LineChart[0].HiLowLines = &cChartLines{}\n\t}\n\treturn plotArea\n}\n\n// drawLine3DChart provides a function to draw the c:plotArea element for line\n// chart by given format sets.\nfunc (f *File) drawLine3DChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\tplotArea := &cPlotArea{\n\t\tLine3DChart: []*cCharts{\n\t\t\t{\n\t\t\t\tGrouping: &attrValString{\n\t\t\t\t\tVal: stringPtr(plotAreaChartGrouping[opts.Type]),\n\t\t\t\t},\n\t\t\t\tVaryColors: &attrValBool{\n\t\t\t\t\tVal: boolPtr(false),\n\t\t\t\t},\n\t\t\t\tSer:   f.drawChartSeries(opts),\n\t\t\t\tDLbls: f.drawChartDLbls(opts),\n\t\t\t\tAxID:  f.genAxID(opts),\n\t\t\t},\n\t\t},\n\t\tCatAx: f.drawPlotAreaCatAx(pa, opts),\n\t\tValAx: f.drawPlotAreaValAx(pa, opts),\n\t}\n\tif opts.XAxis.DropLines {\n\t\tplotArea.Line3DChart[0].DropLines = &cChartLines{}\n\t}\n\treturn plotArea\n}\n\n// drawPieChart provides a function to draw the c:plotArea element for pie\n// chart by given format sets.\nfunc (f *File) drawPieChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\treturn &cPlotArea{\n\t\tPieChart: []*cCharts{\n\t\t\t{\n\t\t\t\tVaryColors: &attrValBool{\n\t\t\t\t\tVal: opts.VaryColors,\n\t\t\t\t},\n\t\t\t\tSer: f.drawChartSeries(opts),\n\t\t\t},\n\t\t},\n\t}\n}\n\n// drawPie3DChart provides a function to draw the c:plotArea element for 3D\n// pie chart by given format sets.\nfunc (f *File) drawPie3DChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\treturn &cPlotArea{\n\t\tPie3DChart: []*cCharts{\n\t\t\t{\n\t\t\t\tVaryColors: &attrValBool{\n\t\t\t\t\tVal: opts.VaryColors,\n\t\t\t\t},\n\t\t\t\tSer: f.drawChartSeries(opts),\n\t\t\t},\n\t\t},\n\t}\n}\n\n// drawPieOfPieChart provides a function to draw the c:plotArea element for\n// pie chart by given format sets.\nfunc (f *File) drawPieOfPieChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\tvar splitPos *attrValInt\n\tif opts.PlotArea.SecondPlotValues > 0 {\n\t\tsplitPos = &attrValInt{Val: intPtr(opts.PlotArea.SecondPlotValues)}\n\t}\n\treturn &cPlotArea{\n\t\tOfPieChart: []*cCharts{\n\t\t\t{\n\t\t\t\tOfPieType: &attrValString{\n\t\t\t\t\tVal: stringPtr(\"pie\"),\n\t\t\t\t},\n\t\t\t\tVaryColors: &attrValBool{\n\t\t\t\t\tVal: opts.VaryColors,\n\t\t\t\t},\n\t\t\t\tSer:      f.drawChartSeries(opts),\n\t\t\t\tSplitPos: splitPos,\n\t\t\t\tSerLines: &attrValString{},\n\t\t\t},\n\t\t},\n\t}\n}\n\n// drawBarOfPieChart provides a function to draw the c:plotArea element for\n// pie chart by given format sets.\nfunc (f *File) drawBarOfPieChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\tvar splitPos *attrValInt\n\tif opts.PlotArea.SecondPlotValues > 0 {\n\t\tsplitPos = &attrValInt{Val: intPtr(opts.PlotArea.SecondPlotValues)}\n\t}\n\treturn &cPlotArea{\n\t\tOfPieChart: []*cCharts{\n\t\t\t{\n\t\t\t\tOfPieType: &attrValString{\n\t\t\t\t\tVal: stringPtr(\"bar\"),\n\t\t\t\t},\n\t\t\t\tVaryColors: &attrValBool{\n\t\t\t\t\tVal: opts.VaryColors,\n\t\t\t\t},\n\t\t\t\tSplitPos: splitPos,\n\t\t\t\tSer:      f.drawChartSeries(opts),\n\t\t\t\tSerLines: &attrValString{},\n\t\t\t},\n\t\t},\n\t}\n}\n\n// drawRadarChart provides a function to draw the c:plotArea element for radar\n// chart by given format sets.\nfunc (f *File) drawRadarChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\treturn &cPlotArea{\n\t\tRadarChart: []*cCharts{\n\t\t\t{\n\t\t\t\tRadarStyle: &attrValString{\n\t\t\t\t\tVal: stringPtr(\"marker\"),\n\t\t\t\t},\n\t\t\t\tVaryColors: &attrValBool{\n\t\t\t\t\tVal: boolPtr(false),\n\t\t\t\t},\n\t\t\t\tSer:   f.drawChartSeries(opts),\n\t\t\t\tDLbls: f.drawChartDLbls(opts),\n\t\t\t\tAxID:  f.genAxID(opts),\n\t\t\t},\n\t\t},\n\t\tCatAx: f.drawPlotAreaCatAx(pa, opts),\n\t\tValAx: f.drawPlotAreaValAx(pa, opts),\n\t}\n}\n\n// drawScatterChart provides a function to draw the c:plotArea element for\n// scatter chart by given format sets.\nfunc (f *File) drawScatterChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\treturn &cPlotArea{\n\t\tScatterChart: []*cCharts{\n\t\t\t{\n\t\t\t\tScatterStyle: &attrValString{\n\t\t\t\t\tVal: stringPtr(\"smoothMarker\"), // line,lineMarker,marker,none,smooth,smoothMarker\n\t\t\t\t},\n\t\t\t\tVaryColors: &attrValBool{\n\t\t\t\t\tVal: boolPtr(false),\n\t\t\t\t},\n\t\t\t\tSer:   f.drawChartSeries(opts),\n\t\t\t\tDLbls: f.drawChartDLbls(opts),\n\t\t\t\tAxID:  f.genAxID(opts),\n\t\t\t},\n\t\t},\n\t\tValAx: append(f.drawPlotAreaCatAx(pa, opts), f.drawPlotAreaValAx(pa, opts)...),\n\t}\n}\n\n// drawSurface3DChart provides a function to draw the c:surface3DChart element by\n// given format sets.\nfunc (f *File) drawSurface3DChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\tplotArea := &cPlotArea{\n\t\tSurface3DChart: []*cCharts{\n\t\t\t{\n\t\t\t\tSer: f.drawChartSeries(opts),\n\t\t\t\tAxID: []*attrValInt{\n\t\t\t\t\t{Val: intPtr(100000000)},\n\t\t\t\t\t{Val: intPtr(100000001)},\n\t\t\t\t\t{Val: intPtr(100000005)},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tCatAx: f.drawPlotAreaCatAx(pa, opts),\n\t\tValAx: f.drawPlotAreaValAx(pa, opts),\n\t\tSerAx: f.drawPlotAreaSerAx(opts),\n\t}\n\tif opts.Type == WireframeSurface3D {\n\t\tplotArea.Surface3DChart[0].Wireframe = &attrValBool{Val: boolPtr(true)}\n\t}\n\treturn plotArea\n}\n\n// drawSurfaceChart provides a function to draw the c:surfaceChart element by\n// given format sets.\nfunc (f *File) drawSurfaceChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\tplotArea := &cPlotArea{\n\t\tSurfaceChart: []*cCharts{\n\t\t\t{\n\t\t\t\tSer: f.drawChartSeries(opts),\n\t\t\t\tAxID: []*attrValInt{\n\t\t\t\t\t{Val: intPtr(100000000)},\n\t\t\t\t\t{Val: intPtr(100000001)},\n\t\t\t\t\t{Val: intPtr(100000005)},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tCatAx: f.drawPlotAreaCatAx(pa, opts),\n\t\tValAx: f.drawPlotAreaValAx(pa, opts),\n\t\tSerAx: f.drawPlotAreaSerAx(opts),\n\t}\n\tif opts.Type == WireframeContour {\n\t\tplotArea.SurfaceChart[0].Wireframe = &attrValBool{Val: boolPtr(true)}\n\t}\n\treturn plotArea\n}\n\n// drawBubbleChart provides a function to draw the c:bubbleChart element by\n// given format sets.\nfunc (f *File) drawBubbleChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\tplotArea := &cPlotArea{\n\t\tBubbleChart: []*cCharts{\n\t\t\t{\n\t\t\t\tVaryColors: &attrValBool{\n\t\t\t\t\tVal: opts.VaryColors,\n\t\t\t\t},\n\t\t\t\tSer:   f.drawChartSeries(opts),\n\t\t\t\tDLbls: f.drawChartDLbls(opts),\n\t\t\t\tAxID:  f.genAxID(opts),\n\t\t\t},\n\t\t},\n\t\tValAx: append(f.drawPlotAreaCatAx(pa, opts), f.drawPlotAreaValAx(pa, opts)...),\n\t}\n\tif opts.BubbleSize > 0 && opts.BubbleSize <= 300 {\n\t\tplotArea.BubbleChart[0].BubbleScale = &attrValFloat{Val: float64Ptr(float64(opts.BubbleSize))}\n\t}\n\treturn plotArea\n}\n\n// drawStockChart provides a function to draw the c:stockChart element by\n// given format sets.\nfunc (f *File) drawStockChart(pa *cPlotArea, opts *Chart) *cPlotArea {\n\tplotArea := &cPlotArea{\n\t\tStockChart: []*cCharts{\n\t\t\t{\n\t\t\t\tVaryColors: &attrValBool{\n\t\t\t\t\tVal: opts.VaryColors,\n\t\t\t\t},\n\t\t\t\tSer:   f.drawChartSeries(opts),\n\t\t\t\tDLbls: f.drawChartDLbls(opts),\n\t\t\t\tAxID:  f.genAxID(opts),\n\t\t\t},\n\t\t},\n\t\tValAx:  f.drawPlotAreaValAx(pa, opts),\n\t\tDateAx: f.drawPlotAreaCatAx(pa, opts),\n\t}\n\tif opts.Type == StockHighLowClose {\n\t\tplotArea.StockChart[0].HiLowLines = &cChartLines{}\n\t}\n\tif opts.Type == StockOpenHighLowClose {\n\t\tplotArea.StockChart[0].HiLowLines = &cChartLines{}\n\t\tplotArea.StockChart[0].UpDownBars = &cUpDownBars{\n\t\t\tGapWidth: &attrValString{Val: stringPtr(\"150\")},\n\t\t\tUpBars:   &cChartLines{f.drawShapeFill(opts.PlotArea.UpBars.Fill, &cSpPr{Ln: f.drawChartLn(&opts.PlotArea.UpBars.Border)})},\n\t\t\tDownBars: &cChartLines{f.drawShapeFill(opts.PlotArea.DownBars.Fill, &cSpPr{Ln: f.drawChartLn(&opts.PlotArea.UpBars.Border)})},\n\t\t}\n\t}\n\tser := *plotArea.StockChart[0].Ser\n\tser[0].Val.NumRef.NumCache = &cNumCache{}\n\treturn plotArea\n}\n\n// drawChartGapWidth provides a function to draw the c:gapWidth element by given\n// format sets.\nfunc (f *File) drawChartGapWidth(opts *Chart) *attrValInt {\n\tfor _, t := range barColChartTypes {\n\t\tif t == opts.Type && opts.GapWidth != nil && *opts.GapWidth != 150 && *opts.GapWidth <= 500 {\n\t\t\treturn &attrValInt{intPtr(int(*opts.GapWidth))}\n\t\t}\n\t}\n\treturn nil\n}\n\n// drawChartOverlap provides a function to draw the c:overlap element by given\n// format sets.\nfunc (f *File) drawChartOverlap(opts *Chart) *attrValInt {\n\tvar val *attrValInt\n\tif _, ok := plotAreaChartOverlap[opts.Type]; ok {\n\t\tval = &attrValInt{intPtr(100)}\n\t}\n\tif opts.Overlap != nil && -100 <= *opts.Overlap && *opts.Overlap <= 100 {\n\t\tval = &attrValInt{intPtr(*opts.Overlap)}\n\t}\n\tfor _, t := range barColChartTypes {\n\t\tif t == opts.Type {\n\t\t\treturn val\n\t\t}\n\t}\n\treturn nil\n}\n\n// drawChartShape provides a function to draw the c:shape element by given\n// format sets.\nfunc (f *File) drawChartShape(opts *Chart) *attrValString {\n\tshapes := map[ChartType]string{\n\t\tBar3DConeClustered:          \"cone\",\n\t\tBar3DConeStacked:            \"cone\",\n\t\tBar3DConePercentStacked:     \"cone\",\n\t\tBar3DPyramidClustered:       \"pyramid\",\n\t\tBar3DPyramidStacked:         \"pyramid\",\n\t\tBar3DPyramidPercentStacked:  \"pyramid\",\n\t\tBar3DCylinderClustered:      \"cylinder\",\n\t\tBar3DCylinderStacked:        \"cylinder\",\n\t\tBar3DCylinderPercentStacked: \"cylinder\",\n\t\tCol3DCone:                   \"cone\",\n\t\tCol3DConeClustered:          \"cone\",\n\t\tCol3DConeStacked:            \"cone\",\n\t\tCol3DConePercentStacked:     \"cone\",\n\t\tCol3DPyramid:                \"pyramid\",\n\t\tCol3DPyramidClustered:       \"pyramid\",\n\t\tCol3DPyramidStacked:         \"pyramid\",\n\t\tCol3DPyramidPercentStacked:  \"pyramid\",\n\t\tCol3DCylinder:               \"cylinder\",\n\t\tCol3DCylinderClustered:      \"cylinder\",\n\t\tCol3DCylinderStacked:        \"cylinder\",\n\t\tCol3DCylinderPercentStacked: \"cylinder\",\n\t}\n\tif shape, ok := shapes[opts.Type]; ok {\n\t\treturn &attrValString{Val: stringPtr(shape)}\n\t}\n\treturn nil\n}\n\n// drawChartSeries provides a function to draw the c:ser element by given\n// format sets.\nfunc (f *File) drawChartSeries(opts *Chart) *[]cSer {\n\tvar ser []cSer\n\tfor k := range opts.Series {\n\t\tser = append(ser, cSer{\n\t\t\tIDx:   &attrValInt{Val: intPtr(k + opts.order)},\n\t\t\tOrder: &attrValInt{Val: intPtr(k + opts.order)},\n\t\t\tTx: &cTx{\n\t\t\t\tStrRef: &cStrRef{\n\t\t\t\t\tF: opts.Series[k].Name,\n\t\t\t\t},\n\t\t\t},\n\t\t\tSpPr:             f.drawChartSeriesSpPr(k, opts),\n\t\t\tMarker:           f.drawChartSeriesMarker(k, opts),\n\t\t\tDPt:              f.drawChartSeriesDPt(k, opts),\n\t\t\tDLbls:            f.drawChartSeriesDLbls(k, opts),\n\t\t\tInvertIfNegative: &attrValBool{Val: boolPtr(false)},\n\t\t\tCat:              f.drawChartSeriesCat(opts.Series[k], opts),\n\t\t\tSmooth:           &attrValBool{Val: boolPtr(opts.Series[k].Line.Smooth)},\n\t\t\tVal:              f.drawChartSeriesVal(opts.Series[k], opts),\n\t\t\tXVal:             f.drawChartSeriesXVal(opts.Series[k], opts),\n\t\t\tYVal:             f.drawChartSeriesYVal(opts.Series[k], opts),\n\t\t\tBubbleSize:       f.drawCharSeriesBubbleSize(opts.Series[k], opts),\n\t\t\tBubble3D:         f.drawCharSeriesBubble3D(opts),\n\t\t})\n\t}\n\treturn &ser\n}\n\n// drawShapeFill provides a function to draw the a:solidFill element by given\n// fill format sets.\nfunc (f *File) drawShapeFill(fill Fill, spPr *cSpPr) *cSpPr {\n\tif fill.Type == \"pattern\" && fill.Pattern == 1 {\n\t\tif spPr == nil {\n\t\t\tspPr = &cSpPr{}\n\t\t}\n\t\tif len(fill.Color) == 1 {\n\t\t\tspPr.SolidFill = &aSolidFill{SrgbClr: &aSrgbClr{Val: stringPtr(strings.TrimPrefix(fill.Color[0], \"#\"))}}\n\t\t\tif fill.Transparency > 0 {\n\t\t\t\tval := (100 - fill.Transparency) * 1000\n\t\t\t\tspPr.SolidFill.SrgbClr.Alpha = &attrValInt{Val: &val}\n\t\t\t}\n\t\t\treturn spPr\n\t\t}\n\t\tspPr.SolidFill = nil\n\t\tspPr.NoFill = stringPtr(\"\")\n\t}\n\treturn spPr\n}\n\n// drawChartSeriesSpPr provides a function to draw the c:spPr element by given\n// format sets.\nfunc (f *File) drawChartSeriesSpPr(i int, opts *Chart) *cSpPr {\n\tspPr := &cSpPr{SolidFill: &aSolidFill{SchemeClr: &aSchemeClr{Val: \"accent\" + strconv.Itoa((opts.order+i)%6+1)}}}\n\tspPr = f.drawShapeFill(opts.Series[i].Fill, spPr)\n\tsolid := &cSpPr{\n\t\tLn: &aLn{\n\t\t\tW:         f.ptToEMUs(opts.Series[i].Line.Width),\n\t\t\tCap:       \"rnd\", // rnd, sq, flat\n\t\t\tSolidFill: spPr.SolidFill,\n\t\t},\n\t}\n\tif opts.Series[i].Line.Dash != ChartDashUnset {\n\t\tsolid.Ln.PrstDash = &attrValString{Val: stringPtr(chartDashTypes[opts.Series[i].Line.Dash])}\n\t}\n\tnoLn := &cSpPr{Ln: &aLn{NoFill: &attrValString{}}}\n\tif chartSeriesSpPr, ok := map[ChartType]map[ChartLineType]*cSpPr{\n\t\tLine:                  {ChartLineUnset: solid, ChartLineSolid: solid, ChartLineNone: noLn, ChartLineAutomatic: solid},\n\t\tScatter:               {ChartLineUnset: noLn, ChartLineSolid: solid, ChartLineNone: noLn, ChartLineAutomatic: noLn},\n\t\tStockHighLowClose:     {ChartLineUnset: noLn, ChartLineSolid: solid, ChartLineNone: noLn, ChartLineAutomatic: noLn},\n\t\tStockOpenHighLowClose: {ChartLineUnset: noLn, ChartLineSolid: solid, ChartLineNone: noLn, ChartLineAutomatic: noLn},\n\t}[opts.Type]; ok {\n\t\treturn chartSeriesSpPr[opts.Series[i].Line.Type]\n\t}\n\tif spPr.SolidFill.SrgbClr != nil {\n\t\treturn spPr\n\t}\n\treturn nil\n}\n\n// drawChartSeriesDPt provides a function to draw the c:dPt element by given\n// data index and format sets.\nfunc (f *File) drawChartSeriesDPt(i int, opts *Chart) []*cDPt {\n\tdpt := []*cDPt{{\n\t\tIDx:      &attrValInt{Val: intPtr(i)},\n\t\tBubble3D: &attrValBool{Val: boolPtr(false)},\n\t\tSpPr: &cSpPr{\n\t\t\tSolidFill: &aSolidFill{\n\t\t\t\tSchemeClr: &aSchemeClr{Val: \"accent\" + strconv.Itoa(i+1)},\n\t\t\t},\n\t\t\tLn: &aLn{\n\t\t\t\tW:   25400,\n\t\t\t\tCap: \"rnd\",\n\t\t\t\tSolidFill: &aSolidFill{\n\t\t\t\t\tSchemeClr: &aSchemeClr{Val: \"lt1\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tSp3D: &aSp3D{\n\t\t\t\tContourW: 25400,\n\t\t\t\tContourClr: &aContourClr{\n\t\t\t\t\tSchemeClr: &aSchemeClr{Val: \"lt1\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}}\n\tif len(opts.Series[i].DataPoint) > 0 {\n\t\tdpt = []*cDPt{}\n\t}\n\tfor j := 0; j < len(opts.Series[i].DataPoint); j++ {\n\t\tspPr := &cSpPr{}\n\t\tdpt = append(dpt, &cDPt{\n\t\t\tIDx:  &attrValInt{Val: intPtr(opts.Series[i].DataPoint[j].Index)},\n\t\t\tSpPr: f.drawShapeFill(opts.Series[i].DataPoint[j].Fill, spPr),\n\t\t})\n\t}\n\tchartSeriesDPt := map[ChartType][]*cDPt{Doughnut: dpt, Pie: dpt, Pie3D: dpt}\n\treturn chartSeriesDPt[opts.Type]\n}\n\n// drawChartSeriesCat provides a function to draw the c:cat element by given\n// chart series and format sets.\nfunc (f *File) drawChartSeriesCat(v ChartSeries, opts *Chart) *cCat {\n\tcat := &cCat{\n\t\tStrRef: &cStrRef{\n\t\t\tF: v.Categories,\n\t\t},\n\t}\n\tchartSeriesCat := map[ChartType]*cCat{Scatter: nil, Bubble: nil, Bubble3D: nil}\n\tif _, ok := chartSeriesCat[opts.Type]; ok || v.Categories == \"\" {\n\t\treturn nil\n\t}\n\treturn cat\n}\n\n// drawChartSeriesVal provides a function to draw the c:val element by given\n// chart series and format sets.\nfunc (f *File) drawChartSeriesVal(v ChartSeries, opts *Chart) *cVal {\n\tval := &cVal{\n\t\tNumRef: &cNumRef{\n\t\t\tF:        v.Values,\n\t\t\tNumCache: &cNumCache{},\n\t\t},\n\t}\n\tchartSeriesVal := map[ChartType]*cVal{Scatter: nil, Bubble: nil, Bubble3D: nil}\n\tif _, ok := chartSeriesVal[opts.Type]; ok {\n\t\treturn nil\n\t}\n\treturn val\n}\n\n// drawChartSeriesMarker provides a function to draw the c:marker element by\n// given data index and format sets.\nfunc (f *File) drawChartSeriesMarker(i int, opts *Chart) *cMarker {\n\tdefaultSymbol := map[ChartType]*attrValString{\n\t\tScatter:               {Val: stringPtr(\"circle\")},\n\t\tStockHighLowClose:     {Val: stringPtr(\"dot\")},\n\t\tStockOpenHighLowClose: {Val: stringPtr(\"none\")},\n\t}\n\tmarker := &cMarker{\n\t\tSymbol: defaultSymbol[opts.Type],\n\t\tSize:   &attrValInt{Val: intPtr(5)},\n\t}\n\tif symbol := stringPtr(opts.Series[i].Marker.Symbol); *symbol != \"\" {\n\t\tmarker.Symbol = &attrValString{Val: symbol}\n\t}\n\tif size := intPtr(opts.Series[i].Marker.Size); *size != 0 {\n\t\tmarker.Size = &attrValInt{Val: size}\n\t}\n\tif i < 6 {\n\t\tmarker.SpPr = &cSpPr{SolidFill: &aSolidFill{\n\t\t\tSchemeClr: &aSchemeClr{Val: \"accent\" + strconv.Itoa(i+1)},\n\t\t}, Ln: &aLn{W: 9252}}\n\t}\n\tmarker.SpPr = f.drawShapeFill(opts.Series[i].Marker.Fill, marker.SpPr)\n\tif marker.SpPr != nil && marker.SpPr.Ln != nil {\n\t\tmarker.SpPr.Ln = f.drawChartLn(&opts.Series[i].Marker.Border)\n\t}\n\tchartSeriesMarker := map[ChartType]*cMarker{\n\t\tScatter:               marker,\n\t\tLine:                  marker,\n\t\tStockHighLowClose:     marker,\n\t\tStockOpenHighLowClose: marker,\n\t}\n\treturn chartSeriesMarker[opts.Type]\n}\n\n// drawChartSeriesXVal provides a function to draw the c:xVal element by given\n// chart series and format sets.\nfunc (f *File) drawChartSeriesXVal(v ChartSeries, opts *Chart) *cCat {\n\tcat := &cCat{\n\t\tStrRef: &cStrRef{\n\t\t\tF: v.Categories,\n\t\t},\n\t}\n\tchartSeriesXVal := map[ChartType]*cCat{Scatter: cat, Bubble: cat, Bubble3D: cat}\n\treturn chartSeriesXVal[opts.Type]\n}\n\n// drawChartSeriesYVal provides a function to draw the c:yVal element by given\n// chart series and format sets.\nfunc (f *File) drawChartSeriesYVal(v ChartSeries, opts *Chart) *cVal {\n\tval := &cVal{\n\t\tNumRef: &cNumRef{\n\t\t\tF:        v.Values,\n\t\t\tNumCache: &cNumCache{},\n\t\t},\n\t}\n\tchartSeriesYVal := map[ChartType]*cVal{Scatter: val, Bubble: val, Bubble3D: val}\n\treturn chartSeriesYVal[opts.Type]\n}\n\n// drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize\n// element by given chart series and format sets.\nfunc (f *File) drawCharSeriesBubbleSize(v ChartSeries, opts *Chart) *cVal {\n\tif _, ok := map[ChartType]bool{Bubble: true, Bubble3D: true}[opts.Type]; !ok {\n\t\treturn nil\n\t}\n\tfVal := v.Values\n\tif v.Sizes != \"\" {\n\t\tfVal = v.Sizes\n\t}\n\treturn &cVal{\n\t\tNumRef: &cNumRef{\n\t\t\tF:        fVal,\n\t\t\tNumCache: &cNumCache{},\n\t\t},\n\t}\n}\n\n// drawCharSeriesBubble3D provides a function to draw the c:bubble3D element\n// by given format sets.\nfunc (f *File) drawCharSeriesBubble3D(opts *Chart) *attrValBool {\n\tif _, ok := map[ChartType]bool{Bubble3D: true}[opts.Type]; !ok {\n\t\treturn nil\n\t}\n\treturn &attrValBool{Val: boolPtr(true)}\n}\n\n// drawChartNumFmt provides a function to draw the c:numFmt element by given\n// data labels format sets.\nfunc (f *File) drawChartNumFmt(labels ChartNumFmt) *cNumFmt {\n\tvar numFmt *cNumFmt\n\tif labels.CustomNumFmt != \"\" || labels.SourceLinked {\n\t\tnumFmt = &cNumFmt{\n\t\t\tFormatCode:   labels.CustomNumFmt,\n\t\t\tSourceLinked: labels.SourceLinked,\n\t\t}\n\t}\n\treturn numFmt\n}\n\n// drawChartDLbls provides a function to draw the c:dLbls element by given\n// format sets.\nfunc (f *File) drawChartDLbls(opts *Chart) *cDLbls {\n\treturn &cDLbls{\n\t\tNumFmt:          f.drawChartNumFmt(opts.PlotArea.NumFmt),\n\t\tShowLegendKey:   &attrValBool{Val: boolPtr(opts.Legend.ShowLegendKey)},\n\t\tShowVal:         &attrValBool{Val: boolPtr(opts.PlotArea.ShowVal)},\n\t\tShowCatName:     &attrValBool{Val: boolPtr(opts.PlotArea.ShowCatName)},\n\t\tShowSerName:     &attrValBool{Val: boolPtr(opts.PlotArea.ShowSerName)},\n\t\tShowBubbleSize:  &attrValBool{Val: boolPtr(opts.PlotArea.ShowBubbleSize)},\n\t\tShowPercent:     &attrValBool{Val: boolPtr(opts.PlotArea.ShowPercent)},\n\t\tShowLeaderLines: &attrValBool{Val: boolPtr(opts.PlotArea.ShowLeaderLines)},\n\t}\n}\n\n// inSupportedChartDataLabelsPositionType provides a method to check if an\n// element is present in an array, and return the index of its location,\n// otherwise return -1.\nfunc inSupportedChartDataLabelsPositionType(a []ChartDataLabelPositionType, x ChartDataLabelPositionType) int {\n\tfor idx, n := range a {\n\t\tif x == n {\n\t\t\treturn idx\n\t\t}\n\t}\n\treturn -1\n}\n\n// drawChartSeriesDLbls provides a function to draw the c:dLbls element by\n// given format sets.\nfunc (f *File) drawChartSeriesDLbls(i int, opts *Chart) *cDLbls {\n\tdLbls := f.drawChartDLbls(opts)\n\tchartSeriesDLbls := map[ChartType]*cDLbls{\n\t\tScatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil,\n\t}\n\tif _, ok := chartSeriesDLbls[opts.Type]; ok {\n\t\treturn nil\n\t}\n\tif types, ok := supportedChartDataLabelsPosition[opts.Type]; ok && opts.Series[i].DataLabelPosition != ChartDataLabelsPositionUnset {\n\t\tif inSupportedChartDataLabelsPositionType(types, opts.Series[i].DataLabelPosition) != -1 {\n\t\t\tdLbls.DLblPos = &attrValString{Val: stringPtr(chartDataLabelsPositionTypes[opts.Series[i].DataLabelPosition])}\n\t\t}\n\t}\n\tdLbl := opts.Series[i].DataLabel\n\tdLbls.SpPr = f.drawShapeFill(dLbl.Fill, dLbls.SpPr)\n\tdLbls.TxPr = &cTxPr{BodyPr: aBodyPr{}, P: aP{PPr: &aPPr{DefRPr: aRPr{}}}}\n\tdrawChartFont(&dLbl.Font, &dLbls.TxPr.P.PPr.DefRPr)\n\treturn dLbls\n}\n\n// drawPlotAreaCatAx provides a function to draw the c:catAx element.\nfunc (f *File) drawPlotAreaCatAx(pa *cPlotArea, opts *Chart) []*cAxs {\n\tmaxVal := &attrValFloat{Val: opts.XAxis.Maximum}\n\tminVal := &attrValFloat{Val: opts.XAxis.Minimum}\n\tif opts.XAxis.Maximum == nil {\n\t\tmaxVal = nil\n\t}\n\tif opts.XAxis.Minimum == nil {\n\t\tminVal = nil\n\t}\n\tax := &cAxs{\n\t\tAxID: &attrValInt{Val: intPtr(100000000)},\n\t\tScaling: &cScaling{\n\t\t\tOrientation: &attrValString{Val: stringPtr(orientation[opts.XAxis.ReverseOrder])},\n\t\t\tMax:         maxVal,\n\t\t\tMin:         minVal,\n\t\t},\n\t\tDelete:        &attrValBool{Val: boolPtr(opts.XAxis.None)},\n\t\tAxPos:         &attrValString{Val: stringPtr(catAxPos[opts.XAxis.ReverseOrder])},\n\t\tNumFmt:        &cNumFmt{FormatCode: \"General\"},\n\t\tMajorTickMark: &attrValString{Val: stringPtr(\"none\")},\n\t\tMinorTickMark: &attrValString{Val: stringPtr(\"none\")},\n\t\tTitle:         f.drawPlotAreaTitles(opts.XAxis.Title, \"\"),\n\t\tTickLblPos:    &attrValString{Val: stringPtr(tickLblPosVal[opts.XAxis.TickLabelPosition])},\n\t\tSpPr:          f.drawPlotAreaSpPr(),\n\t\tTxPr:          f.drawPlotAreaTxPr(&opts.XAxis),\n\t\tCrossAx:       &attrValInt{Val: intPtr(100000001)},\n\t\tCrosses:       &attrValString{Val: stringPtr(\"autoZero\")},\n\t\tAuto:          &attrValBool{Val: boolPtr(true)},\n\t\tLblAlgn:       &attrValString{Val: stringPtr(\"ctr\")},\n\t\tLblOffset:     &attrValInt{Val: intPtr(100)},\n\t\tNoMultiLvlLbl: &attrValBool{Val: boolPtr(false)},\n\t}\n\tif numFmt := f.drawChartNumFmt(opts.XAxis.NumFmt); numFmt != nil {\n\t\tax.NumFmt = numFmt\n\t}\n\tif opts.XAxis.MajorGridLines {\n\t\tax.MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}\n\t}\n\tif opts.XAxis.MinorGridLines {\n\t\tax.MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}\n\t}\n\tif opts.XAxis.TickLabelSkip != 0 {\n\t\tax.TickLblSkip = &attrValInt{Val: intPtr(opts.XAxis.TickLabelSkip)}\n\t}\n\tif opts.order > 0 && opts.YAxis.Secondary && pa.CatAx != nil {\n\t\tax.AxID = &attrValInt{Val: intPtr(opts.XAxis.axID)}\n\t\tax.Delete = &attrValBool{Val: boolPtr(true)}\n\t\tax.Crosses = nil\n\t\tax.CrossAx = &attrValInt{Val: intPtr(opts.YAxis.axID)}\n\t\treturn []*cAxs{pa.CatAx[0], ax}\n\t}\n\treturn []*cAxs{ax}\n}\n\n// drawPlotAreaValAx provides a function to draw the c:valAx element.\nfunc (f *File) drawPlotAreaValAx(pa *cPlotArea, opts *Chart) []*cAxs {\n\tmaxVal := &attrValFloat{Val: opts.YAxis.Maximum}\n\tminVal := &attrValFloat{Val: opts.YAxis.Minimum}\n\tif opts.YAxis.Maximum == nil {\n\t\tmaxVal = nil\n\t}\n\tif opts.YAxis.Minimum == nil {\n\t\tminVal = nil\n\t}\n\tvar logBase *attrValFloat\n\tif opts.YAxis.LogBase >= 2 && opts.YAxis.LogBase <= 1000 {\n\t\tlogBase = &attrValFloat{Val: float64Ptr(opts.YAxis.LogBase)}\n\t}\n\tax := &cAxs{\n\t\tAxID: &attrValInt{Val: intPtr(100000001)},\n\t\tScaling: &cScaling{\n\t\t\tLogBase:     logBase,\n\t\t\tOrientation: &attrValString{Val: stringPtr(orientation[opts.YAxis.ReverseOrder])},\n\t\t\tMax:         maxVal,\n\t\t\tMin:         minVal,\n\t\t},\n\t\tDelete: &attrValBool{Val: boolPtr(opts.YAxis.None)},\n\t\tAxPos:  &attrValString{Val: stringPtr(valAxPos[opts.YAxis.ReverseOrder])},\n\t\tTitle:  f.drawPlotAreaTitles(opts.YAxis.Title, \"horz\"),\n\t\tNumFmt: &cNumFmt{\n\t\t\tFormatCode: chartValAxNumFmtFormatCode[opts.Type],\n\t\t},\n\t\tMajorTickMark: &attrValString{Val: stringPtr(\"none\")},\n\t\tMinorTickMark: &attrValString{Val: stringPtr(\"none\")},\n\t\tTickLblPos:    &attrValString{Val: stringPtr(tickLblPosVal[opts.YAxis.TickLabelPosition])},\n\t\tSpPr:          f.drawPlotAreaSpPr(),\n\t\tTxPr:          f.drawPlotAreaTxPr(&opts.YAxis),\n\t\tCrossAx:       &attrValInt{Val: intPtr(100000000)},\n\t\tCrosses:       &attrValString{Val: stringPtr(\"autoZero\")},\n\t\tCrossBetween:  &attrValString{Val: stringPtr(chartValAxCrossBetween[opts.Type])},\n\t}\n\tif numFmt := f.drawChartNumFmt(opts.YAxis.NumFmt); numFmt != nil {\n\t\tax.NumFmt = numFmt\n\t}\n\tif opts.YAxis.MajorGridLines {\n\t\tax.MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}\n\t}\n\tif opts.YAxis.MinorGridLines {\n\t\tax.MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}\n\t}\n\tif pos, ok := tickLblPosNone[opts.Type]; ok {\n\t\tax.TickLblPos.Val = stringPtr(pos)\n\t}\n\tif opts.YAxis.MajorUnit != 0 {\n\t\tax.MajorUnit = &attrValFloat{Val: float64Ptr(opts.YAxis.MajorUnit)}\n\t}\n\tif opts.order > 0 && opts.YAxis.Secondary && pa.ValAx != nil {\n\t\tax.AxID = &attrValInt{Val: intPtr(opts.YAxis.axID)}\n\t\tax.AxPos = &attrValString{Val: stringPtr(\"r\")}\n\t\tax.Crosses = &attrValString{Val: stringPtr(\"max\")}\n\t\tax.CrossAx = &attrValInt{Val: intPtr(opts.XAxis.axID)}\n\t\treturn []*cAxs{pa.ValAx[0], ax}\n\t}\n\treturn []*cAxs{ax}\n}\n\n// drawPlotAreaSerAx provides a function to draw the c:serAx element.\nfunc (f *File) drawPlotAreaSerAx(opts *Chart) []*cAxs {\n\tmaxVal := &attrValFloat{Val: opts.YAxis.Maximum}\n\tminVal := &attrValFloat{Val: opts.YAxis.Minimum}\n\tif opts.YAxis.Maximum == nil {\n\t\tmaxVal = nil\n\t}\n\tif opts.YAxis.Minimum == nil {\n\t\tminVal = nil\n\t}\n\treturn []*cAxs{\n\t\t{\n\t\t\tAxID: &attrValInt{Val: intPtr(100000005)},\n\t\t\tScaling: &cScaling{\n\t\t\t\tOrientation: &attrValString{Val: stringPtr(orientation[opts.YAxis.ReverseOrder])},\n\t\t\t\tMax:         maxVal,\n\t\t\t\tMin:         minVal,\n\t\t\t},\n\t\t\tDelete:     &attrValBool{Val: boolPtr(opts.YAxis.None)},\n\t\t\tAxPos:      &attrValString{Val: stringPtr(catAxPos[opts.XAxis.ReverseOrder])},\n\t\t\tTickLblPos: &attrValString{Val: stringPtr(tickLblPosVal[opts.YAxis.TickLabelPosition])},\n\t\t\tSpPr:       f.drawPlotAreaSpPr(),\n\t\t\tTxPr:       f.drawPlotAreaTxPr(nil),\n\t\t\tCrossAx:    &attrValInt{Val: intPtr(100000001)},\n\t\t},\n\t}\n}\n\n// drawChartFont provides a function to draw the a:rPr element.\nfunc drawChartFont(fnt *Font, r *aRPr) {\n\tif fnt == nil {\n\t\treturn\n\t}\n\tr.B = fnt.Bold\n\tr.I = fnt.Italic\n\tif idx := inStrSlice(supportedDrawingUnderlineTypes, fnt.Underline, true); idx != -1 {\n\t\tr.U = supportedDrawingUnderlineTypes[idx]\n\t}\n\tif fnt.Color != \"\" {\n\t\tif r.SolidFill == nil {\n\t\t\tr.SolidFill = &aSolidFill{}\n\t\t}\n\t\tr.SolidFill.SchemeClr = nil\n\t\tr.SolidFill.SrgbClr = &aSrgbClr{Val: stringPtr(strings.ReplaceAll(strings.ToUpper(fnt.Color), \"#\", \"\"))}\n\t}\n\tif fnt.Family != \"\" {\n\t\tif r.Latin == nil {\n\t\t\tr.Latin = &xlsxCTTextFont{}\n\t\t}\n\t\tif r.Ea == nil {\n\t\t\tr.Ea = &xlsxCTTextFont{}\n\t\t}\n\t\tif r.Cs == nil {\n\t\t\tr.Cs = &xlsxCTTextFont{}\n\t\t}\n\t\tr.Latin.Typeface = fnt.Family\n\t\tr.Ea.Typeface = fnt.Family\n\t\tr.Cs.Typeface = fnt.Family\n\t}\n\tif fnt.Size > 0 {\n\t\tr.Sz = fnt.Size * 100\n\t}\n\tif fnt.Strike {\n\t\tr.Strike = \"sngStrike\"\n\t}\n}\n\n// drawPlotAreaTitles provides a function to draw the c:title element.\nfunc (f *File) drawPlotAreaTitles(runs []RichTextRun, vert string) *cTitle {\n\tif len(runs) == 0 {\n\t\treturn nil\n\t}\n\ttitle := &cTitle{Tx: cTx{Rich: &cRich{}}, Overlay: &attrValBool{Val: boolPtr(false)}}\n\tfor _, run := range runs {\n\t\tr := &aR{T: run.Text}\n\t\tdrawChartFont(run.Font, &r.RPr)\n\t\ttitle.Tx.Rich.P = append(title.Tx.Rich.P, aP{\n\t\t\tPPr:        &aPPr{DefRPr: aRPr{}},\n\t\t\tR:          r,\n\t\t\tEndParaRPr: &aEndParaRPr{Lang: \"en-US\", AltLang: \"en-US\"},\n\t\t})\n\t}\n\tif vert == \"horz\" {\n\t\ttitle.Tx.Rich.BodyPr = aBodyPr{Rot: -5400000, Vert: vert}\n\t}\n\treturn title\n}\n\n// drawPlotAreaDTable provides a function to draw the c:dTable element.\nfunc (f *File) drawPlotAreaDTable(opts *Chart) *cDTable {\n\tif _, ok := plotAreaChartGrouping[opts.Type]; ok && opts.PlotArea.ShowDataTable {\n\t\treturn &cDTable{\n\t\t\tShowHorzBorder: &attrValBool{Val: boolPtr(true)},\n\t\t\tShowVertBorder: &attrValBool{Val: boolPtr(true)},\n\t\t\tShowOutline:    &attrValBool{Val: boolPtr(true)},\n\t\t\tShowKeys:       &attrValBool{Val: boolPtr(opts.PlotArea.ShowDataTableKeys)},\n\t\t}\n\t}\n\treturn nil\n}\n\n// drawPlotAreaSpPr provides a function to draw the c:spPr element.\nfunc (f *File) drawPlotAreaSpPr() *cSpPr {\n\treturn &cSpPr{\n\t\tLn: &aLn{\n\t\t\tW:    9525,\n\t\t\tCap:  \"flat\",\n\t\t\tCmpd: \"sng\",\n\t\t\tAlgn: \"ctr\",\n\t\t\tSolidFill: &aSolidFill{\n\t\t\t\tSchemeClr: &aSchemeClr{\n\t\t\t\t\tVal:    \"tx1\",\n\t\t\t\t\tLumMod: &attrValInt{Val: intPtr(15000)},\n\t\t\t\t\tLumOff: &attrValInt{Val: intPtr(85000)},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\n// drawPlotAreaTxPr provides a function to draw the c:txPr element.\nfunc (f *File) drawPlotAreaTxPr(opts *ChartAxis) *cTxPr {\n\tcTxPr := &cTxPr{\n\t\tBodyPr: aBodyPr{\n\t\t\tRot:              -60000000,\n\t\t\tSpcFirstLastPara: true,\n\t\t\tVertOverflow:     \"ellipsis\",\n\t\t\tVert:             \"horz\",\n\t\t\tWrap:             \"square\",\n\t\t\tAnchor:           \"ctr\",\n\t\t\tAnchorCtr:        true,\n\t\t},\n\t\tP: aP{\n\t\t\tPPr: &aPPr{\n\t\t\t\tDefRPr: aRPr{\n\t\t\t\t\tSz:       900,\n\t\t\t\t\tB:        false,\n\t\t\t\t\tI:        false,\n\t\t\t\t\tU:        \"none\",\n\t\t\t\t\tStrike:   \"noStrike\",\n\t\t\t\t\tKern:     1200,\n\t\t\t\t\tBaseline: 0,\n\t\t\t\t\tSolidFill: &aSolidFill{\n\t\t\t\t\t\tSchemeClr: &aSchemeClr{\n\t\t\t\t\t\t\tVal:    \"tx1\",\n\t\t\t\t\t\t\tLumMod: &attrValInt{Val: intPtr(15000)},\n\t\t\t\t\t\t\tLumOff: &attrValInt{Val: intPtr(85000)},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tLatin: &xlsxCTTextFont{Typeface: \"+mn-lt\"},\n\t\t\t\t\tEa:    &xlsxCTTextFont{Typeface: \"+mn-ea\"},\n\t\t\t\t\tCs:    &xlsxCTTextFont{Typeface: \"+mn-cs\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tEndParaRPr: &aEndParaRPr{Lang: \"en-US\"},\n\t\t},\n\t}\n\tif opts != nil {\n\t\tdrawChartFont(&opts.Font, &cTxPr.P.PPr.DefRPr)\n\t\tif -90 <= opts.Alignment.TextRotation && opts.Alignment.TextRotation <= 90 {\n\t\t\tcTxPr.BodyPr.Rot = opts.Alignment.TextRotation * 60000\n\t\t}\n\t\tif idx := inStrSlice(supportedDrawingTextVerticalType, opts.Alignment.Vertical, true); idx != -1 {\n\t\t\tcTxPr.BodyPr.Vert = supportedDrawingTextVerticalType[idx]\n\t\t}\n\t}\n\treturn cTxPr\n}\n\n// drawChartLn provides a function to draw the a:ln element.\nfunc (f *File) drawChartLn(opts *ChartLine) *aLn {\n\tln := &aLn{\n\t\tW:    f.ptToEMUs(opts.Width),\n\t\tCap:  \"flat\",\n\t\tCmpd: \"sng\",\n\t\tAlgn: \"ctr\",\n\t}\n\tif opts.Dash != ChartDashUnset {\n\t\tln.PrstDash = &attrValString{Val: stringPtr(chartDashTypes[opts.Dash])}\n\t}\n\tswitch opts.Type {\n\tcase ChartLineSolid:\n\t\tln.SolidFill = f.drawShapeFill(opts.Fill, &cSpPr{\n\t\t\tSolidFill: &aSolidFill{\n\t\t\t\tSchemeClr: &aSchemeClr{\n\t\t\t\t\tVal: \"tx1\",\n\t\t\t\t\tLumMod: &attrValInt{\n\t\t\t\t\t\tVal: intPtr(15000),\n\t\t\t\t\t},\n\t\t\t\t\tLumOff: &attrValInt{\n\t\t\t\t\t\tVal: intPtr(85000),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t}).SolidFill\n\t\treturn ln\n\tcase ChartLineNone:\n\t\tln.NoFill = &attrValString{}\n\t\treturn ln\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// drawChartLegend provides a function to draw the c:legend element.\nfunc (c *cChart) drawChartLegend(opts *Chart) {\n\tif opts.Legend.Position == \"none\" {\n\t\tc.Legend = nil\n\t\treturn\n\t}\n\tif c.Legend == nil {\n\t\tc.Legend = &cLegend{\n\t\t\tLegendPos: &attrValString{Val: stringPtr(chartLegendPosition[opts.Legend.Position])},\n\t\t\tOverlay:   &attrValBool{Val: boolPtr(false)},\n\t\t}\n\t}\n\tif opts.Legend.Font != nil {\n\t\tc.Legend.TxPr = &cTxPr{P: aP{PPr: &aPPr{}}}\n\t\tdrawChartFont(opts.Legend.Font, &c.Legend.TxPr.P.PPr.DefRPr)\n\t}\n\tfor k := range opts.Series {\n\t\tfont := opts.Series[k].Legend.Font\n\t\tif font == nil {\n\t\t\tcontinue\n\t\t}\n\t\tlegendEntry := cLegendEntry{\n\t\t\tIDx:  &attrValInt{Val: intPtr(k + opts.order)},\n\t\t\tTxPr: &cTxPr{P: aP{PPr: &aPPr{}}},\n\t\t}\n\t\tdrawChartFont(font, &legendEntry.TxPr.P.PPr.DefRPr)\n\t\tc.Legend.LegendEntry = append(c.Legend.LegendEntry, legendEntry)\n\t}\n}\n\n// drawingParser provides a function to parse drawingXML. In order to solve\n// the problem that the label structure is changed after serialization and\n// deserialization, two different structures: decodeWsDr and encodeWsDr are\n// defined.\nfunc (f *File) drawingParser(path string) (*xlsxWsDr, int, error) {\n\tvar (\n\t\terr error\n\t\tok  bool\n\t)\n\t_, ok = f.Drawings.Load(path)\n\tif !ok {\n\t\tcontent := xlsxWsDr{\n\t\t\tNS:  NameSpaceDrawingMLSpreadSheet.Value,\n\t\t\tXdr: NameSpaceDrawingMLSpreadSheet.Value,\n\t\t\tA:   NameSpaceDrawingML.Value,\n\t\t}\n\t\tif _, ok = f.Pkg.Load(path); ok { // Append Model\n\t\t\tdecodeWsDr := decodeWsDr{}\n\t\t\tif err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))).\n\t\t\t\tDecode(&decodeWsDr); err != nil && err != io.EOF {\n\t\t\t\treturn nil, 0, err\n\t\t\t}\n\t\t\tcontent.R = decodeWsDr.R\n\t\t\tfor _, v := range decodeWsDr.AlternateContent {\n\t\t\t\tcontent.AlternateContent = append(content.AlternateContent, &xlsxAlternateContent{\n\t\t\t\t\tContent: v.Content,\n\t\t\t\t\tXMLNSMC: SourceRelationshipCompatibility.Value,\n\t\t\t\t})\n\t\t\t}\n\t\t\tfor _, v := range decodeWsDr.OneCellAnchor {\n\t\t\t\tcontent.OneCellAnchor = append(content.OneCellAnchor, &xdrCellAnchor{\n\t\t\t\t\tEditAs:       v.EditAs,\n\t\t\t\t\tGraphicFrame: v.Content,\n\t\t\t\t})\n\t\t\t}\n\t\t\tfor _, v := range decodeWsDr.TwoCellAnchor {\n\t\t\t\tcontent.TwoCellAnchor = append(content.TwoCellAnchor, &xdrCellAnchor{\n\t\t\t\t\tEditAs:       v.EditAs,\n\t\t\t\t\tGraphicFrame: v.Content,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tf.Drawings.Store(path, &content)\n\t}\n\tvar wsDr *xlsxWsDr\n\tif drawing, ok := f.Drawings.Load(path); ok && drawing != nil {\n\t\twsDr = drawing.(*xlsxWsDr)\n\t}\n\twsDr.mu.Lock()\n\tdefer wsDr.mu.Unlock()\n\treturn wsDr, len(wsDr.OneCellAnchor) + len(wsDr.TwoCellAnchor) + 2, nil\n}\n\n// addDrawingChart provides a function to add chart graphic frame by given\n// sheet, drawingXML, cell, width, height, relationship index and format sets.\nfunc (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rID int, opts *GraphicOptions) error {\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\twidth = int(float64(width) * opts.ScaleX)\n\theight = int(float64(height) * opts.ScaleY)\n\tcolStart, rowStart, colEnd, rowEnd, x1, y1, x2, y2 := f.positionObjectPixels(sheet, col, row, width, height, opts)\n\tcontent, cNvPrID, err := f.drawingParser(drawingXML)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttwoCellAnchor := xdrCellAnchor{}\n\ttwoCellAnchor.EditAs = opts.Positioning\n\tfrom := xlsxFrom{}\n\tfrom.Col = colStart\n\tfrom.ColOff = x1 * EMU\n\tfrom.Row = rowStart\n\tfrom.RowOff = y1 * EMU\n\tto := xlsxTo{}\n\tto.Col = colEnd\n\tto.ColOff = x2 * EMU\n\tto.Row = rowEnd\n\tto.RowOff = y2 * EMU\n\ttwoCellAnchor.From = &from\n\ttwoCellAnchor.To = &to\n\n\tgraphicFrame := xlsxGraphicFrame{\n\t\tNvGraphicFramePr: xlsxNvGraphicFramePr{\n\t\t\tCNvPr: &xlsxCNvPr{\n\t\t\t\tID:    cNvPrID,\n\t\t\t\tName:  \"Chart \" + strconv.Itoa(cNvPrID),\n\t\t\t\tDescr: opts.AltText,\n\t\t\t},\n\t\t},\n\t\tGraphic: &xlsxGraphic{\n\t\t\tGraphicData: &xlsxGraphicData{\n\t\t\t\tURI: NameSpaceDrawingMLChart.Value,\n\t\t\t\tChart: &xlsxChart{\n\t\t\t\t\tC:   NameSpaceDrawingMLChart.Value,\n\t\t\t\t\tR:   SourceRelationship.Value,\n\t\t\t\t\tRID: \"rId\" + strconv.Itoa(rID),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tif len(opts.Name) > 0 {\n\t\tgraphicFrame.NvGraphicFramePr.CNvPr.Name = opts.Name\n\t}\n\tgraphic, _ := xml.Marshal(graphicFrame)\n\ttwoCellAnchor.GraphicFrame = string(graphic)\n\ttwoCellAnchor.ClientData = &xdrClientData{\n\t\tFLocksWithSheet:  *opts.Locked,\n\t\tFPrintsWithSheet: *opts.PrintObject,\n\t}\n\tcontent.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)\n\tf.Drawings.Store(drawingXML, content)\n\treturn err\n}\n\n// addSheetDrawingChart provides a function to add chart graphic frame for\n// chartsheet by given sheet, drawingXML, width, height, relationship index\n// and format sets.\nfunc (f *File) addSheetDrawingChart(drawingXML string, rID int, opts *GraphicOptions) error {\n\tcontent, cNvPrID, err := f.drawingParser(drawingXML)\n\tif err != nil {\n\t\treturn err\n\t}\n\tabsoluteAnchor := xdrCellAnchor{\n\t\tEditAs: opts.Positioning,\n\t\tPos:    &xlsxPoint2D{},\n\t\tExt:    &xlsxPositiveSize2D{Cx: 9280533, Cy: 6051719},\n\t}\n\n\tgraphicFrame := xlsxGraphicFrame{\n\t\tNvGraphicFramePr: xlsxNvGraphicFramePr{\n\t\t\tCNvPr: &xlsxCNvPr{\n\t\t\t\tID:   cNvPrID,\n\t\t\t\tName: \"Chart \" + strconv.Itoa(cNvPrID),\n\t\t\t},\n\t\t},\n\t\tGraphic: &xlsxGraphic{\n\t\t\tGraphicData: &xlsxGraphicData{\n\t\t\t\tURI: NameSpaceDrawingMLChart.Value,\n\t\t\t\tChart: &xlsxChart{\n\t\t\t\t\tC:   NameSpaceDrawingMLChart.Value,\n\t\t\t\t\tR:   SourceRelationship.Value,\n\t\t\t\t\tRID: \"rId\" + strconv.Itoa(rID),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tgraphic, _ := xml.Marshal(graphicFrame)\n\tabsoluteAnchor.GraphicFrame = string(graphic)\n\tabsoluteAnchor.ClientData = &xdrClientData{\n\t\tFLocksWithSheet:  *opts.Locked,\n\t\tFPrintsWithSheet: *opts.PrintObject,\n\t}\n\tcontent.AbsoluteAnchor = append(content.AbsoluteAnchor, &absoluteAnchor)\n\tf.Drawings.Store(drawingXML, content)\n\treturn err\n}\n\n// deleteDrawing provides a function to delete the chart graphic frame and\n// returns deleted embed relationships ID (for unique picture cell anchor) by\n// given coordinates and graphic type.\nfunc (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) ([]string, error) {\n\tvar (\n\t\terr            error\n\t\trID            string\n\t\tdelRID, refRID []string\n\t\trIDMaps        = map[string]int{}\n\t\twsDr           *xlsxWsDr\n\t\tdeCellAnchor   *decodeCellAnchor\n\t)\n\txdrCellAnchorFuncs := map[string]func(anchor *xdrCellAnchor) bool{\n\t\t\"Chart\": func(anchor *xdrCellAnchor) bool { return anchor.Pic == nil },\n\t\t\"Pic\":   func(anchor *xdrCellAnchor) bool { return anchor.Pic != nil },\n\t}\n\tdecodeCellAnchorFuncs := map[string]func(anchor *decodeCellAnchor) bool{\n\t\t\"Chart\": func(anchor *decodeCellAnchor) bool { return anchor.Pic == nil },\n\t\t\"Pic\":   func(anchor *decodeCellAnchor) bool { return anchor.Pic != nil },\n\t}\n\tonAnchorCell := func(c, r int) bool { return c == col && r == row }\n\tif wsDr, _, err = f.drawingParser(drawingXML); err != nil {\n\t\treturn delRID, err\n\t}\n\tdeleteCellAnchor := func(ca []*xdrCellAnchor) ([]*xdrCellAnchor, error) {\n\t\tfor idx := 0; idx < len(ca); idx++ {\n\t\t\tif err = nil; ca[idx].From != nil && xdrCellAnchorFuncs[drawingType](ca[idx]) {\n\t\t\t\trID = extractEmbedRID(ca[idx].Pic, nil)\n\t\t\t\trIDMaps[rID]++\n\t\t\t\tif onAnchorCell(ca[idx].From.Col, ca[idx].From.Row) {\n\t\t\t\t\trefRID = append(refRID, rID)\n\t\t\t\t\tca = append(ca[:idx], ca[idx+1:]...)\n\t\t\t\t\tidx--\n\t\t\t\t\trIDMaps[rID]--\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tdeCellAnchor = new(decodeCellAnchor)\n\t\t\tif err = f.xmlNewDecoder(strings.NewReader(\"<decodeCellAnchor>\" + ca[idx].GraphicFrame + \"</decodeCellAnchor>\")).\n\t\t\t\tDecode(deCellAnchor); err != nil && err != io.EOF {\n\t\t\t\treturn ca, err\n\t\t\t}\n\t\t\tif err = nil; deCellAnchor.From != nil && decodeCellAnchorFuncs[drawingType](deCellAnchor) {\n\t\t\t\trID = extractEmbedRID(nil, deCellAnchor.Pic)\n\t\t\t\trIDMaps[rID]++\n\t\t\t\tif onAnchorCell(deCellAnchor.From.Col, deCellAnchor.From.Row) {\n\t\t\t\t\trefRID = append(refRID, rID)\n\t\t\t\t\tca = append(ca[:idx], ca[idx+1:]...)\n\t\t\t\t\tidx--\n\t\t\t\t\trIDMaps[rID]--\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn ca, err\n\t}\n\tif wsDr.OneCellAnchor, err = deleteCellAnchor(wsDr.OneCellAnchor); err != nil {\n\t\treturn delRID, err\n\t}\n\tif wsDr.TwoCellAnchor, err = deleteCellAnchor(wsDr.TwoCellAnchor); err != nil {\n\t\treturn delRID, err\n\t}\n\tf.Drawings.Store(drawingXML, wsDr)\n\treturn getUnusedCellAnchorRID(delRID, refRID, rIDMaps), err\n}\n\n// extractEmbedRID returns embed relationship ID by giving cell anchor.\nfunc extractEmbedRID(pic *xlsxPic, decodePic *decodePic) string {\n\tvar rID string\n\tif pic != nil {\n\t\trID = pic.BlipFill.Blip.Embed\n\t}\n\tif decodePic != nil {\n\t\trID = decodePic.BlipFill.Blip.Embed\n\t}\n\treturn rID\n}\n\n// getUnusedCellAnchorRID returns relationship ID lists in the cell anchor which\n// for remove.\nfunc getUnusedCellAnchorRID(delRID, refRID []string, rIDMaps map[string]int) []string {\n\tfor _, rID := range refRID {\n\t\tif rIDMaps[rID] == 0 && inStrSlice(delRID, rID, false) == -1 {\n\t\t\tdelRID = append(delRID, rID)\n\t\t}\n\t}\n\treturn delRID\n}\n\n// deleteDrawingRels provides a function to delete relationships in\n// xl/drawings/_rels/drawings%d.xml.rels by giving drawings relationships path\n// and relationship ID.\nfunc (f *File) deleteDrawingRels(rels, rID string) {\n\tdrawingRels, _ := f.relsReader(rels)\n\tif drawingRels == nil {\n\t\tdrawingRels = &xlsxRelationships{}\n\t}\n\tdrawingRels.mu.Lock()\n\tdefer drawingRels.mu.Unlock()\n\tfor k, v := range drawingRels.Relationships {\n\t\tif v.ID == rID {\n\t\t\tdrawingRels.Relationships = append(drawingRels.Relationships[:k], drawingRels.Relationships[k+1:]...)\n\t\t}\n\t}\n\tf.Relationships.Store(rels, drawingRels)\n}\n\n// genAxID provides a function to generate ID for primary and secondary\n// horizontal or vertical axis.\nfunc (f *File) genAxID(opts *Chart) []*attrValInt {\n\topts.XAxis.axID, opts.YAxis.axID = 100000000, 100000001\n\tif opts.order > 0 && opts.YAxis.Secondary {\n\t\topts.XAxis.axID, opts.YAxis.axID = 100000003, 100000004\n\t}\n\treturn []*attrValInt{{Val: intPtr(opts.XAxis.axID)}, {Val: intPtr(opts.YAxis.axID)}}\n}\n"
  },
  {
    "path": "drawing_test.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestDrawingParser(t *testing.T) {\n\tf := File{\n\t\tDrawings: sync.Map{},\n\t\tPkg:      sync.Map{},\n\t}\n\tf.Pkg.Store(\"charset\", MacintoshCyrillicCharset)\n\tf.Pkg.Store(\"wsDr\", []byte(xml.Header+`<xdr:wsDr xmlns:xdr=\"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing\"><xdr:oneCellAnchor><xdr:graphicFrame/></xdr:oneCellAnchor></xdr:wsDr>`))\n\t// Test with one cell anchor\n\t_, _, err := f.drawingParser(\"wsDr\")\n\tassert.NoError(t, err)\n\t// Test with unsupported charset\n\t_, _, err = f.drawingParser(\"charset\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test with alternate content\n\tf.Drawings = sync.Map{}\n\tf.Pkg.Store(\"wsDr\", []byte(xml.Header+`<xdr:wsDr xmlns:xdr=\"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing\"><mc:AlternateContent xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"><mc:Choice xmlns:a14=\"http://schemas.microsoft.com/office/drawing/2010/main\" Requires=\"a14\"><xdr:twoCellAnchor editAs=\"oneCell\"></xdr:twoCellAnchor></mc:Choice><mc:Fallback/></mc:AlternateContent></xdr:wsDr>`))\n\t_, _, err = f.drawingParser(\"wsDr\")\n\tassert.NoError(t, err)\n}\n\nfunc TestDeleteDrawingRels(t *testing.T) {\n\tf := NewFile()\n\t// Test delete drawing relationships with unsupported charset\n\trels := \"xl/drawings/_rels/drawing1.xml.rels\"\n\tf.Relationships.Delete(rels)\n\tf.Pkg.Store(rels, MacintoshCyrillicCharset)\n\tf.deleteDrawingRels(rels, \"\")\n}\n"
  },
  {
    "path": "errors.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nvar (\n\t// ErrAddVBAProject defined the error message on add the VBA project in\n\t// the workbook.\n\tErrAddVBAProject = errors.New(\"unsupported VBA project\")\n\t// ErrAttrValBool defined the error message on marshal and unmarshal\n\t// boolean type XML attribute.\n\tErrAttrValBool = errors.New(\"unexpected child of attrValBool\")\n\t// ErrCellCharsLength defined the error message for receiving a cell\n\t// characters length that exceeds the limit.\n\tErrCellCharsLength = fmt.Errorf(\"cell value must be 0-%d characters\", TotalCellChars)\n\t// ErrCellStyles defined the error message on cell styles exceeds the limit.\n\tErrCellStyles = fmt.Errorf(\"the cell styles exceeds the %d limit\", MaxCellStyles)\n\t// ErrColumnNumber defined the error message on receive an invalid column\n\t// number.\n\tErrColumnNumber = fmt.Errorf(\"the column number must be greater than or equal to %d and less than or equal to %d\", MinColumns, MaxColumns)\n\t// ErrColumnWidth defined the error message on receive an invalid column\n\t// width.\n\tErrColumnWidth = fmt.Errorf(\"the width of the column must be less than or equal to %d characters\", MaxColumnWidth)\n\t// ErrCoordinates defined the error message on invalid coordinates tuples\n\t// length.\n\tErrCoordinates = errors.New(\"coordinates length must be 4\")\n\t// ErrCustomNumFmt defined the error message on receive the empty custom\n\t// number format.\n\tErrCustomNumFmt = errors.New(\"custom number format can not be empty\")\n\t// ErrDataValidationFormulaLength defined the error message for receiving a\n\t// data validation formula length that exceeds the limit.\n\tErrDataValidationFormulaLength = fmt.Errorf(\"data validation must be 0-%d characters\", MaxFieldLength)\n\t// ErrDataValidationRange defined the error message on set decimal range\n\t// exceeds limit.\n\tErrDataValidationRange = errors.New(\"data validation range exceeds limit\")\n\t// ErrDefinedNameDuplicate defined the error message on the same name\n\t// already exists on the scope.\n\tErrDefinedNameDuplicate = errors.New(\"the same name already exists on the scope\")\n\t// ErrDefinedNameScope defined the error message on not found defined name\n\t// in the given scope.\n\tErrDefinedNameScope = errors.New(\"no defined name on the scope\")\n\t// ErrExistsSheet defined the error message on given sheet already exists.\n\tErrExistsSheet = errors.New(\"the same name sheet already exists\")\n\t// ErrExistsTableName defined the error message on given table already\n\t// exists.\n\tErrExistsTableName = errors.New(\"the same name table already exists\")\n\t// ErrFillType defined the error message on receive an invalid fill type.\n\tErrFillType = errors.New(\"fill type value must be one of 'gradient' or 'pattern'\")\n\t// ErrFillGradientColor defined the error message on receive an invalid fill\n\t// color for 'gradient' type.\n\tErrFillGradientColor = errors.New(\"fill color value must be an array of two colors for 'gradient' type\")\n\t// ErrFillGradientShading defined the error message on receive an invalid\n\t// fill shading for 'gradient' type.\n\tErrFillGradientShading = errors.New(\"fill shading value must be between 0 and 16 for 'gradient' type\")\n\t// ErrFillPatternColor defined the error message on receive an invalid fill\n\t// color for 'pattern' type.\n\tErrFillPatternColor = errors.New(\"fill color value must be empty or an array of one color for 'pattern' type\")\n\t// ErrFillPattern defined the error message on receive an invalid fill\n\t// pattern.\n\tErrFillPattern = errors.New(\"fill pattern value must be between 0 and 18\")\n\t// ErrFontLength defined the error message on the length of the font\n\t// family name overflow.\n\tErrFontLength = fmt.Errorf(\"the length of the font family name must be less than or equal to %d\", MaxFontFamilyLength)\n\t// ErrFontSize defined the error message on the size of the font is invalid.\n\tErrFontSize = fmt.Errorf(\"font size must be an integer from %d to %d points\", MinFontSize, MaxFontSize)\n\t// ErrFormControlValue defined the error message for receiving a scroll\n\t// value exceeds limit.\n\tErrFormControlValue = fmt.Errorf(\"scroll value must be an integer from 0 to %d\", MaxFormControlValue)\n\t// ErrGroupSheets defined the error message on group sheets.\n\tErrGroupSheets = errors.New(\"group worksheet must contain an active worksheet\")\n\t// ErrImgExt defined the error message on receive an unsupported image\n\t// extension.\n\tErrImgExt = errors.New(\"unsupported image extension\")\n\t// ErrInvalidFormula defined the error message on receive an invalid\n\t// formula.\n\tErrInvalidFormula = errors.New(\"formula not valid\")\n\t// ErrMaxFilePathLength defined the error message on receive the file path\n\t// length overflow.\n\tErrMaxFilePathLength = fmt.Errorf(\"file path length exceeds maximum limit %d characters\", MaxFilePathLength)\n\t// ErrMaxRowHeight defined the error message on receive an invalid row\n\t// height.\n\tErrMaxRowHeight = fmt.Errorf(\"the height of the row must be less than or equal to %d points\", MaxRowHeight)\n\t// ErrMaxRows defined the error message on receive a row number exceeds\n\t// maximum limit.\n\tErrMaxRows = errors.New(\"row number exceeds maximum limit\")\n\t// ErrNameLength defined the error message on receiving the defined name or\n\t// table name length exceeds the limit.\n\tErrNameLength = fmt.Errorf(\"the name length exceeds the %d characters limit\", MaxFieldLength)\n\t// ErrMaxGraphicAltTextLength defined the error message on receiving the\n\t// graphic alt text length exceeds the limit.\n\tErrMaxGraphicAltTextLength = fmt.Errorf(\"the alt text length exceeds the %d characters limit\", MaxGraphicAltTextLength)\n\t// ErrMaxGraphicNameLength defined the error message on receiving the\n\t// graphic name length exceeds the limit.\n\tErrMaxGraphicNameLength = fmt.Errorf(\"the name length exceeds the %d characters limit\", MaxGraphicNameLength)\n\t// ErrOptionsUnzipSizeLimit defined the error message for receiving\n\t// invalid UnzipSizeLimit and UnzipXMLSizeLimit.\n\tErrOptionsUnzipSizeLimit = errors.New(\"the value of UnzipSizeLimit should be greater than or equal to UnzipXMLSizeLimit\")\n\t// ErrOutlineLevel defined the error message on receive an invalid outline\n\t// level number.\n\tErrOutlineLevel = errors.New(\"invalid outline level\")\n\t// ErrPageSetupAdjustTo defined the error message for receiving a page setup\n\t// adjust to value exceeds limit.\n\tErrPageSetupAdjustTo = errors.New(\"adjust to value must be an integer from 0 to 400\")\n\t// ErrParameterInvalid defined the error message on receive the invalid\n\t// parameter.\n\tErrParameterInvalid = errors.New(\"parameter is invalid\")\n\t// ErrParameterRequired defined the error message on receive the empty\n\t// parameter.\n\tErrParameterRequired = errors.New(\"parameter is required\")\n\t// ErrPasswordLengthInvalid defined the error message on invalid password\n\t// length.\n\tErrPasswordLengthInvalid = errors.New(\"password length invalid\")\n\t// ErrPivotTableClassicLayout defined the error message on enable\n\t// ClassicLayout and CompactData in the same time.\n\tErrPivotTableClassicLayout = errors.New(\"cannot enable ClassicLayout and CompactData in the same time\")\n\t// ErrSave defined the error message for saving file.\n\tErrSave = errors.New(\"no path defined for file, consider File.WriteTo or File.Write\")\n\t// ErrSheetIdx defined the error message on receive the invalid worksheet\n\t// index.\n\tErrSheetIdx = errors.New(\"invalid worksheet index\")\n\t// ErrSheetNameBlank defined the error message on receive the blank sheet\n\t// name.\n\tErrSheetNameBlank = errors.New(\"the sheet name can not be blank\")\n\t// ErrSheetNameInvalid defined the error message on receive the sheet name\n\t// contains invalid characters.\n\tErrSheetNameInvalid = errors.New(\"the sheet can not contain any of the characters :\\\\/?*[or]\")\n\t// ErrSheetNameLength defined the error message on receiving the sheet\n\t// name length exceeds the limit.\n\tErrSheetNameLength = fmt.Errorf(\"the sheet name length exceeds the %d characters limit\", MaxSheetNameLength)\n\t// ErrSheetNameSingleQuote defined the error message on the first or last\n\t// character of the sheet name was a single quote.\n\tErrSheetNameSingleQuote = errors.New(\"the first or last character of the sheet name can not be a single quote\")\n\t// ErrSparkline defined the error message on receive the invalid sparkline\n\t// parameters.\n\tErrSparkline = errors.New(\"must have the same number of 'Location' and 'Range' parameters\")\n\t// ErrSparklineLocation defined the error message on missing Location\n\t// parameters\n\tErrSparklineLocation = errors.New(\"parameter 'Location' is required\")\n\t// ErrSparklineRange defined the error message on missing sparkline Range\n\t// parameters\n\tErrSparklineRange = errors.New(\"parameter 'Range' is required\")\n\t// ErrSparklineStyle defined the error message on receive the invalid\n\t// sparkline Style parameters.\n\tErrSparklineStyle = errors.New(\"parameter 'Style' value must be an integer from 0 to 35\")\n\t// ErrSparklineType defined the error message on receive the invalid\n\t// sparkline Type parameters.\n\tErrSparklineType = errors.New(\"parameter 'Type' value must be one of 'line', 'column' or 'win_loss'\")\n\t// ErrTotalSheetHyperlinks defined the error message on hyperlinks count\n\t// overflow.\n\tErrTotalSheetHyperlinks = errors.New(\"over maximum limit hyperlinks in a worksheet\")\n\t// ErrTransparency defined the error message for receiving a transparency\n\t// value exceeds limit.\n\tErrTransparency = errors.New(\"transparency value must be an integer from 0 to 100\")\n\t// ErrUnknownEncryptMechanism defined the error message on unsupported\n\t// encryption mechanism.\n\tErrUnknownEncryptMechanism = errors.New(\"unknown encryption mechanism\")\n\t// ErrUnprotectSheet defined the error message on worksheet has set no\n\t// protection.\n\tErrUnprotectSheet = errors.New(\"worksheet has set no protect\")\n\t// ErrUnprotectSheetPassword defined the error message on remove sheet\n\t// protection with password verification failed.\n\tErrUnprotectSheetPassword = errors.New(\"worksheet protect password not match\")\n\t// ErrUnprotectWorkbook defined the error message on workbook has set no\n\t// protection.\n\tErrUnprotectWorkbook = errors.New(\"workbook has set no protect\")\n\t// ErrUnprotectWorkbookPassword defined the error message on remove workbook\n\t// protection with password verification failed.\n\tErrUnprotectWorkbookPassword = errors.New(\"workbook protect password not match\")\n\t// ErrUnsupportedEncryptMechanism defined the error message on unsupported\n\t// encryption mechanism.\n\tErrUnsupportedEncryptMechanism = errors.New(\"unsupported encryption mechanism\")\n\t// ErrUnsupportedHashAlgorithm defined the error message on unsupported\n\t// hash algorithm.\n\tErrUnsupportedHashAlgorithm = errors.New(\"unsupported hash algorithm\")\n\t// ErrUnsupportedNumberFormat defined the error message on unsupported\n\t// number format expression.\n\tErrUnsupportedNumberFormat = errors.New(\"unsupported number format token\")\n\t// ErrWorkbookFileFormat defined the error message on receive an\n\t// unsupported workbook file format.\n\tErrWorkbookFileFormat = errors.New(\"unsupported workbook file format\")\n\t// ErrWorkbookPassword defined the error message on receiving the incorrect\n\t// workbook password.\n\tErrWorkbookPassword = errors.New(\"the supplied open workbook password is not correct\")\n)\n\n// ErrSheetNotExist defined an error of sheet that does not exist.\ntype ErrSheetNotExist struct {\n\tSheetName string\n}\n\n// Error returns the error message on receiving the non existing sheet name.\nfunc (err ErrSheetNotExist) Error() string {\n\treturn fmt.Sprintf(\"sheet %s does not exist\", err.SheetName)\n}\n\n// newAddCommentError defined the error message on the comment already exist in\n// the cell.\nfunc newAddCommentError(cell string) error {\n\treturn fmt.Errorf(\"comment already exist on cell %s\", cell)\n}\n\n// newCellNameToCoordinatesError defined the error message on converts\n// alphanumeric cell name to coordinates.\nfunc newCellNameToCoordinatesError(cell string, err error) error {\n\treturn fmt.Errorf(\"cannot convert cell %q to coordinates: %v\", cell, err)\n}\n\n// newCoordinatesToCellNameError defined the error message on converts [X, Y]\n// coordinates to alpha-numeric cell name.\nfunc newCoordinatesToCellNameError(col, row int) error {\n\treturn fmt.Errorf(\"invalid cell reference [%d, %d]\", col, row)\n}\n\n// newFieldLengthError defined the error message on receiving the field length\n// overflow.\nfunc newFieldLengthError(name string) error {\n\treturn fmt.Errorf(\"field %s must be less than or equal to 255 characters\", name)\n}\n\n// newInvalidAutoFilterColumnError defined the error message on receiving the\n// incorrect index of column.\nfunc newInvalidAutoFilterColumnError(col string) error {\n\treturn fmt.Errorf(\"incorrect index of column %q\", col)\n}\n\n// newInvalidAutoFilterExpError defined the error message on receiving the\n// incorrect number of tokens in criteria expression.\nfunc newInvalidAutoFilterExpError(exp string) error {\n\treturn fmt.Errorf(\"incorrect number of tokens in criteria %q\", exp)\n}\n\n// newInvalidAutoFilterOperatorError defined the error message on receiving the\n// incorrect expression operator.\nfunc newInvalidAutoFilterOperatorError(op, exp string) error {\n\treturn fmt.Errorf(\"the operator %q in expression %q is not valid in relation to Blanks/NonBlanks\", op, exp)\n}\n\n// newInvalidCellNameError defined the error message on receiving the invalid\n// cell name.\nfunc newInvalidCellNameError(cell string) error {\n\treturn fmt.Errorf(\"invalid cell name %q\", cell)\n}\n\n// newInvalidColumnNameError defined the error message on receiving the\n// invalid column name.\nfunc newInvalidColumnNameError(col string) error {\n\treturn fmt.Errorf(\"invalid column name %q\", col)\n}\n\n// newInvalidExcelDateError defined the error message on receiving the data\n// with negative values.\nfunc newInvalidExcelDateError(dateValue float64) error {\n\treturn fmt.Errorf(\"invalid date value %f, negative values are not supported\", dateValue)\n}\n\n// newInvalidLinkTypeError defined the error message on receiving the invalid\n// hyper link type.\nfunc newInvalidLinkTypeError(linkType string) error {\n\treturn fmt.Errorf(\"invalid link type %q\", linkType)\n}\n\n// newInvalidNameError defined the error message on receiving the invalid\n// defined name or table name.\nfunc newInvalidNameError(name string) error {\n\treturn fmt.Errorf(\"invalid name %q, the name should be starts with a letter or underscore, can not include a space or character, and can not conflict with an existing name in the workbook\", name)\n}\n\n// newInvalidOptionalValue defined the error message on receiving the invalid\n// optional value.\nfunc newInvalidOptionalValue(name, value string, values []string) error {\n\treturn fmt.Errorf(\"invalid %s value %q, acceptable value should be one of %s\", name, value, strings.Join(values, \", \"))\n}\n\n// newInvalidRowNumberError defined the error message on receiving the invalid\n// row number.\nfunc newInvalidRowNumberError(row int) error {\n\treturn fmt.Errorf(\"invalid row number %d\", row)\n}\n\n// newInvalidSlicerNameError defined the error message on receiving the invalid\n// slicer name.\nfunc newInvalidSlicerNameError(name string) error {\n\treturn fmt.Errorf(\"invalid slicer name %q\", name)\n}\n\n// newInvalidStyleID defined the error message on receiving the invalid style\n// ID.\nfunc newInvalidStyleID(styleID int) error {\n\treturn fmt.Errorf(\"invalid style ID %d\", styleID)\n}\n\n// newNoExistSlicerError defined the error message on receiving the non existing\n// slicer name.\nfunc newNoExistSlicerError(name string) error {\n\treturn fmt.Errorf(\"slicer %s does not exist\", name)\n}\n\n// newNoExistTableError defined the error message on receiving the non existing\n// table name.\nfunc newNoExistTableError(name string) error {\n\treturn fmt.Errorf(\"table %s does not exist\", name)\n}\n\n// newNotWorksheetError defined the error message on receiving a sheet which\n// not a worksheet.\nfunc newNotWorksheetError(name string) error {\n\treturn fmt.Errorf(\"sheet %s is not a worksheet\", name)\n}\n\n// newPivotTableColFieldsError defined the error message on same data field\n// appears both in the pivot table column fields and filter fields.\nfunc newPivotTableColFieldsError(data []string) error {\n\treturn fmt.Errorf(\"data fields %s appear both in the pivot table column fields and filter fields\", strings.Join(data, \", \"))\n}\n\n// newPivotTableRowFieldsError defined the error message on same data field\n// appears both in the pivot table row fields and filter fields.\nfunc newPivotTableRowFieldsError(data []string) error {\n\treturn fmt.Errorf(\"data fields %s appear both in the pivot table row fields and filter fields\", strings.Join(data, \", \"))\n}\n\n// newPivotTableDataRangeError defined the error message on receiving the\n// invalid pivot table data range.\nfunc newPivotTableDataRangeError(msg string) error {\n\treturn fmt.Errorf(\"parameter 'DataRange' parsing error: %s\", msg)\n}\n\n// newPivotTableRangeError defined the error message on receiving the invalid\n// pivot table range.\nfunc newPivotTableRangeError(msg string) error {\n\treturn fmt.Errorf(\"parameter 'PivotTableRange' parsing error: %s\", msg)\n}\n\n// newStreamSetRowError defined the error message on the stream writer\n// receiving the non-ascending row number.\nfunc newStreamSetRowError(row int) error {\n\treturn fmt.Errorf(\"row %d has already been written\", row)\n}\n\n// newStreamSetRowOrderError defined the error message on calling the SetRow\n// function before the order function.\nfunc newStreamSetRowOrderError(name string) error {\n\treturn fmt.Errorf(\"must call the %s function before the SetRow function\", name)\n}\n\n// newUnknownFilterTokenError defined the error message on receiving a unknown\n// filter operator token.\nfunc newUnknownFilterTokenError(token string) error {\n\treturn fmt.Errorf(\"unknown operator: %s\", token)\n}\n\n// newUnsupportedChartType defined the error message on receiving the chart\n// type are unsupported.\nfunc newUnsupportedChartType(chartType ChartType) error {\n\treturn fmt.Errorf(\"unsupported chart type %d\", chartType)\n}\n\n// newUnsupportedPivotCacheSourceType defined the error message on receiving the\n// source type of pivot table cache.\nfunc newUnsupportedPivotCacheSourceType(sourceType string) error {\n\treturn fmt.Errorf(\"unsupported pivot table cache source type: %s\", sourceType)\n}\n\n// newUnzipSizeLimitError defined the error message on unzip size exceeds the\n// limit.\nfunc newUnzipSizeLimitError(unzipSizeLimit int64) error {\n\treturn fmt.Errorf(\"unzip size exceeds the %d bytes limit\", unzipSizeLimit)\n}\n\n// newViewIdxError defined the error message on receiving a invalid sheet view\n// index.\nfunc newViewIdxError(viewIndex int) error {\n\treturn fmt.Errorf(\"view index %d out of range\", viewIndex)\n}\n"
  },
  {
    "path": "errors_test.go",
    "content": "package excelize\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewInvalidColNameError(t *testing.T) {\n\tassert.EqualError(t, newInvalidColumnNameError(\"A\"), \"invalid column name \\\"A\\\"\")\n\tassert.EqualError(t, newInvalidColumnNameError(\"\"), \"invalid column name \\\"\\\"\")\n}\n\nfunc TestNewInvalidRowNumberError(t *testing.T) {\n\tassert.EqualError(t, newInvalidRowNumberError(0), \"invalid row number 0\")\n}\n\nfunc TestNewInvalidCellNameError(t *testing.T) {\n\tassert.EqualError(t, newInvalidCellNameError(\"A\"), \"invalid cell name \\\"A\\\"\")\n\tassert.EqualError(t, newInvalidCellNameError(\"\"), \"invalid cell name \\\"\\\"\")\n}\n\nfunc TestNewInvalidExcelDateError(t *testing.T) {\n\tassert.EqualError(t, newInvalidExcelDateError(-1), \"invalid date value -1.000000, negative values are not supported\")\n}\n"
  },
  {
    "path": "excelize.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n//\n// See https://xuri.me/excelize for more information about this package.\npackage excelize\n\nimport (\n\t\"archive/zip\"\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"golang.org/x/net/html/charset\"\n)\n\n// File define a populated spreadsheet file struct.\ntype File struct {\n\tmu               sync.Mutex\n\tchecked          sync.Map\n\tformulaChecked   bool\n\tzip64Entries     []string\n\toptions          *Options\n\tsharedStringItem [][]uint\n\tsharedStringsMap map[string]int\n\tsharedStringTemp *os.File\n\tsheetMap         map[string]string\n\tstreams          map[string]*StreamWriter\n\ttempFiles        sync.Map\n\txmlAttr          sync.Map\n\tcalcCache        sync.Map\n\tformulaArgCache  sync.Map\n\tCalcChain        *xlsxCalcChain\n\tCharsetReader    func(charset string, input io.Reader) (rdr io.Reader, err error)\n\tComments         map[string]*xlsxComments\n\tContentTypes     *xlsxTypes\n\tDecodeVMLDrawing map[string]*decodeVmlDrawing\n\tDecodeCellImages *decodeCellImages\n\tDrawings         sync.Map\n\tPath             string\n\tPkg              sync.Map\n\tRelationships    sync.Map\n\tSharedStrings    *xlsxSST\n\tSheet            sync.Map\n\tSheetCount       int\n\tStyles           *xlsxStyleSheet\n\tTheme            *decodeTheme\n\tVMLDrawing       map[string]*vmlDrawing\n\tVolatileDeps     *xlsxVolTypes\n\tWorkBook         *xlsxWorkbook\n\tZipWriter        func(io.Writer) ZipWriter\n}\n\n// ZipWriter defines an interface for writing files to a ZIP archive. It\n// provides methods to create new files within the archive, add files from a\n// filesystem, and close the archive when writing is complete.\ntype ZipWriter interface {\n\tCreate(name string) (io.Writer, error)\n\tAddFS(fsys fs.FS) error\n\tClose() error\n}\n\n// Options define the options for opening and reading the spreadsheet.\n//\n// MaxCalcIterations specifies the maximum iterations for iterative\n// calculation, the default value is 0.\n//\n// Password specifies the password of the spreadsheet in plain text.\n//\n// RawCellValue specifies if apply the number format for the cell value or get\n// the raw value.\n//\n// UnzipSizeLimit specifies to unzip size limit in bytes on open the\n// spreadsheet, this value should be greater than or equal to\n// UnzipXMLSizeLimit, the default size limit is 16GB.\n//\n// UnzipXMLSizeLimit specifies the memory limit on unzipping worksheet and\n// shared string table in bytes, worksheet XML will be extracted to system\n// temporary directory when the file size is over this value, this value\n// should be less than or equal to UnzipSizeLimit, the default value is\n// 16MB.\n//\n// TmpDir specifies the temporary directory for creating temporary files, if the\n// value is empty, the system default temporary directory will be used.\n//\n// ShortDatePattern specifies the short date number format code. In the\n// spreadsheet applications, date formats display date and time serial numbers\n// as date values. Date formats that begin with an asterisk (*) respond to\n// changes in regional date and time settings that are specified for the\n// operating system. Formats without an asterisk are not affected by operating\n// system settings. The ShortDatePattern used for specifies apply date formats\n// that begin with an asterisk.\n//\n// LongDatePattern specifies the long date number format code.\n//\n// LongTimePattern specifies the long time number format code.\n//\n// CultureInfo specifies the country code for applying built-in language number\n// format code these effect by the system's local language settings.\ntype Options struct {\n\tMaxCalcIterations uint\n\tPassword          string\n\tRawCellValue      bool\n\tUnzipSizeLimit    int64\n\tUnzipXMLSizeLimit int64\n\tTmpDir            string\n\tShortDatePattern  string\n\tLongDatePattern   string\n\tLongTimePattern   string\n\tCultureInfo       CultureName\n}\n\n// OpenFile take the name of a spreadsheet file and returns a populated\n// spreadsheet file struct for it. For example, open spreadsheet with\n// password protection:\n//\n//\tf, err := excelize.OpenFile(\"Book1.xlsx\", excelize.Options{Password: \"password\"})\n//\n// Close the file by Close function after opening the spreadsheet.\nfunc OpenFile(filename string, opts ...Options) (*File, error) {\n\tfile, err := os.Open(filepath.Clean(filename))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tf, err := OpenReader(file, opts...)\n\tif err != nil {\n\t\tif closeErr := file.Close(); closeErr != nil {\n\t\t\treturn f, closeErr\n\t\t}\n\t\treturn f, err\n\t}\n\tf.Path = filename\n\treturn f, file.Close()\n}\n\n// newFile is object builder\nfunc newFile() *File {\n\treturn &File{\n\t\toptions:          &Options{UnzipSizeLimit: UnzipSizeLimit, UnzipXMLSizeLimit: StreamChunkSize},\n\t\txmlAttr:          sync.Map{},\n\t\tchecked:          sync.Map{},\n\t\tsheetMap:         make(map[string]string),\n\t\ttempFiles:        sync.Map{},\n\t\tComments:         make(map[string]*xlsxComments),\n\t\tDrawings:         sync.Map{},\n\t\tsharedStringsMap: make(map[string]int),\n\t\tSheet:            sync.Map{},\n\t\tDecodeVMLDrawing: make(map[string]*decodeVmlDrawing),\n\t\tVMLDrawing:       make(map[string]*vmlDrawing),\n\t\tRelationships:    sync.Map{},\n\t\tCharsetReader:    charset.NewReaderLabel,\n\t\tZipWriter:        func(w io.Writer) ZipWriter { return zip.NewWriter(w) },\n\t}\n}\n\n// checkOpenReaderOptions check and validate options field value for open\n// reader.\nfunc (f *File) checkOpenReaderOptions() error {\n\tif f.options.UnzipSizeLimit == 0 {\n\t\tf.options.UnzipSizeLimit = UnzipSizeLimit\n\t\tif f.options.UnzipXMLSizeLimit > f.options.UnzipSizeLimit {\n\t\t\tf.options.UnzipSizeLimit = f.options.UnzipXMLSizeLimit\n\t\t}\n\t}\n\tif f.options.UnzipXMLSizeLimit == 0 {\n\t\tf.options.UnzipXMLSizeLimit = StreamChunkSize\n\t\tif f.options.UnzipSizeLimit < f.options.UnzipXMLSizeLimit {\n\t\t\tf.options.UnzipXMLSizeLimit = f.options.UnzipSizeLimit\n\t\t}\n\t}\n\tif f.options.UnzipXMLSizeLimit > f.options.UnzipSizeLimit {\n\t\treturn ErrOptionsUnzipSizeLimit\n\t}\n\treturn f.checkDateTimePattern()\n}\n\n// OpenReader read data stream from io.Reader and return a populated\n// spreadsheet file.\nfunc OpenReader(r io.Reader, opts ...Options) (*File, error) {\n\tb, err := io.ReadAll(r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tf := newFile()\n\tf.options = f.getOptions(opts...)\n\tif err = f.checkOpenReaderOptions(); err != nil {\n\t\treturn nil, err\n\t}\n\tif bytes.Contains(b, oleIdentifier) {\n\t\tif b, err = Decrypt(b, f.options); err != nil {\n\t\t\treturn nil, ErrWorkbookFileFormat\n\t\t}\n\t}\n\tzr, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))\n\tif err != nil {\n\t\tif len(f.options.Password) > 0 {\n\t\t\treturn nil, ErrWorkbookPassword\n\t\t}\n\t\treturn nil, err\n\t}\n\tfile, sheetCount, err := f.ReadZipReader(zr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tf.SheetCount = sheetCount\n\tfor k, v := range file {\n\t\tf.Pkg.Store(k, v)\n\t}\n\tif f.CalcChain, err = f.calcChainReader(); err != nil {\n\t\treturn f, err\n\t}\n\tif f.sheetMap, err = f.getSheetMap(); err != nil {\n\t\treturn f, err\n\t}\n\tif f.Styles, err = f.stylesReader(); err != nil {\n\t\treturn f, err\n\t}\n\tf.Theme, err = f.themeReader()\n\treturn f, err\n}\n\n// getOptions provides a function to parse the optional settings for open\n// and reading spreadsheet.\nfunc (f *File) getOptions(opts ...Options) *Options {\n\toptions := f.options\n\tfor _, opt := range opts {\n\t\toptions = &opt\n\t}\n\treturn options\n}\n\n// CharsetTranscoder set user defined codepage transcoder function for open\n// workbook from non UTF-8 encoding.\nfunc (f *File) CharsetTranscoder(fn func(charset string, input io.Reader) (rdr io.Reader, err error)) *File {\n\tf.CharsetReader = fn\n\treturn f\n}\n\n// SetZipWriter set user defined zip writer function for saving the workbook.\nfunc (f *File) SetZipWriter(fn func(io.Writer) ZipWriter) *File { f.ZipWriter = fn; return f }\n\n// Creates new XML decoder with charset reader.\nfunc (f *File) xmlNewDecoder(rdr io.Reader) (ret *xml.Decoder) {\n\tret = xml.NewDecoder(rdr)\n\tret.CharsetReader = f.CharsetReader\n\treturn\n}\n\n// setDefaultTimeStyle provides a function to set default numbers format for\n// time.Time type cell value by given worksheet name, cell reference and\n// number format code.\nfunc (f *File) setDefaultTimeStyle(sheet, cell string, format int) error {\n\tstyleIdx, err := f.GetCellStyle(sheet, cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif styleIdx == 0 {\n\t\tstyleIdx, _ = f.NewStyle(&Style{NumFmt: format})\n\t} else {\n\t\tstyle, _ := f.GetStyle(styleIdx)\n\t\tstyle.NumFmt = format\n\t\tstyleIdx, _ = f.NewStyle(style)\n\t}\n\treturn f.SetCellStyle(sheet, cell, cell, styleIdx)\n}\n\n// workSheetReader provides a function to get the pointer to the structure\n// after deserialization by given worksheet name.\nfunc (f *File) workSheetReader(sheet string) (ws *xlsxWorksheet, err error) {\n\tvar (\n\t\tname string\n\t\tok   bool\n\t)\n\tif err = checkSheetName(sheet); err != nil {\n\t\treturn\n\t}\n\tif name, ok = f.getSheetXMLPath(sheet); !ok {\n\t\terr = ErrSheetNotExist{sheet}\n\t\treturn\n\t}\n\tif worksheet, ok := f.Sheet.Load(name); ok && worksheet != nil {\n\t\tws = worksheet.(*xlsxWorksheet)\n\t\treturn\n\t}\n\tfor _, sheetType := range []string{\"xl/chartsheets\", \"xl/dialogsheet\", \"xl/macrosheet\"} {\n\t\tif strings.HasPrefix(name, sheetType) {\n\t\t\terr = newNotWorksheetError(sheet)\n\t\t\treturn\n\t\t}\n\t}\n\tws = new(xlsxWorksheet)\n\tif attrs, ok := f.xmlAttr.Load(name); !ok {\n\t\td := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readBytes(name))))\n\t\tif attrs == nil {\n\t\t\tattrs = []xml.Attr{}\n\t\t}\n\t\tattrs = append(attrs.([]xml.Attr), getRootElement(d)...)\n\t\tf.xmlAttr.Store(name, attrs)\n\t}\n\tif err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readBytes(name)))).\n\t\tDecode(ws); err != nil && err != io.EOF {\n\t\treturn\n\t}\n\terr = nil\n\tif _, ok = f.checked.Load(name); !ok {\n\t\tws.checkSheet()\n\t\tif err = ws.checkRow(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tf.checked.Store(name, true)\n\t}\n\tf.Sheet.Store(name, ws)\n\treturn\n}\n\n// checkSheet provides a function to fill each row element and make that is\n// continuous in a worksheet of XML.\nfunc (ws *xlsxWorksheet) checkSheet() {\n\tvar (\n\t\trow        int\n\t\tr0Rows     []xlsxRow\n\t\tlastRowNum = func(r xlsxRow) int {\n\t\t\tvar num int\n\t\t\tfor _, cell := range r.C {\n\t\t\t\tif _, row, err := CellNameToCoordinates(cell.R); err == nil {\n\t\t\t\t\tif row > num {\n\t\t\t\t\t\tnum = row\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn num\n\t\t}\n\t)\n\tfor i := 0; i < len(ws.SheetData.Row); i++ {\n\t\tr := ws.SheetData.Row[i]\n\t\tif r.R == 0 || r.R == row {\n\t\t\tnum := lastRowNum(r)\n\t\t\tif num > row {\n\t\t\t\trow = num\n\t\t\t}\n\t\t\tif num == 0 {\n\t\t\t\trow++\n\t\t\t}\n\t\t\tr.R = row\n\t\t\tr0Rows = append(r0Rows, r)\n\t\t\tws.SheetData.Row = append(ws.SheetData.Row[:i], ws.SheetData.Row[i+1:]...)\n\t\t\ti--\n\t\t\tcontinue\n\t\t}\n\t\tif r.R != 0 && r.R > row {\n\t\t\trow = r.R\n\t\t}\n\t}\n\tsheetData := xlsxSheetData{Row: make([]xlsxRow, row)}\n\trow = 0\n\tfor _, r := range ws.SheetData.Row {\n\t\tif r.R != 0 {\n\t\t\tsheetData.Row[r.R-1] = r\n\t\t\trow = r.R\n\t\t}\n\t}\n\tfor _, r0Row := range r0Rows {\n\t\tsheetData.Row[r0Row.R-1].R = r0Row.R\n\t\tws.checkSheetR0(&sheetData, &r0Row, true)\n\t}\n\tfor i := 1; i <= row; i++ {\n\t\tsheetData.Row[i-1].R = i\n\t\tws.checkSheetR0(&sheetData, &sheetData.Row[i-1], false)\n\t}\n}\n\n// checkSheetR0 handle the row element with r=\"0\" attribute, cells in this row\n// could be disorderly, the cell in this row can be used as the value of\n// which cell is empty in the normal rows.\nfunc (ws *xlsxWorksheet) checkSheetR0(sheetData *xlsxSheetData, rowData *xlsxRow, r0 bool) {\n\tcheckRow := func(col, row int, r0 bool, cell xlsxC) {\n\t\trowIdx := row - 1\n\t\tcolumns, colIdx := len(sheetData.Row[rowIdx].C), col-1\n\t\tfor c := columns; c < col; c++ {\n\t\t\tsheetData.Row[rowIdx].C = append(sheetData.Row[rowIdx].C, xlsxC{})\n\t\t}\n\t\tif !sheetData.Row[rowIdx].C[colIdx].hasValue() {\n\t\t\tsheetData.Row[rowIdx].C[colIdx] = cell\n\t\t}\n\t\tif r0 {\n\t\t\tsheetData.Row[rowIdx].C[colIdx] = cell\n\t\t}\n\t}\n\tvar err error\n\tfor i, cell := range rowData.C {\n\t\tcol, row := i+1, rowData.R\n\t\tif cell.R == \"\" {\n\t\t\tcheckRow(col, row, r0, cell)\n\t\t\tcontinue\n\t\t}\n\t\tif col, row, err = CellNameToCoordinates(cell.R); err == nil && r0 {\n\t\t\tcheckRow(col, row, r0, cell)\n\t\t}\n\t}\n\tws.SheetData = *sheetData\n}\n\n// setRels provides a function to set relationships by given relationship ID,\n// XML path, relationship type, target and target mode.\nfunc (f *File) setRels(rID, relPath, relType, target, targetMode string) int {\n\trels, _ := f.relsReader(relPath)\n\tif rels == nil || rID == \"\" {\n\t\treturn f.addRels(relPath, relType, target, targetMode)\n\t}\n\trels.mu.Lock()\n\tdefer rels.mu.Unlock()\n\tvar ID int\n\tfor i, rel := range rels.Relationships {\n\t\tif rel.ID == rID {\n\t\t\trels.Relationships[i].Type = relType\n\t\t\trels.Relationships[i].Target = target\n\t\t\trels.Relationships[i].TargetMode = targetMode\n\t\t\tID, _ = strconv.Atoi(strings.TrimPrefix(rID, \"rId\"))\n\t\t\tbreak\n\t\t}\n\t}\n\treturn ID\n}\n\n// addRels provides a function to add relationships by given XML path,\n// relationship type, target and target mode.\nfunc (f *File) addRels(relPath, relType, target, targetMode string) int {\n\tuniqPart := map[string]string{\n\t\tSourceRelationshipCustomProperties: \"/docProps/custom.xml\",\n\t\tSourceRelationshipSharedStrings:    \"/xl/sharedStrings.xml\",\n\t}\n\trels, _ := f.relsReader(relPath)\n\tif rels == nil {\n\t\trels = &xlsxRelationships{}\n\t}\n\trels.mu.Lock()\n\tdefer rels.mu.Unlock()\n\tvar rID int\n\tfor idx, rel := range rels.Relationships {\n\t\tID, _ := strconv.Atoi(strings.TrimPrefix(rel.ID, \"rId\"))\n\t\tif ID > rID {\n\t\t\trID = ID\n\t\t}\n\t\tif relType == rel.Type {\n\t\t\tif partName, ok := uniqPart[rel.Type]; ok {\n\t\t\t\trels.Relationships[idx].Target = partName\n\t\t\t\treturn rID\n\t\t\t}\n\t\t}\n\t}\n\trID++\n\tvar ID bytes.Buffer\n\tID.WriteString(\"rId\")\n\tID.WriteString(strconv.Itoa(rID))\n\trels.Relationships = append(rels.Relationships, xlsxRelationship{\n\t\tID:         ID.String(),\n\t\tType:       relType,\n\t\tTarget:     target,\n\t\tTargetMode: targetMode,\n\t})\n\tf.Relationships.Store(relPath, rels)\n\treturn rID\n}\n\n// UpdateLinkedValue fix linked values within a spreadsheet are not updating in\n// Office Excel application. This function will be remove value tag when met a\n// cell have a linked value. Reference\n// https://learn.microsoft.com/en-us/archive/msdn-technet-forums/e16bae1f-6a2c-4325-8013-e989a3479066\n//\n// Notice: after opening generated workbook, Excel will update the linked value\n// and generate a new value and will prompt to save the file or not.\n//\n// For example:\n//\n//\t<row r=\"19\">\n//\t    <c r=\"B19\">\n//\t        <f>SUM(Sheet2!D2,Sheet2!D11)</f>\n//\t        <v>100</v>\n//\t     </c>\n//\t</row>\n//\n// to\n//\n//\t<row r=\"19\">\n//\t    <c r=\"B19\">\n//\t        <f>SUM(Sheet2!D2,Sheet2!D11)</f>\n//\t    </c>\n//\t</row>\nfunc (f *File) UpdateLinkedValue() error {\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\t// recalculate formulas\n\twb.CalcPr = nil\n\tfor _, name := range f.GetSheetList() {\n\t\tws, err := f.workSheetReader(name)\n\t\tif err != nil {\n\t\t\tif err.Error() == newNotWorksheetError(name).Error() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tfor indexR := range ws.SheetData.Row {\n\t\t\tfor indexC, col := range ws.SheetData.Row[indexR].C {\n\t\t\t\tif col.F != nil && col.V != \"\" {\n\t\t\t\t\tws.SheetData.Row[indexR].C[indexC].V = \"\"\n\t\t\t\t\tws.SheetData.Row[indexR].C[indexC].T = \"\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// AddVBAProject provides the method to add vbaProject.bin file which contains\n// functions and/or macros. The file extension should be XLSM or XLTM. For\n// example:\n//\n//\tcodeName := \"Sheet1\"\n//\tif err := f.SetSheetProps(\"Sheet1\", &excelize.SheetPropsOptions{\n//\t    CodeName: &codeName,\n//\t}); err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\tfile, err := os.ReadFile(\"vbaProject.bin\")\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\tif err := f.AddVBAProject(file); err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\tif err := f.SaveAs(\"macros.xlsm\"); err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\nfunc (f *File) AddVBAProject(file []byte) error {\n\tvar err error\n\t// Check vbaProject.bin exists first.\n\tif !bytes.Contains(file, oleIdentifier) {\n\t\treturn ErrAddVBAProject\n\t}\n\trels, err := f.relsReader(f.getWorkbookRelsPath())\n\tif err != nil {\n\t\treturn err\n\t}\n\trels.mu.Lock()\n\tdefer rels.mu.Unlock()\n\tvar rID int\n\tvar ok bool\n\tfor _, rel := range rels.Relationships {\n\t\tif rel.Target == \"vbaProject.bin\" && rel.Type == SourceRelationshipVBAProject {\n\t\t\tok = true\n\t\t\tcontinue\n\t\t}\n\t\tt, _ := strconv.Atoi(strings.TrimPrefix(rel.ID, \"rId\"))\n\t\tif t > rID {\n\t\t\trID = t\n\t\t}\n\t}\n\trID++\n\tif !ok {\n\t\trels.Relationships = append(rels.Relationships, xlsxRelationship{\n\t\t\tID:     \"rId\" + strconv.Itoa(rID),\n\t\t\tTarget: \"vbaProject.bin\",\n\t\t\tType:   SourceRelationshipVBAProject,\n\t\t})\n\t}\n\tf.Pkg.Store(\"xl/vbaProject.bin\", file)\n\treturn err\n}\n\n// setContentTypePartProjectExtensions provides a function to set the content\n// type for relationship parts and the main document part.\nfunc (f *File) setContentTypePartProjectExtensions(contentType string) error {\n\tvar ok bool\n\tcontent, err := f.contentTypesReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tcontent.mu.Lock()\n\tdefer content.mu.Unlock()\n\tfor _, v := range content.Defaults {\n\t\tif v.Extension == \"bin\" {\n\t\t\tok = true\n\t\t}\n\t}\n\tfor idx, o := range content.Overrides {\n\t\tif o.PartName == \"/xl/workbook.xml\" {\n\t\t\tcontent.Overrides[idx].ContentType = contentType\n\t\t}\n\t}\n\tif !ok {\n\t\tcontent.Defaults = append(content.Defaults, xlsxDefault{\n\t\t\tExtension:   \"bin\",\n\t\t\tContentType: ContentTypeVBA,\n\t\t})\n\t}\n\treturn err\n}\n\n// metadataReader provides a function to get the pointer to the structure\n// after deserialization of xl/metadata.xml.\nfunc (f *File) metadataReader() (*xlsxMetadata, error) {\n\tvar metaData xlsxMetadata\n\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLMetadata)))).\n\t\tDecode(&metaData); err != nil && err != io.EOF {\n\t\treturn &metaData, err\n\t}\n\treturn &metaData, nil\n}\n\n// richValueReader provides a function to get the pointer to the structure after\n// deserialization of xl/richData/richvalue.xml.\nfunc (f *File) richValueReader() (*xlsxRichValueData, error) {\n\tvar richValue xlsxRichValueData\n\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLRdRichValue)))).\n\t\tDecode(&richValue); err != nil && err != io.EOF {\n\t\treturn &richValue, err\n\t}\n\treturn &richValue, nil\n}\n\n// richValueRelReader provides a function to get the pointer to the structure\n// after deserialization of xl/richData/richValueRel.xml.\nfunc (f *File) richValueRelReader() (*xlsxRichValueRels, error) {\n\tvar richValueRels xlsxRichValueRels\n\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLRdRichValueRel)))).\n\t\tDecode(&richValueRels); err != nil && err != io.EOF {\n\t\treturn &richValueRels, err\n\t}\n\treturn &richValueRels, nil\n}\n\n// richValueStructuresReader provides a function to get the pointer to the structure after\n// deserialization of xl/richData/rdrichvaluestructure.xml.\nfunc (f *File) richValueStructuresReader() (*xlsxRichValueStructures, error) {\n\tvar richValueStructures xlsxRichValueStructures\n\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLRdRichValueStructure)))).\n\t\tDecode(&richValueStructures); err != nil && err != io.EOF {\n\t\treturn &richValueStructures, err\n\t}\n\treturn &richValueStructures, nil\n}\n\n// richValueWebImageReader provides a function to get the pointer to the\n// structure after deserialization of xl/richData/rdRichValueWebImage.xml.\nfunc (f *File) richValueWebImageReader() (*xlsxWebImagesSupportingRichData, error) {\n\tvar richValueWebImages xlsxWebImagesSupportingRichData\n\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLRdRichValueWebImage)))).\n\t\tDecode(&richValueWebImages); err != nil && err != io.EOF {\n\t\treturn &richValueWebImages, err\n\t}\n\treturn &richValueWebImages, nil\n}\n\n// getRichDataRichValueRelRelationships provides a function to get relationships\n// from xl/richData/_rels/richValueRel.xml.rels by given relationship ID.\nfunc (f *File) getRichDataRichValueRelRelationships(rID string) *xlsxRelationship {\n\tif rels, _ := f.relsReader(defaultXMLRdRichValueRelRels); rels != nil {\n\t\trels.mu.Lock()\n\t\tdefer rels.mu.Unlock()\n\t\tfor _, v := range rels.Relationships {\n\t\t\tif v.ID == rID {\n\t\t\t\treturn &v\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// getRichValueWebImageRelationships provides a function to get relationships\n// from xl/richData/_rels/rdRichValueWebImage.xml.rels by given relationship ID.\nfunc (f *File) getRichValueWebImageRelationships(rID string) *xlsxRelationship {\n\tif rels, _ := f.relsReader(defaultXMLRdRichValueWebImageRels); rels != nil {\n\t\trels.mu.Lock()\n\t\tdefer rels.mu.Unlock()\n\t\tfor _, v := range rels.Relationships {\n\t\t\tif v.ID == rID {\n\t\t\t\treturn &v\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "excelize_test.go",
    "content": "package excelize\n\nimport (\n\t\"archive/zip\"\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"image/color\"\n\t_ \"image/gif\"\n\t_ \"image/jpeg\"\n\t_ \"image/png\"\n\t\"io\"\n\t\"math\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"golang.org/x/net/html/charset\"\n)\n\nfunc TestOpenFile(t *testing.T) {\n\t// Test update the spreadsheet file\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\n\t// Test get all the rows in a not exists worksheet\n\t_, err = f.GetRows(\"Sheet4\")\n\tassert.EqualError(t, err, \"sheet Sheet4 does not exist\")\n\t// Test get all the rows with invalid sheet name\n\t_, err = f.GetRows(\"Sheet:1\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\t// Test get all the rows in a worksheet\n\trows, err := f.GetRows(\"Sheet2\")\n\texpected := [][]string{\n\t\t{\"Monitor\", \"\", \"Brand\", \"\", \"inlineStr\"},\n\t\t{\"> 23 Inch\", \"19\", \"HP\", \"200\"},\n\t\t{\"20-23 Inch\", \"24\", \"DELL\", \"450\"},\n\t\t{\"17-20 Inch\", \"56\", \"Lenove\", \"200\"},\n\t\t{\"< 17 Inch\", \"21\", \"SONY\", \"510\"},\n\t\t{\"\", \"\", \"Acer\", \"315\"},\n\t\t{\"\", \"\", \"IBM\", \"127\"},\n\t\t{\"\", \"\", \"ASUS\", \"89\"},\n\t\t{\"\", \"\", \"Apple\", \"348\"},\n\t\t{\"\", \"\", \"SAMSUNG\", \"53\"},\n\t\t{\"\", \"\", \"Other\", \"37\", \"\", \"\", \"\", \"\", \"\"},\n\t}\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected, rows)\n\n\tassert.NoError(t, f.UpdateLinkedValue())\n\n\tassert.NoError(t, f.SetCellDefault(\"Sheet2\", \"A1\", strconv.FormatFloat(100.1588, 'f', -1, 32)))\n\tassert.NoError(t, f.SetCellDefault(\"Sheet2\", \"A1\", strconv.FormatFloat(-100.1588, 'f', -1, 64)))\n\t// Test set cell value with invalid sheet name\n\tassert.EqualError(t, f.SetCellDefault(\"Sheet:1\", \"A1\", \"\"), ErrSheetNameInvalid.Error())\n\t// Test set cell value with illegal row number\n\tassert.EqualError(t, f.SetCellDefault(\"Sheet2\", \"A\", strconv.FormatFloat(-100.1588, 'f', -1, 64)),\n\t\tnewCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\n\tassert.NoError(t, f.SetCellInt(\"Sheet2\", \"A1\", 100))\n\n\t// Test set cell integer value with illegal row number\n\tassert.EqualError(t, f.SetCellInt(\"Sheet2\", \"A\", 100), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\t// Test set cell integer value with invalid sheet name\n\tassert.EqualError(t, f.SetCellInt(\"Sheet:1\", \"A1\", 100), ErrSheetNameInvalid.Error())\n\n\tassert.NoError(t, f.SetCellStr(\"Sheet2\", \"C11\", \"Knowns\"))\n\t// Test max characters in a cell\n\tassert.NoError(t, f.SetCellStr(\"Sheet2\", \"D11\", strings.Repeat(\"c\", TotalCellChars+2)))\n\t_, err = f.NewSheet(\":\\\\/?*[]Maximum 31 characters allowed in sheet title.\")\n\tassert.EqualError(t, err, ErrSheetNameLength.Error())\n\t// Test set worksheet name with illegal name\n\tassert.EqualError(t, f.SetSheetName(\"Maximum 31 characters allowed i\", \"[Rename]:\\\\/?* Maximum 31 characters allowed in sheet title.\"), ErrSheetNameLength.Error())\n\tassert.EqualError(t, f.SetCellInt(\"Sheet3\", \"A23\", 10), \"sheet Sheet3 does not exist\")\n\tassert.EqualError(t, f.SetCellStr(\"Sheet3\", \"b230\", \"10\"), \"sheet Sheet3 does not exist\")\n\tassert.EqualError(t, f.SetCellStr(\"Sheet10\", \"b230\", \"10\"), \"sheet Sheet10 does not exist\")\n\t// Test set cell string data type value with invalid sheet name\n\tassert.EqualError(t, f.SetCellStr(\"Sheet:1\", \"A1\", \"1\"), ErrSheetNameInvalid.Error())\n\t// Test set cell string value with illegal row number\n\tassert.EqualError(t, f.SetCellStr(\"Sheet1\", \"A\", \"10\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\n\tf.SetActiveSheet(2)\n\t// Test get cell formula with given rows number\n\tformula, err := f.GetCellFormula(\"Sheet1\", \"B19\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"SUM(Sheet2!D2,Sheet2!D11)\", formula)\n\t// Test get cell formula with illegal worksheet name\n\tformula, err = f.GetCellFormula(\"Sheet2\", \"B20\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, formula)\n\n\t// Test get cell formula with illegal rows number\n\t_, err = f.GetCellFormula(\"Sheet1\", \"B\")\n\tassert.EqualError(t, err, newCellNameToCoordinatesError(\"B\", newInvalidCellNameError(\"B\")).Error())\n\t// Test get shared cell formula\n\t_, err = f.GetCellFormula(\"Sheet2\", \"H11\")\n\tassert.NoError(t, err)\n\t_, err = f.GetCellFormula(\"Sheet2\", \"I11\")\n\tassert.NoError(t, err)\n\t_, err = getSharedFormula(&xlsxWorksheet{}, 0, \"\")\n\tassert.NoError(t, err)\n\n\t// Test read cell value with given illegal rows number\n\t_, err = f.GetCellValue(\"Sheet2\", \"a-1\")\n\tassert.EqualError(t, err, newCellNameToCoordinatesError(\"A-1\", newInvalidCellNameError(\"A-1\")).Error())\n\t_, err = f.GetCellValue(\"Sheet2\", \"A\")\n\tassert.EqualError(t, err, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\n\t// Test read cell value with given lowercase column number\n\t_, err = f.GetCellValue(\"Sheet2\", \"a5\")\n\tassert.NoError(t, err)\n\t_, err = f.GetCellValue(\"Sheet2\", \"C11\")\n\tassert.NoError(t, err)\n\t_, err = f.GetCellValue(\"Sheet2\", \"D11\")\n\tassert.NoError(t, err)\n\t_, err = f.GetCellValue(\"Sheet2\", \"D12\")\n\tassert.NoError(t, err)\n\t// Test SetCellValue function\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F1\", \" Hello\"))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"G1\", []byte(\"World\")))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F2\", 42))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F3\", int8(1<<8/2-1)))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F4\", int16(1<<16/2-1)))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F5\", int32(1<<32/2-1)))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F6\", int64(1<<32/2-1)))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F7\", float32(42.65418)))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F8\", -42.65418))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F9\", float32(42)))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F10\", float64(42)))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F11\", uint(1<<32-1)))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F12\", uint8(1<<8-1)))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F13\", uint16(1<<16-1)))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F14\", uint32(1<<32-1)))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F15\", uint64(1<<32-1)))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F16\", true))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F17\", complex64(5+10i)))\n\n\t// Test on not exists worksheet\n\tassert.EqualError(t, f.SetCellDefault(\"SheetN\", \"A1\", \"\"), \"sheet SheetN does not exist\")\n\tassert.EqualError(t, f.SetCellFloat(\"SheetN\", \"A1\", 42.65418, 2, 32), \"sheet SheetN does not exist\")\n\tassert.EqualError(t, f.SetCellBool(\"SheetN\", \"A1\", true), \"sheet SheetN does not exist\")\n\tassert.EqualError(t, f.SetCellFormula(\"SheetN\", \"A1\", \"\"), \"sheet SheetN does not exist\")\n\tassert.EqualError(t, f.SetCellHyperLink(\"SheetN\", \"A1\", \"Sheet1!A40\", \"Location\"), \"sheet SheetN does not exist\")\n\n\t// Test boolean write\n\tboolTest := []struct {\n\t\tvalue    bool\n\t\traw      bool\n\t\texpected string\n\t}{\n\t\t{false, true, \"0\"},\n\t\t{true, true, \"1\"},\n\t\t{false, false, \"FALSE\"},\n\t\t{true, false, \"TRUE\"},\n\t}\n\tfor _, test := range boolTest {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"F16\", test.value))\n\t\tval, err := f.GetCellValue(\"Sheet2\", \"F16\", Options{RawCellValue: test.raw})\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, test.expected, val)\n\t}\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"G2\", nil))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"G4\", time.Now()))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"G4\", time.Now().UTC()))\n\tassert.EqualError(t, f.SetCellValue(\"SheetN\", \"A1\", time.Now()), \"sheet SheetN does not exist\")\n\t// 02:46:40\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"G5\", time.Duration(1e13)))\n\t// Test completion column\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"M2\", nil))\n\t// Test read cell value with given cell reference large than exists row\n\t_, err = f.GetCellValue(\"Sheet2\", \"E231\")\n\tassert.NoError(t, err)\n\t// Test get active worksheet of spreadsheet and get worksheet name of\n\t// spreadsheet by given worksheet index\n\tf.GetSheetName(f.GetActiveSheetIndex())\n\t// Test get worksheet index of spreadsheet by given worksheet name\n\t_, err = f.GetSheetIndex(\"Sheet1\")\n\tassert.NoError(t, err)\n\t// Test get worksheet name of spreadsheet by given invalid worksheet index\n\tf.GetSheetName(4)\n\t// Test get worksheet map of workbook\n\tf.GetSheetMap()\n\tfor i := 1; i <= 300; i++ {\n\t\tassert.NoError(t, f.SetCellStr(\"Sheet2\", \"c\"+strconv.Itoa(i), strconv.Itoa(i)))\n\t}\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestOpenFile.xlsx\")))\n\tassert.EqualError(t, f.SaveAs(filepath.Join(\"test\", strings.Repeat(\"c\", 199), \".xlsx\")), ErrMaxFilePathLength.Error())\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestSaveFile(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\tassert.EqualError(t, f.SaveAs(filepath.Join(\"test\", \"TestSaveFile.xlsb\")), ErrWorkbookFileFormat.Error())\n\tfor _, ext := range []string{\".xlam\", \".xlsm\", \".xlsx\", \".xltm\", \".xltx\"} {\n\t\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", fmt.Sprintf(\"TestSaveFile%s\", ext))))\n\t}\n\tassert.NoError(t, f.Close())\n\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestSaveFile.xlsx\"))\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Save())\n\tassert.NoError(t, f.Close())\n\n\tt.Run(\"for_save_multiple_times\", func(t *testing.T) {\n\t\t{\n\t\t\tf, err := OpenFile(filepath.Join(\"test\", \"TestSaveFile.xlsx\"))\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A20\", 20))\n\t\t\tassert.NoError(t, f.Save())\n\n\t\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A21\", 21))\n\t\t\tassert.NoError(t, f.Save())\n\t\t\tassert.NoError(t, f.Close())\n\t\t}\n\t\t{\n\t\t\tf, err := OpenFile(filepath.Join(\"test\", \"TestSaveFile.xlsx\"))\n\t\t\tassert.NoError(t, err)\n\t\t\tval, err := f.GetCellValue(\"Sheet1\", \"A20\")\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, \"20\", val)\n\t\t\tval, err = f.GetCellValue(\"Sheet1\", \"A21\")\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, \"21\", val)\n\t\t\tassert.NoError(t, f.Close())\n\t\t}\n\t})\n}\n\nfunc TestSaveAsWrongPath(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\t// Test write file to not exist directory\n\tassert.Error(t, f.SaveAs(filepath.Join(\"x\", \"Book1.xlsx\")))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestCharsetTranscoder(t *testing.T) {\n\tf := NewFile()\n\tf.CharsetTranscoder(charset.NewReaderLabel)\n}\n\nfunc TestOpenReader(t *testing.T) {\n\t_, err := OpenReader(strings.NewReader(\"\"))\n\tassert.EqualError(t, err, zip.ErrFormat.Error())\n\t_, err = OpenReader(bytes.NewReader(oleIdentifier), Options{Password: \"password\", UnzipXMLSizeLimit: UnzipSizeLimit + 1})\n\tassert.EqualError(t, err, ErrWorkbookFileFormat.Error())\n\n\t// Prepare unusual workbook, made the specified internal XML parts missing\n\t// or contain unsupported charset\n\tpreset := func(filePath string, notExist bool) *bytes.Buffer {\n\t\tsource, err := zip.OpenReader(filepath.Join(\"test\", \"Book1.xlsx\"))\n\t\tassert.NoError(t, err)\n\t\tbuf := new(bytes.Buffer)\n\t\tzw := zip.NewWriter(buf)\n\t\tfor _, item := range source.File {\n\t\t\t// The following statements can be simplified as zw.Copy(item) in go1.17\n\t\t\tif notExist && item.Name == filePath {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\twriter, err := zw.Create(item.Name)\n\t\t\tassert.NoError(t, err)\n\t\t\treaderCloser, err := item.Open()\n\t\t\tassert.NoError(t, err)\n\t\t\t_, err = io.Copy(writer, readerCloser)\n\t\t\tassert.NoError(t, err)\n\t\t}\n\t\tif !notExist {\n\t\t\tfi, err := zw.Create(filePath)\n\t\t\tassert.NoError(t, err)\n\t\t\t_, err = fi.Write(MacintoshCyrillicCharset)\n\t\t\tassert.NoError(t, err)\n\t\t}\n\t\tassert.NoError(t, zw.Close())\n\t\treturn buf\n\t}\n\t// Test open workbook with unsupported charset internal XML parts\n\tfor _, defaultXMLPath := range []string{\n\t\tdefaultXMLPathCalcChain,\n\t\tdefaultXMLPathStyles,\n\t\tdefaultXMLPathWorkbookRels,\n\t} {\n\t\t_, err = OpenReader(preset(defaultXMLPath, false))\n\t\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t}\n\t// Test open workbook without internal XML parts\n\tfor _, defaultXMLPath := range []string{\n\t\tdefaultXMLPathCalcChain,\n\t\tdefaultXMLPathStyles,\n\t\tdefaultXMLPathWorkbookRels,\n\t} {\n\t\t_, err = OpenReader(preset(defaultXMLPath, true))\n\t\tassert.NoError(t, err)\n\t}\n\n\t// Test open spreadsheet with unzip size limit\n\t_, err = OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"), Options{UnzipSizeLimit: 100})\n\tassert.EqualError(t, err, newUnzipSizeLimitError(100).Error())\n\n\t// Test open password protected spreadsheet created by Microsoft Office Excel 2010\n\tf, err := OpenFile(filepath.Join(\"test\", \"encryptSHA1.xlsx\"), Options{Password: \"password\"})\n\tassert.NoError(t, err)\n\tval, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"SECRET\", val)\n\tassert.NoError(t, f.Close())\n\n\t// Test open password protected spreadsheet created by LibreOffice 7.0.0.3\n\tf, err = OpenFile(filepath.Join(\"test\", \"encryptAES.xlsx\"), Options{Password: \"password\"})\n\tassert.NoError(t, err)\n\tval, err = f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"SECRET\", val)\n\tassert.NoError(t, f.Close())\n\n\t// Test open spreadsheet with invalid options\n\t_, err = OpenReader(bytes.NewReader(oleIdentifier), Options{UnzipSizeLimit: 1, UnzipXMLSizeLimit: 2})\n\tassert.EqualError(t, err, ErrOptionsUnzipSizeLimit.Error())\n\n\t// Test unexpected EOF\n\tvar b bytes.Buffer\n\tw := gzip.NewWriter(&b)\n\tdefer func() {\n\t\tassert.NoError(t, w.Close())\n\t}()\n\tassert.NoError(t, w.Flush())\n\n\tr, _ := gzip.NewReader(&b)\n\tdefer func() {\n\t\tassert.EqualError(t, r.Close(), \"unexpected EOF\")\n\t}()\n\n\t_, err = OpenReader(r)\n\tassert.EqualError(t, err, \"unexpected EOF\")\n\n\t_, err = OpenReader(bytes.NewReader([]byte{\n\t\t0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x09, 0x00, 0x63, 0x00, 0x47, 0xa3, 0xb6, 0x50, 0x00, 0x00,\n\t\t0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x70, 0x61,\n\t\t0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x01, 0x99, 0x07, 0x00, 0x02, 0x00, 0x41, 0x45, 0x03, 0x00,\n\t\t0x00, 0x21, 0x06, 0x59, 0xc0, 0x12, 0xf3, 0x19, 0xc7, 0x51, 0xd1, 0xc9, 0x31, 0xcb, 0xcc, 0x8a,\n\t\t0xe1, 0x44, 0xe1, 0x56, 0x20, 0x24, 0x1f, 0xba, 0x09, 0xda, 0x53, 0xd5, 0xef, 0x50, 0x4b, 0x07,\n\t\t0x08, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x01,\n\t\t0x02, 0x1f, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x63, 0x00, 0x47, 0xa3, 0xb6, 0x50, 0x00, 0x00, 0x00,\n\t\t0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00,\n\t\t0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x61, 0x73, 0x73, 0x77,\n\t\t0x6f, 0x72, 0x64, 0x01, 0x99, 0x07, 0x00, 0x02, 0x00, 0x41, 0x45, 0x03, 0x00, 0x00, 0x50, 0x4b,\n\t\t0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x41, 0x00, 0x00, 0x00, 0x5d, 0x00,\n\t\t0x00, 0x00, 0x00, 0x00,\n\t}))\n\tassert.EqualError(t, err, zip.ErrAlgorithm.Error())\n}\n\nfunc TestBrokenFile(t *testing.T) {\n\t// Test write file with broken file struct\n\tf := File{ZipWriter: func(w io.Writer) ZipWriter { return zip.NewWriter(w) }}\n\n\tt.Run(\"SaveWithoutName\", func(t *testing.T) {\n\t\tassert.EqualError(t, f.Save(), \"no path defined for file, consider File.WriteTo or File.Write\")\n\t})\n\n\tt.Run(\"SaveAsEmptyStruct\", func(t *testing.T) {\n\t\t// Test write file with broken file struct with given path\n\t\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"BadWorkbook.SaveAsEmptyStruct.xlsx\")))\n\t})\n\n\tt.Run(\"OpenBadWorkbook\", func(t *testing.T) {\n\t\t// Test set active sheet without BookViews and Sheets maps in xl/workbook.xml\n\t\tf3, err := OpenFile(filepath.Join(\"test\", \"BadWorkbook.xlsx\"))\n\t\tf3.GetActiveSheetIndex()\n\t\tf3.SetActiveSheet(1)\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f3.Close())\n\t})\n\n\tt.Run(\"OpenNotExistsFile\", func(t *testing.T) {\n\t\t// Test open a spreadsheet file with given illegal path\n\t\t_, err := OpenFile(filepath.Join(\"test\", \"NotExistsFile.xlsx\"))\n\t\tif assert.Error(t, err) {\n\t\t\tassert.True(t, os.IsNotExist(err), \"Expected os.IsNotExists(err) == true\")\n\t\t}\n\t})\n}\n\nfunc TestNewFile(t *testing.T) {\n\t// Test create a spreadsheet file\n\tf := NewFile()\n\t_, err := f.NewSheet(\"Sheet1\")\n\tassert.NoError(t, err)\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\t_, err = f.NewSheet(\"Sheet3\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellInt(\"Sheet2\", \"A23\", 56))\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", \"B20\", \"42\"))\n\tf.SetActiveSheet(0)\n\n\t// Test add picture to sheet with scaling and positioning\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"H2\", filepath.Join(\"test\", \"images\", \"excel.gif\"),\n\t\t&GraphicOptions{ScaleX: 0.5, ScaleY: 0.5, Positioning: \"absolute\"}))\n\n\t// Test add picture to worksheet without options\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"C2\", filepath.Join(\"test\", \"images\", \"excel.png\"), nil))\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestNewFile.xlsx\")))\n\tassert.NoError(t, f.Save())\n}\n\nfunc TestSetCellHyperLink(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\t// Test set cell hyperlink in a work sheet already have hyperlinks\n\tassert.NoError(t, f.SetCellHyperLink(\"Sheet1\", \"B19\", \"https://github.com/xuri/excelize\", \"External\"))\n\t// Test add first hyperlink in a work sheet\n\tassert.NoError(t, f.SetCellHyperLink(\"Sheet2\", \"C1\", \"https://github.com/xuri/excelize\", \"External\"))\n\t// Test add Location hyperlink in a work sheet\n\tassert.NoError(t, f.SetCellHyperLink(\"Sheet2\", \"D6\", \"Sheet1!D8\", \"Location\"))\n\t// Test add Location hyperlink with display & tooltip in a work sheet\n\tdisplay, tooltip := \"Display value\", \"Hover text\"\n\tassert.NoError(t, f.SetCellHyperLink(\"Sheet2\", \"D7\", \"Sheet1!D9\", \"Location\", HyperlinkOpts{\n\t\tDisplay: &display,\n\t\tTooltip: &tooltip,\n\t}))\n\tcells, err := f.GetHyperLinkCells(\"Sheet1\", \"\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"A22\", \"B19\"}, cells)\n\tcells, err = f.GetHyperLinkCells(\"Sheet2\", \"\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"C1\", \"D6\", \"D7\"}, cells)\n\tcells, err = f.GetHyperLinkCells(\"Sheet2\", \"External\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"C1\"}, cells)\n\tcells, err = f.GetHyperLinkCells(\"Sheet2\", \"Location\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"D6\", \"D7\"}, cells)\n\tcells, err = f.GetHyperLinkCells(\"Sheet2\", \"None\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, cells)\n\t// Test get hyperlink cells on not exists worksheet\n\t_, err = f.GetHyperLinkCells(\"SheetN\", \"\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get hyperlink cells with invalid link type\n\t_, err = f.GetHyperLinkCells(\"Sheet2\", \"InvalidType\")\n\tassert.Equal(t, err, newInvalidLinkTypeError(\"InvalidType\"))\n\n\t// Test set cell hyperlink with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.SetCellHyperLink(\"Sheet:1\", \"A1\", \"Sheet1!D60\", \"Location\"))\n\tassert.Equal(t, newInvalidLinkTypeError(\"\"), f.SetCellHyperLink(\"Sheet2\", \"C3\", \"Sheet1!D8\", \"\"))\n\tassert.EqualError(t, f.SetCellHyperLink(\"Sheet2\", \"\", \"Sheet1!D60\", \"Location\"), `invalid cell name \"\"`)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellHyperLink.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\t_, err = f.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).Hyperlinks = &xlsxHyperlinks{Hyperlink: make([]xlsxHyperlink, 65530)}\n\tassert.EqualError(t, f.SetCellHyperLink(\"Sheet1\", \"A65531\", \"https://github.com/xuri/excelize\", \"External\"), ErrTotalSheetHyperlinks.Error())\n\n\tf = NewFile()\n\t_, err = f.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: \"A:A\"}}}\n\terr = f.SetCellHyperLink(\"Sheet1\", \"A1\", \"https://github.com/xuri/excelize\", \"External\")\n\tassert.EqualError(t, err, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\n\t// Test update cell hyperlink\n\tf = NewFile()\n\tassert.NoError(t, f.SetCellHyperLink(\"Sheet1\", \"A1\", \"https://github.com\", \"External\"))\n\tassert.NoError(t, f.SetCellHyperLink(\"Sheet1\", \"A1\", \"https://github.com/xuri/excelize\", \"External\"))\n\tlink, target, err := f.GetCellHyperLink(\"Sheet1\", \"A1\")\n\tassert.Equal(t, link, true)\n\tassert.Equal(t, \"https://github.com/xuri/excelize\", target)\n\tassert.NoError(t, err)\n\n\t// Test remove hyperlink for a cell\n\tf = NewFile()\n\tassert.NoError(t, f.SetCellHyperLink(\"Sheet1\", \"A1\", \"Sheet1!D8\", \"Location\"))\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).Hyperlinks.Hyperlink[0].Ref = \"A1:D4\"\n\tassert.NoError(t, f.SetCellHyperLink(\"Sheet1\", \"B2\", \"\", \"None\"))\n\t// Test remove hyperlink for a cell with invalid cell reference\n\tassert.NoError(t, f.SetCellHyperLink(\"Sheet1\", \"A1\", \"Sheet1!D8\", \"Location\"))\n\tws.(*xlsxWorksheet).Hyperlinks.Hyperlink[0].Ref = \"A:A\"\n\tassert.Error(t, f.SetCellHyperLink(\"Sheet1\", \"B2\", \"\", \"None\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")))\n\n\t// Test get hyperlink cells on worksheet without hyperlinks\n\tf = NewFile()\n\tcells, err = f.GetHyperLinkCells(\"Sheet1\", \"\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, cells)\n}\n\nfunc TestGetCellHyperLink(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\n\t_, _, err = f.GetCellHyperLink(\"Sheet1\", \"\")\n\tassert.EqualError(t, err, `invalid cell name \"\"`)\n\n\tlink, target, err := f.GetCellHyperLink(\"Sheet1\", \"A22\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, link, true)\n\tassert.Equal(t, target, \"https://github.com/xuri/excelize\")\n\n\tlink, target, err = f.GetCellHyperLink(\"Sheet2\", \"D6\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, link, false)\n\tassert.Equal(t, target, \"\")\n\n\tlink, target, err = f.GetCellHyperLink(\"Sheet3\", \"H3\")\n\tassert.EqualError(t, err, \"sheet Sheet3 does not exist\")\n\tassert.Equal(t, link, false)\n\tassert.Equal(t, target, \"\")\n\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\t_, err = f.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).Hyperlinks = &xlsxHyperlinks{\n\t\tHyperlink: []xlsxHyperlink{{Ref: \"A1\"}},\n\t}\n\tlink, target, err = f.GetCellHyperLink(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, link, true)\n\tassert.Equal(t, target, \"\")\n\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).Hyperlinks = &xlsxHyperlinks{Hyperlink: []xlsxHyperlink{{Ref: \"A:A\"}}}\n\tlink, target, err = f.GetCellHyperLink(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\tassert.Equal(t, link, false)\n\tassert.Equal(t, target, \"\")\n\n\t// Test get cell hyperlink with invalid sheet name\n\t_, _, err = f.GetCellHyperLink(\"Sheet:1\", \"A1\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n}\n\nfunc TestSetSheetBackground(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetSheetBackground(\"Sheet2\", filepath.Join(\"test\", \"images\", \"background.jpg\")))\n\tassert.NoError(t, f.SetSheetBackground(\"Sheet2\", filepath.Join(\"test\", \"images\", \"background.jpg\")))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetSheetBackground.xlsx\")))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestSetSheetBackgroundErrors(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\n\terr = f.SetSheetBackground(\"Sheet2\", filepath.Join(\"test\", \"not_exists\", \"not_exists.png\"))\n\tif assert.Error(t, err) {\n\t\tassert.True(t, os.IsNotExist(err), \"Expected os.IsNotExists(err) == true\")\n\t}\n\n\terr = f.SetSheetBackground(\"Sheet2\", filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.EqualError(t, err, ErrImgExt.Error())\n\t// Test set sheet background on not exist worksheet\n\terr = f.SetSheetBackground(\"SheetN\", filepath.Join(\"test\", \"images\", \"background.jpg\"))\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test set sheet background with invalid sheet name\n\tassert.EqualError(t, f.SetSheetBackground(\"Sheet:1\", filepath.Join(\"test\", \"images\", \"background.jpg\")), ErrSheetNameInvalid.Error())\n\tassert.NoError(t, f.Close())\n\n\t// Test set sheet background with unsupported charset content types\n\tf = NewFile()\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetSheetBackground(\"Sheet1\", filepath.Join(\"test\", \"images\", \"background.jpg\")), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\n// TestWriteArrayFormula tests the extended options of SetCellFormula by writing\n// an array function to a workbook. In the resulting file, the lines 2 and 3 as\n// well as 4 and 5 should have matching contents\nfunc TestWriteArrayFormula(t *testing.T) {\n\tcell := func(col, row int) string {\n\t\tc, err := CoordinatesToCellName(col, row)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\treturn c\n\t}\n\n\tf := NewFile()\n\n\tsample := []string{\"Sample 1\", \"Sample 2\", \"Sample 3\"}\n\tvalues := []int{1855, 1709, 1462, 1115, 1524, 625, 773, 126, 1027, 1696, 1078, 1917, 1109, 1753, 1884, 659, 994, 1911, 1925, 899, 196, 244, 1488, 1056, 1986, 66, 784, 725, 767, 1722, 1541, 1026, 1455, 264, 1538, 877, 1581, 1098, 383, 762, 237, 493, 29, 1923, 474, 430, 585, 688, 308, 200, 1259, 622, 798, 1048, 996, 601, 582, 332, 377, 805, 250, 1860, 1360, 840, 911, 1346, 1651, 1651, 665, 584, 1057, 1145, 925, 1752, 202, 149, 1917, 1398, 1894, 818, 714, 624, 1085, 1566, 635, 78, 313, 1686, 1820, 494, 614, 1913, 271, 1016, 338, 1301, 489, 1733, 1483, 1141}\n\tassoc := []int{2, 0, 0, 0, 0, 1, 1, 0, 0, 1, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 2, 0, 2, 1, 2, 2, 2, 1, 0, 1, 0, 1, 1, 2, 0, 2, 1, 0, 2, 1, 0, 1, 0, 0, 2, 0, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 2, 1, 1, 1, 0, 1, 0, 2, 0, 0, 1, 2, 1, 0, 1, 0, 0, 2, 1, 1, 2, 0, 2, 1, 0, 2, 2, 2, 1, 0, 0, 1, 1, 1, 2, 0, 2, 0, 1, 1}\n\tif len(values) != len(assoc) {\n\t\tt.Fatal(\"values and assoc must be of same length\")\n\t}\n\n\t// Average calculates the average of the n-th sample (0 <= n < len(sample)).\n\taverage := func(n int) int {\n\t\tsum := 0\n\t\tcount := 0\n\t\tfor i := 0; i != len(values); i++ {\n\t\t\tif assoc[i] == n {\n\t\t\t\tsum += values[i]\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\n\t\treturn int(math.Round(float64(sum) / float64(count)))\n\t}\n\n\t// Stdev calculates the standard deviation of the n-th sample (0 <= n < len(sample)).\n\tstdev := func(n int) int {\n\t\tavg := average(n)\n\n\t\tsum := 0\n\t\tcount := 0\n\t\tfor i := 0; i != len(values); i++ {\n\t\t\tif assoc[i] == n {\n\t\t\t\tsum += (values[i] - avg) * (values[i] - avg)\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\n\t\treturn int(math.Round(math.Sqrt(float64(sum) / float64(count))))\n\t}\n\n\t// Line 2 contains the results of AVERAGEIF\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", \"A2\", \"Average\"))\n\n\t// Line 3 contains the average that was calculated in Go\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", \"A3\", \"Average (calculated)\"))\n\n\t// Line 4 contains the results of the array function that calculates the standard deviation\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", \"A4\", \"Std. deviation\"))\n\n\t// Line 5 contains the standard deviations calculated in Go\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", \"A5\", \"Std. deviation (calculated)\"))\n\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", \"B1\", sample[0]))\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", \"C1\", sample[1]))\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", \"D1\", sample[2]))\n\n\tfirstResLine := 8\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", cell(1, firstResLine-1), \"Result Values\"))\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", cell(2, firstResLine-1), \"Sample\"))\n\n\tfor i := 0; i != len(values); i++ {\n\t\tvalCell := cell(1, i+firstResLine)\n\t\tassocCell := cell(2, i+firstResLine)\n\n\t\tassert.NoError(t, f.SetCellInt(\"Sheet1\", valCell, int64(values[i])))\n\t\tassert.NoError(t, f.SetCellStr(\"Sheet1\", assocCell, sample[assoc[i]]))\n\t}\n\n\tvalRange := fmt.Sprintf(\"$A$%d:$A$%d\", firstResLine, len(values)+firstResLine-1)\n\tassocRange := fmt.Sprintf(\"$B$%d:$B$%d\", firstResLine, len(values)+firstResLine-1)\n\n\tfor i := 0; i != len(sample); i++ {\n\t\tnameCell := cell(i+2, 1)\n\t\tavgCell := cell(i+2, 2)\n\t\tcalcAvgCell := cell(i+2, 3)\n\t\tstdevCell := cell(i+2, 4)\n\t\tcalcStdevCell := cell(i+2, 5)\n\n\t\tassert.NoError(t, f.SetCellInt(\"Sheet1\", calcAvgCell, int64(average(i))))\n\t\tassert.NoError(t, f.SetCellInt(\"Sheet1\", calcStdevCell, int64(stdev(i))))\n\n\t\t// Average can be done with AVERAGEIF\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", avgCell, fmt.Sprintf(\"ROUND(AVERAGEIF(%s,%s,%s),0)\", assocRange, nameCell, valRange)))\n\n\t\tref := stdevCell + \":\" + stdevCell\n\t\tarr := STCellFormulaTypeArray\n\t\t// Use an array formula for standard deviation\n\t\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", stdevCell, fmt.Sprintf(\"ROUND(STDEVP(IF(%s=%s,%s)),0)\", assocRange, nameCell, valRange),\n\t\t\tFormulaOpts{}, FormulaOpts{Type: &arr}, FormulaOpts{Ref: &ref}))\n\t}\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestWriteArrayFormula.xlsx\")))\n}\n\nfunc TestSetCellStyleAlignment(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\n\tvar style int\n\tstyle, err = f.NewStyle(&Style{Alignment: &Alignment{Horizontal: \"center\", Indent: 1, JustifyLastLine: true, ReadingOrder: 0, RelativeIndent: 1, ShrinkToFit: true, TextRotation: 45, Vertical: \"top\", WrapText: true}})\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A22\", \"A22\", style))\n\n\t// Test set cell style with given illegal rows number\n\tassert.EqualError(t, f.SetCellStyle(\"Sheet1\", \"A\", \"A22\", style), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\tassert.EqualError(t, f.SetCellStyle(\"Sheet1\", \"A22\", \"A\", style), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\t// Test set cell style with invalid sheet name\n\tassert.EqualError(t, f.SetCellStyle(\"Sheet:1\", \"A1\", \"A2\", style), ErrSheetNameInvalid.Error())\n\t// Test get cell style with given illegal rows number\n\tindex, err := f.GetCellStyle(\"Sheet1\", \"A\")\n\tassert.Equal(t, 0, index)\n\tassert.EqualError(t, err, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\n\t// Test get cell style with invalid sheet name\n\t_, err = f.GetCellStyle(\"Sheet:1\", \"A1\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellStyleAlignment.xlsx\")))\n}\n\nfunc TestSetCellStyleBorder(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\n\tvar style int\n\n\t// Test set border on overlapping range with vertical variants shading styles gradient fill\n\tstyle, err = f.NewStyle(&Style{\n\t\tBorder: []Border{\n\t\t\t{Type: \"left\", Color: \"0000FF\", Style: 3},\n\t\t\t{Type: \"top\", Color: \"00FF00\", Style: 4},\n\t\t\t{Type: \"bottom\", Color: \"FFFF00\", Style: 5},\n\t\t\t{Type: \"right\", Color: \"FF0000\", Style: 6},\n\t\t\t{Type: \"diagonalDown\", Color: \"A020F0\", Style: 7},\n\t\t\t{Type: \"diagonalUp\", Color: \"A020F0\", Style: 8},\n\t\t},\n\t})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"J21\", \"L25\", style))\n\n\tstyle, err = f.NewStyle(&Style{Border: []Border{{Type: \"left\", Color: \"0000FF\", Style: 2}, {Type: \"top\", Color: \"00FF00\", Style: 3}, {Type: \"bottom\", Color: \"FFFF00\", Style: 4}, {Type: \"right\", Color: \"FF0000\", Style: 5}, {Type: \"diagonalDown\", Color: \"A020F0\", Style: 6}, {Type: \"diagonalUp\", Color: \"A020F0\", Style: 7}}, Fill: Fill{Type: \"gradient\", Color: []string{\"FFFFFF\", \"E0EBF5\"}, Shading: 1}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"M28\", \"K24\", style))\n\n\tstyle, err = f.NewStyle(&Style{Border: []Border{{Type: \"left\", Color: \"0000FF\", Style: 2}, {Type: \"top\", Color: \"00FF00\", Style: 3}, {Type: \"bottom\", Color: \"FFFF00\", Style: 4}, {Type: \"right\", Color: \"FF0000\", Style: 5}, {Type: \"diagonalDown\", Color: \"A020F0\", Style: 6}, {Type: \"diagonalUp\", Color: \"A020F0\", Style: 7}}, Fill: Fill{Type: \"gradient\", Color: []string{\"FFFFFF\", \"E0EBF5\"}, Shading: 4}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"M28\", \"K24\", style))\n\n\t// Test set border and solid style pattern fill for a single cell\n\tstyle, err = f.NewStyle(&Style{\n\t\tBorder: []Border{\n\t\t\t{\n\t\t\t\tType:  \"left\",\n\t\t\t\tColor: \"0000FF\",\n\t\t\t\tStyle: 8,\n\t\t\t},\n\t\t\t{\n\t\t\t\tType:  \"top\",\n\t\t\t\tColor: \"00FF00\",\n\t\t\t\tStyle: 9,\n\t\t\t},\n\t\t\t{\n\t\t\t\tType:  \"bottom\",\n\t\t\t\tColor: \"FFFF00\",\n\t\t\t\tStyle: 10,\n\t\t\t},\n\t\t\t{\n\t\t\t\tType:  \"right\",\n\t\t\t\tColor: \"FF0000\",\n\t\t\t\tStyle: 11,\n\t\t\t},\n\t\t\t{\n\t\t\t\tType:  \"diagonalDown\",\n\t\t\t\tColor: \"A020F0\",\n\t\t\t\tStyle: 12,\n\t\t\t},\n\t\t\t{\n\t\t\t\tType:  \"diagonalUp\",\n\t\t\t\tColor: \"A020F0\",\n\t\t\t\tStyle: 13,\n\t\t\t},\n\t\t},\n\t\tFill: Fill{\n\t\t\tType:    \"pattern\",\n\t\t\tColor:   []string{\"E0EBF5\"},\n\t\t\tPattern: 1,\n\t\t},\n\t})\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"O22\", \"O22\", style))\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellStyleBorder.xlsx\")))\n}\n\nfunc TestSetCellStyleBorderErrors(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\n\t// Set border with invalid style index number\n\t_, err = f.NewStyle(&Style{Border: []Border{{Type: \"left\", Color: \"0000FF\", Style: -1}, {Type: \"top\", Color: \"00FF00\", Style: 14}, {Type: \"bottom\", Color: \"FFFF00\", Style: 5}, {Type: \"right\", Color: \"FF0000\", Style: 6}, {Type: \"diagonalDown\", Color: \"A020F0\", Style: 9}, {Type: \"diagonalUp\", Color: \"A020F0\", Style: 8}}})\n\tassert.NoError(t, err)\n}\n\nfunc TestSetCellStyleNumberFormat(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\n\t// Test only set fill and number format for a cell\n\tcol := []string{\"L\", \"M\", \"N\", \"O\", \"P\"}\n\tidxTbl := []int{0, 1, 2, 3, 4, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49}\n\tvalue := []string{\"37947.7500001\", \"-37947.7500001\", \"0.007\", \"2.1\", \"String\"}\n\texpected := [][]string{\n\t\t{\"37947.75\", \"37948\", \"37947.75\", \"37,948\", \"37,947.75\", \"3794775%\", \"3794775.00%\", \"3.79E+04\", \"37947 3/4\", \"37947  3/4 \", \"11-22-03\", \"22-Nov-03\", \"22-Nov\", \"Nov-03\", \"6:00 PM\", \"6:00:00 PM\", \"18:00\", \"18:00:00\", \"11/22/03 18:00\", \"37,948 \", \"37,948 \", \"37,947.75 \", \"37,947.75 \", \" 37,948 \", \" $37,948 \", \" 37,947.75 \", \" $37,947.75 \", \"00:00\", \"910746:00:00\", \"00:00.0\", \"37947.7500001\", \"37947.7500001\"},\n\t\t{\"-37947.75\", \"-37948\", \"-37947.75\", \"-37,948\", \"-37,947.75\", \"-3794775%\", \"-3794775.00%\", \"-3.79E+04\", \"-37947 3/4\", \"-37947  3/4 \", \"-37947.7500001\", \"-37947.7500001\", \"-37947.7500001\", \"-37947.7500001\", \"-37947.7500001\", \"-37947.7500001\", \"-37947.7500001\", \"-37947.7500001\", \"-37947.7500001\", \"(37,948)\", \"(37,948)\", \"(37,947.75)\", \"(37,947.75)\", \" (37,948)\", \" $(37,948)\", \" (37,947.75)\", \" $(37,947.75)\", \"-37947.7500001\", \"-37947.7500001\", \"-37947.7500001\", \"-37947.7500001\", \"-37947.7500001\"},\n\t\t{\"0.007\", \"0\", \"0.01\", \"0\", \"0.01\", \"1%\", \"0.70%\", \"7.00E-03\", \"0    \", \"0      \", \"01-00-00\", \"0-Jan-00\", \"0-Jan\", \"Jan-00\", \"12:10 AM\", \"12:10:05 AM\", \"00:10\", \"00:10:05\", \"1/0/00 00:10\", \"0 \", \"0 \", \"0.01 \", \"0.01 \", \" 0 \", \" $0 \", \" 0.01 \", \" $0.01 \", \"10:05\", \"0:10:05\", \"10:04.8\", \"0.007\", \"0.007\"},\n\t\t{\"2.1\", \"2\", \"2.10\", \"2\", \"2.10\", \"210%\", \"210.00%\", \"2.10E+00\", \"2 1/9\", \"2  1/10\", \"01-02-00\", \"2-Jan-00\", \"2-Jan\", \"Jan-00\", \"2:24 AM\", \"2:24:00 AM\", \"02:24\", \"02:24:00\", \"1/2/00 02:24\", \"2 \", \"2 \", \"2.10 \", \"2.10 \", \" 2 \", \" $2 \", \" 2.10 \", \" $2.10 \", \"24:00\", \"50:24:00\", \"24:00.0\", \"2.1\", \"2.1\"},\n\t\t{\"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \"String\", \" String \", \" String \", \" String \", \" String \", \"String\", \"String\", \"String\", \"String\", \"String\"},\n\t}\n\n\tfor c, v := range value {\n\t\tfor r, idx := range idxTbl {\n\t\t\tcell := col[c] + strconv.Itoa(r+1)\n\t\t\tvar val float64\n\t\t\tval, err = strconv.ParseFloat(v, 64)\n\t\t\tif err != nil {\n\t\t\t\tassert.NoError(t, f.SetCellValue(\"Sheet2\", cell, v))\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, f.SetCellValue(\"Sheet2\", cell, val))\n\t\t\t}\n\t\t\tstyle, err := f.NewStyle(&Style{Fill: Fill{Type: \"gradient\", Color: []string{\"FFFFFF\", \"E0EBF5\"}, Shading: 5}, NumFmt: idx})\n\t\t\tif !assert.NoError(t, err) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t\tassert.NoError(t, f.SetCellStyle(\"Sheet2\", cell, cell, style))\n\t\t\tcellValue, err := f.GetCellValue(\"Sheet2\", cell)\n\t\t\tassert.Equal(t, expected[c][r], cellValue, fmt.Sprintf(\"Sheet2!%s value: %s, number format: %s c: %d r: %d\", cell, value[c], builtInNumFmt[idx], c, r))\n\t\t\tassert.NoError(t, err)\n\t\t}\n\t}\n\tvar style int\n\tstyle, err = f.NewStyle(&Style{NumFmt: -1})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet2\", \"L33\", \"L33\", style))\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellStyleNumberFormat.xlsx\")))\n\n\t// Test get cell value with built-in number format code 22 with custom short date pattern\n\tf = NewFile(Options{ShortDatePattern: \"yyyy-m-dd\"})\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 45074.625694444447))\n\tstyle, err = f.NewStyle(&Style{NumFmt: 22})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A1\", \"A1\", style))\n\tcellValue, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"2023-5-28 15:01\", cellValue)\n}\n\nfunc TestSetCellStyleCurrencyNumberFormat(t *testing.T) {\n\tt.Run(\"TestBook3\", func(t *testing.T) {\n\t\tf, err := prepareTestBook3()\n\t\tassert.NoError(t, err)\n\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 56))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A2\", -32.3))\n\t\tvar style int\n\t\tstyle, err = f.NewStyle(&Style{NumFmt: 188, DecimalPlaces: intPtr(-1)})\n\t\tassert.NoError(t, err)\n\n\t\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A1\", \"A1\", style))\n\t\tstyle, err = f.NewStyle(&Style{NumFmt: 188, DecimalPlaces: intPtr(31), NegRed: true})\n\t\tassert.NoError(t, err)\n\n\t\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A2\", \"A2\", style))\n\n\t\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellStyleCurrencyNumberFormat.TestBook3.xlsx\")))\n\t})\n\n\tt.Run(\"TestBook4\", func(t *testing.T) {\n\t\tf, err := prepareTestBook4()\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 42920.5))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A2\", 42920.5))\n\n\t\t_, err = f.NewStyle(&Style{NumFmt: 26})\n\t\tassert.NoError(t, err)\n\n\t\tstyle, err := f.NewStyle(&Style{NumFmt: 27})\n\t\tassert.NoError(t, err)\n\n\t\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A1\", \"A1\", style))\n\t\tstyle, err = f.NewStyle(&Style{NumFmt: 31})\n\t\tassert.NoError(t, err)\n\n\t\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A2\", \"A2\", style))\n\n\t\tstyle, err = f.NewStyle(&Style{NumFmt: 71})\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A2\", \"A2\", style))\n\n\t\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellStyleCurrencyNumberFormat.TestBook4.xlsx\")))\n\t})\n}\n\nfunc TestSetCellStyleLangNumberFormat(t *testing.T) {\n\trawCellValues := make([][]string, 42)\n\tfor i := 0; i < 42; i++ {\n\t\trawCellValues[i] = []string{\"45162\"}\n\t}\n\tfor lang, expected := range map[CultureName][][]string{\n\t\tCultureNameUnknown: rawCellValues,\n\t\tCultureNameEnUS:    {{\"8/24/23\"}, {\"8/24/23\"}, {\"8/24/23\"}, {\"8/24/23\"}, {\"8/24/23\"}, {\"0:00:00\"}, {\"0:00:00\"}, {\"0:00:00\"}, {\"0:00:00\"}, {\"45162\"}, {\"8/24/23\"}, {\"8/24/23\"}, {\"8/24/23\"}, {\"8/24/23\"}, {\"8/24/23\"}, {\"8/24/23\"}, {\"8/24/23\"}, {\"8/24/23\"}, {\"8/24/23\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}},\n\t\tCultureNameJaJP:    {{\"R5.8.24\"}, {\"令和5年8月24日\"}, {\"令和5年8月24日\"}, {\"8/24/23\"}, {\"2023年8月24日\"}, {\"0時00分\"}, {\"0時00分00秒\"}, {\"2023年8月\"}, {\"8月24日\"}, {\"R5.8.24\"}, {\"R5.8.24\"}, {\"令和5年8月24日\"}, {\"2023年8月\"}, {\"8月24日\"}, {\"令和5年8月24日\"}, {\"2023年8月\"}, {\"8月24日\"}, {\"R5.8.24\"}, {\"令和5年8月24日\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}},\n\t\tCultureNameKoKR:    {{\"4356年 08月 24日\"}, {\"08-24\"}, {\"08-24\"}, {\"08-24-56\"}, {\"4356년 08월 24일\"}, {\"0시 00분\"}, {\"0시 00분 00초\"}, {\"4356-08-24\"}, {\"4356-08-24\"}, {\"4356年 08月 24日\"}, {\"4356年 08月 24日\"}, {\"08-24\"}, {\"4356-08-24\"}, {\"4356-08-24\"}, {\"08-24\"}, {\"4356-08-24\"}, {\"4356-08-24\"}, {\"4356年 08月 24日\"}, {\"08-24\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}},\n\t\tCultureNameZhCN:    {{\"2023年8月\"}, {\"8月24日\"}, {\"8月24日\"}, {\"8/24/23\"}, {\"2023年8月24日\"}, {\"0时00分\"}, {\"0时00分00秒\"}, {\"上午12时00分\"}, {\"上午12时00分00秒\"}, {\"2023年8月\"}, {\"2023年8月\"}, {\"8月24日\"}, {\"2023年8月\"}, {\"8月24日\"}, {\"8月24日\"}, {\"上午12时00分\"}, {\"上午12时00分00秒\"}, {\"2023年8月\"}, {\"8月24日\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}},\n\t\tCultureNameZhTW:    {{\"112/8/24\"}, {\"112年8月24日\"}, {\"112年8月24日\"}, {\"8/24/23\"}, {\"2023年8月24日\"}, {\"00時00分\"}, {\"00時00分00秒\"}, {\"上午12時00分\"}, {\"上午12時00分00秒\"}, {\"112/8/24\"}, {\"112/8/24\"}, {\"112年8月24日\"}, {\"上午12時00分\"}, {\"上午12時00分00秒\"}, {\"112年8月24日\"}, {\"上午12時00分\"}, {\"上午12時00分00秒\"}, {\"112/8/24\"}, {\"112年8月24日\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}},\n\t} {\n\t\tf, err := prepareTestBook5(Options{CultureInfo: lang})\n\t\tassert.NoError(t, err)\n\t\trows, err := f.GetRows(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, rows)\n\t\tassert.NoError(t, f.Close())\n\t}\n\t// Test apply language number format code with date and time pattern\n\tfor lang, expected := range map[CultureName][][]string{\n\t\tCultureNameEnUS: {{\"2023-8-24\"}, {\"2023-8-24\"}, {\"2023-8-24\"}, {\"2023-8-24\"}, {\"2023-8-24\"}, {\"00:00:00\"}, {\"00:00:00\"}, {\"00:00:00\"}, {\"00:00:00\"}, {\"45162\"}, {\"2023-8-24\"}, {\"2023-8-24\"}, {\"2023-8-24\"}, {\"2023-8-24\"}, {\"2023-8-24\"}, {\"2023-8-24\"}, {\"2023-8-24\"}, {\"2023-8-24\"}, {\"2023-8-24\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}},\n\t\tCultureNameJaJP: {{\"R5.8.24\"}, {\"令和5年8月24日\"}, {\"令和5年8月24日\"}, {\"2023-8-24\"}, {\"2023年8月24日\"}, {\"00:00:00\"}, {\"00:00:00\"}, {\"2023年8月\"}, {\"8月24日\"}, {\"R5.8.24\"}, {\"R5.8.24\"}, {\"令和5年8月24日\"}, {\"2023年8月\"}, {\"8月24日\"}, {\"令和5年8月24日\"}, {\"2023年8月\"}, {\"8月24日\"}, {\"R5.8.24\"}, {\"令和5年8月24日\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}},\n\t\tCultureNameKoKR: {{\"4356年 08月 24日\"}, {\"08-24\"}, {\"08-24\"}, {\"4356-8-24\"}, {\"4356년 08월 24일\"}, {\"00:00:00\"}, {\"00:00:00\"}, {\"4356-08-24\"}, {\"4356-08-24\"}, {\"4356年 08月 24日\"}, {\"4356年 08月 24日\"}, {\"08-24\"}, {\"4356-08-24\"}, {\"4356-08-24\"}, {\"08-24\"}, {\"4356-08-24\"}, {\"4356-08-24\"}, {\"4356年 08月 24日\"}, {\"08-24\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}},\n\t\tCultureNameZhCN: {{\"2023年8月\"}, {\"8月24日\"}, {\"8月24日\"}, {\"2023-8-24\"}, {\"2023年8月24日\"}, {\"00:00:00\"}, {\"00:00:00\"}, {\"上午12时00分\"}, {\"上午12时00分00秒\"}, {\"2023年8月\"}, {\"2023年8月\"}, {\"8月24日\"}, {\"2023年8月\"}, {\"8月24日\"}, {\"8月24日\"}, {\"上午12时00分\"}, {\"上午12时00分00秒\"}, {\"2023年8月\"}, {\"8月24日\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}},\n\t\tCultureNameZhTW: {{\"112/8/24\"}, {\"112年8月24日\"}, {\"112年8月24日\"}, {\"2023-8-24\"}, {\"2023年8月24日\"}, {\"00:00:00\"}, {\"00:00:00\"}, {\"上午12時00分\"}, {\"上午12時00分00秒\"}, {\"112/8/24\"}, {\"112/8/24\"}, {\"112年8月24日\"}, {\"上午12時00分\"}, {\"上午12時00分00秒\"}, {\"112年8月24日\"}, {\"上午12時00分\"}, {\"上午12時00分00秒\"}, {\"112/8/24\"}, {\"112年8月24日\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}, {\"45162\"}},\n\t} {\n\t\tf, err := prepareTestBook5(Options{CultureInfo: lang, ShortDatePattern: \"yyyy-M-d\", LongTimePattern: \"hh:mm:ss\"})\n\t\tassert.NoError(t, err)\n\t\trows, err := f.GetRows(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, rows)\n\t\tassert.NoError(t, f.Close())\n\t}\n\t// Test open workbook with invalid date and time pattern options\n\t_, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"), Options{LongDatePattern: \"0.00\"})\n\tassert.Equal(t, ErrUnsupportedNumberFormat, err)\n\t_, err = OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"), Options{LongTimePattern: \"0.00\"})\n\tassert.Equal(t, ErrUnsupportedNumberFormat, err)\n\t_, err = OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"), Options{ShortDatePattern: \"0.00\"})\n\tassert.Equal(t, ErrUnsupportedNumberFormat, err)\n}\n\nfunc TestSetCellStyleCustomNumberFormat(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 42920.5))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A2\", 42920.5))\n\tcustomNumFmt := \"[$-380A]dddd\\\\,\\\\ dd\\\" de \\\"mmmm\\\" de \\\"yyyy;@\"\n\tstyle, err := f.NewStyle(&Style{CustomNumFmt: &customNumFmt})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A1\", \"A1\", style))\n\tstyle, err = f.NewStyle(&Style{CustomNumFmt: &customNumFmt, Font: &Font{Color: \"9A0511\"}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A2\", \"A2\", style))\n\n\tcustomNumFmt = \"[$-380A]dddd\\\\,\\\\ dd\\\" de \\\"mmmm\\\" de \\\"yy;@\"\n\t_, err = f.NewStyle(&Style{CustomNumFmt: &customNumFmt})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellStyleCustomNumberFormat.xlsx\")))\n}\n\nfunc TestSetCellStyleFill(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\n\tvar style int\n\t// Test set fill for cell with invalid parameter\n\tstyle, err = f.NewStyle(&Style{Fill: Fill{Type: \"gradient\", Color: []string{\"FFFFFF\", \"E0EBF5\"}, Shading: 6}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"O23\", \"O23\", style))\n\n\tstyle, err = f.NewStyle(&Style{Fill: Fill{Type: \"pattern\", Color: []string{}, Shading: 1}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"O23\", \"O23\", style))\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellStyleFill.xlsx\")))\n}\n\nfunc TestSetCellStyleFont(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\n\tvar style int\n\tstyle, err = f.NewStyle(&Style{Font: &Font{Bold: true, Italic: true, Family: \"Times New Roman\", Size: 36, Color: \"777777\", Underline: \"single\"}})\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.SetCellStyle(\"Sheet2\", \"A1\", \"A1\", style))\n\n\tstyle, err = f.NewStyle(&Style{Font: &Font{Italic: true, Underline: \"double\"}})\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.SetCellStyle(\"Sheet2\", \"A2\", \"A2\", style))\n\n\tstyle, err = f.NewStyle(&Style{Font: &Font{Bold: true}})\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.SetCellStyle(\"Sheet2\", \"A3\", \"A3\", style))\n\n\tstyle, err = f.NewStyle(&Style{Font: &Font{Bold: true, Family: \"\", Size: 0, Color: \"\", Underline: \"\"}})\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.SetCellStyle(\"Sheet2\", \"A4\", \"A4\", style))\n\n\tstyle, err = f.NewStyle(&Style{Font: &Font{Color: \"777777\", Strike: true}})\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.SetCellStyle(\"Sheet2\", \"A5\", \"A5\", style))\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetCellStyleFont.xlsx\")))\n}\n\nfunc TestSetCellStyleProtection(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\n\tvar style int\n\tstyle, err = f.NewStyle(&Style{Protection: &Protection{Hidden: true, Locked: true}})\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.SetCellStyle(\"Sheet2\", \"A6\", \"A6\", style))\n\terr = f.SaveAs(filepath.Join(\"test\", \"TestSetCellStyleProtection.xlsx\"))\n\tassert.NoError(t, err)\n}\n\nfunc TestSetDeleteSheet(t *testing.T) {\n\tt.Run(\"TestBook3\", func(t *testing.T) {\n\t\tf, err := prepareTestBook3()\n\t\tassert.NoError(t, err)\n\n\t\tassert.NoError(t, f.DeleteSheet(\"Sheet3\"))\n\t\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetDeleteSheet.TestBook3.xlsx\")))\n\t})\n\n\tt.Run(\"TestBook4\", func(t *testing.T) {\n\t\tf, err := prepareTestBook4()\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.DeleteSheet(\"Sheet1\"))\n\t\tassert.NoError(t, f.AddComment(\"Sheet1\", Comment{Cell: \"A1\", Author: \"Excelize\", Paragraph: []RichTextRun{{Text: \"Excelize: \", Font: &Font{Bold: true}}, {Text: \"This is a comment.\"}}}))\n\t\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetDeleteSheet.TestBook4.xlsx\")))\n\t})\n}\n\nfunc TestSheetVisibility(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.SetSheetVisible(\"Sheet2\", false))\n\tassert.NoError(t, f.SetSheetVisible(\"Sheet2\", false, true))\n\tassert.NoError(t, f.SetSheetVisible(\"Sheet1\", false))\n\tassert.NoError(t, f.SetSheetVisible(\"Sheet1\", true))\n\tvisible, err := f.GetSheetVisible(\"Sheet1\")\n\tassert.Equal(t, true, visible)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSheetVisibility.xlsx\")))\n}\n\nfunc TestCopySheet(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\n\tidx, err := f.NewSheet(\"CopySheet\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.CopySheet(0, idx))\n\n\tassert.NoError(t, f.SetCellValue(\"CopySheet\", \"F1\", \"Hello\"))\n\tval, err := f.GetCellValue(\"Sheet1\", \"F1\")\n\tassert.NoError(t, err)\n\tassert.NotEqual(t, \"Hello\", val)\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestCopySheet.xlsx\")))\n}\n\nfunc TestCopySheetError(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\tassert.EqualError(t, f.copySheet(-1, -2), ErrSheetNameBlank.Error())\n\tassert.EqualError(t, f.CopySheet(-1, -2), ErrSheetIdx.Error())\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestCopySheetError.xlsx\")))\n}\n\nfunc TestGetSheetComments(t *testing.T) {\n\tf := NewFile()\n\tassert.Empty(t, f.getSheetComments(\"sheet0\"))\n}\n\nfunc TestGetActiveSheetIndex(t *testing.T) {\n\tf := NewFile()\n\tf.WorkBook.BookViews = nil\n\tassert.Equal(t, 0, f.GetActiveSheetIndex())\n}\n\nfunc TestRelsWriter(t *testing.T) {\n\tf := NewFile()\n\tf.Relationships.Store(\"xl/worksheets/sheet/rels/sheet1.xml.rel\", &xlsxRelationships{})\n\tf.relsWriter()\n}\n\nfunc TestConditionalFormat(t *testing.T) {\n\tf := NewFile()\n\tsheet1 := f.GetSheetName(0)\n\n\tassert.NoError(t, fillCells(f, sheet1, 10, 15))\n\n\tvar format1, format2, format3, format4 int\n\tvar err error\n\t// Rose format for bad conditional\n\tformat1, err = f.NewConditionalStyle(&Style{Font: &Font{Color: \"9A0511\"}, Fill: Fill{Type: \"pattern\", Color: []string{\"FEC7CE\"}, Pattern: 1}})\n\tassert.NoError(t, err)\n\n\t// Light yellow format for neutral conditional\n\tformat2, err = f.NewConditionalStyle(&Style{Fill: Fill{Type: \"pattern\", Color: []string{\"FEEAA0\"}, Pattern: 1}})\n\tassert.NoError(t, err)\n\n\t// Light green format for good conditional\n\tformat3, err = f.NewConditionalStyle(&Style{Font: &Font{Color: \"09600B\"}, Fill: Fill{Type: \"pattern\", Color: []string{\"C7EECF\"}, Pattern: 1}})\n\tassert.NoError(t, err)\n\n\t// conditional style with align and left border\n\tformat4, err = f.NewConditionalStyle(&Style{Alignment: &Alignment{WrapText: true}, Border: []Border{{Type: \"left\", Color: \"000000\", Style: 1}}})\n\tassert.NoError(t, err)\n\n\t// Color scales: 2 color\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"A1:A10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"2_color_scale\",\n\t\t\t\tCriteria: \"=\",\n\t\t\t\tMinType:  \"min\",\n\t\t\t\tMaxType:  \"max\",\n\t\t\t\tMinColor: \"#F8696B\",\n\t\t\t\tMaxColor: \"#63BE7B\",\n\t\t\t},\n\t\t},\n\t))\n\t// Color scales: 3 color\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"B1:B10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"3_color_scale\",\n\t\t\t\tCriteria: \"=\",\n\t\t\t\tMinType:  \"min\",\n\t\t\t\tMidType:  \"percentile\",\n\t\t\t\tMaxType:  \"max\",\n\t\t\t\tMinColor: \"#F8696B\",\n\t\t\t\tMidColor: \"#FFEB84\",\n\t\t\t\tMaxColor: \"#63BE7B\",\n\t\t\t},\n\t\t},\n\t))\n\t// Highlight cells rules: between...\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"C1:C10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"cell\",\n\t\t\t\tCriteria: \"between\",\n\t\t\t\tFormat:   &format1,\n\t\t\t\tMinValue: \"6\",\n\t\t\t\tMaxValue: \"8\",\n\t\t\t},\n\t\t},\n\t))\n\t// Highlight cells rules: Greater Than...\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"D1:D10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"cell\",\n\t\t\t\tCriteria: \">\",\n\t\t\t\tFormat:   &format3,\n\t\t\t\tValue:    \"6\",\n\t\t\t},\n\t\t},\n\t))\n\t// Highlight cells rules: Equal To...\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"E1:E10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"top\",\n\t\t\t\tCriteria: \"=\",\n\t\t\t\tFormat:   &format3,\n\t\t\t},\n\t\t},\n\t))\n\t// Highlight cells rules: Not Equal To...\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"F1:F10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"unique\",\n\t\t\t\tCriteria: \"=\",\n\t\t\t\tFormat:   &format2,\n\t\t\t},\n\t\t},\n\t))\n\t// Highlight cells rules: Duplicate Values...\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"G1:G10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"duplicate\",\n\t\t\t\tCriteria: \"=\",\n\t\t\t\tFormat:   &format2,\n\t\t\t},\n\t\t},\n\t))\n\t// Top/Bottom rules: Top 10%.\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"H1:H10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"top\",\n\t\t\t\tCriteria: \"=\",\n\t\t\t\tFormat:   &format1,\n\t\t\t\tValue:    \"6\",\n\t\t\t\tPercent:  true,\n\t\t\t},\n\t\t},\n\t))\n\t// Top/Bottom rules: Above Average...\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"I1:I10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:         \"average\",\n\t\t\t\tCriteria:     \"=\",\n\t\t\t\tFormat:       &format3,\n\t\t\t\tAboveAverage: true,\n\t\t\t},\n\t\t},\n\t))\n\t// Top/Bottom rules: Below Average...\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"J1:J10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:         \"average\",\n\t\t\t\tCriteria:     \"=\",\n\t\t\t\tFormat:       &format1,\n\t\t\t\tAboveAverage: false,\n\t\t\t},\n\t\t},\n\t))\n\t// Data Bars: Gradient Fill\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"K1:K10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"data_bar\",\n\t\t\t\tCriteria: \"=\",\n\t\t\t\tMinType:  \"min\",\n\t\t\t\tMaxType:  \"max\",\n\t\t\t\tBarColor: \"#638EC6\",\n\t\t\t},\n\t\t},\n\t))\n\t// Use a formula to determine which cells to format\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"L1:L10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"formula\",\n\t\t\t\tCriteria: \"L2<3\",\n\t\t\t\tFormat:   &format1,\n\t\t\t},\n\t\t},\n\t))\n\t// Alignment/Border cells rules\n\tassert.NoError(t, f.SetConditionalFormat(sheet1, \"M1:M10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"cell\",\n\t\t\t\tCriteria: \">\",\n\t\t\t\tFormat:   &format4,\n\t\t\t\tValue:    \"0\",\n\t\t\t},\n\t\t},\n\t))\n\t// Test set conditional format with invalid cell reference\n\tassert.Equal(t, newCellNameToCoordinatesError(\"-\", newInvalidCellNameError(\"-\")), f.SetConditionalFormat(\"Sheet1\", \"A1:-\", nil))\n\t// Test set conditional format on not exists worksheet\n\tassert.EqualError(t, f.SetConditionalFormat(\"SheetN\", \"L1:L10\", nil), \"sheet SheetN does not exist\")\n\t// Test set conditional format with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.SetConditionalFormat(\"Sheet:1\", \"L1:L10\", nil))\n\n\terr = f.SaveAs(filepath.Join(\"test\", \"TestConditionalFormat.xlsx\"))\n\tassert.NoError(t, err)\n\n\t// Set conditional format with illegal valid type\n\tassert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat(sheet1, \"K1:K10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"\",\n\t\t\t\tCriteria: \"=\",\n\t\t\t\tMinType:  \"min\",\n\t\t\t\tMaxType:  \"max\",\n\t\t\t\tBarColor: \"#638EC6\",\n\t\t\t},\n\t\t},\n\t))\n\t// Set conditional format with illegal criteria type\n\tassert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat(sheet1, \"K1:K10\",\n\t\t[]ConditionalFormatOptions{\n\t\t\t{\n\t\t\t\tType:     \"data_bar\",\n\t\t\t\tCriteria: \"\",\n\t\t\t\tMinType:  \"min\",\n\t\t\t\tMaxType:  \"max\",\n\t\t\t\tBarColor: \"#638EC6\",\n\t\t\t},\n\t\t},\n\t))\n\t// Test create conditional format with invalid custom number format\n\tvar exp string\n\t_, err = f.NewConditionalStyle(&Style{CustomNumFmt: &exp})\n\tassert.Equal(t, ErrCustomNumFmt, err)\n\n\t// Set conditional format with file without dxfs element should not return error\n\tf, err = OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\n\t_, err = f.NewConditionalStyle(&Style{Font: &Font{Color: \"9A0511\"}, Fill: Fill{Type: \"\", Color: []string{\"FEC7CE\"}, Pattern: 1}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestSharedStrings(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"SharedStrings.xlsx\"))\n\tassert.NoError(t, err)\n\trows, err := f.GetRows(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A\", rows[0][0])\n\trows, err = f.GetRows(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Test Weight (Kgs)\", rows[0][0])\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestSetSheetCol(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.SetSheetCol(\"Sheet1\", \"B27\", &[]interface{}{\"cell\", nil, int32(42), float64(42), time.Now().UTC()}))\n\n\tassert.EqualError(t, f.SetSheetCol(\"Sheet1\", \"\", &[]interface{}{\"cell\", nil, 2}),\n\t\tnewCellNameToCoordinatesError(\"\", newInvalidCellNameError(\"\")).Error())\n\t// Test set worksheet column values with invalid sheet name\n\tassert.EqualError(t, f.SetSheetCol(\"Sheet:1\", \"A1\", &[]interface{}{nil}), ErrSheetNameInvalid.Error())\n\tassert.EqualError(t, f.SetSheetCol(\"Sheet1\", \"B27\", []interface{}{}), ErrParameterInvalid.Error())\n\tassert.EqualError(t, f.SetSheetCol(\"Sheet1\", \"B27\", &f), ErrParameterInvalid.Error())\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetSheetCol.xlsx\")))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestSetSheetRow(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"B27\", &[]interface{}{\"cell\", nil, int32(42), float64(42), time.Now().UTC()}))\n\n\tassert.EqualError(t, f.SetSheetRow(\"Sheet1\", \"\", &[]interface{}{\"cell\", nil, 2}),\n\t\tnewCellNameToCoordinatesError(\"\", newInvalidCellNameError(\"\")).Error())\n\t// Test set worksheet row with invalid sheet name\n\tassert.EqualError(t, f.SetSheetRow(\"Sheet:1\", \"A1\", &[]interface{}{1}), ErrSheetNameInvalid.Error())\n\tassert.EqualError(t, f.SetSheetRow(\"Sheet1\", \"B27\", []interface{}{}), ErrParameterInvalid.Error())\n\tassert.EqualError(t, f.SetSheetRow(\"Sheet1\", \"B27\", &f), ErrParameterInvalid.Error())\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetSheetRow.xlsx\")))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestHSL(t *testing.T) {\n\tvar hsl HSL\n\tr, g, b, a := hsl.RGBA()\n\tassert.Equal(t, uint32(0), r)\n\tassert.Equal(t, uint32(0), g)\n\tassert.Equal(t, uint32(0), b)\n\tassert.Equal(t, uint32(0xffff), a)\n\tassert.Equal(t, HSL{0, 0, 0}, hslModel(hsl))\n\tassert.Equal(t, HSL{0, 0, 0}, hslModel(color.Gray16{Y: uint16(1)}))\n\tR, G, B := HSLToRGB(0, 1, 0.4)\n\tassert.Equal(t, uint8(204), R)\n\tassert.Equal(t, uint8(0), G)\n\tassert.Equal(t, uint8(0), B)\n\tR, G, B = HSLToRGB(0, 1, 0.6)\n\tassert.Equal(t, uint8(255), R)\n\tassert.Equal(t, uint8(51), G)\n\tassert.Equal(t, uint8(51), B)\n\tassert.Equal(t, 0.0, hueToRGB(0, 0, -1))\n\tassert.Equal(t, 0.0, hueToRGB(0, 0, 2))\n\tassert.Equal(t, 0.0, hueToRGB(0, 0, 1.0/7))\n\tassert.Equal(t, 0.0, hueToRGB(0, 0, 0.4))\n\tassert.Equal(t, 0.0, hueToRGB(0, 0, 2.0/4))\n\th, s, l := RGBToHSL(255, 255, 0)\n\tassert.Equal(t, 0.16666666666666666, h)\n\tassert.Equal(t, 1.0, s)\n\tassert.Equal(t, 0.5, l)\n\th, s, l = RGBToHSL(0, 255, 255)\n\tassert.Equal(t, 0.5, h)\n\tassert.Equal(t, 1.0, s)\n\tassert.Equal(t, 0.5, l)\n\th, s, l = RGBToHSL(250, 100, 50)\n\tassert.Equal(t, 0.041666666666666664, h)\n\tassert.Equal(t, 0.9523809523809524, s)\n\tassert.Equal(t, 0.5882352941176471, l)\n\th, s, l = RGBToHSL(50, 100, 250)\n\tassert.Equal(t, 0.625, h)\n\tassert.Equal(t, 0.9523809523809524, s)\n\tassert.Equal(t, 0.5882352941176471, l)\n\th, s, l = RGBToHSL(250, 50, 100)\n\tassert.Equal(t, 0.9583333333333334, h)\n\tassert.Equal(t, 0.9523809523809524, s)\n\tassert.Equal(t, 0.5882352941176471, l)\n}\n\nfunc TestProtectSheet(t *testing.T) {\n\tf := NewFile()\n\tsheetName := f.GetSheetName(0)\n\tassert.EqualError(t, f.ProtectSheet(sheetName, nil), ErrParameterInvalid.Error())\n\t// Test protect worksheet with XOR hash algorithm\n\tassert.NoError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{\n\t\tPassword:      \"password\",\n\t\tEditScenarios: false,\n\t}))\n\topts, err := f.GetSheetProtection(sheetName)\n\tassert.NoError(t, err)\n\tassert.Equal(t, false, opts.EditScenarios)\n\tws, err := f.workSheetReader(sheetName)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"83AF\", ws.SheetProtection.Password)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestProtectSheet.xlsx\")))\n\t// Test protect worksheet with SHA-512 hash algorithm\n\tassert.NoError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{\n\t\tAlgorithmName: \"SHA-512\",\n\t\tPassword:      \"password\",\n\t}))\n\tws, err = f.workSheetReader(sheetName)\n\tassert.NoError(t, err)\n\tassert.Len(t, ws.SheetProtection.SaltValue, 24)\n\tassert.Len(t, ws.SheetProtection.HashValue, 88)\n\tassert.Equal(t, int(sheetProtectionSpinCount), ws.SheetProtection.SpinCount)\n\t// Test remove sheet protection with an incorrect password\n\tassert.EqualError(t, f.UnprotectSheet(sheetName, \"wrongPassword\"), ErrUnprotectSheetPassword.Error())\n\t// Test remove sheet protection with invalid sheet name\n\tassert.EqualError(t, f.UnprotectSheet(\"Sheet:1\", \"wrongPassword\"), ErrSheetNameInvalid.Error())\n\t// Test remove sheet protection with password verification\n\tassert.NoError(t, f.UnprotectSheet(sheetName, \"password\"))\n\t// Test protect worksheet with empty password\n\tassert.NoError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{}))\n\tassert.Empty(t, ws.SheetProtection.Password)\n\t// Test protect worksheet with password exceeds the limit length\n\tassert.EqualError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{\n\t\tAlgorithmName: \"MD4\",\n\t\tPassword:      strings.Repeat(\"s\", MaxFieldLength+1),\n\t}), ErrPasswordLengthInvalid.Error())\n\t// Test protect worksheet with unsupported hash algorithm\n\tassert.EqualError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{\n\t\tAlgorithmName: \"RIPEMD-160\",\n\t\tPassword:      \"password\",\n\t}), ErrUnsupportedHashAlgorithm.Error())\n\t// Test protect not exists worksheet\n\tassert.EqualError(t, f.ProtectSheet(\"SheetN\", nil), \"sheet SheetN does not exist\")\n\t// Test protect sheet with invalid sheet name\n\tassert.EqualError(t, f.ProtectSheet(\"Sheet:1\", nil), ErrSheetNameInvalid.Error())\n\t// Test get sheet protection on not exists worksheet\n\t_, err = f.GetSheetProtection(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n}\n\nfunc TestUnprotectSheet(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\t// Test remove protection on not exists worksheet\n\tassert.EqualError(t, f.UnprotectSheet(\"SheetN\"), \"sheet SheetN does not exist\")\n\n\tassert.NoError(t, f.UnprotectSheet(\"Sheet1\"))\n\tassert.EqualError(t, f.UnprotectSheet(\"Sheet1\", \"password\"), ErrUnprotectSheet.Error())\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestUnprotectSheet.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tsheetName := f.GetSheetName(0)\n\tassert.NoError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{Password: \"password\"}))\n\t// Test remove sheet protection with an incorrect password\n\tassert.EqualError(t, f.UnprotectSheet(sheetName, \"wrongPassword\"), ErrUnprotectSheetPassword.Error())\n\t// Test remove sheet protection with password verification\n\tassert.NoError(t, f.UnprotectSheet(sheetName, \"password\"))\n\t// Test with invalid salt value\n\tassert.NoError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{\n\t\tAlgorithmName: \"SHA-512\",\n\t\tPassword:      \"password\",\n\t}))\n\tws, err := f.workSheetReader(sheetName)\n\tassert.NoError(t, err)\n\tws.SheetProtection.SaltValue = \"YWJjZA=====\"\n\tassert.EqualError(t, f.UnprotectSheet(sheetName, \"wrongPassword\"), \"illegal base64 data at input byte 8\")\n}\n\nfunc TestProtectWorkbook(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.ProtectWorkbook(nil))\n\t// Test protect workbook with default hash algorithm\n\tassert.NoError(t, f.ProtectWorkbook(&WorkbookProtectionOptions{\n\t\tPassword:      \"password\",\n\t\tLockStructure: true,\n\t}))\n\twb, err := f.workbookReader()\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"SHA-512\", wb.WorkbookProtection.WorkbookAlgorithmName)\n\tassert.Len(t, wb.WorkbookProtection.WorkbookSaltValue, 24)\n\tassert.Len(t, wb.WorkbookProtection.WorkbookHashValue, 88)\n\tassert.Equal(t, int(workbookProtectionSpinCount), wb.WorkbookProtection.WorkbookSpinCount)\n\n\t// Test protect workbook with password exceeds the limit length\n\tassert.EqualError(t, f.ProtectWorkbook(&WorkbookProtectionOptions{\n\t\tAlgorithmName: \"MD4\",\n\t\tPassword:      strings.Repeat(\"s\", MaxFieldLength+1),\n\t}), ErrPasswordLengthInvalid.Error())\n\t// Test protect workbook with unsupported hash algorithm\n\tassert.EqualError(t, f.ProtectWorkbook(&WorkbookProtectionOptions{\n\t\tAlgorithmName: \"RIPEMD-160\",\n\t\tPassword:      \"password\",\n\t}), ErrUnsupportedHashAlgorithm.Error())\n\t// Test protect workbook with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.ProtectWorkbook(nil), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestUnprotectWorkbook(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.UnprotectWorkbook())\n\tassert.EqualError(t, f.UnprotectWorkbook(\"password\"), ErrUnprotectWorkbook.Error())\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestUnprotectWorkbook.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tassert.NoError(t, f.ProtectWorkbook(&WorkbookProtectionOptions{Password: \"password\"}))\n\t// Test remove workbook protection with an incorrect password\n\tassert.EqualError(t, f.UnprotectWorkbook(\"wrongPassword\"), ErrUnprotectWorkbookPassword.Error())\n\t// Test remove workbook protection with password verification\n\tassert.NoError(t, f.UnprotectWorkbook(\"password\"))\n\t// Test with invalid salt value\n\tassert.NoError(t, f.ProtectWorkbook(&WorkbookProtectionOptions{\n\t\tAlgorithmName: \"SHA-512\",\n\t\tPassword:      \"password\",\n\t}))\n\twb, err := f.workbookReader()\n\tassert.NoError(t, err)\n\twb.WorkbookProtection.WorkbookSaltValue = \"YWJjZA=====\"\n\tassert.EqualError(t, f.UnprotectWorkbook(\"wrongPassword\"), \"illegal base64 data at input byte 8\")\n\t// Test remove workbook protection with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.UnprotectWorkbook(), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestSetDefaultTimeStyle(t *testing.T) {\n\tf := NewFile()\n\t// Test set default time style on not exists worksheet.\n\tassert.EqualError(t, f.setDefaultTimeStyle(\"SheetN\", \"\", 0), \"sheet SheetN does not exist\")\n\n\t// Test set default time style on invalid cell\n\tassert.EqualError(t, f.setDefaultTimeStyle(\"Sheet1\", \"\", 42), newCellNameToCoordinatesError(\"\", newInvalidCellNameError(\"\")).Error())\n}\n\nfunc TestAddVBAProject(t *testing.T) {\n\tf := NewFile()\n\tfile, err := os.ReadFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetSheetProps(\"Sheet1\", &SheetPropsOptions{CodeName: stringPtr(\"Sheet1\")}))\n\tassert.EqualError(t, f.AddVBAProject(file), ErrAddVBAProject.Error())\n\tfile, err = os.ReadFile(filepath.Join(\"test\", \"vbaProject.bin\"))\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.AddVBAProject(file))\n\t// Test add VBA project twice\n\tassert.NoError(t, f.AddVBAProject(file))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddVBAProject.xlsm\")))\n\t// Test add VBA with unsupported charset workbook relationships\n\tf.Relationships.Delete(defaultXMLPathWorkbookRels)\n\tf.Pkg.Store(defaultXMLPathWorkbookRels, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddVBAProject(file), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestContentTypesReader(t *testing.T) {\n\t// Test unsupported charset\n\tf := NewFile()\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\t_, err := f.contentTypesReader()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestWorkbookReader(t *testing.T) {\n\t// Test unsupported charset\n\tf := NewFile()\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\t_, err := f.workbookReader()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestWorkSheetReader(t *testing.T) {\n\t// Test unsupported charset\n\tf := NewFile()\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", MacintoshCyrillicCharset)\n\t_, err := f.workSheetReader(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.EqualError(t, f.UpdateLinkedValue(), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test on no checked worksheet\n\tf = NewFile()\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(`<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><sheetData/></worksheet>`))\n\tf.checked = sync.Map{}\n\t_, err = f.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n}\n\nfunc TestRelsReader(t *testing.T) {\n\t// Test unsupported charset\n\tf := NewFile()\n\trels := defaultXMLPathWorkbookRels\n\tf.Relationships.Store(rels, nil)\n\tf.Pkg.Store(rels, MacintoshCyrillicCharset)\n\t_, err := f.relsReader(rels)\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestDeleteSheetFromWorkbookRels(t *testing.T) {\n\tf := NewFile()\n\trels := defaultXMLPathWorkbookRels\n\tf.Relationships.Store(rels, nil)\n\tassert.Equal(t, f.deleteSheetFromWorkbookRels(\"rID\"), \"\")\n}\n\nfunc TestUpdateLinkedValue(t *testing.T) {\n\tf := NewFile()\n\t// Test update lined value with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.UpdateLinkedValue(), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestAttrValToInt(t *testing.T) {\n\t_, err := attrValToInt(\"r\", []xml.Attr{\n\t\t{Name: xml.Name{Local: \"r\"}, Value: \"s\"},\n\t})\n\tassert.EqualError(t, err, `strconv.Atoi: parsing \"s\": invalid syntax`)\n}\n\nfunc prepareTestBook1() (*File, error) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err = f.AddPicture(\"Sheet2\", \"I9\", filepath.Join(\"test\", \"images\", \"excel.jpg\"),\n\t\t&GraphicOptions{OffsetX: 140, OffsetY: 120, Hyperlink: \"#Sheet2!D8\", HyperlinkType: \"Location\"}); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Test add picture to worksheet with offset, external hyperlink and positioning\n\tif err := f.AddPicture(\"Sheet1\", \"F21\", filepath.Join(\"test\", \"images\", \"excel.png\"),\n\t\t&GraphicOptions{\n\t\t\tOffsetX:       10,\n\t\t\tOffsetY:       10,\n\t\t\tHyperlink:     \"https://github.com/xuri/excelize\",\n\t\t\tHyperlinkType: \"External\",\n\t\t\tPositioning:   \"oneCell\",\n\t\t},\n\t); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfile, err := os.ReadFile(filepath.Join(\"test\", \"images\", \"excel.jpg\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = f.AddPictureFromBytes(\"Sheet1\", \"Q1\", &Picture{Extension: \".jpg\", File: file, Format: &GraphicOptions{AltText: \"Excel Logo\"}})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn f, nil\n}\n\nfunc prepareTestBook3() (*File, error) {\n\tf := NewFile()\n\tif _, err := f.NewSheet(\"Sheet2\"); err != nil {\n\t\treturn nil, err\n\t}\n\tif _, err := f.NewSheet(\"Sheet3\"); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := f.SetCellInt(\"Sheet2\", \"A23\", 56); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := f.SetCellStr(\"Sheet1\", \"B20\", \"42\"); err != nil {\n\t\treturn nil, err\n\t}\n\tf.SetActiveSheet(0)\n\tif err := f.AddPicture(\"Sheet1\", \"H2\", filepath.Join(\"test\", \"images\", \"excel.gif\"),\n\t\t&GraphicOptions{ScaleX: 0.5, ScaleY: 0.5, Positioning: \"absolute\"}); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := f.AddPicture(\"Sheet1\", \"C2\", filepath.Join(\"test\", \"images\", \"excel.png\"), nil); err != nil {\n\t\treturn nil, err\n\t}\n\treturn f, nil\n}\n\nfunc prepareTestBook4() (*File, error) {\n\tf := NewFile()\n\tif err := f.SetColWidth(\"Sheet1\", \"B\", \"A\", 12); err != nil {\n\t\treturn f, err\n\t}\n\tif err := f.SetColWidth(\"Sheet1\", \"A\", \"B\", 12); err != nil {\n\t\treturn f, err\n\t}\n\tif _, err := f.GetColWidth(\"Sheet1\", \"A\"); err != nil {\n\t\treturn f, err\n\t}\n\tif _, err := f.GetColWidth(\"Sheet1\", \"C\"); err != nil {\n\t\treturn f, err\n\t}\n\n\treturn f, nil\n}\n\nfunc prepareTestBook5(opts Options) (*File, error) {\n\tf := NewFile(opts)\n\tvar rowNum int\n\tfor _, idxRange := range [][]int{{27, 36}, {50, 81}} {\n\t\tfor numFmtIdx := idxRange[0]; numFmtIdx <= idxRange[1]; numFmtIdx++ {\n\t\t\trowNum++\n\t\t\tstyleID, err := f.NewStyle(&Style{NumFmt: numFmtIdx})\n\t\t\tif err != nil {\n\t\t\t\treturn f, err\n\t\t\t}\n\t\t\tcell, err := CoordinatesToCellName(1, rowNum)\n\t\t\tif err != nil {\n\t\t\t\treturn f, err\n\t\t\t}\n\t\t\tif err := f.SetCellValue(\"Sheet1\", cell, 45162); err != nil {\n\t\t\t\treturn f, err\n\t\t\t}\n\t\t\tif err := f.SetCellStyle(\"Sheet1\", cell, cell, styleID); err != nil {\n\t\t\t\treturn f, err\n\t\t\t}\n\t\t}\n\t}\n\treturn f, nil\n}\n\nfunc fillCells(f *File, sheet string, colCount, rowCount int) error {\n\tfor col := 1; col <= colCount; col++ {\n\t\tfor row := 1; row <= rowCount; row++ {\n\t\t\tcell, _ := CoordinatesToCellName(col, row)\n\t\t\tif err := f.SetCellStr(sheet, cell, cell); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc BenchmarkOpenFile(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\t\tif err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t\tif err := f.Close(); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "file.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/xml\"\n\t\"io\"\n\t\"math\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n)\n\n// NewFile provides a function to create new file by default template.\n// For example:\n//\n//\tf := NewFile()\nfunc NewFile(opts ...Options) *File {\n\tf := newFile()\n\tf.Pkg.Store(defaultXMLPathRels, []byte(xml.Header+templateRels))\n\tf.Pkg.Store(defaultXMLPathDocPropsApp, []byte(xml.Header+templateDocpropsApp))\n\tf.Pkg.Store(defaultXMLPathDocPropsCore, []byte(xml.Header+templateDocpropsCore))\n\tf.Pkg.Store(defaultXMLPathWorkbookRels, []byte(xml.Header+templateWorkbookRels))\n\tf.Pkg.Store(defaultXMLPathTheme, []byte(xml.Header+templateTheme))\n\tf.Pkg.Store(defaultXMLPathSheet, []byte(xml.Header+templateSheet))\n\tf.Pkg.Store(defaultXMLPathStyles, []byte(xml.Header+templateStyles))\n\tf.Pkg.Store(defaultXMLPathWorkbook, []byte(xml.Header+templateWorkbook))\n\tf.Pkg.Store(defaultXMLPathContentTypes, []byte(xml.Header+templateContentTypes))\n\tf.SheetCount = 1\n\tf.CalcChain, _ = f.calcChainReader()\n\tf.ContentTypes, _ = f.contentTypesReader()\n\tf.Styles, _ = f.stylesReader()\n\tf.WorkBook, _ = f.workbookReader()\n\tf.Relationships = sync.Map{}\n\trels, _ := f.relsReader(defaultXMLPathWorkbookRels)\n\tf.Relationships.Store(defaultXMLPathWorkbookRels, rels)\n\tf.sheetMap[\"Sheet1\"] = defaultXMLPathSheet\n\tws, _ := f.workSheetReader(\"Sheet1\")\n\tf.Sheet.Store(defaultXMLPathSheet, ws)\n\tf.Theme, _ = f.themeReader()\n\tf.options = f.getOptions(opts...)\n\treturn f\n}\n\n// Save provides a function to override the spreadsheet with origin path.\nfunc (f *File) Save(opts ...Options) error {\n\tif f.Path == \"\" {\n\t\treturn ErrSave\n\t}\n\tfor i := range opts {\n\t\tf.options = &opts[i]\n\t}\n\treturn f.SaveAs(f.Path, *f.options)\n}\n\n// SaveAs provides a function to create or update to a spreadsheet at the\n// provided path.\nfunc (f *File) SaveAs(name string, opts ...Options) error {\n\tif countUTF16String(name) > MaxFilePathLength {\n\t\treturn ErrMaxFilePathLength\n\t}\n\tf.Path = name\n\tif _, ok := supportedContentTypes[strings.ToLower(filepath.Ext(f.Path))]; !ok {\n\t\treturn ErrWorkbookFileFormat\n\t}\n\tfile, err := os.OpenFile(filepath.Clean(name), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\t_ = file.Close()\n\t}()\n\treturn f.Write(file, opts...)\n}\n\n// Close closes and cleanup the open temporary file for the spreadsheet.\nfunc (f *File) Close() error {\n\tvar firstErr error\n\tif f.sharedStringTemp != nil {\n\t\tfirstErr = f.sharedStringTemp.Close()\n\t\tf.sharedStringTemp = nil\n\t}\n\tfor _, stream := range f.streams {\n\t\t_ = stream.rawData.Close()\n\t}\n\tf.streams = nil\n\tf.tempFiles.Range(func(k, v interface{}) bool {\n\t\tif err := os.Remove(v.(string)); err != nil && firstErr == nil {\n\t\t\tfirstErr = err\n\t\t}\n\t\treturn true\n\t})\n\tf.tempFiles.Clear()\n\treturn firstErr\n}\n\n// Write provides a function to write to an io.Writer.\nfunc (f *File) Write(w io.Writer, opts ...Options) error {\n\t_, err := f.WriteTo(w, opts...)\n\treturn err\n}\n\n// WriteTo implements io.WriterTo to write the file.\nfunc (f *File) WriteTo(w io.Writer, opts ...Options) (int64, error) {\n\tfor i := range opts {\n\t\tf.options = &opts[i]\n\t}\n\tif len(f.Path) != 0 {\n\t\tcontentType, ok := supportedContentTypes[strings.ToLower(filepath.Ext(f.Path))]\n\t\tif !ok {\n\t\t\treturn 0, ErrWorkbookFileFormat\n\t\t}\n\t\tif err := f.setContentTypePartProjectExtensions(contentType); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\tbuf, err := f.WriteToBuffer()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn buf.WriteTo(w)\n}\n\n// WriteToBuffer provides a function to get bytes.Buffer from the saved file,\n// and it allocates space in memory. Be careful when the file size is large.\nfunc (f *File) WriteToBuffer() (*bytes.Buffer, error) {\n\tbuf := new(bytes.Buffer)\n\tzw := f.ZipWriter(buf)\n\n\tif err := f.writeToZip(zw); err != nil {\n\t\t_ = zw.Close()\n\t\treturn buf, err\n\t}\n\tif err := zw.Close(); err != nil {\n\t\treturn buf, err\n\t}\n\terr := f.writeZip64LFH(buf)\n\tif f.options != nil && f.options.Password != \"\" {\n\t\tb, err := Encrypt(buf.Bytes(), f.options)\n\t\tif err != nil {\n\t\t\treturn buf, err\n\t\t}\n\t\tbuf.Reset()\n\t\tbuf.Write(b)\n\t}\n\treturn buf, err\n}\n\n// writeToZip provides a function to write to ZipWriter.\nfunc (f *File) writeToZip(zw ZipWriter) error {\n\tf.calcChainWriter()\n\tf.commentsWriter()\n\tf.contentTypesWriter()\n\tf.drawingsWriter()\n\tf.volatileDepsWriter()\n\tf.vmlDrawingWriter()\n\tf.workBookWriter()\n\tf.workSheetWriter()\n\tf.relsWriter()\n\t_ = f.sharedStringsLoader()\n\tf.sharedStringsWriter()\n\tf.styleSheetWriter()\n\tf.themeWriter()\n\n\tfor path, stream := range f.streams {\n\t\tfi, err := zw.Create(path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar from io.Reader\n\t\tif from, err = stream.rawData.Reader(); err != nil {\n\t\t\t_ = stream.rawData.Close()\n\t\t\treturn err\n\t\t}\n\t\twritten, err := io.Copy(fi, from)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif written > math.MaxUint32 {\n\t\t\tf.zip64Entries = append(f.zip64Entries, path)\n\t\t}\n\t}\n\tvar (\n\t\tn                int\n\t\terr              error\n\t\tfiles, tempFiles []string\n\t)\n\tf.Pkg.Range(func(path, content interface{}) bool {\n\t\tif _, ok := f.streams[path.(string)]; ok {\n\t\t\treturn true\n\t\t}\n\t\tfiles = append(files, path.(string))\n\t\treturn true\n\t})\n\tsort.Sort(sort.Reverse(sort.StringSlice(files)))\n\tfor _, path := range files {\n\t\tvar fi io.Writer\n\t\tif fi, err = zw.Create(path); err != nil {\n\t\t\tbreak\n\t\t}\n\t\tcontent, _ := f.Pkg.Load(path)\n\t\tif n, err = fi.Write(content.([]byte)); int64(n) > math.MaxUint32 {\n\t\t\tf.zip64Entries = append(f.zip64Entries, path)\n\t\t}\n\t}\n\tf.tempFiles.Range(func(path, content interface{}) bool {\n\t\tif _, ok := f.Pkg.Load(path); ok {\n\t\t\treturn true\n\t\t}\n\t\ttempFiles = append(tempFiles, path.(string))\n\t\treturn true\n\t})\n\tsort.Sort(sort.Reverse(sort.StringSlice(tempFiles)))\n\tfor _, path := range tempFiles {\n\t\tvar fi io.Writer\n\t\tif fi, err = zw.Create(path); err != nil {\n\t\t\tbreak\n\t\t}\n\t\tif n, err = fi.Write(f.readBytes(path)); int64(n) > math.MaxUint32 {\n\t\t\tf.zip64Entries = append(f.zip64Entries, path)\n\t\t}\n\t}\n\treturn err\n}\n\n// writeZip64LFH function sets the ZIP version to 0x2D (45) in the Local File\n// Header (LFH). Excel strictly enforces ZIP64 format validation rules. When any\n// file within the workbook (OCP) exceeds 4GB in size, the ZIP64 format must be\n// used according to the PKZIP specification. However, ZIP files generated using\n// Go's standard archive/zip library always set the version in the local file\n// header to 20 (ZIP version 2.0) by default, as defined in the internal\n// 'writeHeader' function during ZIP creation. The archive/zip package only sets\n// the 'ReaderVersion' to 45 (ZIP64 version 4.5) in the central directory for\n// entries larger than 4GB. This results in a version mismatch between the\n// central directory and the local file header. As a result, opening the\n// generated workbook with spreadsheet application will prompt file corruption.\nfunc (f *File) writeZip64LFH(buf *bytes.Buffer) error {\n\tif len(f.zip64Entries) == 0 {\n\t\treturn nil\n\t}\n\tdata, offset := buf.Bytes(), 0\n\tfor offset < len(data) {\n\t\tidx := bytes.Index(data[offset:], []byte{0x50, 0x4b, 0x03, 0x04})\n\t\tif idx == -1 {\n\t\t\tbreak\n\t\t}\n\t\tidx += offset\n\t\tif idx+30 > len(data) {\n\t\t\tbreak\n\t\t}\n\t\tfilenameLen := int(binary.LittleEndian.Uint16(data[idx+26 : idx+28]))\n\t\tif idx+30+filenameLen > len(data) {\n\t\t\tbreak\n\t\t}\n\t\tif inStrSlice(f.zip64Entries, string(data[idx+30:idx+30+filenameLen]), true) != -1 {\n\t\t\tbinary.LittleEndian.PutUint16(data[idx+4:idx+6], 45)\n\t\t}\n\t\toffset = idx + 1\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "file_test.go",
    "content": "package excelize\n\nimport (\n\t\"archive/zip\"\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"io/fs\"\n\t\"math\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// errZipWriter is a mock ZipWriter whose Create and Close can be configured to\n// return errors.\ntype errZipWriter struct {\n\tcreateFunc func(string) (io.Writer, error)\n\tcloseErr   error\n}\n\nfunc (m *errZipWriter) Create(name string) (io.Writer, error) {\n\tif m.createFunc != nil {\n\t\treturn m.createFunc(name)\n\t}\n\treturn &bytes.Buffer{}, nil\n}\n\nfunc (m *errZipWriter) AddFS(fs.FS) error { return nil }\n\nfunc (m *errZipWriter) Close() error { return m.closeErr }\n\ntype errWriter struct{ err error }\n\nfunc (e *errWriter) Write([]byte) (int, error) { return 0, e.err }\n\nfunc BenchmarkWrite(b *testing.B) {\n\tconst s = \"This is test data\"\n\tfor b.Loop() {\n\t\tf := NewFile()\n\t\tfor row := 1; row <= 10000; row++ {\n\t\t\tfor col := 1; col <= 20; col++ {\n\t\t\t\tval, err := CoordinatesToCellName(col, row)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Error(err)\n\t\t\t\t}\n\t\t\t\tif err := f.SetCellValue(\"Sheet1\", val, s); err != nil {\n\t\t\t\t\tb.Error(err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Save spreadsheet by the given path.\n\t\tif err := f.SaveAs(\"test.xlsx\"); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc TestWriteTo(t *testing.T) {\n\t// Test WriteToBuffer err\n\t{\n\t\tf, buf := File{Pkg: sync.Map{}}, bytes.Buffer{}\n\t\tf.SetZipWriter(func(w io.Writer) ZipWriter { return zip.NewWriter(w) })\n\t\tf.Pkg.Store(\"/d/\", []byte(\"s\"))\n\t\t_, err := f.WriteTo(bufio.NewWriter(&buf))\n\t\tassert.EqualError(t, err, \"zip: write to directory\")\n\t\tf.Pkg.Delete(\"/d/\")\n\t}\n\t// Test file path overflow\n\t{\n\t\tf, buf := File{Pkg: sync.Map{}}, bytes.Buffer{}\n\t\tf.SetZipWriter(func(w io.Writer) ZipWriter { return zip.NewWriter(w) })\n\t\tconst maxUint16 = 1<<16 - 1\n\t\tf.Pkg.Store(strings.Repeat(\"s\", maxUint16+1), nil)\n\t\t_, err := f.WriteTo(bufio.NewWriter(&buf))\n\t\tassert.EqualError(t, err, \"zip: FileHeader.Name too long\")\n\t}\n\t// Test StreamsWriter err\n\t{\n\t\tf, buf := File{Pkg: sync.Map{}}, bytes.Buffer{}\n\t\tf.SetZipWriter(func(w io.Writer) ZipWriter { return zip.NewWriter(w) })\n\t\tf.Pkg.Store(\"s\", nil)\n\t\tf.streams = make(map[string]*StreamWriter)\n\t\tfile, _ := os.Open(\"123\")\n\t\tf.streams[\"s\"] = &StreamWriter{rawData: bufferedWriter{tmp: file}}\n\t\t_, err := f.WriteTo(bufio.NewWriter(&buf))\n\t\tassert.Nil(t, err)\n\t}\n\t// Test write with temporary file\n\t{\n\t\tf, buf := File{tempFiles: sync.Map{}}, bytes.Buffer{}\n\t\tf.SetZipWriter(func(w io.Writer) ZipWriter { return zip.NewWriter(w) })\n\t\tconst maxUint16 = 1<<16 - 1\n\t\tf.tempFiles.Store(\"s\", \"\")\n\t\tf.tempFiles.Store(strings.Repeat(\"s\", maxUint16+1), \"\")\n\t\t_, err := f.WriteTo(bufio.NewWriter(&buf))\n\t\tassert.EqualError(t, err, \"zip: FileHeader.Name too long\")\n\t}\n\t// Test write with unsupported workbook file format\n\t{\n\t\tf, buf := File{Pkg: sync.Map{}}, bytes.Buffer{}\n\t\tf.SetZipWriter(func(w io.Writer) ZipWriter { return zip.NewWriter(w) })\n\t\tf.Pkg.Store(\"/d\", []byte(\"s\"))\n\t\tf.Path = \"Book1.xls\"\n\t\t_, err := f.WriteTo(bufio.NewWriter(&buf))\n\t\tassert.EqualError(t, err, ErrWorkbookFileFormat.Error())\n\t}\n\t// Test write with unsupported charset content types.\n\t{\n\t\tf, buf := NewFile(), bytes.Buffer{}\n\t\tf.ContentTypes, f.Path = nil, filepath.Join(\"test\", \"TestWriteTo.xlsx\")\n\t\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\t\t_, err := f.WriteTo(bufio.NewWriter(&buf))\n\t\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t}\n\t// Test WriteToBuffer with ZipWriter Close error\n\t{\n\t\tf := NewFile()\n\t\tf.SetZipWriter(func(w io.Writer) ZipWriter {\n\t\t\treturn &errZipWriter{closeErr: errors.New(\"close error\")}\n\t\t})\n\t\t_, err := f.WriteTo(bufio.NewWriter(&bytes.Buffer{}))\n\t\tassert.EqualError(t, err, \"close error\")\n\t}\n\t// Test writeToZip with stream Create error\n\t{\n\t\tf := NewFile()\n\t\tf.streams = make(map[string]*StreamWriter)\n\t\tf.streams[\"s\"] = &StreamWriter{rawData: bufferedWriter{}}\n\t\tf.SetZipWriter(func(w io.Writer) ZipWriter {\n\t\t\treturn &errZipWriter{\n\t\t\t\tcreateFunc: func(name string) (io.Writer, error) {\n\t\t\t\t\tif name == \"s\" {\n\t\t\t\t\t\treturn nil, errors.New(\"create stream error\")\n\t\t\t\t\t}\n\t\t\t\t\treturn &bytes.Buffer{}, nil\n\t\t\t\t},\n\t\t\t}\n\t\t})\n\t\t_, err := f.WriteTo(bufio.NewWriter(&bytes.Buffer{}))\n\t\tassert.EqualError(t, err, \"create stream error\")\n\t}\n\t// Test writeToZip with stream rawData.Reader() error\n\t{\n\t\tf := NewFile()\n\t\tf.streams = make(map[string]*StreamWriter)\n\t\ttmp, err := os.CreateTemp(\"\", \"excelize-test-*\")\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, tmp.Close())\n\t\tf.streams[\"s\"] = &StreamWriter{rawData: bufferedWriter{tmp: tmp}}\n\t\t_, err = f.WriteTo(bufio.NewWriter(&bytes.Buffer{}))\n\t\tassert.Error(t, err)\n\t}\n\t// Test writeToZip with io.Copy error on stream\n\t{\n\t\tf := NewFile()\n\t\tf.streams = make(map[string]*StreamWriter)\n\t\tsw := &StreamWriter{}\n\t\tsw.rawData.buf.WriteString(\"test data\")\n\t\tf.streams[\"s\"] = sw\n\t\tf.SetZipWriter(func(w io.Writer) ZipWriter {\n\t\t\treturn &errZipWriter{\n\t\t\t\tcreateFunc: func(name string) (io.Writer, error) {\n\t\t\t\t\tif name == \"s\" {\n\t\t\t\t\t\treturn &errWriter{err: errors.New(\"copy error\")}, nil\n\t\t\t\t\t}\n\t\t\t\t\treturn &bytes.Buffer{}, nil\n\t\t\t\t},\n\t\t\t}\n\t\t})\n\t\t_, err := f.WriteTo(bufio.NewWriter(&bytes.Buffer{}))\n\t\tassert.EqualError(t, err, \"copy error\")\n\t}\n}\n\nfunc TestClose(t *testing.T) {\n\tf := NewFile()\n\tf.tempFiles.Store(\"/d/\", \"/d/\")\n\trequire.Error(t, f.Close())\n}\n\nfunc TestZip64(t *testing.T) {\n\tf := NewFile()\n\t_, err := f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tsw, err := f.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tfor r := range 131 {\n\t\trowData := make([]interface{}, 1000)\n\t\tfor c := range 1000 {\n\t\t\trowData[c] = strings.Repeat(\"c\", TotalCellChars)\n\t\t}\n\t\tcell, err := CoordinatesToCellName(1, r+1)\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, sw.SetRow(cell, rowData))\n\t}\n\tassert.NoError(t, sw.Flush())\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestZip64.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\t// Test with filename length overflow\n\tf = NewFile()\n\tf.zip64Entries = append(f.zip64Entries, defaultXMLPathSharedStrings)\n\tbuf := new(bytes.Buffer)\n\tbuf.Write([]byte{0x50, 0x4b, 0x03, 0x04})\n\tbuf.Write(make([]byte, 20))\n\tassert.NoError(t, f.writeZip64LFH(buf))\n\n\t// Test with file header less than the required 30 for the fixed header part\n\tf = NewFile()\n\tf.zip64Entries = append(f.zip64Entries, defaultXMLPathSharedStrings)\n\tbuf.Reset()\n\tbuf.Write([]byte{0x50, 0x4b, 0x03, 0x04})\n\tbuf.Write(make([]byte, 22))\n\tassert.NoError(t, binary.Write(buf, binary.LittleEndian, uint16(10)))\n\tbuf.Write(make([]byte, 2))\n\tbuf.WriteString(\"test\")\n\tassert.NoError(t, f.writeZip64LFH(buf))\n\n\tt.Run(\"for_save_zip64_with_in_memory_file_over_4GB\", func(t *testing.T) {\n\t\t// Test save workbook in ZIP64 format with in memory file with size over 4GB.\n\t\tf := NewFile()\n\t\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\t\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", make([]byte, math.MaxUint32+1))\n\t\t_, err := f.WriteToBuffer()\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.Close())\n\t})\n\n\tt.Run(\"for_save_zip64_with_in_temporary_file_over_4GB\", func(t *testing.T) {\n\t\t// Test save workbook in ZIP64 format with temporary file with size over 4GB.\n\t\tif os.Getenv(\"GITHUB_ACTIONS\") == \"true\" {\n\t\t\tt.Skip()\n\t\t}\n\t\tf := NewFile()\n\t\tf.Pkg.Delete(\"xl/worksheets/sheet1.xml\")\n\t\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\t\ttmp, err := os.CreateTemp(os.TempDir(), \"excelize-\")\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, tmp.Truncate(math.MaxUint32+1))\n\t\tf.tempFiles.Store(\"xl/worksheets/sheet1.xml\", tmp.Name())\n\t\tassert.NoError(t, tmp.Close())\n\t\t_, err = f.WriteToBuffer()\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.Close())\n\t})\n}\n\nfunc TestRemoveTempFiles(t *testing.T) {\n\ttmp, err := os.CreateTemp(\"\", \"excelize-*\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttmpName := tmp.Name()\n\tassert.NoError(t, tmp.Close())\n\tf := NewFile()\n\t// Fill the tempFiles map with non-existing files\n\tfor i := range 1000 {\n\t\tf.tempFiles.Store(strconv.Itoa(i), \"/hopefully not existing\")\n\t}\n\tf.tempFiles.Store(\"existing\", tmpName)\n\n\trequire.Error(t, f.Close())\n\tif _, err := os.Stat(tmpName); err == nil {\n\t\tt.Errorf(\"temp file %q still exist\", tmpName)\n\t\tassert.NoError(t, os.Remove(tmpName))\n\t}\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/xuri/excelize/v2\n\ngo 1.25.0\n\nrequire (\n\tgithub.com/richardlehane/mscfb v1.0.6\n\tgithub.com/stretchr/testify v1.11.1\n\tgithub.com/tiendc/go-deepcopy v1.7.2\n\tgithub.com/xuri/efp v0.0.1\n\tgithub.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9\n\tgolang.org/x/crypto v0.49.0\n\tgolang.org/x/image v0.25.0\n\tgolang.org/x/net v0.52.0\n\tgolang.org/x/text v0.35.0\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/richardlehane/msoleps v1.0.6 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/richardlehane/mscfb v1.0.6 h1:eN3bvvZCp00bs7Zf52bxNwAx5lJDBK1tCuH19qq5aC8=\ngithub.com/richardlehane/mscfb v1.0.6/go.mod h1:pe0+IUIc0AHh0+teNzBlJCtSyZdFOGgV4ZK9bsoV+Jo=\ngithub.com/richardlehane/msoleps v1.0.6 h1:9BvkpjvD+iUBalUY4esMwv6uBkfOip/Lzvd93jvR9gg=\ngithub.com/richardlehane/msoleps v1.0.6/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrIj44=\ngithub.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=\ngithub.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=\ngithub.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=\ngithub.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=\ngithub.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=\ngolang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=\ngolang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=\ngolang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=\ngolang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=\ngolang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=\ngolang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=\ngolang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=\ngolang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "hsl.go",
    "content": "// Copyright (c) 2012 Rodrigo Moraes. All rights reserved.\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n// \t * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n// \t * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n// \t * Neither the name of Google Inc. nor the names of its\n// contributors may be used to endorse or promote products derived from\n// this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\npackage excelize\n\nimport (\n\t\"image/color\"\n\t\"math\"\n)\n\n// HSLModel converts any color.Color to a HSL color.\nvar HSLModel = color.ModelFunc(hslModel)\n\n// HSL represents a cylindrical coordinate of points in an RGB color model.\n//\n// Values are in the range 0 to 1.\ntype HSL struct {\n\tH, S, L float64\n}\n\n// RGBA returns the alpha-premultiplied red, green, blue and alpha values\n// for the HSL.\nfunc (c HSL) RGBA() (uint32, uint32, uint32, uint32) {\n\tr, g, b := HSLToRGB(c.H, c.S, c.L)\n\treturn uint32(r) * 0x101, uint32(g) * 0x101, uint32(b) * 0x101, 0xffff\n}\n\n// hslModel converts a color.Color to HSL.\nfunc hslModel(c color.Color) color.Color {\n\tif _, ok := c.(HSL); ok {\n\t\treturn c\n\t}\n\tr, g, b, _ := c.RGBA()\n\th, s, l := RGBToHSL(uint8(r>>8), uint8(g>>8), uint8(b>>8))\n\treturn HSL{h, s, l}\n}\n\n// RGBToHSL converts an RGB triple to an HSL triple.\nfunc RGBToHSL(r, g, b uint8) (h, s, l float64) {\n\tfR := float64(r) / 255\n\tfG := float64(g) / 255\n\tfB := float64(b) / 255\n\tmaxVal := math.Max(math.Max(fR, fG), fB)\n\tminVal := math.Min(math.Min(fR, fG), fB)\n\tl = (maxVal + minVal) / 2\n\tif maxVal == minVal {\n\t\t// Achromatic.\n\t\th, s = 0, 0\n\t} else {\n\t\t// Chromatic.\n\t\td := maxVal - minVal\n\t\tif l > 0.5 {\n\t\t\ts = d / (2.0 - maxVal - minVal)\n\t\t} else {\n\t\t\ts = d / (maxVal + minVal)\n\t\t}\n\t\tswitch maxVal {\n\t\tcase fR:\n\t\t\th = (fG - fB) / d\n\t\t\tif fG < fB {\n\t\t\t\th += 6\n\t\t\t}\n\t\tcase fG:\n\t\t\th = (fB-fR)/d + 2\n\t\tcase fB:\n\t\t\th = (fR-fG)/d + 4\n\t\t}\n\t\th /= 6\n\t}\n\treturn\n}\n\n// HSLToRGB converts an HSL triple to an RGB triple.\nfunc HSLToRGB(h, s, l float64) (r, g, b uint8) {\n\tvar fR, fG, fB float64\n\tif s == 0 {\n\t\tfR, fG, fB = l, l, l\n\t} else {\n\t\tvar q float64\n\t\tif l < 0.5 {\n\t\t\tq = l * (1 + s)\n\t\t} else {\n\t\t\tq = l + s - s*l\n\t\t}\n\t\tp := 2*l - q\n\t\tfR = hueToRGB(p, q, h+1.0/3)\n\t\tfG = hueToRGB(p, q, h)\n\t\tfB = hueToRGB(p, q, h-1.0/3)\n\t}\n\tr = uint8((fR * 255) + 0.5)\n\tg = uint8((fG * 255) + 0.5)\n\tb = uint8((fB * 255) + 0.5)\n\treturn\n}\n\n// hueToRGB is a helper function for HSLToRGB.\nfunc hueToRGB(p, q, t float64) float64 {\n\tif t < 0 {\n\t\tt++\n\t}\n\tif t > 1 {\n\t\tt--\n\t}\n\tif t < 1.0/6 {\n\t\treturn p + (q-p)*6*t\n\t}\n\tif t < 0.5 {\n\t\treturn q\n\t}\n\tif t < 2.0/3 {\n\t\treturn p + (q-p)*(2.0/3-t)*6\n\t}\n\treturn p\n}\n"
  },
  {
    "path": "lib.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"archive/zip\"\n\t\"bytes\"\n\t\"container/list\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"math/big\"\n\t\"os\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode/utf16\"\n)\n\n// ReadZipReader extract spreadsheet with given options.\nfunc (f *File) ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {\n\tvar (\n\t\terr     error\n\t\tdocPart = map[string]string{\n\t\t\t\"[content_types].xml\":  defaultXMLPathContentTypes,\n\t\t\t\"xl/sharedstrings.xml\": defaultXMLPathSharedStrings,\n\t\t}\n\t\tfileList   = make(map[string][]byte, len(r.File))\n\t\tworksheets int\n\t\tunzipSize  int64\n\t)\n\tfor _, v := range r.File {\n\t\tfileSize := v.FileInfo().Size()\n\t\tunzipSize += fileSize\n\t\tif unzipSize > f.options.UnzipSizeLimit {\n\t\t\treturn fileList, worksheets, newUnzipSizeLimitError(f.options.UnzipSizeLimit)\n\t\t}\n\t\tfileName := strings.ReplaceAll(v.Name, \"\\\\\", \"/\")\n\t\tif partName, ok := docPart[strings.ToLower(fileName)]; ok {\n\t\t\tfileName = partName\n\t\t}\n\t\tif strings.EqualFold(fileName, defaultXMLPathSharedStrings) && fileSize > f.options.UnzipXMLSizeLimit {\n\t\t\ttempFile, err := f.unzipToTemp(v)\n\t\t\tif tempFile != \"\" {\n\t\t\t\tf.tempFiles.Store(fileName, tempFile)\n\t\t\t}\n\t\t\tif err == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tif strings.HasPrefix(strings.ToLower(fileName), \"xl/worksheets/sheet\") {\n\t\t\tworksheets++\n\t\t\tif fileSize > f.options.UnzipXMLSizeLimit && !v.FileInfo().IsDir() {\n\t\t\t\ttempFile, err := f.unzipToTemp(v)\n\t\t\t\tif tempFile != \"\" {\n\t\t\t\t\tf.tempFiles.Store(fileName, tempFile)\n\t\t\t\t}\n\t\t\t\tif err == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif fileList[fileName], err = readFile(v); err != nil {\n\t\t\treturn nil, 0, err\n\t\t}\n\t}\n\treturn fileList, worksheets, nil\n}\n\n// unzipToTemp unzip the zip entity to the system temporary directory and\n// returned the unzipped file path.\nfunc (f *File) unzipToTemp(zipFile *zip.File) (string, error) {\n\ttmp, err := os.CreateTemp(f.options.TmpDir, \"excelize-\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\trc, err := zipFile.Open()\n\tif err != nil {\n\t\treturn tmp.Name(), err\n\t}\n\tif _, err = io.Copy(tmp, rc); err != nil {\n\t\treturn tmp.Name(), err\n\t}\n\tif err = rc.Close(); err != nil {\n\t\treturn tmp.Name(), err\n\t}\n\treturn tmp.Name(), tmp.Close()\n}\n\n// readXML provides a function to read XML content as bytes.\nfunc (f *File) readXML(name string) []byte {\n\tif content, _ := f.Pkg.Load(name); content != nil {\n\t\treturn content.([]byte)\n\t}\n\tif content, ok := f.streams[name]; ok {\n\t\treturn content.rawData.buf.Bytes()\n\t}\n\treturn []byte{}\n}\n\n// readBytes read file as bytes by given path.\nfunc (f *File) readBytes(name string) []byte {\n\tcontent := f.readXML(name)\n\tif len(content) != 0 {\n\t\treturn content\n\t}\n\tfile, err := f.readTemp(name)\n\tif err != nil {\n\t\treturn content\n\t}\n\tcontent, _ = io.ReadAll(file)\n\tf.Pkg.Store(name, content)\n\t_ = file.Close()\n\treturn content\n}\n\n// readTemp read file from system temporary directory by given path.\nfunc (f *File) readTemp(name string) (file *os.File, err error) {\n\tpath, ok := f.tempFiles.Load(name)\n\tif !ok {\n\t\treturn\n\t}\n\tfile, err = os.Open(path.(string))\n\treturn\n}\n\n// saveFileList provides a function to update given file content in file list\n// of spreadsheet.\nfunc (f *File) saveFileList(name string, content []byte) {\n\tf.Pkg.Store(name, append([]byte(xml.Header), content...))\n}\n\n// Read file content as string in an archive file.\nfunc readFile(file *zip.File) ([]byte, error) {\n\trc, err := file.Open()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdat := make([]byte, 0, file.FileInfo().Size())\n\tbuff := bytes.NewBuffer(dat)\n\t_, _ = io.Copy(buff, rc)\n\treturn buff.Bytes(), rc.Close()\n}\n\n// SplitCellName splits cell name to column name and row number.\n//\n// Example:\n//\n//\texcelize.SplitCellName(\"AK74\") // return \"AK\", 74, nil\nfunc SplitCellName(cell string) (string, int, error) {\n\talpha := func(r rune) bool {\n\t\treturn ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') || (r == 36)\n\t}\n\tif strings.IndexFunc(cell, alpha) == 0 {\n\t\ti := strings.LastIndexFunc(cell, alpha)\n\t\tif i >= 0 && i < len(cell)-1 {\n\t\t\tcol, rowStr := strings.ReplaceAll(cell[:i+1], \"$\", \"\"), cell[i+1:]\n\t\t\tif row, err := strconv.Atoi(rowStr); err == nil && row > 0 {\n\t\t\t\treturn col, row, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\", -1, newInvalidCellNameError(cell)\n}\n\n// JoinCellName joins cell name from column name and row number.\nfunc JoinCellName(col string, row int) (string, error) {\n\tnormCol := strings.Map(func(rune rune) rune {\n\t\tswitch {\n\t\tcase 'A' <= rune && rune <= 'Z':\n\t\t\treturn rune\n\t\tcase 'a' <= rune && rune <= 'z':\n\t\t\treturn rune - 32\n\t\t}\n\t\treturn -1\n\t}, col)\n\tif len(col) == 0 || len(col) != len(normCol) {\n\t\treturn \"\", newInvalidColumnNameError(col)\n\t}\n\tif row < 1 {\n\t\treturn \"\", newInvalidRowNumberError(row)\n\t}\n\treturn normCol + strconv.Itoa(row), nil\n}\n\n// ColumnNameToNumber provides a function to convert Excel sheet column name\n// (case-insensitive) to int. The function returns an error if column name\n// incorrect.\n//\n// Example:\n//\n//\texcelize.ColumnNameToNumber(\"AK\") // returns 37, nil\nfunc ColumnNameToNumber(name string) (int, error) {\n\tif len(name) == 0 {\n\t\treturn -1, newInvalidColumnNameError(name)\n\t}\n\tcol := 0\n\tmulti := 1\n\tfor i := len(name) - 1; i >= 0; i-- {\n\t\tr := name[i]\n\t\tif r >= 'A' && r <= 'Z' {\n\t\t\tcol += int(r-'A'+1) * multi\n\t\t} else if r >= 'a' && r <= 'z' {\n\t\t\tcol += int(r-'a'+1) * multi\n\t\t} else {\n\t\t\treturn -1, newInvalidColumnNameError(name)\n\t\t}\n\t\tmulti *= 26\n\t}\n\tif col > MaxColumns {\n\t\treturn -1, ErrColumnNumber\n\t}\n\treturn col, nil\n}\n\n// ColumnNumberToName provides a function to convert the integer to Excel\n// sheet column title.\n//\n// Example:\n//\n//\texcelize.ColumnNumberToName(37) // returns \"AK\", nil\nfunc ColumnNumberToName(num int) (string, error) {\n\tif num < MinColumns || num > MaxColumns {\n\t\treturn \"\", ErrColumnNumber\n\t}\n\testimatedLength := 0\n\tfor n := num; n > 0; n = (n - 1) / 26 {\n\t\testimatedLength++\n\t}\n\n\tresult := make([]byte, estimatedLength)\n\tfor num > 0 {\n\t\testimatedLength--\n\t\tresult[estimatedLength] = byte((num-1)%26 + 'A')\n\t\tnum = (num - 1) / 26\n\t}\n\treturn string(result), nil\n}\n\n// CellNameToCoordinates converts alphanumeric cell name to [X, Y] coordinates\n// or returns an error.\n//\n// Example:\n//\n//\texcelize.CellNameToCoordinates(\"A1\") // returns 1, 1, nil\n//\texcelize.CellNameToCoordinates(\"Z3\") // returns 26, 3, nil\nfunc CellNameToCoordinates(cell string) (int, int, error) {\n\tcolName, row, err := SplitCellName(cell)\n\tif err != nil {\n\t\treturn -1, -1, newCellNameToCoordinatesError(cell, err)\n\t}\n\tif row > TotalRows {\n\t\treturn -1, -1, ErrMaxRows\n\t}\n\tcol, err := ColumnNameToNumber(colName)\n\treturn col, row, err\n}\n\n// CoordinatesToCellName converts [X, Y] coordinates to alpha-numeric cell\n// name or returns an error.\n//\n// Example:\n//\n//\texcelize.CoordinatesToCellName(1, 1) // returns \"A1\", nil\n//\texcelize.CoordinatesToCellName(1, 1, true) // returns \"$A$1\", nil\nfunc CoordinatesToCellName(col, row int, abs ...bool) (string, error) {\n\tif col < 1 || row < 1 {\n\t\treturn \"\", newCoordinatesToCellNameError(col, row)\n\t}\n\tif row > TotalRows {\n\t\treturn \"\", ErrMaxRows\n\t}\n\tsign := \"\"\n\tfor _, a := range abs {\n\t\tif a {\n\t\t\tsign = \"$\"\n\t\t}\n\t}\n\tcolName, err := ColumnNumberToName(col)\n\treturn sign + colName + sign + strconv.Itoa(row), err\n}\n\n// rangeRefToCoordinates provides a function to convert range reference to a\n// pair of coordinates.\nfunc rangeRefToCoordinates(ref string) ([]int, error) {\n\trng := strings.Split(strings.ReplaceAll(ref, \"$\", \"\"), \":\")\n\tif len(rng) < 2 {\n\t\treturn nil, ErrParameterInvalid\n\t}\n\treturn cellRefsToCoordinates(rng[0], rng[1])\n}\n\n// cellRefsToCoordinates provides a function to convert cell range to a\n// pair of coordinates.\nfunc cellRefsToCoordinates(firstCell, lastCell string) ([]int, error) {\n\tcoordinates := make([]int, 4)\n\tvar err error\n\tcoordinates[0], coordinates[1], err = CellNameToCoordinates(firstCell)\n\tif err != nil {\n\t\treturn coordinates, err\n\t}\n\tcoordinates[2], coordinates[3], err = CellNameToCoordinates(lastCell)\n\treturn coordinates, err\n}\n\n// sortCoordinates provides a function to correct the cell range, such\n// correct C1:B3 to B1:C3.\nfunc sortCoordinates(coordinates []int) error {\n\tif len(coordinates) != 4 {\n\t\treturn ErrCoordinates\n\t}\n\tif coordinates[2] < coordinates[0] {\n\t\tcoordinates[2], coordinates[0] = coordinates[0], coordinates[2]\n\t}\n\tif coordinates[3] < coordinates[1] {\n\t\tcoordinates[3], coordinates[1] = coordinates[1], coordinates[3]\n\t}\n\treturn nil\n}\n\n// coordinatesToRangeRef provides a function to convert a pair of coordinates\n// to range reference.\nfunc coordinatesToRangeRef(coordinates []int, abs ...bool) (string, error) {\n\tif len(coordinates) != 4 {\n\t\treturn \"\", ErrCoordinates\n\t}\n\tfirstCell, err := CoordinatesToCellName(coordinates[0], coordinates[1], abs...)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tlastCell, err := CoordinatesToCellName(coordinates[2], coordinates[3], abs...)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn firstCell + \":\" + lastCell, err\n}\n\n// getDefinedNameRefTo convert defined name to reference range.\nfunc (f *File) getDefinedNameRefTo(definedNameName, currentSheet string) (refTo string) {\n\tvar workbookRefTo, worksheetRefTo string\n\tfor _, definedName := range f.GetDefinedName() {\n\t\tif definedName.Name == definedNameName {\n\t\t\t// worksheet scope takes precedence over scope workbook when both definedNames exist\n\t\t\tif definedName.Scope == \"Workbook\" {\n\t\t\t\tworkbookRefTo = definedName.RefersTo\n\t\t\t}\n\t\t\tif definedName.Scope == currentSheet {\n\t\t\t\tworksheetRefTo = definedName.RefersTo\n\t\t\t}\n\t\t}\n\t}\n\trefTo = workbookRefTo\n\tif worksheetRefTo != \"\" {\n\t\trefTo = worksheetRefTo\n\t}\n\treturn\n}\n\n// flatSqref convert reference sequence to cell reference list.\nfunc flatSqref(sqref string) (cells map[int][][]int, err error) {\n\tvar coordinates []int\n\tcells = make(map[int][][]int)\n\tfor _, ref := range strings.Fields(sqref) {\n\t\trng := strings.Split(ref, \":\")\n\t\tswitch len(rng) {\n\t\tcase 1:\n\t\t\tvar col, row int\n\t\t\tcol, row, err = CellNameToCoordinates(rng[0])\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcells[col] = append(cells[col], []int{col, row})\n\t\tcase 2:\n\t\t\tif coordinates, err = rangeRefToCoordinates(ref); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t_ = sortCoordinates(coordinates)\n\t\t\tfor c := coordinates[0]; c <= coordinates[2]; c++ {\n\t\t\t\tfor r := coordinates[1]; r <= coordinates[3]; r++ {\n\t\t\t\t\tcells[c] = append(cells[c], []int{c, r})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// Sort each column's cells by row number\n\tfor col := range cells {\n\t\tsort.Slice(cells[col], func(i, j int) bool {\n\t\t\treturn cells[col][i][1] < cells[col][j][1]\n\t\t})\n\t}\n\treturn\n}\n\n// inCoordinates provides a method to check if a coordinate is present in\n// coordinates array, and return the index of its location, otherwise\n// return -1.\nfunc inCoordinates(a [][]int, x []int) int {\n\tfor idx, n := range a {\n\t\tif x[0] == n[0] && x[1] == n[1] {\n\t\t\treturn idx\n\t\t}\n\t}\n\treturn -1\n}\n\n// inStrSlice provides a method to check if an element is present in an array,\n// and return the index of its location, otherwise return -1.\nfunc inStrSlice(a []string, x string, caseSensitive bool) int {\n\tfor idx, n := range a {\n\t\tif !caseSensitive && strings.EqualFold(x, n) {\n\t\t\treturn idx\n\t\t}\n\t\tif x == n {\n\t\t\treturn idx\n\t\t}\n\t}\n\treturn -1\n}\n\n// inFloat64Slice provides a method to check if an element is present in a\n// float64 array, and return the index of its location, otherwise return -1.\nfunc inFloat64Slice(a []float64, x float64) int {\n\tfor idx, n := range a {\n\t\tif x == n {\n\t\t\treturn idx\n\t\t}\n\t}\n\treturn -1\n}\n\n// boolPtr returns a pointer to a bool with the given value.\nfunc boolPtr(b bool) *bool { return &b }\n\n// intPtr returns a pointer to an int with the given value.\nfunc intPtr(i int) *int { return &i }\n\n// uintPtr returns a pointer to an unsigned integer with the given value.\nfunc uintPtr(u uint) *uint { return &u }\n\n// float64Ptr returns a pointer to a float64 with the given value.\nfunc float64Ptr(f float64) *float64 { return &f }\n\n// stringPtr returns a pointer to a string with the given value.\nfunc stringPtr(s string) *string { return &s }\n\n// Value extracts float64 data type numeric from a attribute value.\nfunc (attr *attrValFloat) Value() float64 {\n\tif attr != nil && attr.Val != nil {\n\t\treturn *attr.Val\n\t}\n\treturn 0\n}\n\n// Value extracts boolean data type value from a attribute value.\nfunc (avb *attrValBool) Value() bool {\n\tif avb != nil && avb.Val != nil {\n\t\treturn *avb.Val\n\t}\n\treturn false\n}\n\n// Value extracts string data type text from a attribute value.\nfunc (avb *attrValString) Value() string {\n\tif avb != nil && avb.Val != nil {\n\t\treturn *avb.Val\n\t}\n\treturn \"\"\n}\n\n// MarshalXML convert the boolean data type to literal values 0 or 1 on\n// serialization.\nfunc (avb attrValBool) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\tattr := xml.Attr{\n\t\tName: xml.Name{\n\t\t\tSpace: start.Name.Space,\n\t\t\tLocal: \"val\",\n\t\t},\n\t\tValue: \"0\",\n\t}\n\tif avb.Val != nil {\n\t\tif *avb.Val {\n\t\t\tattr.Value = \"1\"\n\t\t} else {\n\t\t\tattr.Value = \"0\"\n\t\t}\n\t}\n\tstart.Attr = []xml.Attr{attr}\n\tif err := e.EncodeToken(start); err != nil {\n\t\treturn err\n\t}\n\treturn e.EncodeToken(start.End())\n}\n\n// UnmarshalXML convert the literal values true, false, 1, 0 of the XML\n// attribute to boolean data type on deserialization.\nfunc (avb *attrValBool) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tfor {\n\t\tt, err := d.Token()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfound := false\n\t\tswitch t.(type) {\n\t\tcase xml.StartElement:\n\t\t\treturn ErrAttrValBool\n\t\tcase xml.EndElement:\n\t\t\tfound = true\n\t\t}\n\t\tif found {\n\t\t\tbreak\n\t\t}\n\t}\n\tfor _, attr := range start.Attr {\n\t\tif attr.Name.Local == \"val\" {\n\t\t\tif attr.Value == \"\" {\n\t\t\t\tval := true\n\t\t\t\tavb.Val = &val\n\t\t\t} else {\n\t\t\t\tval, err := strconv.ParseBool(attr.Value)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tavb.Val = &val\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n\tdefaultVal := true\n\tavb.Val = &defaultVal\n\treturn nil\n}\n\n// MarshalXML encodes ext element with specified namespace attributes on\n// serialization.\nfunc (ext xlsxExt) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\tstart.Attr = ext.xmlns\n\treturn e.EncodeElement(decodeExt{URI: ext.URI, Content: ext.Content}, start)\n}\n\n// UnmarshalXML extracts ext element attributes namespace by giving XML decoder\n// on deserialization.\nfunc (ext *xlsxExt) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tfor _, attr := range start.Attr {\n\t\tif attr.Name.Local == \"uri\" {\n\t\t\tcontinue\n\t\t}\n\t\tif attr.Name.Space == \"xmlns\" {\n\t\t\tattr.Name.Space = \"\"\n\t\t\tattr.Name.Local = \"xmlns:\" + attr.Name.Local\n\t\t}\n\t\text.xmlns = append(ext.xmlns, attr)\n\t}\n\te := &decodeExt{}\n\tif err := d.DecodeElement(&e, &start); err != nil {\n\t\treturn err\n\t}\n\text.URI, ext.Content = e.URI, e.Content\n\treturn nil\n}\n\n// namespaceStrictToTransitional provides a method to convert Strict and\n// Transitional namespaces.\nfunc namespaceStrictToTransitional(content []byte) []byte {\n\tnamespaceTranslationDic := map[string]string{\n\t\tStrictNameSpaceDocumentPropertiesVariantTypes: NameSpaceDocumentPropertiesVariantTypes.Value,\n\t\tStrictNameSpaceDrawingMLMain:                  NameSpaceDrawingMLMain,\n\t\tStrictNameSpaceExtendedProperties:             NameSpaceExtendedProperties,\n\t\tStrictNameSpaceSpreadSheet:                    NameSpaceSpreadSheet.Value,\n\t\tStrictSourceRelationship:                      SourceRelationship.Value,\n\t\tStrictSourceRelationshipChart:                 SourceRelationshipChart,\n\t\tStrictSourceRelationshipComments:              SourceRelationshipComments,\n\t\tStrictSourceRelationshipExtendProperties:      SourceRelationshipExtendProperties,\n\t\tStrictSourceRelationshipImage:                 SourceRelationshipImage,\n\t\tStrictSourceRelationshipOfficeDocument:        SourceRelationshipOfficeDocument,\n\t}\n\tfor s, n := range namespaceTranslationDic {\n\t\tcontent = bytesReplace(content, []byte(s), []byte(n), -1)\n\t}\n\treturn content\n}\n\n// bytesReplace replace source bytes with given target.\nfunc bytesReplace(s, source, target []byte, n int) []byte {\n\tif n == 0 {\n\t\treturn s\n\t}\n\n\tif len(source) < len(target) {\n\t\treturn bytes.Replace(s, source, target, n)\n\t}\n\n\tif n < 0 {\n\t\tn = len(s)\n\t}\n\n\tvar wid, i, j, w int\n\tfor i, j = 0, 0; i < len(s) && j < n; j++ {\n\t\twid = bytes.Index(s[i:], source)\n\t\tif wid < 0 {\n\t\t\tbreak\n\t\t}\n\n\t\tw += copy(s[w:], s[i:i+wid])\n\t\tw += copy(s[w:], target)\n\t\ti += wid + len(source)\n\t}\n\n\tw += copy(s[w:], s[i:])\n\treturn s[:w]\n}\n\n// genSheetPasswd provides a method to generate password for worksheet\n// protection by given plaintext. When an Excel sheet is being protected with\n// a password, a 16-bit (two byte) long hash is generated. To verify a\n// password, it is compared to the hash. Obviously, if the input data volume\n// is great, numerous passwords will match the same hash. Here is the\n// algorithm to create the hash value:\n//\n// take the ASCII values of all characters shift left the first character 1 bit,\n// the second 2 bits and so on (use only the lower 15 bits and rotate all higher bits,\n// the highest bit of the 16-bit value is always 0 [signed short])\n// XOR all these values\n// XOR the count of characters\n// XOR the constant 0xCE4B\nfunc genSheetPasswd(plaintext string) string {\n\tvar password int64 = 0x0000\n\tvar charPos uint = 1\n\tfor _, v := range plaintext {\n\t\tvalue := int64(v) << charPos\n\t\tcharPos++\n\t\trotatedBits := value >> 15 // rotated bits beyond bit 15\n\t\tvalue &= 0x7fff            // first 15 bits\n\t\tpassword ^= value | rotatedBits\n\t}\n\tpassword ^= int64(len(plaintext))\n\tpassword ^= 0xCE4B\n\treturn strings.ToUpper(strconv.FormatInt(password, 16))\n}\n\n// getRootElement extract root element attributes by given XML decoder.\nfunc getRootElement(d *xml.Decoder) []xml.Attr {\n\ttokenIdx := 0\n\tfor {\n\t\ttoken, _ := d.Token()\n\t\tif token == nil {\n\t\t\tbreak\n\t\t}\n\t\tswitch startElement := token.(type) {\n\t\tcase xml.StartElement:\n\t\t\ttokenIdx++\n\t\t\tif tokenIdx == 1 {\n\t\t\t\tvar ns bool\n\t\t\t\tfor i := 0; i < len(startElement.Attr); i++ {\n\t\t\t\t\tif startElement.Attr[i].Value == NameSpaceSpreadSheet.Value &&\n\t\t\t\t\t\tstartElement.Attr[i].Name == NameSpaceSpreadSheet.Name {\n\t\t\t\t\t\tns = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !ns {\n\t\t\t\t\tstartElement.Attr = append(startElement.Attr, NameSpaceSpreadSheet)\n\t\t\t\t}\n\t\t\t\treturn startElement.Attr\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// genXMLNamespace generate serialized XML attributes with a multi namespace\n// by given element attributes.\nfunc genXMLNamespace(attr []xml.Attr) string {\n\tvar rootElement string\n\tfor _, v := range attr {\n\t\tif lastSpace := getXMLNamespace(v.Name.Space, attr); lastSpace != \"\" {\n\t\t\tif lastSpace == NameSpaceXML {\n\t\t\t\tlastSpace = \"xml\"\n\t\t\t}\n\t\t\trootElement += fmt.Sprintf(\"%s:%s=\\\"%s\\\" \", lastSpace, v.Name.Local, v.Value)\n\t\t\tcontinue\n\t\t}\n\t\trootElement += fmt.Sprintf(\"%s=\\\"%s\\\" \", v.Name.Local, v.Value)\n\t}\n\treturn strings.TrimSpace(rootElement) + \">\"\n}\n\n// getXMLNamespace extract XML namespace from specified element name and attributes.\nfunc getXMLNamespace(space string, attr []xml.Attr) string {\n\tfor _, attribute := range attr {\n\t\tif attribute.Value == space {\n\t\t\treturn attribute.Name.Local\n\t\t}\n\t}\n\treturn space\n}\n\n// replaceNameSpaceBytes provides a function to replace the XML root element\n// attribute by the given component part path and XML content.\nfunc (f *File) replaceNameSpaceBytes(path string, contentMarshal []byte) []byte {\n\tsourceXmlns := []byte(`xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">`)\n\ttargetXmlns := []byte(templateNamespaceIDMap)\n\tif attrs, ok := f.xmlAttr.Load(path); ok {\n\t\ttargetXmlns = []byte(genXMLNamespace(attrs.([]xml.Attr)))\n\t}\n\treturn bytesReplace(contentMarshal, sourceXmlns, bytes.ReplaceAll(targetXmlns, []byte(\" mc:Ignorable=\\\"r\\\"\"), []byte{}), -1)\n}\n\n// addNameSpaces provides a function to add an XML attribute by the given\n// component part path.\nfunc (f *File) addNameSpaces(path string, ns xml.Attr) {\n\texist := false\n\tmc := false\n\tignore := -1\n\tif attrs, ok := f.xmlAttr.Load(path); ok {\n\t\tfor i, attr := range attrs.([]xml.Attr) {\n\t\t\tif attr.Name.Local == ns.Name.Local && attr.Name.Space == ns.Name.Space {\n\t\t\t\texist = true\n\t\t\t}\n\t\t\tif attr.Name.Local == \"Ignorable\" && getXMLNamespace(attr.Name.Space, attrs.([]xml.Attr)) == \"mc\" {\n\t\t\t\tignore = i\n\t\t\t}\n\t\t\tif attr.Name.Local == \"mc\" && attr.Name.Space == \"xmlns\" {\n\t\t\t\tmc = true\n\t\t\t}\n\t\t}\n\t}\n\tif !exist {\n\t\tattrs, _ := f.xmlAttr.Load(path)\n\t\tif attrs == nil {\n\t\t\tattrs = []xml.Attr{}\n\t\t}\n\t\tattrs = append(attrs.([]xml.Attr), ns)\n\t\tf.xmlAttr.Store(path, attrs)\n\t\tif !mc {\n\t\t\tattrs = append(attrs.([]xml.Attr), SourceRelationshipCompatibility)\n\t\t\tf.xmlAttr.Store(path, attrs)\n\t\t}\n\t\tif ignore == -1 {\n\t\t\tattrs = append(attrs.([]xml.Attr), xml.Attr{\n\t\t\t\tName:  xml.Name{Local: \"Ignorable\", Space: \"mc\"},\n\t\t\t\tValue: ns.Name.Local,\n\t\t\t})\n\t\t\tf.xmlAttr.Store(path, attrs)\n\t\t\treturn\n\t\t}\n\t\tf.setIgnorableNameSpace(path, ignore, ns)\n\t}\n}\n\n// setIgnorableNameSpace provides a function to set XML namespace as ignorable\n// by the given attribute.\nfunc (f *File) setIgnorableNameSpace(path string, index int, ns xml.Attr) {\n\tignorableNS := []string{\"c14\", \"cdr14\", \"a14\", \"pic14\", \"x14\", \"xdr14\", \"x14ac\", \"dsp\", \"mso14\", \"dgm14\", \"x15\", \"x12ac\", \"x15ac\", \"xr\", \"xr2\", \"xr3\", \"xr4\", \"xr5\", \"xr6\", \"xr7\", \"xr8\", \"xr9\", \"xr10\", \"xr11\", \"xr12\", \"xr13\", \"xr14\", \"xr15\", \"x15\", \"x16\", \"x16r2\", \"mo\", \"mx\", \"mv\", \"o\", \"v\"}\n\txmlAttrs, _ := f.xmlAttr.Load(path)\n\tif inStrSlice(strings.Fields(xmlAttrs.([]xml.Attr)[index].Value), ns.Name.Local, true) == -1 && inStrSlice(ignorableNS, ns.Name.Local, true) != -1 {\n\t\txmlAttrs.([]xml.Attr)[index].Value = strings.TrimSpace(fmt.Sprintf(\"%s %s\", xmlAttrs.([]xml.Attr)[index].Value, ns.Name.Local))\n\t\tf.xmlAttr.Store(path, xmlAttrs)\n\t}\n}\n\n// addSheetNameSpace add XML attribute for worksheet.\nfunc (f *File) addSheetNameSpace(sheet string, ns xml.Attr) {\n\tname, _ := f.getSheetXMLPath(sheet)\n\tf.addNameSpaces(name, ns)\n}\n\n// isNumeric determines whether an expression is a valid numeric type and get\n// the precision for the numeric.\nfunc isNumeric(s string) (bool, int, float64) {\n\tif strings.Contains(s, \"_\") {\n\t\treturn false, 0, 0\n\t}\n\tvar decimal big.Float\n\t_, ok := decimal.SetString(s)\n\tif !ok {\n\t\treturn false, 0, 0\n\t}\n\tvar noScientificNotation string\n\tflt, _ := decimal.Float64()\n\tnoScientificNotation = strconv.FormatFloat(flt, 'f', -1, 64)\n\treturn true, len(strings.ReplaceAll(noScientificNotation, \".\", \"\")), flt\n}\n\nvar (\n\tbstrExp       = regexp.MustCompile(`_x[a-fA-F\\d]{4}_`)\n\tbstrEscapeExp = regexp.MustCompile(`x[a-fA-F\\d]{4}_`)\n)\n\n// bstrUnmarshal parses the binary basic string, this will trim escaped string\n// literal which not permitted in an XML 1.0 document. The basic string\n// variant type can store any valid Unicode character. Unicode's characters\n// that cannot be directly represented in XML as defined by the XML 1.0\n// specification, shall be escaped using the Unicode numerical character\n// representation escape character format _xHHHH_, where H represents a\n// hexadecimal character in the character's value. For example: The Unicode\n// character 8 is not permitted in an XML 1.0 document, so it shall be\n// escaped as _x0008_. To store the literal form of an escape sequence, the\n// initial underscore shall itself be escaped (i.e. stored as _x005F_). For\n// example: The string literal _x0008_ would be stored as _x005F_x0008_.\nfunc bstrUnmarshal(s string) (result string) {\n\tmatches, l, cursor := bstrExp.FindAllStringSubmatchIndex(s, -1), len(s), 0\n\tfor _, match := range matches {\n\t\tresult += s[cursor:match[0]]\n\t\tsubStr := s[match[0]:match[1]]\n\t\tif subStr == \"_x005F_\" {\n\t\t\tcursor = match[1]\n\t\t\tresult += \"_\"\n\t\t\tcontinue\n\t\t}\n\t\tif bstrExp.MatchString(subStr) {\n\t\t\tcursor = match[1]\n\t\t\tv, _ := strconv.Unquote(`\"\\u` + s[match[0]+2:match[1]-1] + `\"`)\n\t\t\tresult += v\n\t\t}\n\t}\n\tif cursor < l {\n\t\tresult += s[cursor:]\n\t}\n\treturn result\n}\n\n// bstrMarshal encode the escaped string literal which not permitted in an XML\n// 1.0 document.\nfunc bstrMarshal(s string) (result string) {\n\tmatches, l, cursor := bstrExp.FindAllStringSubmatchIndex(s, -1), len(s), 0\n\tfor _, match := range matches {\n\t\tresult += s[cursor:match[0]]\n\t\tsubStr := s[match[0]:match[1]]\n\t\tif subStr == \"_x005F_\" {\n\t\t\tcursor = match[1]\n\t\t\tif match[1]+6 <= l && bstrEscapeExp.MatchString(s[match[1]:match[1]+6]) {\n\t\t\t\t_, err := strconv.Unquote(`\"\\u` + s[match[1]+1:match[1]+5] + `\"`)\n\t\t\t\tif err == nil {\n\t\t\t\t\tresult += subStr + \"x005F\" + subStr\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tresult += subStr + \"x005F_\"\n\t\t\tcontinue\n\t\t}\n\t\tif bstrExp.MatchString(subStr) {\n\t\t\tcursor = match[1]\n\t\t\tif _, err := strconv.Unquote(`\"\\u` + s[match[0]+2:match[1]-1] + `\"`); err == nil {\n\t\t\t\tresult += \"_x005F\" + subStr\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t}\n\tif cursor < l {\n\t\tresult += s[cursor:]\n\t}\n\treturn result\n}\n\n// floatToFraction converts a float number to a fraction string representation\n// with specified placeholder widths for numerator and denominator.\nfunc floatToFraction(x float64, numeratorPlaceHolder, denominatorPlaceHolder int) string {\n\tif denominatorPlaceHolder <= 0 {\n\t\treturn \"\"\n\t}\n\tnum, den := floatToFracUseContinuedFraction(x, int64(math.Pow10(denominatorPlaceHolder)))\n\tif num == 0 {\n\t\treturn strings.Repeat(\" \", numeratorPlaceHolder+denominatorPlaceHolder+1)\n\t}\n\tnumStr := strconv.FormatInt(num, 10)\n\tdenStr := strconv.FormatInt(den, 10)\n\tnumeratorPlaceHolder = max(numeratorPlaceHolder-len(numStr), 0)\n\tdenominatorPlaceHolder = max(denominatorPlaceHolder-len(denStr), 0)\n\treturn fmt.Sprintf(\"%s%s/%s%s\", strings.Repeat(\" \", numeratorPlaceHolder), numStr, denStr, strings.Repeat(\" \", denominatorPlaceHolder))\n}\n\n// floatToFracUseContinuedFraction implement convert a floating-point decimal\n// to a fraction using continued fractions and recurrence relations.\nfunc floatToFracUseContinuedFraction(r float64, denominatorLimit int64) (num, den int64) {\n\tp1 := int64(1) // LaTex: p_{-1}\n\tq1 := int64(0) // LaTex: q_{-1}\n\tp2 := int64(0) // LaTex: p_{-2}\n\tq2 := int64(1) // LaTex: q_{-2}\n\tvar lasta, lastb int64\n\tvar curra, currb int64\n\tfor k := 0; ; k++ {\n\t\t// a_{k} = \\lfloor r_{k} \\rfloor\n\t\ta := int64(math.Floor(r))\n\t\t// Fundamental recurrence formulas:  p_{k} = a_{k} \\cdot p_{k-1} + p_{k-2}\n\t\tcurra, currb = a*p1+p2, a*q1+q2\n\t\tp2 = p1\n\t\tq2 = q1\n\t\tp1 = curra\n\t\tq1 = currb\n\t\tfrac := r - float64(a)\n\t\tif q1 >= denominatorLimit {\n\t\t\treturn lasta, lastb\n\t\t}\n\t\tif math.Abs(frac) < 1e-12 {\n\t\t\t// use safe floating-point number comparison. If the input(r) is a real number, here is 0.\n\t\t\treturn curra, currb\n\t\t}\n\t\tlasta, lastb = curra, currb\n\t\t// r_{k+1} = \\frac{1}{r_{k} - a_{k}}\n\t\tr = 1.0 / frac\n\t}\n}\n\n// assignFieldValue assigns the value from an immutable reflect.Value to a\n// mutable reflect.Value based on the type of the immutable value.\nfunc assignFieldValue(field string, immutable, mutable reflect.Value) {\n\tswitch immutable.Kind() {\n\tcase reflect.Bool:\n\t\tmutable.FieldByName(field).SetBool(immutable.Bool())\n\tcase reflect.Int:\n\t\tmutable.FieldByName(field).SetInt(immutable.Int())\n\tdefault:\n\t\tmutable.FieldByName(field).SetString(immutable.String())\n\t}\n}\n\n// setPtrFields assigns the fields of the immutable struct to the mutable\n// struct. The fields name of the immutable struct must match the field names of\n// the mutable struct.\nfunc setPtrFields(immutable, mutable reflect.Value) {\n\tfor i := range immutable.NumField() {\n\t\tsrcField := immutable.Type().Field(i)\n\t\tdstField := mutable.FieldByName(srcField.Name)\n\t\tif dstField.IsValid() && dstField.CanSet() && dstField.Type() == immutable.Field(i).Type() {\n\t\t\tdstField.Set(immutable.Field(i))\n\t\t}\n\t}\n}\n\n// setNoPtrFieldsVal assigns values from the pointer or no-pointer structs\n// fields (immutable) value to no-pointer struct field.\nfunc setNoPtrFieldsVal(fields []string, immutable, mutable reflect.Value) {\n\tfor _, field := range fields {\n\t\timmutableField := immutable.FieldByName(field)\n\t\tif immutableField.Kind() == reflect.Ptr {\n\t\t\tif immutableField.IsValid() && !immutableField.IsNil() {\n\t\t\t\tassignFieldValue(field, immutableField.Elem(), mutable)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tassignFieldValue(field, immutableField, mutable)\n\t}\n}\n\n// setPtrFieldsVal assigns values from the pointer or no-pointer structs\n// fields (immutable) value to pointer struct field.\nfunc setPtrFieldsVal(fields []string, immutable, mutable reflect.Value) {\n\tfor _, field := range fields {\n\t\timmutableField := immutable.FieldByName(field)\n\t\tif immutableField.Kind() == reflect.Ptr {\n\t\t\tif immutableField.IsValid() && !immutableField.IsNil() {\n\t\t\t\tmutable.FieldByName(field).Set(immutableField.Elem())\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif immutableField.IsZero() {\n\t\t\tcontinue\n\t\t}\n\t\tptr := reflect.New(immutableField.Type())\n\t\tptr.Elem().Set(immutableField)\n\t\tmutable.FieldByName(field).Set(ptr)\n\t}\n}\n\n// countUTF16String returns the number of UTF-16 code units in a string.\nfunc countUTF16String(s string) int {\n\tvar cnt int\n\tfor _, r := range s {\n\t\tcnt += utf16.RuneLen(r)\n\t}\n\treturn cnt\n}\n\n// truncateUTF16Units truncates a string to a maximum number of UTF-16 code\n// units.\nfunc truncateUTF16Units(s string, length int) string {\n\tvar cnt int\n\tfor i, r := range s {\n\t\tif cnt += utf16.RuneLen(r); cnt > length {\n\t\t\treturn s[:i]\n\t\t}\n\t}\n\treturn s\n}\n\n// Stack defined an abstract data type that serves as a collection of elements.\ntype Stack struct {\n\tlist *list.List\n}\n\n// NewStack create a new stack.\nfunc NewStack() *Stack {\n\tl := list.New()\n\treturn &Stack{l}\n}\n\n// Push a value onto the top of the stack.\nfunc (stack *Stack) Push(value interface{}) {\n\tstack.list.PushBack(value)\n}\n\n// Pop the top item of the stack and return it.\nfunc (stack *Stack) Pop() interface{} {\n\te := stack.list.Back()\n\tif e != nil {\n\t\tstack.list.Remove(e)\n\t\treturn e.Value\n\t}\n\treturn nil\n}\n\n// Peek view the top item on the stack.\nfunc (stack *Stack) Peek() interface{} {\n\te := stack.list.Back()\n\tif e != nil {\n\t\treturn e.Value\n\t}\n\treturn nil\n}\n\n// Len return the number of items in the stack.\nfunc (stack *Stack) Len() int {\n\treturn stack.list.Len()\n}\n\n// Empty the stack.\nfunc (stack *Stack) Empty() bool {\n\treturn stack.list.Len() == 0\n}\n"
  },
  {
    "path": "lib_test.go",
    "content": "package excelize\n\nimport (\n\t\"archive/zip\"\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar validColumns = []struct {\n\tName string\n\tNum  int\n}{\n\t{Name: \"A\", Num: 1},\n\t{Name: \"Z\", Num: 26},\n\t{Name: \"AA\", Num: 26 + 1},\n\t{Name: \"AK\", Num: 26 + 11},\n\t{Name: \"ak\", Num: 26 + 11},\n\t{Name: \"Ak\", Num: 26 + 11},\n\t{Name: \"aK\", Num: 26 + 11},\n\t{Name: \"AZ\", Num: 26 + 26},\n\t{Name: \"ZZ\", Num: 26 + 26*26},\n\t{Name: \"AAA\", Num: 26 + 26*26 + 1},\n}\n\nvar invalidColumns = []struct {\n\tName string\n\tNum  int\n}{\n\t{Name: \"\", Num: -1},\n\t{Name: \" \", Num: -1},\n\t{Name: \"_\", Num: -1},\n\t{Name: \"__\", Num: -1},\n\t{Name: \"-1\", Num: -1},\n\t{Name: \"0\", Num: -1},\n\t{Name: \" A\", Num: -1},\n\t{Name: \"A \", Num: -1},\n\t{Name: \"A1\", Num: -1},\n\t{Name: \"1A\", Num: -1},\n\t{Name: \" a\", Num: -1},\n\t{Name: \"a \", Num: -1},\n\t{Name: \"a1\", Num: -1},\n\t{Name: \"1a\", Num: -1},\n\t{Name: \" _\", Num: -1},\n\t{Name: \"_ \", Num: -1},\n\t{Name: \"_1\", Num: -1},\n\t{Name: \"1_\", Num: -1},\n}\n\nvar invalidCells = []string{\"\", \"A\", \"AA\", \" A\", \"A \", \"1A\", \"A1A\", \"A1 \", \" A1\", \"1A1\", \"a-1\", \"A-1\"}\n\nvar invalidIndexes = []int{-100, -2, -1, 0}\n\nfunc TestColumnNameToNumber_OK(t *testing.T) {\n\tconst msg = \"Column %q\"\n\tfor _, col := range validColumns {\n\t\tout, err := ColumnNameToNumber(col.Name)\n\t\tif assert.NoErrorf(t, err, msg, col.Name) {\n\t\t\tassert.Equalf(t, col.Num, out, msg, col.Name)\n\t\t}\n\t}\n}\n\nfunc TestColumnNameToNumber_Error(t *testing.T) {\n\tconst msg = \"Column %q\"\n\tfor _, col := range invalidColumns {\n\t\tout, err := ColumnNameToNumber(col.Name)\n\t\tif assert.Errorf(t, err, msg, col.Name) {\n\t\t\tassert.Equalf(t, col.Num, out, msg, col.Name)\n\t\t}\n\t}\n\t_, err := ColumnNameToNumber(\"XFE\")\n\tassert.ErrorIs(t, err, ErrColumnNumber)\n}\n\nfunc TestColumnNumberToName_OK(t *testing.T) {\n\tconst msg = \"Column %q\"\n\tfor _, col := range validColumns {\n\t\tout, err := ColumnNumberToName(col.Num)\n\t\tif assert.NoErrorf(t, err, msg, col.Name) {\n\t\t\tassert.Equalf(t, strings.ToUpper(col.Name), out, msg, col.Name)\n\t\t}\n\t}\n}\n\nfunc TestColumnNumberToName_Error(t *testing.T) {\n\tout, err := ColumnNumberToName(-1)\n\tif assert.Error(t, err) {\n\t\tassert.Empty(t, out)\n\t}\n\n\tout, err = ColumnNumberToName(0)\n\tif assert.Error(t, err) {\n\t\tassert.Empty(t, out)\n\t}\n\n\t_, err = ColumnNumberToName(MaxColumns + 1)\n\tassert.ErrorIs(t, err, ErrColumnNumber)\n}\n\nfunc TestSplitCellName_OK(t *testing.T) {\n\tconst msg = \"Cell \\\"%s%d\\\"\"\n\tfor i, col := range validColumns {\n\t\trow := i + 1\n\t\tc, r, err := SplitCellName(col.Name + strconv.Itoa(row))\n\t\tif assert.NoErrorf(t, err, msg, col.Name, row) {\n\t\t\tassert.Equalf(t, col.Name, c, msg, col.Name, row)\n\t\t\tassert.Equalf(t, row, r, msg, col.Name, row)\n\t\t}\n\t}\n}\n\nfunc TestSplitCellName_Error(t *testing.T) {\n\tconst msg = \"Cell %q\"\n\tfor _, cell := range invalidCells {\n\t\tc, r, err := SplitCellName(cell)\n\t\tif assert.Errorf(t, err, msg, cell) {\n\t\t\tassert.Equalf(t, \"\", c, msg, cell)\n\t\t\tassert.Equalf(t, -1, r, msg, cell)\n\t\t}\n\t}\n}\n\nfunc TestJoinCellName_OK(t *testing.T) {\n\tconst msg = \"Cell \\\"%s%d\\\"\"\n\n\tfor i, col := range validColumns {\n\t\trow := i + 1\n\t\tcell, err := JoinCellName(col.Name, row)\n\t\tif assert.NoErrorf(t, err, msg, col.Name, row) {\n\t\t\tassert.Equalf(t, strings.ToUpper(fmt.Sprintf(\"%s%d\", col.Name, row)), cell, msg, row)\n\t\t}\n\t}\n}\n\nfunc TestJoinCellName_Error(t *testing.T) {\n\tconst msg = \"Cell \\\"%s%d\\\"\"\n\n\ttest := func(col string, row int) {\n\t\tcell, err := JoinCellName(col, row)\n\t\tif assert.Errorf(t, err, msg, col, row) {\n\t\t\tassert.Equalf(t, \"\", cell, msg, col, row)\n\t\t}\n\t}\n\n\tfor _, col := range invalidColumns {\n\t\ttest(col.Name, 1)\n\t\tfor _, row := range invalidIndexes {\n\t\t\ttest(\"A\", row)\n\t\t\ttest(col.Name, row)\n\t\t}\n\t}\n}\n\nfunc TestCellNameToCoordinates_OK(t *testing.T) {\n\tconst msg = \"Cell \\\"%s%d\\\"\"\n\tfor i, col := range validColumns {\n\t\trow := i + 1\n\t\tc, r, err := CellNameToCoordinates(col.Name + strconv.Itoa(row))\n\t\tif assert.NoErrorf(t, err, msg, col.Name, row) {\n\t\t\tassert.Equalf(t, col.Num, c, msg, col.Name, row)\n\t\t\tassert.Equalf(t, i+1, r, msg, col.Name, row)\n\t\t}\n\t}\n}\n\nfunc TestCellNameToCoordinates_Error(t *testing.T) {\n\tconst msg = \"Cell %q\"\n\tfor _, cell := range invalidCells {\n\t\tc, r, err := CellNameToCoordinates(cell)\n\t\tif assert.Errorf(t, err, msg, cell) {\n\t\t\tassert.Equalf(t, -1, c, msg, cell)\n\t\t\tassert.Equalf(t, -1, r, msg, cell)\n\t\t}\n\t}\n\t_, _, err := CellNameToCoordinates(\"A1048577\")\n\tassert.EqualError(t, err, ErrMaxRows.Error())\n}\n\nfunc TestCoordinatesToCellName_OK(t *testing.T) {\n\tconst msg = \"Coordinates [%d, %d]\"\n\tfor i, col := range validColumns {\n\t\trow := i + 1\n\t\tcell, err := CoordinatesToCellName(col.Num, row)\n\t\tif assert.NoErrorf(t, err, msg, col.Num, row) {\n\t\t\tassert.Equalf(t, strings.ToUpper(col.Name+strconv.Itoa(row)), cell, msg, col.Num, row)\n\t\t}\n\t}\n}\n\nfunc TestCoordinatesToCellName_Error(t *testing.T) {\n\tconst msg = \"Coordinates [%d, %d]\"\n\n\ttest := func(col, row int) {\n\t\tcell, err := CoordinatesToCellName(col, row)\n\t\tif assert.Errorf(t, err, msg, col, row) {\n\t\t\tassert.Equalf(t, \"\", cell, msg, col, row)\n\t\t}\n\t}\n\n\tfor _, col := range invalidIndexes {\n\t\ttest(col, 1)\n\t\tfor _, row := range invalidIndexes {\n\t\t\ttest(1, row)\n\t\t\ttest(col, row)\n\t\t}\n\t}\n}\n\nfunc TestCoordinatesToRangeRef(t *testing.T) {\n\t_, err := coordinatesToRangeRef([]int{})\n\tassert.EqualError(t, err, ErrCoordinates.Error())\n\t_, err = coordinatesToRangeRef([]int{1, -1, 1, 1})\n\tassert.Equal(t, newCoordinatesToCellNameError(1, -1), err)\n\t_, err = coordinatesToRangeRef([]int{1, 1, 1, -1})\n\tassert.Equal(t, newCoordinatesToCellNameError(1, -1), err)\n\tref, err := coordinatesToRangeRef([]int{1, 1, 1, 1})\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, ref, \"A1:A1\")\n}\n\nfunc TestSortCoordinates(t *testing.T) {\n\tassert.EqualError(t, sortCoordinates(make([]int, 3)), ErrCoordinates.Error())\n}\n\nfunc TestInStrSlice(t *testing.T) {\n\tassert.EqualValues(t, -1, inStrSlice([]string{}, \"\", true))\n}\n\nfunc TestAttrValue(t *testing.T) {\n\tassert.Empty(t, (&attrValString{}).Value())\n\tassert.False(t, (&attrValBool{}).Value())\n\tassert.Zero(t, (&attrValFloat{}).Value())\n}\n\nfunc TestBoolValMarshal(t *testing.T) {\n\tbold := true\n\tnode := &xlsxFont{B: &attrValBool{Val: &bold}}\n\tdata, err := xml.Marshal(node)\n\tassert.NoError(t, err)\n\tassert.Equal(t, `<xlsxFont><b val=\"1\"></b></xlsxFont>`, string(data))\n\n\tnode = &xlsxFont{}\n\terr = xml.Unmarshal(data, node)\n\tassert.NoError(t, err)\n\tassert.NotEqual(t, nil, node)\n\tassert.NotEqual(t, nil, node.B)\n\tassert.NotEqual(t, nil, node.B.Val)\n\tassert.Equal(t, true, *node.B.Val)\n}\n\nfunc TestBoolValUnmarshalXML(t *testing.T) {\n\tnode := xlsxFont{}\n\tassert.NoError(t, xml.Unmarshal([]byte(\"<xlsxFont><b val=\\\"\\\"></b></xlsxFont>\"), &node))\n\tassert.Equal(t, true, *node.B.Val)\n\tfor content, err := range map[string]string{\n\t\t\"<xlsxFont><b val=\\\"0\\\"><i></i></b></xlsxFont>\": \"unexpected child of attrValBool\",\n\t\t\"<xlsxFont><b val=\\\"x\\\"></b></xlsxFont>\":        \"strconv.ParseBool: parsing \\\"x\\\": invalid syntax\",\n\t} {\n\t\tassert.EqualError(t, xml.Unmarshal([]byte(content), &node), err)\n\t}\n\tattr := attrValBool{}\n\tassert.EqualError(t, attr.UnmarshalXML(xml.NewDecoder(strings.NewReader(\"\")), xml.StartElement{}), io.EOF.Error())\n}\n\nfunc TestExtUnmarshalXML(t *testing.T) {\n\tf, extLst := NewFile(), decodeExtLst{}\n\texpected := fmt.Sprintf(`<extLst><ext uri=\"%s\" xmlns:x14=\"%s\"/></extLst>`,\n\t\tExtURISlicerCachesX14, NameSpaceSpreadSheetX14.Value)\n\tassert.NoError(t, f.xmlNewDecoder(strings.NewReader(expected)).Decode(&extLst))\n\tassert.Len(t, extLst.Ext, 1)\n\tassert.Equal(t, extLst.Ext[0].URI, ExtURISlicerCachesX14)\n}\n\nfunc TestBytesReplace(t *testing.T) {\n\ts := []byte{0x01}\n\tassert.EqualValues(t, s, bytesReplace(s, []byte{}, []byte{}, 0))\n}\n\nfunc TestGetRootElement(t *testing.T) {\n\tassert.Len(t, getRootElement(xml.NewDecoder(strings.NewReader(\"\"))), 0)\n\t// Test get workbook root element which all workbook XML namespace has prefix\n\tf := NewFile()\n\td := f.xmlNewDecoder(bytes.NewReader([]byte(`<x:workbook xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:x=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"></x:workbook>`)))\n\tassert.Len(t, getRootElement(d), 3)\n}\n\nfunc TestSetIgnorableNameSpace(t *testing.T) {\n\tf := NewFile()\n\tf.xmlAttr.Store(\"xml_path\", []xml.Attr{{}})\n\tf.setIgnorableNameSpace(\"xml_path\", 0, xml.Attr{Name: xml.Name{Local: \"c14\"}})\n\tattrs, ok := f.xmlAttr.Load(\"xml_path\")\n\tassert.EqualValues(t, \"c14\", attrs.([]xml.Attr)[0].Value)\n\tassert.True(t, ok)\n}\n\nfunc TestStack(t *testing.T) {\n\ts := NewStack()\n\tassert.Equal(t, s.Peek(), nil)\n\tassert.Equal(t, s.Pop(), nil)\n}\n\nfunc TestGenXMLNamespace(t *testing.T) {\n\tassert.Equal(t, genXMLNamespace([]xml.Attr{\n\t\t{Name: xml.Name{Space: NameSpaceXML, Local: \"space\"}, Value: \"preserve\"},\n\t}), `xml:space=\"preserve\">`)\n}\n\nfunc TestBstrUnmarshal(t *testing.T) {\n\tbstrs := map[string]string{\n\t\t\"*\":                           \"*\",\n\t\t\"*_x0000_\":                    \"*\\x00\",\n\t\t\"*_x0008_\":                    \"*\\b\",\n\t\t\"_x0008_*\":                    \"\\b*\",\n\t\t\"*_x0008_*\":                   \"*\\b*\",\n\t\t\"*_x4F60__x597D_\":             \"*你好\",\n\t\t\"*_xG000_\":                    \"*_xG000_\",\n\t\t\"*_xG05F_x0001_*\":             \"*_xG05F\\x01*\",\n\t\t\"*_x005F__x0008_*\":            \"*_\\b*\",\n\t\t\"*_x005F_x0001_*\":             \"*_x0001_*\",\n\t\t\"*_x005f_x005F__x0008_*\":      \"*_x005F_\\b*\",\n\t\t\"*_x005F_x005F_xG05F_x0006_*\": \"*_x005F_xG05F\\x06*\",\n\t\t\"*_x005F_x005F_x005F_x0006_*\": \"*_x005F_x0006_*\",\n\t\t\"_x005F__x0008_******\":        \"_\\b******\",\n\t\t\"******_x005F__x0008_\":        \"******_\\b\",\n\t\t\"******_x005F__x0008_******\":  \"******_\\b******\",\n\t\t\"_x000x_x005F_x000x_\":         \"_x000x_x000x_\",\n\t}\n\tfor bstr, expected := range bstrs {\n\t\tassert.Equal(t, expected, bstrUnmarshal(bstr), bstr)\n\t}\n}\n\nfunc TestBstrMarshal(t *testing.T) {\n\tbstrs := map[string]string{\n\t\t\"*_xG05F_*\":       \"*_xG05F_*\",\n\t\t\"*_x0008_*\":       \"*_x005F_x0008_*\",\n\t\t\"*_x005F_*\":       \"*_x005F_x005F_*\",\n\t\t\"*_x005F_xG006_*\": \"*_x005F_x005F_xG006_*\",\n\t\t\"*_x005F_x0006_*\": \"*_x005F_x005F_x005F_x0006_*\",\n\t}\n\tfor bstr, expected := range bstrs {\n\t\tassert.Equal(t, expected, bstrMarshal(bstr))\n\t}\n}\n\nfunc TestTruncateUTF16Units(t *testing.T) {\n\tassertTrunc := func(s string, max int, expected string) {\n\t\tassert.Equal(t, expected, truncateUTF16Units(s, max), \"src=%q max=%d\", s, max)\n\t\tassert.LessOrEqual(t, countUTF16String(truncateUTF16Units(s, max)), max)\n\t}\n\t// No truncation\n\tassertTrunc(\"ABC\", 3, \"ABC\")\n\tassertTrunc(\"A\\U0001F600B\", 4, \"A\\U0001F600B\")\n\t// Truncate cutting before BMP rune\n\tassertTrunc(\"ABCDE\", 3, \"ABC\")\n\t// Truncate with surrogate pair boundary: keep pair intact\n\tassertTrunc(\"A\\U0001F600B\", 3, \"A\\U0001F600\") // 1 + 2 units\n\tassertTrunc(\"A\\U0001F600B\", 2, \"A\")           // pair would overflow\n\tassertTrunc(\"\\U0001F600B\", 1, \"\")             // first rune (2 units) exceeds limit\n\tassertTrunc(\"\\U0001F600B\", 2, \"\\U0001F600\")   // exact fit\n\tassertTrunc(\"\\U0001F600B\", 3, \"\\U0001F600B\")  // allow extra\n\t// Multiple surrogate pairs\n\tassertTrunc(\"\\U0001F600\\U0001F600B\", 2, \"\\U0001F600\")           // corrected expectation per logic\n\tassertTrunc(\"\\U0001F600\\U0001F600B\", 3, \"\\U0001F600\")           // 2 units kept, next pair would exceed\n\tassertTrunc(\"\\U0001F600\\U0001F600B\", 4, \"\\U0001F600\\U0001F600\") // both pairs (4 units)\n}\n\nfunc TestReadBytes(t *testing.T) {\n\tf := &File{tempFiles: sync.Map{}}\n\tsheet := \"xl/worksheets/sheet1.xml\"\n\tf.tempFiles.Store(sheet, \"/d/\")\n\tassert.Equal(t, []byte{}, f.readBytes(sheet))\n}\n\nfunc TestUnzipToTemp(t *testing.T) {\n\tassert.NoError(t, os.Setenv(\"TMPDIR\", \"test\"))\n\tdefer func() {\n\t\tassert.NoError(t, os.Unsetenv(\"TMPDIR\"))\n\t}()\n\tassert.NoError(t, os.Chmod(os.TempDir(), 0o444))\n\tf := NewFile()\n\tdata := []byte(\"PK\\x03\\x040000000PK\\x01\\x0200000\" +\n\t\t\"0000000000000000000\\x00\" +\n\t\t\"\\x00\\x00\\x00\\x00\\x00000000000000PK\\x01\" +\n\t\t\"\\x020000000000000000000\" +\n\t\t\"00000\\v\\x00\\x00\\x00\\x00\\x00000000000\" +\n\t\t\"00000000000000PK\\x01\\x0200\" +\n\t\t\"00000000000000000000\" +\n\t\t\"00\\v\\x00\\x00\\x00\\x00\\x00000000000000\" +\n\t\t\"00000000000PK\\x01\\x020000<\" +\n\t\t\"0\\x00\\x0000000000000000\\v\\x00\\v\" +\n\t\t\"\\x00\\x00\\x00\\x00\\x0000000000\\x00\\x00\\x00\\x00000\" +\n\t\t\"00000000PK\\x01\\x0200000000\" +\n\t\t\"0000000000000000\\v\\x00\\x00\\x00\" +\n\t\t\"\\x00\\x0000PK\\x05\\x06000000\\x05\\x00\\xfd\\x00\\x00\\x00\" +\n\t\t\"\\v\\x00\\x00\\x00\\x00\\x00\")\n\tz, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))\n\tassert.NoError(t, err)\n\n\t_, err = f.unzipToTemp(z.File[0])\n\trequire.Error(t, err)\n\tassert.NoError(t, os.Chmod(os.TempDir(), 0o755))\n\n\t_, err = f.unzipToTemp(z.File[0])\n\tassert.EqualError(t, err, \"EOF\")\n}\n\nfunc TestFloat2Frac(t *testing.T) {\n\tassert.Empty(t, floatToFraction(0.19, 0, 0))\n\tassert.Equal(t, \"1/5\", floatToFraction(0.19, 1, 1))\n\tassert.Equal(t, \"9999/10000\", strings.Trim(floatToFraction(0.9999, 10, 10), \" \"))\n\tassert.Equal(t, \"954888175898973913/351283728530932463\", floatToFraction(math.E, 1, 18))\n}\n"
  },
  {
    "path": "merge.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"strings\"\n\n// Rect gets merged cell rectangle coordinates sequence.\nfunc (mc *xlsxMergeCell) Rect() ([]int, error) {\n\tvar err error\n\tif mc.rect == nil {\n\t\tmergedCellsRef := mc.Ref\n\t\tif !strings.Contains(mergedCellsRef, \":\") {\n\t\t\tmergedCellsRef += \":\" + mergedCellsRef\n\t\t}\n\t\tmc.rect, err = rangeRefToCoordinates(mergedCellsRef)\n\t}\n\treturn mc.rect, err\n}\n\n// MergeCell provides a function to merge cells by given range reference and\n// sheet name. Merging cells only keeps the upper-left cell value, and\n// discards the other values. For example create a merged cell of D3:E9 on\n// Sheet1:\n//\n//\terr := f.MergeCell(\"Sheet1\", \"D3\", \"E9\")\n//\n// If you create a merged cell that overlaps with another existing merged cell,\n// those merged cells that already exist will be removed. The cell references\n// tuple after merging in the following range will be: A1(x3,y1) D1(x2,y1)\n// A8(x3,y4) D8(x2,y4)\n//\n//\t             B1(x1,y1)      D1(x2,y1)\n//\t           +------------------------+\n//\t           |                        |\n//\tA4(x3,y3)  |    C4(x4,y3)           |\n//\t+------------------------+          |\n//\t|          |             |          |\n//\t|          |B5(x1,y2)    | D5(x2,y2)|\n//\t|          +------------------------+\n//\t|                        |\n//\t|A8(x3,y4)      C8(x4,y4)|\n//\t+------------------------+\nfunc (f *File) MergeCell(sheet, topLeftCell, bottomRightCell string) error {\n\trect, err := rangeRefToCoordinates(topLeftCell + \":\" + bottomRightCell)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Correct the range reference, such correct C1:B3 to B1:C3.\n\t_ = sortCoordinates(rect)\n\n\ttopLeftCell, _ = CoordinatesToCellName(rect[0], rect[1])\n\tbottomRightCell, _ = CoordinatesToCellName(rect[2], rect[3])\n\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tfor col := rect[0]; col <= rect[2]; col++ {\n\t\tfor row := rect[1]; row <= rect[3]; row++ {\n\t\t\tif col == rect[0] && row == rect[1] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tws.prepareSheetXML(col, row)\n\t\t\tc := &ws.SheetData.Row[row-1].C[col-1]\n\t\t\tc.setCellDefault(\"\")\n\t\t\t_ = f.removeFormula(c, ws, sheet)\n\t\t}\n\t}\n\tref := topLeftCell + \":\" + bottomRightCell\n\tif ws.MergeCells != nil {\n\t\tws.MergeCells.Cells = append(ws.MergeCells.Cells, &xlsxMergeCell{Ref: ref, rect: rect})\n\t} else {\n\t\tws.MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: ref, rect: rect}}}\n\t}\n\tws.MergeCells.Count = len(ws.MergeCells.Cells)\n\treturn err\n}\n\n// UnmergeCell provides a function to unmerge a given range reference.\n// For example unmerge range reference D3:E9 on Sheet1:\n//\n//\terr := f.UnmergeCell(\"Sheet1\", \"D3\", \"E9\")\n//\n// Attention: overlapped range will also be unmerged.\nfunc (f *File) UnmergeCell(sheet, topLeftCell, bottomRightCell string) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\trect1, err := rangeRefToCoordinates(topLeftCell + \":\" + bottomRightCell)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Correct the range reference, such correct C1:B3 to B1:C3.\n\t_ = sortCoordinates(rect1)\n\n\t// return nil since no MergeCells in the sheet\n\tif ws.MergeCells == nil {\n\t\treturn nil\n\t}\n\tif err = ws.mergeOverlapCells(); err != nil {\n\t\treturn err\n\t}\n\tf.clearCalcCache()\n\ti := 0\n\tfor _, mergeCell := range ws.MergeCells.Cells {\n\t\tif rect2, _ := rangeRefToCoordinates(mergeCell.Ref); isOverlap(rect1, rect2) {\n\t\t\tcontinue\n\t\t}\n\t\tws.MergeCells.Cells[i] = mergeCell\n\t\ti++\n\t}\n\tws.MergeCells.Cells = ws.MergeCells.Cells[:i]\n\tws.MergeCells.Count = len(ws.MergeCells.Cells)\n\tif ws.MergeCells.Count == 0 {\n\t\tws.MergeCells = nil\n\t}\n\treturn nil\n}\n\n// GetMergeCells provides a function to get all merged cells from a specific\n// worksheet. If the `withoutValues` parameter is set to true, it will not\n// return the cell values of merged cells, only the range reference will be\n// returned. For example get all merged cells on Sheet1:\n//\n//\tmergeCells, err := f.GetMergeCells(\"Sheet1\")\n//\n// If you want to get merged cells without cell values, you can use the\n// following code:\n//\n//\tmergeCells, err := f.GetMergeCells(\"Sheet1\", true)\nfunc (f *File) GetMergeCells(sheet string, withoutValues ...bool) ([]MergeCell, error) {\n\tvar (\n\t\tmergeCells []MergeCell\n\t\twithoutVal bool\n\t)\n\tif len(withoutValues) > 0 {\n\t\twithoutVal = withoutValues[0]\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn mergeCells, err\n\t}\n\tif ws.MergeCells != nil {\n\t\tif err = ws.mergeOverlapCells(); err != nil {\n\t\t\treturn mergeCells, err\n\t\t}\n\t\tmergeCells = make([]MergeCell, 0, len(ws.MergeCells.Cells))\n\t\tfor i := range ws.MergeCells.Cells {\n\t\t\tref, val := ws.MergeCells.Cells[i].Ref, \"\"\n\t\t\tif !withoutVal {\n\t\t\t\tcell := strings.Split(ref, \":\")[0]\n\t\t\t\tval, _ = f.GetCellValue(sheet, cell)\n\t\t\t}\n\t\t\tmergeCells = append(mergeCells, []string{ref, val})\n\t\t}\n\t}\n\treturn mergeCells, err\n}\n\n// mergeOverlapCells merge overlap cells.\nfunc (ws *xlsxWorksheet) mergeOverlapCells() error {\n\tvar (\n\t\terr      error\n\t\trectList [][]int\n\t\tmerged   = true\n\t)\n\tfor _, cell := range ws.MergeCells.Cells {\n\t\tif cell == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif cell.rect, err = cell.Rect(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\trectList = append(rectList, cell.rect)\n\t}\n\tfor merged {\n\t\tmerged = false\n\t\tvar mergedRectList [][]int\n\t\tused := make([]bool, len(rectList))\n\t\tfor i := 0; i < len(rectList); i++ {\n\t\t\tif used[i] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tr1 := rectList[i]\n\t\t\tfor j := i + 1; j < len(rectList); j++ {\n\t\t\t\tif used[j] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif r2 := rectList[j]; isOverlap(r1, r2) {\n\t\t\t\t\tr1 = []int{\n\t\t\t\t\t\tmin(r1[0], r2[0]),\n\t\t\t\t\t\tmin(r1[1], r2[1]),\n\t\t\t\t\t\tmax(r1[2], r2[2]),\n\t\t\t\t\t\tmax(r1[3], r2[3]),\n\t\t\t\t\t}\n\t\t\t\t\tmerged, used[j] = true, true\n\t\t\t\t}\n\t\t\t}\n\t\t\tmergedRectList = append(mergedRectList, r1)\n\t\t}\n\t\trectList = mergedRectList\n\t}\n\tws.MergeCells.Cells = make([]*xlsxMergeCell, 0, len(rectList))\n\tfor _, r := range rectList {\n\t\tref, _ := coordinatesToRangeRef(r)\n\t\tws.MergeCells.Cells = append(ws.MergeCells.Cells, &xlsxMergeCell{Ref: ref, rect: r})\n\t}\n\tws.MergeCells.Count = len(ws.MergeCells.Cells)\n\treturn err\n}\n\n// MergeCell define a merged cell data.\n// It consists of the following structure.\n// example: []string{\"D4:E10\", \"cell value\"}\ntype MergeCell []string\n\n// GetCellValue returns merged cell value.\nfunc (m *MergeCell) GetCellValue() string {\n\treturn (*m)[1]\n}\n\n// GetStartAxis returns the top left cell reference of merged range, for\n// example: \"C2\".\nfunc (m *MergeCell) GetStartAxis() string {\n\treturn strings.Split((*m)[0], \":\")[0]\n}\n\n// GetEndAxis returns the bottom right cell reference of merged range, for\n// example: \"D4\".\nfunc (m *MergeCell) GetEndAxis() string {\n\treturn strings.Split((*m)[0], \":\")[1]\n}\n"
  },
  {
    "path": "merge_test.go",
    "content": "package excelize\n\nimport (\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMergeCell(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tassert.EqualError(t, f.MergeCell(\"Sheet1\", \"A\", \"B\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\tfor _, cells := range [][]string{\n\t\t{\"D9\", \"D9\"},\n\t\t{\"D9\", \"E9\"},\n\t\t{\"H14\", \"G13\"},\n\t\t{\"C9\", \"D8\"},\n\t\t{\"F11\", \"G13\"},\n\t\t{\"H7\", \"B15\"},\n\t\t{\"D11\", \"F13\"},\n\t\t{\"G10\", \"K12\"},\n\t} {\n\t\tassert.NoError(t, f.MergeCell(\"Sheet1\", cells[0], cells[1]))\n\t}\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"G11\", \"set value in merged cell\"))\n\tassert.NoError(t, f.SetCellInt(\"Sheet1\", \"H11\", 100))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"I11\", 0.5))\n\tassert.NoError(t, f.SetCellHyperLink(\"Sheet1\", \"J11\", \"https://github.com/xuri/excelize\", \"External\"))\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"G12\", \"SUM(Sheet1!B19,Sheet1!C19)\"))\n\tvalue, err := f.GetCellValue(\"Sheet1\", \"H11\")\n\tassert.Equal(t, \"100\", value)\n\tassert.NoError(t, err)\n\t// Merged cell ref is single coordinate\n\tvalue, err = f.GetCellValue(\"Sheet2\", \"A6\")\n\tassert.Empty(t, value)\n\tassert.NoError(t, err)\n\tvalue, err = f.GetCellFormula(\"Sheet1\", \"G12\")\n\tassert.Equal(t, \"SUM(Sheet1!B19,Sheet1!C19)\", value)\n\tassert.NoError(t, err)\n\n\t_, err = f.NewSheet(\"Sheet3\")\n\tassert.NoError(t, err)\n\n\tfor _, cells := range [][]string{\n\t\t{\"D11\", \"F13\"},\n\t\t{\"G10\", \"K12\"},\n\t\t{\"B1\", \"D5\"}, // B1:D5\n\t\t{\"E1\", \"F5\"}, // E1:F5\n\t\t{\"H2\", \"I5\"},\n\t\t{\"I4\", \"J6\"}, // H2:J6\n\t\t{\"M2\", \"N5\"},\n\t\t{\"L4\", \"M6\"}, // L2:N6\n\t\t{\"P4\", \"Q7\"},\n\t\t{\"O2\", \"P5\"}, // O2:Q7\n\t\t{\"A9\", \"B12\"},\n\t\t{\"B7\", \"C9\"}, // A7:C12\n\t\t{\"E9\", \"F10\"},\n\t\t{\"D8\", \"G12\"},\n\t\t{\"I8\", \"I12\"},\n\t\t{\"I10\", \"K10\"},\n\t\t{\"M8\", \"Q13\"},\n\t\t{\"N10\", \"O11\"},\n\t} {\n\t\tassert.NoError(t, f.MergeCell(\"Sheet3\", cells[0], cells[1]))\n\t}\n\n\t// Test merge cells on not exists worksheet\n\tassert.EqualError(t, f.MergeCell(\"SheetN\", \"N10\", \"O11\"), \"sheet SheetN does not exist\")\n\t// Test merged cells with invalid sheet name\n\tassert.EqualError(t, f.MergeCell(\"Sheet:1\", \"N10\", \"O11\"), ErrSheetNameInvalid.Error())\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestMergeCell.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tassert.NoError(t, f.MergeCell(\"Sheet1\", \"A2\", \"B3\"))\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{nil, nil}}\n\tassert.NoError(t, f.MergeCell(\"Sheet1\", \"A2\", \"B3\"))\n\t// Test getting merged cells with the same start and end axis\n\tws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: \"A1\"}}}\n\tmergedCells, err := f.GetMergeCells(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A1\", mergedCells[0].GetStartAxis())\n\tassert.Equal(t, \"A1\", mergedCells[0].GetEndAxis())\n\tassert.Empty(t, mergedCells[0].GetCellValue())\n}\n\nfunc TestMergeCellOverlap(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.MergeCell(\"Sheet1\", \"A1\", \"C2\"))\n\tassert.NoError(t, f.MergeCell(\"Sheet1\", \"B2\", \"D3\"))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestMergeCellOverlap.xlsx\")))\n\n\tf, err := OpenFile(filepath.Join(\"test\", \"TestMergeCellOverlap.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tmc, err := f.GetMergeCells(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, mc, 1)\n\tassert.Equal(t, \"A1\", mc[0].GetStartAxis())\n\tassert.Equal(t, \"D3\", mc[0].GetEndAxis())\n\tassert.Empty(t, mc[0].GetCellValue())\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestGetMergeCells(t *testing.T) {\n\twants := []struct {\n\t\tvalue string\n\t\tstart string\n\t\tend   string\n\t}{{\n\t\tvalue: \"A1\",\n\t\tstart: \"A1\",\n\t\tend:   \"B1\",\n\t}, {\n\t\tvalue: \"A2\",\n\t\tstart: \"A2\",\n\t\tend:   \"A3\",\n\t}, {\n\t\tvalue: \"A4\",\n\t\tstart: \"A4\",\n\t\tend:   \"B5\",\n\t}, {\n\t\tvalue: \"A7\",\n\t\tstart: \"A7\",\n\t\tend:   \"C10\",\n\t}}\n\n\tf, err := OpenFile(filepath.Join(\"test\", \"MergeCell.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tsheet1 := f.GetSheetName(0)\n\n\tmergeCells, err := f.GetMergeCells(sheet1)\n\tassert.NoError(t, err)\n\tassert.Len(t, mergeCells, len(wants))\n\n\tfor i, m := range mergeCells {\n\t\tassert.Equal(t, wants[i].value, m.GetCellValue())\n\t\tassert.Equal(t, wants[i].start, m.GetStartAxis())\n\t\tassert.Equal(t, wants[i].end, m.GetEndAxis())\n\t}\n\t// Test get merged cells without cell values\n\tmergeCells, err = f.GetMergeCells(sheet1, true)\n\tassert.NoError(t, err)\n\tassert.Len(t, mergeCells, len(wants))\n\tfor i, m := range mergeCells {\n\t\tassert.Empty(t, m.GetCellValue())\n\t\tassert.Equal(t, wants[i].start, m.GetStartAxis())\n\t\tassert.Equal(t, wants[i].end, m.GetEndAxis())\n\t}\n\n\t// Test get merged cells with invalid sheet name\n\t_, err = f.GetMergeCells(\"Sheet:1\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\t// Test get merged cells on not exists worksheet\n\t_, err = f.GetMergeCells(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestUnmergeCell(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"MergeCell.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tsheet1 := f.GetSheetName(0)\n\n\tsheet, err := f.workSheetReader(sheet1)\n\tassert.NoError(t, err)\n\n\tmergeCellNum := len(sheet.MergeCells.Cells)\n\n\tassert.EqualError(t, f.UnmergeCell(\"Sheet1\", \"A\", \"A\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\n\t// Test unmerge the merged cells that contains A1\n\tassert.NoError(t, f.UnmergeCell(sheet1, \"A1\", \"A1\"))\n\tif len(sheet.MergeCells.Cells) != mergeCellNum-1 {\n\t\tt.FailNow()\n\t}\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestUnmergeCell.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tassert.NoError(t, f.MergeCell(\"Sheet1\", \"A2\", \"B3\"))\n\t// Test unmerged range reference on not exists worksheet\n\tassert.EqualError(t, f.UnmergeCell(\"SheetN\", \"A1\", \"A1\"), \"sheet SheetN does not exist\")\n\n\t// Test unmerge the merged cells with invalid sheet name\n\tassert.EqualError(t, f.UnmergeCell(\"Sheet:1\", \"A1\", \"A1\"), ErrSheetNameInvalid.Error())\n\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).MergeCells = nil\n\tassert.NoError(t, f.UnmergeCell(\"Sheet1\", \"H7\", \"B15\"))\n\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{nil, nil}}\n\tassert.NoError(t, f.UnmergeCell(\"Sheet1\", \"H15\", \"B7\"))\n\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: \"A1\"}}}\n\tassert.NoError(t, f.UnmergeCell(\"Sheet1\", \"A2\", \"B3\"))\n\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: \"A:A\"}}}\n\tassert.EqualError(t, f.UnmergeCell(\"Sheet1\", \"A2\", \"B3\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n}\n\nfunc TestMergeCellsParser(t *testing.T) {\n\tws := &xlsxWorksheet{MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{nil}}}\n\t_, err := ws.mergeCellsParser(\"A1\")\n\tassert.NoError(t, err)\n}\n"
  },
  {
    "path": "numfmt.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"math/big\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/xuri/nfp\"\n)\n\n// languageInfo defined the required fields of localization support for number\n// format.\ntype languageInfo struct {\n\tapFmt                                string\n\ttags, weekdayNames, weekdayNamesAbbr []string\n\tuseGannen                            bool\n\tlocalMonth                           func(t time.Time, abbr int) string\n}\n\n// numberFormat directly maps the number format parser runtime required\n// fields.\ntype numberFormat struct {\n\topts                                                                     *Options\n\tcellType                                                                 CellType\n\tsection                                                                  []nfp.Section\n\tt                                                                        time.Time\n\tsectionIdx                                                               int\n\tdate1904, isNumeric, hours, seconds, useMillisecond, useGannen           bool\n\tnumber                                                                   float64\n\tap, localCode, result, value, valueSectionType                           string\n\tswitchArgument, currencyString                                           string\n\tfracHolder, fracPadding, intHolder, intPadding, expBaseLen               int\n\tpercent                                                                  int\n\tuseCommaSep, useFraction, usePointer, usePositive, useScientificNotation bool\n}\n\n// CultureName is the type of supported language country codes types for apply\n// number format.\ntype CultureName byte\n\n// This section defines the currently supported country code types enumeration\n// for apply number format.\nconst (\n\tCultureNameUnknown CultureName = iota\n\tCultureNameEnUS\n\tCultureNameJaJP\n\tCultureNameKoKR\n\tCultureNameZhCN\n\tCultureNameZhTW\n)\n\nvar (\n\t// Excel styles can reference number formats that are built-in, all of which\n\t// have an id less than 164. Note that this number format code list is under\n\t// English localization.\n\tbuiltInNumFmt = map[int]string{\n\t\t0:  \"general\",\n\t\t1:  \"0\",\n\t\t2:  \"0.00\",\n\t\t3:  \"#,##0\",\n\t\t4:  \"#,##0.00\",\n\t\t9:  \"0%\",\n\t\t10: \"0.00%\",\n\t\t11: \"0.00E+00\",\n\t\t12: \"# ?/?\",\n\t\t13: \"# ??/??\",\n\t\t14: \"mm-dd-yy\",\n\t\t15: \"d-mmm-yy\",\n\t\t16: \"d-mmm\",\n\t\t17: \"mmm-yy\",\n\t\t18: \"h:mm AM/PM\",\n\t\t19: \"h:mm:ss AM/PM\",\n\t\t20: \"hh:mm\",\n\t\t21: \"hh:mm:ss\",\n\t\t22: \"m/d/yy hh:mm\",\n\t\t37: \"#,##0 ;(#,##0)\",\n\t\t38: \"#,##0 ;[red](#,##0)\",\n\t\t39: \"#,##0.00 ;(#,##0.00)\",\n\t\t40: \"#,##0.00 ;[red](#,##0.00)\",\n\t\t41: \"_(* #,##0_);_(* \\\\(#,##0\\\\);_(* \\\"-\\\"_);_(@_)\",\n\t\t42: \"_(\\\"$\\\"* #,##0_);_(\\\"$\\\"* \\\\(#,##0\\\\);_(\\\"$\\\"* \\\"-\\\"_);_(@_)\",\n\t\t43: \"_(* #,##0.00_);_(* \\\\(#,##0.00\\\\);_(* \\\"-\\\"??_);_(@_)\",\n\t\t44: \"_(\\\"$\\\"* #,##0.00_);_(\\\"$\\\"* \\\\(#,##0.00\\\\);_(\\\"$\\\"* \\\"-\\\"??_);_(@_)\",\n\t\t45: \"mm:ss\",\n\t\t46: \"[h]:mm:ss\",\n\t\t47: \"mm:ss.0\",\n\t\t48: \"##0.0E+0\",\n\t\t49: \"@\",\n\t}\n\t// langNumFmt defined number format code provided for language glyphs where\n\t// they occur in different language.\n\tlangNumFmt = map[string]map[int]string{\n\t\t\"zh-tw\": {\n\t\t\t27: \"[$-404]e/m/d\",\n\t\t\t28: \"[$-404]e\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t29: \"[$-404]e\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t30: \"m/d/yy\",\n\t\t\t31: \"yyyy\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t32: \"hh\\\"時\\\"mm\\\"分\\\"\",\n\t\t\t33: \"hh\\\"時\\\"mm\\\"分\\\"ss\\\"秒\\\"\",\n\t\t\t34: \"上午/下午hh\\\"時\\\"mm\\\"分\\\"\",\n\t\t\t35: \"上午/下午hh\\\"時\\\"mm\\\"分\\\"ss\\\"秒\\\"\",\n\t\t\t36: \"[$-404]e/m/d\",\n\t\t\t50: \"[$-404]e/m/d\",\n\t\t\t51: \"[$-404]e\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t52: \"上午/下午hh\\\"時\\\"mm\\\"分\\\"\",\n\t\t\t53: \"上午/下午hh\\\"時\\\"mm\\\"分\\\"ss\\\"秒\\\"\",\n\t\t\t54: \"[$-404]e\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t55: \"上午/下午hh\\\"時\\\"mm\\\"分\\\"\",\n\t\t\t56: \"上午/下午hh\\\"時\\\"mm\\\"分\\\"ss\\\"秒\\\"\",\n\t\t\t57: \"[$-404]e/m/d\",\n\t\t\t58: \"[$-404]e\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t},\n\t\t\"zh-cn\": {\n\t\t\t27: \"yyyy\\\"年\\\"m\\\"月\\\"\",\n\t\t\t28: \"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t29: \"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t30: \"m/d/yy\",\n\t\t\t31: \"yyyy\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t32: \"h\\\"时\\\"mm\\\"分\\\"\",\n\t\t\t33: \"h\\\"时\\\"mm\\\"分\\\"ss\\\"秒\\\"\",\n\t\t\t34: \"上午/下午h\\\"时\\\"mm\\\"分\\\"\",\n\t\t\t35: \"上午/下午h\\\"时\\\"mm\\\"分\\\"ss\\\"秒\\\"\",\n\t\t\t36: \"yyyy\\\"年\\\"m\\\"月\\\"\",\n\t\t\t50: \"yyyy\\\"年\\\"m\\\"月\\\"\",\n\t\t\t51: \"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t52: \"yyyy\\\"年\\\"m\\\"月\\\"\",\n\t\t\t53: \"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t54: \"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t55: \"上午/下午h\\\"时\\\"mm\\\"分\\\"\",\n\t\t\t56: \"上午/下午h\\\"时\\\"mm\\\"分\\\"ss\\\"秒\\\"\",\n\t\t\t57: \"yyyy\\\"年\\\"m\\\"月\\\"\",\n\t\t\t58: \"m\\\"月\\\"d\\\"日\\\"\",\n\t\t},\n\t\t\"ja-jp\": {\n\t\t\t27: \"[$-411]ge.m.d\",\n\t\t\t28: \"[$-411]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t29: \"[$-411]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t30: \"m/d/yy\",\n\t\t\t31: \"yyyy\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t32: \"h\\\"時\\\"mm\\\"分\\\"\",\n\t\t\t33: \"h\\\"時\\\"mm\\\"分\\\"ss\\\"秒\\\"\",\n\t\t\t34: \"yyyy\\\"年\\\"m\\\"月\\\"\",\n\t\t\t35: \"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t36: \"[$-411]ge.m.d\",\n\t\t\t50: \"[$-411]ge.m.d\",\n\t\t\t51: \"[$-411]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t52: \"yyyy\\\"年\\\"m\\\"月\\\"\",\n\t\t\t53: \"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t54: \"[$-411]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t55: \"yyyy\\\"年\\\"m\\\"月\\\"\",\n\t\t\t56: \"m\\\"月\\\"d\\\"日\\\"\",\n\t\t\t57: \"[$-411]ge.m.d\",\n\t\t\t58: \"[$-411]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\"\",\n\t\t},\n\t\t\"ko-kr\": {\n\t\t\t27: \"yyyy\\\"年\\\" mm\\\"月\\\" dd\\\"日\\\"\",\n\t\t\t28: \"mm-dd\",\n\t\t\t29: \"mm-dd\",\n\t\t\t30: \"mm-dd-yy\",\n\t\t\t31: \"yyyy\\\"년\\\" mm\\\"월\\\" dd\\\"일\\\"\",\n\t\t\t32: \"h\\\"시\\\" mm\\\"분\\\"\",\n\t\t\t33: \"h\\\"시\\\" mm\\\"분\\\" ss\\\"초\\\"\",\n\t\t\t34: \"yyyy-mm-dd\",\n\t\t\t35: \"yyyy-mm-dd\",\n\t\t\t36: \"yyyy\\\"年\\\" mm\\\"月\\\" dd\\\"日\\\"\",\n\t\t\t50: \"yyyy\\\"年\\\" mm\\\"月\\\" dd\\\"日\\\"\",\n\t\t\t51: \"mm-dd\",\n\t\t\t52: \"yyyy-mm-dd\",\n\t\t\t53: \"yyyy-mm-dd\",\n\t\t\t54: \"mm-dd\",\n\t\t\t55: \"yyyy-mm-dd\",\n\t\t\t56: \"yyyy-mm-dd\",\n\t\t\t57: \"yyyy\\\"年\\\" mm\\\"月\\\" dd\\\"日\\\"\",\n\t\t\t58: \"mm-dd\",\n\t\t},\n\t\t\"th-th\": {\n\t\t\t59: \"t0\",\n\t\t\t60: \"t0.00\",\n\t\t\t61: \"t#,##0\",\n\t\t\t62: \"t#,##0.00\",\n\t\t\t67: \"t0%\",\n\t\t\t68: \"t0.00%\",\n\t\t\t69: \"t# ?/?\",\n\t\t\t70: \"t# ??/??\",\n\t\t\t71: \"\\u0E27/\\u0E14/\\u0E1B\\u0E1B\\u0E1B\\u0E1B\",\n\t\t\t72: \"\\u0E27-\\u0E14\\u0E14\\u0E14-\\u0E1B\\u0E1B\",\n\t\t\t73: \"\\u0E27-\\u0E14\\u0E14\\u0E14\",\n\t\t\t74: \"\\u0E14\\u0E14\\u0E14-\\u0E1B\\u0E1B\",\n\t\t\t75: \"\\u0E0A:\\u0E19\\u0E19\",\n\t\t\t76: \"\\u0E0A:\\u0E19\\u0E19:\\u0E17\\u0E17\",\n\t\t\t77: \"\\u0E27/\\u0E14/\\u0E1B\\u0E1B\\u0E1B\\u0E1B \\u0E0A:\\u0E19\\u0E19\",\n\t\t\t78: \"\\u0E19\\u0E19:\\u0E17\\u0E17\",\n\t\t\t79: \"[\\u0E0A]:\\u0E19\\u0E19:\\u0E17\\u0E17\",\n\t\t\t80: \"\\u0E19\\u0E19:\\u0E17\\u0E17.0\",\n\t\t\t81: \"d/m/bb\",\n\t\t},\n\t}\n\t// currencyNumFmt defined the currency number format map.\n\tcurrencyNumFmt = map[int]string{\n\t\t164: \"\\\"¥\\\"#,##0.00\",\n\t\t165: \"[$$-409]#,##0.00\",\n\t\t166: \"[$$-45C]#,##0.00\",\n\t\t167: \"[$$-1004]#,##0.00\",\n\t\t168: \"[$$-404]#,##0.00\",\n\t\t169: \"[$$-C09]#,##0.00\",\n\t\t170: \"[$$-2809]#,##0.00\",\n\t\t171: \"[$$-1009]#,##0.00\",\n\t\t172: \"[$$-2009]#,##0.00\",\n\t\t173: \"[$$-1409]#,##0.00\",\n\t\t174: \"[$$-4809]#,##0.00\",\n\t\t175: \"[$$-2C09]#,##0.00\",\n\t\t176: \"[$$-2409]#,##0.00\",\n\t\t177: \"[$$-1000]#,##0.00\",\n\t\t178: \"#,##0.00\\\\ [$$-C0C]\",\n\t\t179: \"[$$-475]#,##0.00\",\n\t\t180: \"[$$-83E]#,##0.00\",\n\t\t181: \"[$$-86B]\\\\ #,##0.00\",\n\t\t182: \"[$$-340A]\\\\ #,##0.00\",\n\t\t183: \"[$$-240A]#,##0.00\",\n\t\t184: \"[$$-300A]\\\\ #,##0.00\",\n\t\t185: \"[$$-440A]#,##0.00\",\n\t\t186: \"[$$-80A]#,##0.00\",\n\t\t187: \"[$$-500A]#,##0.00\",\n\t\t188: \"[$$-540A]#,##0.00\",\n\t\t189: \"[$$-380A]\\\\ #,##0.00\",\n\t\t190: \"[$£-809]#,##0.00\",\n\t\t191: \"[$£-491]#,##0.00\",\n\t\t192: \"[$£-452]#,##0.00\",\n\t\t193: \"[$¥-804]#,##0.00\",\n\t\t194: \"[$¥-411]#,##0.00\",\n\t\t195: \"[$¥-478]#,##0.00\",\n\t\t196: \"[$¥-451]#,##0.00\",\n\t\t197: \"[$¥-480]#,##0.00\",\n\t\t198: \"#,##0.00\\\\ [$\\u058F-42B]\",\n\t\t199: \"[$\\u060B-463]#,##0.00\",\n\t\t200: \"[$\\u060B-48C]#,##0.00\",\n\t\t201: \"[$\\u09F3-845]\\\\ #,##0.00\",\n\t\t202: \"#,##0.00[$\\u17DB-453]\",\n\t\t203: \"[$\\u20A1-140A]#,##0.00\",\n\t\t204: \"[$\\u20A6-468]\\\\ #,##0.00\",\n\t\t205: \"[$\\u20A6-470]\\\\ #,##0.00\",\n\t\t206: \"[$\\u20A9-412]#,##0.00\",\n\t\t207: \"[$\\u20AA-40D]\\\\ #,##0.00\",\n\t\t208: \"#,##0.00\\\\ [$\\u20AB-42A]\",\n\t\t209: \"#,##0.00\\\\ [$\\u20AC-42D]\",\n\t\t210: \"#,##0.00\\\\ [$\\u20AC-47E]\",\n\t\t211: \"#,##0.00\\\\ [$\\u20AC-403]\",\n\t\t212: \"#,##0.00\\\\ [$\\u20AC-483]\",\n\t\t213: \"[$\\u20AC-813]\\\\ #,##0.00\",\n\t\t214: \"[$\\u20AC-413]\\\\ #,##0.00\",\n\t\t215: \"[$\\u20AC-1809]#,##0.00\",\n\t\t216: \"#,##0.00\\\\ [$\\u20AC-425]\",\n\t\t217: \"[$\\u20AC-2]\\\\ #,##0.00\",\n\t\t218: \"#,##0.00\\\\ [$\\u20AC-1]\",\n\t\t219: \"#,##0.00\\\\ [$\\u20AC-40B]\",\n\t\t220: \"#,##0.00\\\\ [$\\u20AC-80C]\",\n\t\t221: \"#,##0.00\\\\ [$\\u20AC-40C]\",\n\t\t222: \"#,##0.00\\\\ [$\\u20AC-140C]\",\n\t\t223: \"#,##0.00\\\\ [$\\u20AC-180C]\",\n\t\t224: \"[$\\u20AC-200C]#,##0.00\",\n\t\t225: \"#,##0.00\\\\ [$\\u20AC-456]\",\n\t\t226: \"#,##0.00\\\\ [$\\u20AC-C07]\",\n\t\t227: \"#,##0.00\\\\ [$\\u20AC-407]\",\n\t\t228: \"#,##0.00\\\\ [$\\u20AC-1007]\",\n\t\t229: \"#,##0.00\\\\ [$\\u20AC-408]\",\n\t\t230: \"#,##0.00\\\\ [$\\u20AC-243B]\",\n\t\t231: \"[$\\u20AC-83C]#,##0.00\",\n\t\t232: \"[$\\u20AC-410]\\\\ #,##0.00\",\n\t\t233: \"[$\\u20AC-476]#,##0.00\",\n\t\t234: \"#,##0.00\\\\ [$\\u20AC-2C1A]\",\n\t\t235: \"[$\\u20AC-426]\\\\ #,##0.00\",\n\t\t236: \"#,##0.00\\\\ [$\\u20AC-427]\",\n\t\t237: \"#,##0.00\\\\ [$\\u20AC-82E]\",\n\t\t238: \"#,##0.00\\\\ [$\\u20AC-46E]\",\n\t\t239: \"[$\\u20AC-43A]#,##0.00\",\n\t\t240: \"#,##0.00\\\\ [$\\u20AC-C3B]\",\n\t\t241: \"#,##0.00\\\\ [$\\u20AC-482]\",\n\t\t242: \"#,##0.00\\\\ [$\\u20AC-816]\",\n\t\t243: \"#,##0.00\\\\ [$\\u20AC-301A]\",\n\t\t244: \"#,##0.00\\\\ [$\\u20AC-203B]\",\n\t\t245: \"#,##0.00\\\\ [$\\u20AC-41B]\",\n\t\t246: \"#,##0.00\\\\ [$\\u20AC-424]\",\n\t\t247: \"#,##0.00\\\\ [$\\u20AC-C0A]\",\n\t\t248: \"#,##0.00\\\\ [$\\u20AC-81D]\",\n\t\t249: \"#,##0.00\\\\ [$\\u20AC-484]\",\n\t\t250: \"#,##0.00\\\\ [$\\u20AC-42E]\",\n\t\t251: \"[$\\u20AC-462]\\\\ #,##0.00\",\n\t\t252: \"#,##0.00\\\\ [$₭-454]\",\n\t\t253: \"#,##0.00\\\\ [$₮-450]\",\n\t\t254: \"[$\\u20AE-C50]#,##0.00\",\n\t\t255: \"[$\\u20B1-3409]#,##0.00\",\n\t\t256: \"[$\\u20B1-464]#,##0.00\",\n\t\t257: \"#,##0.00[$\\u20B4-422]\",\n\t\t258: \"[$\\u20B8-43F]#,##0.00\",\n\t\t259: \"[$\\u20B9-460]#,##0.00\",\n\t\t260: \"[$\\u20B9-4009]\\\\ #,##0.00\",\n\t\t261: \"[$\\u20B9-447]\\\\ #,##0.00\",\n\t\t262: \"[$\\u20B9-439]\\\\ #,##0.00\",\n\t\t263: \"[$\\u20B9-44B]\\\\ #,##0.00\",\n\t\t264: \"[$\\u20B9-860]#,##0.00\",\n\t\t265: \"[$\\u20B9-457]\\\\ #,##0.00\",\n\t\t266: \"[$\\u20B9-458]#,##0.00\",\n\t\t267: \"[$\\u20B9-44E]\\\\ #,##0.00\",\n\t\t268: \"[$\\u20B9-861]#,##0.00\",\n\t\t269: \"[$\\u20B9-448]\\\\ #,##0.00\",\n\t\t270: \"[$\\u20B9-446]\\\\ #,##0.00\",\n\t\t271: \"[$\\u20B9-44F]\\\\ #,##0.00\",\n\t\t272: \"[$\\u20B9-459]#,##0.00\",\n\t\t273: \"[$\\u20B9-449]\\\\ #,##0.00\",\n\t\t274: \"[$\\u20B9-820]#,##0.00\",\n\t\t275: \"#,##0.00\\\\ [$\\u20BA-41F]\",\n\t\t276: \"#,##0.00\\\\ [$\\u20BC-42C]\",\n\t\t277: \"#,##0.00\\\\ [$\\u20BC-82C]\",\n\t\t278: \"#,##0.00\\\\ [$\\u20BD-419]\",\n\t\t279: \"#,##0.00[$\\u20BD-485]\",\n\t\t280: \"#,##0.00\\\\ [$\\u20BE-437]\",\n\t\t281: \"[$B/.-180A]\\\\ #,##0.00\",\n\t\t282: \"[$Br-472]#,##0.00\",\n\t\t283: \"[$Br-477]#,##0.00\",\n\t\t284: \"#,##0.00[$Br-473]\",\n\t\t285: \"[$Bs-46B]\\\\ #,##0.00\",\n\t\t286: \"[$Bs-400A]\\\\ #,##0.00\",\n\t\t287: \"[$Bs.-200A]\\\\ #,##0.00\",\n\t\t288: \"[$BWP-832]\\\\ #,##0.00\",\n\t\t289: \"[$C$-4C0A]#,##0.00\",\n\t\t290: \"[$CA$-85D]#,##0.00\",\n\t\t291: \"[$CA$-47C]#,##0.00\",\n\t\t292: \"[$CA$-45D]#,##0.00\",\n\t\t293: \"[$CFA-340C]#,##0.00\",\n\t\t294: \"[$CFA-280C]#,##0.00\",\n\t\t295: \"#,##0.00\\\\ [$CFA-867]\",\n\t\t296: \"#,##0.00\\\\ [$CFA-488]\",\n\t\t297: \"#,##0.00\\\\ [$CHF-100C]\",\n\t\t298: \"[$CHF-1407]\\\\ #,##0.00\",\n\t\t299: \"[$CHF-807]\\\\ #,##0.00\",\n\t\t300: \"[$CHF-810]\\\\ #,##0.00\",\n\t\t301: \"[$CHF-417]\\\\ #,##0.00\",\n\t\t302: \"[$CLP-47A]\\\\ #,##0.00\",\n\t\t303: \"[$CN¥-850]#,##0.00\",\n\t\t304: \"#,##0.00\\\\ [$DZD-85F]\",\n\t\t305: \"[$FCFA-2C0C]#,##0.00\",\n\t\t306: \"#,##0.00\\\\ [$Ft-40E]\",\n\t\t307: \"[$G-3C0C]#,##0.00\",\n\t\t308: \"[$Gs.-3C0A]\\\\ #,##0.00\",\n\t\t309: \"[$GTQ-486]#,##0.00\",\n\t\t310: \"[$HK$-C04]#,##0.00\",\n\t\t311: \"[$HK$-3C09]#,##0.00\",\n\t\t312: \"#,##0.00\\\\ [$HRK-41A]\",\n\t\t313: \"[$IDR-3809]#,##0.00\",\n\t\t314: \"[$IQD-492]#,##0.00\",\n\t\t315: \"#,##0.00\\\\ [$ISK-40F]\",\n\t\t316: \"[$K-455]#,##0.00\",\n\t\t317: \"#,##0.00\\\\ [$K\\u010D-405]\",\n\t\t318: \"#,##0.00\\\\ [$KM-141A]\",\n\t\t319: \"#,##0.00\\\\ [$KM-101A]\",\n\t\t320: \"#,##0.00\\\\ [$KM-181A]\",\n\t\t321: \"[$kr-438]\\\\ #,##0.00\",\n\t\t322: \"[$kr-43B]\\\\ #,##0.00\",\n\t\t323: \"#,##0.00\\\\ [$kr-83B]\",\n\t\t324: \"[$kr-414]\\\\ #,##0.00\",\n\t\t325: \"[$kr-814]\\\\ #,##0.00\",\n\t\t326: \"#,##0.00\\\\ [$kr-41D]\",\n\t\t327: \"[$kr.-406]\\\\ #,##0.00\",\n\t\t328: \"[$kr.-46F]\\\\ #,##0.00\",\n\t\t329: \"[$Ksh-441]#,##0.00\",\n\t\t330: \"[$L-818]#,##0.00\",\n\t\t331: \"[$L-819]#,##0.00\",\n\t\t332: \"[$L-480A]\\\\ #,##0.00\",\n\t\t333: \"#,##0.00\\\\ [$Lek\\u00EB-41C]\",\n\t\t334: \"[$MAD-45F]#,##0.00\",\n\t\t335: \"[$MAD-380C]#,##0.00\",\n\t\t336: \"#,##0.00\\\\ [$MAD-105F]\",\n\t\t337: \"[$MOP$-1404]#,##0.00\",\n\t\t338: \"#,##0.00\\\\ [$MVR-465]_-\",\n\t\t339: \"#,##0.00[$Nfk-873]\",\n\t\t340: \"[$NGN-466]#,##0.00\",\n\t\t341: \"[$NGN-467]#,##0.00\",\n\t\t342: \"[$NGN-469]#,##0.00\",\n\t\t343: \"[$NGN-471]#,##0.00\",\n\t\t344: \"[$NOK-103B]\\\\ #,##0.00\",\n\t\t345: \"[$NOK-183B]\\\\ #,##0.00\",\n\t\t346: \"[$NZ$-481]#,##0.00\",\n\t\t347: \"[$PKR-859]\\\\ #,##0.00\",\n\t\t348: \"[$PYG-474]#,##0.00\",\n\t\t349: \"[$Q-100A]#,##0.00\",\n\t\t350: \"[$R-436]\\\\ #,##0.00\",\n\t\t351: \"[$R-1C09]\\\\ #,##0.00\",\n\t\t352: \"[$R-435]\\\\ #,##0.00\",\n\t\t353: \"[$R$-416]\\\\ #,##0.00\",\n\t\t354: \"[$RD$-1C0A]#,##0.00\",\n\t\t355: \"#,##0.00\\\\ [$RF-487]\",\n\t\t356: \"[$RM-4409]#,##0.00\",\n\t\t357: \"[$RM-43E]#,##0.00\",\n\t\t358: \"#,##0.00\\\\ [$RON-418]\",\n\t\t359: \"[$Rp-421]#,##0.00\",\n\t\t360: \"[$Rs-420]#,##0.00_-\",\n\t\t361: \"[$Rs.-849]\\\\ #,##0.00\",\n\t\t362: \"#,##0.00\\\\ [$RSD-81A]\",\n\t\t363: \"#,##0.00\\\\ [$RSD-C1A]\",\n\t\t364: \"#,##0.00\\\\ [$RUB-46D]\",\n\t\t365: \"#,##0.00\\\\ [$RUB-444]\",\n\t\t366: \"[$S/.-C6B]\\\\ #,##0.00\",\n\t\t367: \"[$S/.-280A]\\\\ #,##0.00\",\n\t\t368: \"#,##0.00\\\\ [$SEK-143B]\",\n\t\t369: \"#,##0.00\\\\ [$SEK-1C3B]\",\n\t\t370: \"#,##0.00\\\\ [$so\\u02BBm-443]\",\n\t\t371: \"#,##0.00\\\\ [$so\\u02BBm-843]\",\n\t\t372: \"#,##0.00\\\\ [$SYP-45A]\",\n\t\t373: \"[$THB-41E]#,##0.00\",\n\t\t374: \"#,##0.00[$TMT-442]\",\n\t\t375: \"[$US$-3009]#,##0.00\",\n\t\t376: \"[$ZAR-46C]\\\\ #,##0.00\",\n\t\t377: \"[$ZAR-430]#,##0.00\",\n\t\t378: \"[$ZAR-431]#,##0.00\",\n\t\t379: \"[$ZAR-432]\\\\ #,##0.00\",\n\t\t380: \"[$ZAR-433]#,##0.00\",\n\t\t381: \"[$ZAR-434]\\\\ #,##0.00\",\n\t\t382: \"#,##0.00\\\\ [$z\\u0142-415]\",\n\t\t383: \"#,##0.00\\\\ [$\\u0434\\u0435\\u043D-42F]\",\n\t\t384: \"#,##0.00\\\\ [$КМ-201A]\",\n\t\t385: \"#,##0.00\\\\ [$КМ-1C1A]\",\n\t\t386: \"#,##0.00\\\\ [$\\u043B\\u0432.-402]\",\n\t\t387: \"#,##0.00\\\\ [$р.-423]\",\n\t\t388: \"#,##0.00\\\\ [$\\u0441\\u043E\\u043C-440]\",\n\t\t389: \"#,##0.00\\\\ [$\\u0441\\u043E\\u043C-428]\",\n\t\t390: \"[$\\u062C.\\u0645.-C01]\\\\ #,##0.00_-\",\n\t\t391: \"[$\\u062F.\\u0623.-2C01]\\\\ #,##0.00_-\",\n\t\t392: \"[$\\u062F.\\u0625.-3801]\\\\ #,##0.00_-\",\n\t\t393: \"[$\\u062F.\\u0628.-3C01]\\\\ #,##0.00_-\",\n\t\t394: \"[$\\u062F.\\u062A.-1C01]\\\\ #,##0.00_-\",\n\t\t395: \"[$\\u062F.\\u062C.-1401]\\\\ #,##0.00_-\",\n\t\t396: \"[$\\u062F.\\u0639.-801]\\\\ #,##0.00_-\",\n\t\t397: \"[$\\u062F.\\u0643.-3401]\\\\ #,##0.00_-\",\n\t\t398: \"[$\\u062F.\\u0644.-1001]#,##0.00_-\",\n\t\t399: \"[$\\u062F.\\u0645.-1801]\\\\ #,##0.00_-\",\n\t\t400: \"[$\\u0631-846]\\\\ #,##0.00\",\n\t\t401: \"[$\\u0631.\\u0633.-401]\\\\ #,##0.00_-\",\n\t\t402: \"[$\\u0631.\\u0639.-2001]\\\\ #,##0.00_-\",\n\t\t403: \"[$\\u0631.\\u0642.-4001]\\\\ #,##0.00_-\",\n\t\t404: \"[$\\u0631.\\u064A.-2401]\\\\ #,##0.00_-\",\n\t\t405: \"[$\\u0631\\u06CC\\u0627\\u0644-429]#,##0.00_-\",\n\t\t406: \"[$\\u0644.\\u0633.-2801]\\\\ #,##0.00_-\",\n\t\t407: \"[$\\u0644.\\u0644.-3001]\\\\ #,##0.00_-\",\n\t\t408: \"[$\\u1265\\u122D-45E]#,##0.00\",\n\t\t409: \"[$\\u0930\\u0942-461]#,##0.00\",\n\t\t410: \"[$\\u0DBB\\u0DD4.-45B]\\\\ #,##0.00\",\n\t\t411: \"[$ADP]\\\\ #,##0.00\",\n\t\t412: \"[$AED]\\\\ #,##0.00\",\n\t\t413: \"[$AFA]\\\\ #,##0.00\",\n\t\t414: \"[$AFN]\\\\ #,##0.00\",\n\t\t415: \"[$ALL]\\\\ #,##0.00\",\n\t\t416: \"[$AMD]\\\\ #,##0.00\",\n\t\t417: \"[$ANG]\\\\ #,##0.00\",\n\t\t418: \"[$AOA]\\\\ #,##0.00\",\n\t\t419: \"[$ARS]\\\\ #,##0.00\",\n\t\t420: \"[$ATS]\\\\ #,##0.00\",\n\t\t421: \"[$AUD]\\\\ #,##0.00\",\n\t\t422: \"[$AWG]\\\\ #,##0.00\",\n\t\t423: \"[$AZM]\\\\ #,##0.00\",\n\t\t424: \"[$AZN]\\\\ #,##0.00\",\n\t\t425: \"[$BAM]\\\\ #,##0.00\",\n\t\t426: \"[$BBD]\\\\ #,##0.00\",\n\t\t427: \"[$BDT]\\\\ #,##0.00\",\n\t\t428: \"[$BEF]\\\\ #,##0.00\",\n\t\t429: \"[$BGL]\\\\ #,##0.00\",\n\t\t430: \"[$BGN]\\\\ #,##0.00\",\n\t\t431: \"[$BHD]\\\\ #,##0.00\",\n\t\t432: \"[$BIF]\\\\ #,##0.00\",\n\t\t433: \"[$BMD]\\\\ #,##0.00\",\n\t\t434: \"[$BND]\\\\ #,##0.00\",\n\t\t435: \"[$BOB]\\\\ #,##0.00\",\n\t\t436: \"[$BOV]\\\\ #,##0.00\",\n\t\t437: \"[$BRL]\\\\ #,##0.00\",\n\t\t438: \"[$BSD]\\\\ #,##0.00\",\n\t\t439: \"[$BTN]\\\\ #,##0.00\",\n\t\t440: \"[$BWP]\\\\ #,##0.00\",\n\t\t441: \"[$BYR]\\\\ #,##0.00\",\n\t\t442: \"[$BZD]\\\\ #,##0.00\",\n\t\t443: \"[$CAD]\\\\ #,##0.00\",\n\t\t444: \"[$CDF]\\\\ #,##0.00\",\n\t\t445: \"[$CHE]\\\\ #,##0.00\",\n\t\t446: \"[$CHF]\\\\ #,##0.00\",\n\t\t447: \"[$CHW]\\\\ #,##0.00\",\n\t\t448: \"[$CLF]\\\\ #,##0.00\",\n\t\t449: \"[$CLP]\\\\ #,##0.00\",\n\t\t450: \"[$CNY]\\\\ #,##0.00\",\n\t\t451: \"[$COP]\\\\ #,##0.00\",\n\t\t452: \"[$COU]\\\\ #,##0.00\",\n\t\t453: \"[$CRC]\\\\ #,##0.00\",\n\t\t454: \"[$CSD]\\\\ #,##0.00\",\n\t\t455: \"[$CUC]\\\\ #,##0.00\",\n\t\t456: \"[$CVE]\\\\ #,##0.00\",\n\t\t457: \"[$CYP]\\\\ #,##0.00\",\n\t\t458: \"[$CZK]\\\\ #,##0.00\",\n\t\t459: \"[$DEM]\\\\ #,##0.00\",\n\t\t460: \"[$DJF]\\\\ #,##0.00\",\n\t\t461: \"[$DKK]\\\\ #,##0.00\",\n\t\t462: \"[$DOP]\\\\ #,##0.00\",\n\t\t463: \"[$DZD]\\\\ #,##0.00\",\n\t\t464: \"[$ECS]\\\\ #,##0.00\",\n\t\t465: \"[$ECV]\\\\ #,##0.00\",\n\t\t466: \"[$EEK]\\\\ #,##0.00\",\n\t\t467: \"[$EGP]\\\\ #,##0.00\",\n\t\t468: \"[$ERN]\\\\ #,##0.00\",\n\t\t469: \"[$ESP]\\\\ #,##0.00\",\n\t\t470: \"[$ETB]\\\\ #,##0.00\",\n\t\t471: \"[$EUR]\\\\ #,##0.00\",\n\t\t472: \"[$FIM]\\\\ #,##0.00\",\n\t\t473: \"[$FJD]\\\\ #,##0.00\",\n\t\t474: \"[$FKP]\\\\ #,##0.00\",\n\t\t475: \"[$FRF]\\\\ #,##0.00\",\n\t\t476: \"[$GBP]\\\\ #,##0.00\",\n\t\t477: \"[$GEL]\\\\ #,##0.00\",\n\t\t478: \"[$GHC]\\\\ #,##0.00\",\n\t\t479: \"[$GHS]\\\\ #,##0.00\",\n\t\t480: \"[$GIP]\\\\ #,##0.00\",\n\t\t481: \"[$GMD]\\\\ #,##0.00\",\n\t\t482: \"[$GNF]\\\\ #,##0.00\",\n\t\t483: \"[$GRD]\\\\ #,##0.00\",\n\t\t484: \"[$GTQ]\\\\ #,##0.00\",\n\t\t485: \"[$GYD]\\\\ #,##0.00\",\n\t\t486: \"[$HKD]\\\\ #,##0.00\",\n\t\t487: \"[$HNL]\\\\ #,##0.00\",\n\t\t488: \"[$HRK]\\\\ #,##0.00\",\n\t\t489: \"[$HTG]\\\\ #,##0.00\",\n\t\t490: \"[$HUF]\\\\ #,##0.00\",\n\t\t491: \"[$IDR]\\\\ #,##0.00\",\n\t\t492: \"[$IEP]\\\\ #,##0.00\",\n\t\t493: \"[$ILS]\\\\ #,##0.00\",\n\t\t494: \"[$INR]\\\\ #,##0.00\",\n\t\t495: \"[$IQD]\\\\ #,##0.00\",\n\t\t496: \"[$IRR]\\\\ #,##0.00\",\n\t\t497: \"[$ISK]\\\\ #,##0.00\",\n\t\t498: \"[$ITL]\\\\ #,##0.00\",\n\t\t499: \"[$JMD]\\\\ #,##0.00\",\n\t\t500: \"[$JOD]\\\\ #,##0.00\",\n\t\t501: \"[$JPY]\\\\ #,##0.00\",\n\t\t502: \"[$KAF]\\\\ #,##0.00\",\n\t\t503: \"[$KES]\\\\ #,##0.00\",\n\t\t504: \"[$KGS]\\\\ #,##0.00\",\n\t\t505: \"[$KHR]\\\\ #,##0.00\",\n\t\t506: \"[$KMF]\\\\ #,##0.00\",\n\t\t507: \"[$KPW]\\\\ #,##0.00\",\n\t\t508: \"[$KRW]\\\\ #,##0.00\",\n\t\t509: \"[$KWD]\\\\ #,##0.00\",\n\t\t510: \"[$KYD]\\\\ #,##0.00\",\n\t\t511: \"[$KZT]\\\\ #,##0.00\",\n\t\t512: \"[$LAK]\\\\ #,##0.00\",\n\t\t513: \"[$LBP]\\\\ #,##0.00\",\n\t\t514: \"[$LKR]\\\\ #,##0.00\",\n\t\t515: \"[$LRD]\\\\ #,##0.00\",\n\t\t516: \"[$LSL]\\\\ #,##0.00\",\n\t\t517: \"[$LTL]\\\\ #,##0.00\",\n\t\t518: \"[$LUF]\\\\ #,##0.00\",\n\t\t519: \"[$LVL]\\\\ #,##0.00\",\n\t\t520: \"[$LYD]\\\\ #,##0.00\",\n\t\t521: \"[$MAD]\\\\ #,##0.00\",\n\t\t522: \"[$MDL]\\\\ #,##0.00\",\n\t\t523: \"[$MGA]\\\\ #,##0.00\",\n\t\t524: \"[$MGF]\\\\ #,##0.00\",\n\t\t525: \"[$MKD]\\\\ #,##0.00\",\n\t\t526: \"[$MMK]\\\\ #,##0.00\",\n\t\t527: \"[$MNT]\\\\ #,##0.00\",\n\t\t528: \"[$MOP]\\\\ #,##0.00\",\n\t\t529: \"[$MRO]\\\\ #,##0.00\",\n\t\t530: \"[$MTL]\\\\ #,##0.00\",\n\t\t531: \"[$MUR]\\\\ #,##0.00\",\n\t\t532: \"[$MVR]\\\\ #,##0.00\",\n\t\t533: \"[$MWK]\\\\ #,##0.00\",\n\t\t534: \"[$MXN]\\\\ #,##0.00\",\n\t\t535: \"[$MXV]\\\\ #,##0.00\",\n\t\t536: \"[$MYR]\\\\ #,##0.00\",\n\t\t537: \"[$MZM]\\\\ #,##0.00\",\n\t\t538: \"[$MZN]\\\\ #,##0.00\",\n\t\t539: \"[$NAD]\\\\ #,##0.00\",\n\t\t540: \"[$NGN]\\\\ #,##0.00\",\n\t\t541: \"[$NIO]\\\\ #,##0.00\",\n\t\t542: \"[$NLG]\\\\ #,##0.00\",\n\t\t543: \"[$NOK]\\\\ #,##0.00\",\n\t\t544: \"[$NPR]\\\\ #,##0.00\",\n\t\t545: \"[$NTD]\\\\ #,##0.00\",\n\t\t546: \"[$NZD]\\\\ #,##0.00\",\n\t\t547: \"[$OMR]\\\\ #,##0.00\",\n\t\t548: \"[$PAB]\\\\ #,##0.00\",\n\t\t549: \"[$PEN]\\\\ #,##0.00\",\n\t\t550: \"[$PGK]\\\\ #,##0.00\",\n\t\t551: \"[$PHP]\\\\ #,##0.00\",\n\t\t552: \"[$PKR]\\\\ #,##0.00\",\n\t\t553: \"[$PLN]\\\\ #,##0.00\",\n\t\t554: \"[$PTE]\\\\ #,##0.00\",\n\t\t555: \"[$PYG]\\\\ #,##0.00\",\n\t\t556: \"[$QAR]\\\\ #,##0.00\",\n\t\t557: \"[$ROL]\\\\ #,##0.00\",\n\t\t558: \"[$RON]\\\\ #,##0.00\",\n\t\t559: \"[$RSD]\\\\ #,##0.00\",\n\t\t560: \"[$RUB]\\\\ #,##0.00\",\n\t\t561: \"[$RUR]\\\\ #,##0.00\",\n\t\t562: \"[$RWF]\\\\ #,##0.00\",\n\t\t563: \"[$SAR]\\\\ #,##0.00\",\n\t\t564: \"[$SBD]\\\\ #,##0.00\",\n\t\t565: \"[$SCR]\\\\ #,##0.00\",\n\t\t566: \"[$SDD]\\\\ #,##0.00\",\n\t\t567: \"[$SDG]\\\\ #,##0.00\",\n\t\t568: \"[$SDP]\\\\ #,##0.00\",\n\t\t569: \"[$SEK]\\\\ #,##0.00\",\n\t\t570: \"[$SGD]\\\\ #,##0.00\",\n\t\t571: \"[$SHP]\\\\ #,##0.00\",\n\t\t572: \"[$SIT]\\\\ #,##0.00\",\n\t\t573: \"[$SKK]\\\\ #,##0.00\",\n\t\t574: \"[$SLL]\\\\ #,##0.00\",\n\t\t575: \"[$SOS]\\\\ #,##0.00\",\n\t\t576: \"[$SPL]\\\\ #,##0.00\",\n\t\t577: \"[$SRD]\\\\ #,##0.00\",\n\t\t578: \"[$SRG]\\\\ #,##0.00\",\n\t\t579: \"[$STD]\\\\ #,##0.00\",\n\t\t580: \"[$SVC]\\\\ #,##0.00\",\n\t\t581: \"[$SYP]\\\\ #,##0.00\",\n\t\t582: \"[$SZL]\\\\ #,##0.00\",\n\t\t583: \"[$THB]\\\\ #,##0.00\",\n\t\t584: \"[$TJR]\\\\ #,##0.00\",\n\t\t585: \"[$TJS]\\\\ #,##0.00\",\n\t\t586: \"[$TMM]\\\\ #,##0.00\",\n\t\t587: \"[$TMT]\\\\ #,##0.00\",\n\t\t588: \"[$TND]\\\\ #,##0.00\",\n\t\t589: \"[$TOP]\\\\ #,##0.00\",\n\t\t590: \"[$TRL]\\\\ #,##0.00\",\n\t\t591: \"[$TRY]\\\\ #,##0.00\",\n\t\t592: \"[$TTD]\\\\ #,##0.00\",\n\t\t593: \"[$TWD]\\\\ #,##0.00\",\n\t\t594: \"[$TZS]\\\\ #,##0.00\",\n\t\t595: \"[$UAH]\\\\ #,##0.00\",\n\t\t596: \"[$UGX]\\\\ #,##0.00\",\n\t\t597: \"[$USD]\\\\ #,##0.00\",\n\t\t598: \"[$USN]\\\\ #,##0.00\",\n\t\t599: \"[$USS]\\\\ #,##0.00\",\n\t\t600: \"[$UYI]\\\\ #,##0.00\",\n\t\t601: \"[$UYU]\\\\ #,##0.00\",\n\t\t602: \"[$UZS]\\\\ #,##0.00\",\n\t\t603: \"[$VEB]\\\\ #,##0.00\",\n\t\t604: \"[$VEF]\\\\ #,##0.00\",\n\t\t605: \"[$VND]\\\\ #,##0.00\",\n\t\t606: \"[$VUV]\\\\ #,##0.00\",\n\t\t607: \"[$WST]\\\\ #,##0.00\",\n\t\t608: \"[$XAF]\\\\ #,##0.00\",\n\t\t609: \"[$XAG]\\\\ #,##0.00\",\n\t\t610: \"[$XAU]\\\\ #,##0.00\",\n\t\t611: \"[$XB5]\\\\ #,##0.00\",\n\t\t612: \"[$XBA]\\\\ #,##0.00\",\n\t\t613: \"[$XBB]\\\\ #,##0.00\",\n\t\t614: \"[$XBC]\\\\ #,##0.00\",\n\t\t615: \"[$XBD]\\\\ #,##0.00\",\n\t\t616: \"[$XCD]\\\\ #,##0.00\",\n\t\t617: \"[$XDR]\\\\ #,##0.00\",\n\t\t618: \"[$XFO]\\\\ #,##0.00\",\n\t\t619: \"[$XFU]\\\\ #,##0.00\",\n\t\t620: \"[$XOF]\\\\ #,##0.00\",\n\t\t621: \"[$XPD]\\\\ #,##0.00\",\n\t\t622: \"[$XPF]\\\\ #,##0.00\",\n\t\t623: \"[$XPT]\\\\ #,##0.00\",\n\t\t624: \"[$XTS]\\\\ #,##0.00\",\n\t\t625: \"[$XXX]\\\\ #,##0.00\",\n\t\t626: \"[$YER]\\\\ #,##0.00\",\n\t\t627: \"[$YUM]\\\\ #,##0.00\",\n\t\t628: \"[$ZAR]\\\\ #,##0.00\",\n\t\t629: \"[$ZMK]\\\\ #,##0.00\",\n\t\t630: \"[$ZMW]\\\\ #,##0.00\",\n\t\t631: \"[$ZWD]\\\\ #,##0.00\",\n\t\t632: \"[$ZWL]\\\\ #,##0.00\",\n\t\t633: \"[$ZWN]\\\\ #,##0.00\",\n\t\t634: \"[$ZWR]\\\\ #,##0.00\",\n\t}\n\t// supportedTokenTypes list the supported number format token types\n\t// currently.\n\tsupportedTokenTypes = []string{\n\t\tnfp.TokenTypeAlignment,\n\t\tnfp.TokenSubTypeCurrencyString,\n\t\tnfp.TokenSubTypeLanguageInfo,\n\t\tnfp.TokenTypeColor,\n\t\tnfp.TokenTypeCurrencyLanguage,\n\t\tnfp.TokenTypeDateTimes,\n\t\tnfp.TokenTypeDecimalPoint,\n\t\tnfp.TokenTypeDenominator,\n\t\tnfp.TokenTypeDigitalPlaceHolder,\n\t\tnfp.TokenTypeElapsedDateTimes,\n\t\tnfp.TokenTypeExponential,\n\t\tnfp.TokenTypeFraction,\n\t\tnfp.TokenTypeGeneral,\n\t\tnfp.TokenTypeHashPlaceHolder,\n\t\tnfp.TokenTypeLiteral,\n\t\tnfp.TokenTypePercent,\n\t\tnfp.TokenTypeRepeatsChar,\n\t\tnfp.TokenTypeSwitchArgument,\n\t\tnfp.TokenTypeTextPlaceHolder,\n\t\tnfp.TokenTypeThousandsSeparator,\n\t\tnfp.TokenTypeZeroPlaceHolder,\n\t}\n\t// supportedNumberTokenTypes list the supported number token types.\n\tsupportedNumberTokenTypes = []string{\n\t\tnfp.TokenTypeDenominator,\n\t\tnfp.TokenTypeDigitalPlaceHolder,\n\t\tnfp.TokenTypeExponential,\n\t\tnfp.TokenTypeFraction,\n\t\tnfp.TokenTypeHashPlaceHolder,\n\t\tnfp.TokenTypePercent,\n\t\tnfp.TokenTypeZeroPlaceHolder,\n\t}\n\t// supportedDateTimeTokenTypes list the supported date and time token types.\n\tsupportedDateTimeTokenTypes = []string{\n\t\tnfp.TokenTypeDateTimes,\n\t\tnfp.TokenTypeElapsedDateTimes,\n\t}\n\t// supportedLanguageInfo directly maps the supported language decimal ID and\n\t// tags.\n\tsupportedLanguageInfo = map[int]languageInfo{\n\t\t54:    {tags: []string{\"af\"}, localMonth: localMonthsNameAfrikaans, apFmt: apFmtAfrikaans, weekdayNames: weekdayNamesAfrikaans, weekdayNamesAbbr: weekdayNamesAfrikaansAbbr},\n\t\t1078:  {tags: []string{\"af-ZA\"}, localMonth: localMonthsNameAfrikaans, apFmt: apFmtAfrikaans, weekdayNames: weekdayNamesAfrikaans, weekdayNamesAbbr: weekdayNamesAfrikaansAbbr},\n\t\t28:    {tags: []string{\"sq\"}, localMonth: localMonthsNameAlbanian, apFmt: apFmtAlbanian, weekdayNames: weekdayNamesAlbanian, weekdayNamesAbbr: weekdayNamesAlbanianAbbr},\n\t\t1052:  {tags: []string{\"sq-AL\"}, localMonth: localMonthsNameAlbanian, apFmt: apFmtAlbanian, weekdayNames: weekdayNamesAlbanian, weekdayNamesAbbr: weekdayNamesAlbanianAbbr},\n\t\t132:   {tags: []string{\"gsw\"}, localMonth: localMonthsNameAlsatian, apFmt: apFmtAlsatian, weekdayNames: weekdayNamesAlsatian, weekdayNamesAbbr: weekdayNamesAlsatianAbbr},\n\t\t1156:  {tags: []string{\"gsw-FR\"}, localMonth: localMonthsNameAlsatianFrance, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesAlsatianFrance, weekdayNamesAbbr: weekdayNamesAlsatianFranceAbbr},\n\t\t94:    {tags: []string{\"am\"}, localMonth: localMonthsNameAmharic, apFmt: apFmtAmharic, weekdayNames: weekdayNamesAmharic, weekdayNamesAbbr: weekdayNamesAmharicAbbr},\n\t\t1118:  {tags: []string{\"am-ET\"}, localMonth: localMonthsNameAmharic, apFmt: apFmtAmharic, weekdayNames: weekdayNamesAmharic, weekdayNamesAbbr: weekdayNamesAmharicAbbr},\n\t\t1:     {tags: []string{\"ar\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t5121:  {tags: []string{\"ar-DZ\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t15361: {tags: []string{\"ar-BH\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t3073:  {tags: []string{\"ar-EG\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t2049:  {tags: []string{\"ar-IQ\"}, localMonth: localMonthsNameArabicIraq, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t11265: {tags: []string{\"ar-JO\"}, localMonth: localMonthsNameArabicIraq, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t13313: {tags: []string{\"ar-KW\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t12289: {tags: []string{\"ar-LB\"}, localMonth: localMonthsNameArabicIraq, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t4097:  {tags: []string{\"ar-LY\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t6145:  {tags: []string{\"ar-MA\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t8193:  {tags: []string{\"ar-OM\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t16385: {tags: []string{\"ar-QA\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t1025:  {tags: []string{\"ar-SA\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t10241: {tags: []string{\"ar-SY\"}, localMonth: localMonthsNameArabicIraq, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t7169:  {tags: []string{\"ar-TN\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t14337: {tags: []string{\"ar-AE\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t9217:  {tags: []string{\"ar-YE\"}, localMonth: localMonthsNameArabic, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t43:    {tags: []string{\"hy\"}, localMonth: localMonthsNameArmenian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesArmenian, weekdayNamesAbbr: weekdayNamesArmenianAbbr},\n\t\t1067:  {tags: []string{\"hy-AM\"}, localMonth: localMonthsNameArmenian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesArmenian, weekdayNamesAbbr: weekdayNamesArmenianAbbr},\n\t\t77:    {tags: []string{\"as\"}, localMonth: localMonthsNameAssamese, apFmt: apFmtAssamese, weekdayNames: weekdayNamesAssamese, weekdayNamesAbbr: weekdayNamesAssameseAbbr},\n\t\t1101:  {tags: []string{\"as-IN\"}, localMonth: localMonthsNameAssamese, apFmt: apFmtAssamese, weekdayNames: weekdayNamesAssamese, weekdayNamesAbbr: weekdayNamesAssameseAbbr},\n\t\t29740: {tags: []string{\"az-Cyrl\"}, localMonth: localMonthsNameAzerbaijaniCyrillic, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesAzerbaijaniCyrillic, weekdayNamesAbbr: weekdayNamesAzerbaijaniCyrillicAbbr},\n\t\t2092:  {tags: []string{\"az-Cyrl-AZ\"}, localMonth: localMonthsNameAzerbaijaniCyrillic, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesAzerbaijaniCyrillic, weekdayNamesAbbr: weekdayNamesAzerbaijaniCyrillicAbbr},\n\t\t44:    {tags: []string{\"az\"}, localMonth: localMonthsNameAzerbaijani, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesAzerbaijani, weekdayNamesAbbr: weekdayNamesAzerbaijaniAbbr},\n\t\t30764: {tags: []string{\"az-Latn\"}, localMonth: localMonthsNameAzerbaijani, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesAzerbaijani, weekdayNamesAbbr: weekdayNamesAzerbaijaniAbbr},\n\t\t1068:  {tags: []string{\"az-Latn-AZ\"}, localMonth: localMonthsNameAzerbaijani, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesAzerbaijani, weekdayNamesAbbr: weekdayNamesAzerbaijaniAbbr},\n\t\t69:    {tags: []string{\"bn\"}, localMonth: localMonthsNameBangla, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBangla, weekdayNamesAbbr: weekdayNamesBanglaAbbr},\n\t\t2117:  {tags: []string{\"bn-BD\"}, localMonth: localMonthsNameBangla, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBangla, weekdayNamesAbbr: weekdayNamesBanglaAbbr},\n\t\t1093:  {tags: []string{\"bn-IN\"}, localMonth: localMonthsNameBangla, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBangla, weekdayNamesAbbr: weekdayNamesBanglaAbbr},\n\t\t109:   {tags: []string{\"ba\"}, localMonth: localMonthsNameBashkir, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBashkir, weekdayNamesAbbr: weekdayNamesBashkirAbbr},\n\t\t1133:  {tags: []string{\"ba-RU\"}, localMonth: localMonthsNameBashkir, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBashkir, weekdayNamesAbbr: weekdayNamesBashkirAbbr},\n\t\t45:    {tags: []string{\"eu\"}, localMonth: localMonthsNameBasque, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBasque, weekdayNamesAbbr: weekdayNamesBasqueAbbr},\n\t\t1069:  {tags: []string{\"eu-ES\"}, localMonth: localMonthsNameBasque, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBasque, weekdayNamesAbbr: weekdayNamesBasqueAbbr},\n\t\t35:    {tags: []string{\"be\"}, localMonth: localMonthsNameBelarusian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBelarusian, weekdayNamesAbbr: weekdayNamesBelarusianAbbr},\n\t\t1059:  {tags: []string{\"be-BY\"}, localMonth: localMonthsNameBelarusian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBelarusian, weekdayNamesAbbr: weekdayNamesBelarusianAbbr},\n\t\t25626: {tags: []string{\"bs-Cyrl\"}, localMonth: localMonthsNameBosnianCyrillic, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBosnianCyrillic, weekdayNamesAbbr: weekdayNamesBosnianCyrillicAbbr},\n\t\t8218:  {tags: []string{\"bs-Cyrl-BA\"}, localMonth: localMonthsNameBosnianCyrillic, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBosnianCyrillic, weekdayNamesAbbr: weekdayNamesBosnianCyrillicAbbr},\n\t\t26650: {tags: []string{\"bs-Latn\"}, localMonth: localMonthsNameBosnian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBosnian, weekdayNamesAbbr: weekdayNamesBosnianAbbr},\n\t\t30746: {tags: []string{\"bs\"}, localMonth: localMonthsNameBosnian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBosnian, weekdayNamesAbbr: weekdayNamesBosnianAbbr},\n\t\t5146:  {tags: []string{\"bs-Latn-BA\"}, localMonth: localMonthsNameBosnian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBosnian, weekdayNamesAbbr: weekdayNamesBosnianAbbr},\n\t\t126:   {tags: []string{\"br\"}, localMonth: localMonthsNameBreton, apFmt: apFmtBreton, weekdayNames: weekdayNamesBreton, weekdayNamesAbbr: weekdayNamesBretonAbbr},\n\t\t1150:  {tags: []string{\"br-FR\"}, localMonth: localMonthsNameBreton, apFmt: apFmtBreton, weekdayNames: weekdayNamesBreton, weekdayNamesAbbr: weekdayNamesBretonAbbr},\n\t\t2:     {tags: []string{\"bg\"}, localMonth: localMonthsNameBulgarian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBulgarian, weekdayNamesAbbr: weekdayNamesBulgarianAbbr},\n\t\t1026:  {tags: []string{\"bg-BG\"}, localMonth: localMonthsNameBulgarian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesBulgarian, weekdayNamesAbbr: weekdayNamesBulgarianAbbr},\n\t\t85:    {tags: []string{\"my\"}, localMonth: localMonthsNameBurmese, apFmt: apFmtBurmese, weekdayNames: weekdayNamesBurmese, weekdayNamesAbbr: weekdayNamesBurmese},\n\t\t1109:  {tags: []string{\"my-MM\"}, localMonth: localMonthsNameBurmese, apFmt: apFmtBurmese, weekdayNames: weekdayNamesBurmese, weekdayNamesAbbr: weekdayNamesBurmese},\n\t\t3:     {tags: []string{\"ca\"}, localMonth: localMonthsNameValencian, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesValencian, weekdayNamesAbbr: weekdayNamesValencianAbbr},\n\t\t1027:  {tags: []string{\"ca-ES\"}, localMonth: localMonthsNameValencian, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesValencian, weekdayNamesAbbr: weekdayNamesValencianAbbr},\n\t\t1119:  {tags: []string{\"tzm-Arab-MA\"}, localMonth: localMonthsNameArabicIraq, apFmt: apFmtArabic, weekdayNames: weekdayNamesArabic, weekdayNamesAbbr: weekdayNamesArabicAbbr},\n\t\t146:   {tags: []string{\"ku\"}, localMonth: localMonthsNameCentralKurdish, apFmt: apFmtCentralKurdish, weekdayNames: weekdayNamesCentralKurdish, weekdayNamesAbbr: weekdayNamesCentralKurdish},\n\t\t31890: {tags: []string{\"ku-Arab\"}, localMonth: localMonthsNameCentralKurdish, apFmt: apFmtCentralKurdish, weekdayNames: weekdayNamesCentralKurdish, weekdayNamesAbbr: weekdayNamesCentralKurdish},\n\t\t1170:  {tags: []string{\"ku-Arab-IQ\"}, localMonth: localMonthsNameCentralKurdish, apFmt: apFmtCentralKurdish, weekdayNames: weekdayNamesCentralKurdish, weekdayNamesAbbr: weekdayNamesCentralKurdish},\n\t\t92:    {tags: []string{\"chr\"}, localMonth: localMonthsNameCherokee, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesCherokee, weekdayNamesAbbr: weekdayNamesCherokeeAbbr},\n\t\t31836: {tags: []string{\"chr-Cher\"}, localMonth: localMonthsNameCherokee, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesCherokee, weekdayNamesAbbr: weekdayNamesCherokeeAbbr},\n\t\t1116:  {tags: []string{\"chr-Cher-US\"}, localMonth: localMonthsNameCherokee, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesCherokee, weekdayNamesAbbr: weekdayNamesCherokeeAbbr},\n\t\t4:     {tags: []string{\"zh-Hans\"}, localMonth: localMonthsNameChinese1, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2},\n\t\t30724: {tags: []string{\"zh\"}, localMonth: localMonthsNameChinese1, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2},\n\t\t2052:  {tags: []string{\"zh-CN\"}, localMonth: localMonthsNameChinese1, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr},\n\t\t4100:  {tags: []string{\"zh-SG\"}, localMonth: localMonthsNameChinese2, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr},\n\t\t31748: {tags: []string{\"zh-Hant\"}, localMonth: localMonthsNameChinese3, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2},\n\t\t3076:  {tags: []string{\"zh-HK\"}, localMonth: localMonthsNameChinese2, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2},\n\t\t5124:  {tags: []string{\"zh-MO\"}, localMonth: localMonthsNameChinese3, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2},\n\t\t1028:  {tags: []string{\"zh-TW\"}, localMonth: localMonthsNameChinese3, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2, useGannen: true},\n\t\t131:   {tags: []string{\"co\"}, localMonth: localMonthsNameCorsican, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesCorsican, weekdayNamesAbbr: weekdayNamesCorsicanAbbr},\n\t\t1155:  {tags: []string{\"co-FR\"}, localMonth: localMonthsNameCorsican, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesCorsican, weekdayNamesAbbr: weekdayNamesCorsicanAbbr},\n\t\t26:    {tags: []string{\"hr\"}, localMonth: localMonthsNameCroatian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesCroatian, weekdayNamesAbbr: weekdayNamesCroatianAbbr},\n\t\t1050:  {tags: []string{\"hr-HR\"}, localMonth: localMonthsNameCroatian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesCroatian, weekdayNamesAbbr: weekdayNamesCroatianAbbr},\n\t\t4122:  {tags: []string{\"hr-BA\"}, localMonth: localMonthsNameCroatianLatin, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesCroatian, weekdayNamesAbbr: weekdayNamesCroatianAbbr},\n\t\t5:     {tags: []string{\"cs\"}, localMonth: localMonthsNameCzech, apFmt: apFmtCzech, weekdayNames: weekdayNamesCzech, weekdayNamesAbbr: weekdayNamesCzechAbbr},\n\t\t1029:  {tags: []string{\"cs-CZ\"}, localMonth: localMonthsNameCzech, apFmt: apFmtCzech, weekdayNames: weekdayNamesCzech, weekdayNamesAbbr: weekdayNamesCzechAbbr},\n\t\t6:     {tags: []string{\"da\"}, localMonth: localMonthsNameDanish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesDanish, weekdayNamesAbbr: weekdayNamesDanishAbbr},\n\t\t1030:  {tags: []string{\"da-DK\"}, localMonth: localMonthsNameDanish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesDanish, weekdayNamesAbbr: weekdayNamesDanishAbbr},\n\t\t140:   {tags: []string{\"prs\"}, localMonth: localMonthsNameDari, apFmt: apFmtPersian, weekdayNames: weekdayNamesDari, weekdayNamesAbbr: weekdayNamesDari},\n\t\t1164:  {tags: []string{\"prs-AF\"}, localMonth: localMonthsNameDariAfghanistan, apFmt: apFmtDari, weekdayNames: weekdayNamesDari, weekdayNamesAbbr: weekdayNamesDari},\n\t\t101:   {tags: []string{\"dv\"}, localMonth: localMonthsNameDivehi, apFmt: apFmtDivehi, weekdayNames: weekdayNamesDivehi, weekdayNamesAbbr: weekdayNamesDivehi},\n\t\t1125:  {tags: []string{\"dv-MV\"}, localMonth: localMonthsNameDivehi, apFmt: apFmtDivehi, weekdayNames: weekdayNamesDivehi, weekdayNamesAbbr: weekdayNamesDivehi},\n\t\t19:    {tags: []string{\"nl\"}, localMonth: localMonthsNameDutch, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesDutch, weekdayNamesAbbr: weekdayNamesDutchAbbr},\n\t\t2067:  {tags: []string{\"nl-BE\"}, localMonth: localMonthsNameDutch, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesDutch, weekdayNamesAbbr: weekdayNamesDutchAbbr},\n\t\t1043:  {tags: []string{\"nl-NL\"}, localMonth: localMonthsNameDutch, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesDutch, weekdayNamesAbbr: weekdayNamesDutchAbbr},\n\t\t3153:  {tags: []string{\"dz-BT\"}, localMonth: localMonthsNameDzongkha, apFmt: apFmtDzongkha, weekdayNames: weekdayNamesDzongkha, weekdayNamesAbbr: weekdayNamesDzongkhaAbbr},\n\t\t9:     {tags: []string{\"en\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t4096: {tags: []string{\n\t\t\t\"aa\", \"aa-DJ\", \"aa-ER\", \"aa-ET\", \"af-NA\", \"agq\", \"agq-CM\", \"ak\",\n\t\t\t\"ak-GH\", \"sq-MK\", \"gsw-LI\", \"gsw-CH\", \"ar-TD\", \"ar-KM\", \"ar-DJ\",\n\t\t\t\"ar-ER\", \"ar-IL\", \"ar-MR\", \"ar-PS\", \"ar-SO\", \"ar-SS\", \"ar-SD\",\n\t\t\t\"ar-001\", \"ast\", \"ast-ES\", \"asa\", \"asa-TZ\", \"ksf\", \"ksf-CM\", \"bm\",\n\t\t\t\"bm-Latn-ML\", \"bas\", \"bas-CM\", \"bem\", \"bem-ZM\", \"bez\", \"bez-TZ\",\n\t\t\t\"bho\", \"bho-Deva\", \"bho-Deva-IN\", \"byn\", \"byn-ER\", \"brx\", \"brx-IN\",\n\t\t\t\"ca-AD\", \"ca-FR\", \"ca-IT\", \"ceb\", \"ceb-Latn\", \"ceb-Latn-PH\",\n\t\t\t\"tzm-Latn-MA\", \"ccp\", \"ccp-Cakm\", \"ccp-Cakm-BD\", \"ccp-Cakm-IN\",\n\t\t\t\"ce-RU\", \"cgg\", \"cgg-UG\", \"cu-RU\", \"cv\", \"cv-Cyrl\", \"cv-Cyrl-RU\",\n\t\t\t\"swc\", \"swc-CD\", \"kw\", \"kw-GB\", \"da-GL\", \"dua\", \"dua-CM\", \"nl-AW\",\n\t\t\t\"nl-BQ\", \"nl-CW\", \"nl-SX\", \"nl-SR\", \"dz\", \"ebu\", \"ebu-KE\", \"en-AS\",\n\t\t\t\"en-AI\", \"en-AG\", \"en-AT\", \"en-BS\", \"en-BB\", \"en-BE\", \"en-BM\",\n\t\t\t\"en-BW\", \"en-IO\", \"en-VG\", \"en-BI\", \"en-CM\", \"en-KY\", \"en-CX\",\n\t\t\t\"en-CC\", \"en-CK\", \"en-CY\", \"en-DK\", \"en-DM\", \"en-ER\", \"en-150\",\n\t\t\t\"en-FK\", \"en-FI\", \"en-FJ\", \"en-GM\", \"en-DE\", \"en-GH\", \"en-GI\",\n\t\t\t\"en-GD\", \"en-GU\", \"en-GG\", \"en-GY\", \"en-IM\", \"en-IL\", \"en-JE\",\n\t\t\t\"en-KE\", \"en-KI\", \"en-LS\", \"en-LR\", \"en-MO\", \"en-MG\", \"en-MW\",\n\t\t\t\"en-MV\", \"en-MT\", \"en-MH\", \"en-MU\", \"en-FM\", \"en-MS\", \"en-NA\",\n\t\t\t\"en-NR\", \"en-NL\", \"en-NG\", \"en-NU\", \"en-NF\", \"en-MP\", \"en-PK\",\n\t\t\t\"en-PW\", \"en-PG\", \"en-PN\", \"en-PR\", \"en-RW\", \"en-KN\", \"en-LC\",\n\t\t\t\"en-VC\", \"en-WS\", \"en-SC\", \"en-SL\", \"en-SX\", \"en-SI\", \"en-SB\",\n\t\t\t\"en-SS\", \"en-SH\", \"en-SD\", \"en-SZ\", \"en-SE\", \"en-CH\", \"en-TZ\",\n\t\t\t\"en-TK\", \"en-TO\", \"en-TC\", \"en-TV\", \"en-UG\", \"en-UM\", \"en-VI\",\n\t\t\t\"en-VU\", \"en-001\", \"en-ZM\", \"eo\", \"eo-001\", \"ee\", \"ee-GH\", \"ee-TG\",\n\t\t\t\"ewo\", \"ewo-CM\", \"fo-DK\", \"fr-DZ\", \"fr-BJ\", \"fr-BF\", \"fr-BI\",\n\t\t\t\"fr-CF\", \"fr-TD\", \"fr-KM\", \"fr-CG\", \"fr-DJ\", \"fr-GQ\", \"fr-GF\",\n\t\t\t\"fr-PF\", \"fr-GA\", \"fr-GP\", \"fr-GN\", \"fr-MG\", \"fr-MQ\", \"fr-MR\",\n\t\t\t\"fr-MU\", \"fr-YT\", \"fr-NC\", \"fr-NE\", \"fr-RW\", \"fr-BL\", \"fr-MF\",\n\t\t\t\"fr-PM\", \"fr-SC\", \"fr-SY\", \"fr-TG\", \"fr-TN\", \"fr-VU\", \"fr-WF\",\n\t\t\t\"fur\", \"fur-IT\", \"ff-Latn-BF\", \"ff-CM\", \"ff-Latn-CM\", \"ff-Latn-GM\",\n\t\t\t\"ff-Latn-GH\", \"ff-GN\", \"ff-Latn-GN\", \"ff-Latn-GW\", \"ff-Latn-LR\",\n\t\t\t\"ff-MR\", \"ff-Latn-MR\", \"ff-Latn-NE\", \"ff-Latn-SL\", \"lg\", \"lg-UG\",\n\t\t\t\"de-BE\", \"de-IT\", \"el-CY\", \"guz\", \"guz-KE\", \"bgc\", \"bgc-Deva\",\n\t\t\t\"bgc-Deva-IN\", \"ha-Latn-GH\", \"ha-Latn-NE\", \"hi-Latn\", \"hi-Latn-IN\",\n\t\t\t\"ia\", \"ia-FR\", \"ia-001\", \"it-SM\", \"it-VA\", \"jv\", \"jv-Latn\",\n\t\t\t\"jv-Latn-ID\", \"dyo\", \"dyo-SN\", \"kea\", \"kea-CV\", \"kab\", \"kab-DZ\",\n\t\t\t\"kgp\", \"kgp-Latn\", \"kgp-Latn-BR\", \"kkj\", \"kkj-CM\", \"kln\", \"kln-KE\",\n\t\t\t\"kam\", \"kam-KE\", \"ks-Arab-IN\", \"ki\", \"ki-KE\", \"sw-TZ\", \"sw-UG\",\n\t\t\t\"ko-KP\", \"khq\", \"khq-ML\", \"ses\", \"ses-ML\", \"nmg\", \"nmg-CM\",\n\t\t\t\"ku-Arab-IR\", \"lkt\", \"lkt-US\", \"lag\", \"lag-TZ\", \"ln\", \"ln-AO\",\n\t\t\t\"ln-CF\", \"ln-CG\", \"ln-CD\", \"nds\", \"nds-DE\", \"nds-NL\", \"lu\", \"lu-CD\",\n\t\t\t\"luo\", \"luo-KE\", \"luy\", \"luy-KE\", \"jmc\", \"jmc-TZ\", \"mgh\", \"mgh-MZ\",\n\t\t\t\"kde\", \"kde-TZ\", \"mg\", \"mg-MG\", \"gv\", \"gv-IM\", \"mas\", \"mas-KE\",\n\t\t\t\"mas-TZ\", \"mzn-IR\", \"mer\", \"mer-KE\", \"mgo\", \"mgo-CM\", \"mfe\",\n\t\t\t\"mfe-MU\", \"mua\", \"mua-CM\", \"nqo\", \"nqo-GN\", \"naq\", \"naq-NA\", \"nnh\",\n\t\t\t\"nnh-CM\", \"jgo\", \"jgo-CM\", \"yrl\", \"yrl-Latn\", \"yrl-Latn-BR\",\n\t\t\t\"yrl-Latn-CO\", \"yrl-Latn-VE\", \"lrc-IQ\", \"lrc-IR\", \"nd\", \"nd-ZW\",\n\t\t\t\"nb-SJ\", \"nus\", \"nus-SD\", \"nus-SS\", \"nyn\", \"nyn-UG\", \"oc-ES\",\n\t\t\t\"om-KE\", \"os\", \"os-GE\", \"os-RU\", \"ps-PK\", \"fa-AF\", \"pt-AO\", \"pt-CV\",\n\t\t\t\"pt-GQ\", \"pt-GW\", \"pt-LU\", \"pt-MO\", \"pt-MZ\", \"pt-ST\", \"pt-CH\",\n\t\t\t\"pt-TL\", \"prg-001\", \"raj\", \"raj-Deva\", \"raj-Deva-IN\", \"ksh\",\n\t\t\t\"ksh-DE\", \"rof\", \"rof-TZ\", \"rn\", \"rn-BI\", \"ru-BY\", \"ru-KZ\", \"ru-KG\",\n\t\t\t\"ru-UA\", \"rwk\", \"rwk-TZ\", \"ssy\", \"ssy-ER\", \"saq\", \"saq-KE\", \"sg\",\n\t\t\t\"sg-CF\", \"sbp\", \"sbp-TZ\", \"sc\", \"sc-Latn\", \"sc-Latn-IT\", \"seh\",\n\t\t\t\"seh-MZ\", \"ksb\", \"ksb-TZ\", \"sn\", \"sn-Latn\", \"sn-Latn-ZW\", \"xog\",\n\t\t\t\"xog-UG\", \"so-DJ\", \"so-ET\", \"so-KE\", \"nr\", \"nr-ZA\", \"st-LS\",\n\t\t\t\"es-BZ\", \"es-BR\", \"es-GQ\", \"es-PH\", \"zgh\", \"zgh-Tfng-MA\",\n\t\t\t\"zgh-Tfng\", \"ss\", \"ss-ZA\", \"ss-SZ\", \"sv-AX\", \"shi\", \"shi-Tfng\",\n\t\t\t\"shi-Tfng-MA\", \"shi-Latn\", \"shi-Latn-MA\", \"dav\", \"dav-KE\", \"ta-MY\",\n\t\t\t\"ta-SG\", \"twq\", \"twq-NE\", \"teo\", \"teo-KE\", \"teo-UG\", \"bo-IN\", \"tig\",\n\t\t\t\"tig-ER\", \"to\", \"to-TO\", \"tr-CY\", \"uz-Arab\", \"uz-Arab-AF\", \"vai\",\n\t\t\t\"vai-Vaii\", \"vai-Vaii-LR\", \"vai-Latn-LR\", \"vai-Latn\", \"vo\",\n\t\t\t\"vo-001\", \"vun\", \"vun-TZ\", \"wae\", \"wae-CH\", \"wal\", \"wal-ET\", \"yav\",\n\t\t\t\"yav-CM\", \"yo-BJ\", \"dje\", \"dje-NE\",\n\t\t}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t3081:  {tags: []string{\"en-AU\"}, localMonth: localMonthsNameEnglish, apFmt: strings.ToLower(nfp.AmPm[0]), weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t10249: {tags: []string{\"en-BZ\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t4105:  {tags: []string{\"en-CA\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t9225:  {tags: []string{\"en-029\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t15369: {tags: []string{\"en-HK\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t16393: {tags: []string{\"en-IN\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t6153:  {tags: []string{\"en-IE\"}, localMonth: localMonthsNameEnglish, apFmt: strings.ToLower(nfp.AmPm[0]), weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t8201:  {tags: []string{\"en-JM\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t17417: {tags: []string{\"en-MY\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t5129:  {tags: []string{\"en-NZ\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t13321: {tags: []string{\"en-PH\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t18441: {tags: []string{\"en-SG\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t7177:  {tags: []string{\"en-ZA\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t11273: {tags: []string{\"en-TT\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t19465: {tags: []string{\"en-AE\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t2057:  {tags: []string{\"en-GB\"}, localMonth: localMonthsNameEnglish, apFmt: strings.ToLower(nfp.AmPm[0]), weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t1033:  {tags: []string{\"en-US\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t12297: {tags: []string{\"en-ZW\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t37:    {tags: []string{\"et\"}, localMonth: localMonthsNameEstonian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEstonian, weekdayNamesAbbr: weekdayNamesEstonianAbbr},\n\t\t1061:  {tags: []string{\"et-EE\"}, localMonth: localMonthsNameEstonian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEstonian, weekdayNamesAbbr: weekdayNamesEstonianAbbr},\n\t\t56:    {tags: []string{\"fo\"}, localMonth: localMonthsNameFaroese, apFmt: apFmtFaroese, weekdayNames: weekdayNamesFaroese, weekdayNamesAbbr: weekdayNamesFaroeseAbbr},\n\t\t1080:  {tags: []string{\"fo-FO\"}, localMonth: localMonthsNameFaroese, apFmt: apFmtFaroese, weekdayNames: weekdayNamesFaroese, weekdayNamesAbbr: weekdayNamesFaroeseAbbr},\n\t\t100:   {tags: []string{\"fil\"}, localMonth: localMonthsNameFilipino, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFilipino, weekdayNamesAbbr: weekdayNamesFilipinoAbbr},\n\t\t1124:  {tags: []string{\"fil-PH\"}, localMonth: localMonthsNameFilipino, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFilipino, weekdayNamesAbbr: weekdayNamesFilipinoAbbr},\n\t\t11:    {tags: []string{\"fi\"}, localMonth: localMonthsNameFinnish, apFmt: apFmtFinnish, weekdayNames: weekdayNamesFinnish, weekdayNamesAbbr: weekdayNamesFinnishAbbr},\n\t\t1035:  {tags: []string{\"fi-FI\"}, localMonth: localMonthsNameFinnish, apFmt: apFmtFinnish, weekdayNames: weekdayNamesFinnish, weekdayNamesAbbr: weekdayNamesFinnishAbbr},\n\t\t12:    {tags: []string{\"fr\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t2060:  {tags: []string{\"fr-BE\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t11276: {tags: []string{\"fr-CM\"}, localMonth: localMonthsNameFrench, apFmt: apFmtCameroon, weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t3084:  {tags: []string{\"fr-CA\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t7180:  {tags: []string{\"fr-029\"}, localMonth: localMonthsNameCaribbean, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t9228:  {tags: []string{\"fr-CD\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t12300: {tags: []string{\"fr-CI\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t1036:  {tags: []string{\"fr-FR\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t15372: {tags: []string{\"fr-HT\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t5132:  {tags: []string{\"fr-LU\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t13324: {tags: []string{\"fr-ML\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t14348: {tags: []string{\"fr-MA\"}, localMonth: localMonthsNameMorocco, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t6156:  {tags: []string{\"fr-MC\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t8204:  {tags: []string{\"fr-RE\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t10252: {tags: []string{\"fr-SN\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t4108:  {tags: []string{\"fr-CH\"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrench, weekdayNamesAbbr: weekdayNamesFrenchAbbr},\n\t\t98:    {tags: []string{\"fy\"}, localMonth: localMonthsNameFrisian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrisian, weekdayNamesAbbr: weekdayNamesFrisianAbbr},\n\t\t1122:  {tags: []string{\"fy-NL\"}, localMonth: localMonthsNameFrisian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFrisian, weekdayNamesAbbr: weekdayNamesFrisianAbbr},\n\t\t103:   {tags: []string{\"ff\"}, localMonth: localMonthsNameFulah, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFulah, weekdayNamesAbbr: weekdayNamesFulahAbbr},\n\t\t31847: {tags: []string{\"ff-Latn\"}, localMonth: localMonthsNameFulah, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesFulah, weekdayNamesAbbr: weekdayNamesFulahAbbr},\n\t\t1127:  {tags: []string{\"ff-NG\", \"ff-Latn-NG\"}, localMonth: localMonthsNameNigeria, apFmt: apFmtNigeria, weekdayNames: weekdayNamesNigeria, weekdayNamesAbbr: weekdayNamesNigeriaAbbr},\n\t\t2151:  {tags: []string{\"ff-SN\", \"ff-Latn-SN\"}, localMonth: localMonthsNameNigeria, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesNigeria, weekdayNamesAbbr: weekdayNamesNigeriaAbbr},\n\t\t86:    {tags: []string{\"gl\"}, localMonth: localMonthsNameGalician, apFmt: apFmtCuba, weekdayNames: weekdayNamesGalician, weekdayNamesAbbr: weekdayNamesGalicianAbbr},\n\t\t1110:  {tags: []string{\"gl-ES\"}, localMonth: localMonthsNameGalician, apFmt: apFmtCuba, weekdayNames: weekdayNamesGalician, weekdayNamesAbbr: weekdayNamesGalicianAbbr},\n\t\t55:    {tags: []string{\"ka\"}, localMonth: localMonthsNameGeorgian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesGeorgian, weekdayNamesAbbr: weekdayNamesGeorgianAbbr},\n\t\t1079:  {tags: []string{\"ka-GE\"}, localMonth: localMonthsNameGeorgian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesGeorgian, weekdayNamesAbbr: weekdayNamesGeorgianAbbr},\n\t\t7:     {tags: []string{\"de\"}, localMonth: localMonthsNameGerman, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesGerman, weekdayNamesAbbr: weekdayNamesGermanAbbr},\n\t\t3079:  {tags: []string{\"de-AT\"}, localMonth: localMonthsNameAustria, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesGerman, weekdayNamesAbbr: weekdayNamesGermanAbbr},\n\t\t1031:  {tags: []string{\"de-DE\"}, localMonth: localMonthsNameGerman, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesGerman, weekdayNamesAbbr: weekdayNamesGermanAbbr},\n\t\t5127:  {tags: []string{\"de-LI\"}, localMonth: localMonthsNameGerman, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesGerman, weekdayNamesAbbr: weekdayNamesGermanAbbr},\n\t\t4103:  {tags: []string{\"de-LU\"}, localMonth: localMonthsNameGerman, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesGerman, weekdayNamesAbbr: weekdayNamesGermanAbbr},\n\t\t2055:  {tags: []string{\"de-CH\"}, localMonth: localMonthsNameGerman, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesGerman, weekdayNamesAbbr: weekdayNamesGermanAbbr},\n\t\t8:     {tags: []string{\"el\"}, localMonth: localMonthsNameGreek, apFmt: apFmtGreek, weekdayNames: weekdayNamesGreek, weekdayNamesAbbr: weekdayNamesGreekAbbr},\n\t\t1032:  {tags: []string{\"el-GR\"}, localMonth: localMonthsNameGreek, apFmt: apFmtGreek, weekdayNames: weekdayNamesGreek, weekdayNamesAbbr: weekdayNamesGreekAbbr},\n\t\t111:   {tags: []string{\"kl\"}, localMonth: localMonthsNameGreenlandic, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesGreenlandic, weekdayNamesAbbr: weekdayNamesGreenlandicAbbr},\n\t\t1135:  {tags: []string{\"kl-GL\"}, localMonth: localMonthsNameGreenlandic, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesGreenlandic, weekdayNamesAbbr: weekdayNamesGreenlandicAbbr},\n\t\t116:   {tags: []string{\"gn\"}, localMonth: localMonthsNameGuarani, apFmt: apFmtCuba, weekdayNames: weekdayNamesGuarani, weekdayNamesAbbr: weekdayNamesGuaraniAbbr},\n\t\t1140:  {tags: []string{\"gn-PY\"}, localMonth: localMonthsNameGuarani, apFmt: apFmtCuba, weekdayNames: weekdayNamesGuarani, weekdayNamesAbbr: weekdayNamesGuaraniAbbr},\n\t\t71:    {tags: []string{\"gu\"}, localMonth: localMonthsNameGujarati, apFmt: apFmtGujarati, weekdayNames: weekdayNamesGujarati, weekdayNamesAbbr: weekdayNamesGujaratiAbbr},\n\t\t1095:  {tags: []string{\"gu-IN\"}, localMonth: localMonthsNameGujarati, apFmt: apFmtGujarati, weekdayNames: weekdayNamesGujarati, weekdayNamesAbbr: weekdayNamesGujaratiAbbr},\n\t\t104:   {tags: []string{\"ha\"}, localMonth: localMonthsNameHausa, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesHausa, weekdayNamesAbbr: weekdayNamesHausaAbbr},\n\t\t31848: {tags: []string{\"ha-Latn\"}, localMonth: localMonthsNameHausa, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesHausa, weekdayNamesAbbr: weekdayNamesHausaAbbr},\n\t\t1128:  {tags: []string{\"ha-Latn-NG\"}, localMonth: localMonthsNameHausa, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesHausa, weekdayNamesAbbr: weekdayNamesHausaAbbr},\n\t\t117:   {tags: []string{\"haw\"}, localMonth: localMonthsNameHawaiian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesHawaiian, weekdayNamesAbbr: weekdayNamesHawaiianAbbr},\n\t\t1141:  {tags: []string{\"haw-US\"}, localMonth: localMonthsNameHawaiian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesHawaiian, weekdayNamesAbbr: weekdayNamesHawaiianAbbr},\n\t\t13:    {tags: []string{\"he\"}, localMonth: localMonthsNameHebrew, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesHebrew, weekdayNamesAbbr: weekdayNamesHebrewAbbr},\n\t\t1037:  {tags: []string{\"he-IL\"}, localMonth: localMonthsNameHebrew, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesHebrew, weekdayNamesAbbr: weekdayNamesHebrewAbbr},\n\t\t57:    {tags: []string{\"hi\"}, localMonth: localMonthsNameHindi, apFmt: apFmtHindi, weekdayNames: weekdayNamesHindi, weekdayNamesAbbr: weekdayNamesHindiAbbr},\n\t\t1081:  {tags: []string{\"hi-IN\"}, localMonth: localMonthsNameHindi, apFmt: apFmtHindi, weekdayNames: weekdayNamesHindi, weekdayNamesAbbr: weekdayNamesHindiAbbr},\n\t\t14:    {tags: []string{\"hu\"}, localMonth: localMonthsNameHungarian, apFmt: apFmtHungarian, weekdayNames: weekdayNamesHungarian, weekdayNamesAbbr: weekdayNamesHungarianAbbr},\n\t\t1038:  {tags: []string{\"hu-HU\"}, localMonth: localMonthsNameHungarian, apFmt: apFmtHungarian, weekdayNames: weekdayNamesHungarian, weekdayNamesAbbr: weekdayNamesHungarianAbbr},\n\t\t15:    {tags: []string{\"is\"}, localMonth: localMonthsNameIcelandic, apFmt: apFmtIcelandic, weekdayNames: weekdayNamesIcelandic, weekdayNamesAbbr: weekdayNamesIcelandicAbbr},\n\t\t1039:  {tags: []string{\"is-IS\"}, localMonth: localMonthsNameIcelandic, apFmt: apFmtIcelandic, weekdayNames: weekdayNamesIcelandic, weekdayNamesAbbr: weekdayNamesIcelandicAbbr},\n\t\t112:   {tags: []string{\"ig\"}, localMonth: localMonthsNameIgbo, apFmt: apFmtIgbo, weekdayNames: weekdayNamesIgbo, weekdayNamesAbbr: weekdayNamesIgboAbbr},\n\t\t1136:  {tags: []string{\"ig-NG\"}, localMonth: localMonthsNameIgbo, apFmt: apFmtIgbo, weekdayNames: weekdayNamesIgbo, weekdayNamesAbbr: weekdayNamesIgboAbbr},\n\t\t33:    {tags: []string{\"id\"}, localMonth: localMonthsNameIndonesian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesIndonesian, weekdayNamesAbbr: weekdayNamesIndonesianAbbr},\n\t\t1057:  {tags: []string{\"id-ID\"}, localMonth: localMonthsNameIndonesian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesIndonesian, weekdayNamesAbbr: weekdayNamesIndonesianAbbr},\n\t\t93:    {tags: []string{\"iu\"}, localMonth: localMonthsNameInuktitut, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesInuktitut, weekdayNamesAbbr: weekdayNamesInuktitutAbbr},\n\t\t31837: {tags: []string{\"iu-Latn\"}, localMonth: localMonthsNameInuktitut, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesInuktitut, weekdayNamesAbbr: weekdayNamesInuktitutAbbr},\n\t\t2141:  {tags: []string{\"iu-Latn-CA\"}, localMonth: localMonthsNameInuktitut, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesInuktitut, weekdayNamesAbbr: weekdayNamesInuktitutAbbr},\n\t\t30813: {tags: []string{\"iu-Cans\"}, localMonth: localMonthsNameSyllabics, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSyllabics, weekdayNamesAbbr: weekdayNamesSyllabicsAbbr},\n\t\t1117:  {tags: []string{\"iu-Cans-CA\"}, localMonth: localMonthsNameSyllabics, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSyllabics, weekdayNamesAbbr: weekdayNamesSyllabicsAbbr},\n\t\t60:    {tags: []string{\"ga\"}, localMonth: localMonthsNameIrish, apFmt: apFmtIrish, weekdayNames: weekdayNamesIrish, weekdayNamesAbbr: weekdayNamesIrishAbbr},\n\t\t2108:  {tags: []string{\"ga-IE\"}, localMonth: localMonthsNameIrish, apFmt: apFmtIrish, weekdayNames: weekdayNamesIrish, weekdayNamesAbbr: weekdayNamesIrishAbbr},\n\t\t16:    {tags: []string{\"it\"}, localMonth: localMonthsNameItalian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesItalian, weekdayNamesAbbr: weekdayNamesItalianAbbr},\n\t\t1040:  {tags: []string{\"it-IT\"}, localMonth: localMonthsNameItalian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesItalian, weekdayNamesAbbr: weekdayNamesItalianAbbr},\n\t\t2064:  {tags: []string{\"it-CH\"}, localMonth: localMonthsNameItalian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesItalian, weekdayNamesAbbr: weekdayNamesItalianAbbr},\n\t\t17:    {tags: []string{\"ja\"}, localMonth: localMonthsNameChinese3, apFmt: apFmtJapanese, weekdayNames: weekdayNamesJapanese, weekdayNamesAbbr: weekdayNamesJapaneseAbbr},\n\t\t1041:  {tags: []string{\"ja-JP\"}, localMonth: localMonthsNameChinese3, apFmt: apFmtJapanese, weekdayNames: weekdayNamesJapanese, weekdayNamesAbbr: weekdayNamesJapaneseAbbr},\n\t\t75:    {tags: []string{\"kn\"}, localMonth: localMonthsNameKannada, apFmt: apFmtKannada, weekdayNames: weekdayNamesKannada, weekdayNamesAbbr: weekdayNamesKannadaAbbr},\n\t\t1099:  {tags: []string{\"kn-IN\"}, localMonth: localMonthsNameKannada, apFmt: apFmtKannada, weekdayNames: weekdayNamesKannada, weekdayNamesAbbr: weekdayNamesKannadaAbbr},\n\t\t1137:  {tags: []string{\"kr-Latn-NG\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t96:    {tags: []string{\"ks\"}, localMonth: localMonthsNameKashmiri, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesKashmiri, weekdayNamesAbbr: weekdayNamesKashmiriAbbr},\n\t\t1120:  {tags: []string{\"ks-Arab\"}, localMonth: localMonthsNameKashmiri, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesKashmiri, weekdayNamesAbbr: weekdayNamesKashmiriAbbr},\n\t\t2144:  {tags: []string{\"ks-Deva-IN\"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t63:    {tags: []string{\"kk\"}, localMonth: localMonthsNameKazakh, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesKazakh, weekdayNamesAbbr: weekdayNamesKazakhAbbr},\n\t\t1087:  {tags: []string{\"kk-KZ\"}, localMonth: localMonthsNameKazakh, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesKazakh, weekdayNamesAbbr: weekdayNamesKazakhAbbr},\n\t\t83:    {tags: []string{\"km\"}, localMonth: localMonthsNameKhmer, apFmt: apFmtKhmer, weekdayNames: weekdayNamesKhmer, weekdayNamesAbbr: weekdayNamesKhmerAbbr},\n\t\t1107:  {tags: []string{\"km-KH\"}, localMonth: localMonthsNameKhmer, apFmt: apFmtKhmer, weekdayNames: weekdayNamesKhmer, weekdayNamesAbbr: weekdayNamesKhmerAbbr},\n\t\t134:   {tags: []string{\"quc\"}, localMonth: localMonthsNameKiche, apFmt: apFmtCuba, weekdayNames: weekdayNamesKiche, weekdayNamesAbbr: weekdayNamesKicheAbbr},\n\t\t1158:  {tags: []string{\"quc-Latn-GT\"}, localMonth: localMonthsNameKiche, apFmt: apFmtCuba, weekdayNames: weekdayNamesKiche, weekdayNamesAbbr: weekdayNamesKicheAbbr},\n\t\t135:   {tags: []string{\"rw\"}, localMonth: localMonthsNameKinyarwanda, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesKinyarwanda, weekdayNamesAbbr: weekdayNamesKinyarwandaAbbr},\n\t\t1159:  {tags: []string{\"rw-RW\"}, localMonth: localMonthsNameKinyarwanda, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesKinyarwanda, weekdayNamesAbbr: weekdayNamesKinyarwandaAbbr},\n\t\t65:    {tags: []string{\"sw\"}, localMonth: localMonthsNameKiswahili, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesKiswahili, weekdayNamesAbbr: weekdayNamesKiswahiliAbbr},\n\t\t1089:  {tags: []string{\"sw-KE\"}, localMonth: localMonthsNameKiswahili, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesKiswahili, weekdayNamesAbbr: weekdayNamesKiswahiliAbbr},\n\t\t87:    {tags: []string{\"kok\"}, localMonth: localMonthsNameKonkani, apFmt: apFmtKonkani, weekdayNames: weekdayNamesKonkani, weekdayNamesAbbr: weekdayNamesKonkaniAbbr},\n\t\t1111:  {tags: []string{\"kok-IN\"}, localMonth: localMonthsNameKonkani, apFmt: apFmtKonkani, weekdayNames: weekdayNamesKonkani, weekdayNamesAbbr: weekdayNamesKonkaniAbbr},\n\t\t18:    {tags: []string{\"ko\"}, localMonth: localMonthsNameKorean, apFmt: apFmtKorean, weekdayNames: weekdayNamesKorean, weekdayNamesAbbr: weekdayNamesKoreanAbbr},\n\t\t1042:  {tags: []string{\"ko-KR\"}, localMonth: localMonthsNameKorean, apFmt: apFmtKorean, weekdayNames: weekdayNamesKorean, weekdayNamesAbbr: weekdayNamesKoreanAbbr},\n\t\t64:    {tags: []string{\"ky\"}, localMonth: localMonthsNameKyrgyz, apFmt: apFmtKyrgyz, weekdayNames: weekdayNamesKyrgyz, weekdayNamesAbbr: weekdayNamesKyrgyzAbbr},\n\t\t1088:  {tags: []string{\"ky-KG\"}, localMonth: localMonthsNameKyrgyz, apFmt: apFmtKyrgyz, weekdayNames: weekdayNamesKyrgyz, weekdayNamesAbbr: weekdayNamesKyrgyzAbbr},\n\t\t84:    {tags: []string{\"lo\"}, localMonth: localMonthsNameLao, apFmt: apFmtLao, weekdayNames: weekdayNamesLao, weekdayNamesAbbr: weekdayNamesLaoAbbr},\n\t\t1108:  {tags: []string{\"lo-LA\"}, localMonth: localMonthsNameLao, apFmt: apFmtLao, weekdayNames: weekdayNamesLao, weekdayNamesAbbr: weekdayNamesLaoAbbr},\n\t\t1142:  {tags: []string{\"la-VA\"}, localMonth: localMonthsNameLatin, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesLatin, weekdayNamesAbbr: weekdayNamesLatinAbbr},\n\t\t38:    {tags: []string{\"lv\"}, localMonth: localMonthsNameLatvian, apFmt: apFmtLatvian, weekdayNames: weekdayNamesLatvian, weekdayNamesAbbr: weekdayNamesLatvianAbbr},\n\t\t1062:  {tags: []string{\"lv-LV\"}, localMonth: localMonthsNameLatvian, apFmt: apFmtLatvian, weekdayNames: weekdayNamesLatvian, weekdayNamesAbbr: weekdayNamesLatvianAbbr},\n\t\t39:    {tags: []string{\"lt\"}, localMonth: localMonthsNameLithuanian, apFmt: apFmtLithuanian, weekdayNames: weekdayNamesLithuanian, weekdayNamesAbbr: weekdayNamesLithuanianAbbr},\n\t\t1063:  {tags: []string{\"lt-LT\"}, localMonth: localMonthsNameLithuanian, apFmt: apFmtLithuanian, weekdayNames: weekdayNamesLithuanian, weekdayNamesAbbr: weekdayNamesLithuanianAbbr},\n\t\t31790: {tags: []string{\"dsb\"}, localMonth: localMonthsNameLowerSorbian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesLowerSorbian, weekdayNamesAbbr: weekdayNamesLowerSorbianAbbr},\n\t\t2094:  {tags: []string{\"dsb-DE\"}, localMonth: localMonthsNameLowerSorbian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesLowerSorbian, weekdayNamesAbbr: weekdayNamesLowerSorbianAbbr},\n\t\t110:   {tags: []string{\"lb\"}, localMonth: localMonthsNameLuxembourgish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesLuxembourgish, weekdayNamesAbbr: weekdayNamesLuxembourgishAbbr},\n\t\t1134:  {tags: []string{\"lb-LU\"}, localMonth: localMonthsNameLuxembourgish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesLuxembourgish, weekdayNamesAbbr: weekdayNamesLuxembourgishAbbr},\n\t\t47:    {tags: []string{\"mk\"}, localMonth: localMonthsNameMacedonian, apFmt: apFmtMacedonian, weekdayNames: weekdayNamesMacedonian, weekdayNamesAbbr: weekdayNamesMacedonianAbbr},\n\t\t1071:  {tags: []string{\"mk-MK\"}, localMonth: localMonthsNameMacedonian, apFmt: apFmtMacedonian, weekdayNames: weekdayNamesMacedonian, weekdayNamesAbbr: weekdayNamesMacedonianAbbr},\n\t\t62:    {tags: []string{\"ms\"}, localMonth: localMonthsNameMalay, apFmt: apFmtMalay, weekdayNames: weekdayNamesMalay, weekdayNamesAbbr: weekdayNamesMalayAbbr},\n\t\t2110:  {tags: []string{\"ms-BN\"}, localMonth: localMonthsNameMalay, apFmt: apFmtMalay, weekdayNames: weekdayNamesMalay, weekdayNamesAbbr: weekdayNamesMalayAbbr},\n\t\t1086:  {tags: []string{\"ms-MY\"}, localMonth: localMonthsNameMalay, apFmt: apFmtMalay, weekdayNames: weekdayNamesMalay, weekdayNamesAbbr: weekdayNamesMalayAbbr},\n\t\t76:    {tags: []string{\"ml\"}, localMonth: localMonthsNameMalayalam, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesMalayalam, weekdayNamesAbbr: weekdayNamesMalayalamAbbr},\n\t\t1100:  {tags: []string{\"ml-IN\"}, localMonth: localMonthsNameMalayalam, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesMalayalam, weekdayNamesAbbr: weekdayNamesMalayalamAbbr},\n\t\t58:    {tags: []string{\"mt\"}, localMonth: localMonthsNameMaltese, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesMaltese, weekdayNamesAbbr: weekdayNamesMalteseAbbr},\n\t\t1082:  {tags: []string{\"mt-MT\"}, localMonth: localMonthsNameMaltese, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesMaltese, weekdayNamesAbbr: weekdayNamesMalteseAbbr},\n\t\t129:   {tags: []string{\"mi\"}, localMonth: localMonthsNameMaori, apFmt: apFmtCuba, weekdayNames: weekdayNamesMaori, weekdayNamesAbbr: weekdayNamesMaoriAbbr},\n\t\t1153:  {tags: []string{\"mi-NZ\"}, localMonth: localMonthsNameMaori, apFmt: apFmtCuba, weekdayNames: weekdayNamesMaori, weekdayNamesAbbr: weekdayNamesMaoriAbbr},\n\t\t122:   {tags: []string{\"arn\"}, localMonth: localMonthsNameMapudungun, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesMapudungun, weekdayNamesAbbr: weekdayNamesMapudungunAbbr},\n\t\t1146:  {tags: []string{\"arn-CL\"}, localMonth: localMonthsNameMapudungun, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesMapudungun, weekdayNamesAbbr: weekdayNamesMapudungunAbbr},\n\t\t78:    {tags: []string{\"mr\"}, localMonth: localMonthsNameMarathi, apFmt: apFmtKonkani, weekdayNames: weekdayNamesMarathi, weekdayNamesAbbr: weekdayNamesMarathiAbbr},\n\t\t1102:  {tags: []string{\"mr-IN\"}, localMonth: localMonthsNameMarathi, apFmt: apFmtKonkani, weekdayNames: weekdayNamesMarathi, weekdayNamesAbbr: weekdayNamesMarathiAbbr},\n\t\t124:   {tags: []string{\"moh\"}, localMonth: localMonthsNameMohawk, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesMohawk, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t1148:  {tags: []string{\"moh-CA\"}, localMonth: localMonthsNameMohawk, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesMohawk, weekdayNamesAbbr: weekdayNamesEnglishAbbr},\n\t\t80:    {tags: []string{\"mn\"}, localMonth: localMonthsNameMongolian, apFmt: apFmtMongolian, weekdayNames: weekdayNamesMongolian, weekdayNamesAbbr: weekdayNamesMongolianAbbr},\n\t\t30800: {tags: []string{\"mn-Cyrl\"}, localMonth: localMonthsNameMongolian, apFmt: apFmtMongolian, weekdayNames: weekdayNamesMongolian, weekdayNamesAbbr: weekdayNamesMongolianCyrlAbbr},\n\t\t1104:  {tags: []string{\"mn-MN\"}, localMonth: localMonthsNameMongolian, apFmt: apFmtMongolian, weekdayNames: weekdayNamesMongolian, weekdayNamesAbbr: weekdayNamesMongolianCyrlAbbr},\n\t\t31824: {tags: []string{\"mn-Mong\"}, localMonth: localMonthsNameTraditionalMongolian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTraditionalMongolian, weekdayNamesAbbr: weekdayNamesTraditionalMongolian},\n\t\t2128:  {tags: []string{\"mn-Mong-CN\"}, localMonth: localMonthsNameTraditionalMongolian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTraditionalMongolian, weekdayNamesAbbr: weekdayNamesTraditionalMongolian},\n\t\t3152:  {tags: []string{\"mn-Mong-MN\"}, localMonth: localMonthsNameTraditionalMongolian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTraditionalMongolianMN, weekdayNamesAbbr: weekdayNamesTraditionalMongolianMN},\n\t\t97:    {tags: []string{\"ne\"}, localMonth: localMonthsNameNepali, apFmt: apFmtHindi, weekdayNames: weekdayNamesNepali, weekdayNamesAbbr: weekdayNamesNepaliAbbr},\n\t\t2145:  {tags: []string{\"ne-IN\"}, localMonth: localMonthsNameNepaliIN, apFmt: apFmtHindi, weekdayNames: weekdayNamesNepaliIN, weekdayNamesAbbr: weekdayNamesNepaliINAbbr},\n\t\t1121:  {tags: []string{\"ne-NP\"}, localMonth: localMonthsNameNepali, apFmt: apFmtHindi, weekdayNames: weekdayNamesNepali, weekdayNamesAbbr: weekdayNamesNepaliAbbr},\n\t\t20:    {tags: []string{\"no\"}, localMonth: localMonthsNameNorwegian, apFmt: apFmtCuba, weekdayNames: weekdayNamesNorwegian, weekdayNamesAbbr: weekdayNamesNorwegianAbbr},\n\t\t31764: {tags: []string{\"nb\"}, localMonth: localMonthsNameNorwegian, apFmt: apFmtCuba, weekdayNames: weekdayNamesNorwegian, weekdayNamesAbbr: weekdayNamesNorwegianNOAbbr},\n\t\t1044:  {tags: []string{\"nb-NO\"}, localMonth: localMonthsNameNorwegian, apFmt: apFmtCuba, weekdayNames: weekdayNamesNorwegian, weekdayNamesAbbr: weekdayNamesNorwegianNOAbbr},\n\t\t30740: {tags: []string{\"nn\"}, localMonth: localMonthsNameNorwegian, apFmt: apFmtNorwegian, weekdayNames: weekdayNamesNorwegianNynorsk, weekdayNamesAbbr: weekdayNamesNorwegianNynorskAbbr},\n\t\t2068:  {tags: []string{\"nn-NO\"}, localMonth: localMonthsNameNorwegian, apFmt: apFmtNorwegian, weekdayNames: weekdayNamesNorwegianNynorsk, weekdayNamesAbbr: weekdayNamesNorwegianNynorskAbbr},\n\t\t130:   {tags: []string{\"oc\"}, localMonth: localMonthsNameOccitan, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesOccitan, weekdayNamesAbbr: weekdayNamesOccitanAbbr},\n\t\t1154:  {tags: []string{\"oc-FR\"}, localMonth: localMonthsNameOccitan, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesOccitan, weekdayNamesAbbr: weekdayNamesOccitanAbbr},\n\t\t72:    {tags: []string{\"or\"}, localMonth: localMonthsNameOdia, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesOdia, weekdayNamesAbbr: weekdayNamesOdiaAbbr},\n\t\t1096:  {tags: []string{\"or-IN\"}, localMonth: localMonthsNameOdia, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesOdia, weekdayNamesAbbr: weekdayNamesOdiaAbbr},\n\t\t114:   {tags: []string{\"om\"}, localMonth: localMonthsNameOromo, apFmt: apFmtOromo, weekdayNames: weekdayNamesOromo, weekdayNamesAbbr: weekdayNamesOromoAbbr},\n\t\t1138:  {tags: []string{\"om-ET\"}, localMonth: localMonthsNameOromo, apFmt: apFmtOromo, weekdayNames: weekdayNamesOromo, weekdayNamesAbbr: weekdayNamesOromoAbbr},\n\t\t99:    {tags: []string{\"ps\"}, localMonth: localMonthsNamePashto, apFmt: apFmtPashto, weekdayNames: weekdayNamesPashto, weekdayNamesAbbr: weekdayNamesPashto},\n\t\t1123:  {tags: []string{\"ps-AF\"}, localMonth: localMonthsNamePashto, apFmt: apFmtPashto, weekdayNames: weekdayNamesPashto, weekdayNamesAbbr: weekdayNamesPashto},\n\t\t41:    {tags: []string{\"fa\"}, localMonth: localMonthsNamePersian, apFmt: apFmtPersian, weekdayNames: weekdayNamesPersian, weekdayNamesAbbr: weekdayNamesPersian},\n\t\t1065:  {tags: []string{\"fa-IR\"}, localMonth: localMonthsNamePersian, apFmt: apFmtPersian, weekdayNames: weekdayNamesPersian, weekdayNamesAbbr: weekdayNamesPersian},\n\t\t21:    {tags: []string{\"pl\"}, localMonth: localMonthsNamePolish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesPolish, weekdayNamesAbbr: weekdayNamesPolishAbbr},\n\t\t1045:  {tags: []string{\"pl-PL\"}, localMonth: localMonthsNamePolish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesPolish, weekdayNamesAbbr: weekdayNamesPolishAbbr},\n\t\t22:    {tags: []string{\"pt\"}, localMonth: localMonthsNamePortuguese, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesPortuguese, weekdayNamesAbbr: weekdayNamesPortugueseAbbr},\n\t\t1046:  {tags: []string{\"pt-BR\"}, localMonth: localMonthsNamePortuguese, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesPortuguese, weekdayNamesAbbr: weekdayNamesPortugueseAbbr},\n\t\t2070:  {tags: []string{\"pt-PT\"}, localMonth: localMonthsNamePortuguese, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesPortuguese, weekdayNamesAbbr: weekdayNamesPortugueseAbbr},\n\t\t70:    {tags: []string{\"pa\"}, localMonth: localMonthsNamePunjabi, apFmt: apFmtPunjabi, weekdayNames: weekdayNamesPunjabi, weekdayNamesAbbr: weekdayNamesPunjabiAbbr},\n\t\t31814: {tags: []string{\"pa-Arab\"}, localMonth: localMonthsNamePunjabiArab, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesPunjabiArab, weekdayNamesAbbr: weekdayNamesPunjabiArab},\n\t\t1094:  {tags: []string{\"pa-IN\"}, localMonth: localMonthsNamePunjabi, apFmt: apFmtPunjabi, weekdayNames: weekdayNamesPunjabi, weekdayNamesAbbr: weekdayNamesPunjabiAbbr},\n\t\t2118:  {tags: []string{\"pa-Arab-PK\"}, localMonth: localMonthsNamePunjabiArab, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesPunjabiArab, weekdayNamesAbbr: weekdayNamesPunjabiArab},\n\t\t107:   {tags: []string{\"quz\"}, localMonth: localMonthsNameQuechua, apFmt: apFmtCuba, weekdayNames: weekdayNamesQuechua, weekdayNamesAbbr: weekdayNamesQuechuaAbbr},\n\t\t1131:  {tags: []string{\"quz-BO\"}, localMonth: localMonthsNameQuechua, apFmt: apFmtCuba, weekdayNames: weekdayNamesQuechua, weekdayNamesAbbr: weekdayNamesQuechuaAbbr},\n\t\t2155:  {tags: []string{\"quz-EC\"}, localMonth: localMonthsNameQuechuaEcuador, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesQuechuaEcuador, weekdayNamesAbbr: weekdayNamesQuechuaEcuadorAbbr},\n\t\t3179:  {tags: []string{\"quz-PE\"}, localMonth: localMonthsNameQuechua, apFmt: apFmtCuba, weekdayNames: weekdayNamesQuechuaPeru, weekdayNamesAbbr: weekdayNamesQuechuaPeruAbbr},\n\t\t24:    {tags: []string{\"ro\"}, localMonth: localMonthsNameRomanian, apFmt: apFmtCuba, weekdayNames: weekdayNamesRomanian, weekdayNamesAbbr: weekdayNamesRomanianAbbr},\n\t\t2072:  {tags: []string{\"ro-MD\"}, localMonth: localMonthsNameRomanian, apFmt: apFmtCuba, weekdayNames: weekdayNamesRomanian, weekdayNamesAbbr: weekdayNamesRomanianMoldovaAbbr},\n\t\t1048:  {tags: []string{\"ro-RO\"}, localMonth: localMonthsNameRomanian, apFmt: apFmtCuba, weekdayNames: weekdayNamesRomanian, weekdayNamesAbbr: weekdayNamesRomanianAbbr},\n\t\t23:    {tags: []string{\"rm\"}, localMonth: localMonthsNameRomansh, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesRomansh, weekdayNamesAbbr: weekdayNamesRomanshAbbr},\n\t\t1047:  {tags: []string{\"rm-CH\"}, localMonth: localMonthsNameRomansh, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesRomansh, weekdayNamesAbbr: weekdayNamesRomanshAbbr},\n\t\t25:    {tags: []string{\"ru\"}, localMonth: localMonthsNameRussian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesRussian, weekdayNamesAbbr: weekdayNamesRussianAbbr},\n\t\t2073:  {tags: []string{\"ru-MD\"}, localMonth: localMonthsNameRussian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesRussian, weekdayNamesAbbr: weekdayNamesRussianAbbr},\n\t\t1049:  {tags: []string{\"ru-RU\"}, localMonth: localMonthsNameRussian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesRussian, weekdayNamesAbbr: weekdayNamesRussianAbbr},\n\t\t133:   {tags: []string{\"sah\"}, localMonth: localMonthsNameSakha, apFmt: apFmtSakha, weekdayNames: weekdayNamesSakha, weekdayNamesAbbr: weekdayNamesSakhaAbbr},\n\t\t1157:  {tags: []string{\"sah-RU\"}, localMonth: localMonthsNameSakha, apFmt: apFmtSakha, weekdayNames: weekdayNamesSakha, weekdayNamesAbbr: weekdayNamesSakhaAbbr},\n\t\t28731: {tags: []string{\"smn\"}, localMonth: localMonthsNameSami, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSami, weekdayNamesAbbr: weekdayNamesSamiAbbr},\n\t\t9275:  {tags: []string{\"smn-FI\"}, localMonth: localMonthsNameSami, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSami, weekdayNamesAbbr: weekdayNamesSamiAbbr},\n\t\t31803: {tags: []string{\"smj\"}, localMonth: localMonthsNameSamiLule, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSamiSamiLule, weekdayNamesAbbr: weekdayNamesSamiSwedenAbbr},\n\t\t4155:  {tags: []string{\"smj-NO\"}, localMonth: localMonthsNameSamiLule, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSamiSamiLule, weekdayNamesAbbr: weekdayNamesSamiSamiLuleAbbr},\n\t\t5179:  {tags: []string{\"smj-SE\"}, localMonth: localMonthsNameSamiLule, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSamiSweden, weekdayNamesAbbr: weekdayNamesSamiSwedenAbbr},\n\t\t59:    {tags: []string{\"se\"}, localMonth: localMonthsNameSamiNorthern, apFmt: apFmtSamiNorthern, weekdayNames: weekdayNamesSamiNorthern, weekdayNamesAbbr: weekdayNamesSamiNorthernAbbr},\n\t\t3131:  {tags: []string{\"se-FI\"}, localMonth: localMonthsNameSamiNorthernFI, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSamiNorthernFI, weekdayNamesAbbr: weekdayNamesSamiNorthernFIAbbr},\n\t\t1083:  {tags: []string{\"se-NO\"}, localMonth: localMonthsNameSamiNorthern, apFmt: apFmtSamiNorthern, weekdayNames: weekdayNamesSamiNorthern, weekdayNamesAbbr: weekdayNamesSamiNorthernAbbr},\n\t\t2107:  {tags: []string{\"se-SE\"}, localMonth: localMonthsNameSamiNorthern, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSamiNorthernSE, weekdayNamesAbbr: weekdayNamesSamiNorthernSEAbbr},\n\t\t29755: {tags: []string{\"sms\"}, localMonth: localMonthsNameSamiSkolt, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSamiSkolt, weekdayNamesAbbr: weekdayNamesSamiSkoltAbbr},\n\t\t8251:  {tags: []string{\"sms-FI\"}, localMonth: localMonthsNameSamiSkolt, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSamiSkolt, weekdayNamesAbbr: weekdayNamesSamiSkoltAbbr},\n\t\t30779: {tags: []string{\"sma\"}, localMonth: localMonthsNameSamiSouthern, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSamiSouthern, weekdayNamesAbbr: weekdayNamesSamiSouthernAbbr},\n\t\t6203:  {tags: []string{\"sma-NO\"}, localMonth: localMonthsNameSamiSouthern, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSamiSouthern, weekdayNamesAbbr: weekdayNamesSamiSouthernAbbr},\n\t\t7227:  {tags: []string{\"sma-SE\"}, localMonth: localMonthsNameSamiSouthern, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSamiSouthern, weekdayNamesAbbr: weekdayNamesSamiSouthernAbbr},\n\t\t79:    {tags: []string{\"sa\"}, localMonth: localMonthsNameSanskrit, apFmt: apFmtSanskrit, weekdayNames: weekdayNamesSanskrit, weekdayNamesAbbr: weekdayNamesSanskritAbbr},\n\t\t1103:  {tags: []string{\"sa-IN\"}, localMonth: localMonthsNameSanskrit, apFmt: apFmtSanskrit, weekdayNames: weekdayNamesSanskrit, weekdayNamesAbbr: weekdayNamesSanskritAbbr},\n\t\t145:   {tags: []string{\"gd\"}, localMonth: localMonthsNameScottishGaelic, apFmt: apFmtScottishGaelic, weekdayNames: weekdayNamesGaelic, weekdayNamesAbbr: weekdayNamesGaelicAbbr},\n\t\t1169:  {tags: []string{\"gd-GB\"}, localMonth: localMonthsNameScottishGaelic, apFmt: apFmtScottishGaelic, weekdayNames: weekdayNamesGaelic, weekdayNamesAbbr: weekdayNamesGaelicAbbr},\n\t\t27674: {tags: []string{\"sr-Cyrl\"}, localMonth: localMonthsNameSerbian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSerbian, weekdayNamesAbbr: weekdayNamesSerbianAbbr},\n\t\t7194:  {tags: []string{\"sr-Cyrl-BA\"}, localMonth: localMonthsNameSerbianBA, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSerbianBA, weekdayNamesAbbr: weekdayNamesSerbianBAAbbr},\n\t\t12314: {tags: []string{\"sr-Cyrl-ME\"}, localMonth: localMonthsNameSerbian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSerbianME, weekdayNamesAbbr: weekdayNamesSerbianBAAbbr},\n\t\t10266: {tags: []string{\"sr-Cyrl-RS\"}, localMonth: localMonthsNameSerbian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSerbian, weekdayNamesAbbr: weekdayNamesSerbianAbbr},\n\t\t3098:  {tags: []string{\"sr-Cyrl-CS\"}, localMonth: localMonthsNameSerbian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSerbian, weekdayNamesAbbr: weekdayNamesSerbianAbbr},\n\t\t28698: {tags: []string{\"sr-Latn\"}, localMonth: localMonthsNameSerbianLatin, apFmt: apFmtSerbianLatin, weekdayNames: weekdayNamesSerbianLatin, weekdayNamesAbbr: weekdayNamesSerbianLatinAbbr},\n\t\t31770: {tags: []string{\"sr\"}, localMonth: localMonthsNameSerbianLatin, apFmt: apFmtSerbianLatin, weekdayNames: weekdayNamesSerbianLatin, weekdayNamesAbbr: weekdayNamesSerbianLatinAbbr},\n\t\t6170:  {tags: []string{\"sr-Latn-BA\"}, localMonth: localMonthsNameSerbianLatin, apFmt: apFmtSerbianLatinBA, weekdayNames: weekdayNamesSerbianLatinBA, weekdayNamesAbbr: weekdayNamesSerbianLatinBAAbbr},\n\t\t11290: {tags: []string{\"sr-Latn-ME\"}, localMonth: localMonthsNameSerbianLatin, apFmt: apFmtSerbianLatinBA, weekdayNames: weekdayNamesSerbianLatinME, weekdayNamesAbbr: weekdayNamesSerbianLatinAbbr},\n\t\t9242:  {tags: []string{\"sr-Latn-RS\"}, localMonth: localMonthsNameSerbianLatin, apFmt: apFmtSerbianLatin, weekdayNames: weekdayNamesSerbianLatin, weekdayNamesAbbr: weekdayNamesSerbianLatinAbbr},\n\t\t2074:  {tags: []string{\"sr-Latn-CS\"}, localMonth: localMonthsNameSerbianLatinCS, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSerbianLatin, weekdayNamesAbbr: weekdayNamesSerbianLatinCSAbbr},\n\t\t108:   {tags: []string{\"nso\"}, localMonth: localMonthsNameSesothoSaLeboa, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSesothoSaLeboa, weekdayNamesAbbr: weekdayNamesSesothoSaLeboaAbbr},\n\t\t1132:  {tags: []string{\"nso-ZA\"}, localMonth: localMonthsNameSesothoSaLeboa, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSesothoSaLeboa, weekdayNamesAbbr: weekdayNamesSesothoSaLeboaAbbr},\n\t\t50:    {tags: []string{\"tn\"}, localMonth: localMonthsNameSetswana, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSetswana, weekdayNamesAbbr: weekdayNamesSetswanaAbbr},\n\t\t2098:  {tags: []string{\"tn-BW\"}, localMonth: localMonthsNameSetswana, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSetswana, weekdayNamesAbbr: weekdayNamesSetswanaAbbr},\n\t\t1074:  {tags: []string{\"tn-ZA\"}, localMonth: localMonthsNameSetswana, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSetswana, weekdayNamesAbbr: weekdayNamesSetswanaAbbr},\n\t\t89:    {tags: []string{\"sd\"}, localMonth: localMonthsNameSindhi, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSindhi, weekdayNamesAbbr: weekdayNamesSindhiAbbr},\n\t\t31833: {tags: []string{\"sd-Arab\"}, localMonth: localMonthsNameSindhi, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSindhi, weekdayNamesAbbr: weekdayNamesSindhiAbbr},\n\t\t2137:  {tags: []string{\"sd-Arab-PK\"}, localMonth: localMonthsNameSindhi, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSindhi, weekdayNamesAbbr: weekdayNamesSindhiAbbr},\n\t\t91:    {tags: []string{\"si\"}, localMonth: localMonthsNameSinhala, apFmt: apFmtSinhala, weekdayNames: weekdayNamesSindhi, weekdayNamesAbbr: weekdayNamesSindhiAbbr},\n\t\t1115:  {tags: []string{\"si-LK\"}, localMonth: localMonthsNameSinhala, apFmt: apFmtSinhala, weekdayNames: weekdayNamesSindhi, weekdayNamesAbbr: weekdayNamesSindhiAbbr},\n\t\t27:    {tags: []string{\"sk\"}, localMonth: localMonthsNameSlovak, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSlovak, weekdayNamesAbbr: weekdayNamesSlovakAbbr},\n\t\t1051:  {tags: []string{\"sk-SK\"}, localMonth: localMonthsNameSlovak, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSlovak, weekdayNamesAbbr: weekdayNamesSlovakAbbr},\n\t\t36:    {tags: []string{\"sl\"}, localMonth: localMonthsNameSlovenian, apFmt: apFmtSlovenian, weekdayNames: weekdayNamesSlovenian, weekdayNamesAbbr: weekdayNamesSlovenianAbbr},\n\t\t1060:  {tags: []string{\"sl-SI\"}, localMonth: localMonthsNameSlovenian, apFmt: apFmtSlovenian, weekdayNames: weekdayNamesSlovenian, weekdayNamesAbbr: weekdayNamesSlovenianAbbr},\n\t\t119:   {tags: []string{\"so\"}, localMonth: localMonthsNameSomali, apFmt: apFmtSomali, weekdayNames: weekdayNamesSomali, weekdayNamesAbbr: weekdayNamesSomaliAbbr},\n\t\t1143:  {tags: []string{\"so-SO\"}, localMonth: localMonthsNameSomali, apFmt: apFmtSomali, weekdayNames: weekdayNamesSomali, weekdayNamesAbbr: weekdayNamesSomaliAbbr},\n\t\t48:    {tags: []string{\"st\"}, localMonth: localMonthsNameSotho, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSotho, weekdayNamesAbbr: weekdayNamesSothoAbbr},\n\t\t1072:  {tags: []string{\"st-ZA\"}, localMonth: localMonthsNameSotho, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSotho, weekdayNamesAbbr: weekdayNamesSothoAbbr},\n\t\t10:    {tags: []string{\"es\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanish, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishAbbr},\n\t\t11274: {tags: []string{\"es-AR\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t8202:  {tags: []string{\"es-VE\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t16394: {tags: []string{\"es-BO\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t13322: {tags: []string{\"es-CL\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t9226:  {tags: []string{\"es-CO\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t5130:  {tags: []string{\"es-CR\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t23562: {tags: []string{\"es-CU\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtCuba, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t7178:  {tags: []string{\"es-DO\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t12298: {tags: []string{\"es-EC\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t17418: {tags: []string{\"es-SV\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t4106:  {tags: []string{\"es-GT\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t18442: {tags: []string{\"es-HN\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t22538: {tags: []string{\"es-419\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtCuba, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t2058:  {tags: []string{\"es-MX\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanish, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t19466: {tags: []string{\"es-NI\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t6154:  {tags: []string{\"es-PA\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t15370: {tags: []string{\"es-PY\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t10250: {tags: []string{\"es-PE\"}, localMonth: localMonthsNameSpanishPE, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t20490: {tags: []string{\"es-PR\"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t1034:  {tags: []string{\"es-ES_tradnl\"}, localMonth: localMonthsNameSpanish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishAbbr},\n\t\t3082:  {tags: []string{\"es-ES\"}, localMonth: localMonthsNameSpanish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishAbbr},\n\t\t21514: {tags: []string{\"es-US\"}, localMonth: localMonthsNameSpanish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishUSAbbr},\n\t\t14346: {tags: []string{\"es-UY\"}, localMonth: localMonthsNameSpanishPE, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesSpanish, weekdayNamesAbbr: weekdayNamesSpanishARAbbr},\n\t\t29:    {tags: []string{\"sv\"}, localMonth: localMonthsNameSwedish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSwedish, weekdayNamesAbbr: weekdayNamesSwedishAbbr},\n\t\t2077:  {tags: []string{\"sv-FI\"}, localMonth: localMonthsNameSwedishFI, apFmt: apFmtSwedish, weekdayNames: weekdayNamesSwedish, weekdayNamesAbbr: weekdayNamesSwedishAbbr},\n\t\t1053:  {tags: []string{\"sv-SE\"}, localMonth: localMonthsNameSwedishFI, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesSwedish, weekdayNamesAbbr: weekdayNamesSwedishAbbr},\n\t\t90:    {tags: []string{\"syr\"}, localMonth: localMonthsNameSyriac, apFmt: apFmtSyriac, weekdayNames: weekdayNamesSyriac, weekdayNamesAbbr: weekdayNamesSyriacAbbr},\n\t\t1114:  {tags: []string{\"syr-SY\"}, localMonth: localMonthsNameSyriac, apFmt: apFmtSyriac, weekdayNames: weekdayNamesSyriac, weekdayNamesAbbr: weekdayNamesSyriacAbbr},\n\t\t40:    {tags: []string{\"tg\"}, localMonth: localMonthsNameTajik, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTajik, weekdayNamesAbbr: weekdayNamesTajikAbbr},\n\t\t31784: {tags: []string{\"tg-Cyrl\"}, localMonth: localMonthsNameTajik, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTajik, weekdayNamesAbbr: weekdayNamesTajikAbbr},\n\t\t1064:  {tags: []string{\"tg-Cyrl-TJ\"}, localMonth: localMonthsNameTajik, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTajik, weekdayNamesAbbr: weekdayNamesTajikAbbr},\n\t\t95:    {tags: []string{\"tzm\"}, localMonth: localMonthsNameTamazight, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTamazight, weekdayNamesAbbr: weekdayNamesTamazightAbbr},\n\t\t31839: {tags: []string{\"tzm-Latn\"}, localMonth: localMonthsNameTamazight, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTamazight, weekdayNamesAbbr: weekdayNamesTamazightAbbr},\n\t\t2143:  {tags: []string{\"tzm-Latn-DZ\"}, localMonth: localMonthsNameTamazight, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTamazight, weekdayNamesAbbr: weekdayNamesTamazightAbbr},\n\t\t73:    {tags: []string{\"ta\"}, localMonth: localMonthsNameTamil, apFmt: apFmtTamil, weekdayNames: weekdayNamesTamil, weekdayNamesAbbr: weekdayNamesTamilAbbr},\n\t\t1097:  {tags: []string{\"ta-IN\"}, localMonth: localMonthsNameTamil, apFmt: apFmtTamil, weekdayNames: weekdayNamesTamil, weekdayNamesAbbr: weekdayNamesTamilAbbr},\n\t\t2121:  {tags: []string{\"ta-LK\"}, localMonth: localMonthsNameTamilLK, apFmt: apFmtTamil, weekdayNames: weekdayNamesTamilLK, weekdayNamesAbbr: weekdayNamesTamilLKAbbr},\n\t\t68:    {tags: []string{\"tt\"}, localMonth: localMonthsNameTatar, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTatar, weekdayNamesAbbr: weekdayNamesTatarAbbr},\n\t\t1092:  {tags: []string{\"tt-RU\"}, localMonth: localMonthsNameTatar, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTatar, weekdayNamesAbbr: weekdayNamesTatarAbbr},\n\t\t74:    {tags: []string{\"te\"}, localMonth: localMonthsNameTelugu, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTelugu, weekdayNamesAbbr: weekdayNamesTeluguAbbr},\n\t\t1098:  {tags: []string{\"te-IN\"}, localMonth: localMonthsNameTelugu, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTelugu, weekdayNamesAbbr: weekdayNamesTeluguAbbr},\n\t\t30:    {tags: []string{\"th\"}, localMonth: localMonthsNameThai, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesThai, weekdayNamesAbbr: weekdayNamesThaiAbbr},\n\t\t1054:  {tags: []string{\"th-TH\"}, localMonth: localMonthsNameThai, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesThai, weekdayNamesAbbr: weekdayNamesThaiAbbr},\n\t\t81:    {tags: []string{\"bo\"}, localMonth: localMonthsNameTibetan, apFmt: apFmtTibetan, weekdayNames: weekdayNamesTibetan, weekdayNamesAbbr: weekdayNamesTibetanAbbr},\n\t\t1105:  {tags: []string{\"bo-CN\"}, localMonth: localMonthsNameTibetan, apFmt: apFmtTibetan, weekdayNames: weekdayNamesTibetan, weekdayNamesAbbr: weekdayNamesTibetanAbbr},\n\t\t115:   {tags: []string{\"ti\"}, localMonth: localMonthsNameTigrinya, apFmt: apFmtTigrinya, weekdayNames: weekdayNamesTigrinya, weekdayNamesAbbr: weekdayNamesTigrinyaAbbr},\n\t\t2163:  {tags: []string{\"ti-ER\"}, localMonth: localMonthsNameTigrinya, apFmt: apFmtTigrinyaER, weekdayNames: weekdayNamesTigrinya, weekdayNamesAbbr: weekdayNamesTigrinyaAbbr},\n\t\t1139:  {tags: []string{\"ti-ET\"}, localMonth: localMonthsNameTigrinya, apFmt: apFmtTigrinya, weekdayNames: weekdayNamesTigrinya, weekdayNamesAbbr: weekdayNamesTigrinyaAbbr},\n\t\t49:    {tags: []string{\"ts\"}, localMonth: localMonthsNameTsonga, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTsonga, weekdayNamesAbbr: weekdayNamesTsongaAbbr},\n\t\t1073:  {tags: []string{\"ts-ZA\"}, localMonth: localMonthsNameTsonga, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTsonga, weekdayNamesAbbr: weekdayNamesTsongaAbbr},\n\t\t31:    {tags: []string{\"tr\"}, localMonth: localMonthsNameTurkish, apFmt: apFmtTurkish, weekdayNames: weekdayNamesTurkish, weekdayNamesAbbr: weekdayNamesTurkishAbbr},\n\t\t1055:  {tags: []string{\"tr-TR\"}, localMonth: localMonthsNameTurkish, apFmt: apFmtTurkish, weekdayNames: weekdayNamesTurkish, weekdayNamesAbbr: weekdayNamesTurkishAbbr},\n\t\t66:    {tags: []string{\"tk\"}, localMonth: localMonthsNameTurkmen, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTurkmen, weekdayNamesAbbr: weekdayNamesTurkmenAbbr},\n\t\t1090:  {tags: []string{\"tk-TM\"}, localMonth: localMonthsNameTurkmen, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesTurkmen, weekdayNamesAbbr: weekdayNamesTurkmenAbbr},\n\t\t34:    {tags: []string{\"uk\"}, localMonth: localMonthsNameUkrainian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesUkrainian, weekdayNamesAbbr: weekdayNamesUkrainianAbbr},\n\t\t1058:  {tags: []string{\"uk-UA\"}, localMonth: localMonthsNameUkrainian, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesUkrainian, weekdayNamesAbbr: weekdayNamesUkrainianAbbr},\n\t\t46:    {tags: []string{\"hsb\"}, localMonth: localMonthsNameUpperSorbian, apFmt: apFmtUpperSorbian, weekdayNames: weekdayNamesSorbian, weekdayNamesAbbr: weekdayNamesSorbianAbbr},\n\t\t1070:  {tags: []string{\"hsb-DE\"}, localMonth: localMonthsNameUpperSorbian, apFmt: apFmtUpperSorbian, weekdayNames: weekdayNamesSorbian, weekdayNamesAbbr: weekdayNamesSorbianAbbr},\n\t\t32:    {tags: []string{\"ur\"}, localMonth: localMonthsNamePunjabiArab, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesUrdu, weekdayNamesAbbr: weekdayNamesUrdu},\n\t\t2080:  {tags: []string{\"ur-IN\"}, localMonth: localMonthsNamePunjabiArab, apFmt: apFmtUrdu, weekdayNames: weekdayNamesUrduIN, weekdayNamesAbbr: weekdayNamesUrduIN},\n\t\t1056:  {tags: []string{\"ur-PK\"}, localMonth: localMonthsNamePunjabiArab, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesUrdu, weekdayNamesAbbr: weekdayNamesUrdu},\n\t\t128:   {tags: []string{\"ug\"}, localMonth: localMonthsNameUyghur, apFmt: apFmtUyghur, weekdayNames: weekdayNamesUyghur, weekdayNamesAbbr: weekdayNamesUyghurAbbr},\n\t\t1152:  {tags: []string{\"ug-CN\"}, localMonth: localMonthsNameUyghur, apFmt: apFmtUyghur, weekdayNames: weekdayNamesUyghur, weekdayNamesAbbr: weekdayNamesUyghurAbbr},\n\t\t30787: {tags: []string{\"uz-Cyrl\"}, localMonth: localMonthsNameUzbekCyrillic, apFmt: apFmtUzbekCyrillic, weekdayNames: weekdayNamesUzbekCyrillic, weekdayNamesAbbr: weekdayNamesUzbekCyrillicAbbr},\n\t\t2115:  {tags: []string{\"uz-Cyrl-UZ\"}, localMonth: localMonthsNameUzbekCyrillic, apFmt: apFmtUzbekCyrillic, weekdayNames: weekdayNamesUzbekCyrillic, weekdayNamesAbbr: weekdayNamesUzbekCyrillicAbbr},\n\t\t67:    {tags: []string{\"uz\"}, localMonth: localMonthsNameUzbek, apFmt: apFmtUzbek, weekdayNames: weekdayNamesUzbek, weekdayNamesAbbr: weekdayNamesUzbekAbbr},\n\t\t31811: {tags: []string{\"uz-Latn\"}, localMonth: localMonthsNameUzbek, apFmt: apFmtUzbek, weekdayNames: weekdayNamesUzbek, weekdayNamesAbbr: weekdayNamesUzbekAbbr},\n\t\t1091:  {tags: []string{\"uz-Latn-UZ\"}, localMonth: localMonthsNameUzbek, apFmt: apFmtUzbek, weekdayNames: weekdayNamesUzbek, weekdayNamesAbbr: weekdayNamesUzbekAbbr},\n\t\t2051:  {tags: []string{\"ca-ES-valencia\"}, localMonth: localMonthsNameValencian, apFmt: apFmtSpanishAR, weekdayNames: weekdayNamesValencian, weekdayNamesAbbr: weekdayNamesValencianAbbr},\n\t\t51:    {tags: []string{\"ve\"}, localMonth: localMonthsNameVenda, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesVenda, weekdayNamesAbbr: weekdayNamesVendaAbbr},\n\t\t1075:  {tags: []string{\"ve-ZA\"}, localMonth: localMonthsNameVenda, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesVenda, weekdayNamesAbbr: weekdayNamesVendaAbbr},\n\t\t42:    {tags: []string{\"vi\"}, localMonth: localMonthsNameVietnamese, apFmt: apFmtVietnamese, weekdayNames: weekdayNamesVietnamese, weekdayNamesAbbr: weekdayNamesVietnameseAbbr},\n\t\t1066:  {tags: []string{\"vi-VN\"}, localMonth: localMonthsNameVietnamese, apFmt: apFmtVietnamese, weekdayNames: weekdayNamesVietnamese, weekdayNamesAbbr: weekdayNamesVietnameseAbbr},\n\t\t82:    {tags: []string{\"cy\"}, localMonth: localMonthsNameWelsh, apFmt: apFmtWelsh, weekdayNames: weekdayNamesWelsh, weekdayNamesAbbr: weekdayNamesWelshAbbr},\n\t\t1106:  {tags: []string{\"cy-GB\"}, localMonth: localMonthsNameWelsh, apFmt: apFmtWelsh, weekdayNames: weekdayNamesWelsh, weekdayNamesAbbr: weekdayNamesWelshAbbr},\n\t\t136:   {tags: []string{\"wo\"}, localMonth: localMonthsNameWolof, apFmt: apFmtWolof, weekdayNames: weekdayNamesWolof, weekdayNamesAbbr: weekdayNamesWolofAbbr},\n\t\t1160:  {tags: []string{\"wo-SN\"}, localMonth: localMonthsNameWolof, apFmt: apFmtWolof, weekdayNames: weekdayNamesWolof, weekdayNamesAbbr: weekdayNamesWolofAbbr},\n\t\t52:    {tags: []string{\"xh\"}, localMonth: localMonthsNameXhosa, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesXhosa, weekdayNamesAbbr: weekdayNamesXhosaAbbr},\n\t\t1076:  {tags: []string{\"xh-ZA\"}, localMonth: localMonthsNameXhosa, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesXhosa, weekdayNamesAbbr: weekdayNamesXhosaAbbr},\n\t\t120:   {tags: []string{\"ii\"}, localMonth: localMonthsNameYi, apFmt: apFmtYi, weekdayNames: weekdayNamesYi, weekdayNamesAbbr: weekdayNamesYiAbbr},\n\t\t1144:  {tags: []string{\"ii-CN\"}, localMonth: localMonthsNameYi, apFmt: apFmtYi, weekdayNames: weekdayNamesYi, weekdayNamesAbbr: weekdayNamesYiAbbr},\n\t\t1085:  {tags: []string{\"yi-001\"}, localMonth: localMonthsNameYiddish, apFmt: apFmtYiddish, weekdayNames: weekdayNamesYiddish, weekdayNamesAbbr: weekdayNamesYiddishAbbr},\n\t\t106:   {tags: []string{\"yo\"}, localMonth: localMonthsNameYoruba, apFmt: apFmtYoruba, weekdayNames: weekdayNamesYoruba, weekdayNamesAbbr: weekdayNamesYorubaAbbr},\n\t\t1130:  {tags: []string{\"yo-NG\"}, localMonth: localMonthsNameYoruba, apFmt: apFmtYoruba, weekdayNames: weekdayNamesYoruba, weekdayNamesAbbr: weekdayNamesYorubaAbbr},\n\t\t53:    {tags: []string{\"zu\"}, localMonth: localMonthsNameZulu, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesZulu, weekdayNamesAbbr: weekdayNamesZuluAbbr},\n\t\t1077:  {tags: []string{\"zu-ZA\"}, localMonth: localMonthsNameZulu, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesZulu, weekdayNamesAbbr: weekdayNamesZuluAbbr},\n\t}\n\t// supportedLanguageCodeInfo directly maps the supported language code and\n\t// tags.\n\tsupportedLanguageCodeInfo = map[string]languageInfo{\n\t\t\"JA-JP-X-GANNEN\":    {tags: []string{\"ja-JP\"}, localMonth: localMonthsNameChinese3, apFmt: apFmtJapanese, weekdayNames: weekdayNamesJapanese, weekdayNamesAbbr: weekdayNamesJapaneseAbbr},\n\t\t\"JA-JP-X-GANNEN,80\": {tags: []string{\"ja-JP\"}, localMonth: localMonthsNameChinese3, apFmt: apFmtJapanese, weekdayNames: weekdayNamesJapanese, weekdayNamesAbbr: weekdayNamesJapaneseAbbr, useGannen: true},\n\t}\n\t// republicOfChinaYear defined start time of the Republic of China\n\trepublicOfChinaYear = time.Date(1912, time.January, 1, 0, 0, 0, 0, time.UTC)\n\t// republicOfChinaEraName defined the Republic of China era name for the\n\t// Republic of China calendar.\n\trepublicOfChinaEraName = []string{\"\\u4E2D\\u83EF\\u6C11\\u570B\", \"\\u6C11\\u570B\", \"\\u524D\"}\n\t// japaneseEraYears list the Japanese era name periods.\n\tjapaneseEraYears = []time.Time{\n\t\ttime.Date(1868, time.August, 8, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(1912, time.June, 30, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(1926, time.November, 25, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(1989, time.January, 8, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(2019, time.April, 1, 0, 0, 0, 0, time.UTC),\n\t}\n\t// japaneseEraNames list the Japanese era name for the Japanese emperor\n\t// reign calendar.\n\tjapaneseEraNames = []string{\"\\u660E\\u6CBB\", \"\\u5927\\u6B63\", \"\\u662D\\u548C\", \"\\u5E73\\u6210\", \"\\u4EE4\\u548C\"}\n\t// japaneseEraYear list the Japanese era name symbols.\n\tjapaneseEraSymbols = []string{\"M\", \"T\", \"S\", \"H\", \"R\"}\n\t// monthNamesAfrikaans list the month names in the Afrikaans.\n\tmonthNamesAfrikaans = []string{\"Januarie\", \"Februarie\", \"Maart\", \"April\", \"Mei\", \"Junie\", \"Julie\", \"Augustus\", \"September\", \"Oktober\", \"November\", \"Desember\"}\n\t// monthNamesAfrikaansAbbr lists the month name abbreviations in the\n\t// Afrikaans.\n\tmonthNamesAfrikaansAbbr = []string{\"Jan.\", \"Feb.\", \"Maa.\", \"Apr.\", \"Mei\", \"Jun.\", \"Jul.\", \"Aug.\", \"Sep.\", \"Okt.\", \"Nov.\", \"Des.\"}\n\t// monthNamesAlbanian list the month names in the Albanian.\n\tmonthNamesAlbanian = []string{\"janar\", \"shkurt\", \"mars\", \"prill\", \"maj\", \"qershor\", \"korrik\", \"gusht\", \"shtator\", \"tetor\", \"nëntor\", \"dhjetor\"}\n\t// monthNamesAlbanianAbbr lists the month name abbreviations in the\n\t// Albanian.\n\tmonthNamesAlbanianAbbr = []string{\"jan\", \"shk\", \"mar\", \"pri\", \"maj\", \"qer\", \"krr\", \"gush\", \"sht\", \"tet\", \"nën\", \"dhj\"}\n\t// monthNamesAlsatian list the month names in the Alsatian.\n\tmonthNamesAlsatian = []string{\"Januar\", \"Februar\", \"März\", \"April\", \"Mai\", \"Juni\", \"Juli\", \"Auguscht\", \"Septämber\", \"Oktoober\", \"Novämber\", \"Dezämber\"}\n\t// monthNamesAlsatianAbbr lists the month name abbreviations in the\n\t// Alsatian France.\n\tmonthNamesAlsatianAbbr = []string{\"Jan\", \"Feb\", \"Mär\", \"Apr\", \"Mai\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Okt\", \"Nov\", \"Dez\"}\n\t// monthNamesAlsatianFrance list the month names in the Alsatian.\n\tmonthNamesAlsatianFrance = []string{\"Jänner\", \"Feverje\", \"März\", \"Àpril\", \"Mai\", \"Jüni\", \"Jüli\", \"Augscht\", \"September\", \"Oktower\", \"Nowember\", \"Dezember\"}\n\t// monthNamesAlsatianFranceAbbr lists the month name abbreviations in the\n\t// Alsatian France.\n\tmonthNamesAlsatianFranceAbbr = []string{\"Jän.\", \"Fev.\", \"März\", \"Apr.\", \"Mai\", \"Jüni\", \"Jüli\", \"Aug.\", \"Sept.\", \"Okt.\", \"Now.\", \"Dez.\"}\n\t// monthNamesAmharic list the month names in the Amharic.\n\tmonthNamesAmharic = []string{\n\t\t\"\\u1303\\u1295\\u12E9\\u12C8\\u122A\",\n\t\t\"\\u134C\\u1265\\u1229\\u12C8\\u122A\",\n\t\t\"\\u121B\\u122D\\u127D\",\n\t\t\"\\u12A4\\u1355\\u122A\\u120D\",\n\t\t\"\\u121C\\u12ED\",\n\t\t\"\\u1301\\u1295\",\n\t\t\"\\u1301\\u120B\\u12ED\",\n\t\t\"\\u12A6\\u1308\\u1235\\u1275\",\n\t\t\"\\u1234\\u1355\\u1274\\u121D\\u1260\\u122D\",\n\t\t\"\\u12A6\\u12AD\\u1276\\u1260\\u122D\",\n\t\t\"\\u1296\\u126C\\u121D\\u1260\\u122D\",\n\t\t\"\\u12F2\\u1234\\u121D\\u1260\\u122D\",\n\t}\n\t// monthNamesAmharicAbbr lists the month name abbreviations in the Amharic.\n\tmonthNamesAmharicAbbr = []string{\n\t\t\"\\u1303\\u1295\\u12E9\",\n\t\t\"\\u134C\\u1265\\u1229\",\n\t\t\"\\u121B\\u122D\\u127D\",\n\t\t\"\\u12A4\\u1355\\u122A\",\n\t\t\"\\u121C\\u12ED\",\n\t\t\"\\u1301\\u1295\",\n\t\t\"\\u1301\\u120B\\u12ED\",\n\t\t\"\\u12A6\\u1308\\u1235\",\n\t\t\"\\u1234\\u1355\\u1274\",\n\t\t\"\\u12A6\\u12AD\\u1276\",\n\t\t\"\\u1296\\u126C\\u121D\",\n\t\t\"\\u12F2\\u1234\\u121D\",\n\t}\n\t// monthNamesArabic list the month names in the Arabic.\n\tmonthNamesArabic = []string{\n\t\t\"\\u064A\\u0646\\u0627\\u064A\\u0631\",\n\t\t\"\\u0641\\u0628\\u0631\\u0627\\u064A\\u0631\",\n\t\t\"\\u0645\\u0627\\u0631\\u0633\",\n\t\t\"\\u0623\\u0628\\u0631\\u064A\\u0644\",\n\t\t\"\\u0645\\u0627\\u064A\\u0648\",\n\t\t\"\\u064A\\u0648\\u0646\\u064A\\u0648\",\n\t\t\"\\u064A\\u0648\\u0644\\u064A\\u0648\",\n\t\t\"\\u0623\\u063A\\u0633\\u0637\\u0633\",\n\t\t\"\\u0633\\u0628\\u062A\\u0645\\u0628\\u0631\",\n\t\t\"\\u0623\\u0643\\u062A\\u0648\\u0628\\u0631\",\n\t\t\"\\u0646\\u0648\\u0641\\u0645\\u0628\\u0631\",\n\t\t\"\\u062F\\u064A\\u0633\\u0645\\u0628\\u0631\",\n\t}\n\t// monthNamesArabicIraq list the month names in the Arabic Iraq.\n\tmonthNamesArabicIraq = []string{\n\t\t\"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A\",\n\t\t\"\\u0634\\u0628\\u0627\\u0637\",\n\t\t\"\\u0622\\u0630\\u0627\\u0631\",\n\t\t\"\\u0646\\u064A\\u0633\\u0627\\u0646\",\n\t\t\"\\u0623\\u064A\\u0627\\u0631\",\n\t\t\"\\u062D\\u0632\\u064A\\u0631\\u0627\\u0646\",\n\t\t\"\\u062A\\u0645\\u0648\\u0632\",\n\t\t\"\\u0622\\u0628\",\n\t\t\"\\u0623\\u064A\\u0644\\u0648\\u0644\",\n\t\t\"\\u062A\\u0634\\u0631\\u064A\\u0646 \\u0627\\u0644\\u0623\\u0648\\u0644\",\n\t\t\"\\u062A\\u0634\\u0631\\u064A\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A\",\n\t\t\"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u0623\\u0648\\u0644\",\n\t}\n\t// monthNamesArmenian list the month names in the Armenian.\n\tmonthNamesArmenian = []string{\n\t\t\"\\u0540\\u0578\\u0582\\u0576\\u057E\\u0561\\u0580\",\n\t\t\"\\u0553\\u0565\\u057F\\u0580\\u057E\\u0561\\u0580\",\n\t\t\"\\u0544\\u0561\\u0580\\u057F\",\n\t\t\"\\u0531\\u057A\\u0580\\u056B\\u056C\",\n\t\t\"\\u0544\\u0561\\u0575\\u056B\\u057D\",\n\t\t\"\\u0540\\u0578\\u0582\\u0576\\u056B\\u057D\",\n\t\t\"\\u0540\\u0578\\u0582\\u056C\\u056B\\u057D\",\n\t\t\"\\u0555\\u0563\\u0578\\u057D\\u057F\\u0578\\u057D\",\n\t\t\"\\u054D\\u0565\\u057A\\u057F\\u0565\\u0574\\u0562\\u0565\\u0580\",\n\t\t\"\\u0540\\u0578\\u056F\\u057F\\u0565\\u0574\\u0562\\u0565\\u0580\",\n\t\t\"\\u0546\\u0578\\u0575\\u0565\\u0574\\u0562\\u0565\\u0580\",\n\t\t\"\\u0534\\u0565\\u056F\\u057F\\u0565\\u0574\\u0562\\u0565\\u0580\",\n\t}\n\t// monthNamesArmenianAbbr lists the month name abbreviations in the\n\t// Armenian.\n\tmonthNamesArmenianAbbr = []string{\n\t\t\"\\u0540\\u0576\\u057E\",\n\t\t\"\\u0553\\u057F\\u057E\",\n\t\t\"\\u0544\\u0580\\u057F\",\n\t\t\"\\u0531\\u057A\\u0580\",\n\t\t\"\\u0544\\u0575\\u057D\",\n\t\t\"\\u0540\\u0576\\u057D\",\n\t\t\"\\u0540\\u056C\\u057D\",\n\t\t\"\\u0555\\u0563\\u057D\",\n\t\t\"\\u054D\\u057A\\u057F\",\n\t\t\"\\u0540\\u056F\\u057F\",\n\t\t\"\\u0546\\u0575\\u0574\",\n\t\t\"\\u0534\\u056F\\u057F\",\n\t}\n\t// monthNamesAssamese list the month names in the Assamese.\n\tmonthNamesAssamese = []string{\n\t\t\"\\u099C\\u09BE\\u09A8\\u09C1\\u09F1\\u09BE\\u09F0\\u09C0\",\n\t\t\"\\u09AB\\u09C7\\u09AC\\u09CD\\u09B0\\u09C1\\u09F1\\u09BE\\u09F0\\u09C0\",\n\t\t\"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A\",\n\t\t\"\\u098F\\u09AA\\u09CD\\u09B0\\u09BF\\u09B2\",\n\t\t\"\\u09AE\\u09C7\",\n\t\t\"\\u099C\\u09C1\\u09A8\",\n\t\t\"\\u099C\\u09C1\\u09B2\\u09BE\\u0987\",\n\t\t\"\\u0986\\u0997\\u09B7\\u09CD\\u099F\",\n\t\t\"\\u099A\\u09C7\\u09AA\\u09CD\\u099F\\u09C7\\u09AE\\u09CD\\u09AC\\u09F0\",\n\t\t\"\\u0985\\u0995\\u09CD\\u099F\\u09CB\\u09AC\\u09F0\",\n\t\t\"\\u09A8\\u09AC\\u09C7\\u09AE\\u09CD\\u09AC\\u09F0\",\n\t\t\"\\u09A1\\u09BF\\u099A\\u09C7\\u09AE\\u09CD\\u09AC\\u09F0\",\n\t}\n\t// monthNamesAssameseAbbr lists the month name abbreviations in the\n\t// Assamese.\n\tmonthNamesAssameseAbbr = []string{\n\t\t\"\\u099C\\u09BE\\u09A8\\u09C1\",\n\t\t\"\\u09AB\\u09C7\\u09AC\\u09CD\\u09B0\\u09C1\",\n\t\t\"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A\",\n\t\t\"\\u098F\\u09AA\\u09CD\\u09B0\\u09BF\\u09B2\",\n\t\t\"\\u09AE\\u09C7\",\n\t\t\"\\u099C\\u09C1\\u09A8\",\n\t\t\"\\u099C\\u09C1\\u09B2\\u09BE\\u0987\",\n\t\t\"\\u0986\\u0997\\u09B7\\u09CD\\u099F\",\n\t\t\"\\u099A\\u09C7\\u09AA\\u09CD\\u099F\\u09C7\",\n\t\t\"\\u0985\\u0995\\u09CD\\u099F\\u09CB\",\n\t\t\"\\u09A8\\u09AC\\u09C7\",\n\t\t\"\\u09A1\\u09BF\\u099A\\u09C7\",\n\t}\n\t// monthNamesAzerbaijaniCyrillic list the month names in the Azerbaijani\n\t// (Cyrillic).\n\tmonthNamesAzerbaijaniCyrillic = []string{\n\t\t\"j\\u0430\\u043D\\u0432\\u0430\\u0440\",\n\t\t\"\\u0444\\u0435\\u0432\\u0440\\u0430\\u043B\",\n\t\t\"\\u043C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0430\\u043F\\u0440\\u0435\\u043B\",\n\t\t\"\\u043C\\u0430\\u0458\",\n\t\t\"\\u0438\\u0458\\u0443\\u043D\",\n\t\t\"\\u0438\\u0458\\u0443\\u043B\",\n\t\t\"\\u0430\\u0432\\u0433\\u0443\\u0441\\u0442\",\n\t\t\"\\u0441\\u0435\\u043D\\u0442\\u0458\\u0430\\u0431\\u0440\",\n\t\t\"\\u043E\\u043A\\u0442\\u0458\\u0430\\u0431\\u0440\",\n\t\t\"\\u043D\\u043E\\u0458\\u0430\\u0431\\u0440\",\n\t\t\"\\u0434\\u0435\\u043A\\u0430\\u0431\\u0440\",\n\t}\n\t// monthNamesAzerbaijaniCyrillicAbbr lists the month name abbreviations in\n\t// the Azerbaijani (Cyrillic).\n\tmonthNamesAzerbaijaniCyrillicAbbr = []string{\n\t\t\"\\u0408\\u0430\\u043D\",\n\t\t\"\\u0424\\u0435\\u0432\",\n\t\t\"\\u041C\\u0430\\u0440\",\n\t\t\"\\u0410\\u043F\\u0440\",\n\t\t\"\\u041C\\u0430\\u0458\",\n\t\t\"\\u0418\\u0458\\u0443\\u043D\",\n\t\t\"\\u0418\\u0458\\u0443\\u043B\",\n\t\t\"\\u0410\\u0432\\u0433\",\n\t\t\"\\u0421\\u0435\\u043D\",\n\t\t\"\\u041E\\u043A\\u0442\",\n\t\t\"\\u041D\\u043E\\u044F\",\n\t\t\"\\u0414\\u0435\\u043A\",\n\t}\n\t// monthNamesAzerbaijani list the month names in the Azerbaijani.\n\tmonthNamesAzerbaijani = []string{\"yanvar\", \"fevral\", \"mart\", \"aprel\", \"may\", \"iyun\", \"iyul\", \"avgust\", \"sentyabr\", \"oktyabr\", \"noyabr\", \"dekabr\"}\n\t// monthNamesAzerbaijaniAbbr lists the month name abbreviations in the\n\t// Azerbaijani.\n\tmonthNamesAzerbaijaniAbbr = []string{\"yan\", \"fev\", \"mar\", \"apr\", \"may\", \"iyn\", \"iyl\", \"avq\", \"sen\", \"okt\", \"noy\", \"dek\"}\n\t// monthNamesAustria list the month names in the Austrian.\n\tmonthNamesAustria = []string{\"Jänner\", \"Februar\", \"März\", \"April\", \"Mai\", \"Juni\", \"Juli\", \"August\", \"September\", \"Oktober\", \"November\", \"Dezember\"}\n\t// monthNamesAustriaAbbr list the month name abbreviations in the Austrian.\n\tmonthNamesAustriaAbbr = []string{\"Jän\", \"Feb\", \"Mär\", \"Apr\", \"Mai\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Okt\", \"Nov\", \"Dez\"}\n\t// monthNamesBangla list the month names in the Bangla.\n\tmonthNamesBangla = []string{\n\t\t\"\\u099C\\u09BE\\u09A8\\u09C1\\u09AF\\u09BC\\u09BE\\u09B0\\u09C0\",\n\t\t\"\\u09AB\\u09C7\\u09AC\\u09CD\\u09B0\\u09C1\\u09AF\\u09BC\\u09BE\\u09B0\\u09C0\",\n\t\t\"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A\",\n\t\t\"\\u098F\\u09AA\\u09CD\\u09B0\\u09BF\\u09B2\",\n\t\t\"\\u09AE\\u09C7\",\n\t\t\"\\u099C\\u09C1\\u09A8\",\n\t\t\"\\u099C\\u09C1\\u09B2\\u09BE\\u0987\",\n\t\t\"\\u0986\\u0997\\u09B8\\u09CD\\u099F\",\n\t\t\"\\u09B8\\u09C7\\u09AA\\u09CD\\u099F\\u09C7\\u09AE\\u09CD\\u09AC\\u09B0\",\n\t\t\"\\u0985\\u0995\\u09CD\\u099F\\u09CB\\u09AC\\u09B0\",\n\t\t\"\\u09A8\\u09AD\\u09C7\\u09AE\\u09CD\\u09AC\\u09B0\",\n\t\t\"\\u09A1\\u09BF\\u09B8\\u09C7\\u09AE\\u09CD\\u09AC\\u09B0\",\n\t}\n\t// monthNamesBashkir list the month names in the Bashkir.\n\tmonthNamesBashkir = []string{\n\t\t\"\\u0493\\u0438\\u043D\\u0443\\u0430\\u0440\",\n\t\t\"\\u0444\\u0435\\u0432\\u0440\\u0430\\u043B\\u044C\",\n\t\t\"\\u043C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0430\\u043F\\u0440\\u0435\\u043B\\u044C\",\n\t\t\"\\u043C\\u0430\\u0439\",\n\t\t\"\\u0438\\u044E\\u043D\\u044C\",\n\t\t\"\\u0438\\u044E\\u043B\\u044C\",\n\t\t\"\\u0430\\u0432\\u0433\\u0443\\u0441\\u0442\",\n\t\t\"\\u0441\\u0435\\u043D\\u0442\\u044F\\u0431\\u0440\\u044C\",\n\t\t\"\\u043E\\u043A\\u0442\\u044F\\u0431\\u0440\\u044C\",\n\t\t\"\\u043D\\u043E\\u044F\\u0431\\u0440\\u044C\",\n\t\t\"\\u0434\\u0435\\u043A\\u0430\\u0431\\u0440\\u044C\",\n\t}\n\t// monthNamesBashkirAbbr lists the month name abbreviations in the Bashkir.\n\tmonthNamesBashkirAbbr = []string{\n\t\t\"\\u0493\\u0438\\u043D\",\n\t\t\"\\u0444\\u0435\\u0432\",\n\t\t\"\\u043C\\u0430\\u0440\",\n\t\t\"\\u0430\\u043F\\u0440\",\n\t\t\"\\u043C\\u0430\\u0439\",\n\t\t\"\\u0438\\u044E\\u043D\",\n\t\t\"\\u0438\\u044E\\u043B\",\n\t\t\"\\u0430\\u0432\\u0433\",\n\t\t\"\\u0441\\u0435\\u043D\",\n\t\t\"\\u043E\\u043A\\u0442\",\n\t\t\"\\u043D\\u043E\\u044F\",\n\t\t\"\\u0434\\u0435\\u043A\",\n\t}\n\t// monthNamesBasque list the month names in the Basque.\n\tmonthNamesBasque = []string{\"urtarrila\", \"otsaila\", \"martxoa\", \"apirila\", \"maiatza\", \"ekaina\", \"uztaila\", \"abuztua\", \"iraila\", \"urria\", \"azaroa\", \"abendua\"}\n\t// monthNamesBasqueAbbr lists the month name abbreviations in the Basque.\n\tmonthNamesBasqueAbbr = []string{\"urt.\", \"ots.\", \"mar.\", \"api.\", \"mai.\", \"eka.\", \"uzt.\", \"abu.\", \"ira.\", \"urr.\", \"aza.\", \"abe.\"}\n\t// monthNamesBelarusian list the month names in the Belarusian.\n\tmonthNamesBelarusian = []string{\n\t\t\"\\u0441\\u0442\\u0443\\u0434\\u0437\\u0435\\u043D\\u044C\",\n\t\t\"\\u043B\\u044E\\u0442\\u044B\",\n\t\t\"\\u0441\\u0430\\u043A\\u0430\\u0432\\u0456\\u043A\",\n\t\t\"\\u043A\\u0440\\u0430\\u0441\\u0430\\u0432\\u0456\\u043A\",\n\t\t\"\\u043C\\u0430\\u0439\",\n\t\t\"\\u0447\\u044D\\u0440\\u0432\\u0435\\u043D\\u044C\",\n\t\t\"\\u043B\\u0456\\u043F\\u0435\\u043D\\u044C\",\n\t\t\"\\u0436\\u043D\\u0456\\u0432\\u0435\\u043D\\u044C\",\n\t\t\"\\u0432\\u0435\\u0440\\u0430\\u0441\\u0435\\u043D\\u044C\",\n\t\t\"\\u043A\\u0430\\u0441\\u0442\\u0440\\u044B\\u0447\\u043D\\u0456\\u043A\",\n\t\t\"\\u043B\\u0456\\u0441\\u0442\\u0430\\u043F\\u0430\\u0434\",\n\t\t\"\\u0441\\u043D\\u0435\\u0436\\u0430\\u043D\\u044C\",\n\t}\n\t// monthNamesBelarusianAbbr lists the month name abbreviations in the\n\t// Belarusian.\n\tmonthNamesBelarusianAbbr = []string{\n\t\t\"\\u0441\\u0442\\u0443\\u0434\\u0437\",\n\t\t\"\\u043B\\u044E\\u0442\",\n\t\t\"\\u0441\\u0430\\u043A\",\n\t\t\"\\u043A\\u0440\\u0430\\u0441\",\n\t\t\"\\u043C\\u0430\\u0439\",\n\t\t\"\\u0447\\u044D\\u0440\\u0432\",\n\t\t\"\\u043B\\u0456\\u043F\",\n\t\t\"\\u0436\\u043D\",\n\t\t\"\\u0432\\u0435\\u0440\",\n\t\t\"\\u043A\\u0430\\u0441\\u0442\\u0440\",\n\t\t\"\\u043B\\u0456\\u0441\\u0442\",\n\t\t\"\\u0441\\u043D\\u0435\\u0436\",\n\t}\n\t// monthNamesBosnianCyrillic list the month names in the Bosnian (Cyrillic).\n\tmonthNamesBosnianCyrillic = []string{\n\t\t\"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440\",\n\t\t\"\\u0444\\u0435\\u0431\\u0440\\u0443\\u0430\\u0440\",\n\t\t\"\\u043C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0430\\u043F\\u0440\\u0438\\u043B\",\n\t\t\"\\u043C\\u0430\\u0458\",\n\t\t\"\\u0458\\u0443\\u043D\",\n\t\t\"\\u0458\\u0443\\u043B\",\n\t\t\"\\u0430\\u0432\\u0433\\u0443\\u0441\\u0442\",\n\t\t\"\\u0441\\u0435\\u043F\\u0442\\u0435\\u043C\\u0431\\u0430\\u0440\",\n\t\t\"\\u043E\\u043A\\u0442\\u043E\\u0431\\u0430\\u0440\",\n\t\t\"\\u043D\\u043E\\u0432\\u0435\\u043C\\u0431\\u0430\\u0440\",\n\t\t\"\\u0434\\u0435\\u0446\\u0435\\u043C\\u0431\\u0430\\u0440\",\n\t}\n\t// monthNamesBosnianCyrillicAbbr lists the month name abbreviations in the\n\t// Bosnian (Cyrillic).\n\tmonthNamesBosnianCyrillicAbbr = []string{\n\t\t\"\\u0458\\u0430\\u043D\",\n\t\t\"\\u0444\\u0435\\u0431\",\n\t\t\"\\u043C\\u0430\\u0440\",\n\t\t\"\\u0430\\u043F\\u0440\",\n\t\t\"\\u043C\\u0430\\u0458\",\n\t\t\"\\u0458\\u0443\\u043D\",\n\t\t\"\\u0458\\u0443\\u043B\",\n\t\t\"\\u0430\\u0432\\u0433\",\n\t\t\"\\u0441\\u0435\\u043F\",\n\t\t\"\\u043E\\u043A\\u0442\",\n\t\t\"\\u043D\\u043E\\u0432\",\n\t\t\"\\u0434\\u0435\\u0446\",\n\t}\n\t// monthNamesBosnian list the month names in the Bosnian.\n\tmonthNamesBosnian = []string{\"januar\", \"februar\", \"mart\", \"april\", \"maj\", \"juni\", \"juli\", \"august\", \"septembar\", \"oktobar\", \"novembar\", \"decembar\"}\n\t// monthNamesBosnianAbbr lists the month name abbreviations in the Bosnian.\n\tmonthNamesBosnianAbbr = []string{\"jan\", \"feb\", \"mar\", \"apr\", \"maj\", \"jun\", \"jul\", \"aug\", \"sep\", \"okt\", \"nov\", \"dec\"}\n\t// monthNamesBreton list the month names in the Breton.\n\tmonthNamesBreton = []string{\"Genver\", \"C\\u02bchwevrer\", \"Meurzh\", \"Ebrel\", \"Mae\", \"Mezheven\", \"Gouere\", \"Eost\", \"Gwengolo\", \"Here\", \"Du\", \"Kerzu\"}\n\t// monthNamesBretonAbbr lists the month name abbreviations in the Breton.\n\tmonthNamesBretonAbbr = []string{\"Gen.\", \"C\\u02bchwe.\", \"Meur.\", \"Ebr.\", \"Mae\", \"Mezh.\", \"Goue.\", \"Eost\", \"Gwen.\", \"Here\", \"Du\", \"Kzu.\"}\n\t// monthNamesBulgarian list the month names in the Bulgarian.\n\tmonthNamesBulgarian = []string{\n\t\t\"\\u044F\\u043D\\u0443\\u0430\\u0440\\u0438\",\n\t\t\"\\u0444\\u0435\\u0432\\u0440\\u0443\\u0430\\u0440\\u0438\",\n\t\t\"\\u043C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0430\\u043F\\u0440\\u0438\\u043B\",\n\t\t\"\\u043C\\u0430\\u0439\",\n\t\t\"\\u044E\\u043D\\u0438\",\n\t\t\"\\u044E\\u043B\\u0438\",\n\t\t\"\\u0430\\u0432\\u0433\\u0443\\u0441\\u0442\",\n\t\t\"\\u0441\\u0435\\u043F\\u0442\\u0435\\u043C\\u0432\\u0440\\u0438\",\n\t\t\"\\u043E\\u043A\\u0442\\u043E\\u043C\\u0432\\u0440\\u0438\",\n\t\t\"\\u043D\\u043E\\u0435\\u043C\\u0432\\u0440\\u0438\",\n\t\t\"\\u0434\\u0435\\u043A\\u0435\\u043C\\u0432\\u0440\\u0438\",\n\t}\n\t// monthNamesBurmese list the month names in the Burmese.\n\tmonthNamesBurmese = []string{\n\t\t\"\\u1007\\u1014\\u103A\\u1014\\u101D\\u102B\\u101B\\u102E\",\n\t\t\"\\u1016\\u1031\\u1016\\u1031\\u102C\\u103A\\u101D\\u102B\\u101B\\u102E\",\n\t\t\"\\u1019\\u1010\\u103A\",\n\t\t\"\\u1027\\u1015\\u103C\\u102E\",\n\t\t\"\\u1019\\u1031\",\n\t\t\"\\u1007\\u103D\\u1014\\u103A\",\n\t\t\"\\u1007\\u1030\\u101C\\u102D\\u102F\\u1004\\u103A\",\n\t\t\"\\u1029\\u1002\\u102F\\u1010\\u103A\",\n\t\t\"\\u1005\\u1000\\u103A\\u1010\\u1004\\u103A\\u1018\\u102C\",\n\t\t\"\\u1021\\u1031\\u102C\\u1000\\u103A\\u1010\\u102D\\u102F\\u1018\\u102C\",\n\t\t\"\\u1014\\u102D\\u102F\\u101D\\u1004\\u103A\\u1018\\u102C\",\n\t\t\"\\u1012\\u102E\\u1007\\u1004\\u103A\\u1018\\u102C\",\n\t}\n\t// monthNamesBurmeseAbbr lists the month name abbreviations in the Burmese.\n\tmonthNamesBurmeseAbbr = []string{\n\t\t\"\\u1007\\u1014\\u103A\",\n\t\t\"\\u1016\\u1031\",\n\t\t\"\\u1019\\u1010\\u103A\",\n\t\t\"\\u1027\",\n\t\t\"\\u1019\\u1031\",\n\t\t\"\\u1007\\u103D\\u1014\\u103A\",\n\t\t\"\\u1007\\u1030\",\n\t\t\"\\u1029\",\n\t\t\"\\u1005\\u1000\\u103A\",\n\t\t\"\\u1021\\u1031\\u102C\\u1000\\u103A\",\n\t\t\"\\u1014\\u102D\\u102F\",\n\t\t\"\\u1012\\u102E\",\n\t}\n\t//\tmonthNamesCaribbean list the month names in the Caribbean.\n\tmonthNamesCaribbean = []string{\"Janvier\", \"Février\", \"Mars\", \"Avril\", \"Mai\", \"Juin\", \"Juillet\", \"Août\", \"Septembre\", \"Octobre\", \"Novembre\", \"Décembre\"}\n\t//\tmonthNamesCaribbeanAbbr lists the month name abbreviations in the\n\t// Caribbean.\n\tmonthNamesCaribbeanAbbr = []string{\"Janv.\", \"Févr.\", \"Mars\", \"Avr.\", \"Mai\", \"Juin\", \"Juil.\", \"Août\", \"Sept.\", \"Oct.\", \"Nov.\", \"Déc.\"}\n\t//\tmonthNamesCentralKurdish list the month names in the Central Kurdish.\n\tmonthNamesCentralKurdish = []string{\n\t\t\"\\u06A9\\u0627\\u0646\\u0648\\u0648\\u0646\\u06CC \\u062F\\u0648\\u0648\\u06D5\\u0645\",\n\t\t\"\\u0634\\u0648\\u0628\\u0627\\u062A\",\n\t\t\"\\u0626\\u0627\\u0632\\u0627\\u0631\",\n\t\t\"\\u0646\\u06CC\\u0633\\u0627\\u0646\",\n\t\t\"\\u0626\\u0627\\u06CC\\u0627\\u0631\",\n\t\t\"\\u062D\\u0648\\u0632\\u06D5\\u06CC\\u0631\\u0627\\u0646\",\n\t\t\"\\u062A\\u06D5\\u0645\\u0648\\u0648\\u0632\",\n\t\t\"\\u0626\\u0627\\u0628\",\n\t\t\"\\u0626\\u06D5\\u06CC\\u0644\\u0648\\u0648\\u0644\",\n\t\t\"\\u062A\\u0634\\u0631\\u06CC\\u0646\\u06CC \\u06CC\\u06D5\\u06A9\\u06D5\\u0645\",\n\t\t\"\\u062A\\u0634\\u0631\\u06CC\\u0646\\u06CC \\u062F\\u0648\\u0648\\u06D5\\u0645\",\n\t\t\"\\u06A9\\u0627\\u0646\\u0648\\u0646\\u06CC \\u06CC\\u06D5\\u06A9\\u06D5\\u0645\",\n\t}\n\t// monthNamesCherokee list the month names in the Cherokee.\n\tmonthNamesCherokee = []string{\n\t\t\"\\u13A4\\u13C3\\u13B8\\u13D4\\u13C5\",\n\t\t\"\\u13A7\\u13A6\\u13B5\",\n\t\t\"\\u13A0\\u13C5\\u13F1\",\n\t\t\"\\u13DD\\u13EC\\u13C2\",\n\t\t\"\\u13A0\\u13C2\\u13CD\\u13AC\\u13D8\",\n\t\t\"\\u13D5\\u13AD\\u13B7\\u13F1\",\n\t\t\"\\u13AB\\u13F0\\u13C9\\u13C2\",\n\t\t\"\\u13A6\\u13B6\\u13C2\",\n\t\t\"\\u13DA\\u13B5\\u13CD\\u13D7\",\n\t\t\"\\u13DA\\u13C2\\u13C5\\u13D7\",\n\t\t\"\\u13C5\\u13D3\\u13D5\\u13C6\",\n\t\t\"\\u13A4\\u13CD\\u13A9\\u13F1\",\n\t}\n\t// monthNamesCherokeeAbbr lists the month name abbreviations in the\n\t// Cherokee.\n\tmonthNamesCherokeeAbbr = []string{\n\t\t\"\\u13A4\\u13C3\\u13B8\",\n\t\t\"\\u13A7\\u13A6\\u13B5\",\n\t\t\"\\u13A0\\u13C5\\u13F1\",\n\t\t\"\\u13DD\\u13EC\\u13C2\",\n\t\t\"\\u13A0\\u13C2\\u13CD\",\n\t\t\"\\u13D5\\u13AD\\u13B7\",\n\t\t\"\\u13AB\\u13F0\\u13C9\",\n\t\t\"\\u13A6\\u13B6\\u13C2\",\n\t\t\"\\u13DA\\u13B5\\u13CD\",\n\t\t\"\\u13DA\\u13C2\\u13C5\",\n\t\t\"\\u13C5\\u13D3\\u13D5\",\n\t\t\"\\u13A4\\u13CD\\u13A9\",\n\t}\n\t// monthNamesChinese list the month names in the Chinese.\n\tmonthNamesChinese = []string{\"一月\", \"二月\", \"三月\", \"四月\", \"五月\", \"六月\", \"七月\", \"八月\", \"九月\", \"十月\", \"十一月\", \"十二月\"}\n\t// monthNamesChineseAbbr lists the month name abbreviations in the Chinese.\n\tmonthNamesChineseAbbr = []string{\"一\", \"二\", \"三\", \"四\", \"五\", \"六\", \"七\", \"八\", \"九\", \"十\", \"十一\", \"十二\"}\n\t// monthNamesChineseNum list the month number and character abbreviation in\n\t// the Chinese.\n\tmonthNamesChineseNum = []string{\"1月\", \"2月\", \"3月\", \"4月\", \"5月\", \"6月\", \"7月\", \"8月\", \"9月\", \"10月\", \"11月\", \"12月\"}\n\t// monthNamesCorsican list the month names in the Corsican.\n\tmonthNamesCorsican = []string{\"ghjennaghju\", \"ferraghju\", \"marzu\", \"aprile\", \"maghju\", \"ghjunghju\", \"lugliu\", \"aostu\", \"settembre\", \"ottobre\", \"nuvembre\", \"dicembre\"}\n\t// monthNamesCorsican lists the month name abbreviations in the Corsican.\n\tmonthNamesCorsicanAbbr = []string{\"ghje\", \"ferr\", \"marz\", \"apri\", \"magh\", \"ghju\", \"lugl\", \"aost\", \"sett\", \"otto\", \"nuve\", \"dice\"}\n\t// monthNamesCroatian list the month names in the Croatian.\n\tmonthNamesCroatian = []string{\"siječanj\", \"veljača\", \"ožujak\", \"travanj\", \"svibanj\", \"lipanj\", \"srpanj\", \"kolovoz\", \"rujan\", \"listopad\", \"studeni\", \"prosinac\"}\n\t// monthNamesCroatian lists the month name abbreviations in the Croatian.\n\tmonthNamesCroatianAbbr = []string{\"sij\", \"vlj\", \"ožu\", \"tra\", \"svi\", \"lip\", \"srp\", \"kol\", \"ruj\", \"lis\", \"stu\", \"pro\"}\n\t// monthNamesCzech list the month names in the Czech.\n\tmonthNamesCzech = []string{\"leden\", \"únor\", \"březen\", \"duben\", \"květen\", \"červen\", \"červenec\", \"srpen\", \"září\", \"říjen\", \"listopad\", \"prosinec\"}\n\t// monthNamesCzech lists the month name abbreviations in the Czech.\n\tmonthNamesCzechAbbr = []string{\"I\", \"II\", \"III\", \"IV\", \"V\", \"VI\", \"VII\", \"VIII\", \"IX\", \"X\", \"XI\", \"XII\"}\n\t// monthNamesDanish list the month names in the Danish.\n\tmonthNamesDanish = []string{\"januar\", \"februar\", \"marts\", \"april\", \"maj\", \"juni\", \"juli\", \"august\", \"september\", \"oktober\", \"november\", \"december\"}\n\t// monthNamesDanish lists the month name abbreviations in the Danish.\n\tmonthNamesDanishAbbr = []string{\"jan\", \"feb\", \"mar\", \"apr\", \"maj\", \"jun\", \"jul\", \"aug\", \"sep\", \"okt\", \"nov\", \"dec\"}\n\t// monthNamesDari list the month names in the Dari.\n\tmonthNamesDari = []string{\n\t\t\"\\u062C\\u0646\\u0648\\u0631\\u06CC\",\n\t\t\"\\u0641\\u0628\\u0631\\u0648\\u0631\\u06CC\",\n\t\t\"\\u0645\\u0627\\u0631\\u0686\",\n\t\t\"\\u0627\\u067E\\u0631\\u06CC\\u0644\",\n\t\t\"\\u0645\\u06CC\",\n\t\t\"\\u062C\\u0648\\u0646\",\n\t\t\"\\u062C\\u0648\\u0644\\u0627\\u06CC\",\n\t\t\"\\u0627\\u06AF\\u0633\\u062A\",\n\t\t\"\\u0633\\u067E\\u062A\\u0645\\u0628\\u0631\",\n\t\t\"\\u0627\\u06A9\\u062A\\u0648\\u0628\\u0631\",\n\t\t\"\\u0646\\u0648\\u0645\\u0628\\u0631\",\n\t\t\"\\u062F\\u0633\\u0645\\u0628\\u0631\",\n\t}\n\t// monthNamesDariAbbr lists the month name abbreviations in the Dari.\n\tmonthNamesDariAbbr = []string{\n\t\t\"\\u062C\\u062F\\u06CC\",\n\t\t\"\\u062F\\u0644\\u0648\",\n\t\t\"\\u062D\\u0648\\u062A\",\n\t\t\"\\u062D\\u0645\\u0644\",\n\t\t\"\\u062B\\u0648\\u0631\",\n\t\t\"\\u062C\\u0648\\u0632\\u0627\",\n\t\t\"\\u0633\\u0631\\u0637\\u0627\\u0646\",\n\t\t\"\\u0627\\u0633\\u062F\",\n\t\t\"\\u0633\\u0646\\u0628\\u0644\\u0647\",\n\t\t\"\\u0645\\u06CC\\u0632\\u0627\\u0646\",\n\t\t\"\\u0639\\u0642\\u0631\\u0628\",\n\t\t\"\\u0642\\u0648\\u0633\",\n\t}\n\t// monthNamesDivehi lists the month names in the Divehi.\n\tmonthNamesDivehi = []string{\n\t\t\"\\u0796\\u07A6\\u0782\\u07A6\\u0788\\u07A6\\u0783\\u07A9\",\n\t\t\"\\u078A\\u07AC\\u0784\\u07B0\\u0783\\u07AA\\u0787\\u07A6\\u0783\\u07A9\",\n\t\t\"\\u0789\\u07A7\\u0783\\u0797\\u07B0\",\n\t\t\"\\u0787\\u07AD\\u0795\\u07B0\\u0783\\u07A8\\u078D\\u07B0\",\n\t\t\"\\u0789\\u07AC\\u0787\\u07A8\",\n\t\t\"\\u0796\\u07AB\\u0782\\u07B0\",\n\t\t\"\\u0796\\u07AA\\u078D\\u07A6\\u0787\\u07A8\",\n\t\t\"\\u0787\\u07AE\\u078E\\u07A6\\u0790\\u07B0\\u0793\\u07B0\",\n\t\t\"\\u0790\\u07AC\\u0795\\u07B0\\u0793\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783\",\n\t\t\"\\u0787\\u07AE\\u0786\\u07B0\\u0793\\u07AF\\u0784\\u07A6\\u0783\",\n\t\t\"\\u0782\\u07AE\\u0788\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783\",\n\t\t\"\\u0791\\u07A8\\u0790\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783\",\n\t}\n\t// monthNamesDutch list the month names in the Dutch.\n\tmonthNamesDutch = []string{\"januari\", \"februari\", \"maart\", \"april\", \"mei\", \"juni\", \"juli\", \"augustus\", \"september\", \"oktober\", \"november\", \"december\"}\n\t// monthNamesDutch lists the month name abbreviations in the Dutch.\n\tmonthNamesDutchAbbr = []string{\"jan\", \"feb\", \"mrt\", \"apr\", \"mei\", \"jun\", \"jul\", \"aug\", \"sep\", \"okt\", \"nov\", \"dec\"}\n\t// monthNamesDzongkha list the month names in the Dzongkha.\n\tmonthNamesDzongkha = []string{\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F51\\u0F44\\u0F54\\u0F0B\",\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F42\\u0F49\\u0F72\\u0F66\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F42\\u0F66\\u0F74\\u0F58\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F5E\\u0F72\\u0F0B\\u0F54\",\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F63\\u0F94\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F51\\u0FB2\\u0F74\\u0F42\\u0F0B\\u0F54\",\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F51\\u0F74\\u0F53\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F62\\u0F92\\u0FB1\\u0F51\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F51\\u0F42\\u0F74\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F54\\u0F0D\",\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F42\\u0F45\\u0F72\\u0F42\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F42\\u0F49\\u0F72\\u0F66\\u0F0B\\u0F54\\u0F0B\",\n\t}\n\t// monthNamesDzongkhaAbbr lists the month name abbreviations in the\n\t// Dzongkha.\n\tmonthNamesDzongkhaAbbr = []string{\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F21\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F22\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F23\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F24\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F25\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F26\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F27\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F28\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F29\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F21\\u0F20\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F21\\u0F21\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F21\\u0F22\",\n\t}\n\t// monthNamesEstonian list the month names in the Estonian.\n\tmonthNamesEstonian = []string{\"jaanuar\", \"veebruar\", \"märts\", \"aprill\", \"mai\", \"juuni\", \"juuli\", \"august\", \"september\", \"oktoober\", \"november\", \"detsember\"}\n\t// monthNamesEstonianAbbr lists the month name abbreviations in the\n\t// Estonian.\n\tmonthNamesEstonianAbbr = []string{\"jaan\", \"veebr\", \"märts\", \"apr\", \"mai\", \"juuni\", \"juuli\", \"aug\", \"sept\", \"okt\", \"nov\", \"dets\"}\n\t// monthNamesFaroese list the month names in the Faroese.\n\tmonthNamesFaroese = []string{\"januar\", \"februar\", \"mars\", \"apríl\", \"mai\", \"juni\", \"juli\", \"august\", \"september\", \"oktober\", \"november\", \"desember\"}\n\t// monthsNameFaroeseAbbr lists the month name abbreviations in the Faroese.\n\tmonthsNameFaroeseAbbr = []string{\"jan\", \"feb\", \"mar\", \"apr\", \"mai\", \"jun\", \"jul\", \"aug\", \"sep\", \"okt\", \"nov\", \"des\"}\n\t// monthNamesFilipino list the month names in the Filipino.\n\tmonthNamesFilipino = []string{\"Enero\", \"Pebrero\", \"Marso\", \"Abril\", \"Mayo\", \"Hunyo\", \"Hulyo\", \"Agosto\", \"Setyembre\", \"Oktubre\", \"Nobyembre\", \"Disyembre\"}\n\t// monthNamesFilipinoAbbr lists the month name abbreviations in the\n\t// Filipino.\n\tmonthNamesFilipinoAbbr = []string{\"Ene\", \"Peb\", \"Mar\", \"Abr\", \"May\", \"Hun\", \"Hul\", \"Ago\", \"Set\", \"Okt\", \"Nob\", \"Dis\"}\n\t// monthsNamesFinnish list the month names in the Finnish.\n\tmonthNamesFinnish = []string{\"Etammikuu\", \"helmikuu\", \"maaliskuu\", \"huhtikuu\", \"toukokuu\", \"kesäkuu\", \"heinäkuu\", \"elokuu\", \"syyskuu\", \"lokakuu\", \"marraskuu\", \"joulukuu\"}\n\t// monthsNamesFinnishAbbr lists the month name abbreviations in the Finnish.\n\tmonthNamesFinnishAbbr = []string{\"tammi\", \"helmi\", \"maalis\", \"huhti\", \"touko\", \"kesä\", \"heinä\", \"elo\", \"syys\", \"loka\", \"marras\", \"joulu\"}\n\t// monthNamesFrench list the month names in the French.\n\tmonthNamesFrench = []string{\"janvier\", \"février\", \"mars\", \"avril\", \"mai\", \"juin\", \"juillet\", \"août\", \"septembre\", \"octobre\", \"novembre\", \"décembre\"}\n\t// monthNamesFrenchAbbr lists the month name abbreviations in the French.\n\tmonthNamesFrenchAbbr = []string{\"janv.\", \"févr.\", \"mars\", \"avr.\", \"mai\", \"juin\", \"juil.\", \"août\", \"sept.\", \"oct.\", \"nov.\", \"déc.\"}\n\t// monthNamesFrisian list the month names in the Frisian.\n\tmonthNamesFrisian = []string{\"Jannewaris\", \"Febrewaris\", \"Maart\", \"April\", \"Maaie\", \"Juny\", \"July\", \"Augustus\", \"Septimber\", \"Oktober\", \"Novimber\", \"Desimber\"}\n\t// monthNamesFrisianAbbr lists the month name abbreviations in the Frisian.\n\tmonthNamesFrisianAbbr = []string{\"jan\", \"feb\", \"Mrt\", \"Apr\", \"maa\", \"Jun\", \"Jul\", \"Aug\", \"sep\", \"Okt\", \"Nov\", \"Des\"}\n\t// monthNamesFulah list the month names in the Fulah.\n\tmonthNamesFulah = []string{\"siilo\", \"colte\", \"mbooy\", \"seeɗto\", \"duujal\", \"korse\", \"morso\", \"juko\", \"siilto\", \"yarkomaa\", \"jolal\", \"bowte\"}\n\t// monthNamesFulahAbbr lists the month name abbreviations in the Fulah.\n\tmonthNamesFulahAbbr = []string{\"sii\", \"col\", \"mbo\", \"see\", \"duu\", \"kor\", \"mor\", \"juk\", \"slt\", \"yar\", \"jol\", \"bow\"}\n\t// monthNamesGalician list the month names in the Galician.\n\tmonthNamesGalician = []string{\"Xaneiro\", \"Febreiro\", \"Marzo\", \"Abril\", \"Maio\", \"Xuño\", \"Xullo\", \"Agosto\", \"Setembro\", \"Outubro\", \"Novembro\", \"Decembro\"}\n\t// monthNamesGalicianAbbr lists the month name abbreviations in the\n\t// Galician.\n\tmonthNamesGalicianAbbr = []string{\"Xan.\", \"Feb.\", \"Mar.\", \"Abr.\", \"Maio\", \"Xuño\", \"Xul.\", \"Ago.\", \"Set.\", \"Out.\", \"Nov.\", \"Dec.\"}\n\t// monthNamesGeorgian list the month names in the Georgian.\n\tmonthNamesGeorgian = []string{\n\t\t\"\\u10D8\\u10D0\\u10DC\\u10D5\\u10D0\\u10E0\\u10D8\",\n\t\t\"\\u10D7\\u10D4\\u10D1\\u10D4\\u10E0\\u10D5\\u10D0\\u10DA\\u10D8\",\n\t\t\"\\u10DB\\u10D0\\u10E0\\u10E2\\u10D8\",\n\t\t\"\\u10D0\\u10DE\\u10E0\\u10D8\\u10DA\\u10D8\",\n\t\t\"\\u10DB\\u10D0\\u10D8\\u10E1\\u10D8\",\n\t\t\"\\u10D8\\u10D5\\u10DC\\u10D8\\u10E1\\u10D8\",\n\t\t\"\\u10D8\\u10D5\\u10DA\\u10D8\\u10E1\\u10D8\",\n\t\t\"\\u10D0\\u10D2\\u10D5\\u10D8\\u10E1\\u10E2\\u10DD\",\n\t\t\"\\u10E1\\u10D4\\u10E5\\u10E2\\u10D4\\u10DB\\u10D1\\u10D4\\u10E0\\u10D8\",\n\t\t\"\\u10DD\\u10E5\\u10E2\\u10DD\\u10DB\\u10D1\\u10D4\\u10E0\\u10D8\",\n\t\t\"\\u10DC\\u10DD\\u10D4\\u10DB\\u10D1\\u10D4\\u10E0\\u10D8\",\n\t\t\"\\u10D3\\u10D4\\u10D9\\u10D4\\u10DB\\u10D1\\u10D4\\u10E0\\u10D8\",\n\t}\n\t// monthNamesGeorgianAbbr lists the month name abbreviations in the\n\t// Georgian.\n\tmonthNamesGeorgianAbbr = []string{\n\t\t\"\\u10D8\\u10D0\\u10DC\",\n\t\t\"\\u10D7\\u10D4\\u10D1\",\n\t\t\"\\u10DB\\u10D0\\u10E0\",\n\t\t\"\\u10D0\\u10DE\\u10E0\",\n\t\t\"\\u10DB\\u10D0\\u10D8\",\n\t\t\"\\u10D8\\u10D5\\u10DC\",\n\t\t\"\\u10D8\\u10D5\\u10DA\",\n\t\t\"\\u10D0\\u10D2\\u10D5\",\n\t\t\"\\u10E1\\u10D4\\u10E5\",\n\t\t\"\\u10DD\\u10E5\\u10E2\",\n\t\t\"\\u10DC\\u10DD\\u10D4\",\n\t\t\"\\u10D3\\u10D4\\u10D9\",\n\t}\n\t// monthNamesGerman list the month names in the German.\n\tmonthNamesGerman = []string{\"Januar\", \"Februar\", \"März\", \"April\", \"Mai\", \"Juni\", \"Juli\", \"August\", \"September\", \"Oktober\", \"November\", \"Dezember\"}\n\t// monthNamesGermanAbbr list the month abbreviations in the German.\n\tmonthNamesGermanAbbr = []string{\"Jan\", \"Feb\", \"Mär\", \"Apr\", \"Mai\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Okt\", \"Nov\", \"Dez\"}\n\t// monthNamesGreek list the month names in the Greek.\n\tmonthNamesGreek = []string{\n\t\t\"\\u0399\\u03B1\\u03BD\\u03BF\\u03C5\\u03AC\\u03C1\\u03B9\\u03BF\\u03C2\",\n\t\t\"\\u03A6\\u03B5\\u03B2\\u03C1\\u03BF\\u03C5\\u03AC\\u03C1\\u03B9\\u03BF\\u03C2\",\n\t\t\"\\u039C\\u03AC\\u03C1\\u03C4\\u03B9\\u03BF\\u03C2\",\n\t\t\"\\u0391\\u03C0\\u03C1\\u03AF\\u03BB\\u03B9\\u03BF\\u03C2\",\n\t\t\"\\u039C\\u03AC\\u03B9\\u03BF\\u03C2\",\n\t\t\"\\u0399\\u03BF\\u03CD\\u03BD\\u03B9\\u03BF\\u03C2\",\n\t\t\"\\u0399\\u03BF\\u03CD\\u03BB\\u03B9\\u03BF\\u03C2\",\n\t\t\"\\u0391\\u03CD\\u03B3\\u03BF\\u03C5\\u03C3\\u03C4\\u03BF\\u03C2\",\n\t\t\"\\u03A3\\u03B5\\u03C0\\u03C4\\u03AD\\u03BC\\u03B2\\u03C1\\u03B9\\u03BF\\u03C2\",\n\t\t\"\\u039F\\u03BA\\u03C4\\u03CE\\u03B2\\u03C1\\u03B9\\u03BF\\u03C2\",\n\t\t\"\\u039D\\u03BF\\u03AD\\u03BC\\u03B2\\u03C1\\u03B9\\u03BF\\u03C2\",\n\t\t\"\\u0394\\u03B5\\u03BA\\u03AD\\u03BC\\u03B2\\u03C1\\u03B9\\u03BF\\u03C2\",\n\t}\n\t// monthNamesGreekAbbr list the month abbreviations in the Greek.\n\tmonthNamesGreekAbbr = []string{\n\t\t\"\\u0399\\u03B1\\u03BD\",\n\t\t\"\\u03A6\\u03B5\\u03B2\",\n\t\t\"\\u039C\\u03B1\\u03C1\",\n\t\t\"\\u0391\\u03C0\\u03C1\",\n\t\t\"\\u039C\\u03B1\\u03CA\",\n\t\t\"\\u0399\\u03BF\\u03C5\\u03BD\",\n\t\t\"\\u0399\\u03BF\\u03C5\\u03BB\",\n\t\t\"\\u0391\\u03C5\\u03B3\",\n\t\t\"\\u03A3\\u03B5\\u03C0\",\n\t\t\"\\u039F\\u03BA\\u03C4\",\n\t\t\"\\u039D\\u03BF\\u03B5\",\n\t\t\"\\u0394\\u03B5\\u03BA\",\n\t}\n\t// monthNamesGreenlandic list the month names in the Greenlandic.\n\tmonthNamesGreenlandic = []string{\"januaari\", \"februaari\", \"marsi\", \"apriili\", \"maaji\", \"juuni\", \"juuli\", \"aggusti\", \"septembari\", \"oktobari\", \"novembari\", \"decembari\"}\n\t// monthNamesGreenlandicAbbr list the month abbreviations in the\n\t// Greenlandic.\n\tmonthNamesGreenlandicAbbr = []string{\"jan\", \"feb\", \"mar\", \"apr\", \"mai\", \"jun\", \"jul\", \"aug\", \"sep\", \"okt\", \"nov\", \"dec\"}\n\t// monthNamesGuarani list the month names in the Guarani.\n\tmonthNamesGuarani = []string{\"jasyte\\u0129\", \"jasykõi\", \"jasyapy\", \"jasyrundy\", \"jasypo\", \"jasypote\\u0129\", \"jasypokõi\", \"jasypoapy\", \"jasyporundy\", \"jasypa\", \"jasypate\\u0129\", \"jasypakõi\"}\n\t// monthNamesGuaraniAbbr list the month abbreviations in the Guarani.\n\tmonthNamesGuaraniAbbr = []string{\"jteĩ\", \"jkõi\", \"japy\", \"jrun\", \"jpo\", \"jpot\", \"jpok\", \"jpoa\", \"jpor\", \"jpa\", \"jpat\", \"jpak\"}\n\t// monthNamesGujarati list the month names in the Gujarati.\n\tmonthNamesGujarati = []string{\n\t\t\"\\u0A9C\\u0ABE\\u0AA8\\u0ACD\\u0AAF\\u0AC1\\u0A86\\u0AB0\\u0AC0\",\n\t\t\"\\u0AAB\\u0AC7\\u0AAC\\u0ACD\\u0AB0\\u0AC1\\u0A86\\u0AB0\\u0AC0\",\n\t\t\"\\u0AAE\\u0ABE\\u0AB0\\u0ACD\\u0A9A\",\n\t\t\"\\u0A8F\\u0AAA\\u0ACD\\u0AB0\\u0ABF\\u0AB2\",\n\t\t\"\\u0AAE\\u0AC7\",\n\t\t\"\\u0A9C\\u0AC2\\u0AA8\",\n\t\t\"\\u0A9C\\u0AC1\\u0AB2\\u0ABE\\u0A88\",\n\t\t\"\\u0A91\\u0A97\\u0AB8\\u0ACD\\u0A9F\",\n\t\t\"\\u0AB8\\u0AAA\\u0ACD\\u0A9F\\u0AC7\\u0AAE\\u0ACD\\u0AAC\\u0AB0\",\n\t\t\"\\u0A91\\u0A95\\u0ACD\\u0A9F\\u0ACB\\u0AAC\\u0AB0\",\n\t\t\"\\u0AA8\\u0AB5\\u0AC7\\u0AAE\\u0ACD\\u0AAC\\u0AB0\",\n\t\t\"\\u0AA1\\u0ABF\\u0AB8\\u0AC7\\u0AAE\\u0ACD\\u0AAC\\u0AB0\",\n\t}\n\t// monthNamesGujaratiAbbr list the month abbreviations in the Gujarati.\n\tmonthNamesGujaratiAbbr = []string{\n\t\t\"\\u0A9C\\u0ABE\\u0AA8\\u0ACD\\u0AAF\\u0AC1\",\n\t\t\"\\u0AAB\\u0AC7\\u0AAC\\u0ACD\\u0AB0\\u0AC1\",\n\t\t\"\\u0AAE\\u0ABE\\u0AB0\\u0ACD\\u0A9A\",\n\t\t\"\\u0A8F\\u0AAA\\u0ACD\\u0AB0\\u0ABF\\u0AB2\",\n\t\t\"\\u0AAE\\u0AC7\",\n\t\t\"\\u0A9C\\u0AC2\\u0AA8\",\n\t\t\"\\u0A9C\\u0AC1\\u0AB2\\u0ABE\\u0A88\",\n\t\t\"\\u0A91\\u0A97\",\n\t\t\"\\u0AB8\\u0AAA\\u0ACD\\u0A9F\\u0AC7\",\n\t\t\"\\u0A91\\u0A95\\u0ACD\\u0A9F\\u0ACB\",\n\t\t\"\\u0AA8\\u0AB5\\u0AC7\",\n\t\t\"\\u0AA1\\u0ABF\\u0AB8\\u0AC7\",\n\t}\n\t// monthNamesHausa list the month names in the Hausa.\n\tmonthNamesHausa = []string{\"Janairu\", \"Fabrairu\", \"Maris\", \"Afirilu\", \"Mayu\", \"Yuni\", \"Yuli\", \"Agusta\", \"Satumba\", \"Oktoba\", \"Nuwamba\", \"Disamba\"}\n\t// monthNamesHawaiian list the month names in the Hawaiian.\n\tmonthNamesHawaiian = []string{\"Ianuali\", \"Pepeluali\", \"Malaki\", \"\\u02bbApelila\", \"Mei\", \"Iune\", \"Iulai\", \"\\u02bbAukake\", \"Kepakemapa\", \"\\u02bbOkakopa\", \"Nowemapa\", \"Kekemapa\"}\n\t// monthNamesHawaiianAbbr list the month name abbreviations in the\n\t// Hawaiiann.\n\tmonthNamesHawaiianAbbr = []string{\"Ian.\", \"Pep.\", \"Mal.\", \"\\u02bbAp.\", \"Mei\", \"Iun.\", \"Iul.\", \"\\u02bbAu.\", \"Kep.\", \"\\u02bbOk.\", \"Now.\", \"Kek.\"}\n\t// monthNamesHebrew list the month names in the Hebrew.\n\tmonthNamesHebrew = []string{\n\t\t\"\\u05D9\\u05E0\\u05D5\\u05D0\\u05E8\",\n\t\t\"\\u05E4\\u05D1\\u05E8\\u05D5\\u05D0\\u05E8\",\n\t\t\"\\u05DE\\u05E8\\u05E5\",\n\t\t\"\\u05D0\\u05E4\\u05E8\\u05D9\\u05DC\",\n\t\t\"\\u05DE\\u05D0\\u05D9\",\n\t\t\"\\u05D9\\u05D5\\u05E0\\u05D9\",\n\t\t\"\\u05D9\\u05D5\\u05DC\\u05D9\",\n\t\t\"\\u05D0\\u05D5\\u05D2\\u05D5\\u05E1\\u05D8\",\n\t\t\"\\u05E1\\u05E4\\u05D8\\u05DE\\u05D1\\u05E8\",\n\t\t\"\\u05D0\\u05D5\\u05E7\\u05D8\\u05D5\\u05D1\\u05E8\",\n\t\t\"\\u05E0\\u05D5\\u05D1\\u05DE\\u05D1\\u05E8\",\n\t\t\"\\u05D3\\u05E6\\u05DE\\u05D1\\u05E8\",\n\t}\n\t// monthNamesHindi list the month names in the Hindi.\n\tmonthNamesHindi = []string{\n\t\t\"\\u091C\\u0928\\u0935\\u0930\\u0940\",\n\t\t\"\\u092B\\u0930\\u0935\\u0930\\u0940\",\n\t\t\"\\u092E\\u093E\\u0930\\u094D\\u091A\",\n\t\t\"\\u0905\\u092A\\u094D\\u0930\\u0948\\u0932\",\n\t\t\"\\u092E\\u0908\",\n\t\t\"\\u091C\\u0942\\u0928\",\n\t\t\"\\u091C\\u0941\\u0932\\u093E\\u0908\",\n\t\t\"\\u0905\\u0917\\u0938\\u094D\\u0924\",\n\t\t\"\\u0938\\u093F\\u0924\\u092E\\u094D\\u092C\\u0930\",\n\t\t\"\\u0905\\u0915\\u094D\\u0924\\u0942\\u092C\\u0930\",\n\t\t\"\\u0928\\u0935\\u092E\\u094D\\u092C\\u0930\",\n\t\t\"\\u0926\\u093F\\u0938\\u092E\\u094D\\u092C\\u0930\",\n\t}\n\t// monthNamesHungarian list the month names in the Hungarian.\n\tmonthNamesHungarian = []string{\"január\", \"február\", \"március\", \"április\", \"május\", \"június\", \"július\", \"augusztus\", \"szeptember\", \"október\", \"november\", \"december\"}\n\t// monthNamesHungarianAbbr list the month name abbreviations in the\n\t// Hungarian.\n\tmonthNamesHungarianAbbr = []string{\"jan.\", \"febr.\", \"márc.\", \"ápr.\", \"máj.\", \"jún.\", \"júl.\", \"aug.\", \"szept.\", \"okt.\", \"nov.\", \"dec.\"}\n\t// monthNamesIcelandic list the month names in the Icelandic.\n\tmonthNamesIcelandic = []string{\"janúar\", \"febrúar\", \"mars\", \"apríl\", \"maí\", \"júní\", \"júlí\", \"ágúst\", \"september\", \"október\", \"nóvember\", \"desember\"}\n\t// monthNamesIcelandicAbbr list the month name abbreviations in the\n\t// Icelandic.\n\tmonthNamesIcelandicAbbr = []string{\"jan.\", \"feb.\", \"mar.\", \"apr.\", \"maí\", \"jún.\", \"júl.\", \"ágú.\", \"sep.\", \"okt.\", \"nóv.\", \"des.\"}\n\t// monthNamesIgbo list the month names in the Igbo.\n\tmonthNamesIgbo = []string{\"Jenụwarị\", \"Febụwarị\", \"Machị\", \"Eprelu\", \"Mey\", \"Juun\", \"Julaị\", \"Ọgọst\", \"Septemba\", \"Ọcktọba\", \"Nọvemba\", \"Disemba\"}\n\t// monthNamesIgboAbbr list the month name abbreviations in the Igbo.\n\tmonthNamesIgboAbbr = []string{\"Jen\", \"Feb\", \"Mac\", \"Epr\", \"Mey\", \"Jun\", \"Jul\", \"Ọgọ\", \"Sep\", \"Ọkt\", \"Nọv\", \"Dis\"}\n\t// monthNamesIndonesian list the month names in the Indonesian.\n\tmonthNamesIndonesian = []string{\"Januari\", \"Februari\", \"Maret\", \"April\", \"Mei\", \"Juni\", \"Juli\", \"Agustus\", \"September\", \"Oktober\", \"November\", \"Desember\"}\n\t// monthNamesIndonesianAbbr list the month name abbreviations in the\n\t// Indonesian.\n\tmonthNamesIndonesianAbbr = []string{\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"Mei\", \"Jun\", \"Jul\", \"Agu\", \"Sep\", \"Okt\", \"Nov\", \"Des\"}\n\t// monthNamesInuktitut list the month names in the Inuktitut.\n\tmonthNamesInuktitut = []string{\"Jaannuari\", \"Viivvuari\", \"Maatsi\", \"Iipuri\", \"Mai\", \"Juuni\", \"Julai\", \"Aaggiisi\", \"Sitipiri\", \"Utupiri\", \"Nuvipiri\", \"Tisipiri\"}\n\t// monthNamesInuktitutAbbr list the month name abbreviations in the\n\t// Inuktitut.\n\tmonthNamesInuktitutAbbr = []string{\"Jan\", \"Viv\", \"Mas\", \"Ipu\", \"Mai\", \"Jun\", \"Jul\", \"Agi\", \"Sii\", \"Uut\", \"Nuv\", \"Tis\"}\n\t// monthNamesIrish list the month names in the Irish.\n\tmonthNamesIrish = []string{\"Eanáir\", \"Feabhra\", \"Márta\", \"Aibreán\", \"Bealtaine\", \"Meitheamh\", \"Iúil\", \"Lúnasa\", \"Meán Fómhair\", \"Deireadh Fómhair\", \"Samhain\", \"Nollaig\"}\n\t// monthNamesIrishAbbr lists the month abbreviations in the Irish.\n\tmonthNamesIrishAbbr = []string{\"Ean\", \"Feabh\", \"Márta\", \"Aib\", \"Beal\", \"Meith\", \"Iúil\", \"Lún\", \"MFómh\", \"DFómh\", \"Samh\", \"Noll\"}\n\t// monthNamesItalian list the month names in the Italian.\n\tmonthNamesItalian = []string{\"gennaio\", \"febbraio\", \"marzo\", \"aprile\", \"maggio\", \"giugno\", \"luglio\", \"agosto\", \"settembre\", \"ottobre\", \"novembre\", \"dicembre\"}\n\t// monthNamesItalianAbbr list the month name abbreviations in the Italian.\n\tmonthNamesItalianAbbr = []string{\"gen\", \"feb\", \"mar\", \"apr\", \"mag\", \"giu\", \"lug\", \"ago\", \"set\", \"ott\", \"nov\", \"dic\"}\n\t// monthNamesKannada list the month names in the Kannada.\n\tmonthNamesKannada = []string{\n\t\t\"\\u0C9C\\u0CA8\\u0CB5\\u0CB0\\u0CBF\",\n\t\t\"\\u0CAB\\u0CC6\\u0CAC\\u0CCD\\u0CB0\\u0CB5\\u0CB0\\u0CBF\",\n\t\t\"\\u0CAE\\u0CBE\\u0CB0\\u0CCD\\u0C9A\\u0CCD\",\n\t\t\"\\u0C8F\\u0C8F\\u0CAA\\u0CCD\\u0CB0\\u0CBF\\u0CB2\\u0CCD\",\n\t\t\"\\u0CAE\\u0CC7\",\n\t\t\"\\u0C9C\\u0CC2\\u0CA8\\u0CCD\",\n\t\t\"\\u0C9C\\u0CC1\\u0CB2\\u0CC8\",\n\t\t\"\\u0C86\\u0C97\\u0CB8\\u0CCD\\u0C9F\\u0CCD\",\n\t\t\"\\u0CB8\\u0CC6\\u0CAA\\u0CCD\\u0C9F\\u0C82\\u0CAC\\u0CB0\\u0CCD\",\n\t\t\"\\u0C85\\u0C95\\u0CCD\\u0C9F\\u0CCB\\u0CAC\\u0CB0\\u0CCD\",\n\t\t\"\\u0CA8\\u0CB5\\u0CC6\\u0C82\\u0CAC\\u0CB0\\u0CCD\",\n\t\t\"\\u0CA1\\u0CBF\\u0CB8\\u0CC6\\u0C82\\u0CAC\\u0CB0\\u0CCD\",\n\t}\n\t// monthNamesKannadaAbbr lists the month abbreviations in the Kannada.\n\tmonthNamesKannadaAbbr = []string{\n\t\t\"\\u0C9C\\u0CA8\\u0CB5\\u0CB0\\u0CBF\",\n\t\t\"\\u0CAB\\u0CC6\\u0CAC\\u0CCD\\u0CB0\\u0CB5\\u0CB0\\u0CBF\",\n\t\t\"\\u0CAE\\u0CBE\\u0CB0\\u0CCD\\u0C9A\\u0CCD\",\n\t\t\"\\u0C8E\\u0CAA\\u0CCD\\u0CB0\\u0CBF\\u0CB2\\u0CCD\",\n\t\t\"\\u0CAE\\u0CC7\",\n\t\t\"\\u0C9C\\u0CC2\\u0CA8\\u0CCD\",\n\t\t\"\\u0C9C\\u0CC1\\u0CB2\\u0CC8\",\n\t\t\"\\u0C86\\u0C97\\u0CB8\\u0CCD\\u0C9F\\u0CCD\",\n\t\t\"\\u0CB8\\u0CC6\\u0CAA\\u0CCD\\u0C9F\\u0C82\\u0CAC\\u0CB0\\u0CCD\",\n\t\t\"\\u0C85\\u0C95\\u0CCD\\u0C9F\\u0CCB\\u0CAC\\u0CB0\\u0CCD\",\n\t\t\"\\u0CA8\\u0CB5\\u0CC6\\u0C82\\u0CAC\\u0CB0\\u0CCD\",\n\t\t\"\\u0CA1\\u0CBF\\u0CB8\\u0CC6\\u0C82\\u0CAC\\u0CB0\\u0CCD\",\n\t}\n\t// monthNamesKashmiri list the month names in the Kashmiri.\n\tmonthNamesKashmiri = []string{\n\t\t\"\\u062C\\u0646\\u0624\\u0631\\u06CC\",\n\t\t\"\\u0641\\u0631\\u0624\\u0631\\u06CC\",\n\t\t\"\\u0645\\u0627\\u0631\\u0655\\u0686\",\n\t\t\"\\u0627\\u067E\\u0631\\u06CC\\u0644\",\n\t\t\"\\u0645\\u06CC\\u0654\",\n\t\t\"\\u062C\\u0648\\u0657\\u0646\",\n\t\t\"\\u062C\\u0648\\u0657\\u0644\\u0627\\u06CC\\u06CC\",\n\t\t\"\\u0627\\u06AF\\u0633\\u062A\",\n\t\t\"\\u0633\\u062A\\u0645\\u0628\\u0631\",\n\t\t\"\\u0627\\u06A9\\u062A\\u0648\\u0657\\u0628\\u0631\",\n\t\t\"\\u0646\\u0648\\u0645\\u0628\\u0631\",\n\t\t\"\\u062F\\u0633\\u0645\\u0628\\u0631\",\n\t}\n\t// monthNamesKazakh list the month names in the Kazakh.\n\tmonthNamesKazakh = []string{\n\t\t\"\\u049A\\u0430\\u04A3\\u0442\\u0430\\u0440\",\n\t\t\"\\u0410\\u049B\\u043F\\u0430\\u043D\",\n\t\t\"\\u041D\\u0430\\u0443\\u0440\\u044B\\u0437\",\n\t\t\"\\u0421\\u04D9\\u0443\\u0456\\u0440\",\n\t\t\"\\u041C\\u0430\\u043C\\u044B\\u0440\",\n\t\t\"\\u041C\\u0430\\u0443\\u0441\\u044B\\u043C\",\n\t\t\"\\u0428\\u0456\\u043B\\u0434\\u0435\",\n\t\t\"\\u0422\\u0430\\u043C\\u044B\\u0437\",\n\t\t\"\\u049A\\u044B\\u0440\\u043A\\u04AF\\u0439\\u0435\\u043A\",\n\t\t\"\\u049A\\u0430\\u0437\\u0430\\u043D\",\n\t\t\"\\u049A\\u0430\\u0440\\u0430\\u0448\\u0430\",\n\t\t\"\\u0416\\u0435\\u043B\\u0442\\u043E\\u049B\\u0441\\u0430\\u043D\",\n\t}\n\t// monthNamesKazakhAbbr list the month name abbreviations in the Kazakh.\n\tmonthNamesKazakhAbbr = []string{\n\t\t\"\\u049B\\u0430\\u04A3\",\n\t\t\"\\u0430\\u049B\\u043F\",\n\t\t\"\\u043D\\u0430\\u0443\",\n\t\t\"\\u0441\\u04D9\\u0443\",\n\t\t\"\\u043C\\u0430\\u043C\",\n\t\t\"\\u043C\\u0430\\u0443\",\n\t\t\"\\u0448\\u0456\\u043B\",\n\t\t\"\\u0442\\u0430\\u043C\",\n\t\t\"\\u049B\\u044B\\u0440\",\n\t\t\"\\u049B\\u0430\\u0437\",\n\t\t\"\\u049B\\u0430\\u0440\",\n\t\t\"\\u0436\\u0435\\u043B\",\n\t}\n\t// monthNamesKhmer list the month names in the Khmer.\n\tmonthNamesKhmer = []string{\n\t\t\"\\u1798\\u1780\\u179A\\u17B6\",\n\t\t\"\\u1780\\u17BB\\u1798\\u17D2\\u1797\\u17C8\",\n\t\t\"\\u1798\\u17B7\\u1793\\u17B6\",\n\t\t\"\\u1798\\u17C1\\u179F\\u17B6\",\n\t\t\"\\u17A7\\u179F\\u1797\\u17B6\",\n\t\t\"\\u1798\\u17B7\\u1790\\u17BB\\u1793\\u17B6\",\n\t\t\"\\u1780\\u1780\\u17D2\\u1780\\u178A\\u17B6\",\n\t\t\"\\u179F\\u17B8\\u17A0\\u17B6\",\n\t\t\"\\u1780\\u1789\\u17D2\\u1789\\u17B6\",\n\t\t\"\\u178F\\u17BB\\u179B\\u17B6\",\n\t\t\"\\u179C\\u17B7\\u1785\\u17D2\\u1786\\u17B7\\u1780\\u17B6\",\n\t\t\"\\u1792\\u17D2\\u1793\\u17BC\",\n\t}\n\t// monthNamesKhmerAbbr list the month name abbreviations in the Khmer.\n\tmonthNamesKhmerAbbr = []string{\n\t\t\"\\u17E1\", \"\\u17E2\", \"\\u17E3\", \"\\u17E4\", \"\\u17E5\", \"\\u17E6\", \"\\u17E7\", \"\\u17E8\", \"\\u17E9\", \"\\u17E1\\u17E0\", \"\\u17E1\\u17E1\", \"\\u17E1\\u17E2\",\n\t\t\"\\u1798\", \"\\u1780\", \"\\u1798\", \"\\u1798\", \"\\u17A7\", \"\\u1798\", \"\\u1780\", \"\\u179F\", \"\\u1780\", \"\\u178F\", \"\\u179C\", \"\\u1792\",\n\t}\n\t// monthNamesKiche list the month names in the Kiche.\n\tmonthNamesKiche = []string{\"nab'e ik'\", \"ukab' ik'\", \"urox ik'\", \"ukaj ik'\", \"uro ik'\", \"uwaq ik'\", \"uwuq ik'\", \"uwajxaq ik'\", \"ub'elej ik'\", \"ulaj ik'\", \"ujulaj ik'\", \"ukab'laj ik'\"}\n\t// monthNamesKicheAbbr list the month name abbreviations in the Kiche.\n\tmonthNamesKicheAbbr = []string{\"nab'e\", \"ukab'\", \"urox\", \"ukaj\", \"uro\", \"uwaq\", \"uwuq\", \"uwajxaq\", \"ub'elej\", \"ulaj\", \"ujulaj\", \"ukab'laj\"}\n\t// monthNamesKinyarwanda list the month names in the Kinyarwanda.\n\tmonthNamesKinyarwanda = []string{\"Mutarama\", \"Gashyantare\", \"Werurwe\", \"Mata\", \"Gicuransi\", \"Kamena\", \"Nyakanga\", \"Kanama\", \"Nzeli\", \"Ukwakira\", \"Ugushyingo\", \"Ukuboza\"}\n\t// monthNamesKinyarwandaAbbr list the month name abbreviations in the Kinyarwanda.\n\tmonthNamesKinyarwandaAbbr = []string{\"mut.\", \"gas.\", \"wer.\", \"mat.\", \"gic.\", \"kam.\", \"Nyak\", \"kan.\", \"nze.\", \"Ukwak\", \"Ugus\", \"Ukub\"}\n\t// monthNamesKiswahili list the month names in the Kiswahili.\n\tmonthNamesKiswahili = []string{\"Januari\", \"Februari\", \"Machi\", \"Aprili\", \"Mei\", \"Juni\", \"Julai\", \"Agosti\", \"Septemba\", \"Oktoba\", \"Novemba\", \"Desemba\"}\n\t// monthNamesKiswahiliAbbr list the month name abbreviations in the Kiswahili.\n\tmonthNamesKiswahiliAbbr = []string{\"Jan\", \"Feb\", \"Mac\", \"Apr\", \"Mei\", \"Jun\", \"Jul\", \"Ago\", \"Sep\", \"Okt\", \"Nov\", \"Des\"}\n\t// monthNamesKonkani list the month names in the Konkani.\n\tmonthNamesKonkani = []string{\n\t\t\"\\u091C\\u093E\\u0928\\u0947\\u0935\\u093E\\u0930\\u0940\",\n\t\t\"\\u092B\\u0947\\u092C\\u094D\\u0930\\u0941\\u0935\\u093E\\u0930\\u0940\",\n\t\t\"\\u092E\\u093E\\u0930\\u094D\\u091A\",\n\t\t\"\\u090F\\u092A\\u094D\\u0930\\u093F\\u0932\",\n\t\t\"\\u092E\\u0947\",\n\t\t\"\\u091C\\u0942\\u0928\",\n\t\t\"\\u091C\\u0941\\u0932\\u0948\",\n\t\t\"\\u0911\\u0917\\u0938\\u094D\\u091F\",\n\t\t\"\\u0938\\u092A\\u094D\\u091F\\u0947\\u0902\\u092C\\u0930\",\n\t\t\"\\u0911\\u0915\\u094D\\u091F\\u094B\\u092C\\u0930\",\n\t\t\"\\u0928\\u094B\\u0935\\u0947\\u092E\\u094D\\u092C\\u0930\",\n\t\t\"\\u0921\\u093F\\u0938\\u0947\\u0902\\u092C\\u0930\",\n\t}\n\t// monthNamesKonkaniAbbr list the month name abbreviations in the Konkani.\n\tmonthNamesKonkaniAbbr = []string{\n\t\t\"\\u091C\\u093E\\u0928\\u0947\",\n\t\t\"\\u092B\\u0947\\u092C\\u094D\\u0930\\u0941\",\n\t\t\"\\u092E\\u093E\\u0930\\u094D\\u091A\",\n\t\t\"\\u090F\\u092A\\u094D\\u0930\\u093F\\u0932\",\n\t\t\"\\u092E\\u0947\",\n\t\t\"\\u091C\\u0942\\u0928\",\n\t\t\"\\u091C\\u0941\\u0932\\u0948\",\n\t\t\"\\u0911\\u0917.\",\n\t\t\"\\u0938\\u092A\\u094D\\u091F\\u0947\\u0902.\",\n\t\t\"\\u0911\\u0915\\u094D\\u091F\\u094B.\",\n\t\t\"\\u0928\\u094B\\u0935\\u0947.\",\n\t\t\"\\u0921\\u093F\\u0938\\u0947\\u0902\",\n\t}\n\t// monthNamesKoreanAbbr lists out the month number plus 월 for the Korean language.\n\tmonthNamesKoreanAbbr = []string{\"1월\", \"2월\", \"3월\", \"4월\", \"5월\", \"6월\", \"7월\", \"8월\", \"9월\", \"10월\", \"11월\", \"12월\"}\n\t// monthNamesKyrgyz list the month names in the Kyrgyz.\n\tmonthNamesKyrgyz = []string{\n\t\t\"\\u042F\\u043D\\u0432\\u0430\\u0440\\u044C\",\n\t\t\"\\u0424\\u0435\\u0432\\u0440\\u0430\\u043B\\u044C\",\n\t\t\"\\u041C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0410\\u043F\\u0440\\u0435\\u043B\\u044C\",\n\t\t\"\\u041C\\u0430\\u0439\",\n\t\t\"\\u0418\\u044E\\u043D\\u044C\",\n\t\t\"\\u0418\\u044E\\u043B\\u044C\",\n\t\t\"\\u0410\\u0432\\u0433\\u0443\\u0441\\u0442\",\n\t\t\"\\u0421\\u0435\\u043D\\u0442\\u044F\\u0431\\u0440\\u044C\",\n\t\t\"\\u041E\\u043A\\u0442\\u044F\\u0431\\u0440\\u044C\",\n\t\t\"\\u041D\\u043E\\u044F\\u0431\\u0440\\u044C\",\n\t\t\"\\u0414\\u0435\\u043A\\u0430\\u0431\\u0440\\u044C\",\n\t}\n\t// monthNamesKyrgyzAbbr lists the month name abbreviations in the Kyrgyz.\n\tmonthNamesKyrgyzAbbr = []string{\n\t\t\"\\u042F\\u043D\\u0432\",\n\t\t\"\\u0424\\u0435\\u0432\",\n\t\t\"\\u041C\\u0430\\u0440\",\n\t\t\"\\u0410\\u043F\\u0440\",\n\t\t\"\\u041C\\u0430\\u0439\",\n\t\t\"\\u0418\\u044E\\u043D\",\n\t\t\"\\u0418\\u044E\\u043B\",\n\t\t\"\\u0410\\u0432\\u0433\",\n\t\t\"\\u0421\\u0435\\u043D\",\n\t\t\"\\u041E\\u043A\\u0442\",\n\t\t\"\\u041D\\u043E\\u044F\",\n\t\t\"\\u0414\\u0435\\u043A\",\n\t}\n\t// monthNamesLao list the month names in the Lao.\n\tmonthNamesLao = []string{\n\t\t\"\\u0EA1\\u0EB1\\u0E87\\u0E81\\u0EAD\\u0E99\",\n\t\t\"\\u0E81\\u0EB8\\u0EA1\\u0E9E\\u0EB2\",\n\t\t\"\\u0EA1\\u0EB5\\u0E99\\u0EB2\",\n\t\t\"\\u0EC0\\u0EA1\\u0EAA\\u0EB2\",\n\t\t\"\\u0E9E\\u0EB6\\u0E94\\u0EAA\\u0EB0\\u0E9E\\u0EB2\",\n\t\t\"\\u0EA1\\u0EB4\\u0E96\\u0EB8\\u0E99\\u0EB2\",\n\t\t\"\\u0E81\\u0ECD\\u0EA5\\u0EB0\\u0E81\\u0EBB\\u0E94\",\n\t\t\"\\u0EAA\\u0EB4\\u0E87\\u0EAB\\u0EB2\",\n\t\t\"\\u0E81\\u0EB1\\u0E99\\u0E8D\\u0EB2\",\n\t\t\"\\u0E95\\u0EB8\\u0EA5\\u0EB2\",\n\t\t\"\\u0E9E\\u0EB0\\u0E88\\u0EB4\\u0E81\",\n\t\t\"\\u0E97\\u0EB1\\u0E99\\u0EA7\\u0EB2\",\n\t}\n\t// monthNamesLaoAbbr lists the month name abbreviations in the Lao.\n\tmonthNamesLaoAbbr = []string{\n\t\t\"\\u0EA1.\\u0E81.\",\n\t\t\"\\u0E81.\\u0E9E.\",\n\t\t\"\\u0EA1.\\u0E99.\",\n\t\t\"\\u0EA1.\\u0EAA.\",\n\t\t\"\\u0E9E.\\u0E9E.\",\n\t\t\"\\u0EA1\\u0EB4.\\u0E96.\",\n\t\t\"\\u0E81.\\u0EA5.\",\n\t\t\"\\u0EAA.\\u0EAB.\",\n\t\t\"\\u0E81.\\u0E8D.\",\n\t\t\"\\u0E95.\\u0EA5.\",\n\t\t\"\\u0E9E.\\u0E88.\",\n\t\t\"\\u0E97.\\u0EA7.\",\n\t}\n\t// monthNamesLatin list the month names in the Latin.\n\tmonthNamesLatin = []string{\"Ianuarius\", \"Februarius\", \"Martius\", \"Aprilis\", \"Maius\", \"Iunius\", \"Quintilis\", \"Sextilis\", \"September\", \"October\", \"November\", \"December\"}\n\t// monthNamesLatinAbbr list the month name abbreviations in the Latin.\n\tmonthNamesLatinAbbr = []string{\"Ian\", \"Feb\", \"Mar\", \"Apr\", \"Mai\", \"Iun\", \"Quint\", \"Sext\", \"Sept\", \"Oct\", \"Nov\", \"Dec\"}\n\t// monthNamesLatvian list the month names in the Latvian.\n\tmonthNamesLatvian = []string{\"janvāris\", \"februāris\", \"marts\", \"aprīlis\", \"maijs\", \"jūnijs\", \"jūlijs\", \"augusts\", \"septembris\", \"oktobris\", \"novembris\", \"decembris\"}\n\t// monthNamesLatvianAbbr list the month name abbreviations in the Latvian.\n\tmonthNamesLatvianAbbr = []string{\"janv.\", \"febr.\", \"marts\", \"apr.\", \"maijs\", \"jūn.\", \"jūl.\", \"aug.\", \"sept.\", \"okt.\", \"nov.\", \"dec.\"}\n\t// monthNamesLithuanian list the month names in the Lithuanian.\n\tmonthNamesLithuanian = []string{\"sausis\", \"vasaris\", \"kovas\", \"balandis\", \"gegužė\", \"birželis\", \"liepa\", \"rugpjūtis\", \"rugsėjis\", \"spalis\", \"lapkritis\", \"gruodis\"}\n\t// monthNamesLithuanianAbbr list the month name abbreviations in the Lithuanian.\n\tmonthNamesLithuanianAbbr = []string{\"saus.\", \"vas.\", \"kov.\", \"bal.\", \"geg.\", \"birž.\", \"liep.\", \"rugp.\", \"rugs.\", \"spal.\", \"lapkr.\", \"gruod.\"}\n\t// monthNamesLowerSorbian list the month names in the Lower Sorbian.\n\tmonthNamesLowerSorbian = []string{\"januar\", \"februar\", \"měrc\", \"apryl\", \"maj\", \"junij\", \"julij\", \"awgust\", \"september\", \"oktober\", \"nowember\", \"december\"}\n\t// monthNamesLowerSorbianAbbr list the month name abbreviations in the LowerSorbian.\n\tmonthNamesLowerSorbianAbbr = []string{\"jan\", \"feb\", \"měr\", \"apr\", \"maj\", \"jun\", \"jul\", \"awg\", \"sep\", \"okt\", \"now\", \"dec\"}\n\t// monthNamesLuxembourgish list the month names in the Lower Sorbian.\n\tmonthNamesLuxembourgish = []string{\"Januar\", \"Februar\", \"Mäerz\", \"Abrëll\", \"Mee\", \"Juni\", \"Juli\", \"August\", \"September\", \"Oktober\", \"November\", \"Dezember\"}\n\t// monthNamesLuxembourgishAbbr list the month name abbreviations in the Luxembourgish.\n\tmonthNamesLuxembourgishAbbr = []string{\"Jan\", \"Feb\", \"Mäe\", \"Abr\", \"Mee\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Okt\", \"Nov\", \"Dez\"}\n\t// monthNamesMacedonian list the month names in the Lower Sorbian.\n\tmonthNamesMacedonian = []string{\n\t\t\"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440\\u0438\",\n\t\t\"\\u0444\\u0435\\u0432\\u0440\\u0443\\u0430\\u0440\\u0438\",\n\t\t\"\\u043C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0430\\u043F\\u0440\\u0438\\u043B\",\n\t\t\"\\u043C\\u0430\\u0458\",\n\t\t\"\\u0458\\u0443\\u043D\\u0438\",\n\t\t\"\\u0458\\u0443\\u043B\\u0438\",\n\t\t\"\\u0430\\u0432\\u0433\\u0443\\u0441\\u0442\",\n\t\t\"\\u0441\\u0435\\u043F\\u0442\\u0435\\u043C\\u0432\\u0440\\u0438\",\n\t\t\"\\u043E\\u043A\\u0442\\u043E\\u043C\\u0432\\u0440\\u0438\",\n\t\t\"\\u043D\\u043E\\u0435\\u043C\\u0432\\u0440\\u0438\",\n\t\t\"\\u0434\\u0435\\u043A\\u0435\\u043C\\u0432\\u0440\\u0438\",\n\t}\n\t// monthNamesMacedonianAbbr list the month name abbreviations in the Macedonian.\n\tmonthNamesMacedonianAbbr = []string{\n\t\t\"\\u0458\\u0430\\u043D.\",\n\t\t\"\\u0444\\u0435\\u0432.\",\n\t\t\"\\u043C\\u0430\\u0440.\",\n\t\t\"\\u0430\\u043F\\u0440.\",\n\t\t\"\\u043C\\u0430\\u0458\",\n\t\t\"\\u0458\\u0443\\u043D.\",\n\t\t\"\\u0458\\u0443\\u043B.\",\n\t\t\"\\u0430\\u0432\\u0433.\",\n\t\t\"\\u0441\\u0435\\u043F\\u0442.\",\n\t\t\"\\u043E\\u043A\\u0442.\",\n\t\t\"\\u043D\\u043E\\u0435\\u043C.\",\n\t\t\"\\u0434\\u0435\\u043A.\",\n\t}\n\t// monthNamesMalay list the month names in the Malay.\n\tmonthNamesMalay = []string{\"Januari\", \"Februari\", \"Mac\", \"April\", \"Mei\", \"Jun\", \"Julai\", \"Ogos\", \"September\", \"Oktober\", \"November\", \"Disember\"}\n\t// monthNamesMalayAbbr list the month name abbreviations in the Malay.\n\tmonthNamesMalayAbbr = []string{\"Jan\", \"Feb\", \"Mac\", \"Apr\", \"Mei\", \"Jun\", \"Jul\", \"Ogo\", \"Sep\", \"Okt\", \"Nov\", \"Dis\"}\n\t// monthNamesMalayalam list the month names in the Malayalam.\n\tmonthNamesMalayalam = []string{\n\t\t\"\\u0D1C\\u0D28\\u0D41\\u0D35\\u0D30\\u0D3F\",\n\t\t\"\\u0D2B\\u0D46\\u0D2C\\u0D4D\\u0D30\\u0D41\\u0D35\\u0D30\\u0D3F\",\n\t\t\"\\u0D2E\\u0D3E\\u0D30\\u0D4D\\u200D\\u200C\\u0D1A\\u0D4D\\u0D1A\\u0D4D\",\n\t\t\"\\u0D0F\\u0D2A\\u0D4D\\u0D30\\u0D3F\\u0D32\\u0D4D\\u200D\",\n\t\t\"\\u0D2E\\u0D47\\u0D2F\\u0D4D\",\n\t\t\"\\u0D1C\\u0D42\\u0D7A\",\n\t\t\"\\u0D1C\\u0D42\\u0D32\\u0D48\",\n\t\t\"\\u0D06\\u0D17\\u0D38\\u0D4D\\u0D31\\u0D4D\\u0D31\\u0D4D\",\n\t\t\"\\u0D38\\u0D46\\u0D2A\\u0D4D\\u200C\\u0D31\\u0D4D\\u0D31\\u0D02\\u0D2C\\u0D30\\u0D4D\\u200D\",\n\t\t\"\\u0D12\\u0D15\\u0D4D\\u200C\\u0D1F\\u0D4B\\u0D2C\\u0D30\\u0D4D\\u200D\",\n\t\t\"\\u0D28\\u0D35\\u0D02\\u0D2C\\u0D30\\u0D4D\\u200D\",\n\t\t\"\\u0D21\\u0D3F\\u0D38\\u0D02\\u0D2C\\u0D30\\u0D4D\\u200D\",\n\t}\n\t// monthNamesMalayalamAbbr list the month name abbreviations in the Malayalam.\n\tmonthNamesMalayalamAbbr = []string{\n\t\t\"\\u0D1C\\u0D28\\u0D41\",\n\t\t\"\\u0D2B\\u0D46\\u0D2C\\u0D4D\\u0D30\\u0D41\",\n\t\t\"\\u0D2E\\u0D3E\\u0D7C\",\n\t\t\"\\u0D0F\\u0D2A\\u0D4D\\u0D30\\u0D3F\",\n\t\t\"\\u0D2E\\u0D47\\u0D2F\\u0D4D\",\n\t\t\"\\u0D1C\\u0D42\\u0D7A\",\n\t\t\"\\u0D1C\\u0D42\\u0D32\\u0D48\",\n\t\t\"\\u0D13\\u0D17\",\n\t\t\"\\u0D38\\u0D46\\u0D2A\\u0D4D\\u0D31\\u0D4D\\u0D31\\u0D02\",\n\t\t\"\\u0D12\\u0D15\\u0D4D\\u0D1F\\u0D4B\",\n\t\t\"\\u0D28\\u0D35\\u0D02\",\n\t\t\"\\u0D21\\u0D3F\\u0D38\\u0D02\",\n\t}\n\t// monthNamesMaltese list the month names in the Maltese.\n\tmonthNamesMaltese = []string{\"Jannar\", \"Frar\", \"Marzu\", \"April\", \"Mejju\", \"Ġunju\", \"Lulju\", \"Awwissu\", \"Settembru\", \"Ottubru\", \"Novembru\", \"Diċembru\"}\n\t// monthNamesMalteseAbbr list the month name abbreviations in the Maltese.\n\tmonthNamesMalteseAbbr = []string{\"Jan\", \"Fra\", \"Mar\", \"Apr\", \"Mej\", \"Ġun\", \"Lul\", \"Aww\", \"Set\", \"Ott\", \"Nov\", \"Diċ\"}\n\t// monthNamesMaori list the month names in the Maori.\n\tmonthNamesMaori = []string{\"Kohitātea\", \"Huitanguru\", \"Poutūterangi\", \"Paengawhāwhā\", \"Haratua\", \"Pipiri\", \"Hōngongoi\", \"Hereturikōkā\", \"Mahuru\", \"Whiringa ā-nuku\", \"Whiringa ā-rangi\", \"Hakihea\"}\n\t// monthNamesMaoriAbbr list the month name abbreviations in the Maori.\n\tmonthNamesMaoriAbbr = []string{\"Kohi\", \"Hui\", \"Pou\", \"Pae\", \"Hara\", \"Pipi\", \"Hōngo\", \"Here\", \"Mahu\", \"Nuku\", \"Rangi\", \"Haki\"}\n\t// monthNamesMapudungun list the month name abbreviations in the Mapudungun.\n\tmonthNamesMapudungun = []string{\"Kiñe Tripantu\", \"Epu\", \"Kila\", \"Meli\", \"Kechu\", \"Cayu\", \"Regle\", \"Purha\", \"Aiya\", \"Marhi\", \"Marhi Kiñe\", \"Marhi Epu\"}\n\t// monthNamesMarathi list the month names in the Marathi.\n\tmonthNamesMarathi = []string{\n\t\t\"\\u091C\\u093E\\u0928\\u0947\\u0935\\u093E\\u0930\\u0940\",\n\t\t\"\\u092B\\u0947\\u092C\\u094D\\u0930\\u0941\\u0935\\u093E\\u0930\\u0940\",\n\t\t\"\\u092E\\u093E\\u0930\\u094D\\u091A\",\n\t\t\"\\u090F\\u092A\\u094D\\u0930\\u093F\\u0932\",\n\t\t\"\\u092E\\u0947\",\n\t\t\"\\u091C\\u0942\\u0928\",\n\t\t\"\\u091C\\u0941\\u0932\\u0948\",\n\t\t\"\\u0911\\u0917\\u0938\\u094D\\u091F\",\n\t\t\"\\u0938\\u092A\\u094D\\u091F\\u0947\\u0902\\u092C\\u0930\",\n\t\t\"\\u0911\\u0915\\u094D\\u091F\\u094B\\u092C\\u0930\",\n\t\t\"\\u0928\\u094B\\u0935\\u094D\\u0939\\u0947\\u0902\\u092C\\u0930\",\n\t\t\"\\u0921\\u093F\\u0938\\u0947\\u0902\\u092C\\u0930\",\n\t}\n\t// monthNamesMarathiAbbr lists the month name abbreviations in Marathi.\n\tmonthNamesMarathiAbbr = []string{\n\t\t\"\\u091C\\u093E\\u0928\\u0947.\",\n\t\t\"\\u092B\\u0947\\u092C\\u094D\\u0930\\u0941.\",\n\t\t\"\\u092E\\u093E\\u0930\\u094D\\u091A\",\n\t\t\"\\u090F\\u092A\\u094D\\u0930\\u093F\",\n\t\t\"\\u092E\\u0947\",\n\t\t\"\\u091C\\u0942\\u0928\",\n\t\t\"\\u091C\\u0941\\u0932\\u0948\",\n\t\t\"\\u0911\\u0917.\",\n\t\t\"\\u0938\\u092A\\u094D\\u091F\\u0947\\u0902.\",\n\t\t\"\\u0911\\u0915\\u094D\\u091F\\u094B.\",\n\t\t\"\\u0928\\u094B\\u0935\\u094D\\u0939\\u0947\\u0902.\",\n\t\t\"\\u0921\\u093F\\u0938\\u0947\\u0902.\",\n\t}\n\t// monthNamesMohawk list the month names in the Mohawk.\n\tmonthNamesMohawk = []string{\"Tsothohrkó:Wa\", \"Enniska\", \"Enniskó:Wa\", \"Onerahtókha\", \"Onerahtohkó:Wa\", \"Ohiari:Ha\", \"Ohiarihkó:Wa\", \"Seskéha\", \"Seskehkó:Wa\", \"Kenténha\", \"Kentenhkó:Wa\", \"Tsothóhrha\"}\n\t// monthNamesMongolian list the month names in the Mongolian.\n\tmonthNamesMongolian = []string{\n\t\t\"\\u041D\\u044D\\u0433\\u0434\\u04AF\\u0433\\u044D\\u044D\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"\\u0425\\u043E\\u0451\\u0440\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"\\u0413\\u0443\\u0440\\u0430\\u0432\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"\\u0414\\u04E9\\u0440\\u04E9\\u0432\\u0434\\u04AF\\u0433\\u044D\\u044D\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"\\u0422\\u0430\\u0432\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"\\u0417\\u0443\\u0440\\u0433\\u0430\\u0430\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"\\u0414\\u043E\\u043B\\u043E\\u043E\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"\\u041D\\u0430\\u0439\\u043C\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"\\u0415\\u0441\\u0434\\u04AF\\u0433\\u044D\\u044D\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"\\u0410\\u0440\\u0430\\u0432\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"\\u0410\\u0440\\u0432\\u0430\\u043D \\u043D\\u044D\\u0433\\u0434\\u04AF\\u0433\\u044D\\u044D\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"\\u0410\\u0440\\u0432\\u0430\\u043D \\u0445\\u043E\\u0451\\u0440\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440\",\n\t}\n\t// monthNamesMongolianAbbr lists the month name abbreviations in Mongolian.\n\tmonthNamesMongolianAbbr = []string{\n\t\t\"1-\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"2-\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"3-\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"4-\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"5-\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"6-\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"7-\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"8-\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"9-\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"10-\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"11-\\u0440 \\u0441\\u0430\\u0440\",\n\t\t\"12-\\u0440 \\u0441\\u0430\\u0440\",\n\t}\n\t// monthNamesMoroccoAbbr lists the month name abbreviations in the Morocco.\n\tmonthNamesMoroccoAbbr = []string{\"jan.\", \"fév.\", \"mar.\", \"avr.\", \"mai\", \"jui.\", \"juil.\", \"août\", \"sept.\", \"oct.\", \"nov.\", \"déc.\"}\n\t// monthNamesNepali list the month names in the Nepali.\n\tmonthNamesNepali = []string{\n\t\t\"\\u091C\\u0928\\u0935\\u0930\\u0940\",\n\t\t\"\\u092B\\u0947\\u092C\\u094D\\u0930\\u0941\\u0905\\u0930\\u0940\",\n\t\t\"\\u092E\\u093E\\u0930\\u094D\\u091A\",\n\t\t\"\\u0905\\u092A\\u094D\\u0930\\u093F\\u0932\",\n\t\t\"\\u092E\\u0947\",\n\t\t\"\\u091C\\u0942\\u0928\",\n\t\t\"\\u091C\\u0941\\u0932\\u093E\\u0908\",\n\t\t\"\\u0905\\u0917\\u0938\\u094D\\u0924\",\n\t\t\"\\u0938\\u0947\\u092A\\u094D\\u091F\\u0947\\u092E\\u094D\\u092C\\u0930\",\n\t\t\"\\u0905\\u0915\\u094D\\u091F\\u094B\\u092C\\u0930\",\n\t\t\"\\u0928\\u094B\\u092D\\u0947\\u092E\\u094D\\u092C\\u0930\",\n\t\t\"\\u0921\\u093F\\u0938\\u0947\\u092E\\u094D\\u092C\\u0930\",\n\t}\n\t// monthNamesNepaliAbbr lists the month name abbreviations in the Nepali.\n\tmonthNamesNepaliAbbr = []string{\n\t\t\"\\u091C\\u0928\",\n\t\t\"\\u092B\\u0947\\u092C\",\n\t\t\"\\u092E\\u093E\\u0930\\u094D\\u091A\",\n\t\t\"\\u0905\\u092A\\u094D\\u0930\\u093F\\u0932\",\n\t\t\"\\u092E\\u0947\",\n\t\t\"\\u091C\\u0942\\u0928\",\n\t\t\"\\u091C\\u0941\\u0932\\u093E\\u0908\",\n\t\t\"\\u0905\\u0917\",\n\t\t\"\\u0938\\u0947\\u092A\\u094D\\u091F\",\n\t\t\"\\u0905\\u0915\\u094D\\u091F\",\n\t\t\"\\u0928\\u094B\\u092D\",\n\t\t\"\\u0921\\u093F\\u0938\",\n\t}\n\t// monthNamesNepaliIN list the month names in the India Nepali.\n\tmonthNamesNepaliIN = []string{\n\t\t\"\\u091C\\u0928\\u0935\\u0930\\u0940\",\n\t\t\"\\u092B\\u0930\\u0935\\u0930\\u0940\",\n\t\t\"\\u092E\\u093E\\u0930\\u094D\\u091A\",\n\t\t\"\\u0905\\u092A\\u094D\\u0930\\u0947\\u0932\",\n\t\t\"\\u092E\\u0908\",\n\t\t\"\\u091C\\u0941\\u0928\",\n\t\t\"\\u091C\\u0941\\u0932\\u093E\\u0908\",\n\t\t\"\\u0905\\u0917\\u0938\\u094D\\u091F\",\n\t\t\"\\u0938\\u0947\\u092A\\u094D\\u091F\\u0947\\u092E\\u094D\\u092C\\u0930\",\n\t\t\"\\u0905\\u0915\\u094D\\u091F\\u094B\\u092C\\u0930\",\n\t\t\"\\u0928\\u094B\\u092D\\u0947\\u092E\\u094D\\u092C\\u0930\",\n\t\t\"\\u0926\\u093F\\u0938\\u092E\\u094D\\u092C\\u0930\",\n\t}\n\t// monthNamesNepaliINAbbr lists the month name abbreviations in the India Nepali.\n\tmonthNamesNepaliINAbbr = []string{\n\t\t\"\\u091C\\u0928\\u0935\\u0930\\u0940\",\n\t\t\"\\u092B\\u0947\\u092C\\u094D\\u0930\\u0941\\u0905\\u0930\\u0940\",\n\t\t\"\\u092E\\u093E\\u0930\\u094D\\u091A\",\n\t\t\"\\u0905\\u092A\\u094D\\u0930\\u093F\",\n\t\t\"\\u092E\\u0947\",\n\t\t\"\\u091C\\u0941\\u0928\",\n\t\t\"\\u091C\\u0941\\u0932\\u093E\",\n\t\t\"\\u0905\\u0917\\u0938\\u094D\\u091F\",\n\t\t\"\\u0938\\u0947\\u092A\\u094D\\u091F\\u0947\\u092E\\u094D\\u092C\\u0930\",\n\t\t\"\\u0905\\u0915\\u094D\\u091F\\u094B\",\n\t\t\"\\u0928\\u094B\\u092D\\u0947\",\n\t\t\"\\u0921\\u093F\\u0938\\u0947\",\n\t}\n\t// monthNamesNigeria list the month names in the Nigeria.\n\tmonthNamesNigeria = []string{\"samwiee\", \"feeburyee\", \"marsa\", \"awril\", \"me\", \"suyeŋ\", \"sulyee\", \"ut\", \"satambara\", \"oktoobar\", \"nowamburu\", \"deesamburu\"}\n\t// monthNamesNigeriaAbbr lists the month name abbreviations in the Nigeria.\n\tmonthNamesNigeriaAbbr = []string{\"samw\", \"feeb\", \"mar\", \"awr\", \"me\", \"suy\", \"sul\", \"ut\", \"sat\", \"okt\", \"now\", \"dees\"}\n\t// monthNamesNorwegian list the month names in the Norwegian.\n\tmonthNamesNorwegian = []string{\"januar\", \"februar\", \"mars\", \"april\", \"mai\", \"juni\", \"juli\", \"august\", \"september\", \"oktober\", \"november\", \"desember\"}\n\t// monthNamesOccitan list the month names in the Occitan.\n\tmonthNamesOccitan = []string{\"genièr\", \"febrièr\", \"març\", \"abril\", \"mai\", \"junh\", \"julhet\", \"agost\", \"setembre\", \"octobre\", \"novembre\", \"decembre\"}\n\t// monthNamesOccitanAbbr lists the month name abbreviations in the Occitan.\n\tmonthNamesOccitanAbbr = []string{\"gen.\", \"feb.\", \"març\", \"abr.\", \"mai\", \"junh\", \"julh\", \"ag.\", \"set.\", \"oct.\", \"nov.\", \"dec.\"}\n\t// monthNamesOdia list the month names in the Odia.\n\tmonthNamesOdia = []string{\n\t\t\"\\u0B1C\\u0B3E\\u0B28\\u0B41\\u0B5F\\u0B3E\\u0B30\\u0B40\",\n\t\t\"\\u0B2B\\u0B47\\u0B2C\\u0B43\\u0B06\\u0B30\\u0B40\",\n\t\t\"\\u0B2E\\u0B3E\\u0B30\\u0B4D\\u0B1A\\u0B4D\\u0B1A\",\n\t\t\"\\u0B0F\\u0B2A\\u0B4D\\u0B30\\u0B3F\\u0B32\\u0B4D\\u200C\",\n\t\t\"\\u0B2E\\u0B47\",\n\t\t\"\\u0B1C\\u0B41\\u0B28\\u0B4D\\u200C\",\n\t\t\"\\u0B1C\\u0B41\\u0B32\\u0B3E\\u0B07\",\n\t\t\"\\u0B05\\u0B17\\u0B37\\u0B4D\\u0B1F\",\n\t\t\"\\u0B38\\u0B47\\u0B2A\\u0B4D\\u0B1F\\u0B47\\u0B2E\\u0B4D\\u0B2C\\u0B30\",\n\t\t\"\\u0B05\\u0B15\\u0B4D\\u0B1F\\u0B4B\\u0B2C\\u0B30\",\n\t\t\"\\u0B28\\u0B2D\\u0B47\\u0B2E\\u0B4D\\u0B2C\\u0B30\",\n\t\t\"\\u0B21\\u0B3F\\u0B38\\u0B47\\u0B2E\\u0B4D\\u0B2C\\u0B30\",\n\t}\n\t// monthNamesOromo list the month names in the Oromo.\n\tmonthNamesOromo = []string{\"Amajjii\", \"Guraandhala\", \"Bitooteessa\", \"Elba\", \"Caamsa\", \"Waxabajjii\", \"Adooleessa\", \"Hagayya\", \"Fuulbana\", \"Onkololeessa\", \"Sadaasa\", \"Muddee\"}\n\t// monthNamesOromoAbbr list the month abbreviations in the Oromo.\n\tmonthNamesOromoAbbr = []string{\"Ama\", \"Gur\", \"Bit\", \"Elb\", \"Cam\", \"Wax\", \"Ado\", \"Hag\", \"Ful\", \"Onk\", \"Sad\", \"Mud\"}\n\t// monthNamesPashto list the month names in the Pashto.\n\tmonthNamesPashto = []string{\n\t\t\"\\u0633\\u0644\\u0648\\u0627\\u063A\\u0647\",\n\t\t\"\\u0643\\u0628\",\n\t\t\"\\u0648\\u0631\\u0649\",\n\t\t\"\\u063A\\u0648\\u064A\\u0649\",\n\t\t\"\\u063A\\u0628\\u0631\\u06AB\\u0648\\u0644\\u0649\",\n\t\t\"\\u0686\\u0646\\u06AB\\u0627 \\u069A\\u0632\\u0645\\u0631\\u0649\",\n\t\t\"\\u0632\\u0645\\u0631\\u0649\",\n\t\t\"\\u0648\\u0696\\u0649\",\n\t\t\"\\u062A\\u0644\\u0647\",\n\t\t\"\\u0644\\u0693\\u0645\",\n\t\t\"\\u0644\\u0646\\u0688 \\u06CD\",\n\t\t\"\\u0645\\u0631\\u063A\\u0648\\u0645\\u0649\",\n\t}\n\t// monthNamesPersian list the month names in the Persian.\n\tmonthNamesPersian = []string{\n\t\t\"\\u0698\\u0627\\u0646\\u0648\\u064A\\u0647\",\n\t\t\"\\u0641\\u0648\\u0631\\u064A\\u0647\",\n\t\t\"\\u0645\\u0627\\u0631\\u0633\",\n\t\t\"\\u0622\\u0648\\u0631\\u064A\\u0644\",\n\t\t\"\\u0645\\u0647\",\n\t\t\"\\u0698\\u0648\\u0626\\u0646\",\n\t\t\"\\u0698\\u0648\\u0626\\u064A\\u0647\",\n\t\t\"\\u0627\\u0648\\u062A\",\n\t\t\"\\u0633\\u067E\\u062A\\u0627\\u0645\\u0628\\u0631\",\n\t\t\"\\u0627\\u064F\\u0643\\u062A\\u0628\\u0631\",\n\t\t\"\\u0646\\u0648\\u0627\\u0645\\u0628\\u0631\",\n\t\t\"\\u062F\\u0633\\u0627\\u0645\\u0628\\u0631\",\n\t}\n\t// monthNamesPolish list the month names in the Polish.\n\tmonthNamesPolish = []string{\"styczeń\", \"luty\", \"marzec\", \"kwiecień\", \"maj\", \"czerwiec\", \"lipiec\", \"sierpień\", \"wrzesień\", \"październik\", \"listopad\", \"grudzień\"}\n\t// monthNamesPortuguese list the month names in the Portuguese.\n\tmonthNamesPortuguese = []string{\"janeiro\", \"fevereiro\", \"março\", \"abril\", \"maio\", \"junho\", \"julho\", \"agosto\", \"setembro\", \"outubro\", \"novembro\", \"dezembro\"}\n\t// monthNamesPunjabi list the month names in the Punjabi.\n\tmonthNamesPunjabi = []string{\n\t\t\"\\u0A1C\\u0A28\\u0A35\\u0A30\\u0A40\",\n\t\t\"\\u0A2B\\u0A3C\\u0A30\\u0A35\\u0A30\\u0A40\",\n\t\t\"\\u0A2E\\u0A3E\\u0A30\\u0A1A\",\n\t\t\"\\u0A05\\u0A2A\\u0A4D\\u0A30\\u0A48\\u0A32\",\n\t\t\"\\u0A2E\\u0A08\",\n\t\t\"\\u0A1C\\u0A42\\u0A28\",\n\t\t\"\\u0A1C\\u0A41\\u0A32\\u0A3E\\u0A08\",\n\t\t\"\\u0A05\\u0A17\\u0A38\\u0A24\",\n\t\t\"\\u0A38\\u0A24\\u0A70\\u0A2C\\u0A30\",\n\t\t\"\\u0A05\\u0A15\\u0A24\\u0A42\\u0A2C\\u0A30\",\n\t\t\"\\u0A28\\u0A35\\u0A70\\u0A2C\\u0A30\",\n\t\t\"\\u0A26\\u0A38\\u0A70\\u0A2C\\u0A30\",\n\t}\n\t// monthNamesPunjabiArab list the month names in the Punjabi Arab.\n\tmonthNamesPunjabiArab = []string{\n\t\t\"\\u062C\\u0646\\u0648\\u0631\\u06CC\",\n\t\t\"\\u0641\\u0631\\u0648\\u0631\\u06CC\",\n\t\t\"\\u0645\\u0627\\u0631\\u0686\",\n\t\t\"\\u0627\\u067E\\u0631\\u06CC\\u0644\",\n\t\t\"\\u0645\\u0626\\u06CC\",\n\t\t\"\\u062C\\u0648\\u0646\",\n\t\t\"\\u062C\\u0648\\u0644\\u0627\\u0626\\u06CC\",\n\t\t\"\\u0627\\u06AF\\u0633\\u062A\",\n\t\t\"\\u0633\\u062A\\u0645\\u0628\\u0631\",\n\t\t\"\\u0627\\u06A9\\u062A\\u0648\\u0628\\u0631\",\n\t\t\"\\u0646\\u0648\\u0645\\u0628\\u0631\",\n\t\t\"\\u062F\\u0633\\u0645\\u0628\\u0631\",\n\t}\n\t// monthNamesQuechua list the month names in the Quechua.\n\tmonthNamesQuechua = []string{\"Qulla puquy\", \"Hatun puquy\", \"Pauqar waray\", \"ayriwa\", \"Aymuray\", \"Inti raymi\", \"Anta Sitwa\", \"Qhapaq Sitwa\", \"Uma raymi\", \"Kantaray\", \"Ayamarq'a\", \"Kapaq Raymi\"}\n\t// monthNamesQuechuaEcuador list the month names in the Quechua Ecuador.\n\tmonthNamesQuechuaEcuador = []string{\"kulla\", \"panchi\", \"pawkar\", \"ayriwa\", \"aymuray\", \"raymi\", \"sitwa\", \"karwa\", \"kuski\", \"wayru\", \"sasi\", \"kapak\"}\n\t// monthNamesRomanian list the month names in the Romanian.\n\tmonthNamesRomanian = []string{\"ianuarie\", \"februarie\", \"martie\", \"aprilie\", \"mai\", \"iunie\", \"iulie\", \"august\", \"septembrie\", \"octombrie\", \"noiembrie\", \"decembrie\"}\n\t// monthNamesRomanianAbbr list the month abbreviations in the Romanian.\n\tmonthNamesRomanianAbbr = []string{\"ian.\", \"feb.\", \"mar.\", \"apr.\", \"mai\", \"iun.\", \"iul.\", \"aug.\", \"sept.\", \"oct.\", \"nov.\", \"dec.\"}\n\t// monthNamesRomansh list the month names in the Romansh.\n\tmonthNamesRomansh = []string{\"schaner\", \"favrer\", \"mars\", \"avrigl\", \"matg\", \"zercladur\", \"fanadur\", \"avust\", \"settember\", \"october\", \"november\", \"december\"}\n\t// monthNamesRomanshAbbr list the month abbreviations in the Romansh.\n\tmonthNamesRomanshAbbr = []string{\"schan.\", \"favr.\", \"mars\", \"avr.\", \"matg\", \"zercl.\", \"fan.\", \"avust\", \"sett.\", \"oct.\", \"nov.\", \"dec.\"}\n\t// monthNamesRussian list the month names in the Russian.\n\tmonthNamesRussian = []string{\n\t\t\"\\u044F\\u043D\\u0432\\u0430\\u0440\\u044C\",\n\t\t\"\\u0444\\u0435\\u0432\\u0440\\u0430\\u043B\\u044C\",\n\t\t\"\\u043C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0430\\u043F\\u0440\\u0435\\u043B\\u044C\",\n\t\t\"\\u043C\\u0430\\u0439\",\n\t\t\"\\u0438\\u044E\\u043D\\u044C\",\n\t\t\"\\u0438\\u044E\\u043B\\u044C\",\n\t\t\"\\u0430\\u0432\\u0433\\u0443\\u0441\\u0442\",\n\t\t\"\\u0441\\u0435\\u043D\\u0442\\u044F\\u0431\\u0440\\u044C\",\n\t\t\"\\u043E\\u043A\\u0442\\u044F\\u0431\\u0440\\u044C\",\n\t\t\"\\u043D\\u043E\\u044F\\u0431\\u0440\\u044C\",\n\t\t\"\\u0434\\u0435\\u043A\\u0430\\u0431\\u0440\\u044C\",\n\t}\n\t// monthNamesRussianAbbr list the month abbreviations in the Russian.\n\tmonthNamesRussianAbbr = []string{\n\t\t\"\\u044F\\u043D\\u0432.\",\n\t\t\"\\u0444\\u0435\\u0432.\",\n\t\t\"\\u043C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0430\\u043F\\u0440.\",\n\t\t\"\\u043C\\u0430\\u0439\",\n\t\t\"\\u0438\\u044E\\u043D\\u044C\",\n\t\t\"\\u0438\\u044E\\u043B\\u044C\",\n\t\t\"\\u0430\\u0432\\u0433.\",\n\t\t\"\\u0441\\u0435\\u043D.\",\n\t\t\"\\u043E\\u043A\\u0442.\",\n\t\t\"\\u043D\\u043E\\u044F.\",\n\t\t\"\\u0434\\u0435\\u043A.\",\n\t}\n\t// monthNamesSakha list the month names in the Sakha.\n\tmonthNamesSakha = []string{\n\t\t\"\\u0422\\u043E\\u0445\\u0441\\u0443\\u043D\\u043D\\u044C\\u0443\",\n\t\t\"\\u041E\\u043B\\u0443\\u043D\\u043D\\u044C\\u0443\",\n\t\t\"\\u041A\\u0443\\u043B\\u0443\\u043D \\u0442\\u0443\\u0442\\u0430\\u0440\",\n\t\t\"\\u041C\\u0443\\u0443\\u0441 \\u0443\\u0441\\u0442\\u0430\\u0440\",\n\t\t\"\\u042B\\u0430\\u043C \\u044B\\u0439\\u0430\",\n\t\t\"\\u0411\\u044D\\u0441 \\u044B\\u0439\\u0430\",\n\t\t\"\\u041E\\u0442 \\u044B\\u0439\\u0430\",\n\t\t\"\\u0410\\u0442\\u044B\\u0440\\u0434\\u044C\\u0430\\u0445 \\u044B\\u0439\\u0430\",\n\t\t\"\\u0411\\u0430\\u043B\\u0430\\u0495\\u0430\\u043D \\u044B\\u0439\\u0430\",\n\t\t\"\\u0410\\u043B\\u0442\\u044B\\u043D\\u043D\\u044C\\u044B\",\n\t\t\"\\u0421\\u044D\\u0442\\u0438\\u043D\\u043D\\u044C\\u0438\",\n\t\t\"\\u0410\\u0445\\u0441\\u044B\\u043D\\u043D\\u044C\\u044B\",\n\t}\n\t// monthNamesSakhaAbbr list the month abbreviations in the Sakha.\n\tmonthNamesSakhaAbbr = []string{\n\t\t\"\\u0422\\u0445\\u0441\",\n\t\t\"\\u041E\\u043B\\u043D\",\n\t\t\"\\u041A\\u043B\\u043D\",\n\t\t\"\\u041C\\u0441\\u0443\",\n\t\t\"\\u042B\\u0430\\u043C\",\n\t\t\"\\u0411\\u044D\\u0441\",\n\t\t\"\\u041E\\u0442\\u044B\",\n\t\t\"\\u0410\\u0442\\u0440\",\n\t\t\"\\u0411\\u043B\\u0495\",\n\t\t\"\\u0410\\u043B\\u0442\",\n\t\t\"\\u0421\\u044D\\u0442\",\n\t\t\"\\u0410\\u0445\\u0441\",\n\t}\n\t// monthNamesSami list the month names in the Sami.\n\tmonthNamesSami = []string{\"uđđâivemáánu\", \"kuovâmáánu\", \"njuhčâmáánu\", \"cuáŋuimáánu\", \"vyesimáánu\", \"kesimáánu\", \"syeinimáánu\", \"porgemáánu\", \"čohčâmáánu\", \"roovvâdmáánu\", \"skammâmáánu\", \"juovlâmáánu\"}\n\t// monthNamesSamiAbbr list the month abbreviations in the Sami.\n\tmonthNamesSamiAbbr = []string{\"uđiv\", \"kuov\", \"njuh\", \"cuáŋ\", \"vyes\", \"kesi\", \"syei\", \"porg\", \"čohč\", \"roov\", \"skam\", \"juov\"}\n\t// monthNamesSamiLule list the month names in the Sami (Lule).\n\tmonthNamesSamiLule = []string{\"ådåjakmánno\", \"guovvamánno\", \"sjnjuktjamánno\", \"vuoratjismánno\", \"moarmesmánno\", \"biehtsemánno\", \"sjnjilltjamánno\", \"bårggemánno\", \"ragátmánno\", \"gålgådismánno\", \"basádismánno\", \"javllamánno\"}\n\t// monthNamesSamiLuleAbbr list the month abbreviations in the Sami (Lule).\n\tmonthNamesSamiLuleAbbr = []string{\"ådåj\", \"guov\", \"snju\", \"vuor\", \"moar\", \"bieh\", \"snji\", \"bårg\", \"ragá\", \"gålg\", \"basá\", \"javl\"}\n\t// monthNamesSamiNorthern list the month names in the Sami (Northern).\n\tmonthNamesSamiNorthern = []string{\"ođđajagemánnu\", \"guovvamánnu\", \"njukčamánnu\", \"cuoŋománnu\", \"miessemánnu\", \"geassemánnu\", \"suoidnemánnu\", \"borgemánnu\", \"čakčamánnu\", \"golggotmánnu\", \"skábmamánnu\", \"juovlamánnu\"}\n\t// monthNamesSamiNorthernAbbr list the month abbreviations in the Sami (Northern).\n\tmonthNamesSamiNorthernAbbr = []string{\"ođđj\", \"guov\", \"njuk\", \"cuoŋ\", \"mies\", \"geas\", \"suoi\", \"borg\", \"čakč\", \"golg\", \"skáb\", \"juov\"}\n\t// monthNamesSamiSkolt list the month names in the Sami (Skolt).\n\tmonthNamesSamiSkolt = []string{\"ođđee´jjmään\", \"tä´lvvmään\", \"pâ´zzlâšttam-mään\", \"njuhččmään\", \"vue´ssmään\", \"ǩie´ssmään\", \"suei´nnmään\", \"på´rǧǧmään\", \"čõhččmään\", \"kålggmään\", \"skamm-mään\", \"rosttovmään\"}\n\t// monthNamesSamiSouthern list the month names in the Sami (Southern).\n\tmonthNamesSamiSouthern = []string{\"tsïengele\", \"goevte\", \"njoktje\", \"voerhtje\", \"suehpede\", \"ruffie\", \"snjaltje\", \"mïetske\", \"skïerede\", \"golke\", \"rahka\", \"goeve\"}\n\t// monthNamesSamiSouthernAbbr list the month abbreviations in the Sami (Southern).\n\tmonthNamesSamiSouthernAbbr = []string{\"tsïen\", \"goevt\", \"njok\", \"voer\", \"sueh\", \"ruff\", \"snja\", \"mïet\", \"skïer\", \"golk\", \"rahk\", \"goev\"}\n\t// monthNamesSanskrit list the month names in the Sanskrit.\n\tmonthNamesSanskrit = []string{\n\t\t\"\\u091C\\u093E\\u0928\\u094D\\u092F\\u0941\\u0905\\u0930\\u0940\",\n\t\t\"\\u092B\\u0947\\u092C\\u094D\\u0930\\u0941\\u0905\\u0930\\u0940\",\n\t\t\"\\u092E\\u093E\\u0930\\u094D\\u091A\",\n\t\t\"\\u090F\\u092A\\u094D\\u0930\\u093F\\u0932\",\n\t\t\"\\u092E\\u0947\",\n\t\t\"\\u091C\\u0942\\u0928\",\n\t\t\"\\u091C\\u0941\\u0932\\u0948\",\n\t\t\"\\u0911\\u0917\\u0938\\u094D\\u091F\",\n\t\t\"\\u0938\\u092A\\u094D\\u091F\\u0947\\u0902\\u092C\\u0930\",\n\t\t\"\\u0911\\u0915\\u094D\\u091F\\u094B\\u092C\\u0930\",\n\t\t\"\\u0928\\u094B\\u0935\\u094D\\u0939\\u0947\\u0902\\u092C\\u0930\",\n\t\t\"\\u0921\\u093F\\u0938\\u0947\\u0902\\u092C\\u0930\",\n\t}\n\t// monthNamesScottishGaelic list the month names in the Scottish Gaelic.\n\tmonthNamesScottishGaelic = []string{\"Am Faoilleach\", \"An Gearran\", \"Am Màrt\", \"An Giblean\", \"An Cèitean\", \"An t-Ògmhios\", \"An t-Iuchar\", \"An Lùnastal\", \"An t-Sultain\", \"An Dàmhair\", \"An t-Samhain\", \"An Dùbhlachd\"}\n\t// monthNamesScottishGaelicAbbr list the month abbreviations in the ScottishGaelic.\n\tmonthNamesScottishGaelicAbbr = []string{\"Faoi\", \"Gear\", \"Màrt\", \"Gibl\", \"Cèit\", \"Ògmh\", \"Iuch\", \"Lùna\", \"Sult\", \"Dàmh\", \"Samh\", \"Dùbh\"}\n\t// monthNamesSerbian list the month names in the Serbian (Cyrillic).\n\tmonthNamesSerbian = []string{\n\t\t\"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440\",\n\t\t\"\\u0444\\u0435\\u0431\\u0440\\u0443\\u0430\\u0440\",\n\t\t\"\\u043C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0430\\u043F\\u0440\\u0438\\u043B\",\n\t\t\"\\u043C\\u0430\\u0458\",\n\t\t\"\\u0458\\u0443\\u043D\",\n\t\t\"\\u0458\\u0443\\u043B\",\n\t\t\"\\u0430\\u0432\\u0433\\u0443\\u0441\\u0442\",\n\t\t\"\\u0441\\u0435\\u043F\\u0442\\u0435\\u043C\\u0431\\u0430\\u0440\",\n\t\t\"\\u043E\\u043A\\u0442\\u043E\\u0431\\u0430\\u0440\",\n\t\t\"\\u043D\\u043E\\u0432\\u0435\\u043C\\u0431\\u0430\\u0440\",\n\t\t\"\\u0434\\u0435\\u0446\\u0435\\u043C\\u0431\\u0430\\u0440\",\n\t}\n\t// monthNamesSerbianAbbr lists the month name abbreviations in the Serbian\n\t// (Cyrillic).\n\tmonthNamesSerbianAbbr = []string{\n\t\t\"\\u0458\\u0430\\u043D.\",\n\t\t\"\\u0444\\u0435\\u0431.\",\n\t\t\"\\u043C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0430\\u043F\\u0440.\",\n\t\t\"\\u043C\\u0430\\u0458\",\n\t\t\"\\u0458\\u0443\\u043D\",\n\t\t\"\\u0458\\u0443\\u043B\",\n\t\t\"\\u0430\\u0432\\u0433.\",\n\t\t\"\\u0441\\u0435\\u043F\\u0442.\",\n\t\t\"\\u043E\\u043A\\u0442.\",\n\t\t\"\\u043D\\u043E\\u0432.\",\n\t\t\"\\u0434\\u0435\\u0446.\",\n\t}\n\t// monthNamesSerbianBA list the month names in the Serbian (Cyrillic) Bosnia\n\t// and Herzegovina.\n\tmonthNamesSerbianBA = []string{\n\t\t\"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440\",\n\t\t\"\\u0444\\u0435\\u0431\\u0440\\u0443\\u0430\\u0440\",\n\t\t\"\\u043C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0430\\u043F\\u0440\\u0438\\u043B\",\n\t\t\"\\u043C\\u0430\\u0458\",\n\t\t\"\\u0458\\u0443\\u043D\\u0438\",\n\t\t\"\\u0458\\u0443\\u043B\\u0438\",\n\t\t\"\\u0430\\u0432\\u0433\\u0443\\u0441\\u0442\",\n\t\t\"\\u0441\\u0435\\u043F\\u0442\\u0435\\u043C\\u0431\\u0430\\u0440\",\n\t\t\"\\u043E\\u043A\\u0442\\u043E\\u0431\\u0430\\u0440\",\n\t\t\"\\u043D\\u043E\\u0432\\u0435\\u043C\\u0431\\u0430\\u0440\",\n\t\t\"\\u0434\\u0435\\u0446\\u0435\\u043C\\u0431\\u0430\\u0440\",\n\t}\n\t// monthNamesSerbianBAAbbr lists the month name abbreviations in the Serbian\n\t// (Cyrillic) Bosnia and Herzegovina.\n\tmonthNamesSerbianBAAbbr = []string{\n\t\t\"\\u0458\\u0430\\u043D\",\n\t\t\"\\u0444\\u0435\\u0431\",\n\t\t\"\\u043C\\u0430\\u0440\",\n\t\t\"\\u0430\\u043F\\u0440\",\n\t\t\"\\u043C\\u0430\\u0458\",\n\t\t\"\\u0458\\u0443\\u043D\",\n\t\t\"\\u0458\\u0443\\u043B\",\n\t\t\"\\u0430\\u0432\\u0433\",\n\t\t\"\\u0441\\u0435\\u043F\",\n\t\t\"\\u043E\\u043A\\u0442\",\n\t\t\"\\u043D\\u043E\\u0432\",\n\t\t\"\\u0434\\u0435\\u0446\",\n\t}\n\t// monthNamesSerbianLatin list the month names in the Serbian (Latin).\n\tmonthNamesSerbianLatin = []string{\"januar\", \"februar\", \"mart\", \"april\", \"maj\", \"jun\", \"jul\", \"avgust\", \"septembar\", \"oktobar\", \"novembar\", \"decembar\"}\n\t// monthNamesSerbianLatinAbbr lists the month name abbreviations in the\n\t// Serbian(Latin) and Montenegro (Former).\n\tmonthNamesSerbianLatinAbbr = []string{\"jan.\", \"feb.\", \"mart\", \"apr.\", \"maj\", \"jun\", \"jul\", \"avg.\", \"sept.\", \"okt.\", \"nov.\", \"dec.\"}\n\t// monthNamesSesothoSaLeboa list the month names in the Sesotho sa Leboa.\n\tmonthNamesSesothoSaLeboa = []string{\"Janaware\", \"Feberware\", \"Matšhe\", \"Aprele\", \"Mei\", \"June\", \"Julae\", \"Agostose\", \"Setemere\", \"Oktoboro\", \"Nofemere\", \"Disemere\"}\n\t// monthNamesSesothoSaLeboaAbbr lists the month name abbreviations in the\n\t// Sesotho sa Leboa.\n\tmonthNamesSesothoSaLeboaAbbr = []string{\"Jan\", \"Feb\", \"Matš\", \"Apr\", \"Mei\", \"June\", \"Julae\", \"Agost\", \"Set\", \"Oky\", \"Nof\", \"Dis\"}\n\t// monthNamesSetswana list the month names in the Setswana.\n\tmonthNamesSetswana = []string{\"Ferikgong\", \"Tlhakole\", \"Mopitlwe\", \"Moranang\", \"Motsheganang\", \"Seetebosigo\", \"Phukwi\", \"Phatwe\", \"Lwetse\", \"Diphalane\", \"Ngwanatsele\", \"Sedimonthole\"}\n\t// monthNamesSetswanaAbbr lists the month name abbreviations in the Setswana.\n\tmonthNamesSetswanaAbbr = []string{\"Fer.\", \"Tlh.\", \"Mop.\", \"Mor.\", \"Motsh.\", \"Seet.\", \"Phk.\", \"Pht.\", \"Lwetse.\", \"Diph.\", \"Ngwn.\", \"Sed.\"}\n\t// monthNamesSindhi list the month names in the Sindhi.\n\tmonthNamesSindhi = []string{\n\t\t\"\\u062C\\u0646\\u0648\\u0631\\u064A\",\n\t\t\"\\u0641\\u0631\\u0648\\u0631\\u064A\",\n\t\t\"\\u0645\\u0627\\u0631\\u0686\",\n\t\t\"\\u0627\\u067E\\u0631\\u064A\\u0644\",\n\t\t\"\\u0645\\u0654\\u064A\",\n\t\t\"\\u062C\\u0648\\u0646\",\n\t\t\"\\u062C\\u0648\\u0644\\u0627\\u0621\\u0650\",\n\t\t\"\\u0622\\u06AF\\u0633\\u062A\",\n\t\t\"\\u0633\\u062A\\u0645\\u0628\\u0631\",\n\t\t\"\\u0622\\u06A9\\u062A\\u0648\\u0628\\u0631\",\n\t\t\"\\u0646\\u0648\\u0645\\u0628\\u0631\",\n\t\t\"\\u068A\\u0633\\u0645\\u0628\\u0631\",\n\t}\n\t// monthNamesSinhala list the month names in the Sinhala.\n\tmonthNamesSinhala = []string{\n\t\t\"\\u0DA2\\u0DB1\\u0DC0\\u0DCF\\u0DBB\\u0DD2\",\n\t\t\"\\u0DB4\\u0DD9\\u0DB6\\u0DBB\\u0DC0\\u0DCF\\u0DBB\\u0DD2\",\n\t\t\"\\u0DB8\\u0DCF\\u0DBB\\u0DCA\\u0DAD\\u0DD4\",\n\t\t\"\\u0D85\\u0DB4\\u0DCA\\u200D\\u0DBB\\u0DDA\\u0DBD\\u0DCA\",\n\t\t\"\\u0DB8\\u0DD0\\u0DBA\\u0DD2\",\n\t\t\"\\u0DA2\\u0DD6\\u0DB1\\u0DD2\",\n\t\t\"\\u0DA2\\u0DD6\\u0DBD\\u0DD2\",\n\t\t\"\\u0D85\\u0D9C\\u0DDD\\u0DC3\\u0DCA\\u0DAD\\u0DD4\",\n\t\t\"\\u0DC3\\u0DD0\\u0DB4\\u0DCA\\u0DAD\\u0DD0\\u0DB8\\u0DCA\\u0DB6\\u0DBB\\u0DCA\",\n\t\t\"\\u0D94\\u0D9A\\u0DCA\\u0DAD\\u0DDD\\u0DB6\\u0DBB\\u0DCA\",\n\t\t\"\\u0DB1\\u0DDC\\u0DC0\\u0DD0\\u0DB8\\u0DCA\\u0DB6\\u0DBB\\u0DCA\",\n\t\t\"\\u0DAF\\u0DD9\\u0DC3\\u0DD0\\u0DB8\\u0DCA\\u0DB6\\u0DBB\\u0DCA\",\n\t}\n\t// monthNamesSinhalaAbbr lists the month name abbreviations in Sinhala.\n\tmonthNamesSinhalaAbbr = []string{\n\t\t\"\\u0DA2\\u0DB1.\",\n\t\t\"\\u0DB4\\u0DD9\\u0DB6.\",\n\t\t\"\\u0DB8\\u0DCF\\u0DBB\\u0DCA\\u0DAD\\u0DD4.\",\n\t\t\"\\u0D85\\u0DB4\\u0DCA\\u200D\\u0DBB\\u0DDA\\u0DBD\\u0DCA.\",\n\t\t\"\\u0DB8\\u0DD0\\u0DBA\\u0DD2\",\n\t\t\"\\u0DA2\\u0DD6\\u0DB1\\u0DD2\",\n\t\t\"\\u0DA2\\u0DD6\\u0DBD\\u0DD2\",\n\t\t\"\\u0D85\\u0D9C\\u0DDD.\",\n\t\t\"\\u0DC3\\u0DD0\\u0DB4\\u0DCA.\",\n\t\t\"\\u0D94\\u0D9A\\u0DCA.\",\n\t\t\"\\u0DB1\\u0DDC\\u0DC0\\u0DD0.\",\n\t\t\"\\u0DAF\\u0DD9\\u0DC3\\u0DD0.\",\n\t}\n\t// monthNamesSlovak list the month names in the Slovak.\n\tmonthNamesSlovak = []string{\"január\", \"február\", \"marec\", \"apríl\", \"máj\", \"jún\", \"júl\", \"august\", \"september\", \"október\", \"november\", \"december\"}\n\t// monthNamesSlovenian list the month names in the Slovenian.\n\tmonthNamesSlovenian = []string{\"januar\", \"februar\", \"marec\", \"april\", \"maj\", \"junij\", \"julij\", \"avgust\", \"september\", \"oktober\", \"november\", \"december\"}\n\t// monthNamesSlovenianAbbr list the month abbreviations in the Slovenian.\n\tmonthNamesSlovenianAbbr = []string{\"jan.\", \"feb.\", \"mar.\", \"apr.\", \"maj\", \"jun.\", \"jul.\", \"avg.\", \"sep.\", \"okt.\", \"nov.\", \"dec.\"}\n\t// monthNamesSomali list the month names in the Somali.\n\tmonthNamesSomali = []string{\"Jannaayo\", \"Febraayo\", \"Maarso\", \"Abriil\", \"May\", \"Juun\", \"Luuliyo\", \"Ogost\", \"Sebtembar\", \"Oktoobar\", \"Nofembar\", \"Desembar\"}\n\t// monthNamesSomaliAbbr list the month abbreviations in the Somali.\n\tmonthNamesSomaliAbbr = []string{\"Jan\", \"Feb\", \"Mar\", \"Abr\", \"May\", \"Jun\", \"Lul\", \"Ogs\", \"Seb\", \"Okt\", \"Nof\", \"Dis\"}\n\t// monthNamesSotho list the month names in the Sotho.\n\tmonthNamesSotho = []string{\"Phesekgong\", \"Hlakola\", \"Hlakubele\", \"Mmese\", \"Motsheanong\", \"Phupjane\", \"Phupu\", \"Phata\", \"Leotshe\", \"Mphalane\", \"Pundungwane\", \"Tshitwe\"}\n\t// monthNamesSothoAbbr list the month abbreviations in the Sotho.\n\tmonthNamesSothoAbbr = []string{\"Phe\", \"Kol\", \"Ube\", \"Mme\", \"Mot\", \"Jan\", \"Upu\", \"Pha\", \"Leo\", \"Mph\", \"Pun\", \"Tsh\"}\n\t// monthNamesSpanish list the month names in the Spanish.\n\tmonthNamesSpanish = []string{\"enero\", \"febrero\", \"marzo\", \"abril\", \"mayo\", \"junio\", \"julio\", \"agosto\", \"septiembre\", \"octubre\", \"noviembre\", \"diciembre\"}\n\t// monthNamesSpanishAbbr list the month abbreviations in the Spanish.\n\tmonthNamesSpanishAbbr = []string{\"ene\", \"feb\", \"mar\", \"abr\", \"may\", \"jun\", \"jul\", \"ago\", \"sep\", \"oct\", \"nov\", \"dic\"}\n\t// monthNamesSpanishPE list the month names in the Spanish Peru.\n\tmonthNamesSpanishPE = []string{\"Enero\", \"Febrero\", \"Marzo\", \"Abril\", \"Mayo\", \"Junio\", \"Julio\", \"Agosto\", \"Setiembre\", \"Octubre\", \"Noviembre\", \"Diciembre\"}\n\t// monthNamesSpanishPEAbbr list the month abbreviations in the Spanish Peru.\n\tmonthNamesSpanishPEAbbr = []string{\"Ene.\", \"Feb.\", \"Mar.\", \"Abr.\", \"May.\", \"Jun.\", \"Jul.\", \"Ago.\", \"Set.\", \"Oct.\", \"Nov.\", \"Dic.\"}\n\t// monthNamesSwedish list the month names in the Swedish.\n\tmonthNamesSwedish = []string{\"januari\", \"februari\", \"mars\", \"april\", \"maj\", \"juni\", \"juli\", \"augusti\", \"september\", \"oktober\", \"november\", \"december\"}\n\t// monthNamesSwedishAbbr list the month abbreviations in the Swedish.\n\tmonthNamesSwedishAbbr = []string{\"jan\", \"feb\", \"mar\", \"apr\", \"maj\", \"jun\", \"jul\", \"aug\", \"sep\", \"okt\", \"nov\", \"dec\"}\n\t// monthNamesSwedishFIAbbr list the month abbreviations in the Swedish Finland.\n\tmonthNamesSwedishFIAbbr = []string{\"jan.\", \"feb.\", \"mars\", \"apr.\", \"maj\", \"juni\", \"juli\", \"aug.\", \"sep.\", \"okt.\", \"nov.\", \"dec.\"}\n\t// monthNamesSyriac list the month names in the Syriac.\n\tmonthNamesSyriac = []string{\n\t\t\"\\u071F\\u0722\\u0718\\u0722 \\u0710\\u071A\\u072A\\u071D\",\n\t\t\"\\u072B\\u0712\\u071B\",\n\t\t\"\\u0710\\u0715\\u072A\",\n\t\t\"\\u0722\\u071D\\u0723\\u0722\",\n\t\t\"\\u0710\\u071D\\u072A\",\n\t\t\"\\u071A\\u0719\\u071D\\u072A\\u0722\",\n\t\t\"\\u072C\\u0721\\u0718\\u0719\",\n\t\t\"\\u0710\\u0712\",\n\t\t\"\\u0710\\u071D\\u0720\\u0718\\u0720\",\n\t\t\"\\u072C\\u072B\\u072A\\u071D \\u0729\\u0715\\u071D\\u0721\",\n\t\t\"\\u072C\\u072B\\u072A\\u071D \\u0710\\u071A\\u072A\\u071D\",\n\t\t\"\\u071F\\u0722\\u0718\\u0722 \\u0729\\u0715\\u071D\\u0721\",\n\t}\n\t// monthNamesSyriacAbbr lists the month name abbreviations in the Syriac.\n\tmonthNamesSyriacAbbr = []string{\n\t\t\"\\u071F\\u0722 \\u070F\\u0712\",\n\t\t\"\\u072B\\u0712\\u071B\",\n\t\t\"\\u0710\\u0715\\u072A\",\n\t\t\"\\u0722\\u071D\\u0723\\u0722\",\n\t\t\"\\u0710\\u071D\\u072A\",\n\t\t\"\\u071A\\u0719\\u071D\\u072A\\u0722\",\n\t\t\"\\u072C\\u0721\\u0718\\u0719\",\n\t\t\"\\u0710\\u0712\",\n\t\t\"\\u0710\\u071D\\u0720\\u0718\\u0720\",\n\t\t\"\\u070F\\u072C\\u072B \\u070F\\u0710\",\n\t\t\"\\u070F\\u072C\\u072B \\u070F\\u0712\",\n\t\t\"\\u070F\\u071F\\u0722 \\u070F\\u0710\",\n\t}\n\t// monthNamesSyllabics list the month names in the Syllabics.\n\tmonthNamesSyllabics = []string{\n\t\t\"\\u152E\\u14D0\\u14C4\\u140A\\u1546\",\n\t\t\"\\u1556\\u155D\\u1557\\u140A\\u1546\",\n\t\t\"\\u14AB\\u1466\\u14EF\",\n\t\t\"\\u1404\\u1433\\u1546\",\n\t\t\"\\u14AA\\u1403\",\n\t\t\"\\u152B\\u14C2\",\n\t\t\"\\u152A\\u14DA\\u1403\",\n\t\t\"\\u140B\\u14A1\\u148C\\u14EF\",\n\t\t\"\\u14EF\\u144E\\u1431\\u1546\",\n\t\t\"\\u1405\\u1450\\u1431\\u1546\",\n\t\t\"\\u14C4\\u1555\\u1431\\u1546\",\n\t\t\"\\u144E\\u14EF\\u1431\\u1546\",\n\t}\n\t// monthNamesSyllabicsAbbr lists the month name abbreviations in the Syllabics.\n\tmonthNamesSyllabicsAbbr = []string{\n\t\t\"\\u152E\\u14D0\\u14C4\",\n\t\t\"\\u1556\\u155D\\u1557\",\n\t\t\"\\u14AB\\u1466\\u14EF\",\n\t\t\"\\u1404\\u1433\\u1546\",\n\t\t\"\\u14AA\\u1403\",\n\t\t\"\\u152B\\u14C2\",\n\t\t\"\\u152A\\u14DA\\u1403\",\n\t\t\"\\u140B\\u14A1\\u148C\",\n\t\t\"\\u14EF\\u144E\\u1431\",\n\t\t\"\\u1405\\u1450\\u1431\",\n\t\t\"\\u14C4\\u1555\\u1431\",\n\t\t\"\\u144E\\u14EF\\u1431\",\n\t}\n\t// monthNamesTajik list the month names in the Tajik.\n\tmonthNamesTajik = []string{\n\t\t\"\\u044F\\u043D\\u0432\\u0430\\u0440\",\n\t\t\"\\u0444\\u0435\\u0432\\u0440\\u0430\\u043B\",\n\t\t\"\\u043C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0430\\u043F\\u0440\\u0435\\u043B\",\n\t\t\"\\u043C\\u0430\\u0439\",\n\t\t\"\\u0438\\u044E\\u043D\",\n\t\t\"\\u0438\\u044E\\u043B\",\n\t\t\"\\u0430\\u0432\\u0433\\u0443\\u0441\\u0442\",\n\t\t\"\\u0441\\u0435\\u043D\\u0442\\u044F\\u0431\\u0440\",\n\t\t\"\\u043E\\u043A\\u0442\\u044F\\u0431\\u0440\",\n\t\t\"\\u043D\\u043E\\u044F\\u0431\\u0440\",\n\t\t\"\\u0434\\u0435\\u043A\\u0430\\u0431\\u0440\",\n\t}\n\t// monthNamesTajikAbbr lists the month name abbreviations in Tajik.\n\tmonthNamesTajikAbbr = []string{\n\t\t\"\\u044F\\u043D\\u0432\",\n\t\t\"\\u0444\\u0435\\u0432\",\n\t\t\"\\u043C\\u0430\\u0440\",\n\t\t\"\\u0430\\u043F\\u0440\",\n\t\t\"\\u043C\\u0430\\u0439\",\n\t\t\"\\u0438\\u044E\\u043D\",\n\t\t\"\\u0438\\u044E\\u043B\",\n\t\t\"\\u0430\\u0432\\u0433\",\n\t\t\"\\u0441\\u0435\\u043D\",\n\t\t\"\\u043E\\u043A\\u0442\",\n\t\t\"\\u043D\\u043E\\u044F\",\n\t\t\"\\u0434\\u0435\\u043A\",\n\t}\n\t// monthNamesTamazight list the month names in the Tamazight.\n\tmonthNamesTamazight = []string{\"Yennayer\", \"Furar\", \"Meghres\", \"Yebrir\", \"Magu\", \"Yunyu\", \"Yulyu\", \"Ghuct\", \"Cutenber\", \"Tuber\", \"Nunember\", \"Dujanbir\"}\n\t// monthNamesTamazightAbbr list the month abbreviations in the Tamazight.\n\tmonthNamesTamazightAbbr = []string{\"Yen\", \"Fur\", \"Megh\", \"Yeb\", \"May\", \"Yun\", \"Yul\", \"Ghu\", \"Cut\", \"Tub\", \"Nun\", \"Duj\"}\n\t// monthNamesTamil list the month names in the Tamil.\n\tmonthNamesTamil = []string{\n\t\t\"\\u0B9C\\u0BA9\\u0BB5\\u0BB0\\u0BBF\",\n\t\t\"\\u0BAA\\u0BBF\\u0BAA\\u0BCD\\u0BB0\\u0BB5\\u0BB0\\u0BBF\",\n\t\t\"\\u0BAE\\u0BBE\\u0BB0\\u0BCD\\u0B9A\\u0BCD\",\n\t\t\"\\u0B8F\\u0BAA\\u0BCD\\u0BB0\\u0BB2\\u0BCD\",\n\t\t\"\\u0BAE\\u0BC7\",\n\t\t\"\\u0B9C\\u0BC2\\u0BA9\\u0BCD\",\n\t\t\"\\u0B9C\\u0BC2\\u0BB2\\u0BC8\",\n\t\t\"\\u0B86\\u0B95\\u0BB8\\u0BCD\\u0B9F\\u0BCD\",\n\t\t\"\\u0B9A\\u0BC6\\u0BAA\\u0BCD\\u0B9F\\u0BAE\\u0BCD\\u0BAA\\u0BB0\\u0BCD\",\n\t\t\"\\u0B85\\u0B95\\u0BCD\\u0B9F\\u0BCB\\u0BAA\\u0BB0\\u0BCD\",\n\t\t\"\\u0BA8\\u0BB5\\u0BAE\\u0BCD\\u0BAA\\u0BB0\\u0BCD\",\n\t\t\"\\u0B9F\\u0BBF\\u0B9A\\u0BAE\\u0BCD\\u0BAA\\u0BB0\\u0BCD\",\n\t}\n\t// monthNamesTamilAbbr lists the month name abbreviations in Tamil.\n\tmonthNamesTamilAbbr = []string{\n\t\t\"\\u0B9C\\u0BA9.\",\n\t\t\"\\u0BAA\\u0BBF\\u0BAA\\u0BCD.\",\n\t\t\"\\u0BAE\\u0BBE\\u0BB0\\u0BCD.\",\n\t\t\"\\u0B8F\\u0BAA\\u0BCD.\",\n\t\t\"\\u0BAE\\u0BC7\",\n\t\t\"\\u0B9C\\u0BC2\\u0BA9\\u0BCD\",\n\t\t\"\\u0B9C\\u0BC2\\u0BB2\\u0BC8\",\n\t\t\"\\u0B86\\u0B95.\",\n\t\t\"\\u0B9A\\u0BC6\\u0BAA\\u0BCD.\",\n\t\t\"\\u0B85\\u0B95\\u0BCD.\",\n\t\t\"\\u0BA8\\u0BB5.\",\n\t\t\"\\u0B9F\\u0BBF\\u0B9A.\",\n\t}\n\t// monthNamesTatar list the month names in the Tatar.\n\tmonthNamesTatar = []string{\n\t\t\"\\u0433\\u044B\\u0439\\u043D\\u0432\\u0430\\u0440\",\n\t\t\"\\u0444\\u0435\\u0432\\u0440\\u0430\\u043B\\u044C\",\n\t\t\"\\u043C\\u0430\\u0440\\u0442\",\n\t\t\"\\u0430\\u043F\\u0440\\u0435\\u043B\\u044C\",\n\t\t\"\\u043C\\u0430\\u0439\",\n\t\t\"\\u0438\\u044E\\u043D\\u044C\",\n\t\t\"\\u0438\\u044E\\u043B\\u044C\",\n\t\t\"\\u0430\\u0432\\u0433\\u0443\\u0441\\u0442\",\n\t\t\"\\u0441\\u0435\\u043D\\u0442\\u044F\\u0431\\u0440\\u044C\",\n\t\t\"\\u043E\\u043A\\u0442\\u044F\\u0431\\u0440\\u044C\",\n\t\t\"\\u043D\\u043E\\u044F\\u0431\\u0440\\u044C\",\n\t\t\"\\u0434\\u0435\\u043A\\u0430\\u0431\\u0440\\u044C\",\n\t}\n\t// monthNamesTatarAbbr lists the month name abbreviations in the Tatar.\n\tmonthNamesTatarAbbr = []string{\n\t\t\"\\u0433\\u044B\\u0439\\u043D.\",\n\t\t\"\\u0444\\u0435\\u0432.\",\n\t\t\"\\u043C\\u0430\\u0440.\",\n\t\t\"\\u0430\\u043F\\u0440.\",\n\t\t\"\\u043C\\u0430\\u0439\",\n\t\t\"\\u0438\\u044E\\u043D\\u044C\",\n\t\t\"\\u0438\\u044E\\u043B\\u044C\",\n\t\t\"\\u0430\\u0432\\u0433.\",\n\t\t\"\\u0441\\u0435\\u043D.\",\n\t\t\"\\u043E\\u043A\\u0442.\",\n\t\t\"\\u043D\\u043E\\u044F\\u0431.\",\n\t\t\"\\u0434\\u0435\\u043A.\",\n\t}\n\t// monthNamesTelugu list the month names in the Telugu.\n\tmonthNamesTelugu = []string{\n\t\t\"\\u0C1C\\u0C28\\u0C35\\u0C30\\u0C3F\",\n\t\t\"\\u0C2B\\u0C3F\\u0C2C\\u0C4D\\u0C30\\u0C35\\u0C30\\u0C3F\",\n\t\t\"\\u0C2E\\u0C3E\\u0C30\\u0C4D\\u0C1A\\u0C3F\",\n\t\t\"\\u0C0F\\u0C2A\\u0C4D\\u0C30\\u0C3F\\u0C32\\u0C4D\",\n\t\t\"\\u0C2E\\u0C47\",\n\t\t\"\\u0C1C\\u0C42\\u0C28\\u0C4D\",\n\t\t\"\\u0C1C\\u0C41\\u0C32\\u0C48\",\n\t\t\"\\u0C06\\u0C17\\u0C38\\u0C4D\\u0C1F\\u0C41\",\n\t\t\"\\u0C38\\u0C46\\u0C2A\\u0C4D\\u0C1F\\u0C46\\u0C02\\u0C2C\\u0C30\\u0C4D\",\n\t\t\"\\u0C05\\u0C15\\u0C4D\\u0C1F\\u0C4B\\u0C2C\\u0C30\\u0C4D\",\n\t\t\"\\u0C28\\u0C35\\u0C02\\u0C2C\\u0C30\\u0C4D\",\n\t\t\"\\u0C21\\u0C3F\\u0C38\\u0C46\\u0C02\\u0C2C\\u0C30\\u0C4D\",\n\t}\n\t// monthNamesTeluguAbbr lists the month name abbreviations in the Telugu.\n\tmonthNamesTeluguAbbr = []string{\n\t\t\"\\u0C1C\\u0C28\",\n\t\t\"\\u0C2B\\u0C3F\\u0C2C\\u0C4D\\u0C30\",\n\t\t\"\\u0C2E\\u0C3E\\u0C30\\u0C4D\\u0C1A\\u0C3F\",\n\t\t\"\\u0C0F\\u0C2A\\u0C4D\\u0C30\\u0C3F\",\n\t\t\"\\u0C2E\\u0C47\",\n\t\t\"\\u0C1C\\u0C42\\u0C28\\u0C4D\",\n\t\t\"\\u0C1C\\u0C41\\u0C32\\u0C48\",\n\t\t\"\\u0C06\\u0C17\",\n\t\t\"\\u0C38\\u0C46\\u0C2A\\u0C4D\\u0C1F\\u0C46\\u0C02\",\n\t\t\"\\u0C05\\u0C15\\u0C4D\\u0C1F\\u0C4B\",\n\t\t\"\\u0C28\\u0C35\\u0C02\",\n\t\t\"\\u0C21\\u0C3F\\u0C38\\u0C46\\u0C02\",\n\t}\n\t// monthNamesThai list the month names in the Thai.\n\tmonthNamesThai = []string{\n\t\t\"\\u0E21\\u0E01\\u0E23\\u0E32\\u0E04\\u0E21\",\n\t\t\"\\u0E01\\u0E38\\u0E21\\u0E20\\u0E32\\u0E1E\\u0E31\\u0E19\\u0E18\\u0E4C\",\n\t\t\"\\u0E21\\u0E35\\u0E19\\u0E32\\u0E04\\u0E21\",\n\t\t\"\\u0E40\\u0E21\\u0E29\\u0E32\\u0E22\\u0E19\",\n\t\t\"\\u0E1E\\u0E24\\u0E29\\u0E20\\u0E32\\u0E04\\u0E21\",\n\t\t\"\\u0E21\\u0E34\\u0E16\\u0E38\\u0E19\\u0E32\\u0E22\\u0E19\",\n\t\t\"\\u0E01\\u0E23\\u0E01\\u0E0E\\u0E32\\u0E04\\u0E21\",\n\t\t\"\\u0E2A\\u0E34\\u0E07\\u0E2B\\u0E32\\u0E04\\u0E21\",\n\t\t\"\\u0E01\\u0E31\\u0E19\\u0E22\\u0E32\\u0E22\\u0E19\",\n\t\t\"\\u0E15\\u0E38\\u0E25\\u0E32\\u0E04\\u0E21\",\n\t\t\"\\u0E1E\\u0E24\\u0E28\\u0E08\\u0E34\\u0E01\\u0E32\\u0E22\\u0E19\",\n\t\t\"\\u0E18\\u0E31\\u0E19\\u0E27\\u0E32\\u0E04\\u0E21\",\n\t}\n\t// monthNamesTibetan list the month names in the Tibetan.\n\tmonthNamesTibetan = []string{\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F51\\u0F44\\u0F0B\\u0F54\\u0F7C\\u0F0B\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F42\\u0F49\\u0F72\\u0F66\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F42\\u0F66\\u0F74\\u0F58\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F5E\\u0F72\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F63\\u0F94\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F51\\u0FB2\\u0F74\\u0F42\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F51\\u0F74\\u0F53\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F62\\u0F92\\u0FB1\\u0F51\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F51\\u0F42\\u0F74\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F54\\u0F0D\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F42\\u0F45\\u0F72\\u0F42\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F42\\u0F49\\u0F72\\u0F66\\u0F0B\\u0F54\\u0F0B\",\n\t}\n\t// monthNamesTibetanAbbr lists the month name abbreviations in the Tibetan.\n\tmonthNamesTibetanAbbr = []string{\n\t\t\"\\u0F5F\\u0FB3\\u0F0B \\u0F21\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B \\u0F22\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B \\u0F23\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B \\u0F24\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B \\u0F25\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B \\u0F26\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B \\u0F27\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B \\u0F28\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B \\u0F29\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B \\u0F21\\u0F20\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B \\u0F21\\u0F21\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B \\u0F21\\u0F22\",\n\t}\n\t// monthNamesTigrinya list the month names in the Tigrinya.\n\tmonthNamesTigrinya = []string{\n\t\t\"\\u1325\\u122A\",\n\t\t\"\\u1208\\u12AB\\u1272\\u1275\",\n\t\t\"\\u1218\\u130B\\u1262\\u1275\",\n\t\t\"\\u121A\\u12EB\\u12DD\\u12EB\",\n\t\t\"\\u130D\\u1295\\u1266\\u1275\",\n\t\t\"\\u1230\\u1290\",\n\t\t\"\\u1213\\u121D\\u1208\",\n\t\t\"\\u1290\\u1213\\u1230\",\n\t\t\"\\u1218\\u1235\\u12A8\\u1228\\u121D\",\n\t\t\"\\u1325\\u1245\\u121D\\u1272\",\n\t\t\"\\u1215\\u12F3\\u122D\",\n\t\t\"\\u1273\\u1215\\u1233\\u1235\",\n\t}\n\t// monthNamesTigrinyaAbbr lists the month name abbreviations in the\n\t// Tigrinya.\n\tmonthNamesTigrinyaAbbr = []string{\n\t\t\"\\u1325\\u122A\",\n\t\t\"\\u1208\\u12AB\",\n\t\t\"\\u1218\\u130B\",\n\t\t\"\\u121A\\u12EB\",\n\t\t\"\\u130D\\u1295\",\n\t\t\"\\u1230\\u1290\",\n\t\t\"\\u1213\\u121D\",\n\t\t\"\\u1290\\u1213\",\n\t\t\"\\u1218\\u1235\",\n\t\t\"\\u1325\\u1245\",\n\t\t\"\\u1215\\u12F3\",\n\t\t\"\\u1273\\u1215\",\n\t}\n\t// monthNamesTsonga list the month names in the Tsonga.\n\tmonthNamesTsonga = []string{\"Sunguti\", \"Nyenyenyani\", \"Nyenyankulu\", \"Dzivamisoko\", \"Mudyaxihi\", \"Khotavuxika\", \"Mawuwani\", \"Mhawuri\", \"Ndzhati\", \"Nhlangula\", \"Hukuri\", \"N\\u2019wendzamhala\"}\n\t// monthNamesTsongaAbbr lists the month name abbreviations in Tsonga, this\n\t// prevents string concatenation.\n\tmonthNamesTsongaAbbr = []string{\"Sun\", \"Yan\", \"Kul\", \"Dzi\", \"Mud\", \"Kho\", \"Maw\", \"Mha\", \"Ndz\", \"Nhl\", \"Huk\", \"N\\u2019w\"}\n\t// monthNamesTradMongolian lists the month number for use with traditional\n\t// Mongolian.\n\tmonthNamesTradMongolian = []string{\"M01\", \"M02\", \"M03\", \"M04\", \"M05\", \"M06\", \"M07\", \"M08\", \"M09\", \"M10\", \"M11\", \"M12\"}\n\t// monthNamesTurkish list the month names in the Turkish.\n\tmonthNamesTurkish = []string{\"Ocak\", \"Şubat\", \"Mart\", \"Nisan\", \"May\\u0131s\", \"Haziran\", \"Temmuz\", \"Ağustos\", \"Eylül\", \"Ekim\", \"Kas\\u0131m\", \"Aral\\u0131k\"}\n\t// monthNamesTurkishAbbr lists the month name abbreviations in Turkish, this\n\t// prevents string concatenation.\n\tmonthNamesTurkishAbbr = []string{\"Oca\", \"Şub\", \"Mar\", \"Nis\", \"May\", \"Haz\", \"Tem\", \"Ağu\", \"Eyl\", \"Eki\", \"Kas\", \"Ara\"}\n\t// monthNamesTurkmen list the month names in the Turkmen.\n\tmonthNamesTurkmen = []string{\"Ýanwar\", \"Fewral\", \"Mart\", \"Aprel\", \"Maý\", \"lýun\", \"lýul\", \"Awgust\", \"Sentýabr\", \"Oktýabr\", \"Noýabr\", \"Dekabr\"}\n\t// monthNamesTurkmenAbbr lists the month name abbreviations in Turkmen, this\n\t// prevents string concatenation.\n\tmonthNamesTurkmenAbbr = []string{\"Ýan\", \"Few\", \"Mart\", \"Apr\", \"Maý\", \"lýun\", \"lýul\", \"Awg\", \"Sen\", \"Okt\", \"Noý\", \"Dek\"}\n\t// monthNamesUkrainian list the month names in the Ukrainian.\n\tmonthNamesUkrainian = []string{\n\t\t\"\\u0441\\u0456\\u0447\\u0435\\u043D\\u044C\",\n\t\t\"\\u043B\\u044E\\u0442\\u0438\\u0439\",\n\t\t\"\\u0431\\u0435\\u0440\\u0435\\u0437\\u0435\\u043D\\u044C\",\n\t\t\"\\u043A\\u0432\\u0456\\u0442\\u0435\\u043D\\u044C\",\n\t\t\"\\u0442\\u0440\\u0430\\u0432\\u0435\\u043D\\u044C\",\n\t\t\"\\u0447\\u0435\\u0440\\u0432\\u0435\\u043D\\u044C\",\n\t\t\"\\u043B\\u0438\\u043F\\u0435\\u043D\\u044C\",\n\t\t\"\\u0441\\u0435\\u0440\\u043F\\u0435\\u043D\\u044C\",\n\t\t\"\\u0432\\u0435\\u0440\\u0435\\u0441\\u0435\\u043D\\u044C\",\n\t\t\"\\u0436\\u043E\\u0432\\u0442\\u0435\\u043D\\u044C\",\n\t\t\"\\u043B\\u0438\\u0441\\u0442\\u043E\\u043F\\u0430\\u0434\",\n\t\t\"\\u0433\\u0440\\u0443\\u0434\\u0435\\u043D\\u044C\",\n\t}\n\t// monthNamesUkrainianAbbr lists the month name abbreviations in Ukrainian.\n\tmonthNamesUkrainianAbbr = []string{\n\t\t\"\\u0421\\u0456\\u0447\",\n\t\t\"\\u041B\\u044E\\u0442\",\n\t\t\"\\u0411\\u0435\\u0440\",\n\t\t\"\\u041A\\u0432\\u0456\",\n\t\t\"\\u0422\\u0440\\u0430\",\n\t\t\"\\u0427\\u0435\\u0440\",\n\t\t\"\\u041B\\u0438\\u043F\",\n\t\t\"\\u0421\\u0435\\u0440\",\n\t\t\"\\u0412\\u0435\\u0440\",\n\t\t\"\\u0416\\u043E\\u0432\",\n\t\t\"\\u041B\\u0438\\u0441\",\n\t\t\"\\u0413\\u0440\\u0443\",\n\t}\n\t// monthNamesUpperSorbian list the month names in the Upper Sorbian.\n\tmonthNamesUpperSorbian = []string{\"januar\", \"februar\", \"měrc\", \"apryl\", \"meja\", \"junij\", \"julij\", \"awgust\", \"september\", \"oktober\", \"nowember\", \"december\"}\n\t// monthNamesUpperSorbianAbbr lists the month name abbreviations in the\n\t// Upper Sorbian, this prevents string concatenation.\n\tmonthNamesUpperSorbianAbbr = []string{\"jan\", \"feb\", \"měr\", \"apr\", \"mej\", \"jun\", \"jul\", \"awg\", \"sep\", \"okt\", \"now\", \"dec\"}\n\t// monthNamesUyghur list the month names in the Uyghur.\n\tmonthNamesUyghur = []string{\n\t\t\"\\u064A\\u0627\\u0646\\u06CB\\u0627\\u0631\",\n\t\t\"\\u0641\\u06D0\\u06CB\\u0631\\u0627\\u0644\",\n\t\t\"\\u0645\\u0627\\u0631\\u062A\",\n\t\t\"\\u0626\\u0627\\u067E\\u0631\\u06D0\\u0644\",\n\t\t\"\\u0645\\u0627\\u064A\",\n\t\t\"\\u0626\\u0649\\u064A\\u06C7\\u0646\",\n\t\t\"\\u0626\\u0649\\u064A\\u06C7\\u0644\",\n\t\t\"\\u0626\\u0627\\u06CB\\u063A\\u06C7\\u0633\\u062A\",\n\t\t\"\\u0633\\u06D0\\u0646\\u062A\\u06D5\\u0628\\u0649\\u0631\",\n\t\t\"\\u0626\\u06C6\\u0643\\u062A\\u06D5\\u0628\\u0649\\u0631\",\n\t\t\"\\u0646\\u0648\\u064A\\u0627\\u0628\\u0649\\u0631\",\n\t\t\"\\u062F\\u06D0\\u0643\\u0627\\u0628\\u0649\\u0631\",\n\t}\n\t// monthNamesUzbek list the month names in the Uzbek.\n\tmonthNamesUzbek = []string{\"Yanvar\", \"Fevral\", \"Mart\", \"Aprel\", \"May\", \"Iyun\", \"Iyul\", \"Avgust\", \"Sentabr\", \"Oktabr\", \"Noyabr\", \"Dekabr\"}\n\t// monthNamesUzbekAbbr lists the month name abbreviations in the Uzbek, this\n\t// prevents string concatenation.\n\tmonthNamesUzbekAbbr = []string{\"Yan\", \"Fev\", \"Mar\", \"Apr\", \"May\", \"Iyn\", \"Iyl\", \"Avg\", \"Sen\", \"Okt\", \"Noy\", \"Dek\"}\n\t// monthNamesValencian list the month names in the Valencian.\n\tmonthNamesValencian = []string{\"gener\", \"febrer\", \"març\", \"abril\", \"maig\", \"juny\", \"juliol\", \"agost\", \"setembre\", \"octubre\", \"novembre\", \"desembre\"}\n\t// monthNamesValencianAbbr lists the month name abbreviations in the\n\t// Valencian, this prevents string concatenation.\n\tmonthNamesValencianAbbr = []string{\"gen.\", \"febr.\", \"març\", \"abr.\", \"maig\", \"juny\", \"jul.\", \"ag.\", \"set.\", \"oct.\", \"nov.\", \"des.\"}\n\t// monthNamesVenda list the month names in the Venda.\n\tmonthNamesVenda = []string{\"Phando\", \"Luhuhi\", \"Ṱhafamuhwe\", \"Lambamai\", \"Shundunthule\", \"Fulwi\", \"Fulwana\", \"Ṱhangule\", \"Khubvumedzi\", \"Tshimedzi\", \"Ḽara\", \"Nyendavhusiku\"}\n\t// monthNamesVendaAbbr lists the month name abbreviations in the Venda, this\n\t// prevents string concatenation.\n\tmonthNamesVendaAbbr = []string{\"Pha\", \"Luh\", \"Ṱhf\", \"Lam\", \"Shu\", \"Lwi\", \"Lwa\", \"Ṱha\", \"Khu\", \"Tsh\", \"Ḽar\", \"Nye\"}\n\t// monthNamesVietnamese list the month name used for Vietnamese\n\tmonthNamesVietnamese = []string{\"Tháng 1\", \"Tháng 2\", \"Tháng 3\", \"Tháng 4\", \"Tháng 5\", \"Tháng 6\", \"Tháng 7\", \"Tháng 8\", \"Tháng 9\", \"Tháng 10\", \"Tháng 11\", \"Tháng 12\"}\n\t// monthNamesVietnameseAbbr3 list the mid-form abbreviation for Vietnamese\n\t// months.\n\tmonthNamesVietnameseAbbr3 = []string{\"Thg 1\", \"Thg 2\", \"Thg 3\", \"Thg 4\", \"Thg 5\", \"Thg 6\", \"Thg 7\", \"Thg 8\", \"Thg 9\", \"Thg 10\", \"Thg 11\", \"Thg 12\"}\n\t// monthNamesVietnameseAbbr5 list the short-form abbreviation for Vietnamese\n\t// months.\n\tmonthNamesVietnameseAbbr5 = []string{\"T 1\", \"T 2\", \"T 3\", \"T 4\", \"T 5\", \"T 6\", \"T 7\", \"T 8\", \"T 9\", \"T 10\", \"T 11\", \"T 12\"}\n\t// monthNamesWelsh list the month names in the Welsh.\n\tmonthNamesWelsh = []string{\"Ionawr\", \"Chwefror\", \"Mawrth\", \"Ebrill\", \"Mai\", \"Mehefin\", \"Gorffennaf\", \"Awst\", \"Medi\", \"Hydref\", \"Tachwedd\", \"Rhagfyr\"}\n\t// monthNamesWelshAbbr lists the month name abbreviations in the Welsh, this\n\t// prevents string concatenation.\n\tmonthNamesWelshAbbr = []string{\"Ion\", \"Chwef\", \"Maw\", \"Ebr\", \"Mai\", \"Meh\", \"Gorff\", \"Awst\", \"Medi\", \"Hyd\", \"Tach\", \"Rhag\"}\n\t// monthNamesWolof list the month names in the Wolof.\n\tmonthNamesWolof = []string{\"Samwiye\", \"Fewriye\", \"Maars\", \"Awril\", \"Me\", \"Suwe\", \"Sullet\", \"Ut\", \"Septàmbar\", \"Oktoobar\", \"Noowàmbar\", \"Desàmbar\"}\n\t// monthNamesWolofAbbr list the month name abbreviations in the Wolof, this\n\t// prevents string concatenation.\n\tmonthNamesWolofAbbr = []string{\"Sam.\", \"Few.\", \"Maa\", \"Awr.\", \"Me\", \"Suw\", \"Sul.\", \"Ut\", \"Sept.\", \"Okt.\", \"Now.\", \"Des.\"}\n\t// monthNamesXhosa list the month names in the Xhosa.\n\tmonthNamesXhosa = []string{\"uJanuwari\", \"uFebuwari\", \"uMatshi\", \"uAprili\", \"uMeyi\", \"uJuni\", \"uJulayi\", \"uAgasti\", \"uSeptemba\", \"uOktobha\", \"uNovemba\", \"uDisemba\"}\n\t// monthNamesXhosaAbbr list the month abbreviations in the Xhosa, this\n\t// prevents string concatenation.\n\tmonthNamesXhosaAbbr = []string{\"uJan.\", \"uFeb.\", \"uMat.\", \"uEpr.\", \"uMey.\", \"uJun.\", \"uJul.\", \"uAg.\", \"uSep.\", \"uOkt.\", \"uNov.\", \"uDis.\"}\n\t// monthNamesYi list the month names in the Yi.\n\tmonthNamesYi = []string{\"\\uA2CD\", \"\\uA44D\", \"\\uA315\", \"\\uA1D6\", \"\\uA26C\", \"\\uA0D8\", \"\\uA3C3\", \"\\uA246\", \"\\uA22C\", \"\\uA2B0\", \"\\uA2B0\\uA2AA\", \"\\uA2B0\\uA44B\"}\n\t// monthNamesYiSuffix lists the month names in Yi with the \"\\uA1AA\" suffix.\n\tmonthNamesYiSuffix = []string{\"\\uA2CD\\uA1AA\", \"\\uA44D\\uA1AA\", \"\\uA315\\uA1AA\", \"\\uA1D6\\uA1AA\", \"\\uA26C\\uA1AA\", \"\\uA0D8\\uA1AA\", \"\\uA3C3\\uA1AA\", \"\\uA246\\uA1AA\", \"\\uA22C\\uA1AA\", \"\\uA2B0\\uA1AA\", \"\\uA2B0\\uA2AA\\uA1AA\", \"\\uA2B0\\uA44B\\uA1AA\"}\n\t// monthNamesYiddish list the month names in the Yiddish.\n\tmonthNamesYiddish = []string{\n\t\t\"\\u05D9\\u05D0\\u05B7\\u05E0\\u05D5\\u05D0\\u05B7\\u05E8\",\n\t\t\"\\u05E4\\u05BF\\u05E2\\u05D1\\u05E8\\u05D5\\u05D0\\u05B7\\u05E8\",\n\t\t\"\\u05DE\\u05E2\\u05E8\\u05E5\",\n\t\t\"\\u05D0\\u05B7\\u05E4\\u05BC\\u05E8\\u05D9\\u05DC\",\n\t\t\"\\u05DE\\u05D9\\u05D9\",\n\t\t\"\\u05D9\\u05D5\\u05E0\\u05D9\",\n\t\t\"\\u05D9\\u05D5\\u05DC\\u05D9\",\n\t\t\"\\u05D0\\u05D5\\u05D9\\u05D2\\u05D5\\u05E1\\u05D8\",\n\t\t\"\\u05E1\\u05E2\\u05E4\\u05BC\\u05D8\\u05E2\\u05DE\\u05D1\\u05E2\\u05E8\",\n\t\t\"\\u05D0\\u05E7\\u05D8\\u05D0\\u05D1\\u05E2\\u05E8\",\n\t\t\"\\u05E0\\u05D0\\u05D5\\u05D5\\u05E2\\u05DE\\u05D1\\u05E2\\u05E8\",\n\t\t\"\\u05D3\\u05E2\\u05E6\\u05E2\\u05DE\\u05D1\\u05E2\\u05E8\",\n\t}\n\t// monthNamesYiddishAbbr lists the month name abbreviations in Yiddish.\n\tmonthNamesYiddishAbbr = []string{\n\t\t\"\\u05D9\\u05D0\\u05B7\\u05E0\",\n\t\t\"\\u05E4\\u05BF\\u05E2\\u05D1\",\n\t\t\"\\u05DE\\u05E2\\u05E8\\u05E5\",\n\t\t\"\\u05D0\\u05B7\\u05E4\\u05BC\\u05E8\",\n\t\t\"\\u05DE\\u05D9\\u05D9\",\n\t\t\"\\u05D9\\u05D5\\u05E0\\u05D9\",\n\t\t\"\\u05D9\\u05D5\\u05DC\\u05D9\",\n\t\t\"\\u05D0\\u05D5\\u05D9\\u05D2\",\n\t\t\"\\u05E1\\u05E2\\u05E4\\u05BC\",\n\t\t\"\\u05D0\\u05E7\\u05D8\",\n\t\t\"\\u05E0\\u05D0\\u05D5\\u05D5\",\n\t\t\"\\u05D3\\u05E2\\u05E6\",\n\t}\n\t// monthNamesYoruba list the month names in the Yoruba.\n\tmonthNamesYoruba = []string{\n\t\t\"\\u1E62\\u1EB9\\u0301r\\u1EB9\\u0301\",\n\t\t\"Èrèlè\",\n\t\t\"\\u1EB8r\\u1EB9\\u0300nà\",\n\t\t\"Ìgbé\",\n\t\t\"\\u1EB8\\u0300bibi\",\n\t\t\"Òkúdu\",\n\t\t\"Age\\u0323mo\\u0323\",\n\t\t\"Ògún\",\n\t\t\"Owewe\",\n\t\t\"\\u1ECC\\u0300wàrà\",\n\t\t\"Bélú\",\n\t\t\"\\u1ECC\\u0300p\\u1EB9\\u0300\",\n\t}\n\t// monthNamesYorubaAbbr lists the month name abbreviations in the Yoruba.\n\tmonthNamesYorubaAbbr = []string{\n\t\t\"\\u1E62\\u1EB9\\u0301\",\n\t\t\"Èr\",\n\t\t\"\\u1EB8r\",\n\t\t\"Ìg\",\n\t\t\"\\u1EB8\\u0300b\",\n\t\t\"Òk\",\n\t\t\"Ag\",\n\t\t\"Òg\",\n\t\t\"Ow\",\n\t\t\"\\u1ECC\\u0300w\",\n\t\t\"Bé\",\n\t\t\"\\u1ECC\\u0300p\",\n\t}\n\t// monthNamesZulu list the month names in the Zulu.\n\tmonthNamesZulu = []string{\"Januwari\", \"Febhuwari\", \"Mashi\", \"Ephreli\", \"Meyi\", \"Juni\", \"Julayi\", \"Agasti\", \"Septemba\", \"Okthoba\", \"Novemba\", \"Disemba\"}\n\t// monthNamesZuluAbbr list the month name abbreviations in the Zulu.\n\tmonthNamesZuluAbbr = []string{\"Jan\", \"Feb\", \"Mas\", \"Eph\", \"Mey\", \"Jun\", \"Jul\", \"Agas\", \"Sep\", \"Okt\", \"Nov\", \"Dis\"}\n\t// weekdayNamesAfrikaans list the weekday name in the Afrikaans.\n\tweekdayNamesAfrikaans = []string{\"Sondag\", \"Maandag\", \"Dinsdag\", \"Woensdag\", \"Donderdag\", \"Vrydag\", \"Saterdag\"}\n\t// weekdayNamesAfrikaansAbbr list the weekday name abbreviations in the\n\t// Afrikaans.\n\tweekdayNamesAfrikaansAbbr = []string{\"So.\", \"Ma.\", \"Di.\", \"Wo.\", \"Do.\", \"Vr.\", \"Sa.\"}\n\t// weekdayNamesAlbanian list the weekday name in the Albanian.\n\tweekdayNamesAlbanian = []string{\"e diel\", \"e hënë\", \"e martë\", \"e mërkurë\", \"e enjte\", \"e premte\", \"e shtunë\"}\n\t// weekdayNamesAlbanianAbbr list the weekday name abbreviations in the\n\t// Albanian.\n\tweekdayNamesAlbanianAbbr = []string{\"die\", \"hën\", \"mar\", \"mër\", \"enj\", \"pre\", \"sht\"}\n\t// weekdayNamesAlsatian list the weekday name in the Alsatian.\n\tweekdayNamesAlsatian = []string{\"Sunntig\", \"Määntig\", \"Ziischtig\", \"Mittwuch\", \"Dunschtig\", \"Friitig\", \"Samschtig\"}\n\t// weekdayNamesAlsatianAbbr list the weekday name abbreviations in the\n\t// Alsatian.\n\tweekdayNamesAlsatianAbbr = []string{\"Su.\", \"Mä.\", \"Zi.\", \"Mi.\", \"Du.\", \"Fr.\", \"Sa.\"}\n\t// weekdayNamesAlsatianFrance list the weekday name in the Alsatian France.\n\tweekdayNamesAlsatianFrance = []string{\"Sundi\", \"Manti\", \"Zischti\", \"Mettwuch\", \"Dunnerschti\", \"Friti\", \"Sàmschti\"}\n\t// weekdayNamesAlsatianFranceAbbr list the weekday name abbreviations in the\n\t// Alsatian France.\n\tweekdayNamesAlsatianFranceAbbr = []string{\"Su.\", \"Ma.\", \"Zi.\", \"Me.\", \"Du.\", \"Fr.\", \"Sà.\"}\n\t// weekdayNamesAmharic list the weekday name in the Amharic.\n\tweekdayNamesAmharic = []string{\n\t\t\"\\u12A5\\u1211\\u12F5\",\n\t\t\"\\u1230\\u129E\",\n\t\t\"\\u121B\\u12AD\\u1230\\u129E\",\n\t\t\"\\u1228\\u1261\\u12D5\",\n\t\t\"\\u1210\\u1219\\u1235\",\n\t\t\"\\u12D3\\u122D\\u1265\",\n\t\t\"\\u1245\\u12F3\\u121C\",\n\t}\n\t// weekdayNamesAmharicAbbr list the weekday name abbreviations in the\n\t// Amharic.\n\tweekdayNamesAmharicAbbr = []string{\n\t\t\"\\u12A5\\u1211\\u12F5\",\n\t\t\"\\u1230\\u129E\",\n\t\t\"\\u121B\\u12AD\\u1230\",\n\t\t\"\\u1228\\u1261\\u12D5\",\n\t\t\"\\u1210\\u1219\\u1235\",\n\t\t\"\\u12D3\\u122D\\u1265\",\n\t\t\"\\u1245\\u12F3\\u121C\",\n\t}\n\t// weekdayNamesArabic list the weekday name in the Arabic.\n\tweekdayNamesArabic = []string{\n\t\t\"\\u0627\\u0644\\u0623\\u062D\\u062F\",\n\t\t\"\\u0627\\u0644\\u0625\\u062B\\u0646\\u064A\\u0646\",\n\t\t\"\\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\",\n\t\t\"\\u0627\\u0644\\u0623\\u0631\\u0628\\u0639\\u0627\\u0621\",\n\t\t\"\\u0627\\u0644\\u062E\\u0645\\u064A\\u0633\",\n\t\t\"\\u0627\\u0644\\u062C\\u0645\\u0639\\u0629\",\n\t\t\"\\u0627\\u0644\\u0633\\u0628\\u062A\",\n\t}\n\t// weekdayNamesArabicAbbr list the weekday name abbreviations in the Arabic.\n\tweekdayNamesArabicAbbr = []string{\n\t\t\"\\u0627\\u0644\\u0623\\u062D\\u062F\",\n\t\t\"\\u0627\\u0644\\u0625\\u062B\\u0646\\u064A\\u0646\",\n\t\t\"\\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\",\n\t\t\"\\u0627\\u0644\\u0623\\u0631\\u0628\\u0639\\u0627\\u0621\",\n\t\t\"\\u0627\\u0644\\u062E\\u0645\\u064A\\u0633\",\n\t\t\"\\u0627\\u0644\\u062C\\u0645\\u0639\\u0629\",\n\t\t\"\\u0627\\u0644\\u0633\\u0628\\u062A\",\n\t}\n\t// weekdayNamesArmenian list the weekday name in the Armenian.\n\tweekdayNamesArmenian = []string{\n\t\t\"\\u053F\\u056B\\u0580\\u0561\\u056F\\u056B\",\n\t\t\"\\u0535\\u0580\\u056F\\u0578\\u0582\\u0577\\u0561\\u0562\\u0569\\u056B\",\n\t\t\"\\u0535\\u0580\\u0565\\u0584\\u0577\\u0561\\u0562\\u0569\\u056B\",\n\t\t\"\\u0549\\u0578\\u0580\\u0565\\u0584\\u0577\\u0561\\u0562\\u0569\\u056B\",\n\t\t\"\\u0540\\u056B\\u0576\\u0563\\u0577\\u0561\\u0562\\u0569\\u056B\",\n\t\t\"\\u0548\\u0582\\u0580\\u0562\\u0561\\u0569\",\n\t\t\"\\u0547\\u0561\\u0562\\u0561\\u0569\",\n\t}\n\t// weekdayNamesArmenianAbbr list the weekday name abbreviations in the\n\t// Armenian.\n\tweekdayNamesArmenianAbbr = []string{\n\t\t\"\\u053F\\u056B\\u0580\",\n\t\t\"\\u0535\\u0580\\u056F\",\n\t\t\"\\u0535\\u0580\\u0584\",\n\t\t\"\\u0549\\u0580\\u0584\",\n\t\t\"\\u0540\\u0576\\u0563\",\n\t\t\"\\u0548\\u0582\\u0580\",\n\t\t\"\\u0547\\u0562\\u0569\",\n\t}\n\t// weekdayNamesAssamese list the weekday name in the Assamese.\n\tweekdayNamesAssamese = []string{\n\t\t\"\\u09F0\\u09AC\\u09BF\\u09AC\\u09BE\\u09F0\",\n\t\t\"\\u09B8\\u09CB\\u09AE\\u09AC\\u09BE\\u09F0\",\n\t\t\"\\u09AE\\u0999\\u09CD\\u0997\\u09B2\\u09AC\\u09BE\\u09F0\",\n\t\t\"\\u09AC\\u09C1\\u09A7\\u09AC\\u09BE\\u09F0\",\n\t\t\"\\u09AC\\u09C3\\u09B9\\u09B8\\u09CD\\u09AA\\u09A4\\u09BF\\u09AC\\u09BE\\u09F0\",\n\t\t\"\\u09B6\\u09C1\\u0995\\u09CD\\u09B0\\u09AC\\u09BE\\u09F0\",\n\t\t\"\\u09B6\\u09A8\\u09BF\\u09AC\\u09BE\\u09F0\",\n\t}\n\t// weekdayNamesAssameseAbbr list the weekday name abbreviations in the\n\t// Assamese.\n\tweekdayNamesAssameseAbbr = []string{\n\t\t\"\\u09F0\\u09AC\\u09BF.\",\n\t\t\"\\u09B8\\u09CB\\u09AE.\",\n\t\t\"\\u09AE\\u0999\\u09CD\\u0997\\u09B2.\",\n\t\t\"\\u09AC\\u09C1\\u09A7.\",\n\t\t\"\\u09AC\\u09C3\\u09B9.\",\n\t\t\"\\u09B6\\u09C1\\u0995\\u09CD\\u09B0.\",\n\t\t\"\\u09B6\\u09A8\\u09BF.\",\n\t}\n\t// weekdayNamesAzerbaijaniCyrillic list the weekday name in the Azerbaijani\n\t// (Cyrillic).\n\tweekdayNamesAzerbaijaniCyrillic = []string{\n\t\t\"\\u0431\\u0430\\u0437\\u0430\\u0440\",\n\t\t\"\\u0431\\u0430\\u0437\\u0430\\u0440 \\u0435\\u0440\\u0442\\u04D9\\u0441\\u0438\",\n\t\t\"\\u0447\\u04D9\\u0440\\u0448\\u04D9\\u043D\\u0431\\u04D9 \\u0430\\u0445\\u0448\\u0430\\u043C\\u044B\",\n\t\t\"\\u0447\\u04D9\\u0440\\u0448\\u04D9\\u043D\\u0431\\u04D9\",\n\t\t\"\\u04B9\\u04AF\\u043C\\u04D9 \\u0430\\u0445\\u0448\\u0430\\u043C\\u044B\",\n\t\t\"\\u04B9\\u04AF\\u043C\\u04D9\",\n\t\t\"\\u0448\\u04D9\\u043D\\u0431\\u04D9\",\n\t}\n\t// weekdayNamesAzerbaijaniCyrillicAbbr list the weekday name abbreviations\n\t// in the Azerbaijani (Cyrillic).\n\tweekdayNamesAzerbaijaniCyrillicAbbr = []string{\"\\u0411\", \"\\u0411\\u0435\", \"\\u0427\\u0430\", \"\\u0427\", \"\\u04B8\\u0430\", \"\\u04B8\", \"\\u0428\"}\n\t// weekdayNamesAzerbaijani list the weekday name in the Azerbaijani.\n\tweekdayNamesAzerbaijani = []string{\n\t\t\"bazar\",\n\t\t\"bazarçert\\u0259si\",\n\t\t\"ç\\u0259r\\u015F\\u0259nb\\u0259 ax\\u015Fam\\u0131\",\n\t\t\"ç\\u0259r\\u015F\\u0259nb\\u0259\",\n\t\t\"cüm\\u0259 ax\\u015Fam\\u0131\",\n\t\t\"cüm\\u0259\",\n\t\t\"\\u015F\\u0259nb\\u0259\",\n\t}\n\t// weekdayNamesAzerbaijaniAbbr list the weekday name abbreviations in the\n\t// Azerbaijani.\n\tweekdayNamesAzerbaijaniAbbr = []string{\"B.\", \"B.E.\", \"Ç.A.\", \"Ç.\", \"C.A.\", \"C.\", \"\\u015E.\"}\n\t// weekdayNamesBangla list the weekday name in the Bangla.\n\tweekdayNamesBangla = []string{\n\t\t\"\\u09B0\\u09AC\\u09BF\\u09AC\\u09BE\\u09B0\",\n\t\t\"\\u09B8\\u09CB\\u09AE\\u09AC\\u09BE\\u09B0\",\n\t\t\"\\u09AE\\u0999\\u09CD\\u0997\\u09B2\\u09AC\\u09BE\\u09B0\",\n\t\t\"\\u09AC\\u09C1\\u09A7\\u09AC\\u09BE\\u09B0\",\n\t\t\"\\u09AC\\u09C3\\u09B9\\u09B8\\u09CD\\u09AA\\u09A4\\u09BF\\u09AC\\u09BE\\u09B0\",\n\t\t\"\\u09B6\\u09C1\\u0995\\u09CD\\u09B0\\u09AC\\u09BE\\u09B0\",\n\t\t\"\\u09B6\\u09A8\\u09BF\\u09AC\\u09BE\\u09B0\",\n\t}\n\t// weekdayNamesBanglaAbbr list the weekday name abbreviations in the Bangla.\n\tweekdayNamesBanglaAbbr = []string{\n\t\t\"\\u09B0\\u09AC\\u09BF.\",\n\t\t\"\\u09B8\\u09CB\\u09AE.\",\n\t\t\"\\u09AE\\u0999\\u09CD\\u0997\\u09B2.\",\n\t\t\"\\u09AC\\u09C1\\u09A7.\",\n\t\t\"\\u09AC\\u09C3\\u09B9\\u09B8\\u09CD\\u09AA\\u09A4\\u09BF.\",\n\t\t\"\\u09B6\\u09C1\\u0995\\u09CD\\u09B0.\",\n\t\t\"\\u09B6\\u09A8\\u09BF.\",\n\t}\n\t// weekdayNamesBashkir list the weekday name in the Bashkir.\n\tweekdayNamesBashkir = []string{\n\t\t\"\\u0419\\u04D9\\u043A\\u0448\\u04D9\\u043C\\u0431\\u0435\",\n\t\t\"\\u0414\\u04AF\\u0448\\u04D9\\u043C\\u0431\\u0435\",\n\t\t\"\\u0428\\u0438\\u0448\\u04D9\\u043C\\u0431\\u0435\",\n\t\t\"\\u0428\\u0430\\u0440\\u0448\\u0430\\u043C\\u0431\\u044B\",\n\t\t\"\\u041A\\u0435\\u0441\\u0430\\u0499\\u043D\\u0430\",\n\t\t\"\\u0419\\u043E\\u043C\\u0430\",\n\t\t\"\\u0428\\u04D9\\u043C\\u0431\\u0435\",\n\t}\n\t// weekdayNamesBashkirAbbr list the weekday name abbreviations in the\n\t// Bashkir.\n\tweekdayNamesBashkirAbbr = []string{\n\t\t\"\\u0419\\u0448\",\n\t\t\"\\u0414\\u0448\",\n\t\t\"\\u0428\\u0448\",\n\t\t\"\\u0428\\u0440\",\n\t\t\"\\u041A\\u0441\",\n\t\t\"\\u0419\\u043C\",\n\t\t\"\\u0428\\u0431\",\n\t}\n\t// weekdayNamesBasque list the weekday name in the Basque.\n\tweekdayNamesBasque = []string{\"igandea\", \"astelehena\", \"asteartea\", \"asteazkena\", \"osteguna\", \"ostirala\", \"larunbata\"}\n\t// weekdayNamesBasqueAbbr list the weekday name abbreviations in the Basque.\n\tweekdayNamesBasqueAbbr = []string{\"ig.\", \"al.\", \"ar.\", \"az.\", \"og.\", \"or.\", \"lr.\"}\n\t// weekdayNamesBelarusian list the weekday name in the Belarusian.\n\tweekdayNamesBelarusian = []string{\n\t\t\"\\u043D\\u044F\\u0434\\u0437\\u0435\\u043B\\u044F\",\n\t\t\"\\u043F\\u0430\\u043D\\u044F\\u0434\\u0437\\u0435\\u043B\\u0430\\u043A\",\n\t\t\"\\u0430\\u045E\\u0442\\u043E\\u0440\\u0430\\u043A\",\n\t\t\"\\u0441\\u0435\\u0440\\u0430\\u0434\\u0430\",\n\t\t\"\\u0447\\u0430\\u0446\\u0432\\u0435\\u0440\",\n\t\t\"\\u043F\\u044F\\u0442\\u043D\\u0456\\u0446\\u0430\",\n\t\t\"\\u0441\\u0443\\u0431\\u043E\\u0442\\u0430\",\n\t}\n\t// weekdayNamesBelarusianAbbr list the weekday name abbreviations in the\n\t// Belarusian.\n\tweekdayNamesBelarusianAbbr = []string{\n\t\t\"\\u043D\\u0434\",\n\t\t\"\\u043F\\u043D\",\n\t\t\"\\u0430\\u045E\\u0442\",\n\t\t\"\\u0441\\u0440\",\n\t\t\"\\u0447\\u0446\",\n\t\t\"\\u043F\\u0442\",\n\t\t\"\\u0441\\u0431\",\n\t}\n\t// weekdayNamesBosnianCyrillic list the weekday name in the Bosnian\n\t// (Cyrillic).\n\tweekdayNamesBosnianCyrillic = []string{\n\t\t\"\\u043D\\u0435\\u0434\\u0458\\u0435\\u0459\\u0430\",\n\t\t\"\\u043F\\u043E\\u043D\\u0435\\u0434\\u0458\\u0435\\u0459\\u0430\\u043A\",\n\t\t\"\\u0443\\u0442\\u043E\\u0440\\u0430\\u043A\",\n\t\t\"\\u0441\\u0440\\u0438\\u0458\\u0435\\u0434\\u0430\",\n\t\t\"\\u0447\\u0435\\u0442\\u0432\\u0440\\u0442\\u0430\\u043A\",\n\t\t\"\\u043F\\u0435\\u0442\\u0430\\u043A\",\n\t\t\"\\u0441\\u0443\\u0431\\u043E\\u0442\\u0430\",\n\t}\n\t// weekdayNamesBosnianCyrillicAbbr list the weekday name abbreviations in\n\t// the Bosnian (Cyrillic).\n\tweekdayNamesBosnianCyrillicAbbr = []string{\n\t\t\"\\u043D\\u0435\\u0434\",\n\t\t\"\\u043F\\u043E\\u043D\",\n\t\t\"\\u0443\\u0442\\u043E\",\n\t\t\"\\u0441\\u0440\\u0435\",\n\t\t\"\\u0447\\u0435\\u0442\",\n\t\t\"\\u043F\\u0435\\u0442\",\n\t\t\"\\u0441\\u0443\\u0431\",\n\t}\n\t// weekdayNamesBosnian list the weekday name in the Bosnian.\n\tweekdayNamesBosnian = []string{\"nedjelja\", \"ponedjeljak\", \"utorak\", \"srijeda\", \"četvrtak\", \"petak\", \"subota\"}\n\t// weekdayNamesBosnianAbbr list the weekday name abbreviations in the\n\t// Bosnian.\n\tweekdayNamesBosnianAbbr = []string{\"ned\", \"pon\", \"uto\", \"sri\", \"čet\", \"pet\", \"sub\"}\n\t// weekdayNamesBreton list the weekday name in the Breton.\n\tweekdayNamesBreton = []string{\"Sul\", \"Lun\", \"Meurzh\", \"Merc'her\", \"Yaou\", \"Gwener\", \"Sadorn\"}\n\t// weekdayNamesBretonAbbr list the weekday name abbreviations in the Breton.\n\tweekdayNamesBretonAbbr = []string{\"Sul\", \"Lun\", \"Meu.\", \"Mer.\", \"Yaou\", \"Gwe.\", \"Sad.\"}\n\t// weekdayNamesBulgarian list the weekday name in the Bulgarian.\n\tweekdayNamesBulgarian = []string{\n\t\t\"\\u043D\\u0435\\u0434\\u0435\\u043B\\u044F\",\n\t\t\"\\u043F\\u043E\\u043D\\u0435\\u0434\\u0435\\u043B\\u043D\\u0438\\u043A\",\n\t\t\"\\u0432\\u0442\\u043E\\u0440\\u043D\\u0438\\u043A\",\n\t\t\"\\u0441\\u0440\\u044F\\u0434\\u0430\",\n\t\t\"\\u0447\\u0435\\u0442\\u0432\\u044A\\u0440\\u0442\\u044A\\u043A\",\n\t\t\"\\u043F\\u0435\\u0442\\u044A\\u043A\",\n\t\t\"\\u0441\\u044A\\u0431\\u043E\\u0442\\u0430\",\n\t}\n\t// weekdayNamesBulgarianAbbr list the weekday name abbreviations in the\n\t// Bulgarian.\n\tweekdayNamesBulgarianAbbr = []string{\n\t\t\"\\u043D\\u0435\\u0434\",\n\t\t\"\\u043F\\u043E\\u043D\",\n\t\t\"\\u0432\\u0442\",\n\t\t\"\\u0441\\u0440\",\n\t\t\"\\u0447\\u0435\\u0442\\u0432\",\n\t\t\"\\u043F\\u0435\\u0442\",\n\t\t\"\\u0441\\u044A\\u0431\",\n\t}\n\t// weekdayNamesBurmese list the weekday name in the Burmese.\n\tweekdayNamesBurmese = []string{\n\t\t\"\\u1010\\u1014\\u1004\\u103A\\u1039\\u1002\\u1014\\u103D\\u1031\",\n\t\t\"\\u1010\\u1014\\u1004\\u103A\\u1039\\u101C\\u102C\",\n\t\t\"\\u1021\\u1004\\u103A\\u1039\\u1002\\u102B\",\n\t\t\"\\u1017\\u102F\\u1012\\u1039\\u1013\\u101F\\u1030\\u1038\",\n\t\t\"\\u1000\\u103C\\u102C\\u101E\\u1015\\u1010\\u1031\\u1038\",\n\t\t\"\\u101E\\u1031\\u102C\\u1000\\u103C\\u102C\",\n\t\t\"\\u1005\\u1014\\u1031\",\n\t}\n\t// weekdayNamesCentralKurdish list the weekday name in the Central Kurdish.\n\tweekdayNamesCentralKurdish = []string{\n\t\t\"\\u06CC\\u06D5\\u06A9\\u0634\\u06D5\\u0645\\u0645\\u06D5\",\n\t\t\"\\u062F\\u0648\\u0648\\u0634\\u06D5\\u0645\\u0645\\u06D5\",\n\t\t\"\\u0633\\u06CE\\u0634\\u06D5\\u0645\\u0645\\u06D5\",\n\t\t\"\\u0686\\u0648\\u0627\\u0631\\u0634\\u06D5\\u0645\\u0645\\u06D5\",\n\t\t\"\\u067E\\u06CE\\u0646\\u062C\\u0634\\u06D5\\u0645\\u0645\\u06D5\",\n\t\t\"\\u06BE\\u06D5\\u06CC\\u0646\\u06CC\",\n\t\t\"\\u0634\\u06D5\\u0645\\u0645\\u06D5\",\n\t}\n\t// weekdayNamesCherokee list the weekday name in the Cherokee.\n\tweekdayNamesCherokee = []string{\n\t\t\"\\u13A4\\u13BE\\u13D9\\u13D3\\u13C6\\u13CD\\u13AC\",\n\t\t\"\\u13A4\\u13BE\\u13D9\\u13D3\\u13C9\\u13C5\\u13AF\",\n\t\t\"\\u13D4\\u13B5\\u13C1\\u13A2\\u13A6\",\n\t\t\"\\u13E6\\u13A2\\u13C1\\u13A2\\u13A6\",\n\t\t\"\\u13C5\\u13A9\\u13C1\\u13A2\\u13A6\",\n\t\t\"\\u13E7\\u13BE\\u13A9\\u13B6\\u13CD\\u13D7\",\n\t\t\"\\u13A4\\u13BE\\u13D9\\u13D3\\u13C8\\u13D5\\u13BE\",\n\t}\n\t// weekdayNamesCherokeeAbbr list the weekday name abbreviations in the\n\t// Cherokee.\n\tweekdayNamesCherokeeAbbr = []string{\n\t\t\"\\u13C6\\u13CD\\u13AC\",\n\t\t\"\\u13C9\\u13C5\\u13AF\",\n\t\t\"\\u13D4\\u13B5\\u13C1\",\n\t\t\"\\u13E6\\u13A2\\u13C1\",\n\t\t\"\\u13C5\\u13A9\\u13C1\",\n\t\t\"\\u13E7\\u13BE\\u13A9\",\n\t\t\"\\u13C8\\u13D5\\u13BE\",\n\t}\n\t// weekdayNamesCorsican list the weekday name in the Corsican.\n\tweekdayNamesCorsican = []string{\"dumenica\", \"luni\", \"marti\", \"mercuri\", \"ghjovi\", \"venneri\", \"sabbatu\"}\n\t// weekdayNamesCorsicanAbbr list the weekday name abbreviations in the Corsican.\n\tweekdayNamesCorsicanAbbr = []string{\"dum.\", \"lun.\", \"mar.\", \"mer.\", \"ghj.\", \"ven.\", \"sab.\"}\n\t// weekdayNamesCroatian list the weekday name in the Croatian.\n\tweekdayNamesCroatian = []string{\"nedjelja\", \"ponedjeljak\", \"utorak\", \"srijeda\", \"četvrtak\", \"petak\", \"subota\"}\n\t// weekdayNamesCroatianAbbr list the weekday name abbreviations in the Croatian.\n\tweekdayNamesCroatianAbbr = []string{\"ned\", \"pon\", \"uto\", \"sri\", \"čet\", \"pet\", \"sub\"}\n\t// weekdayNamesCzech list the weekday name in the Czech.\n\tweekdayNamesCzech = []string{\"neděle\", \"pondělí\", \"úterý\", \"středa\", \"čtvrtek\", \"pátek\", \"sobota\"}\n\t// weekdayNamesCzechAbbr list the weekday name abbreviations in the Czech.\n\tweekdayNamesCzechAbbr = []string{\"ne\", \"po\", \"út\", \"st\", \"čt\", \"pá\", \"so\"}\n\t// weekdayNamesDanish list the weekday name in the Danish.\n\tweekdayNamesDanish = []string{\"søndag\", \"mandag\", \"tirsdag\", \"onsdag\", \"torsdag\", \"fredag\", \"lørdag\"}\n\t// weekdayNamesDanishAbbr list the weekday name abbreviations in the Danish.\n\tweekdayNamesDanishAbbr = []string{\"sø\", \"ma\", \"ti\", \"on\", \"to\", \"fr\", \"lø\"}\n\t// weekdayNamesDari list the weekday name in the Dari.\n\tweekdayNamesDari = []string{\n\t\t\"\\u06CC\\u06A9\\u0634\\u0646\\u0628\\u0647\",\n\t\t\"\\u062F\\u0648\\u0634\\u0646\\u0628\\u0647\",\n\t\t\"\\u0633\\u0647\\u200C \\u0634\\u0646\\u0628\\u0647\",\n\t\t\"\\u0686\\u0647\\u0627\\u0631 \\u0634\\u0646\\u0628\\u0647\",\n\t\t\"\\u067E\\u0646\\u062C\\u0634\\u0646\\u0628\\u0647\",\n\t\t\"\\u062C\\u0645\\u0639\\u0647\",\n\t\t\"\\u0634\\u0646\\u0628\\u0647\",\n\t}\n\t// weekdayNamesDivehi list the weekday name in the Divehi.\n\tweekdayNamesDivehi = []string{\n\t\t\"\\u0787\\u07A7\\u078B\\u07A9\\u0787\\u07B0\\u078C\\u07A6\",\n\t\t\"\\u0780\\u07AF\\u0789\\u07A6\",\n\t\t\"\\u0787\\u07A6\\u0782\\u07B0\\u078E\\u07A7\\u0783\\u07A6\",\n\t\t\"\\u0784\\u07AA\\u078B\\u07A6\",\n\t\t\"\\u0784\\u07AA\\u0783\\u07A7\\u0790\\u07B0\\u078A\\u07A6\\u078C\\u07A8\",\n\t\t\"\\u0780\\u07AA\\u0786\\u07AA\\u0783\\u07AA\",\n\t\t\"\\u0780\\u07AE\\u0782\\u07A8\\u0780\\u07A8\\u0783\\u07AA\",\n\t}\n\t// weekdayNamesDutch list the weekday name in the Dutch.\n\tweekdayNamesDutch = []string{\"zondag\", \"maandag\", \"dinsdag\", \"woensdag\", \"donderdag\", \"vrijdag\", \"zaterdag\"}\n\t// weekdayNamesDutchAbbr list the weekday name abbreviations in the Dutch.\n\tweekdayNamesDutchAbbr = []string{\"zo\", \"ma\", \"di\", \"wo\", \"do\", \"vr\", \"za\"}\n\t// weekdayNamesDzongkha list the weekday name in the Dzongkha.\n\tweekdayNamesDzongkha = []string{\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\",\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F58\\u0F72\\u0F42\\u0F0B\\u0F51\\u0F58\\u0F62\\u0F0B\",\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F63\\u0FB7\\u0F42\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F55\\u0F74\\u0F62\\u0F0B\\u0F56\\u0F74\\u0F0B\",\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F54\\u0F0B\\u0F66\\u0F44\\u0F66\\u0F0B\",\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F66\\u0FA4\\u0F7A\\u0F53\\u0F0B\\u0F54\\u0F0B\",\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F49\\u0F72\\u0F0B\\u0F58\\u0F0B\",\n\t}\n\t// weekdayNamesDzongkhaAbbr list the weekday name abbreviations in the\n\t// Dzongkha.\n\tweekdayNamesDzongkhaAbbr = []string{\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\",\n\t\t\"\\u0F58\\u0F72\\u0F62\\u0F0B\",\n\t\t\"\\u0F63\\u0FB7\\u0F42\\u0F0B\",\n\t\t\"\\u0F55\\u0F74\\u0F62\\u0F0B\",\n\t\t\"\\u0F66\\u0F44\\u0F66\\u0F0B\",\n\t\t\"\\u0F66\\u0FA4\\u0F7A\\u0F53\\u0F0B\",\n\t\t\"\\u0F49\\u0F72\\u0F0B\",\n\t}\n\t// weekdayNamesChinese list the weekday name in the Chinese.\n\tweekdayNamesChinese = []string{\"星期日\", \"星期一\", \"星期二\", \"星期三\", \"星期四\", \"星期五\", \"星期六\"}\n\t// weekdayNamesChineseAbbr list the weekday name abbreviations in the\n\t// Chinese.\n\tweekdayNamesChineseAbbr = []string{\"周日\", \"周一\", \"周二\", \"周三\", \"周四\", \"周五\", \"周六\"}\n\t// weekdayNamesChineseAbbr list the weekday name abbreviations in the\n\t// Chinese.\n\tweekdayNamesChineseAbbr2 = []string{\"週日\", \"週一\", \"週二\", \"週三\", \"週四\", \"週五\", \"週六\"}\n\t// weekdayNamesEnglish list the weekday name in the English.\n\tweekdayNamesEnglish = []string{\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"}\n\t// weekdayNamesEnglishAbbr list the weekday name abbreviations in the\n\t// English.\n\tweekdayNamesEnglishAbbr = []string{\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"}\n\t// weekdayNamesEstonian list the weekday name in the Estonian.\n\tweekdayNamesEstonian = []string{\"pühapäev\", \"esmaspäev\", \"teisipäev\", \"kolmapäev\", \"neljapäev\", \"reede\", \"laupäev\"}\n\t// weekdayNamesEstonianAbbr list the weekday name abbreviations in the\n\t// Estonian.\n\tweekdayNamesEstonianAbbr = []string{\"P\", \"E\", \"T\", \"K\", \"N\", \"R\", \"L\"}\n\t// weekdayNamesFaroese list the weekday name in the Faroese.\n\tweekdayNamesFaroese = []string{\"sunnudagur\", \"mánadagur\", \"týsdagur\", \"mikudagur\", \"hósdagur\", \"fríggjadagur\", \"leygardagur\"}\n\t// weekdayNamesFaroeseAbbr list the weekday name abbreviations in the\n\t// Faroese.\n\tweekdayNamesFaroeseAbbr = []string{\"sun.\", \"mán.\", \"týs.\", \"mik.\", \"hós.\", \"frí.\", \"ley.\"}\n\t// weekdayNamesFilipino list the weekday name in the Filipino.\n\tweekdayNamesFilipino = []string{\"Linggo\", \"Lunes\", \"Martes\", \"Miyerkules\", \"Huwebes\", \"Biyernes\", \"Sabado\"}\n\t// weekdayNamesFilipinoAbbr list the weekday name abbreviations in the\n\t// Filipino.\n\tweekdayNamesFilipinoAbbr = []string{\"Lin\", \"Lun\", \"Mar\", \"Miy\", \"Huw\", \"Biy\", \"Sab\"}\n\t// weekdayNamesFinnish list the weekday name in the Finnish\n\tweekdayNamesFinnish = []string{\"sunnuntai\", \"maanantai\", \"tiistai\", \"keskiviikko\", \"torstai\", \"perjantai\", \"lauantai\"}\n\t// weekdayNamesFinnishAbbr list the weekday name abbreviations in the\n\t// Finnish.\n\tweekdayNamesFinnishAbbr = []string{\"su\", \"ma\", \"ti\", \"ke\", \"to\", \"pe\", \"la\"}\n\t// weekdayNamesFrench list the weekday name in the French.\n\tweekdayNamesFrench = []string{\"dimanche\", \"lundi\", \"mardi\", \"mercredi\", \"jeudi\", \"vendredi\", \"samedi\"}\n\t// weekdayNamesFrenchAbbr list the weekday name abbreviations in the French.\n\tweekdayNamesFrenchAbbr = []string{\"dim.\", \"lun.\", \"mar.\", \"mer.\", \"jeu.\", \"ven.\", \"sam.\"}\n\t// weekdayNamesFrisian list the weekday name in the Frisian.\n\tweekdayNamesFrisian = []string{\"snein\", \"moandei\", \"tiisdei\", \"woansdei\", \"tongersdei\", \"freed\", \"sneon\"}\n\t// weekdayNamesFrisianAbbr list the weekday name abbreviations in the\n\t// Frisian.\n\tweekdayNamesFrisianAbbr = []string{\"sni\", \"moa\", \"tii\", \"woa\", \"ton\", \"fre\", \"sno\"}\n\t// weekdayNamesFulah list the weekday name in the Fulah.\n\tweekdayNamesFulah = []string{\"dewo\", \"aaɓnde\", \"mawbaare\", \"njeslaare\", \"naasaande\", \"mawnde\", \"hoore-biir\"}\n\t// weekdayNamesFulahAbbr list the weekday name abbreviations in the Fulah\n\tweekdayNamesFulahAbbr = []string{\"dew\", \"aaɓ\", \"maw\", \"nje\", \"naa\", \"mwd\", \"hbi\"}\n\t// weekdayNamesNigeria list the weekday name in the Nigeria\n\tweekdayNamesNigeria = []string{\"alete\", \"altine\", \"talaata\", \"alarba\", \"alkamiisa\", \"aljumaa\", \"asete\"}\n\t// weekdayNamesNigeriaAbbr list the weekday name abbreviations in the\n\t// Nigeria.\n\tweekdayNamesNigeriaAbbr = []string{\"alet\", \"alt.\", \"tal.\", \"alar.\", \"alk.\", \"alj.\", \"aset\"}\n\t// weekdayNamesGalician list the weekday name in the Galician.\n\tweekdayNamesGalician = []string{\"domingo\", \"luns\", \"martes\", \"mércores\", \"xoves\", \"venres\", \"sábado\"}\n\t// weekdayNamesGalicianAbbr list the weekday name abbreviations in the\n\t// Galician.\n\tweekdayNamesGalicianAbbr = []string{\"dom.\", \"luns\", \"mar.\", \"mér.\", \"xov.\", \"ven.\", \"sáb.\"}\n\t// weekdayNamesGeorgian list the weekday name in the Georgian.\n\tweekdayNamesGeorgian = []string{\n\t\t\"\\u10D9\\u10D5\\u10D8\\u10E0\\u10D0\",\n\t\t\"\\u10DD\\u10E0\\u10E8\\u10D0\\u10D1\\u10D0\\u10D7\\u10D8\",\n\t\t\"\\u10E1\\u10D0\\u10DB\\u10E8\\u10D0\\u10D1\\u10D0\\u10D7\\u10D8\",\n\t\t\"\\u10DD\\u10D7\\u10EE\\u10E8\\u10D0\\u10D1\\u10D0\\u10D7\\u10D8\",\n\t\t\"\\u10EE\\u10E3\\u10D7\\u10E8\\u10D0\\u10D1\\u10D0\\u10D7\\u10D8\",\n\t\t\"\\u10DE\\u10D0\\u10E0\\u10D0\\u10E1\\u10D9\\u10D4\\u10D5\\u10D8\",\n\t\t\"\\u10E8\\u10D0\\u10D1\\u10D0\\u10D7\\u10D8\",\n\t}\n\t// weekdayNamesGeorgianAbbr list the weekday name abbreviations in the\n\t// Georgian.\n\tweekdayNamesGeorgianAbbr = []string{\n\t\t\"\\u10D9\\u10D5.\",\n\t\t\"\\u10DD\\u10E0\\u10E8.\",\n\t\t\"\\u10E1\\u10D0\\u10DB\\u10E8.\",\n\t\t\"\\u10DD\\u10D7\\u10EE\\u10E8.\",\n\t\t\"\\u10EE\\u10E3\\u10D7\\u10E8.\",\n\t\t\"\\u10DE\\u10D0\\u10E0.\",\n\t\t\"\\u10E8\\u10D0\\u10D1.\",\n\t}\n\t// weekdayNamesGerman list the weekday name in the German.\n\tweekdayNamesGerman = []string{\"Sonntag\", \"Montag\", \"Dienstag\", \"Mittwoch\", \"Donnerstag\", \"Freitag\", \"Samstag\"}\n\t// weekdayNamesGermanAbbr list the weekday name abbreviations in the German.\n\tweekdayNamesGermanAbbr = []string{\"So\", \"Mo\", \"Di\", \"Mi\", \"Do\", \"Fr\", \"Sa\"}\n\t// weekdayNamesGreek list the weekday name in the Greek.\n\tweekdayNamesGreek = []string{\n\t\t\"\\u039A\\u03C5\\u03C1\\u03B9\\u03B1\\u03BA\\u03AE\",\n\t\t\"\\u0394\\u03B5\\u03C5\\u03C4\\u03AD\\u03C1\\u03B1\",\n\t\t\"\\u03A4\\u03C1\\u03AF\\u03C4\\u03B7\",\n\t\t\"\\u03A4\\u03B5\\u03C4\\u03AC\\u03C1\\u03C4\\u03B7\",\n\t\t\"\\u03A0\\u03AD\\u03BC\\u03C0\\u03C4\\u03B7\",\n\t\t\"\\u03A0\\u03B1\\u03C1\\u03B1\\u03C3\\u03BA\\u03B5\\u03C5\\u03AE\",\n\t\t\"\\u03A3\\u03AC\\u03B2\\u03B2\\u03B1\\u03C4\\u03BF\",\n\t}\n\t// weekdayNamesGreekAbbr list the weekday name abbreviations in the Greek.\n\tweekdayNamesGreekAbbr = []string{\n\t\t\"\\u039A\\u03C5\\u03C1\",\n\t\t\"\\u0394\\u03B5\\u03C5\",\n\t\t\"\\u03A4\\u03C1\\u03B9\",\n\t\t\"\\u03A4\\u03B5\\u03C4\",\n\t\t\"\\u03A0\\u03B5\\u03BC\",\n\t\t\"\\u03A0\\u03B1\\u03C1\",\n\t\t\"\\u03A3\\u03B1\\u03B2\",\n\t}\n\t// weekdayNamesGreenlandic list the weekday name in the Greenlandic.\n\tweekdayNamesGreenlandic = []string{\"sapaat\", \"ataasinngorneq\", \"marlunngorneq\", \"pingasunngorneq\", \"sisamanngorneq\", \"tallimanngorneq\", \"arfininngorneq\"}\n\t// weekdayNamesGreenlandicAbbr list the weekday name abbreviations in the\n\t// Greenlandic.\n\tweekdayNamesGreenlandicAbbr = []string{\"sap.\", \"at.\", \"marl.\", \"ping.\", \"sis.\", \"tall.\", \"arf.\"}\n\t// weekdayNamesGuarani list the weekday name in the Guarani.\n\tweekdayNamesGuarani = []string{\"arate\\u0129\", \"arakõi\", \"araapy\", \"ararundy\", \"arapo\", \"arapote\\u0129\", \"arapokõi\"}\n\t// weekdayNamesGuaraniAbbr list the weekday name abbreviations in the\n\t// Guarani.\n\tweekdayNamesGuaraniAbbr = []string{\"te\\u0129\", \"kõi\", \"apy\", \"ndy\", \"po\", \"ote\\u0129\", \"okõi\"}\n\t// weekdayNamesGujarati list the weekday name in the Gujarati.\n\tweekdayNamesGujarati = []string{\n\t\t\"\\u0AB0\\u0AB5\\u0ABF\\u0AB5\\u0ABE\\u0AB0\",\n\t\t\"\\u0AB8\\u0ACB\\u0AAE\\u0AB5\\u0ABE\\u0AB0\",\n\t\t\"\\u0AAE\\u0A82\\u0A97\\u0AB3\\u0AB5\\u0ABE\\u0AB0\",\n\t\t\"\\u0AAC\\u0AC1\\u0AA7\\u0AB5\\u0ABE\\u0AB0\",\n\t\t\"\\u0A97\\u0AC1\\u0AB0\\u0AC1\\u0AB5\\u0ABE\\u0AB0\",\n\t\t\"\\u0AB6\\u0AC1\\u0A95\\u0ACD\\u0AB0\\u0AB5\\u0ABE\\u0AB0\",\n\t\t\"\\u0AB6\\u0AA8\\u0ABF\\u0AB5\\u0ABE\\u0AB0\",\n\t}\n\t// weekdayNamesGujaratiAbbr list the weekday name abbreviations in the\n\t// Gujarati.\n\tweekdayNamesGujaratiAbbr = []string{\n\t\t\"\\u0AB0\\u0AB5\\u0ABF\",\n\t\t\"\\u0AB8\\u0ACB\\u0AAE\",\n\t\t\"\\u0AAE\\u0A82\\u0A97\\u0AB3\",\n\t\t\"\\u0AAC\\u0AC1\\u0AA7\",\n\t\t\"\\u0A97\\u0AC1\\u0AB0\\u0AC1\",\n\t\t\"\\u0AB6\\u0AC1\\u0A95\\u0ACD\\u0AB0\",\n\t\t\"\\u0AB6\\u0AA8\\u0ABF\",\n\t}\n\t// weekdayNamesHausa list the weekday name in the Hausa.\n\tweekdayNamesHausa = []string{\"Lahadi\", \"Litinin\", \"Talata\", \"Laraba\", \"Alhamis\", \"Jumma\\u02bca\", \"Asabar\"}\n\t// weekdayNamesHausaAbbr list the weekday name abbreviations in the Hausa.\n\tweekdayNamesHausaAbbr = []string{\"Lah\", \"Lit\", \"Tal\", \"Lar\", \"Alh\", \"Jum\", \"Asa\"}\n\t// weekdayNamesHawaiian list the weekday name in the Hawaiian.\n\tweekdayNamesHawaiian = []string{\"Lāpule\", \"Po\\u02bbakahi\", \"Po\\u02bbalua\", \"Po\\u02bbakolu\", \"Po\\u02bbahā\", \"Po\\u02bbalima\", \"Po\\u02bbaono\"}\n\t// weekdayNamesHawaiianAbbr list the weekday name abbreviations in the\n\t// Hawaiian.\n\tweekdayNamesHawaiianAbbr = []string{\"LP\", \"P1\", \"P2\", \"P3\", \"P4\", \"P5\", \"P6\"}\n\t// weekdayNamesHebrew list the weekday name in the Hebrew.\n\tweekdayNamesHebrew = []string{\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05E8\\u05D0\\u05E9\\u05D5\\u05DF\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05E9\\u05E0\\u05D9\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05E9\\u05DC\\u05D9\\u05E9\\u05D9\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05E8\\u05D1\\u05D9\\u05E2\\u05D9\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D7\\u05DE\\u05D9\\u05E9\\u05D9\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05E9\\u05D9\\u05E9\\u05D9\",\n\t\t\"\\u05E9\\u05D1\\u05EA\",\n\t}\n\t// weekdayNamesHebrewAbbr list the weekday name abbreviations in the Hebrew.\n\tweekdayNamesHebrewAbbr = []string{\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D0\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D1\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D2\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D3\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D4\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D5\",\n\t\t\"\\u05E9\\u05D1\\u05EA\",\n\t}\n\t// weekdayNamesHindi list the weekday name in the Hindi.\n\tweekdayNamesHindi = []string{\n\t\t\"\\u0930\\u0935\\u093F\\u0935\\u093E\\u0930\",\n\t\t\"\\u0938\\u094B\\u092E\\u0935\\u093E\\u0930\",\n\t\t\"\\u092E\\u0902\\u0917\\u0932\\u0935\\u093E\\u0930\",\n\t\t\"\\u092C\\u0941\\u0927\\u0935\\u093E\\u0930\",\n\t\t\"\\u0917\\u0941\\u0930\\u0941\\u0935\\u093E\\u0930\",\n\t\t\"\\u0936\\u0941\\u0915\\u094D\\u0930\\u0935\\u093E\\u0930\",\n\t\t\"\\u0936\\u0928\\u093F\\u0935\\u093E\\u0930\",\n\t}\n\t// weekdayNamesHindiAbbr list the weekday name abbreviations in the Hindi.\n\tweekdayNamesHindiAbbr = []string{\n\t\t\"\\u0930\\u0935\\u093F.\",\n\t\t\"\\u0938\\u094B\\u092E.\",\n\t\t\"\\u092E\\u0902\\u0917\\u0932.\",\n\t\t\"\\u092C\\u0941\\u0927.\",\n\t\t\"\\u0917\\u0941\\u0930\\u0941.\",\n\t\t\"\\u0936\\u0941\\u0915\\u094D\\u0930.\",\n\t\t\"\\u0936\\u0928\\u093F.\",\n\t}\n\t// weekdayNamesHungarian list the weekday name in the Hungarian.\n\tweekdayNamesHungarian = []string{\"vasárnap\", \"hétfő\", \"kedd\", \"szerda\", \"csütörtök\", \"péntek\", \"szombat\"}\n\t// weekdayNamesHungarianAbbr list the weekday name abbreviations in the\n\t// Hungarian.\n\tweekdayNamesHungarianAbbr = []string{\"V\", \"H\", \"K\", \"Sze\", \"Cs\", \"P\", \"Szo\"}\n\t// weekdayNamesIcelandic list the weekday name in the Icelandic.\n\tweekdayNamesIcelandic = []string{\"sunnudagur\", \"mánudagur\", \"þriðjudagur\", \"miðvikudagur\", \"fimmtudagur\", \"föstudagur\", \"laugardagur\"}\n\t// weekdayNamesIcelandicAbbr list the weekday name abbreviations in the\n\t// Icelandic.\n\tweekdayNamesIcelandicAbbr = []string{\"sun.\", \"mán.\", \"þri.\", \"mið.\", \"fim.\", \"fös.\", \"lau.\"}\n\t// weekdayNamesIgbo list the weekday name in the Igbo.\n\tweekdayNamesIgbo = []string{\"Ụbọchị Ụka\", \"Mọnde\", \"Tiuzdee\", \"Wenezdee\", \"Tọọzdee\", \"Fraịdee\", \"Satọdee\"}\n\t// weekdayNamesIgboAbbr list the weekday name abbreviations in the Igbo.\n\tweekdayNamesIgboAbbr = []string{\"Ụka\", \"Mọn\", \"Tiu\", \"Wen\", \"Tọọ\", \"Fraị\", \"Satọdee\"}\n\t// weekdayNamesIndonesian list the weekday name in the Indonesian.\n\tweekdayNamesIndonesian = []string{\"Minggu\", \"Senin\", \"Selasa\", \"Rabu\", \"Kamis\", \"Jumat\", \"Sabtu\"}\n\t// weekdayNamesIndonesianAbbr list the weekday name abbreviations in the\n\t// Indonesian.\n\tweekdayNamesIndonesianAbbr = []string{\"Mgg\", \"Sen\", \"Sel\", \"Rab\", \"Kam\", \"Jum\", \"Sab\"}\n\t// weekdayNamesInuktitut list the weekday name in the Inuktitut.\n\tweekdayNamesInuktitut = []string{\"Naattiinguja\", \"Naggajjau\", \"Aippiq\", \"Pingatsiq\", \"Sitammiq\", \"Tallirmiq\", \"Sivataarvik\"}\n\t// weekdayNamesInuktitutAbbr list the weekday name abbreviations in the\n\t// Inuktitut.\n\tweekdayNamesInuktitutAbbr = []string{\"Nat\", \"Nag\", \"Aip\", \"Pi\", \"Sit\", \"Tal\", \"Siv\"}\n\t// weekdayNamesSyllabics list the weekday name in the Syllabics.\n\tweekdayNamesSyllabics = []string{\n\t\t\"\\u14C8\\u1466\\u144F\\u1591\\u152D\",\n\t\t\"\\u14C7\\u14A1\\u1490\\u153E\\u152D\\u1405\",\n\t\t\"\\u140A\\u1403\\u1449\\u1431\\u1585\",\n\t\t\"\\u1431\\u1593\\u1466\\u14EF\\u1585\",\n\t\t\"\\u14EF\\u1455\\u14BB\\u14A5\\u1585\",\n\t\t\"\\u1455\\u14EA\\u14D5\\u1550\\u14A5\\u1585\",\n\t\t\"\\u14EF\\u1559\\u1456\\u1550\\u1555\\u1483\",\n\t}\n\t// weekdayNamesSyllabicsAbbr list the weekday name abbreviations in the\n\t// Syllabics.\n\tweekdayNamesSyllabicsAbbr = []string{\n\t\t\"\\u14C8\\u1466\\u144F\",\n\t\t\"\\u14C7\\u14A1\\u1490\",\n\t\t\"\\u140A\\u1403\\u1449\\u1431\",\n\t\t\"\\u1431\\u1593\\u1466\\u14EF\",\n\t\t\"\\u14EF\\u1455\",\n\t\t\"\\u1455\\u14EA\\u14D5\",\n\t\t\"\\u14EF\\u1559\\u1456\\u1550\\u1555\\u1483\",\n\t}\n\t// weekdayNamesIrish list the weekday name in the Irish.\n\tweekdayNamesIrish = []string{\"Dé Domhnaigh\", \"Dé Luain\", \"Dé Máirt\", \"Dé Céadaoin\", \"Déardaoin\", \"Dé hAoine\", \"Dé Sathairn\"}\n\t// weekdayNamesIrishAbbr list the weekday name abbreviations in the Irish.\n\tweekdayNamesIrishAbbr = []string{\"Domh\", \"Luan\", \"Máirt\", \"Céad\", \"Déar\", \"Aoine\", \"Sath\"}\n\t// weekdayNamesItalian list the weekday name in the Italian.\n\tweekdayNamesItalian = []string{\"domenica\", \"lunedì\", \"martedì\", \"mercoledì\", \"giovedì\", \"venerdì\", \"sabato\"}\n\t// weekdayNamesItalianAbbr list the weekday name abbreviations in the\n\t// Italian.\n\tweekdayNamesItalianAbbr = []string{\"dom\", \"lun\", \"mar\", \"mer\", \"gio\", \"ven\", \"sab\"}\n\t// weekdayNamesJapanese list the weekday name in the Japanese.\n\tweekdayNamesJapanese = []string{\"日曜日\", \"月曜日\", \"火曜日\", \"水曜日\", \"木曜日\", \"金曜日\", \"土曜日\"}\n\t// weekdayNamesJapaneseAbbr list the weekday name abbreviations in the\n\t// Japanese.\n\tweekdayNamesJapaneseAbbr = []string{\"日\", \"月\", \"火\", \"水\", \"木\", \"金\", \"土\"}\n\t// weekdayNamesKannada list the weekday name in the Kannada.\n\tweekdayNamesKannada = []string{\n\t\t\"\\u0CAD\\u0CBE\\u0CA8\\u0CC1\\u0CB5\\u0CBE\\u0CB0\",\n\t\t\"\\u0CB8\\u0CCB\\u0CAE\\u0CB5\\u0CBE\\u0CB0\",\n\t\t\"\\u0CAE\\u0C82\\u0C97\\u0CB3\\u0CB5\\u0CBE\\u0CB0\",\n\t\t\"\\u0CAC\\u0CC1\\u0CA7\\u0CB5\\u0CBE\\u0CB0\",\n\t\t\"\\u0C97\\u0CC1\\u0CB0\\u0CC1\\u0CB5\\u0CBE\\u0CB0\",\n\t\t\"\\u0CB6\\u0CC1\\u0C95\\u0CCD\\u0CB0\\u0CB5\\u0CBE\\u0CB0\",\n\t\t\"\\u0CB6\\u0CA8\\u0CBF\\u0CB5\\u0CBE\\u0CB0\",\n\t}\n\t// weekdayNamesKannadaAbbr list the weekday name abbreviations in the\n\t// Kannada.\n\tweekdayNamesKannadaAbbr = []string{\n\t\t\"\\u0CAD\\u0CBE\\u0CA8\\u0CC1.\",\n\t\t\"\\u0CB8\\u0CCB\\u0CAE.\",\n\t\t\"\\u0CAE\\u0C82\\u0C97\\u0CB3.\",\n\t\t\"\\u0CAC\\u0CC1\\u0CA7.\",\n\t\t\"\\u0C97\\u0CC1\\u0CB0\\u0CC1.\",\n\t\t\"\\u0CB6\\u0CC1\\u0C95\\u0CCD\\u0CB0.\",\n\t\t\"\\u0CB6\\u0CA8\\u0CBF.\",\n\t}\n\t// weekdayNamesKashmiri list the weekday name in the Kashmiri.\n\tweekdayNamesKashmiri = []string{\n\t\t\"\\u0627\\u064E\\u062A\\u06BE\\u0648\\u0627\\u0631\",\n\t\t\"\\u0698\\u0654\\u0646\\u062F\\u0631\\u0655\\u0631\\u0648\\u0627\\u0631\",\n\t\t\"\\u0628\\u06C6\\u0645\\u0648\\u0627\\u0631\",\n\t\t\"\\u0628\\u0648\\u062F\\u0648\\u0627\\u0631\",\n\t\t\"\\u0628\\u0631\\u0620\\u0633\\u0648\\u0627\\u0631\",\n\t\t\"\\u062C\\u064F\\u0645\\u06C1\",\n\t\t\"\\u0628\\u0679\\u0648\\u0627\\u0631\",\n\t}\n\t// weekdayNamesKashmiriAbbr list the weekday name abbreviations in the\n\t// Kashmiri.\n\tweekdayNamesKashmiriAbbr = []string{\n\t\t\"\\u0622\\u062A\\u06BE\\u0648\\u0627\\u0631\",\n\t\t\"\\u0698\\u0654\\u0646\\u062F\\u0655\\u0631\\u0648\\u0627\\u0631\",\n\t\t\"\\u0628\\u06C6\\u0645\\u0648\\u0627\\u0631\",\n\t\t\"\\u0628\\u0648\\u062F\\u0648\\u0627\\u0631\",\n\t\t\"\\u0628\\u0631\\u0620\\u0633\\u0648\\u0627\\u0631\",\n\t\t\"\\u062C\\u064F\\u0645\\u06C1\",\n\t\t\"\\u0628\\u0679\\u0648\\u0627\\u0631\",\n\t}\n\t// weekdayNamesKazakh list the weekday name in the Kazakh.\n\tweekdayNamesKazakh = []string{\n\t\t\"\\u0436\\u0435\\u043A\\u0441\\u0435\\u043D\\u0431\\u0456\",\n\t\t\"\\u0434\\u04AF\\u0439\\u0441\\u0435\\u043D\\u0431\\u0456\",\n\t\t\"\\u0441\\u0435\\u0439\\u0441\\u0435\\u043D\\u0431\\u0456\",\n\t\t\"\\u0441\\u04D9\\u0440\\u0441\\u0435\\u043D\\u0431\\u0456\",\n\t\t\"\\u0431\\u0435\\u0439\\u0441\\u0435\\u043D\\u0431\\u0456\",\n\t\t\"\\u0436\\u04B1\\u043C\\u0430\",\n\t\t\"\\u0441\\u0435\\u043D\\u0431\\u0456\",\n\t}\n\t// weekdayNamesKazakhAbbr list the weekday name abbreviations in the Kazakh.\n\tweekdayNamesKazakhAbbr = []string{\n\t\t\"\\u0436\\u0435\\u043A\",\n\t\t\"\\u0434\\u04AF\\u0439\",\n\t\t\"\\u0441\\u0435\\u0439\",\n\t\t\"\\u0441\\u04D9\\u0440\",\n\t\t\"\\u0431\\u0435\\u0439\",\n\t\t\"\\u0436\\u04B1\\u043C\",\n\t\t\"\\u0441\\u0435\\u043D\",\n\t}\n\t// weekdayNamesKhmer list the weekday name in the Khmer.\n\tweekdayNamesKhmer = []string{\n\t\t\"\\u1790\\u17D2\\u1784\\u17C3\\u17A2\\u17B6\\u1791\\u17B7\\u178F\\u17D2\\u1799\",\n\t\t\"\\u1790\\u17D2\\u1784\\u17C3\\u1785\\u17D0\\u1793\\u17D2\\u1791\",\n\t\t\"\\u1790\\u17D2\\u1784\\u17C3\\u17A2\\u1784\\u17D2\\u1782\\u17B6\\u179A\",\n\t\t\"\\u1790\\u17D2\\u1784\\u17C3\\u1796\\u17BB\\u1792\",\n\t\t\"\\u1790\\u17D2\\u1784\\u17C3\\u1796\\u17D2\\u179A\\u17A0\\u179F\\u17D2\\u1794\\u178F\\u17B7\\u17CD\",\n\t\t\"\\u1790\\u17D2\\u1784\\u17C3\\u179F\\u17BB\\u1780\\u17D2\\u179A\",\n\t\t\"\\u1790\\u17D2\\u1784\\u17C3\\u179F\\u17C5\\u179A\\u17CD\",\n\t}\n\t// weekdayNamesKhmerAbbr list the weekday name abbreviations in the Khmer.\n\tweekdayNamesKhmerAbbr = []string{\n\t\t\"\\u17A2\\u17B6\\u1791\\u17B7.\",\n\t\t\"\\u1785.\",\n\t\t\"\\u17A2.\",\n\t\t\"\\u1796\\u17BB\",\n\t\t\"\\u1796\\u17D2\\u179A\\u17A0.\",\n\t\t\"\\u179F\\u17BB.\",\n\t\t\"\\u179F.\",\n\t}\n\t// weekdayNamesKiche list the weekday name in the Kiche.\n\tweekdayNamesKiche = []string{\"juq'ij\", \"kaq'ij\", \"oxq'ij\", \"kajq'ij\", \"joq'ij\", \"waqq'ij\", \"wuqq'ij\"}\n\t// weekdayNamesKicheAbbr list the weekday name abbreviations in the Kiche.\n\tweekdayNamesKicheAbbr = []string{\"juq'\", \"kaq'\", \"oxq'\", \"kajq'\", \"joq'\", \"waqq'\", \"wuqq'\"}\n\t// weekdayNamesKinyarwanda list the weekday name in the Kinyarwanda.\n\tweekdayNamesKinyarwanda = []string{\"Ku cyumweru\", \"Ku wa mbere\", \"Ku wa kabiri\", \"Ku wa gatatu\", \"Ku wa kane\", \"Ku wa gatanu\", \"Ku wa gatandatu\"}\n\t// weekdayNamesKinyarwandaAbbr list the weekday name abbreviations in the\n\t// Kinyarwanda.\n\tweekdayNamesKinyarwandaAbbr = []string{\"cyu.\", \"mbe.\", \"kab.\", \"gat.\", \"kan.\", \"gnu.\", \"gat.\"}\n\t// weekdayNamesKiswahili list the weekday name in the Kiswahili.\n\tweekdayNamesKiswahili = []string{\"Jumapili\", \"Jumatatu\", \"Jumanne\", \"Jumatano\", \"Alhamisi\", \"Ijumaa\", \"Jumamosi\"}\n\t// weekdayNamesKiswahiliAbbr list the weekday name abbreviations in the\n\t// Kiswahili.\n\tweekdayNamesKiswahiliAbbr = []string{\"Jpl\", \"Jtt\", \"Jnn\", \"Jtn\", \"Alh\", \"Ijm\", \"Jms\"}\n\t// weekdayNamesKonkani list the weekday name in the Konkani.\n\tweekdayNamesKonkani = []string{\n\t\t\"\\u0906\\u092F\\u0924\\u093E\\u0930\",\n\t\t\"\\u0938\\u094B\\u092E\\u093E\\u0930\",\n\t\t\"\\u092E\\u0902\\u0917\\u0933\\u093E\\u0930\",\n\t\t\"\\u092C\\u0941\\u0927\\u0935\\u093E\\u0930\",\n\t\t\"\\u092C\\u093F\\u0930\\u0947\\u0938\\u094D\\u0924\\u093E\\u0930\",\n\t\t\"\\u0938\\u0941\\u0915\\u094D\\u0930\\u093E\\u0930\",\n\t\t\"\\u0936\\u0947\\u0928\\u0935\\u093E\\u0930\",\n\t}\n\t// weekdayNamesKonkaniAbbr list the weekday name abbreviations in the\n\t// Konkani.\n\tweekdayNamesKonkaniAbbr = []string{\n\t\t\"\\u0906\\u092F.\",\n\t\t\"\\u0938\\u094B\\u092E.\",\n\t\t\"\\u092E\\u0902\\u0917\\u0933.\",\n\t\t\"\\u092C\\u0941\\u0927.\",\n\t\t\"\\u092C\\u093F\\u0930\\u0947.\",\n\t\t\"\\u0938\\u0941\\u0915\\u094D\\u0930.\",\n\t\t\"\\u0936\\u0947\\u0928.\",\n\t}\n\t// weekdayNamesKorean list the weekday name in the Korean.\n\tweekdayNamesKorean = []string{\"일요일\", \"월요일\", \"화요일\", \"수요일\", \"목요일\", \"금요일\", \"토요일\"}\n\t// weekdayNamesKoreanAbbr list the weekday name abbreviations in the Korean.\n\tweekdayNamesKoreanAbbr = []string{\"일\", \"월\", \"화\", \"수\", \"목\", \"금\", \"토\"}\n\t// weekdayNamesKyrgyz list the weekday name in the Kyrgyz.\n\tweekdayNamesKyrgyz = []string{\n\t\t\"\\u0436\\u0435\\u043A\\u0448\\u0435\\u043C\\u0431\\u0438\",\n\t\t\"\\u0434\\u04AF\\u0439\\u0448\\u04E9\\u043C\\u0431\\u04AF\",\n\t\t\"\\u0448\\u0435\\u0439\\u0448\\u0435\\u043C\\u0431\\u0438\",\n\t\t\"\\u0448\\u0430\\u0440\\u0448\\u0435\\u043C\\u0431\\u0438\",\n\t\t\"\\u0431\\u0435\\u0439\\u0448\\u0435\\u043C\\u0431\\u0438\",\n\t\t\"\\u0436\\u0443\\u043C\\u0430\",\n\t\t\"\\u0438\\u0448\\u0435\\u043C\\u0431\\u0438\",\n\t}\n\t// weekdayNamesKyrgyzAbbr list the weekday name abbreviations in the Kyrgyz.\n\tweekdayNamesKyrgyzAbbr = []string{\n\t\t\"\\u0436\\u0435\\u043A.\",\n\t\t\"\\u0434\\u04AF\\u0439.\",\n\t\t\"\\u0448\\u0435\\u0439\\u0448.\",\n\t\t\"\\u0448\\u0430\\u0440\\u0448.\",\n\t\t\"\\u0431\\u0435\\u0439\\u0448.\",\n\t\t\"\\u0436\\u0443\\u043C\\u0430\",\n\t\t\"\\u0438\\u0448\\u043C.\",\n\t}\n\t// weekdayNamesLao list the weekday name in the Lao.\n\tweekdayNamesLao = []string{\n\t\t\"\\u0EA7\\u0EB1\\u0E99\\u0EAD\\u0EB2\\u0E97\\u0EB4\\u0E94\",\n\t\t\"\\u0EA7\\u0EB1\\u0E99\\u0E88\\u0EB1\\u0E99\",\n\t\t\"\\u0EA7\\u0EB1\\u0E99\\u0EAD\\u0EB1\\u0E87\\u0E84\\u0EB2\\u0E99\",\n\t\t\"\\u0EA7\\u0EB1\\u0E99\\u0E9E\\u0EB8\\u0E94\",\n\t\t\"\\u0EA7\\u0EB1\\u0E99\\u0E9E\\u0EB0\\u0EAB\\u0EB1\\u0E94\",\n\t\t\"\\u0EA7\\u0EB1\\u0E99\\u0EAA\\u0EB8\\u0E81\",\n\t\t\"\\u0EA7\\u0EB1\\u0E99\\u0EC0\\u0EAA\\u0EBB\\u0EB2\",\n\t}\n\t// weekdayNamesLaoAbbr list the weekday name abbreviations in the Lao.\n\tweekdayNamesLaoAbbr = []string{\n\t\t\"\\u0EAD\\u0EB2\\u0E97\\u0EB4\\u0E94\",\n\t\t\"\\u0E88\\u0EB1\\u0E99\",\n\t\t\"\\u0EAD\\u0EB1\\u0E87\\u0E84\\u0EB2\\u0E99\",\n\t\t\"\\u0E9E\\u0EB8\\u0E94\",\n\t\t\"\\u0E9E\\u0EB0\\u0EAB\\u0EB1\\u0E94\",\n\t\t\"\\u0EAA\\u0EB8\\u0E81\",\n\t\t\"\\u0EC0\\u0EAA\\u0EBB\\u0EB2\",\n\t}\n\t// weekdayNamesLatin list the weekday name in the Latin.\n\tweekdayNamesLatin = []string{\"Solis\", \"Lunae\", \"Martis\", \"Mercurii\", \"Jovis\", \"Veneris\", \"Saturni\"}\n\t// weekdayNamesLatinAbbr list the weekday name abbreviations in the Latin.\n\tweekdayNamesLatinAbbr = []string{\"Sol\", \"Lun\", \"Mar\", \"Mer\", \"Jov\", \"Ven\", \"Sat\"}\n\t// weekdayNamesLatvian list the weekday name in the Latvian.\n\tweekdayNamesLatvian = []string{\"svētdiena\", \"pirmdiena\", \"otrdiena\", \"trešdiena\", \"ceturtdiena\", \"piektdiena\", \"sestdiena\"}\n\t// weekdayNamesLatvianAbbr list the weekday name abbreviations in the\n\t// Latvian.\n\tweekdayNamesLatvianAbbr = []string{\"svētd.\", \"pirmd.\", \"otrd.\", \"trešd.\", \"ceturtd.\", \"piektd.\", \"sestd.\"}\n\t// weekdayNamesLithuanian list the weekday name in the Lithuanian.\n\tweekdayNamesLithuanian = []string{\"sekmadienis\", \"pirmadienis\", \"antradienis\", \"trečiadienis\", \"ketvirtadienis\", \"penktadienis\", \"šeštadienis\"}\n\t// weekdayNamesLithuanianAbbr list the weekday name abbreviations in the\n\t// Lithuanian.\n\tweekdayNamesLithuanianAbbr = []string{\"sk\", \"pr\", \"an\", \"tr\", \"kt\", \"pn\", \"št\"}\n\t// weekdayNamesLowerSorbian list the weekday name in the Lower Sorbian.\n\tweekdayNamesLowerSorbian = []string{\"njeźela\", \"ponjeźele\", \"wałtora\", \"srjoda\", \"stwórtk\", \"pětk\", \"sobota\"}\n\t// weekdayNamesLowerSorbianAbbr list the weekday name abbreviations in the\n\t// Lower Sorbian.\n\tweekdayNamesLowerSorbianAbbr = []string{\"nje\", \"pon\", \"wa\\u0142\", \"srj\", \"stw\", \"p\\u011Bt\", \"sob\"}\n\t// weekdayNamesLuxembourgish list the weekday name in the Luxembourgish.\n\tweekdayNamesLuxembourgish = []string{\"Sonndeg\", \"Méindeg\", \"Dënschdeg\", \"Mëttwoch\", \"Donneschdeg\", \"Freideg\", \"Samschdeg\"}\n\t// weekdayNamesLuxembourgishAbbr list the weekday name abbreviations in the\n\t// Luxembourgish.\n\tweekdayNamesLuxembourgishAbbr = []string{\"Son\", \"Méi\", \"Dën\", \"Mët\", \"Don\", \"Fre\", \"Sam\"}\n\t// weekdayNamesMacedonian list the weekday name in the Macedonian.\n\tweekdayNamesMacedonian = []string{\n\t\t\"\\u043D\\u0435\\u0434\\u0435\\u043B\\u0430\",\n\t\t\"\\u043F\\u043E\\u043D\\u0435\\u0434\\u0435\\u043B\\u043D\\u0438\\u043A\",\n\t\t\"\\u0432\\u0442\\u043E\\u0440\\u043D\\u0438\\u043A\",\n\t\t\"\\u0441\\u0440\\u0435\\u0434\\u0430\",\n\t\t\"\\u0447\\u0435\\u0442\\u0432\\u0440\\u0442\\u043E\\u043A\",\n\t\t\"\\u043F\\u0435\\u0442\\u043E\\u043A\",\n\t\t\"\\u0441\\u0430\\u0431\\u043E\\u0442\\u0430\",\n\t}\n\t// weekdayNamesMacedonianAbbr list the weekday name abbreviations in the\n\t// Macedonian.\n\tweekdayNamesMacedonianAbbr = []string{\n\t\t\"\\u043D\\u0435\\u0434.\",\n\t\t\"\\u043F\\u043E\\u043D.\",\n\t\t\"\\u0432\\u0442.\",\n\t\t\"\\u0441\\u0440\\u0435.\",\n\t\t\"\\u0447\\u0435\\u0442.\",\n\t\t\"\\u043F\\u0435\\u0442.\",\n\t\t\"\\u0441\\u0430\\u0431.\",\n\t}\n\t// weekdayNamesMalay list the weekday name in the Malay.\n\tweekdayNamesMalay = []string{\"Ahad\", \"Isnin\", \"Selasa\", \"Rabu\", \"Khamis\", \"Jumaat\", \"Sabtu\"}\n\t// weekdayNamesMalayAbbr list the weekday name abbreviations in the Lower\n\t// Sorbian.\n\tweekdayNamesMalayAbbr = []string{\"Ahd\", \"Isn\", \"Sel\", \"Rab\", \"Kha\", \"Jum\", \"Sab\"}\n\t// weekdayNamesMalayalam list the weekday name in the Malayalam.\n\tweekdayNamesMalayalam = []string{\n\t\t\"\\u0D1E\\u0D3E\\u0D2F\\u0D31\\u0D3E\\u0D34\\u0D4D\\u200C\\u0D1A\",\n\t\t\"\\u0D24\\u0D3F\\u0D19\\u0D4D\\u0D15\\u0D33\\u0D3E\\u0D34\\u0D4D\\u200C\\u0D1A\",\n\t\t\"\\u0D1A\\u0D4A\\u0D35\\u0D4D\\u0D35\\u0D3E\\u0D34\\u0D4D\\u0D1A\",\n\t\t\"\\u0D2C\\u0D41\\u0D27\\u0D28\\u0D3E\\u0D34\\u0D4D\\u200C\\u0D1A\",\n\t\t\"\\u0D35\\u0D4D\\u0D2F\\u0D3E\\u0D34\\u0D3E\\u0D34\\u0D4D\\u200C\\u0D1A\",\n\t\t\"\\u0D35\\u0D46\\u0D33\\u0D4D\\u0D33\\u0D3F\\u0D2F\\u0D3E\\u0D34\\u0D4D\\u200C\\u0D1A\",\n\t\t\"\\u0D36\\u0D28\\u0D3F\\u0D2F\\u0D3E\\u0D34\\u0D4D\\u200C\\u0D1A\",\n\t}\n\t// weekdayNamesMalayalamAbbr list the weekday name abbreviations in the\n\t// Malayalam.\n\tweekdayNamesMalayalamAbbr = []string{\n\t\t\"\\u0D1E\\u0D3E\\u0D2F\\u0D7C\",\n\t\t\"\\u0D24\\u0D3F\\u0D19\\u0D4D\\u0D15\\u0D7E\",\n\t\t\"\\u0D1A\\u0D4A\\u0D35\\u0D4D\\u0D35\",\n\t\t\"\\u0D2C\\u0D41\\u0D27\\u0D7B\",\n\t\t\"\\u0D35\\u0D4D\\u0D2F\\u0D3E\\u0D34\\u0D02\",\n\t\t\"\\u0D35\\u0D46\\u0D33\\u0D4D\\u0D33\\u0D3F\",\n\t\t\"\\u0D36\\u0D28\\u0D3F\",\n\t}\n\t// weekdayNamesMaltese list the weekday name in the Maltese.\n\tweekdayNamesMaltese = []string{\"Il-\\u0126add\", \"It-Tnejn\", \"It-Tlieta\", \"L-Erbg\\u0127a\", \"Il-\\u0126amis\", \"Il-\\u0120img\\u0127a\", \"Is-Sibt\"}\n\t// weekdayNamesMalteseAbbr list the weekday name abbreviations in the\n\t// Maltese.\n\tweekdayNamesMalteseAbbr = []string{\"\\u0126ad\", \"Tne\", \"Tli\", \"Erb\", \"\\u0126am\", \"\\u0120im\", \"Sib\"}\n\t// weekdayNamesMaori list the weekday name in the Maori.\n\tweekdayNamesMaori = []string{\"Rātapu\", \"Rāhina\", \"Rātū\", \"Rāapa\", \"Rāpare\", \"Rāmere\", \"Rāhoroi\"}\n\t// weekdayNamesMaoriAbbr list the weekday name abbreviations in the Maori.\n\tweekdayNamesMaoriAbbr = []string{\"Ta\", \"Hi\", \"Tū\", \"Apa\", \"Pa\", \"Me\", \"Ho\"}\n\t// weekdayNamesMapudungun list the weekday name in the Mapudungun.\n\tweekdayNamesMapudungun = []string{\"Kiñe Ante\", \"Epu Ante\", \"Kila Ante\", \"Meli Ante\", \"Kechu Ante\", \"Cayu Ante\", \"Regle Ante\"}\n\t// weekdayNamesMapudungunAbbr list the weekday name abbreviations in the\n\t// Mapudungun.\n\tweekdayNamesMapudungunAbbr = []string{\"Kiñe\", \"Epu\", \"Kila\", \"Meli\", \"Kechu\", \"Cayu\", \"Regle\"}\n\t// weekdayNamesMarathi list the weekday name in the Marathi.\n\tweekdayNamesMarathi = []string{\n\t\t\"\\u0930\\u0935\\u093F\\u0935\\u093E\\u0930\",\n\t\t\"\\u0938\\u094B\\u092E\\u0935\\u093E\\u0930\",\n\t\t\"\\u092E\\u0902\\u0917\\u0933\\u0935\\u093E\\u0930\",\n\t\t\"\\u092C\\u0941\\u0927\\u0935\\u093E\\u0930\",\n\t\t\"\\u0917\\u0941\\u0930\\u0941\\u0935\\u093E\\u0930\",\n\t\t\"\\u0936\\u0941\\u0915\\u094D\\u0930\\u0935\\u093E\\u0930\",\n\t\t\"\\u0936\\u0928\\u093F\\u0935\\u093E\\u0930\",\n\t}\n\t// weekdayNamesMarathiAbbr list the weekday name abbreviations in the\n\t// Marathi.\n\tweekdayNamesMarathiAbbr = []string{\n\t\t\"\\u0930\\u0935\\u093F.\",\n\t\t\"\\u0938\\u094B\\u092E.\",\n\t\t\"\\u092E\\u0902\\u0917\\u0933.\",\n\t\t\"\\u092C\\u0941\\u0927.\",\n\t\t\"\\u0917\\u0941\\u0930\\u0941.\",\n\t\t\"\\u0936\\u0941\\u0915\\u094D\\u0930.\",\n\t\t\"\\u0936\\u0928\\u093F.\",\n\t}\n\t// weekdayNamesMohawk list the weekday name in the Mohawk.\n\tweekdayNamesMohawk = []string{\"Awentatokentì:ke\", \"Awentataón'ke\", \"Ratironhia'kehronòn:ke\", \"Soséhne\", \"Okaristiiáhne\", \"Ronwaia'tanentaktonhne\", \"Entákta\"}\n\t// weekdayNamesMongolian list the weekday name in the Mongolian.\n\tweekdayNamesMongolian = []string{\n\t\t\"\\u043D\\u044F\\u043C\",\n\t\t\"\\u0434\\u0430\\u0432\\u0430\\u0430\",\n\t\t\"\\u043C\\u044F\\u0433\\u043C\\u0430\\u0440\",\n\t\t\"\\u043B\\u0445\\u0430\\u0433\\u0432\\u0430\",\n\t\t\"\\u043F\\u04AF\\u0440\\u044D\\u0432\",\n\t\t\"\\u0431\\u0430\\u0430\\u0441\\u0430\\u043D\",\n\t\t\"\\u0431\\u044F\\u043C\\u0431\\u0430\",\n\t}\n\t// weekdayNamesMongolianAbbr list the weekday name abbreviations in the\n\t// Mongolian.\n\tweekdayNamesMongolianAbbr = []string{\n\t\t\"\\u041D\\u044F\",\n\t\t\"\\u0414\\u0430\",\n\t\t\"\\u041C\\u044F\",\n\t\t\"\\u041B\\u0445\",\n\t\t\"\\u041F\\u04AF\",\n\t\t\"\\u0411\\u0430\",\n\t\t\"\\u0411\\u044F\",\n\t}\n\t// weekdayNamesMongolianCyrlAbbr list the weekday name abbreviations in the\n\t// Mongolian (Cyrillic).\n\tweekdayNamesMongolianCyrlAbbr = []string{\n\t\t\"\\u041D\\u044F\",\n\t\t\"\\u0414\\u0430\",\n\t\t\"\\u041C\\u044F\",\n\t\t\"\\u041B\\u0445\\u0430\",\n\t\t\"\\u041F\\u04AF\",\n\t\t\"\\u0411\\u0430\",\n\t\t\"\\u0411\\u044F\",\n\t}\n\t// weekdayNamesTraditionalMongolian list the weekday name abbreviations in\n\t// the Traditional Mongolian.\n\tweekdayNamesTraditionalMongolian = []string{\n\t\t\"\\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u1821\\u1833\\u1826\\u1837\",\n\t\t\"\\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u1828\\u1822\\u182D\\u1821\\u1828\",\n\t\t\"\\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u182C\\u1823\\u1836\\u1820\\u1837\",\n\t\t\"\\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u182D\\u1824\\u1837\\u182A\\u1820\\u1828\",\n\t\t\"\\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u1833\\u1825\\u1837\\u182A\\u1821\\u1828\",\n\t\t\"\\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u1832\\u1820\\u182A\\u1824\\u1828\",\n\t\t\"\\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u1835\\u1822\\u1837\\u182D\\u1824\\u182D\\u1820\\u1828\",\n\t}\n\t// weekdayNamesTraditionalMongolianMN list the weekday name abbreviations in\n\t// the Traditional Mongolian MN.\n\tweekdayNamesTraditionalMongolianMN = []string{\n\t\t\"\\u1828\\u1822\\u182E\\u180E\\u1820\",\n\t\t\"\\u1833\\u1820\\u1838\\u1820\",\n\t\t\"\\u182E\\u1822\\u182D\\u182E\\u1820\\u1837\",\n\t\t\"\\u1840\\u1820\\u182D\\u182A\\u1820\",\n\t\t\"\\u182B\\u1826\\u1837\\u182A\\u1826\",\n\t\t\"\\u182A\\u1820\\u1830\\u1820\\u1829\",\n\t\t\"\\u182A\\u1822\\u182E\\u182A\\u1820\",\n\t}\n\t// weekdayNamesNepali list the weekday name in the Nepali.\n\tweekdayNamesNepali = []string{\n\t\t\"\\u0906\\u0907\\u0924\\u0935\\u093E\\u0930\",\n\t\t\"\\u0938\\u094B\\u092E\\u0935\\u093E\\u0930\",\n\t\t\"\\u092E\\u0919\\u094D\\u0917\\u0932\\u0935\\u093E\\u0930\",\n\t\t\"\\u092C\\u0941\\u0927\\u0935\\u093E\\u0930\",\n\t\t\"\\u092C\\u093F\\u0939\\u0940\\u0935\\u093E\\u0930\",\n\t\t\"\\u0936\\u0941\\u0915\\u094D\\u0930\\u0935\\u093E\\u0930\",\n\t\t\"\\u0936\\u0928\\u093F\\u0935\\u093E\\u0930\",\n\t}\n\t// weekdayNamesNepaliAbbr list the weekday name abbreviations in the Nepali.\n\tweekdayNamesNepaliAbbr = []string{\n\t\t\"\\u0906\\u0907\\u0924\",\n\t\t\"\\u0938\\u094B\\u092E\",\n\t\t\"\\u092E\\u0919\\u094D\\u0917\\u0932\",\n\t\t\"\\u092C\\u0941\\u0927\",\n\t\t\"\\u092C\\u093F\\u0939\\u0940\",\n\t\t\"\\u0936\\u0941\\u0915\\u094D\\u0930\",\n\t\t\"\\u0936\\u0928\\u093F\",\n\t}\n\t// weekdayNamesNepaliIN list the weekday name in the Nepali India.\n\tweekdayNamesNepaliIN = []string{\n\t\t\"\\u0906\\u0907\\u0924\\u092C\\u093E\\u0930\",\n\t\t\"\\u0938\\u094B\\u092E\\u092C\\u093E\\u0930\",\n\t\t\"\\u092E\\u0919\\u094D\\u0917\\u0932\\u092C\\u093E\\u0930\",\n\t\t\"\\u092C\\u0941\\u0927\\u092C\\u093E\\u0930\",\n\t\t\"\\u092C\\u093F\\u0939\\u093F\\u092C\\u093E\\u0930\",\n\t\t\"\\u0936\\u0941\\u0915\\u094D\\u0930\\u092C\\u093E\\u0930\",\n\t\t\"\\u0936\\u0928\\u093F\\u092C\\u093E\\u0930\",\n\t}\n\t// weekdayNamesNepaliINAbbr list the weekday name abbreviations in the\n\t// Nepali India.\n\tweekdayNamesNepaliINAbbr = []string{\n\t\t\"\\u0906\\u0907\\u0924\",\n\t\t\"\\u0938\\u094B\\u092E\",\n\t\t\"\\u092E\\u0919\\u094D\\u0917\\u0932\",\n\t\t\"\\u092C\\u0941\\u0927\",\n\t\t\"\\u092C\\u093F\\u0939\\u093F\",\n\t\t\"\\u0936\\u0941\\u0915\\u094D\\u0930\",\n\t\t\"\\u0936\\u0928\\u093F\",\n\t}\n\t// weekdayNamesNorwegian list the weekday name in the Norwegian.\n\tweekdayNamesNorwegian = []string{\"søndag\", \"mandag\", \"tirsdag\", \"onsdag\", \"torsdag\", \"fredag\", \"lørdag\"}\n\t// weekdayNamesNorwegianAbbr list the weekday name abbreviations in the\n\t// Norwegian.\n\tweekdayNamesNorwegianAbbr = []string{\"søn.\", \"man.\", \"tir.\", \"ons.\", \"tor.\", \"fre.\", \"lør.\"}\n\t// weekdayNamesNorwegianNOAbbr list the weekday name abbreviations in the\n\t// Norwegian Norway.\n\tweekdayNamesNorwegianNOAbbr = []string{\"søn\", \"man\", \"tir\", \"ons\", \"tor\", \"fre\", \"lør\"}\n\t// weekdayNamesNorwegianNynorsk list the weekday name abbreviations in the\n\t// Norwegian Nynorsk.\n\tweekdayNamesNorwegianNynorsk = []string{\"søndag\", \"måndag\", \"tysdag\", \"onsdag\", \"torsdag\", \"fredag\", \"laurdag\"}\n\t// weekdayNamesNorwegianNynorskAbbr list the weekday name abbreviations in\n\t// the Norwegian Nynorsk.\n\tweekdayNamesNorwegianNynorskAbbr = []string{\"søn\", \"mån\", \"tys\", \"ons\", \"tor\", \"fre\", \"lau\"}\n\t// weekdayNamesOccitan list the weekday name abbreviations in the Occitan.\n\tweekdayNamesOccitan = []string{\"dimenge\", \"diluns\", \"dimarts\", \"dimècres\", \"dijòus\", \"divendres\", \"dissabte\"}\n\t// weekdayNamesOccitanAbbr list the weekday name abbreviations in the\n\t// Occitan.\n\tweekdayNamesOccitanAbbr = []string{\"dg.\", \"dl.\", \"dma.\", \"dmc.\", \"dj.\", \"dv.\", \"ds.\"}\n\t// weekdayNamesOdia list the weekday name in the Odia.\n\tweekdayNamesOdia = []string{\n\t\t\"\\u0B30\\u0B2C\\u0B3F\\u0B2C\\u0B3E\\u0B30\",\n\t\t\"\\u0B38\\u0B4B\\u0B2E\\u0B2C\\u0B3E\\u0B30\",\n\t\t\"\\u0B2E\\u0B19\\u0B4D\\u0B17\\u0B33\\u0B2C\\u0B3E\\u0B30\",\n\t\t\"\\u0B2C\\u0B41\\u0B27\\u0B2C\\u0B3E\\u0B30\",\n\t\t\"\\u0B17\\u0B41\\u0B30\\u0B41\\u0B2C\\u0B3E\\u0B30\",\n\t\t\"\\u0B36\\u0B41\\u0B15\\u0B4D\\u0B30\\u0B2C\\u0B3E\\u0B30\",\n\t\t\"\\u0B36\\u0B28\\u0B3F\\u0B2C\\u0B3E\\u0B30\",\n\t}\n\t// weekdayNamesOdiaAbbr list the weekday name abbreviations in the Odia.\n\tweekdayNamesOdiaAbbr = []string{\n\t\t\"\\u0B30\\u0B2C\\u0B3F.\",\n\t\t\"\\u0B38\\u0B4B\\u0B2E.\",\n\t\t\"\\u0B2E\\u0B19\\u0B4D\\u0B17\\u0B33.\",\n\t\t\"\\u0B2C\\u0B41\\u0B27.\",\n\t\t\"\\u0B17\\u0B41\\u0B30\\u0B41.\",\n\t\t\"\\u0B36\\u0B41\\u0B15\\u0B4D\\u0B30.\",\n\t\t\"\\u0B36\\u0B28\\u0B3F.\",\n\t}\n\t// weekdayNamesOromo list the weekday name abbreviations in the Oromo.\n\tweekdayNamesOromo = []string{\"Dilbata\", \"Wiixata\", \"Qibxata\", \"Roobii\", \"Kamiisa\", \"Jimaata\", \"Sanbata\"}\n\t// weekdayNamesOromoAbbr list the weekday name abbreviations in the Oromo.\n\tweekdayNamesOromoAbbr = []string{\"Dil\", \"Wix\", \"Qib\", \"Rob\", \"Kam\", \"Jim\", \"San\"}\n\t// weekdayNamesPashto list the weekday name in the Pashto.\n\tweekdayNamesPashto = []string{\n\t\t\"\\u064A\\u0648\\u0646\\u06CD\",\n\t\t\"\\u062F\\u0648\\u0646\\u06CD\",\n\t\t\"\\u062F\\u0631\\u06D0\\u0646\\u06CD\",\n\t\t\"\\u0685\\u0644\\u0631\\u0646\\u06CD\",\n\t\t\"\\u067E\\u064A\\u0646\\u0681\\u0646\\u06CD\",\n\t\t\"\\u062C\\u0645\\u0639\\u0647\",\n\t\t\"\\u0627\\u0648\\u0646\\u06CD\",\n\t}\n\t// weekdayNamesPersian list the weekday name in the Persian.\n\tweekdayNamesPersian = []string{\n\t\t\"\\u064A\\u0643\\u0634\\u0646\\u0628\\u0647\",\n\t\t\"\\u062F\\u0648\\u0634\\u0646\\u0628\\u0647\",\n\t\t\"\\u0633\\u0647 \\u0634\\u0646\\u0628\\u0647\",\n\t\t\"\\u0686\\u0647\\u0627\\u0631\\u0634\\u0646\\u0628\\u0647\",\n\t\t\"\\u067E\\u0646\\u062C\\u0634\\u0646\\u0628\\u0647\",\n\t\t\"\\u062C\\u0645\\u0639\\u0647\",\n\t\t\"\\u0634\\u0646\\u0628\\u0647\",\n\t}\n\t// weekdayNamesPolish list the weekday name abbreviations in the Polish.\n\tweekdayNamesPolish = []string{\"niedziela\", \"poniedziałek\", \"wtorek\", \"środa\", \"czwartek\", \"piątek\", \"sobota\"}\n\t// weekdayNamesPolishAbbr list the weekday name abbreviations in the Polish.\n\tweekdayNamesPolishAbbr = []string{\"niedz.\", \"pon.\", \"wt.\", \"śr.\", \"czw.\", \"pt.\", \"sob.\"}\n\t// weekdayNamesPortuguese list the weekday name abbreviations in the\n\t// Portuguese.\n\tweekdayNamesPortuguese = []string{\"domingo\", \"segunda-feira\", \"terça-feira\", \"quarta-feira\", \"quinta-feira\", \"sexta-feira\", \"sábado\"}\n\t// weekdayNamesPortugueseAbbr list the weekday name abbreviations in the\n\t// Portuguese.\n\tweekdayNamesPortugueseAbbr = []string{\"dom\", \"seg\", \"ter\", \"qua\", \"qui\", \"sex\", \"sáb\"}\n\t// weekdayNamesPunjabi list the weekday name in the Punjabi.\n\tweekdayNamesPunjabi = []string{\n\t\t\"\\u0A10\\u0A24\\u0A35\\u0A3E\\u0A30\",\n\t\t\"\\u0A38\\u0A4B\\u0A2E\\u0A35\\u0A3E\\u0A30\",\n\t\t\"\\u0A2E\\u0A70\\u0A17\\u0A32\\u0A35\\u0A3E\\u0A30\",\n\t\t\"\\u0A2C\\u0A41\\u0A71\\u0A27\\u0A35\\u0A3E\\u0A30\",\n\t\t\"\\u0A35\\u0A40\\u0A30\\u0A35\\u0A3E\\u0A30\",\n\t\t\"\\u0A38\\u0A3C\\u0A41\\u0A71\\u0A15\\u0A30\\u0A35\\u0A3E\\u0A30\",\n\t\t\"\\u0A38\\u0A3C\\u0A28\\u0A3F\\u0A71\\u0A1A\\u0A30\\u0A35\\u0A3E\\u0A30\",\n\t}\n\t// weekdayNamesPunjabiAbbr list the weekday name abbreviations in the\n\t// Punjabi.\n\tweekdayNamesPunjabiAbbr = []string{\n\t\t\"\\u0A10\\u0A24.\",\n\t\t\"\\u0A38\\u0A4B\\u0A2E.\",\n\t\t\"\\u0A2E\\u0A70\\u0A17\\u0A32.\",\n\t\t\"\\u0A2C\\u0A41\\u0A71\\u0A27.\",\n\t\t\"\\u0A35\\u0A40\\u0A30.\",\n\t\t\"\\u0A38\\u0A3C\\u0A41\\u0A15\\u0A30.\",\n\t\t\"\\u0A38\\u0A3C\\u0A28\\u0A3F\\u0A71\\u0A1A\\u0A30.\",\n\t}\n\t// weekdayNamesPunjabiArab list the weekday name in the Punjabi Arab.\n\tweekdayNamesPunjabiArab = []string{\n\t\t\"\\u067E\\u064A\\u0631\",\n\t\t\"\\u0645\\u0646\\u06AF\\u0644\",\n\t\t\"\\u0628\\u062F\\u06BE\",\n\t\t\"\\u062C\\u0645\\u0639\\u0631\\u0627\\u062A\",\n\t\t\"\\u062C\\u0645\\u0639\\u0647\",\n\t\t\"\\u0647\\u0641\\u062A\\u0647\",\n\t\t\"\\u0627\\u062A\\u0648\\u0627\\u0631\",\n\t}\n\t// weekdayNamesQuechua list the weekday name abbreviations in the Quechua.\n\tweekdayNamesQuechua = []string{\"intichaw\", \"killachaw\", \"atipachaw\", \"quyllurchaw\", \"Ch' askachaw\", \"Illapachaw\", \"k'uychichaw\"}\n\t// weekdayNamesQuechuaAbbr list the weekday name abbreviations in the Quechua.\n\tweekdayNamesQuechuaAbbr = []string{\"int\", \"kil\", \"ati\", \"quy\", \"Ch'\", \"Ill\", \"k'u\"}\n\t// weekdayNamesQuechuaEcuador list the weekday name abbreviations in the\n\t// Quechua Ecuador.\n\tweekdayNamesQuechuaEcuador = []string{\"inti\", \"awaki\", \"wanra\", \"chillay\", \"kullka\", \"chaska\", \"wakma\"}\n\t// weekdayNamesQuechuaEcuadorAbbr list the weekday name abbreviations in the\n\t// Quechua Ecuador.\n\tweekdayNamesQuechuaEcuadorAbbr = []string{\"int\", \"awk\", \"wan\", \"chy\", \"kuk\", \"cha\", \"wak\"}\n\t// weekdayNamesQuechuaPeru list the weekday name abbreviations in the\n\t// Quechua Peru.\n\tweekdayNamesQuechuaPeru = []string{\"Domingo\", \"Lunes\", \"Martes\", \"Miércoles\", \"Jueves\", \"Viernes\", \"Sábado\"}\n\t// weekdayNamesQuechuaPeruAbbr list the weekday name abbreviations in the\n\t// Quechua Peru.\n\tweekdayNamesQuechuaPeruAbbr = []string{\"Dom\", \"Lun\", \"Mar\", \"Mié\", \"Jue\", \"Vie\", \"Sab\"}\n\t// weekdayNamesRomanian list the weekday name abbreviations in the Romanian.\n\tweekdayNamesRomanian = []string{\"duminică\", \"luni\", \"marți\", \"miercuri\", \"joi\", \"vineri\", \"sâmbătă\"}\n\t// weekdayNamesRomanianAbbr list the weekday name abbreviations in the\n\t// Romanian.\n\tweekdayNamesRomanianAbbr = []string{\"dum.\", \"lun.\", \"mar.\", \"mie.\", \"joi\", \"vin.\", \"sâm.\"}\n\t// weekdayNamesRomanianMoldovaAbbr list the weekday name abbreviations in\n\t// the Romanian Moldova.\n\tweekdayNamesRomanianMoldovaAbbr = []string{\"Du\", \"Lu\", \"Mar\", \"Mie\", \"Jo\", \"Vi\", \"Sâ\"}\n\t// weekdayNamesRomansh list the weekday name abbreviations in the Romansh.\n\tweekdayNamesRomansh = []string{\"dumengia\", \"glindesdi\", \"mardi\", \"mesemna\", \"gievgia\", \"venderdi\", \"sonda\"}\n\t// weekdayNamesRomanshAbbr list the weekday name abbreviations in the\n\t// Romansh.\n\tweekdayNamesRomanshAbbr = []string{\"du\", \"gli\", \"ma\", \"me\", \"gie\", \"ve\", \"so\"}\n\t// weekdayNamesRussian list the weekday name abbreviations in the Russian.\n\tweekdayNamesRussian = []string{\n\t\t\"\\u0432\\u043E\\u0441\\u043A\\u0440\\u0435\\u0441\\u0435\\u043D\\u044C\\u0435\",\n\t\t\"\\u043F\\u043E\\u043D\\u0435\\u0434\\u0435\\u043B\\u044C\\u043D\\u0438\\u043A\",\n\t\t\"\\u0432\\u0442\\u043E\\u0440\\u043D\\u0438\\u043A\",\n\t\t\"\\u0441\\u0440\\u0435\\u0434\\u0430\",\n\t\t\"\\u0447\\u0435\\u0442\\u0432\\u0435\\u0440\\u0433\",\n\t\t\"\\u043F\\u044F\\u0442\\u043D\\u0438\\u0446\\u0430\",\n\t\t\"\\u0441\\u0443\\u0431\\u0431\\u043E\\u0442\\u0430\",\n\t}\n\t// weekdayNamesRussianAbbr list the weekday name abbreviations in the\n\t// Russian.\n\tweekdayNamesRussianAbbr = []string{\n\t\t\"\\u0412\\u0441\",\n\t\t\"\\u041F\\u043D\",\n\t\t\"\\u0412\\u0442\",\n\t\t\"\\u0421\\u0440\",\n\t\t\"\\u0427\\u0442\",\n\t\t\"\\u041F\\u0442\",\n\t\t\"\\u0421\\u0431\",\n\t}\n\t// weekdayNamesSakha list the weekday name abbreviations in the Sakha.\n\tweekdayNamesSakha = []string{\n\t\t\"\\u04E8\\u0440\\u04E9\\u0431\\u04AF\\u043B\",\n\t\t\"\\u044D\\u043D\\u0438\\u0434\\u0438\\u044D\\u043D\\u043D\\u044C\\u0438\\u043A\",\n\t\t\"\\u041E\\u043F\\u0442\\u0443\\u043E\\u0440\\u0443\\u043D\\u043D\\u044C\\u0443\\u043A\",\n\t\t\"\\u0421\\u044D\\u0440\\u044D\\u0434\\u044D\\u044D\",\n\t\t\"\\u0427\\u044D\\u043F\\u043F\\u0438\\u044D\\u0440\",\n\t\t\"\\u0411\\u044D\\u044D\\u0442\\u0438\\u043D\\u0441\\u044D\",\n\t\t\"\\u0421\\u0443\\u0431\\u0443\\u043E\\u0442\\u0430\",\n\t}\n\t// weekdayNamesSakhaAbbr list the weekday name abbreviations in the Sakha.\n\tweekdayNamesSakhaAbbr = []string{\n\t\t\"\\u04E8\\u0440\",\n\t\t\"\\u0431\\u043D\",\n\t\t\"\\u043E\\u043F\",\n\t\t\"\\u0441\\u044D\",\n\t\t\"\\u0447\\u043F\",\n\t\t\"\\u0431\\u044D\",\n\t\t\"\\u0441\\u0431\",\n\t}\n\t// weekdayNamesSami list the weekday name abbreviations in the Sami.\n\tweekdayNamesSami = []string{\"pasepeivi\", \"vuossargâ\", \"majebargâ\", \"koskokko\", \"tuorâstâh\", \"vástuppeivi\", \"lávurdâh\"}\n\t// weekdayNamesSamiAbbr list the weekday name abbreviations in the Sami.\n\tweekdayNamesSamiAbbr = []string{\"pas\", \"vuo\", \"maj\", \"kos\", \"tuo\", \"vás\", \"láv\"}\n\t// weekdayNamesSamiSamiLule list the weekday name abbreviations in the Sami\n\t// (SamiLule).\n\tweekdayNamesSamiSamiLule = []string{\"ájllek\", \"mánnodahka\", \"dijstahka\", \"gasskavahkko\", \"duorastahka\", \"bierjjedahka\", \"lávvodahka\"}\n\t// weekdayNamesSamiSamiLuleAbbr list the weekday name abbreviations in the\n\t// Sami (SamiLule).\n\tweekdayNamesSamiSamiLuleAbbr = []string{\"ájl\", \"mán\", \"dis\", \"gas\", \"duor\", \"bier\", \"láv\"}\n\t// weekdayNamesSamiSweden list the weekday name abbreviations in the Sami\n\t// (Lule) Sweden.\n\tweekdayNamesSamiSweden = []string{\"sådnåbiejvve\", \"mánnodahka\", \"dijstahka\", \"gasskavahkko\", \"duorastahka\", \"bierjjedahka\", \"lávvodahka\"}\n\t// weekdayNamesSamiSwedenAbbr list the weekday name abbreviations in the\n\t// Sami (Lule) Sweden.\n\tweekdayNamesSamiSwedenAbbr = []string{\"såd\", \"mán\", \"dis\", \"gas\", \"duor\", \"bier\", \"láv\"}\n\t// weekdayNamesSamiNorthern list the weekday name abbreviations in the Sami\n\t// (Northern).\n\tweekdayNamesSamiNorthern = []string{\"sotnabeaivi\", \"vuossárga\", \"maŋŋebárga\", \"gaskavahkku\", \"duorasdat\t\", \"bearjadat\", \"lávvardat\"}\n\t// weekdayNamesSamiNorthernFIAbbr list the weekday name abbreviations in the\n\t// Sami (Northern).\n\tweekdayNamesSamiNorthernAbbr = []string{\"sotn\", \"vuos\", \"maŋ\", \"gask\", \"duor\", \"bear\", \"láv\"}\n\t// weekdayNamesSamiNorthernFI list the weekday name abbreviations in the\n\t// Sami (Northern) Finland.\n\tweekdayNamesSamiNorthernFI = []string{\"sotnabeaivi\", \"vuossárga\", \"maŋŋebárga\", \"gaskavahkku\", \"duorastat\", \"bearjadat\", \"lávvardat\"}\n\t// weekdayNamesSamiNorthernFIAbbr list the weekday name abbreviations in the\n\t// Sami (Northern) Finland.\n\tweekdayNamesSamiNorthernFIAbbr = []string{\"so\", \"má\", \"di\", \"ga\", \"du\", \"be\", \"lá\"}\n\t// weekdayNamesSamiNorthernSE list the weekday name abbreviations in the\n\t// Sami (Northern) Sweden.\n\tweekdayNamesSamiNorthernSE = []string{\"sotnabeaivi\", \"mánnodat\", \"disdat\", \"gaskavahkku\", \"duorastat\", \"bearjadat\", \"lávvardat\"}\n\t// weekdayNamesSamiNorthernSEAbbr list the weekday name abbreviations in the\n\t// Sami (Northern) Sweden.\n\tweekdayNamesSamiNorthernSEAbbr = []string{\"sotn\", \"mán\", \"dis\", \"gask\", \"duor\", \"bear\", \"láv\"}\n\t// weekdayNamesSamiSkolt list the weekday name abbreviations in the Sami\n\t// (Skolt).\n\tweekdayNamesSamiSkolt = []string{\"pâ\\u00B4sspei\\u00B4vv\", \"vuõssargg\", \"mââibargg\", \"seärad\", \"neljdpei\\u00B4vv\", \"piâtnâc\", \"sue\\u00B4vet\"}\n\t// weekdayNamesSamiSkoltAbbr list the weekday name abbreviations in the Sami\n\t// (Skolt).\n\tweekdayNamesSamiSkoltAbbr = []string{\"pâ\", \"vu\", \"mâ\", \"se\", \"ne\", \"pi\", \"su\"}\n\t// weekdayNamesSamiSouthern list the weekday name abbreviations in the Sami\n\t// (Southern).\n\tweekdayNamesSamiSouthern = []string{\"aejlege\", \"måanta\", \"dæjsta\", \"gaskevåhkoe\", \"duarsta\", \"bearjadahke\", \"laavvardahke\"}\n\t// weekdayNamesSamiSouthernAbbr list the weekday name abbreviations in the\n\t// Sami (Southern).\n\tweekdayNamesSamiSouthernAbbr = []string{\"aej\", \"måa\", \"dæj\", \"gask\", \"duar\", \"bearj\", \"laav\"}\n\t// weekdayNamesSanskrit list the weekday name abbreviations in the Sanskrit.\n\tweekdayNamesSanskrit = []string{\n\t\t\"\\u0930\\u0935\\u093F\\u0935\\u093E\\u0938\\u0930\\u0903\",\n\t\t\"\\u0938\\u094B\\u092E\\u0935\\u093E\\u0938\\u0930\\u0903\",\n\t\t\"\\u092E\\u0902\\u0917\\u0932\\u0935\\u093E\\u0938\\u0930\\u0903\",\n\t\t\"\\u092C\\u0941\\u0927\\u0935\\u093E\\u0938\\u0930\\u0903\",\n\t\t\"\\u0917\\u0941\\u0930\\u0941\\u0935\\u093E\\u0938\\u0930:\",\n\t\t\"\\u0936\\u0941\\u0915\\u094D\\u0930\\u0935\\u093E\\u0938\\u0930\\u0903\",\n\t\t\"\\u0936\\u0928\\u093F\\u0935\\u093E\\u0938\\u0930\\u0903\",\n\t}\n\t// weekdayNamesSanskritAbbr list the weekday name abbreviations in the\n\t// Sanskrit.\n\tweekdayNamesSanskritAbbr = []string{\n\t\t\"\\u0930\\u0935\\u093F\",\n\t\t\"\\u0938\\u094B\\u092E\",\n\t\t\"\\u092E\\u0919\\u094D\\u0917\",\n\t\t\"\\u092C\\u0941\\u0927\",\n\t\t\"\\u0917\\u0941\\u0930\\u0941\",\n\t\t\"\\u0936\\u0941\\u0915\\u094D\\u0930\",\n\t\t\"\\u0936\\u0928\\u093F\",\n\t}\n\t// weekdayNamesGaelic list the weekday name abbreviations in the Gaelic.\n\tweekdayNamesGaelic = []string{\"DiDòmhnaich\", \"DiLuain\", \"DiMàirt\", \"DiCiadain\", \"DiarDaoin\", \"DihAoine\", \"DiSathairne\"}\n\t// weekdayNamesGaelicAbbr list the weekday name abbreviations in the Gaelic.\n\tweekdayNamesGaelicAbbr = []string{\"DiD\", \"DiL\", \"DiM\", \"DiC\", \"Dia\", \"Dih\", \"DiS\"}\n\t// weekdayNamesSerbian list the weekday name abbreviations in the Serbian.\n\tweekdayNamesSerbian = []string{\n\t\t\"\\u043D\\u0435\\u0434\\u0435\\u0459\\u0430\",\n\t\t\"\\u043F\\u043E\\u043D\\u0435\\u0434\\u0435\\u0459\\u0430\\u043A\",\n\t\t\"\\u0443\\u0442\\u043E\\u0440\\u0430\\u043A\",\n\t\t\"\\u0441\\u0440\\u0435\\u0434\\u0430\",\n\t\t\"\\u0447\\u0435\\u0442\\u0432\\u0440\\u0442\\u0430\\u043A\",\n\t\t\"\\u043F\\u0435\\u0442\\u0430\\u043A\",\n\t\t\"\\u0441\\u0443\\u0431\\u043E\\u0442\\u0430\",\n\t}\n\t// weekdayNamesSerbianAbbr list the weekday name abbreviations in the\n\t// Serbian.\n\tweekdayNamesSerbianAbbr = []string{\n\t\t\"\\u043D\\u0435\\u0434.\",\n\t\t\"\\u043F\\u043E\\u043D.\",\n\t\t\"\\u0443\\u0442.\",\n\t\t\"\\u0441\\u0440.\",\n\t\t\"\\u0447\\u0435\\u0442.\",\n\t\t\"\\u043F\\u0435\\u0442.\",\n\t\t\"\\u0441\\u0443\\u0431.\",\n\t}\n\t// weekdayNamesSerbianBA list the weekday name abbreviations in the Serbian\n\t// (Cyrillic) Bosnia and Herzegovina.\n\tweekdayNamesSerbianBA = []string{\n\t\t\"\\u043D\\u0435\\u0434\\u0458\\u0435\\u0459\\u0430\",\n\t\t\"\\u043F\\u043E\\u043D\\u0435\\u0434\\u0458\\u0435\\u0459\\u0430\\u043A\",\n\t\t\"\\u0443\\u0442\\u043E\\u0440\\u0430\\u043A\",\n\t\t\"\\u0441\\u0440\\u0438\\u0458\\u0435\\u0434\\u0430\",\n\t\t\"\\u0447\\u0435\\u0442\\u0432\\u0440\\u0442\\u0430\\u043A\",\n\t\t\"\\u043F\\u0435\\u0442\\u0430\\u043A\",\n\t\t\"\\u0441\\u0443\\u0431\\u043E\\u0442\\u0430\",\n\t}\n\t// weekdayNamesSerbianBAAbbr list the weekday name abbreviations in the\n\t// Serbian (Cyrillic) Bosnia and Herzegovina.\n\tweekdayNamesSerbianBAAbbr = []string{\n\t\t\"\\u043D\\u0435\\u0434\",\n\t\t\"\\u043F\\u043E\\u043D\",\n\t\t\"\\u0443\\u0442\\u043E\",\n\t\t\"\\u0441\\u0440\\u0438\",\n\t\t\"\\u0447\\u0435\\u0442\",\n\t\t\"\\u043F\\u0435\\u0442\",\n\t\t\"\\u0441\\u0443\\u0431\",\n\t}\n\t// weekdayNamesSerbianLatin list the weekday name abbreviations in the\n\t// Serbian (Latin).\n\tweekdayNamesSerbianLatin = []string{\"nedelja\", \"ponedeljak\", \"utorak\", \"sreda\", \"četvrtak\", \"petak\", \"subota\"}\n\t// weekdayNamesSerbianLatinAbbr list the weekday name abbreviations in the\n\t// Serbian (Latin).\n\tweekdayNamesSerbianLatinAbbr = []string{\"ned\", \"pon\", \"uto\", \"sre\", \"čet\", \"pet\", \"sub\"}\n\t// weekdayNamesSerbianLatinBA list the weekday name abbreviations in the\n\t// Serbian (Latin) Bosnia and Herzegovina.\n\tweekdayNamesSerbianLatinBA = []string{\"nedjelja\", \"ponedjeljak\", \"utorak\", \"srijeda\", \"četvrtak\", \"petak\", \"subota\"}\n\t// weekdayNamesSerbianLatinBAAbbr list the weekday name abbreviations in the\n\t// Serbian (Latin) Bosnia and Herzegovina.\n\tweekdayNamesSerbianLatinBAAbbr = []string{\"ned\", \"pon\", \"uto\", \"sri\", \"čet\", \"pet\", \"sub\"}\n\t// weekdayNamesSerbianLatinCSAbbr list the weekday name abbreviations in the\n\t// Serbian (Latin) Serbia and Montenegro (Former).\n\tweekdayNamesSerbianLatinCSAbbr = []string{\"ned.\", \"pon.\", \"uto.\", \"sre.\", \"čet.\", \"pet.\", \"sub.\"}\n\t// weekdayNamesSerbianLatinME list the weekday name abbreviations in the\n\t// Serbian (Latin) Montenegro.\n\tweekdayNamesSerbianLatinME = []string{\"nedjelja\", \"ponedeljak\", \"utorak\", \"srijeda\", \"četvrtak\", \"petak\", \"subota\"}\n\t// weekdayNamesSerbianME list the weekday name abbreviations in the Serbian\n\t// (Cyrillic) Montenegro.\n\tweekdayNamesSerbianME = []string{\n\t\t\"\\u043D\\u0435\\u0434\\u0435\\u0459\\u0430\",\n\t\t\"\\u043F\\u043E\\u043D\\u0435\\u0434\\u0458\\u0435\\u0459\\u0430\\u043A\",\n\t\t\"\\u0443\\u0442\\u043E\\u0440\\u0430\\u043A\",\n\t\t\"\\u0441\\u0440\\u0438\\u0458\\u0435\\u0434\\u0430\",\n\t\t\"\\u0447\\u0435\\u0442\\u0432\\u0440\\u0442\\u0430\\u043A\",\n\t\t\"\\u043F\\u0435\\u0442\\u0430\\u043A\",\n\t\t\"\\u0441\\u0443\\u0431\\u043E\\u0442\\u0430\",\n\t}\n\t// weekdayNamesSesothoSaLeboa list the weekday name abbreviations in the\n\t// Sesotho sa Leboa.\n\tweekdayNamesSesothoSaLeboa = []string{\"Lamorena\", \"Musopologo\", \"Labobedi\", \"Laboraro\", \"Labone\", \"Labohlano\", \"Mokibelo\"}\n\t// weekdayNamesSesothoSaLeboaAbbr list the weekday name abbreviations in the\n\t// Sesotho sa Leboa.\n\tweekdayNamesSesothoSaLeboaAbbr = []string{\"Lam\", \"Moš\", \"Lbb\", \"Lbr\", \"Lbn\", \"Lbh\", \"Mok\"}\n\t// weekdayNamesSetswana list the weekday name abbreviations in the Setswana.\n\tweekdayNamesSetswana = []string{\"Sontaga\", \"Mosopulogo\", \"Labobedi\", \"Laboraro\", \"Labone\", \"Labotlhano\", \"Matlhatso\"}\n\t// weekdayNamesSetswanaAbbr list the weekday name abbreviations in the\n\t// Setswana.\n\tweekdayNamesSetswanaAbbr = []string{\"Sont.\", \"Mos.\", \"Lab.\", \"Labr.\", \"Labn.\", \"Labt.\", \"Matlh.\"}\n\t// weekdayNamesSindhi list the weekday name abbreviations in the Sindhi.\n\tweekdayNamesSindhi = []string{\n\t\t\"\\u0633\\u0648\\u0645\\u0631\",\n\t\t\"\\u0627\\u06B1\\u0627\\u0631\\u0648\",\n\t\t\"\\u0627\\u0631\\u0628\\u0639\",\n\t\t\"\\u062E\\u0645\\u064A\\u0633\",\n\t\t\"\\u062C\\u0645\\u0639\\u0648\",\n\t\t\"\\u0687\\u0646\\u0687\\u0631\",\n\t\t\"\\u0622\\u0686\\u0631\",\n\t}\n\t// weekdayNamesSindhiAbbr list the weekday name abbreviations in the Sindhi.\n\tweekdayNamesSindhiAbbr = []string{\n\t\t\"\\u0633\\u0648\",\n\t\t\"\\u0627\\u06B1\",\n\t\t\"\\u0627\\u0631\",\n\t\t\"\\u062E\\u0645\",\n\t\t\"\\u062C\\u0645\\u0639\\u0648\",\n\t\t\"\\u0687\\u0646\",\n\t\t\"\\u0622\\u0686\",\n\t}\n\t// weekdayNamesSlovak list the weekday name abbreviations in the Slovak.\n\tweekdayNamesSlovak = []string{\"nedeľa\", \"pondelok\", \"utorok\", \"streda\", \"štvrtok\", \"piatok\", \"sobota\"}\n\t// weekdayNamesSlovakAbbr list the weekday name abbreviations in the Slovak.\n\tweekdayNamesSlovakAbbr = []string{\"ne\", \"po\", \"ut\", \"st\", \"št\", \"pi\", \"so\"}\n\t// weekdayNamesSlovenian list the weekday name abbreviations in the\n\t// Slovenian.\n\tweekdayNamesSlovenian = []string{\"nedelja\", \"ponedeljek\", \"torek\", \"sreda\", \"četrtek\", \"petek\", \"sobota\"}\n\t// weekdayNamesSlovenianAbbr list the weekday name abbreviations in the\n\t// Slovenian.\n\tweekdayNamesSlovenianAbbr = []string{\"ned.\", \"pon.\", \"tor.\", \"sre.\", \"čet.\", \"pet.\", \"sob.\"}\n\t// weekdayNamesSomali list the weekday name abbreviations in the Somali.\n\tweekdayNamesSomali = []string{\"Axad\", \"Isniin\", \"Talaado\", \"Arbaco\", \"Khamiis\", \"Jimco\", \"Sabti\"}\n\t// weekdayNamesSomaliAbbr list the weekday name abbreviations in the Somali.\n\tweekdayNamesSomaliAbbr = []string{\"Axd\", \"Isn\", \"Tldo\", \"Arbc\", \"Khms\", \"Jmc\", \"Sbti\"}\n\t// weekdayNamesSotho list the weekday name abbreviations in the Sotho.\n\tweekdayNamesSotho = []string{\"Sontaha\", \"Mmantaha\", \"Labobedi\", \"Laboraru\", \"Labone\", \"Labohlane\", \"Moqebelo\"}\n\t// weekdayNamesSothoAbbr list the weekday name abbreviations in the Sotho.\n\tweekdayNamesSothoAbbr = []string{\"Son\", \"Mma\", \"Bed\", \"Rar\", \"Ne\", \"Hla\", \"Moq\"}\n\t// weekdayNamesSpanish list the weekday name abbreviations in the Spanish.\n\tweekdayNamesSpanish = []string{\"domingo\", \"lunes\", \"martes\", \"miércoles\", \"jueves\", \"viernes\", \"sábado\"}\n\t// weekdayNamesSpanishAbbr list the weekday name abbreviations in the\n\t// Spanish Argentina.\n\tweekdayNamesSpanishAbbr = []string{\"do.\", \"lu.\", \"ma.\", \"mi.\", \"ju.\", \"vi.\", \"sá.\"}\n\t// weekdayNamesSpanishARAbbr list the weekday name abbreviations in the\n\t// Spanish Argentina.\n\tweekdayNamesSpanishARAbbr = []string{\"dom.\", \"lun.\", \"mar.\", \"mié.\", \"jue.\", \"vie.\", \"sáb.\"}\n\t// weekdayNamesSpanishUSAbbr list the weekday name abbreviations in the\n\t// Spanish United States.\n\tweekdayNamesSpanishUSAbbr = []string{\"dom\", \"lun\", \"mar\", \"mié\", \"jue\", \"vie\", \"sáb\"}\n\t// weekdayNamesSwedish list the weekday name abbreviations in the Swedish.\n\tweekdayNamesSwedish = []string{\"söndag\", \"måndag\", \"tisdag\", \"onsdag\", \"torsdag\", \"fredag\", \"lördag\"}\n\t// weekdayNamesSwedishAbbr list the weekday name abbreviations in the\n\t// Swedish Argentina.\n\tweekdayNamesSwedishAbbr = []string{\"sön\", \"mån\", \"tis\", \"ons\", \"tor\", \"fre\", \"lör\"}\n\t// weekdayNamesSyriac list the weekday name abbreviations in the Syriac.\n\tweekdayNamesSyriac = []string{\n\t\t\"\\u071A\\u0715 \\u0712\\u072B\\u0712\\u0710\",\n\t\t\"\\u072C\\u072A\\u071D\\u0722 \\u0712\\u072B\\u0712\\u0710\",\n\t\t\"\\u072C\\u0720\\u072C\\u0710 \\u0712\\u072B\\u0712\\u0710\",\n\t\t\"\\u0710\\u072A\\u0712\\u0725\\u0710 \\u0712\\u072B\\u0712\\u0710\",\n\t\t\"\\u071A\\u0721\\u072B\\u0710 \\u0712\\u072B\\u0712\\u0710\",\n\t\t\"\\u0725\\u072A\\u0718\\u0712\\u072C\\u0710\",\n\t\t\"\\u072B\\u0712\\u072C\\u0710\",\n\t}\n\t// weekdayNamesSyriacAbbr list the weekday name abbreviations in the Syriac.\n\tweekdayNamesSyriacAbbr = []string{\n\t\t\"\\u070F\\u0710 \\u070F\\u0712\\u072B\",\n\t\t\"\\u070F\\u0712 \\u070F\\u0712\\u072B\",\n\t\t\"\\u070F\\u0713 \\u070F\\u0712\\u072B\",\n\t\t\"\\u070F\\u0715 \\u070F\\u0712\\u072B\",\n\t\t\"\\u070F\\u0717 \\u070F\\u0712\\u072B\",\n\t\t\"\\u070F\\u0725\\u072A\\u0718\\u0712\",\n\t\t\"\\u070F\\u072B\\u0712\",\n\t}\n\t// weekdayNamesTajik list the weekday name abbreviations in the Tajik.\n\tweekdayNamesTajik = []string{\n\t\t\"\\u042F\\u043A\\u0448\\u0430\\u043D\\u0431\\u0435\",\n\t\t\"\\u0434\\u0443\\u0448\\u0430\\u043D\\u0431\\u0435\",\n\t\t\"\\u0441\\u0435\\u0448\\u0430\\u043D\\u0431\\u0435\",\n\t\t\"\\u0447\\u043E\\u0440\\u0448\\u0430\\u043D\\u0431\\u0435\",\n\t\t\"\\u043F\\u0430\\u043D\\u04B7\\u0448\\u0430\\u043D\\u0431\\u0435\",\n\t\t\"\\u04B7\\u0443\\u043C\\u044A\\u0430\",\n\t\t\"\\u0448\\u0430\\u043D\\u0431\\u0435\",\n\t}\n\t// weekdayNamesTajikAbbr list the weekday name abbreviations in the Tajik.\n\tweekdayNamesTajikAbbr = []string{\n\t\t\"\\u043F\\u043A\\u0448\",\n\t\t\"\\u0434\\u0448\\u0431\",\n\t\t\"\\u0441\\u0448\\u0431\",\n\t\t\"\\u0447\\u0448\\u0431\",\n\t\t\"\\u043F\\u0448\\u0431\",\n\t\t\"\\u04B7\\u0443\\u043C\",\n\t\t\"\\u0448\\u043D\\u0431\",\n\t}\n\t// weekdayNamesTamazight list the weekday name abbreviations in the\n\t// Tamazight.\n\tweekdayNamesTamazight = []string{\"lh'ed\", \"letnayen\", \"ttlata\", \"larebâa\", \"lexmis\", \"ldjemâa\", \"ssebt\"}\n\t// weekdayNamesTamazightAbbr list the weekday name abbreviations in the\n\t// Tamazight Argentina.\n\tweekdayNamesTamazightAbbr = []string{\"lh'd\", \"let\", \"ttl\", \"lar\", \"lex\", \"ldj\", \"sse\"}\n\t// weekdayNamesTamil list the weekday name abbreviations in the Tamil.\n\tweekdayNamesTamil = []string{\n\t\t\"\\u0B9E\\u0BBE\\u0BAF\\u0BBF\\u0BB1\\u0BCD\\u0BB1\\u0BC1\\u0B95\\u0BCD\\u0B95\\u0BBF\\u0BB4\\u0BAE\\u0BC8\",\n\t\t\"\\u0BA4\\u0BBF\\u0B99\\u0BCD\\u0B95\\u0BB3\\u0BCD\\u0B95\\u0BBF\\u0BB4\\u0BAE\\u0BC8\",\n\t\t\"\\u0B9A\\u0BC6\\u0BB5\\u0BCD\\u0BB5\\u0BBE\\u0BAF\\u0BCD\\u0B95\\u0BCD\\u0B95\\u0BBF\\u0BB4\\u0BAE\\u0BC8\",\n\t\t\"\\u0BAA\\u0BC1\\u0BA4\\u0BA9\\u0BCD\\u0B95\\u0BBF\\u0BB4\\u0BAE\\u0BC8\",\n\t\t\"\\u0BB5\\u0BBF\\u0BAF\\u0BBE\\u0BB4\\u0B95\\u0BCD\\u0B95\\u0BBF\\u0BB4\\u0BAE\\u0BC8\",\n\t\t\"\\u0BB5\\u0BC6\\u0BB3\\u0BCD\\u0BB3\\u0BBF\\u0B95\\u0BCD\\u0B95\\u0BBF\\u0BB4\\u0BAE\\u0BC8\",\n\t\t\"\\u0B9A\\u0BA9\\u0BBF\\u0B95\\u0BCD\\u0B95\\u0BBF\\u0BB4\\u0BAE\\u0BC8\",\n\t}\n\t// weekdayNamesTamilAbbr list the weekday name abbreviations in the Tamil.\n\tweekdayNamesTamilAbbr = []string{\n\t\t\"\\u0B9E\\u0BBE\\u0BAF\\u0BBF\\u0BB1\\u0BC1\",\n\t\t\"\\u0BA4\\u0BBF\\u0B99\\u0BCD\\u0B95\\u0BB3\\u0BCD\",\n\t\t\"\\u0B9A\\u0BC6\\u0BB5\\u0BCD\\u0BB5\\u0BBE\\u0BAF\\u0BCD\",\n\t\t\"\\u0BAA\\u0BC1\\u0BA4\\u0BA9\\u0BCD\",\n\t\t\"\\u0BB5\\u0BBF\\u0BAF\\u0BBE\\u0BB4\\u0BA9\\u0BCD\",\n\t\t\"\\u0BB5\\u0BC6\\u0BB3\\u0BCD\\u0BB3\\u0BBF\",\n\t\t\"\\u0B9A\\u0BA9\\u0BBF\",\n\t}\n\t// weekdayNamesTamilLK list the weekday name abbreviations in the Tamil Sri\n\t// Lanka.\n\tweekdayNamesTamilLK = []string{\n\t\t\"\\u0B9E\\u0BBE\\u0BAF\\u0BBF\\u0BB1\\u0BC1\",\n\t\t\"\\u0BA4\\u0BBF\\u0B99\\u0BCD\\u0B95\\u0BB3\\u0BCD\",\n\t\t\"\\u0B9A\\u0BC6\\u0BB5\\u0BCD\\u0BB5\\u0BBE\\u0BAF\\u0BCD\",\n\t\t\"\\u0BAA\\u0BC1\\u0BA4\\u0BA9\\u0BCD\",\n\t\t\"\\u0BB5\\u0BBF\\u0BAF\\u0BBE\\u0BB4\\u0BA9\\u0BCD\",\n\t\t\"\\u0BB5\\u0BC6\\u0BB3\\u0BCD\\u0BB3\\u0BBF\",\n\t\t\"\\u0B9A\\u0BA9\\u0BBF\",\n\t}\n\t// weekdayNamesTamilLKAbbr list the weekday name abbreviations in the Tamil\n\t// Sri Lanka.\n\tweekdayNamesTamilLKAbbr = []string{\n\t\t\"\\u0B9E\\u0BBE\\u0BAF\\u0BBF.\",\n\t\t\"\\u0BA4\\u0BBF\\u0B99\\u0BCD.\",\n\t\t\"\\u0B9A\\u0BC6\\u0BB5\\u0BCD.\",\n\t\t\"\\u0BAA\\u0BC1\\u0BA4.\",\n\t\t\"\\u0BB5\\u0BBF\\u0BAF\\u0BBE.\",\n\t\t\"\\u0BB5\\u0BC6\\u0BB3\\u0BCD.\",\n\t\t\"\\u0B9A\\u0BA9\\u0BBF\",\n\t}\n\t// weekdayNamesTatar list the weekday name abbreviations in the Tatar.\n\tweekdayNamesTatar = []string{\n\t\t\"\\u044F\\u043A\\u0448\\u04D9\\u043C\\u0431\\u0435\",\n\t\t\"\\u0434\\u04AF\\u0448\\u04D9\\u043C\\u0431\\u0435\",\n\t\t\"\\u0441\\u0438\\u0448\\u04D9\\u043C\\u0431\\u0435\",\n\t\t\"\\u0447\\u04D9\\u0440\\u0448\\u04D9\\u043C\\u0431\\u0435\",\n\t\t\"\\u043F\\u04D9\\u043D\\u0497\\u0435\\u0448\\u04D9\\u043C\\u0431\\u0435\",\n\t\t\"\\u0497\\u043E\\u043C\\u0433\\u0430\",\n\t\t\"\\u0448\\u0438\\u043C\\u0431\\u04D9\",\n\t}\n\t// weekdayNamesTatarAbbr list the weekday name abbreviations in the Tatar.\n\tweekdayNamesTatarAbbr = []string{\n\t\t\"\\u044F\\u043A\\u0448.\",\n\t\t\"\\u0434\\u04AF\\u0448.\",\n\t\t\"\\u0441\\u0438\\u0448.\",\n\t\t\"\\u0447\\u04D9\\u0440\\u0448.\",\n\t\t\"\\u043F\\u04D9\\u043D\\u0497.\",\n\t\t\"\\u0497\\u043E\\u043C.\",\n\t\t\"\\u0448\\u0438\\u043C.\",\n\t}\n\t// weekdayNamesTelugu list the weekday name abbreviations in the Telugu.\n\tweekdayNamesTelugu = []string{\n\t\t\"\\u0C06\\u0C26\\u0C3F\\u0C35\\u0C3E\\u0C30\\u0C02\",\n\t\t\"\\u0C38\\u0C4B\\u0C2E\\u0C35\\u0C3E\\u0C30\\u0C02\",\n\t\t\"\\u0C2E\\u0C02\\u0C17\\u0C33\\u0C35\\u0C3E\\u0C30\\u0C02\",\n\t\t\"\\u0C2C\\u0C41\\u0C27\\u0C35\\u0C3E\\u0C30\\u0C02\",\n\t\t\"\\u0C17\\u0C41\\u0C30\\u0C41\\u0C35\\u0C3E\\u0C30\\u0C02\",\n\t\t\"\\u0C36\\u0C41\\u0C15\\u0C4D\\u0C30\\u0C35\\u0C3E\\u0C30\\u0C02\",\n\t\t\"\\u0C36\\u0C28\\u0C3F\\u0C35\\u0C3E\\u0C30\\u0C02\",\n\t}\n\t// weekdayNamesTeluguAbbr list the weekday name abbreviations in the Telugu.\n\tweekdayNamesTeluguAbbr = []string{\n\t\t\"\\u0C06\\u0C26\\u0C3F\",\n\t\t\"\\u0C38\\u0C4B\\u0C2E\",\n\t\t\"\\u0C2E\\u0C02\\u0C17\\u0C33\",\n\t\t\"\\u0C2C\\u0C41\\u0C27\",\n\t\t\"\\u0C17\\u0C41\\u0C30\\u0C41\",\n\t\t\"\\u0C36\\u0C41\\u0C15\\u0C4D\\u0C30\",\n\t\t\"\\u0C36\\u0C28\\u0C3F\",\n\t}\n\t// weekdayNamesThai list the weekday name abbreviations in the Thai.\n\tweekdayNamesThai = []string{\n\t\t\"\\u0E2D\\u0E32\\u0E17\\u0E34\\u0E15\\u0E22\\u0E4C\",\n\t\t\"\\u0E08\\u0E31\\u0E19\\u0E17\\u0E23\\u0E4C\",\n\t\t\"\\u0E2D\\u0E31\\u0E07\\u0E04\\u0E32\\u0E23\",\n\t\t\"\\u0E1E\\u0E38\\u0E18\",\n\t\t\"\\u0E1E\\u0E24\\u0E2B\\u0E31\\u0E2A\\u0E1A\\u0E14\\u0E35\",\n\t\t\"\\u0E28\\u0E38\\u0E01\\u0E23\\u0E4C\",\n\t\t\"\\u0E40\\u0E2A\\u0E32\\u0E23\\u0E4C\",\n\t}\n\t// weekdayNamesThaiAbbr list the weekday name abbreviations in the Thai.\n\tweekdayNamesThaiAbbr = []string{\n\t\t\"\\u0E2D\\u0E32.\",\n\t\t\"\\u0E08.\",\n\t\t\"\\u0E2D.\",\n\t\t\"\\u0E1E.\",\n\t\t\"\\u0E1E\\u0E24.\",\n\t\t\"\\u0E28.\",\n\t\t\"\\u0E2A.\",\n\t}\n\t// weekdayNamesTibetan list the weekday name abbreviations in the Tibetan.\n\tweekdayNamesTibetan = []string{\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F49\\u0F72\\u0F0B\\u0F58\\u0F0D\",\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0D\",\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F58\\u0F72\\u0F42\\u0F0B\\u0F51\\u0F58\\u0F62\\u0F0D\",\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F63\\u0FB7\\u0F42\\u0F0B\\u0F54\\u0F0D\",\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F55\\u0F74\\u0F62\\u0F0B\\u0F56\\u0F74\\u0F0D\",\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F54\\u0F0B\\u0F66\\u0F44\\u0F66\\u0F0D\",\n\t\t\"\\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F66\\u0FA4\\u0F7A\\u0F53\\u0F0B\\u0F54\\u0F0D\",\n\t}\n\t// weekdayNamesTibetanAbbr list the weekday name abbreviations in the\n\t// Tibetan.\n\tweekdayNamesTibetanAbbr = []string{\n\t\t\"\\u0F49\\u0F72\\u0F0B\\u0F58\\u0F0D\",\n\t\t\"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0D\",\n\t\t\"\\u0F58\\u0F72\\u0F42\\u0F0B\\u0F51\\u0F58\\u0F62\\u0F0D\",\n\t\t\"\\u0F63\\u0FB7\\u0F42\\u0F0B\\u0F54\\u0F0D\",\n\t\t\"\\u0F55\\u0F74\\u0F62\\u0F0B\\u0F56\\u0F74\\u0F0D\",\n\t\t\"\\u0F54\\u0F0B\\u0F66\\u0F44\\u0F66\\u0F0D\",\n\t\t\"\\u0F66\\u0FA4\\u0F7A\\u0F53\\u0F0B\\u0F54\\u0F0D\",\n\t}\n\t// weekdayNamesTigrinya list the weekday name abbreviations in the Tigrinya.\n\tweekdayNamesTigrinya = []string{\n\t\t\"\\u1230\\u1295\\u1260\\u1275\",\n\t\t\"\\u1230\\u1291\\u12ED\",\n\t\t\"\\u1220\\u1209\\u1235\",\n\t\t\"\\u1228\\u1261\\u12D5\",\n\t\t\"\\u1283\\u1219\\u1235\",\n\t\t\"\\u12D3\\u122D\\u1262\",\n\t\t\"\\u1240\\u12F3\\u121D\",\n\t}\n\t// weekdayNamesTigrinyaAbbr list the weekday name abbreviations in the\n\t// Tigrinya.\n\tweekdayNamesTigrinyaAbbr = []string{\n\t\t\"\\u1230\\u1295\",\n\t\t\"\\u1230\\u1291\",\n\t\t\"\\u1230\\u1209\",\n\t\t\"\\u1228\\u1261\",\n\t\t\"\\u1213\\u1219\",\n\t\t\"\\u12D3\\u122D\",\n\t\t\"\\u1240\\u12F3\",\n\t}\n\t// weekdayNamesTsonga list the weekday name abbreviations in the Tsonga.\n\tweekdayNamesTsonga = []string{\"Sonta\", \"Musumbhunuku\", \"Ravumbirhi\", \"Ravunharhu\", \"Ravumune\", \"Ravuntlhanu\", \"Mugqivela\"}\n\t// weekdayNamesTsongaAbbr list the weekday name abbreviations in the Tsonga.\n\tweekdayNamesTsongaAbbr = []string{\"Son\", \"Mus\", \"Bir\", \"Har\", \"Ne\", \"Tlh\", \"Mug\"}\n\t// weekdayNamesTurkish list the weekday name abbreviations in the Turkish.\n\tweekdayNamesTurkish = []string{\"Pazar\", \"Pazartesi\", \"Sal\\u0131\", \"Çarşamba\", \"Perşembe\", \"Cuma\", \"Cumartesi\"}\n\t// weekdayNamesTurkishAbbr list the weekday name abbreviations in the\n\t// Turkish.\n\tweekdayNamesTurkishAbbr = []string{\"Paz\", \"Pzt\", \"Sal\", \"Çar\", \"Per\", \"Cum\", \"Cmt\"}\n\t// weekdayNamesTurkmen list the weekday name abbreviations in the Turkmen.\n\tweekdayNamesTurkmen = []string{\"Ýekşenbe\", \"Duşenbe\", \"Sişenbe\", \"Çarşenbe\", \"Penşenbe\", \"Anna\", \"Şenbe\"}\n\t// weekdayNamesTurkmenAbbr list the weekday name abbreviations in the\n\t// Turkmen.\n\tweekdayNamesTurkmenAbbr = []string{\"Ýb\", \"Db\", \"Sb\", \"Çb\", \"Pb\", \"An\", \"Şb\"}\n\t// weekdayNamesUkrainian list the weekday name abbreviations in the\n\t// Ukrainian.\n\tweekdayNamesUkrainian = []string{\n\t\t\"\\u043D\\u0435\\u0434\\u0456\\u043B\\u044F\",\n\t\t\"\\u043F\\u043E\\u043D\\u0435\\u0434\\u0456\\u043B\\u043E\\u043A\",\n\t\t\"\\u0432\\u0456\\u0432\\u0442\\u043E\\u0440\\u043E\\u043A\",\n\t\t\"\\u0441\\u0435\\u0440\\u0435\\u0434\\u0430\",\n\t\t\"\\u0447\\u0435\\u0442\\u0432\\u0435\\u0440\",\n\t\t\"\\u043F'\\u044F\\u0442\\u043D\\u0438\\u0446\\u044F\",\n\t\t\"\\u0441\\u0443\\u0431\\u043E\\u0442\\u0430\",\n\t}\n\t// weekdayNamesUkrainianAbbr list the weekday name abbreviations in the\n\t// Ukrainian.\n\tweekdayNamesUkrainianAbbr = []string{\n\t\t\"\\u041D\\u0434\",\n\t\t\"\\u041F\\u043D\",\n\t\t\"\\u0412\\u0442\",\n\t\t\"\\u0421\\u0440\",\n\t\t\"\\u0427\\u0442\",\n\t\t\"\\u041F\\u0442\",\n\t\t\"\\u0421\\u0431\",\n\t}\n\t// weekdayNamesSorbian list the weekday name abbreviations in the Sorbian.\n\tweekdayNamesSorbian = []string{\"njedźela\", \"póndźela\", \"wutora\", \"srjeda\", \"štwórtk\", \"pjatk\", \"sobota\"}\n\t// weekdayNamesSorbianAbbr list the weekday name abbreviations in the\n\t// Sorbian.\n\tweekdayNamesSorbianAbbr = []string{\"nje\", \"pón\", \"wut\", \"srj\", \"štw\", \"pja\", \"sob\"}\n\t// weekdayNamesUrdu list the weekday name abbreviations in the Urdu.\n\tweekdayNamesUrdu = []string{\n\t\t\"\\u0627\\u062A\\u0648\\u0627\\u0631\",\n\t\t\"\\u067E\\u064A\\u0631\",\n\t\t\"\\u0645\\u0646\\u06AF\\u0644\",\n\t\t\"\\u0628\\u062F\\u06BE\",\n\t\t\"\\u062C\\u0645\\u0639\\u0631\\u0627\\u062A\",\n\t\t\"\\u062C\\u0645\\u0639\\u0647\",\n\t\t\"\\u0647\\u0641\\u062A\\u0647\",\n\t}\n\t// weekdayNamesUrduIN list the weekday name abbreviations in the Urdu India.\n\tweekdayNamesUrduIN = []string{\n\t\t\"\\u0627\\u062A\\u0648\\u0627\\u0631\",\n\t\t\"\\u067E\\u06CC\\u0631\",\n\t\t\"\\u0645\\u0646\\u06AF\\u0644\",\n\t\t\"\\u0628\\u062F\\u06BE\",\n\t\t\"\\u062C\\u0645\\u0639\\u0631\\u0627\\u062A\",\n\t\t\"\\u062C\\u0645\\u0639\\u06C1\",\n\t\t\"\\u06C1\\u0641\\u062A\\u06C1\",\n\t}\n\t// weekdayNamesUyghur list the weekday name abbreviations in the Uyghur.\n\tweekdayNamesUyghur = []string{\n\t\t\"\\u064A\\u06D5\\u0643\\u0634\\u06D5\\u0646\\u0628\\u06D5\",\n\t\t\"\\u062F\\u06C8\\u0634\\u06D5\\u0646\\u0628\\u06D5\",\n\t\t\"\\u0633\\u06D5\\u064A\\u0634\\u06D5\\u0646\\u0628\\u06D5\",\n\t\t\"\\u0686\\u0627\\u0631\\u0634\\u06D5\\u0646\\u0628\\u06D5\",\n\t\t\"\\u067E\\u06D5\\u064A\\u0634\\u06D5\\u0646\\u0628\\u06D5\",\n\t\t\"\\u062C\\u06C8\\u0645\\u06D5\",\n\t\t\"\\u0634\\u06D5\\u0646\\u0628\\u06D5\",\n\t}\n\t// weekdayNamesUyghurAbbr list the weekday name abbreviations in the Uyghur.\n\tweekdayNamesUyghurAbbr = []string{\n\t\t\"\\u064A\\u06D5\",\n\t\t\"\\u062F\\u06C8\",\n\t\t\"\\u0633\\u06D5\",\n\t\t\"\\u0686\\u0627\",\n\t\t\"\\u067E\\u06D5\",\n\t\t\"\\u062C\\u06C8\",\n\t\t\"\\u0634\\u06D5\",\n\t}\n\t// weekdayNamesUzbekCyrillic list the weekday name abbreviations in the\n\t// Uzbek Cyrillic.\n\tweekdayNamesUzbekCyrillic = []string{\n\t\t\"\\u044F\\u043A\\u0448\\u0430\\u043D\\u0431\\u0430\",\n\t\t\"\\u0434\\u0443\\u0448\\u0430\\u043D\\u0431\\u0430\",\n\t\t\"\\u0441\\u0435\\u0448\\u0430\\u043D\\u0431\\u0430\",\n\t\t\"\\u0447\\u043E\\u0440\\u0448\\u0430\\u043D\\u0431\\u0430\",\n\t\t\"\\u043F\\u0430\\u0439\\u0448\\u0430\\u043D\\u0431\\u0430\",\n\t\t\"\\u0436\\u0443\\u043C\\u0430\",\n\t\t\"\\u0448\\u0430\\u043D\\u0431\\u0430\",\n\t}\n\t// weekdayNamesUzbekCyrillicAbbr list the weekday name abbreviations in the\n\t// Uzbek Cyrillic.\n\tweekdayNamesUzbekCyrillicAbbr = []string{\n\t\t\"\\u044F\\u043A\\u0448\",\n\t\t\"\\u0434\\u0443\\u0448\",\n\t\t\"\\u0441\\u0435\\u0448\",\n\t\t\"\\u0447\\u043E\\u0440\",\n\t\t\"\\u043F\\u0430\\u0439\",\n\t\t\"\\u0436\\u0443\\u043C\",\n\t\t\"\\u0448\\u0430\\u043D\",\n\t}\n\t// weekdayNamesUzbek list the weekday name abbreviations in the Uzbek.\n\tweekdayNamesUzbek = []string{\"yakshanba\", \"dushanba\", \"seshanba\", \"chorshanba\", \"payshanba\", \"juma\", \"shanba\"}\n\t// weekdayNamesUzbekAbbr list the weekday name abbreviations in the Uzbek.\n\tweekdayNamesUzbekAbbr = []string{\"Yak\", \"Dush\", \"Sesh\", \"Chor\", \"Pay\", \"Jum\", \"Shan\"}\n\t// weekdayNamesValencian list the weekday name abbreviations in the\n\t// Valencian.\n\tweekdayNamesValencian = []string{\"diumenge\", \"dilluns\", \"dimarts\", \"dimecres\", \"dijous\", \"divendres\", \"dissabte\"}\n\t// weekdayNamesValencianAbbr list the weekday name abbreviations in the\n\t// Valencian.\n\tweekdayNamesValencianAbbr = []string{\"dg.\", \"dl.\", \"dt.\", \"dc.\", \"dj.\", \"dv.\", \"ds.\"}\n\t// weekdayNamesVenda list the weekday name abbreviations in the Venda.\n\tweekdayNamesVenda = []string{\"Swondaha\", \"Musumbuluwo\", \"Ḽavhuvhili\", \"Ḽavhuraru\", \"Ḽavhuṋa\", \"Ḽavhuṱanu\", \"Mugivhela\"}\n\t// weekdayNamesVendaAbbr list the weekday name abbreviations in the Venda.\n\tweekdayNamesVendaAbbr = []string{\"Swo\", \"Mus\", \"Vhi\", \"Rar\", \"Ṋa\", \"Ṱan\", \"Mug\"}\n\t// weekdayNamesVietnamese list the weekday name abbreviations in the\n\t// Vietnamese.\n\tweekdayNamesVietnamese = []string{\"Chu\\u0309 Nhâ\\u0323t\", \"Th\\u01B0\\u0301 Hai\", \"Th\\u01B0\\u0301 Ba\", \"Th\\u01B0\\u0301 T\\u01B0\", \"Th\\u01B0\\u0301 N\\u0103m\", \"Th\\u01B0\\u0301 Sa\\u0301u\", \"Th\\u01B0\\u0301 Ba\\u0309y\"}\n\t// weekdayNamesVietnameseAbbr list the weekday name abbreviations in the\n\t// Vietnamese.\n\tweekdayNamesVietnameseAbbr = []string{\"CN\", \"T2\", \"T3\", \"T4\", \"T5\", \"T6\", \"T7\"}\n\t// weekdayNamesWelsh list the weekday name abbreviations in the Welsh.\n\tweekdayNamesWelsh = []string{\"Dydd Sul\", \"Dydd Llun\", \"Dydd Mawrth\", \"Dydd Mercher\", \"Dydd Iau\", \"Dydd Gwener\", \"Dydd Sadwrn\"}\n\t// weekdayNamesWelshAbbr list the weekday name abbreviations in the Welsh.\n\tweekdayNamesWelshAbbr = []string{\"Sul\", \"Llun\", \"Maw\", \"Mer\", \"Iau\", \"Gwe\", \"Sad\"}\n\t// weekdayNamesWolof list the weekday name abbreviations in the Wolof.\n\tweekdayNamesWolof = []string{\"Dibéer\", \"Altine\", \"Talaata\", \"Àllarba\", \"Alxames\", \"Àjjuma\", \"Gaawu\"}\n\t// weekdayNamesWolofAbbr list the weekday name abbreviations in the Wolof.\n\tweekdayNamesWolofAbbr = []string{\"Dib.\", \"Alt.\", \"Tal.\", \"Àll.\", \"Alx.\", \"Àjj.\", \"Gaa.\"}\n\t// weekdayNamesXhosa list the weekday name abbreviations in the Xhosa.\n\tweekdayNamesXhosa = []string{\"Cawe\", \"Mvulo\", \"Lwesibini\", \"Lwesithathu\", \"Lwesine\", \"Lwesihlanu\", \"Mgqibelo\"}\n\t// weekdayNamesXhosaAbbr list the weekday name abbreviations in the Xhosa.\n\tweekdayNamesXhosaAbbr = []string{\"iCa.\", \"uMv.\", \"uLwesib.\", \"uLwesith.\", \"uLwesin.\", \"uLwesihl.\", \"uMgq.\"}\n\t// weekdayNamesYi list the weekday name abbreviations in the Yi.\n\tweekdayNamesYi = []string{\n\t\t\"\\uA46D\\uA18F\\uA44D\",\n\t\t\"\\uA18F\\uA282\\uA494\",\n\t\t\"\\uA18F\\uA282\\uA44D\",\n\t\t\"\\uA18F\\uA282\\uA315\",\n\t\t\"\\uA18F\\uA282\\uA1D6\",\n\t\t\"\\uA18F\\uA282\\uA26C\",\n\t\t\"\\uA18F\\uA282\\uA0D8\",\n\t}\n\t// weekdayNamesYiAbbr list the weekday name abbreviations in the Yi.\n\tweekdayNamesYiAbbr = []string{\n\t\t\"\\uA46D\\uA18F\",\n\t\t\"\\uA18F\\uA494\",\n\t\t\"\\uA18F\\uA44D\",\n\t\t\"\\uA18F\\uA315\",\n\t\t\"\\uA18F\\uA1D6\",\n\t\t\"\\uA18F\\uA26C\",\n\t\t\"\\uA18F\\uA0D8\",\n\t}\n\t// weekdayNamesYiddish list the weekday name abbreviations in the Yiddish.\n\tweekdayNamesYiddish = []string{\n\t\t\"\\u05D6\\u05D5\\u05E0\\u05D8\\u05D9\\u05E7\",\n\t\t\"\\u05DE\\u05D0\\u05B8\\u05E0\\u05D8\\u05D9\\u05E7\",\n\t\t\"\\u05D3\\u05D9\\u05E0\\u05E1\\u05D8\\u05D9\\u05E7\",\n\t\t\"\\u05DE\\u05D9\\u05D8\\u05D5\\u05D5\\u05D0\\u05DA\",\n\t\t\"\\u05D3\\u05D0\\u05E0\\u05E2\\u05E8\\u05E9\\u05D8\\u05D9\\u05E7\",\n\t\t\"\\u05E4\\u05BF\\u05E8\\u05F2\\u05B7\\u05D8\\u05D9\\u05E7\",\n\t\t\"\\u05E9\\u05D1\\u05EA\",\n\t}\n\t// weekdayNamesYiddishAbbr list the weekday name abbreviations in the\n\t// Yiddish.\n\tweekdayNamesYiddishAbbr = []string{\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D0\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D1\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D2\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D3\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D4\",\n\t\t\"\\u05D9\\u05D5\\u05DD \\u05D5\",\n\t\t\"\\u05E9\\u05D1\\u05EA\",\n\t}\n\t// weekdayNamesYoruba list the weekday name abbreviations in the Yoruba.\n\tweekdayNamesYoruba = []string{\n\t\t\"\\u1ECCj\\u1ECD\\u0301 Àìkú\",\n\t\t\"\\u1ECCj\\u1ECD\\u0301 Ajé\",\n\t\t\"\\u1ECCj\\u1ECD\\u0301 Ìs\\u1EB9\\u0301gun\",\n\t\t\"\\u1ECCj\\u1ECD\\u0301rú\",\n\t\t\"\\u1ECCj\\u1ECD\\u0301b\\u1ECD\",\n\t\t\"\\u1ECCj\\u1ECD\\u0301 \\u1EB8tì\",\n\t\t\"\\u1ECCj\\u1ECD\\u0301 Àbám\\u1EB9\\u0301ta\",\n\t}\n\t// weekdayNamesYorubaAbbr list the weekday name abbreviations in the Yoruba.\n\tweekdayNamesYorubaAbbr = []string{\"Àìk\", \"Aj\", \"Ì\\u1E63g\", \"\\u1ECCjr\", \"\\u1ECCjb\", \"\\u1EB8t\", \"Àbá\"}\n\t// weekdayNamesZulu list the weekday name abbreviations in the Zulu.\n\tweekdayNamesZulu = []string{\"ISonto\", \"UMsombuluko\", \"ULwesibili\", \"ULwesithathu\", \"ULwesine\", \"ULwesihlanu\", \"UMgqibelo\"}\n\t// weekdayNamesZuluAbbr list the weekday name abbreviations in the Zulu.\n\tweekdayNamesZuluAbbr = []string{\"Son.\", \"Mso.\", \"Bi.\", \"Tha.\", \"Ne.\", \"Hla.\", \"Mgq.\"}\n\t// apFmtAfrikaans defined the AM/PM name in the Afrikaans.\n\tapFmtAfrikaans = \"vm./nm.\"\n\t// apFmtAlbanian defined the AM/PM name in the Albanian.\n\tapFmtAlbanian = \"p.d./m.d.\"\n\t// apFmtAlsatian defined the AM/PM name in the Alsatian.\n\tapFmtAlsatian = \"vorm./nam.\"\n\t// apFmtAmharic defined the AM/PM name in the Amharic.\n\tapFmtAmharic = \"\\u1325\\u12CB\\u1275/\\u12A8\\u1230\\u12D3\\u1275\"\n\t// apFmtArabic defined the AM/PM name in the Arabic.\n\tapFmtArabic = \"\\u0635/\\u0645\"\n\t// apFmtAssamese defined the AM/PM name in the Assamese.\n\tapFmtAssamese = \"\\u09F0\\u09BE\\u09A4\\u09BF\\u09AA\\u09C1/\\u0986\\u09AC\\u09C7\\u09B2\\u09BF\"\n\t// apFmtBreton defined the AM/PM name in the Assamese.\n\tapFmtBreton = \"A.M./G.M.\"\n\t// apFmtBurmese defined the AM/PM name in the Assamese.\n\tapFmtBurmese = \"\\u1014\\u1036\\u1014\\u1000\\u103A/\\u100A\\u1014\\u1031\"\n\t// apFmtCameroon defined the AM/PM name in the Cameroon.\n\tapFmtCameroon = \"mat./soir\"\n\t// apFmtCentralKurdish defined the AM/PM name in the Central Kurdish.\n\tapFmtCentralKurdish = \"\\u067E.\\u0646/\\u062F.\\u0646\"\n\t// apFmtCuba defined the AM/PM name in the Cuba.\n\tapFmtCuba = \"a.m./p.m.\"\n\t// apFmtCzech defined the AM/PM name in the Czech.\n\tapFmtCzech = \"dop./odp.\"\n\t// apFmtDari defined the AM/PM name in the Dari.\n\tapFmtDari = \"\\u0642.\\u0638./\\u0628.\\u0638.\"\n\t// apFmtDivehi defined the AM/PM name in the Divehi.\n\tapFmtDivehi = \"\\u0789\\u0786/\\u0789\\u078A\"\n\t// apFmtDzongkha defined the AM/PM name in the Dzongkha.\n\tapFmtDzongkha = \"\\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B/\\u0F55\\u0FB1\\u0F72\\u0F0B\\u0F46\\u0F0B\"\n\t// apFmtFaroese defined the AM/PM name in the Faroese.\n\tapFmtFaroese = \"um fyr./um sein.\"\n\t// apFmtFinnish defined the AM/PM name in the Finnish.\n\tapFmtFinnish = \"ap./ip.\"\n\t// apFmtGreek defined the AM/PM name in the Greek.\n\tapFmtGreek = \"\\u03C0\\u03BC/\\u03BC\\u03BC\"\n\t// apFmtGujarati defined the AM/PM name in the Gujarati.\n\tapFmtGujarati = \"\\u0AAA\\u0AC2\\u0AB0\\u0ACD\\u0AB5 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8/\\u0A89\\u0AA4\\u0ACD\\u0AA4\\u0AB0 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8\"\n\t// apFmtHindi defined the AM/PM name in the Hindi.\n\tapFmtHindi = \"\\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928/\\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928\"\n\t// apFmtHungarian defined the AM/PM name in the Hungarian.\n\tapFmtHungarian = \"de./du.\"\n\t// apFmtIcelandic defined the AM/PM name in the Icelandic.\n\tapFmtIcelandic = \"f.h./e.h.\"\n\t// apFmtIgbo defined the AM/PM name in the Igbo.\n\tapFmtIgbo = \"A.M./P.M.\"\n\t// apFmtIrish defined the AM/PM name in the Irish.\n\tapFmtIrish = \"r.n./i.n.\"\n\t// apFmtJapanese defined the AM/PM name in the Japanese.\n\tapFmtJapanese = \"午前/午後\"\n\t// apFmtKannada defined the AM/PM name in the Kannada.\n\tapFmtKannada = \"\\u0CAA\\u0CC2\\u0CB0\\u0CCD\\u0CB5\\u0CBE\\u0CB9\\u0CCD\\u0CA8/\\u0C85\\u0CAA\\u0CB0\\u0CBE\\u0CB9\\u0CCD\\u0CA8\"\n\t// apFmtKhmer defined the AM/PM name in the Khmer.\n\tapFmtKhmer = \"\\u1796\\u17D2\\u179A\\u17B9\\u1780/\\u179B\\u17D2\\u1784\\u17B6\\u1785\"\n\t// apFmtKonkani defined the AM/PM name in the Konkani.\n\tapFmtKonkani = \"\\u092E.\\u092A\\u0942./\\u092E.\\u0928\\u0902.\"\n\t// apFmtKorean defined the AM/PM name in the Korean.\n\tapFmtKorean = \"오전/오후\"\n\t// apFmtKyrgyz defined the AM/PM name in the Kyrgyz.\n\tapFmtKyrgyz = \"\\u0442\\u04A3/\\u0442\\u043A\"\n\t// apFmtLao defined the AM/PM name in the Lao.\n\tapFmtLao = \"\\u0E81\\u0EC8\\u0EAD\\u0E99\\u0E97\\u0EC8\\u0EBD\\u0E87/\\u0EAB\\u0EBC\\u0EB1\\u0E87\\u0E97\\u0EC8\\u0EBD\\u0E87\"\n\t// apFmtLatvian defined the AM/PM name in the Latvian.\n\tapFmtLatvian = \"priekšp./pēcp.\"\n\t// apFmtLithuanian defined the AM/PM name in the Lithuanian.\n\tapFmtLithuanian = \"priešpiet/popiet\"\n\t// apFmtMacedonian defined the AM/PM name in the Macedonian.\n\tapFmtMacedonian = \"\\u043F\\u0440\\u0435\\u0442\\u043F\\u043B./\\u043F\\u043E\\u043F\\u043B.\"\n\t// apFmtMalay defined the AM/PM name in the Malay.\n\tapFmtMalay = \"PG/PTG\"\n\t// apFmtMongolian defined the AM/PM name in the Mongolian.\n\tapFmtMongolian = \"\\u04AF.\\u04E9./\\u04AF.\\u0445.\"\n\t// apFmtNigeria defined the AM/PM name in the Nigeria.\n\tapFmtNigeria = \"subaka/kikiiɗe\"\n\t// apFmtNorwegian defined the AM/PM name in the Norwegian.\n\tapFmtNorwegian = \"f.m./e.m.\"\n\t// apFmtOromo defined the AM/PM name in the Oromo.\n\tapFmtOromo = \"WD/WB\"\n\t// apFmtPashto defined the AM/PM name in the Pashto.\n\tapFmtPashto = \"\\u063A.\\u0645./\\u063A.\\u0648.\"\n\t// apFmtPersian defined the AM/PM name in the Persian.\n\tapFmtPersian = \"\\u0642.\\u0638/\\u0628.\\u0638\"\n\t// apFmtPunjabi defined the AM/PM name in the Punjabi.\n\tapFmtPunjabi = \"\\u0A38\\u0A35\\u0A47\\u0A30/\\u0A38\\u0A3C\\u0A3E\\u0A2E\"\n\t// apFmtSakha defined the AM/PM name in the Sakha.\n\tapFmtSakha = \"\\u041A\\u0418/\\u041A\\u041A\"\n\t// apFmtSamiNorthern defined the AM/PM name in the Sami (Northern).\n\tapFmtSamiNorthern = \"i.b./e.b.\"\n\t// apFmtSanskrit defined the AM/PM name in the Sanskrit.\n\tapFmtSanskrit = \"\\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u0942\\u0930\\u094D\\u0935/\\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u091A\\u094D\\u092F\\u093E\\u0924\"\n\t// apFmtScottishGaelic defined the AM/PM name in the Scottish Gaelic.\n\tapFmtScottishGaelic = \"m/f\"\n\t// apFmtSerbianLatin defined the AM/PM name in the Serbian (Latin).\n\tapFmtSerbianLatin = \"pre podne/po podne\"\n\t// apFmtSerbianLatinBA defined the AM/PM name in the Serbian (Latin) Bosnia\n\t// and Herzegovina.\n\tapFmtSerbianLatinBA = \"prije podne/po podne\"\n\t// apFmtSinhala defined the AM/PM name in the Sinhala.\n\tapFmtSinhala = \"\\u0DB4\\u0DD9.\\u0DC0./\\u0DB4.\\u0DC0.\"\n\t// apFmtSlovenian defined the AM/PM name in the Slovenian.\n\tapFmtSlovenian = \"dop./pop.\"\n\t// apFmtSomali defined the AM/PM name in the Somali.\n\tapFmtSomali = \"GH/GD\"\n\t// apFmtSpanish defined the AM/PM name in the Spanish.\n\tapFmtSpanish = \"a. m./p. m.\"\n\t// apFmtSpanishAR defined the AM/PM name in the Spanish Argentina.\n\tapFmtSpanishAR = \"a. m./p. m.\"\n\t// apFmtSwedish defined the AM/PM name in the Swedish.\n\tapFmtSwedish = \"fm/em\"\n\t// apFmtSyriac defined the AM/PM name in the Syriac.\n\tapFmtSyriac = \"\\u0729.\\u071B/\\u0712.\\u071B\"\n\t// apFmtTamil defined the AM/PM name in the Tamil.\n\tapFmtTamil = \"\\u0B95\\u0BBE\\u0BB2\\u0BC8/\\u0BAE\\u0BBE\\u0BB2\\u0BC8\"\n\t// apFmtTibetan defined the AM/PM name in the Tibetan.\n\tapFmtTibetan = \"\\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B/\\u0F55\\u0FB1\\u0F72\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"\n\t// apFmtTigrinya defined the AM/PM name in the Tigrinya.\n\tapFmtTigrinya = \"\\u1295\\u1309\\u1206/\\u12F5\\u1215\\u122A \\u1250\\u1275\\u122A\"\n\t// apFmtTigrinyaER defined the AM/PM name in the Tigrinya Eritrea.\n\tapFmtTigrinyaER = \"\\u1295\\u1309\\u1206 \\u1230\\u12D3\\u1270/\\u12F5\\u1215\\u122D \\u1230\\u12D3\\u1275\"\n\t// apFmtTurkish defined the AM/PM name in the Turkish.\n\tapFmtTurkish = \"\\u00F6\\u00F6/\\u00F6\\u0053\"\n\t// apFmtUpperSorbian defined the AM/PM name in the Upper Sorbian.\n\tapFmtUpperSorbian = \"dopołdnja/popołdnju\"\n\t// apFmtUrdu defined the AM/PM name in the Urdu.\n\tapFmtUrdu = \"\\u062F\\u0646/\\u0631\\u0627\\u062A\"\n\t// apFmtUyghur defined the AM/PM name in the Uyghur.\n\tapFmtUyghur = \"\\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0628\\u06C7\\u0631\\u06C7\\u0646/\\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0643\\u06D0\\u064A\\u0649\\u0646\"\n\t// apFmtUzbek defined the AM/PM name in the Uzbek.\n\tapFmtUzbek = \"TO/TK\"\n\t// apFmtUzbekCyrillic defined the AM/PM name in the Uzbek Cyrillic.\n\tapFmtUzbekCyrillic = \"\\u0422\\u041E/\\u0422\\u041A\"\n\t// apFmtVietnamese defined the AM/PM name in the Vietnamese.\n\tapFmtVietnamese = \"SA/CH\"\n\t// apFmtWelsh defined the AM/PM name in the Welsh.\n\tapFmtWelsh = \"yb/yh\"\n\t// apFmtWolof defined the AM/PM name in the Wolof.\n\tapFmtWolof = \"Sub/Ngo\"\n\t// apFmtYi defined the AM/PM name in the Yi.\n\tapFmtYi = \"\\uA3B8\\uA111/\\uA06F\\uA2D2\"\n\t// apFmtYiddish defined the AM/PM name in the Yiddish.\n\tapFmtYiddish = \"\\u05E4\\u05BF\\u05D0\\u05B7\\u05E8\\u05DE\\u05D9\\u05D8\\u05D0\\u05B8\\u05D2/\\u05E0\\u05D0\\u05B8\\u05DB\\u05DE\\u05D9\\u05D8\\u05D0\\u05B8\\u05D2\"\n\t// apFmtYoruba defined the AM/PM name in the Yoruba.\n\tapFmtYoruba = \"Àár\\u1ECD\\u0300/\\u1ECC\\u0300sán\"\n\t// switchArgumentFunc defined the switch argument printer function.\n\tswitchArgumentFunc = map[string]func(s string) string{\n\t\t\"[DBNum1]\": func(s string) string {\n\t\t\tr := strings.NewReplacer(\n\t\t\t\t\"0\", \"\\u25CB\", \"1\", \"\\u4E00\", \"2\", \"\\u4E8C\", \"3\", \"\\u4E09\", \"4\", \"\\u56DB\",\n\t\t\t\t\"5\", \"\\u4E94\", \"6\", \"\\u516D\", \"7\", \"\\u4E03\", \"8\", \"\\u516B\", \"9\", \"\\u4E5D\",\n\t\t\t)\n\t\t\treturn r.Replace(s)\n\t\t},\n\t\t\"[DBNum2]\": func(s string) string {\n\t\t\tr := strings.NewReplacer(\n\t\t\t\t\"0\", \"\\u96F6\", \"1\", \"\\u58F9\", \"2\", \"\\u8D30\", \"3\", \"\\u53C1\", \"4\", \"\\u8086\",\n\t\t\t\t\"5\", \"\\u4F0D\", \"6\", \"\\u9646\", \"7\", \"\\u67D2\", \"8\", \"\\u634C\", \"9\", \"\\u7396\",\n\t\t\t)\n\t\t\treturn r.Replace(s)\n\t\t},\n\t\t\"[DBNum3]\": func(s string) string {\n\t\t\tr := strings.NewReplacer(\n\t\t\t\t\"0\", \"\\uFF10\", \"1\", \"\\uFF11\", \"2\", \"\\uFF12\", \"3\", \"\\uFF13\", \"4\", \"\\uFF14\",\n\t\t\t\t\"5\", \"\\uFF15\", \"6\", \"\\uFF16\", \"7\", \"\\uFF17\", \"8\", \"\\uFF18\", \"9\", \"\\uFF19\",\n\t\t\t)\n\t\t\treturn r.Replace(s)\n\t\t},\n\t}\n\t// langNumFmtFunc defines functions to apply language number format code.\n\tlangNumFmtFunc = map[CultureName]func(f *File, numFmtID int) string{\n\t\tCultureNameEnUS: func(f *File, numFmtID int) string {\n\t\t\treturn f.langNumFmtFuncEnUS(numFmtID)\n\t\t},\n\t\tCultureNameJaJP: func(f *File, numFmtID int) string {\n\t\t\treturn f.langNumFmtFuncJaJP(numFmtID)\n\t\t},\n\t\tCultureNameKoKR: func(f *File, numFmtID int) string {\n\t\t\treturn f.langNumFmtFuncKoKR(numFmtID)\n\t\t},\n\t\tCultureNameZhCN: func(f *File, numFmtID int) string {\n\t\t\treturn f.langNumFmtFuncZhCN(numFmtID)\n\t\t},\n\t\tCultureNameZhTW: func(f *File, numFmtID int) string {\n\t\t\treturn f.langNumFmtFuncZhTW(numFmtID)\n\t\t},\n\t}\n)\n\n// getSupportedLanguageInfo returns language information by giving language\n// code. This function does not support different calendar type of the language\n// currently. For example: the hexadecimal language code 3010429 (fa-IR,301)\n// will be convert to 0429 (fa-IR).\nfunc getSupportedLanguageInfo(lang string) (languageInfo, bool) {\n\thex := lang\n\tif len(hex) > 4 {\n\t\thex = hex[len(hex)-4:]\n\t}\n\tn := new(big.Int)\n\tn.SetString(hex, 16)\n\tif info, ok := supportedLanguageInfo[int(n.Int64())]; ok {\n\t\treturn info, ok\n\t}\n\tif info, ok := supportedLanguageCodeInfo[lang]; ok {\n\t\treturn info, ok\n\t}\n\tfor _, info := range supportedLanguageInfo {\n\t\tif inStrSlice(info.tags, lang, false) != -1 {\n\t\t\treturn info, true\n\t\t}\n\t}\n\treturn languageInfo{}, false\n}\n\n// applyBuiltInNumFmt provides a function to returns a value after formatted\n// with built-in number format code, or specified sort date format code.\nfunc (f *File) applyBuiltInNumFmt(c *xlsxC, fmtCode string, numFmtID int, date1904 bool, cellType CellType) string {\n\tif f.options != nil && f.options.ShortDatePattern != \"\" {\n\t\tif numFmtID == 14 {\n\t\t\tfmtCode = f.options.ShortDatePattern\n\t\t}\n\t\tif numFmtID == 22 {\n\t\t\tfmtCode = fmt.Sprintf(\"%s hh:mm\", f.options.ShortDatePattern)\n\t\t}\n\t}\n\treturn format(c.V, fmtCode, date1904, cellType, f.options)\n}\n\n// langNumFmtFuncEnUS returns number format code by given date and time pattern\n// for country code en-us.\nfunc (f *File) langNumFmtFuncEnUS(numFmtID int) string {\n\tshortDatePattern, longTimePattern := \"M/d/yy\", \"h:mm:ss\"\n\tif f.options.ShortDatePattern != \"\" {\n\t\tshortDatePattern = f.options.ShortDatePattern\n\t}\n\tif f.options.LongTimePattern != \"\" {\n\t\tlongTimePattern = f.options.LongTimePattern\n\t}\n\tif 32 <= numFmtID && numFmtID <= 35 {\n\t\treturn longTimePattern\n\t}\n\tif (27 <= numFmtID && numFmtID <= 31) || (50 <= numFmtID && numFmtID <= 58) {\n\t\treturn shortDatePattern\n\t}\n\treturn \"\"\n}\n\n// langNumFmtFuncJaJP returns number format code by given date and time pattern\n// for country code ja-jp.\nfunc (f *File) langNumFmtFuncJaJP(numFmtID int) string {\n\tif numFmtID == 30 && f.options.ShortDatePattern != \"\" {\n\t\treturn f.options.ShortDatePattern\n\t}\n\tif (32 <= numFmtID && numFmtID <= 33) && f.options.LongTimePattern != \"\" {\n\t\treturn f.options.LongTimePattern\n\t}\n\treturn langNumFmt[\"ja-jp\"][numFmtID]\n}\n\n// langNumFmtFuncKoKR returns number format code by given date and time pattern\n// for country code ko-kr.\nfunc (f *File) langNumFmtFuncKoKR(numFmtID int) string {\n\tif numFmtID == 30 && f.options.ShortDatePattern != \"\" {\n\t\treturn f.options.ShortDatePattern\n\t}\n\tif (32 <= numFmtID && numFmtID <= 33) && f.options.LongTimePattern != \"\" {\n\t\treturn f.options.LongTimePattern\n\t}\n\treturn langNumFmt[\"ko-kr\"][numFmtID]\n}\n\n// langNumFmtFuncZhCN returns number format code by given date and time pattern\n// for country code zh-cn.\nfunc (f *File) langNumFmtFuncZhCN(numFmtID int) string {\n\tif numFmtID == 30 && f.options.ShortDatePattern != \"\" {\n\t\treturn f.options.ShortDatePattern\n\t}\n\tif (32 <= numFmtID && numFmtID <= 33) && f.options.LongTimePattern != \"\" {\n\t\treturn f.options.LongTimePattern\n\t}\n\treturn langNumFmt[\"zh-cn\"][numFmtID]\n}\n\n// langNumFmtFuncZhTW returns number format code by given date and time pattern\n// for country code zh-tw.\nfunc (f *File) langNumFmtFuncZhTW(numFmtID int) string {\n\tif numFmtID == 30 && f.options.ShortDatePattern != \"\" {\n\t\treturn f.options.ShortDatePattern\n\t}\n\tif (32 <= numFmtID && numFmtID <= 33) && f.options.LongTimePattern != \"\" {\n\t\treturn f.options.LongTimePattern\n\t}\n\treturn langNumFmt[\"zh-tw\"][numFmtID]\n}\n\n// checkDateTimePattern check and validate date and time options field value.\nfunc (f *File) checkDateTimePattern() error {\n\tfor _, pattern := range []string{f.options.LongDatePattern, f.options.LongTimePattern, f.options.ShortDatePattern} {\n\t\tp := nfp.NumberFormatParser()\n\t\tfor _, section := range p.Parse(pattern) {\n\t\t\tfor _, token := range section.Items {\n\t\t\t\tif inStrSlice(supportedTokenTypes, token.TType, false) == -1 || inStrSlice(supportedNumberTokenTypes, token.TType, false) != -1 {\n\t\t\t\t\treturn ErrUnsupportedNumberFormat\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// extractNumFmtDecimal returns decimal places, if has a decimal point token and\n// zero place holder token from a number format code token list.\nfunc extractNumFmtDecimal(tokens []nfp.Token) (int, bool, bool) {\n\tdecimal, point, zero := 0, false, false\n\tfor _, token := range tokens {\n\t\tif token.TType == nfp.TokenTypeDecimalPoint {\n\t\t\tpoint = true\n\t\t}\n\t\tif token.TType == nfp.TokenTypeZeroPlaceHolder {\n\t\t\tif point {\n\t\t\t\tdecimal = len(token.TValue)\n\t\t\t}\n\t\t\tzero = true\n\t\t}\n\t}\n\treturn decimal, point, zero\n}\n\n// extractNumFmtDecimal returns decimal places from a number format code that\n// has the same decimal places in positive part negative part or only positive\n// part, if the given number format code is not suitable for numeric this\n// function will return -1.\nfunc (f *File) extractNumFmtDecimal(fmtCode string) int {\n\tvar (\n\t\tp                                              = nfp.NumberFormatParser()\n\t\tpos, neg, posPoint, negPoint, posZero, negZero bool\n\t\tposDecimal, negDecimal                         int\n\t)\n\tfor i, section := range p.Parse(fmtCode) {\n\t\tif i == 0 {\n\t\t\tpos = true\n\t\t\tposDecimal, posPoint, posZero = extractNumFmtDecimal(section.Items)\n\t\t}\n\t\tif i == 1 {\n\t\t\tneg = true\n\t\t\tnegDecimal, negPoint, negZero = extractNumFmtDecimal(section.Items)\n\t\t}\n\t}\n\tif !pos {\n\t\treturn -1\n\t}\n\tequalPosNegDecimal := posPoint && negPoint && posDecimal == negDecimal\n\tequalPosNegZero := !posPoint && !negPoint && posZero && negZero\n\tif neg {\n\t\tif equalPosNegDecimal {\n\t\t\treturn posDecimal\n\t\t}\n\t\tif equalPosNegZero {\n\t\t\treturn 0\n\t\t}\n\t\treturn -1\n\t}\n\tif posPoint {\n\t\treturn posDecimal\n\t}\n\tif posZero {\n\t\treturn 0\n\t}\n\treturn -1\n}\n\n// getBuiltInNumFmtCode convert number format index to number format code with\n// specified locale and language.\nfunc (f *File) getBuiltInNumFmtCode(numFmtID int) (string, bool) {\n\tif fmtCode, ok := builtInNumFmt[numFmtID]; ok {\n\t\treturn fmtCode, true\n\t}\n\tif isLangNumFmt(numFmtID) {\n\t\tif fn, ok := langNumFmtFunc[f.options.CultureInfo]; ok {\n\t\t\treturn fn(f, numFmtID), true\n\t\t}\n\t}\n\treturn \"\", false\n}\n\n// prepareNumberic split the number into two before and after parts by a\n// decimal point.\nfunc (nf *numberFormat) prepareNumberic(value string) {\n\tif nf.cellType != CellTypeNumber && nf.cellType != CellTypeDate {\n\t\treturn\n\t}\n\tif nf.isNumeric, _, _ = isNumeric(value); !nf.isNumeric {\n\t\treturn\n\t}\n}\n\n// format provides a function to return a string parse by number format\n// expression. If the given number format is not supported, this will return\n// the original cell value.\nfunc format(value, numFmt string, date1904 bool, cellType CellType, opts *Options) string {\n\tp := nfp.NumberFormatParser()\n\tnf := numberFormat{opts: opts, section: p.Parse(numFmt), value: value, date1904: date1904, cellType: cellType}\n\tnf.number, nf.valueSectionType = nf.getValueSectionType(value)\n\tnf.prepareNumberic(value)\n\tfor i, section := range nf.section {\n\t\tnf.sectionIdx = i\n\t\tif section.Type != nf.valueSectionType {\n\t\t\tcontinue\n\t\t}\n\t\tif nf.isNumeric {\n\t\t\tswitch section.Type {\n\t\t\tcase nfp.TokenSectionPositive:\n\t\t\t\treturn nf.alignmentHandler(nf.positiveHandler())\n\t\t\tdefault:\n\t\t\t\treturn nf.alignmentHandler(nf.negativeHandler())\n\t\t\t}\n\t\t}\n\t\treturn nf.alignmentHandler(nf.textHandler())\n\t}\n\treturn value\n}\n\n// getNumberPartLen returns the length of integer and fraction parts for the\n// numeric.\nfunc (nf *numberFormat) getNumberPartLen() (int, int) {\n\tvar intPart, fracPart, intLen, fracLen int\n\tparts := strings.Split(strconv.FormatFloat(math.Abs(nf.number), 'f', -1, 64), \".\")\n\tintPart = len(parts[0])\n\tif len(parts) == 2 {\n\t\tfracPart = len(parts[1])\n\t}\n\tif nf.intHolder > intPart {\n\t\tnf.intHolder = intPart\n\t}\n\tif intLen = intPart; nf.intPadding+nf.intHolder > intPart {\n\t\tintLen = nf.intPadding + nf.intHolder\n\t}\n\tif fracLen = fracPart; fracPart > nf.fracHolder+nf.fracPadding {\n\t\tfracLen = nf.fracHolder + nf.fracPadding\n\t}\n\tif nf.fracPadding > fracPart {\n\t\tfracLen = nf.fracPadding\n\t}\n\treturn intLen, fracLen\n}\n\n// getNumberFmtConf generate the number format padding and placeholder\n// configurations.\nfunc (nf *numberFormat) getNumberFmtConf() {\n\tfor _, token := range nf.section[nf.sectionIdx].Items {\n\t\tif token.TType == nfp.TokenTypeHashPlaceHolder {\n\t\t\tif nf.usePointer {\n\t\t\t\tnf.fracHolder += len(token.TValue)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tnf.intHolder += len(token.TValue)\n\t\t}\n\t\tif token.TType == nfp.TokenTypeExponential {\n\t\t\tnf.useScientificNotation = true\n\t\t}\n\t\tif token.TType == nfp.TokenTypeThousandsSeparator {\n\t\t\tnf.useCommaSep = true\n\t\t}\n\t\tif token.TType == nfp.TokenTypePercent {\n\t\t\tnf.percent += len(token.TValue)\n\t\t}\n\t\tif token.TType == nfp.TokenTypeDecimalPoint {\n\t\t\tnf.usePointer = true\n\t\t}\n\t\tif token.TType == nfp.TokenTypeFraction {\n\t\t\tnf.useFraction = true\n\t\t}\n\t\tif token.TType == nfp.TokenTypeSwitchArgument {\n\t\t\tnf.switchArgument = token.TValue\n\t\t}\n\t\tif token.TType == nfp.TokenTypeZeroPlaceHolder {\n\t\t\tnf.intHolder = 0\n\t\t\tif nf.usePointer {\n\t\t\t\tif nf.useScientificNotation {\n\t\t\t\t\tnf.expBaseLen += len(token.TValue)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tnf.fracPadding += len(token.TValue)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tnf.intPadding += len(token.TValue)\n\t\t}\n\t}\n}\n\n// handleDigitsLiteral apply digit placeholder tokens for the number literal.\nfunc handleDigitsLiteral(text string, tokenValueLen, intPartLen, hashZeroPartLen int, fill bool) (int, string) {\n\tvar result string\n\tl := tokenValueLen\n\tif intPartLen == 0 && len(text) > hashZeroPartLen {\n\t\tl = len(text) + tokenValueLen - hashZeroPartLen\n\t}\n\tif len(text) < hashZeroPartLen {\n\t\tintPartLen += len(text) - hashZeroPartLen\n\t}\n\tfor i := 0; i < l; i++ {\n\t\tj := i + intPartLen\n\t\tif 0 <= j && j < len([]rune(text)) {\n\t\t\tresult += string([]rune(text)[j])\n\t\t} else if fill {\n\t\t\tresult += \" \"\n\t\t}\n\t}\n\treturn l, result\n}\n\n// printNumberLiteral apply literal tokens for the pre-formatted text.\nfunc (nf *numberFormat) printNumberLiteral(text string) string {\n\tvar (\n\t\tresult                      string\n\t\tfrac                        float64\n\t\tuseFraction                 bool\n\t\tintPartLen, hashZeroPartLen int\n\t)\n\tif nf.usePositive {\n\t\tresult += \"-\"\n\t}\n\tlastNonFractionPartDigital := math.MaxInt\n\tappearedFraction := false\n\tnumeratorPlaceHolder := 0\n\tfor idx := len(nf.section[nf.sectionIdx].Items) - 1; idx >= 0; idx-- {\n\t\ttoken := nf.section[nf.sectionIdx].Items[idx]\n\t\tif token.TType == nfp.TokenTypeFraction {\n\t\t\tappearedFraction = true\n\t\t} else if appearedFraction && (token.TType == nfp.TokenTypeHashPlaceHolder || token.TType == nfp.TokenTypeDigitalPlaceHolder) {\n\t\t\tlastNonFractionPartDigital = idx - 1 // current idx belongs to the fraction part (numerator)\n\t\t\tbreak\n\t\t}\n\t}\n\tfor idx, token := range nf.section[nf.sectionIdx].Items {\n\t\tswitch token.TType {\n\t\tcase nfp.TokenTypeHashPlaceHolder, nfp.TokenTypeZeroPlaceHolder:\n\t\t\thashZeroPartLen += len(token.TValue)\n\t\tcase nfp.TokenTypeDigitalPlaceHolder:\n\t\t\tif lastNonFractionPartDigital >= 0 && idx < lastNonFractionPartDigital {\n\t\t\t\thashZeroPartLen += len(token.TValue)\n\t\t\t}\n\t\t}\n\t}\n\tfor idx, token := range nf.section[nf.sectionIdx].Items {\n\t\tif token.TType == nfp.TokenTypeCurrencyLanguage {\n\t\t\t_, _ = nf.currencyLanguageHandler(token)\n\t\t\tresult += nf.currencyString\n\t\t}\n\t\tif token.TType == nfp.TokenTypeLiteral {\n\t\t\tresult += token.TValue\n\t\t}\n\t\tif token.TType == nfp.TokenTypeDigitalPlaceHolder && idx > lastNonFractionPartDigital {\n\t\t\t// If it is a fraction part, it will be filled in by the fractalHandler()\n\t\t\t// If not, fill placeHolder in directly here\n\t\t\tif !useFraction { // numerator part\n\t\t\t\tnumeratorPlaceHolder = len(token.TValue)\n\t\t\t}\n\t\t} else if token.TType == nfp.TokenTypeHashPlaceHolder || token.TType == nfp.TokenTypeZeroPlaceHolder || token.TType == nfp.TokenTypeDigitalPlaceHolder {\n\t\t\tdigits, str := handleDigitsLiteral(text, len(token.TValue), intPartLen, hashZeroPartLen, token.TType == nfp.TokenTypeDigitalPlaceHolder)\n\t\t\tintPartLen += digits\n\t\t\tresult += str\n\t\t}\n\t\tif token.TType == nfp.TokenTypeFraction {\n\t\t\t_, frac = math.Modf(nf.number)\n\t\t\tfrac, useFraction = math.Abs(frac), true\n\t\t}\n\t\tif useFraction {\n\t\t\tresult += nf.fractionHandler(frac, token, numeratorPlaceHolder)\n\t\t}\n\t}\n\treturn nf.printSwitchArgument(result)\n}\n\n// fractionHandler handling fraction number format expression for positive and\n// negative numeric.\nfunc (nf *numberFormat) fractionHandler(frac float64, token nfp.Token, numeratorPlaceHolder int) string {\n\tvar rat, result string\n\tif token.TType == nfp.TokenTypeDigitalPlaceHolder {\n\t\trat = floatToFraction(frac, numeratorPlaceHolder, len(token.TValue))\n\t\tresult += rat\n\t}\n\tif token.TType == nfp.TokenTypeDenominator {\n\t\tdenom, _ := strconv.ParseFloat(token.TValue, 64)\n\t\tresult += fmt.Sprintf(\"%d/%d\", int(math.Round(frac*denom)), int(math.Round(denom)))\n\t}\n\treturn result\n}\n\n// printCommaSep format number with thousands separator.\nfunc printCommaSep(text string) string {\n\tvar (\n\t\ttarget strings.Builder\n\t\tsubStr = strings.Split(text, \".\")\n\t\tlength = len(subStr[0])\n\t)\n\tfor i := 0; i < length; i++ {\n\t\tif i > 0 && (length-i)%3 == 0 {\n\t\t\ttarget.WriteString(\",\")\n\t\t}\n\t\ttarget.WriteByte(text[i])\n\t}\n\tif len(subStr) == 2 {\n\t\ttarget.WriteString(\".\")\n\t\ttarget.WriteString(subStr[1])\n\t}\n\treturn target.String()\n}\n\n// printSwitchArgument format number with switch argument.\nfunc (nf *numberFormat) printSwitchArgument(text string) string {\n\tif nf.switchArgument == \"\" {\n\t\treturn text\n\t}\n\tif fn, ok := switchArgumentFunc[nf.switchArgument]; ok {\n\t\treturn fn(text)\n\t}\n\treturn nf.value\n}\n\n// printBigNumber format number which precision great than 15 with fraction\n// zero padding and percentage symbol.\nfunc (nf *numberFormat) printBigNumber(decimal float64, fracLen int) string {\n\tvar exp float64\n\tif nf.percent > 0 {\n\t\texp = 1\n\t}\n\tresult := strings.TrimLeft(strconv.FormatFloat(decimal*math.Pow(100, exp), 'f', -1, 64), \"-\")\n\tif nf.useCommaSep {\n\t\tresult = printCommaSep(result)\n\t}\n\tif fracLen > 0 {\n\t\tif parts := strings.Split(result, \".\"); len(parts) == 2 {\n\t\t\tfracPartLen := len(parts[1])\n\t\t\tif fracPartLen < fracLen {\n\t\t\t\tresult = fmt.Sprintf(\"%s%s\", result, strings.Repeat(\"0\", fracLen-fracPartLen))\n\t\t\t}\n\t\t\tif fracPartLen > fracLen {\n\t\t\t\tresult = fmt.Sprintf(\"%s.%s\", parts[0], parts[1][:fracLen])\n\t\t\t}\n\t\t} else {\n\t\t\tresult = fmt.Sprintf(\"%s.%s\", result, strings.Repeat(\"0\", fracLen))\n\t\t}\n\t}\n\tif nf.percent > 0 {\n\t\treturn fmt.Sprintf(\"%s%%\", result)\n\t}\n\treturn result\n}\n\n// numberHandler handling number format expression for positive and negative\n// numeric.\nfunc (nf *numberFormat) numberHandler() string {\n\tnf.getNumberFmtConf()\n\tvar (\n\t\tnum             = nf.number\n\t\tintLen, fracLen = nf.getNumberPartLen()\n\t\tresult          string\n\t)\n\tif intLen+fracLen > 15 && !nf.useScientificNotation {\n\t\tisNum, precision, decimal := isNumeric(nf.value)\n\t\tif isNum && precision > 15 {\n\t\t\treturn nf.printNumberLiteral(nf.printBigNumber(decimal, fracLen))\n\t\t}\n\t}\n\tpaddingLen := intLen + fracLen\n\tif fracLen > 0 {\n\t\tpaddingLen++\n\t}\n\tfmtCode := fmt.Sprintf(\"%%0%d.%df%s\", paddingLen, fracLen, strings.Repeat(\"%%\", nf.percent))\n\tif nf.useScientificNotation {\n\t\tif nf.expBaseLen != 2 {\n\t\t\treturn nf.value\n\t\t}\n\t\tfmtCode = fmt.Sprintf(\"%%.%dE%s\", fracLen, strings.Repeat(\"%%\", nf.percent))\n\t}\n\tif nf.percent > 0 {\n\t\tnum *= math.Pow(100, float64(nf.percent))\n\t}\n\tif nf.useFraction {\n\t\tnum = math.Floor(math.Abs(num))\n\t}\n\tif !nf.useScientificNotation {\n\t\tratio := math.Pow(10, float64(fracLen))\n\t\tnum = math.Round(num*ratio) / ratio\n\t}\n\tif result = fmt.Sprintf(fmtCode, math.Abs(num)); nf.useCommaSep {\n\t\tresult = printCommaSep(result)\n\t}\n\treturn nf.printNumberLiteral(result)\n}\n\n// dateTimeHandler handling data and time number format expression for a\n// positive numeric.\nfunc (nf *numberFormat) dateTimeHandler() string {\n\tnf.t, nf.hours, nf.seconds = timeFromExcelTime(nf.number, nf.date1904), false, false\n\tif !nf.useMillisecond {\n\t\tnf.t = nf.t.Add(time.Duration(math.Round(float64(nf.t.Nanosecond())/1e9)) * time.Second)\n\t}\n\tfor i, token := range nf.section[nf.sectionIdx].Items {\n\t\tif token.TType == nfp.TokenTypeCurrencyLanguage {\n\t\t\tif changeNumFmtCode, err := nf.currencyLanguageHandler(token); err != nil || changeNumFmtCode {\n\t\t\t\treturn nf.value\n\t\t\t}\n\t\t\tnf.result += nf.currencyString\n\t\t}\n\t\tif token.TType == nfp.TokenTypeDateTimes {\n\t\t\tnf.dateTimesHandler(i, token)\n\t\t}\n\t\tif token.TType == nfp.TokenTypeElapsedDateTimes {\n\t\t\tnf.elapsedDateTimesHandler(token)\n\t\t}\n\t\tif token.TType == nfp.TokenTypeLiteral {\n\t\t\tnf.result += token.TValue\n\t\t\tcontinue\n\t\t}\n\t\tif token.TType == nfp.TokenTypeDecimalPoint {\n\t\t\tnf.result += \".\"\n\t\t}\n\t\tif token.TType == nfp.TokenTypeSwitchArgument {\n\t\t\tnf.switchArgument = token.TValue\n\t\t}\n\t\tif token.TType == nfp.TokenTypeZeroPlaceHolder {\n\t\t\tzeroHolderLen := len(token.TValue)\n\t\t\tif zeroHolderLen > 3 {\n\t\t\t\tzeroHolderLen = 3\n\t\t\t}\n\t\t\tnf.result += fmt.Sprintf(\"%03d\", nf.t.Nanosecond()/1e6)[:zeroHolderLen]\n\t\t}\n\t}\n\treturn nf.printSwitchArgument(nf.result)\n}\n\n// alignmentHandler will be handling alignment token for each number format\n// selection for a number format expression.\nfunc (nf *numberFormat) alignmentHandler(result string) string {\n\ttokens := nf.section[nf.sectionIdx].Items\n\tif len(tokens) == 0 {\n\t\treturn result\n\t}\n\tif tokens[0].TType == nfp.TokenTypeAlignment {\n\t\tresult = nfp.Whitespace + result\n\t}\n\tif l := len(tokens); tokens[l-1].TType == nfp.TokenTypeAlignment {\n\t\tresult += nfp.Whitespace\n\t}\n\treturn result\n}\n\n// positiveHandler will be handling positive selection for a number format\n// expression.\nfunc (nf *numberFormat) positiveHandler() string {\n\tvar fmtNum bool\n\tfor _, token := range nf.section[nf.sectionIdx].Items {\n\t\tif token.TType == nfp.TokenTypeGeneral {\n\t\t\tif isNum, precision, _ := isNumeric(nf.value); isNum && precision > 11 {\n\t\t\t\treturn strconv.FormatFloat(nf.number, 'G', 10, 64)\n\t\t\t}\n\t\t\treturn nf.value\n\t\t}\n\t\tif inStrSlice(supportedNumberTokenTypes, token.TType, true) != -1 {\n\t\t\tfmtNum = true\n\t\t}\n\t\tif inStrSlice(supportedDateTimeTokenTypes, token.TType, true) != -1 {\n\t\t\tif fmtNum || nf.number < 0 {\n\t\t\t\treturn nf.value\n\t\t\t}\n\t\t\tvar useDateTimeTokens bool\n\t\t\tfor _, token := range nf.section[nf.sectionIdx].Items {\n\t\t\t\tif inStrSlice(supportedDateTimeTokenTypes, token.TType, false) != -1 {\n\t\t\t\t\tif useDateTimeTokens && nf.useMillisecond {\n\t\t\t\t\t\treturn nf.value\n\t\t\t\t\t}\n\t\t\t\t\tuseDateTimeTokens = true\n\t\t\t\t}\n\t\t\t\tif inStrSlice(supportedNumberTokenTypes, token.TType, false) != -1 {\n\t\t\t\t\tif token.TType == nfp.TokenTypeZeroPlaceHolder {\n\t\t\t\t\t\tnf.useMillisecond = true\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\treturn nf.value\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nf.dateTimeHandler()\n\t\t}\n\t}\n\treturn nf.numberHandler()\n}\n\n// currencyLanguageHandler will be handling currency and language types tokens\n// for a number format expression.\nfunc (nf *numberFormat) currencyLanguageHandler(token nfp.Token) (bool, error) {\n\tfor _, part := range token.Parts {\n\t\tif inStrSlice(supportedTokenTypes, part.Token.TType, true) == -1 {\n\t\t\treturn false, ErrUnsupportedNumberFormat\n\t\t}\n\t\tif part.Token.TType == nfp.TokenSubTypeLanguageInfo {\n\t\t\tif inStrSlice([]string{\"F800\", \"x-sysdate\", \"1010000\"}, part.Token.TValue, false) != -1 {\n\t\t\t\tif nf.opts != nil && nf.opts.LongDatePattern != \"\" {\n\t\t\t\t\tnf.value = format(nf.value, nf.opts.LongDatePattern, nf.date1904, nf.cellType, nf.opts)\n\t\t\t\t\treturn true, nil\n\t\t\t\t}\n\t\t\t\tpart.Token.TValue = \"409\"\n\t\t\t}\n\t\t\tif inStrSlice([]string{\"F400\", \"x-systime\"}, part.Token.TValue, false) != -1 {\n\t\t\t\tif nf.opts != nil && nf.opts.LongTimePattern != \"\" {\n\t\t\t\t\tnf.value = format(nf.value, nf.opts.LongTimePattern, nf.date1904, nf.cellType, nf.opts)\n\t\t\t\t\treturn true, nil\n\t\t\t\t}\n\t\t\t\tpart.Token.TValue = \"409\"\n\t\t\t}\n\t\t\tif _, ok := getSupportedLanguageInfo(strings.ToUpper(part.Token.TValue)); !ok {\n\t\t\t\treturn false, ErrUnsupportedNumberFormat\n\t\t\t}\n\t\t\tnf.localCode = strings.ToUpper(part.Token.TValue)\n\t\t}\n\t\tif part.Token.TType == nfp.TokenSubTypeCurrencyString {\n\t\t\tnf.currencyString = part.Token.TValue\n\t\t\treturn false, nil\n\t\t}\n\t}\n\treturn false, nil\n}\n\n// localAmPm return AM/PM name by supported language ID.\nfunc (nf *numberFormat) localAmPm(ap string) string {\n\tif languageInfo, ok := getSupportedLanguageInfo(nf.localCode); ok {\n\t\treturn languageInfo.apFmt\n\t}\n\treturn ap\n}\n\n// localMonthsNameAfrikaans returns the Afrikaans name of the month.\nfunc localMonthsNameAfrikaans(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesAfrikaansAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesAfrikaans[int(t.Month())-1]\n\t}\n\treturn monthNamesAfrikaansAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameAlbanian returns the Albanian name of the month.\nfunc localMonthsNameAlbanian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesAlbanianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesAlbanian[int(t.Month())-1]\n\t}\n\treturn monthNamesAlbanianAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameAlsatian returns the Alsatian name of the month.\nfunc localMonthsNameAlsatian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesAlsatianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesAlsatian[int(t.Month())-1]\n\t}\n\treturn monthNamesAlsatianAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameAlsatianFrance returns the Alsatian France name of the month.\nfunc localMonthsNameAlsatianFrance(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesAlsatianFranceAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesAlsatianFrance[int(t.Month())-1]\n\t}\n\treturn monthNamesAlsatianFranceAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameAmharic returns the Amharic name of the month.\nfunc localMonthsNameAmharic(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesAmharicAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesAmharic[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesAmharic[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameArabic returns the Arabic name of the month.\nfunc localMonthsNameArabic(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesArabic[int(t.Month())-1])[:1])\n\t}\n\treturn monthNamesArabic[int(t.Month())-1]\n}\n\n// localMonthsNameArabicIraq returns the Arabic Iraq name of the month.\nfunc localMonthsNameArabicIraq(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesArabicIraq[int(t.Month())-1])[:1])\n\t}\n\treturn monthNamesArabicIraq[int(t.Month())-1]\n}\n\n// localMonthsNameArmenian returns the Armenian name of the month.\nfunc localMonthsNameArmenian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesArmenianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesArmenian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesArmenianAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameAssamese returns the Assamese name of the month.\nfunc localMonthsNameAssamese(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesAssameseAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesAssamese[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesAssameseAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameAzerbaijaniCyrillic returns the Azerbaijani (Cyrillic) name of the month.\nfunc localMonthsNameAzerbaijaniCyrillic(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesAzerbaijaniCyrillicAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesAzerbaijaniCyrillic[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesAzerbaijaniCyrillic[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameAzerbaijani returns the Azerbaijani name of the month.\nfunc localMonthsNameAzerbaijani(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesAzerbaijaniAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesAzerbaijani[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesAzerbaijani[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameAustria returns the Austria name of the month.\nfunc localMonthsNameAustria(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesAustriaAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesAustria[int(t.Month())-1]\n\t}\n\treturn monthNamesAustriaAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameBangla returns the German name of the month.\nfunc localMonthsNameBangla(t time.Time, abbr int) string {\n\tif abbr == 3 || abbr == 4 {\n\t\treturn monthNamesBangla[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesBangla[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameBashkir returns the Bashkir name of the month.\nfunc localMonthsNameBashkir(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesBashkirAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesBashkir[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesBashkir[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameBasque returns the Basque name of the month.\nfunc localMonthsNameBasque(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesBasqueAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesBasque[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesBasque[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameBelarusian returns the Belarusian name of the month.\nfunc localMonthsNameBelarusian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesBelarusianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesBelarusian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesBelarusian[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameBosnianCyrillic returns the Bosnian (Cyrillic) name of the month.\nfunc localMonthsNameBosnianCyrillic(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesBosnianCyrillicAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesBosnianCyrillic[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesBosnianCyrillic[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameBosnian returns the Bosnian name of the month.\nfunc localMonthsNameBosnian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesBosnianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesBosnian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesBosnian[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameBreton returns the Breton name of the month.\nfunc localMonthsNameBreton(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesBretonAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesBreton[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesBreton[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameBulgarian returns the Bulgarian name of the month.\nfunc localMonthsNameBulgarian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn string([]rune(monthNamesBulgarian[int(t.Month())-1])[:3])\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesBulgarian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesBulgarian[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameBurmese returns the Burmese name of the month.\nfunc localMonthsNameBurmese(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesBurmeseAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesBurmese[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesBurmese[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameCaribbean returns the Caribbean name of the month.\nfunc localMonthsNameCaribbean(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesCaribbeanAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesCaribbean[int(t.Month())-1]\n\t}\n\treturn monthNamesCaribbeanAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameCentralKurdish returns the Central Kurdish name of the month.\nfunc localMonthsNameCentralKurdish(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesCentralKurdish[int(t.Month())-1])[:1])\n\t}\n\treturn monthNamesCentralKurdish[int(t.Month())-1]\n}\n\n// localMonthsNameCherokee returns the Cherokee name of the month.\nfunc localMonthsNameCherokee(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesCherokeeAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesCherokee[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesCherokee[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameChinese1 returns the Chinese name of the month.\nfunc localMonthsNameChinese1(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesChineseNum[t.Month()-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesChinese[int(t.Month())-1]\n\t}\n\treturn monthNamesChineseAbbr[int(t.Month())-1]\n}\n\n// localMonthsNameChinese2 returns the Chinese name of the month.\nfunc localMonthsNameChinese2(t time.Time, abbr int) string {\n\tif abbr == 3 || abbr == 4 {\n\t\treturn monthNamesChinese[int(t.Month())-1]\n\t}\n\treturn monthNamesChineseAbbr[int(t.Month())-1]\n}\n\n// localMonthsNameChinese3 returns the Chinese name of the month.\nfunc localMonthsNameChinese3(t time.Time, abbr int) string {\n\tif abbr == 3 || abbr == 4 {\n\t\treturn monthNamesChineseNum[t.Month()-1]\n\t}\n\treturn strconv.Itoa(int(t.Month()))\n}\n\n// localMonthsNameCorsican returns the Corsican name of the month.\nfunc localMonthsNameCorsican(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesCorsicanAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesCorsican[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesCorsican[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameCroatian returns the Croatian name of the month.\nfunc localMonthsNameCroatian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesCroatianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesCroatian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesCroatian[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameCroatianLatin returns the Croatian (Latin) name of the month.\nfunc localMonthsNameCroatianLatin(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\tif t.Month() == 2 {\n\t\t\treturn string([]rune(monthNamesCroatian[(t.Month() - 1)])[:4])\n\t\t}\n\t\treturn string([]rune(monthNamesCroatian[(t.Month() - 1)])[:3])\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesCroatian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesCroatian[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameCzech returns the Czech name of the month.\nfunc localMonthsNameCzech(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesCzechAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesCzech[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesCzech[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameDanish returns the Danish name of the month.\nfunc localMonthsNameDanish(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesDanishAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesDanish[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesDanish[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameDari returns the Dari name of the month.\nfunc localMonthsNameDari(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesDariAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesPersian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesPersian[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameDariAfghanistan returns the Dari (Afghanistan) name of the\n// month.\nfunc localMonthsNameDariAfghanistan(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesDariAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesDari[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesDari[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameDivehi returns the Divehi name of the month.\nfunc localMonthsNameDivehi(t time.Time, abbr int) string {\n\tif abbr == 3 || abbr == 4 {\n\t\treturn monthNamesDivehi[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesDivehi[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameDutch returns the Dutch name of the month.\nfunc localMonthsNameDutch(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesDutchAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesDutch[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesDutch[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameDzongkha returns the Dzongkha name of the month.\nfunc localMonthsNameDzongkha(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesDzongkhaAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesDzongkha[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesDzongkha[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameEnglish returns the English name of the month.\nfunc localMonthsNameEnglish(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn t.Month().String()[:3]\n\t}\n\tif abbr == 4 {\n\t\treturn t.Month().String()\n\t}\n\treturn t.Month().String()[:1]\n}\n\n// localMonthsNameEstonian returns the Estonian name of the month.\nfunc localMonthsNameEstonian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesEstonianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesEstonian[int(t.Month())-1]\n\t}\n\treturn monthNamesEstonianAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameFaroese returns the Faroese name of the month.\nfunc localMonthsNameFaroese(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthsNameFaroeseAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesFaroese[int(t.Month())-1]\n\t}\n\treturn monthsNameFaroeseAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameFilipino returns the Filipino name of the month.\nfunc localMonthsNameFilipino(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesFilipinoAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesFilipino[int(t.Month())-1]\n\t}\n\treturn fmt.Sprintf(\"%02d\", int(t.Month()))\n}\n\n// localMonthsNameFinnish returns the Finnish name of the month.\nfunc localMonthsNameFinnish(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesFinnishAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesFinnish[int(t.Month())-1]\n\t}\n\treturn fmt.Sprintf(\"%02d\", int(t.Month()))\n}\n\n// localMonthsNameFrench returns the French name of the month.\nfunc localMonthsNameFrench(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\tmonth := monthNamesFrench[int(t.Month())-1]\n\t\tif len([]rune(month)) <= 4 {\n\t\t\treturn monthNamesFrench[int(t.Month())-1]\n\t\t}\n\t\treturn monthNamesFrenchAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesFrench[int(t.Month())-1]\n\t}\n\treturn monthNamesFrenchAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameFrisian returns the Frisian name of the month.\nfunc localMonthsNameFrisian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesFrisianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesFrisian[int(t.Month())-1]\n\t}\n\treturn monthNamesFrisian[int(t.Month())-1][:1]\n}\n\n// localMonthsNameFulah returns the Fulah name of the month.\nfunc localMonthsNameFulah(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesFulahAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesFulah[int(t.Month())-1]\n\t}\n\treturn monthNamesFulah[int(t.Month())-1][:1]\n}\n\n// localMonthsNameGalician returns the Galician name of the month.\nfunc localMonthsNameGalician(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesGalicianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesGalician[int(t.Month())-1]\n\t}\n\treturn monthNamesGalician[int(t.Month())-1][:1]\n}\n\n// localMonthsNameGeorgian returns the Georgian name of the month.\nfunc localMonthsNameGeorgian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesGeorgianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesGeorgian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesGeorgian[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameGerman returns the German name of the month.\nfunc localMonthsNameGerman(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesGermanAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesGerman[int(t.Month())-1]\n\t}\n\treturn monthNamesGermanAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameGreek returns the Greek name of the month.\nfunc localMonthsNameGreek(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesGreekAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesGreek[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesGreekAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameGreenlandic returns the Greenlandic name of the month.\nfunc localMonthsNameGreenlandic(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesGreenlandicAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesGreenlandic[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesGreenlandicAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameGuarani returns the Guarani name of the month.\nfunc localMonthsNameGuarani(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesGuaraniAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesGuarani[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesGuaraniAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameGujarati returns the Gujarati name of the month.\nfunc localMonthsNameGujarati(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesGujaratiAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesGujarati[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesGujaratiAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameHausa returns the Hausa name of the month.\nfunc localMonthsNameHausa(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn string([]rune(monthNamesHausa[int(t.Month())-1])[:3])\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesHausa[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesHausa[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameHawaiian returns the Hawaiian name of the month.\nfunc localMonthsNameHawaiian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesHawaiianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesHawaiian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesHawaiianAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameHebrew returns the Hebrew name of the month.\nfunc localMonthsNameHebrew(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn string([]rune(monthNamesHebrew[int(t.Month())-1])[:3])\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesHebrew[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesHebrew[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameHindi returns the Hindi name of the month.\nfunc localMonthsNameHindi(t time.Time, abbr int) string {\n\tif abbr == 3 || abbr == 4 {\n\t\treturn monthNamesHindi[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesHindi[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameHungarian returns the Hungarian name of the month.\nfunc localMonthsNameHungarian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesHungarianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesHungarian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesHungarianAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameIcelandic returns the Icelandic name of the month.\nfunc localMonthsNameIcelandic(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesIcelandicAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesIcelandic[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesIcelandicAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameIgbo returns the Igbo name of the month.\nfunc localMonthsNameIgbo(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesIgboAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesIgbo[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesIgboAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameIndonesian returns the Indonesian name of the month.\nfunc localMonthsNameIndonesian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesIndonesianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesIndonesian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesIndonesianAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameInuktitut returns the Inuktitut name of the month.\nfunc localMonthsNameInuktitut(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesInuktitutAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesInuktitut[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesInuktitutAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameIrish returns the Irish name of the month.\nfunc localMonthsNameIrish(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesIrishAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesIrish[int(t.Month())-1]\n\t}\n\treturn monthNamesIrishAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameItalian returns the Italian name of the month.\nfunc localMonthsNameItalian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesItalianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesItalian[int(t.Month())-1]\n\t}\n\treturn monthNamesItalianAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameKannada returns the Kannada name of the month.\nfunc localMonthsNameKannada(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesKannadaAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesKannada[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesKannada[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameKashmiri returns the Kashmiri name of the month.\nfunc localMonthsNameKashmiri(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesKashmiri[int(t.Month())-1])[:1])\n\t}\n\treturn monthNamesKashmiri[int(t.Month())-1]\n}\n\n// localMonthsNameKazakh returns the Kazakh name of the month.\nfunc localMonthsNameKazakh(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesKazakhAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesKazakh[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesKazakh[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameKhmer returns the Khmer name of the month.\nfunc localMonthsNameKhmer(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesKhmerAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesKhmer[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesKhmerAbbr[int(t.Month())+11])[:1])\n}\n\n// localMonthsNameKiche returns the Kiche name of the month.\nfunc localMonthsNameKiche(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesKicheAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesKiche[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesKicheAbbr[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameKinyarwanda returns the Kinyarwanda name of the month.\nfunc localMonthsNameKinyarwanda(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesKinyarwandaAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesKinyarwanda[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesKinyarwanda[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameKiswahili returns the Kiswahili name of the month.\nfunc localMonthsNameKiswahili(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesKiswahiliAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesKiswahili[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesKiswahili[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameKonkani returns the Konkani name of the month.\nfunc localMonthsNameKonkani(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesKonkaniAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesKonkani[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesKonkani[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameKorean returns the Korean name of the month.\nfunc localMonthsNameKorean(t time.Time, abbr int) string {\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesKoreanAbbr[int(t.Month())-1]\n\t}\n\treturn strconv.Itoa(int(t.Month()))\n}\n\n// localMonthsNameKyrgyz returns the Kyrgyz name of the month.\nfunc localMonthsNameKyrgyz(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesKyrgyzAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesKyrgyz[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesKyrgyz[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameLao returns the Lao name of the month.\nfunc localMonthsNameLao(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesLaoAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesLao[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesLao[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameLatin returns the Latin name of the month.\nfunc localMonthsNameLatin(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesLatinAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesLatin[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesLatin[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameLatvian returns the Latvian name of the month.\nfunc localMonthsNameLatvian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesLatvianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesLatvian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesLatvian[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameLithuanian returns the Lithuanian name of the month.\nfunc localMonthsNameLithuanian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesLithuanianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesLithuanian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesLithuanian[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameLowerSorbian returns the LowerSorbian name of the month.\nfunc localMonthsNameLowerSorbian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesLowerSorbianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesLowerSorbian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesLowerSorbian[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameLuxembourgish returns the Luxembourgish name of the month.\nfunc localMonthsNameLuxembourgish(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesLuxembourgishAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesLuxembourgish[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesLuxembourgish[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameMacedonian returns the Macedonian name of the month.\nfunc localMonthsNameMacedonian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesMacedonianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesMacedonian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesMacedonian[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameMalay returns the Malay name of the month.\nfunc localMonthsNameMalay(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesMalayAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesMalay[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesMalay[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameMalayalam returns the Malayalam name of the month.\nfunc localMonthsNameMalayalam(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesMalayalamAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesMalayalam[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesMalayalam[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameMaltese returns the Maltese name of the month.\nfunc localMonthsNameMaltese(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesMalteseAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesMaltese[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesMaltese[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameMaori returns the Maori name of the month.\nfunc localMonthsNameMaori(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesMaoriAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesMaori[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesMaori[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameMapudungun returns the Mapudungun name of the month.\nfunc localMonthsNameMapudungun(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesMapudungun[(t.Month() - 1)])[:1])\n\t}\n\treturn monthNamesMapudungun[int(t.Month())-1]\n}\n\n// localMonthsNameMarathi returns the Marathi name of the month.\nfunc localMonthsNameMarathi(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesMarathiAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesMarathi[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesMarathi[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameMohawk returns the Mohawk name of the month.\nfunc localMonthsNameMohawk(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn t.Month().String()[:3]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesMohawk[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesMohawk[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameMongolian returns the Mongolian name of the month.\nfunc localMonthsNameMongolian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesMongolianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesMongolian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesMongolian[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameMorocco returns the Morocco name of the month.\nfunc localMonthsNameMorocco(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesMoroccoAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesFrench[int(t.Month())-1]\n\t}\n\treturn monthNamesFrench[int(t.Month())-1][:1]\n}\n\n// localMonthsNameNepali returns the Nepali name of the month.\nfunc localMonthsNameNepali(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesNepaliAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesNepali[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesNepali[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameNepaliIN returns the India Nepali name of the month.\nfunc localMonthsNameNepaliIN(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesNepaliINAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesNepaliIN[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesNepaliIN[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameNigeria returns the Nigeria name of the month.\nfunc localMonthsNameNigeria(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesNigeriaAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesNigeria[int(t.Month())-1]\n\t}\n\treturn monthNamesNigeria[int(t.Month())-1][:1]\n}\n\n// localMonthsNameNorwegian returns the Norwegian name of the month.\nfunc localMonthsNameNorwegian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthsNameFaroeseAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesNorwegian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesNorwegian[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameOccitan returns the Occitan name of the month.\nfunc localMonthsNameOccitan(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesOccitanAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesOccitan[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesOccitan[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameOdia returns the Odia name of the month.\nfunc localMonthsNameOdia(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesOdia[(t.Month() - 1)])[:1])\n\t}\n\treturn monthNamesOdia[int(t.Month())-1]\n}\n\n// localMonthsNameOromo returns the Oromo name of the month.\nfunc localMonthsNameOromo(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesOromoAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesOromo[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesOromo[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNamePashto returns the Pashto name of the month.\nfunc localMonthsNamePashto(t time.Time, abbr int) string {\n\tif int(t.Month()) == 6 {\n\t\tif abbr == 3 {\n\t\t\treturn \"\\u0686\\u0646\\u06AB\\u0627 \\u069A\"\n\t\t}\n\t\tif abbr == 4 || abbr > 6 {\n\t\t\treturn \"\\u0686\\u0646\\u06AB\\u0627 \\u069A\\u0632\\u0645\\u0631\\u0649\"\n\t\t}\n\t}\n\treturn monthNamesPashto[int(t.Month())-1]\n}\n\n// localMonthsNamePersian returns the Persian name of the month.\nfunc localMonthsNamePersian(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesPersian[(t.Month() - 1)])[:1])\n\t}\n\treturn monthNamesPersian[int(t.Month())-1]\n}\n\n// localMonthsNamePolish returns the Polish name of the month.\nfunc localMonthsNamePolish(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn string([]rune(monthNamesPolish[(t.Month() - 1)])[:3])\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesPolish[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesPolish[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNamePortuguese returns the Portuguese name of the month.\nfunc localMonthsNamePortuguese(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn string([]rune(monthNamesPortuguese[(t.Month() - 1)])[:3])\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesPortuguese[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesPortuguese[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNamePunjabi returns the Punjabi name of the month.\nfunc localMonthsNamePunjabi(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesPunjabi[(t.Month() - 1)])[:1])\n\t}\n\treturn monthNamesPunjabi[int(t.Month())-1]\n}\n\n// localMonthsNamePunjabiArab returns the Punjabi Arab name of the month.\nfunc localMonthsNamePunjabiArab(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesPunjabiArab[(t.Month() - 1)])[:1])\n\t}\n\treturn monthNamesPunjabiArab[int(t.Month())-1]\n}\n\n// localMonthsNameQuechua returns the Quechua name of the month.\nfunc localMonthsNameQuechua(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn string([]rune(monthNamesQuechua[(t.Month() - 1)])[:3])\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesQuechua[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesQuechua[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameQuechuaEcuador returns the QuechuaEcuador name of the month.\nfunc localMonthsNameQuechuaEcuador(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\tif int(t.Month()) == 1 {\n\t\t\treturn string([]rune(monthNamesQuechuaEcuador[(t.Month() - 1)])[:4])\n\t\t}\n\t\treturn string([]rune(monthNamesQuechuaEcuador[(t.Month() - 1)])[:3])\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesQuechuaEcuador[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesQuechuaEcuador[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameRomanian returns the Romanian name of the month.\nfunc localMonthsNameRomanian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesRomanianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesRomanian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesRomanian[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameRomansh returns the Romansh name of the month.\nfunc localMonthsNameRomansh(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesRomanshAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesRomansh[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesRomansh[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameRussian returns the Russian name of the month.\nfunc localMonthsNameRussian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\tmonth := monthNamesRussian[int(t.Month())-1]\n\t\tif len([]rune(month)) <= 4 {\n\t\t\treturn month\n\t\t}\n\t\treturn monthNamesRussianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesRussian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesRussian[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameSakha returns the Sakha name of the month.\nfunc localMonthsNameSakha(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSakhaAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSakha[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSakha[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSami returns the Sami name of the month.\nfunc localMonthsNameSami(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSamiAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSami[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSami[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSamiLule returns the Sami (Lule) name of the month.\nfunc localMonthsNameSamiLule(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSamiLuleAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSamiLule[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSamiLule[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSamiNorthern returns the Sami (Northern) name of the month.\nfunc localMonthsNameSamiNorthern(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSamiNorthernAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSamiNorthern[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSamiNorthern[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSamiNorthernFI returns the Sami (Northern) Finland name of the\n// month.\nfunc localMonthsNameSamiNorthernFI(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSamiNorthernAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\tif int(t.Month()) == 1 {\n\t\t\treturn \"ođđajagemánu\"\n\t\t}\n\t\treturn monthNamesSamiNorthern[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSamiNorthern[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSamiSkolt returns the Sami (Skolt) name of the month.\nfunc localMonthsNameSamiSkolt(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesSamiSkolt[(t.Month() - 1)])[:1])\n\t}\n\treturn monthNamesSamiSkolt[int(t.Month())-1]\n}\n\n// localMonthsNameSamiSouthern returns the Sami (Southern) name of the month.\nfunc localMonthsNameSamiSouthern(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSamiSouthernAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSamiSouthern[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSamiSouthern[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSanskrit returns the Sanskrit name of the month.\nfunc localMonthsNameSanskrit(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesSanskrit[(t.Month() - 1)])[:1])\n\t}\n\treturn monthNamesSanskrit[int(t.Month())-1]\n}\n\n// localMonthsNameScottishGaelic returns the Scottish Gaelic name of the month.\nfunc localMonthsNameScottishGaelic(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesScottishGaelicAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesScottishGaelic[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesScottishGaelic[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSerbian returns the Serbian (Cyrillic) name of the month.\nfunc localMonthsNameSerbian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSerbianAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSerbian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSerbian[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSerbianBA returns the Serbian (Cyrillic) Bosnia and\n// Herzegovina name of the month.\nfunc localMonthsNameSerbianBA(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSerbianBAAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSerbianBA[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSerbianBA[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSerbianLatin returns the Serbian (Latin) name of the month.\nfunc localMonthsNameSerbianLatin(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn string([]rune(monthNamesSerbianLatin[(t.Month() - 1)])[:3])\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSerbianLatin[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSerbianLatin[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSerbianLatinCS returns the Serbian (Latin) name of the month.\nfunc localMonthsNameSerbianLatinCS(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSerbianLatinAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSerbianLatin[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSerbianLatin[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSesothoSaLeboa returns the Sesotho sa Leboa name of the month.\nfunc localMonthsNameSesothoSaLeboa(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSesothoSaLeboaAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSesothoSaLeboa[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSesothoSaLeboa[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSetswana returns the Setswana name of the month.\nfunc localMonthsNameSetswana(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSetswanaAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSetswana[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSetswana[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSindhi returns the Sindhi name of the month.\nfunc localMonthsNameSindhi(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesSindhi[(t.Month() - 1)])[:1])\n\t}\n\treturn monthNamesSindhi[int(t.Month())-1]\n}\n\n// localMonthsNameSinhala returns the Sinhala name of the month.\nfunc localMonthsNameSinhala(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSinhalaAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSinhala[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSinhala[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSlovak returns the Slovak name of the month.\nfunc localMonthsNameSlovak(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn strconv.Itoa(int(t.Month()))\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSlovak[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSlovak[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSlovenian returns the Slovenian name of the month.\nfunc localMonthsNameSlovenian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSlovenianAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSlovenian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSlovenian[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSomali returns the Somali name of the month.\nfunc localMonthsNameSomali(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSomaliAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSomali[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSomali[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSotho returns the Sotho name of the month.\nfunc localMonthsNameSotho(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSothoAbbr[(t.Month() - 1)]\n\t}\n\tif abbr == 4 || abbr > 6 {\n\t\treturn monthNamesSotho[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSotho[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSpanish returns the Spanish name of the month.\nfunc localMonthsNameSpanish(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSpanishAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesSpanish[int(t.Month())-1]\n\t}\n\treturn monthNamesSpanishAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameSpanishPE returns the Spanish Peru name of the month.\nfunc localMonthsNameSpanishPE(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSpanishPEAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesSpanishPE[int(t.Month())-1]\n\t}\n\treturn monthNamesSpanishPEAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameSwedish returns the Swedish name of the month.\nfunc localMonthsNameSwedish(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSwedishAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesSwedish[int(t.Month())-1]\n\t}\n\treturn monthNamesSwedishAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameSwedishFI returns the Swedish Finland name of the month.\nfunc localMonthsNameSwedishFI(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSwedishFIAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesSwedish[int(t.Month())-1]\n\t}\n\treturn monthNamesSwedishFIAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameSyriac returns the Syriac name of the month.\nfunc localMonthsNameSyriac(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSyriacAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesSyriac[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSyriac[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameTajik returns the Tajik name of the month.\nfunc localMonthsNameTajik(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesTajikAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesTajik[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesTajik[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameTamazight returns the Tamazight name of the month.\nfunc localMonthsNameTamazight(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesTamazightAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesTamazight[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesTamazight[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameTamil returns the Tamil name of the month.\nfunc localMonthsNameTamil(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn string([]rune(monthNamesTamil[(t.Month() - 1)])[:1])\n\t}\n\treturn monthNamesTamil[int(t.Month())-1]\n}\n\n// localMonthsNameTamilLK returns the Tamil Sri Lanka name of the month.\nfunc localMonthsNameTamilLK(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesTamilAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesTamil[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesTamil[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameTatar returns the Tatar name of the month.\nfunc localMonthsNameTatar(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesTatarAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesTatar[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesTatar[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameTelugu returns the Telugu name of the month.\nfunc localMonthsNameTelugu(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesTeluguAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesTelugu[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesTelugu[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameSyllabics returns the Syllabics name of the month.\nfunc localMonthsNameSyllabics(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesSyllabicsAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesSyllabics[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesSyllabics[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameThai returns the Thai name of the month.\nfunc localMonthsNameThai(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\tr := []rune(monthNamesThai[int(t.Month())-1])\n\t\treturn string(r[:1]) + \".\" + string(r[len(r)-2:len(r)-1]) + \".\"\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesThai[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesThai[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameTibetan returns the Tibetan name of the month.\nfunc localMonthsNameTibetan(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesTibetanAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 5 {\n\t\tif t.Month() == 10 {\n\t\t\treturn \"\\u0F66\"\n\t\t}\n\t\treturn \"\\u0F5F\"\n\t}\n\treturn monthNamesTibetan[int(t.Month())-1]\n}\n\n// localMonthsNameTigrinya returns the Tigrinya name of the month.\nfunc localMonthsNameTigrinya(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesTigrinyaAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesTigrinya[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesTigrinya[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameTsonga returns the Tsonga name of the month.\nfunc localMonthsNameTsonga(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesTsongaAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesTsonga[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesTsonga[(t.Month() - 1)])[:1])\n}\n\n// localMonthsNameTraditionalMongolian returns the Traditional Mongolian name of\n// the month.\nfunc localMonthsNameTraditionalMongolian(t time.Time, abbr int) string {\n\tif abbr == 5 {\n\t\treturn \"M\"\n\t}\n\treturn monthNamesTradMongolian[t.Month()-1]\n}\n\n// localMonthsNameTurkish returns the Turkish name of the month.\nfunc localMonthsNameTurkish(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesTurkishAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesTurkish[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesTurkishAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameTurkmen returns the Turkmen name of the month.\nfunc localMonthsNameTurkmen(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesTurkmenAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesTurkmen[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesTurkmenAbbr[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameUkrainian returns the Ukrainian name of the month.\nfunc localMonthsNameUkrainian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesUkrainianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesUkrainian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesUkrainian[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameUpperSorbian returns the Upper Sorbian name of the month.\nfunc localMonthsNameUpperSorbian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesUpperSorbianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesUpperSorbian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesUpperSorbian[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameUyghur returns the Uyghur name of the month.\nfunc localMonthsNameUyghur(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn fmt.Sprintf(\"%d-\\u0626\\u0627\\u064A\", int(t.Month()))\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesUyghur[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesUyghur[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameUzbek returns the Uzbek name of the month.\nfunc localMonthsNameUzbek(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesUzbekAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesUzbek[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesUzbek[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameUzbekCyrillic returns the Uzbek (Cyrillic) name of the month.\nfunc localMonthsNameUzbekCyrillic(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesTajikAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesTajik[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesTajik[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameValencian returns the Valencian name of the month.\nfunc localMonthsNameValencian(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesValencianAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesValencian[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesValencian[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameVenda returns the Venda name of the month.\nfunc localMonthsNameVenda(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesVendaAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesVenda[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesVenda[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameVietnamese returns the Vietnamese name of the month.\nfunc localMonthsNameVietnamese(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesVietnameseAbbr3[t.Month()-1]\n\t}\n\tif abbr == 5 {\n\t\treturn monthNamesVietnameseAbbr5[t.Month()-1]\n\t}\n\treturn monthNamesVietnamese[t.Month()-1]\n}\n\n// localMonthsNameWelsh returns the Welsh name of the month.\nfunc localMonthsNameWelsh(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesWelshAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesWelsh[int(t.Month())-1]\n\t}\n\treturn monthNamesWelshAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsNameWolof returns the Wolof name of the month.\nfunc localMonthsNameWolof(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesWolofAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesWolof[int(t.Month())-1]\n\t}\n\treturn monthNamesWolof[int(t.Month())-1][:1]\n}\n\n// localMonthsNameXhosa returns the Xhosa name of the month.\nfunc localMonthsNameXhosa(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesXhosaAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesXhosa[int(t.Month())-1]\n\t}\n\treturn \"u\"\n}\n\n// localMonthsNameYi returns the Yi name of the month.\nfunc localMonthsNameYi(t time.Time, abbr int) string {\n\tif abbr == 3 || abbr == 4 {\n\t\treturn monthNamesYiSuffix[t.Month()-1]\n\t}\n\treturn string([]rune(monthNamesYi[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameYiddish returns the Yiddish name of the month.\nfunc localMonthsNameYiddish(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesYiddishAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesYiddish[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesYiddish[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameYoruba returns the Yoruba name of the month.\nfunc localMonthsNameYoruba(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesYorubaAbbr[int(t.Month())-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesYoruba[int(t.Month())-1]\n\t}\n\treturn string([]rune(monthNamesYoruba[int(t.Month())-1])[:1])\n}\n\n// localMonthsNameZulu returns the Zulu name of the month.\nfunc localMonthsNameZulu(t time.Time, abbr int) string {\n\tif abbr == 3 {\n\t\treturn monthNamesZuluAbbr[t.Month()-1]\n\t}\n\tif abbr == 4 {\n\t\treturn monthNamesZulu[int(t.Month())-1]\n\t}\n\treturn monthNamesZuluAbbr[int(t.Month())-1][:1]\n}\n\n// localMonthsName return months name by supported language ID.\nfunc (nf *numberFormat) localMonthsName(abbr int) string {\n\tt := nf.t\n\tif nf.number < 1 {\n\t\tt = timeFromExcelTime(nf.number+2, nf.date1904)\n\t}\n\tif 1 <= nf.number && nf.number < 60 {\n\t\tt = timeFromExcelTime(nf.number+1, nf.date1904)\n\t}\n\tif languageInfo, ok := getSupportedLanguageInfo(nf.localCode); ok {\n\t\treturn languageInfo.localMonth(t, abbr)\n\t}\n\treturn localMonthsNameEnglish(t, abbr)\n}\n\n// dateAmPmHandler will be handling am/pm types tokens for a number format.\nfunc (nf *numberFormat) dateAmPmHandler(i int, token nfp.Token) {\n\tif nf.ap == \"\" {\n\t\tnextHours := nf.hoursNext(i)\n\t\taps := strings.Split(nf.localAmPm(token.TValue), \"/\")\n\t\tnf.ap = aps[0]\n\t\tif nextHours >= 12 {\n\t\t\tnf.ap = aps[1]\n\t\t}\n\t}\n\tnf.result += nf.ap\n}\n\n// dateTimesHandler will be handling date and times types tokens for a number\n// format expression.\nfunc (nf *numberFormat) dateTimesHandler(i int, token nfp.Token) {\n\tif idx := inStrSlice(nfp.AmPm, strings.ToUpper(token.TValue), false); idx != -1 {\n\t\tnf.dateAmPmHandler(i, token)\n\t\treturn\n\t}\n\tif strings.Contains(strings.ToUpper(token.TValue), \"M\") {\n\t\tm := int(nf.t.Month())\n\t\tif nf.number < 2 {\n\t\t\tm = 1\n\t\t}\n\t\tif 60 <= nf.number && nf.number < 61 {\n\t\t\tm = 2\n\t\t}\n\t\tl := len(token.TValue)\n\t\tif l == 1 && nf.isMonthToken(i) {\n\t\t\tnf.result += strconv.Itoa(int(m))\n\t\t\treturn\n\t\t}\n\t\tif l == 2 && nf.isMonthToken(i) {\n\t\t\tnf.result += fmt.Sprintf(\"%02d\", int(m))\n\t\t\treturn\n\t\t}\n\t\tif l == 3 {\n\t\t\tnf.result += nf.localMonthsName(3)\n\t\t\treturn\n\t\t}\n\t\tif l == 4 || l > 5 {\n\t\t\tnf.result += nf.localMonthsName(4)\n\t\t\treturn\n\t\t}\n\t\tif l == 5 {\n\t\t\tnf.result += nf.localMonthsName(5)\n\t\t\treturn\n\t\t}\n\t}\n\tnf.yearsHandler(token)\n\tnf.daysHandler(token)\n\tnf.hoursHandler(i, token)\n\tnf.minutesHandler(token)\n\tnf.secondsHandler(token)\n}\n\n// eraYear convert time to the Japanese era years.\nfunc eraYear(t time.Time) (int, int) {\n\ti, year := 0, -1\n\tfor i = len(japaneseEraYears) - 1; i > 0; i-- {\n\t\tif y := japaneseEraYears[i]; t.After(y) {\n\t\t\tyear = t.Year() - y.Year() + 1\n\t\t\tbreak\n\t\t}\n\t}\n\treturn i, year\n}\n\n// japaneseYearHandler handling the Japanease calendar years.\nfunc (nf *numberFormat) japaneseYearHandler(token nfp.Token, langInfo languageInfo) {\n\tif strings.Contains(strings.ToUpper(token.TValue), \"G\") {\n\t\ti, year := eraYear(nf.t)\n\t\tif year == -1 {\n\t\t\treturn\n\t\t}\n\t\tnf.useGannen = langInfo.useGannen\n\t\tswitch len(token.TValue) {\n\t\tcase 1:\n\t\t\tnf.useGannen = false\n\t\t\tnf.result += japaneseEraSymbols[i]\n\t\tcase 2:\n\t\t\tnf.result += japaneseEraNames[i][:3]\n\t\tdefault:\n\t\t\tnf.result += japaneseEraNames[i]\n\t\t}\n\t}\n\tif strings.Contains(strings.ToUpper(token.TValue), \"E\") {\n\t\t_, year := eraYear(nf.t)\n\t\tif year == -1 {\n\t\t\tnf.result += strconv.Itoa(nf.t.Year())\n\t\t\treturn\n\t\t}\n\t\tif year == 1 && nf.useGannen {\n\t\t\tnf.result += \"\\u5143\"\n\t\t\treturn\n\t\t}\n\t\tif len(token.TValue) == 1 && !nf.useGannen {\n\t\t\tnf.result += strconv.Itoa(year)\n\t\t\treturn\n\t\t}\n\t\tif len(token.TValue) == 2 {\n\t\t\tnf.result += fmt.Sprintf(\"%02d\", year)\n\t\t}\n\t}\n}\n\n// republicOfChinaYearHandler handling the Republic of China calendar years.\nfunc (nf *numberFormat) republicOfChinaYearHandler(token nfp.Token, langInfo languageInfo) {\n\tyear := nf.t.Year()\n\tif nf.number < 2 {\n\t\tyear = 1900\n\t}\n\tif strings.Contains(strings.ToUpper(token.TValue), \"G\") {\n\t\tyear = year - republicOfChinaYear.Year() + 1\n\t\tif year == 1 {\n\t\t\tnf.useGannen = langInfo.useGannen\n\t\t}\n\t\tvar name string\n\t\tif name = republicOfChinaEraName[0]; len(token.TValue) < 3 {\n\t\t\tname = republicOfChinaEraName[1]\n\t\t}\n\t\tif year < 0 {\n\t\t\tname += republicOfChinaEraName[2]\n\t\t}\n\t\tnf.result += name\n\t}\n\tif strings.Contains(strings.ToUpper(token.TValue), \"E\") {\n\t\tyear = year - republicOfChinaYear.Year() + 1\n\t\tif year < 0 {\n\t\t\tyear = republicOfChinaYear.Year() - nf.t.Year()\n\t\t}\n\t\tif year == 1 && nf.useGannen {\n\t\t\tnf.result += \"\\u5143\"\n\t\t\treturn\n\t\t}\n\t\tif len(token.TValue) == 1 && !nf.useGannen {\n\t\t\tnf.result += strconv.Itoa(year)\n\t\t}\n\t}\n}\n\n// yearsHandler will be handling years in the date and times types tokens for a\n// number format expression.\nfunc (nf *numberFormat) yearsHandler(token nfp.Token) {\n\tlangInfo, _ := getSupportedLanguageInfo(nf.localCode)\n\tyear := nf.t.Year()\n\tif nf.number < 2 {\n\t\tyear = 1900\n\t}\n\tif strings.Contains(strings.ToUpper(token.TValue), \"Y\") {\n\t\tif nf.opts != nil && nf.opts.CultureInfo == CultureNameKoKR {\n\t\t\tyear += 2333\n\t\t}\n\t\tif len(token.TValue) <= 2 {\n\t\t\tnf.result += strconv.Itoa(year)[2:]\n\t\t\treturn\n\t\t}\n\t\tnf.result += strconv.Itoa(year)\n\t\treturn\n\t}\n\tif inStrSlice(langInfo.tags, \"zh-TW\", false) != -1 ||\n\t\tnf.opts != nil && nf.opts.CultureInfo == CultureNameZhTW {\n\t\tnf.republicOfChinaYearHandler(token, langInfo)\n\t\treturn\n\t}\n\tif inStrSlice(langInfo.tags, \"ja-JP\", false) != -1 ||\n\t\tnf.opts != nil && nf.opts.CultureInfo == CultureNameJaJP {\n\t\tnf.japaneseYearHandler(token, langInfo)\n\t\treturn\n\t}\n\tif strings.Contains(strings.ToUpper(token.TValue), \"E\") {\n\t\tnf.result += strconv.Itoa(year)\n\t\treturn\n\t}\n}\n\n// daysHandler will be handling days in the date and times types tokens for a\n// number format expression.\nfunc (nf *numberFormat) daysHandler(token nfp.Token) {\n\tinfo, _ := getSupportedLanguageInfo(nf.localCode)\n\tl := len(token.TValue)\n\tweekdayNames, weekdayNamesAbbr := info.weekdayNames, info.weekdayNamesAbbr\n\tif len(weekdayNames) != 7 {\n\t\tweekdayNames = weekdayNamesEnglish\n\t}\n\tif len(weekdayNamesAbbr) != 7 {\n\t\tweekdayNamesAbbr = weekdayNamesEnglishAbbr\n\t}\n\tif strings.Contains(strings.ToUpper(token.TValue), \"A\") {\n\t\tif l == 3 {\n\t\t\tnf.result += weekdayNamesAbbr[nf.t.Weekday()]\n\t\t}\n\t\tif l > 3 {\n\t\t\tnf.result += weekdayNames[nf.t.Weekday()]\n\t\t}\n\t\treturn\n\t}\n\tif strings.Contains(strings.ToUpper(token.TValue), \"D\") {\n\t\td := nf.t.Day()\n\t\tif nf.number < 1 {\n\t\t\td = 0\n\t\t}\n\t\tif 1 <= nf.number && nf.number < 60 {\n\t\t\td = timeFromExcelTime(nf.number+1, nf.date1904).Day()\n\t\t}\n\t\tif 60 <= nf.number && nf.number < 61 {\n\t\t\td = 29\n\t\t}\n\t\tswitch l {\n\t\tcase 1:\n\t\t\tnf.result += strconv.Itoa(d)\n\t\tcase 2:\n\t\t\tnf.result += fmt.Sprintf(\"%02d\", d)\n\t\tcase 3:\n\t\t\tnf.result += weekdayNamesAbbr[nf.t.Weekday()]\n\t\tdefault:\n\t\t\tnf.result += weekdayNames[nf.t.Weekday()]\n\t\t}\n\t}\n}\n\n// hoursHandler will be handling hours in the date and times types tokens for a\n// number format expression.\nfunc (nf *numberFormat) hoursHandler(i int, token nfp.Token) {\n\tif nf.hours = strings.Contains(strings.ToUpper(token.TValue), \"H\"); nf.hours {\n\t\th := nf.t.Hour()\n\t\tap, ok := nf.apNext(i)\n\t\tif ok {\n\t\t\tnf.ap = ap[0]\n\t\t\tif h >= 12 {\n\t\t\t\tnf.ap = ap[1]\n\t\t\t}\n\t\t\tif h > 12 {\n\t\t\t\th -= 12\n\t\t\t}\n\t\t}\n\t\tif nf.ap != \"\" {\n\t\t\tif nf.hoursNext(i) == -1 && h > 12 {\n\t\t\t\th -= 12\n\t\t\t}\n\t\t\tif h == 0 {\n\t\t\t\th = 12\n\t\t\t}\n\t\t}\n\t\tswitch len(token.TValue) {\n\t\tcase 1:\n\t\t\tnf.result += strconv.Itoa(h)\n\t\t\treturn\n\t\tdefault:\n\t\t\tnf.result += fmt.Sprintf(\"%02d\", h)\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// minutesHandler will be handling minutes in the date and times types tokens\n// for a number format expression.\nfunc (nf *numberFormat) minutesHandler(token nfp.Token) {\n\tif strings.Contains(strings.ToUpper(token.TValue), \"M\") {\n\t\tnf.hours = false\n\t\tswitch len(token.TValue) {\n\t\tcase 1:\n\t\t\tnf.result += strconv.Itoa(nf.t.Minute())\n\t\t\treturn\n\t\tdefault:\n\t\t\tnf.result += fmt.Sprintf(\"%02d\", nf.t.Minute())\n\t\t}\n\t}\n}\n\n// secondsHandler will be handling seconds in the date and times types tokens\n// for a number format expression.\nfunc (nf *numberFormat) secondsHandler(token nfp.Token) {\n\tif nf.seconds = strings.Contains(strings.ToUpper(token.TValue), \"S\"); !nf.seconds {\n\t\treturn\n\t}\n\tif len(token.TValue) == 1 {\n\t\tnf.result += strconv.Itoa(nf.t.Second())\n\t\treturn\n\t}\n\tnf.result += fmt.Sprintf(\"%02d\", nf.t.Second())\n}\n\n// elapsedDateTimesHandler will be handling elapsed date and times types tokens\n// for a number format expression.\nfunc (nf *numberFormat) elapsedDateTimesHandler(token nfp.Token) {\n\tif strings.Contains(strings.ToUpper(token.TValue), \"H\") {\n\t\tnf.result += fmt.Sprintf(\"%.f\", math.Floor(nf.t.Sub(excel1900Epoc).Hours()))\n\t\treturn\n\t}\n\tif strings.Contains(strings.ToUpper(token.TValue), \"M\") {\n\t\tnf.result += fmt.Sprintf(\"%.f\", math.Floor(nf.t.Sub(excel1900Epoc).Minutes()))\n\t\treturn\n\t}\n\tif strings.Contains(strings.ToUpper(token.TValue), \"S\") {\n\t\tnf.result += fmt.Sprintf(\"%.f\", math.Floor(nf.t.Sub(excel1900Epoc).Seconds()))\n\t\treturn\n\t}\n}\n\n// hoursNext detects if a token of type hours exists after a given tokens list.\nfunc (nf *numberFormat) hoursNext(i int) int {\n\ttokens := nf.section[nf.sectionIdx].Items\n\tfor idx := i + 1; idx < len(tokens); idx++ {\n\t\tif tokens[idx].TType == nfp.TokenTypeDateTimes {\n\t\t\tif strings.Contains(strings.ToUpper(tokens[idx].TValue), \"H\") {\n\t\t\t\tt := timeFromExcelTime(nf.number, false)\n\t\t\t\treturn t.Hour()\n\t\t\t}\n\t\t}\n\t}\n\treturn -1\n}\n\n// apNext detects if a token of type AM/PM exists after a given tokens list.\nfunc (nf *numberFormat) apNext(i int) ([]string, bool) {\n\ttokens := nf.section[nf.sectionIdx].Items\n\tfor idx := i + 1; idx < len(tokens); idx++ {\n\t\tif tokens[idx].TType == nfp.TokenTypeDateTimes {\n\t\t\tif strings.Contains(strings.ToUpper(tokens[idx].TValue), \"H\") {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t\tif i := inStrSlice(nfp.AmPm, tokens[idx].TValue, false); i != -1 {\n\t\t\t\treturn strings.Split(nf.localAmPm(tokens[idx].TValue), \"/\"), true\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, false\n}\n\n// isMonthToken detects if the given token represents minutes, if no hours and\n// seconds tokens before the given token or not seconds after the given token,\n// the current token is a minutes token.\nfunc (nf *numberFormat) isMonthToken(i int) bool {\n\ttokens := nf.section[nf.sectionIdx].Items\n\tvar timePrevious, secondsNext bool\n\tfor idx := i - 1; idx >= 0; idx-- {\n\t\tif tokens[idx].TType == nfp.TokenTypeDateTimes {\n\t\t\ttimePrevious = strings.ContainsAny(strings.ToUpper(tokens[idx].TValue), \"HS\")\n\t\t\tbreak\n\t\t}\n\t\tif tokens[idx].TType == nfp.TokenTypeElapsedDateTimes {\n\t\t\ttimePrevious = true\n\t\t\tbreak\n\t\t}\n\t}\n\tfor idx := i + 1; idx < len(tokens); idx++ {\n\t\tif tokens[idx].TType == nfp.TokenTypeDateTimes {\n\t\t\tsecondsNext = strings.Contains(strings.ToUpper(tokens[idx].TValue), \"S\")\n\t\t\tbreak\n\t\t}\n\t}\n\treturn !timePrevious && !secondsNext\n}\n\n// negativeHandler will be handling negative selection for a number format\n// expression.\nfunc (nf *numberFormat) negativeHandler() (result string) {\n\tfor _, token := range nf.section[nf.sectionIdx].Items {\n\t\tif inStrSlice(supportedTokenTypes, token.TType, true) == -1 || token.TType == nfp.TokenTypeGeneral {\n\t\t\treturn nf.value\n\t\t}\n\t\tif inStrSlice(supportedDateTimeTokenTypes, token.TType, true) != -1 {\n\t\t\treturn nf.value\n\t\t}\n\t}\n\treturn nf.numberHandler()\n}\n\n// textHandler will be handling text selection for a number format expression.\nfunc (nf *numberFormat) textHandler() (result string) {\n\tfor _, token := range nf.section[nf.sectionIdx].Items {\n\t\tif token.TType == nfp.TokenTypeLiteral {\n\t\t\tresult += token.TValue\n\t\t}\n\t\tif token.TType == nfp.TokenTypeTextPlaceHolder || token.TType == nfp.TokenTypeZeroPlaceHolder {\n\t\t\tresult += nf.value\n\t\t}\n\t}\n\treturn result\n}\n\n// getValueSectionType returns its applicable number format expression section\n// based on the given value.\nfunc (nf *numberFormat) getValueSectionType(value string) (float64, string) {\n\tif nf.cellType != CellTypeNumber && nf.cellType != CellTypeDate {\n\t\treturn 0, nfp.TokenSectionText\n\t}\n\tisNum, _, _ := isNumeric(value)\n\tif !isNum {\n\t\treturn 0, nfp.TokenSectionText\n\t}\n\tnumber, _ := strconv.ParseFloat(value, 64)\n\tif number >= 0 {\n\t\treturn number, nfp.TokenSectionPositive\n\t}\n\tvar hasNeg bool\n\tfor _, sec := range nf.section {\n\t\tif sec.Type == nfp.TokenSectionNegative {\n\t\t\thasNeg = true\n\t\t}\n\t}\n\tif !hasNeg {\n\t\tnf.usePositive = true\n\t\treturn number, nfp.TokenSectionPositive\n\t}\n\treturn number, nfp.TokenSectionNegative\n}\n"
  },
  {
    "path": "numfmt_test.go",
    "content": "package excelize\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/xuri/nfp\"\n)\n\nfunc TestNumFmt(t *testing.T) {\n\tfor _, item := range [][]string{\n\t\t{\"123\", \"general\", \"123\"},\n\t\t{\"-123\", \";general\", \"-123\"},\n\t\t{\"123.456\", \"0.00\\\"\\\"\", \"123.46\"},\n\t\t{\"123.456\", \"\\\"\\\";\\\"\\\"\", \"\"},\n\t\t{\"123.456\", \"\\\"\\\"0.00foo\", \"123.46foo\"},\n\t\t{\"123.456\", \"\\\"\\\"0.00\\\"foo\\\"\", \"123.46foo\"},\n\t\t{\"12345678901\", \"General\", \"12345678901\"},\n\t\t{\"43543.5448726851\", \"General\", \"43543.54487\"},\n\t\t{\"-43543.5448726851\", \"General\", \"-43543.54487\"},\n\t\t{\"1234567890.12345\", \"General\", \"1234567890\"},\n\t\t{\"43528\", \"y\", \"19\"},\n\t\t{\"43528\", \"Y\", \"19\"},\n\t\t{\"43528\", \"yy\", \"19\"},\n\t\t{\"43528\", \"YY\", \"19\"},\n\t\t{\"43528\", \"yyy\", \"2019\"},\n\t\t{\"43528\", \"YYY\", \"2019\"},\n\t\t{\"43528\", \"yyyy\", \"2019\"},\n\t\t{\"43528\", \"YYYY\", \"2019\"},\n\t\t{\"43528\", \"yyyyy\", \"2019\"},\n\t\t{\"43528\", \"YYYYY\", \"2019\"},\n\t\t{\"43528\", \"m\", \"3\"},\n\t\t{\"43528\", \"mm\", \"03\"},\n\t\t{\"43528\", \"mmm\", \"Mar\"},\n\t\t{\"43528\", \"mmmm\", \"March\"},\n\t\t{\"43528\", \"mmmmm\", \"M\"},\n\t\t{\"43528\", \"mmmmmm\", \"March\"},\n\t\t{\"43528\", \"d\", \"4\"},\n\t\t{\"43528\", \"dd\", \"04\"},\n\t\t{\"43528\", \"ddd\", \"Mon\"},\n\t\t{\"43528\", \"dddd\", \"Monday\"},\n\t\t{\"43528\", \"h\", \"0\"},\n\t\t{\"43528\", \"hh\", \"00\"},\n\t\t{\"43528\", \"hhh\", \"00\"},\n\t\t{\"43543.544872685183\", \"hhmm\", \"1304\"},\n\t\t{\"43543.544872685183\", \"mmhhmmmm\", \"0313March\"},\n\t\t{\"43543.544872685183\", \"mm hh mm mm\", \"03 13 04 03\"},\n\t\t{\"43543.544872685183\", \"mm hh m m\", \"03 13 4 3\"},\n\t\t{\"43543.544872685183\", \"m s\", \"4 37\"},\n\t\t{\"43528\", \"[h]\", \"1044672\"},\n\t\t{\"43528\", \"[m]\", \"62680320\"},\n\t\t{\"43528\", \"s\", \"0\"},\n\t\t{\"43528\", \"ss\", \"00\"},\n\t\t{\"43528\", \"[s]\", \"3760819200\"},\n\t\t{\"43543.544872685183\", \"h:mm:ss AM/PM\", \"1:04:37 PM\"},\n\t\t{\"43543.544872685183\", \"AM/PM h:mm:ss\", \"PM 1:04:37\"},\n\t\t{\"43543.086539351854\", \"hh:mm:ss AM/PM\", \"02:04:37 AM\"},\n\t\t{\"43543.086539351854\", \"AM/PM hh:mm:ss\", \"AM 02:04:37\"},\n\t\t{\"43543.086539351854\", \"AM/PM hh:mm:ss a/p\", \"AM 02:04:37 a\"},\n\t\t{\"0.609375\", \"[HH]:mm:ss\", \"14:37:30\"},\n\t\t{\"43528\", \"YYYY\", \"2019\"},\n\t\t{\"43528\", \"\", \"43528\"},\n\t\t{\"43528.2123\", \"YYYY-MM-DD hh:mm:ss\", \"2019-03-04 05:05:43\"},\n\t\t{\"43528.2123\", \"YYYY-MM-DD hh:mm:ss;YYYY-MM-DD hh:mm:ss\", \"2019-03-04 05:05:43\"},\n\t\t{\"43528.2123\", \"M/D/YYYY h:m:s\", \"3/4/2019 5:5:43\"},\n\t\t{\"43528.003958333335\", \"m/d/yyyy h:m:s\", \"3/4/2019 0:5:42\"},\n\t\t{\"43528.003958333335\", \"M/D/YYYY h:mm:s\", \"3/4/2019 0:05:42\"},\n\t\t{\"0.64583333333333337\", \"h:mm:ss am/pm\", \"3:30:00 pm\"},\n\t\t{\"43528.003958333335\", \"h:mm\", \"0:05\"},\n\t\t{\"6.9444444444444444E-5\", \"h:m\", \"0:0\"},\n\t\t{\"6.9444444444444444E-5\", \"h:mm\", \"0:00\"},\n\t\t{\"6.9444444444444444E-5\", \"h:m\", \"0:0\"},\n\t\t{\"0.50070601851851848\", \"h:m\", \"12:1\"},\n\t\t{\"0.97952546296296295\", \"h:m\", \"23:30\"},\n\t\t{\"43528\", \"mmmm\", \"March\"},\n\t\t{\"43528\", \"dddd\", \"Monday\"},\n\t\t{\"0\", \";;;\", \"\"},\n\t\t{\"0\", \"0%\", \"0%\"},\n\t\t{\"0\", \"0.0%\", \"0.0%\"},\n\t\t{\"0\", \"0.00%\", \"0.00%\"},\n\t\t{\"0\", \"[$-zh-TW]yyyy\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"1900年1月0日\"},\n\t\t{\"60\", \"yyyy-mm-dd;@\", \"1900-02-29\"},\n\t\t{\"43528\", \"[$-409]MM/DD/YYYY\", \"03/04/2019\"},\n\t\t{\"43528\", \"[$-409]MM/DD/YYYY am/pm\", \"03/04/2019 AM\"},\n\t\t{\"43528\", \"[$-111]MM/DD/YYYY\", \"43528\"},\n\t\t{\"43528\", \"[$US-409]MM/DD/YYYY\", \"US03/04/2019\"},\n\t\t{\"43543.586539351854\", \"AM/PM h h:mm\", \"PM 14 2:04\"},\n\t\t{\"45186\", \"DD.MM.YYYY\", \"17.09.2023\"},\n\t\t{\"text\", \"AM/PM h h:mm\", \"text\"},\n\t\t{\"43466.189571759256\", \"[$-404]aaa;@\", \"週二\"},\n\t\t{\"43466.189571759256\", \"[$-404]aaaa;@\", \"星期二\"},\n\t\t{\"43466.189571759256\", \"[$-zh-TW]aaa;@\", \"週二\"},\n\t\t{\"43466.189571759256\", \"[$-zh-TW]aaaa;@\", \"星期二\"},\n\t\t{\"43466.189571759256\", \"[$-804]aaa;@\", \"周二\"},\n\t\t{\"43466.189571759256\", \"[$-804]aaaa;@\", \"星期二\"},\n\t\t{\"43466.189571759256\", \"[$-0804]aaa;@\", \"周二\"},\n\t\t{\"43466.189571759256\", \"[$-0804]aaaa;@\", \"星期二\"},\n\t\t{\"43466.189571759256\", \"[$-zh-CN]aaa;@\", \"周二\"},\n\t\t{\"43466.189571759256\", \"[$-zh-CN]aaaa;@\", \"星期二\"},\n\t\t{\"43466.189571759256\", \"[$-435]aaa;@\", \"Bi.\"},\n\t\t{\"43466.189571759256\", \"[$-435]aaaa;@\", \"ULwesibili\"},\n\t\t{\"43466.189571759256\", \"[$-0435]aaa;@\", \"Bi.\"},\n\t\t{\"43466.189571759256\", \"[$-0435]aaaa;@\", \"ULwesibili\"},\n\t\t{\"43466.189571759256\", \"[$-zu-ZA]aaa;@\", \"Bi.\"},\n\t\t{\"43466.189571759256\", \"[$-zu-ZA]aaaa;@\", \"ULwesibili\"},\n\t\t{\"43466.189571759256\", \"[$-404]ddd;@\", \"週二\"},\n\t\t{\"43466.189571759256\", \"[$-404]dddd;@\", \"星期二\"},\n\t\t{\"43466.189571759256\", \"[$-0404]ddd;@\", \"週二\"},\n\t\t{\"43466.189571759256\", \"[$-0404]dddd;@\", \"星期二\"},\n\t\t{\"43466.189571759256\", \"[$-zh-TW]ddd;@\", \"週二\"},\n\t\t{\"43466.189571759256\", \"[$-zh-TW]dddd;@\", \"星期二\"},\n\t\t{\"43466.189571759256\", \"[$-804]ddd;@\", \"周二\"},\n\t\t{\"43466.189571759256\", \"[$-804]dddd;@\", \"星期二\"},\n\t\t{\"43466.189571759256\", \"[$-0804]ddd;@\", \"周二\"},\n\t\t{\"43466.189571759256\", \"[$-0804]dddd;@\", \"星期二\"},\n\t\t{\"43466.189571759256\", \"[$-zh-CN]ddd;@\", \"周二\"},\n\t\t{\"43466.189571759256\", \"[$-zh-CN]dddd;@\", \"星期二\"},\n\t\t{\"43466.189571759256\", \"[$-435]ddd;@\", \"Bi.\"},\n\t\t{\"43466.189571759256\", \"[$-435]dddd;@\", \"ULwesibili\"},\n\t\t{\"43466.189571759256\", \"[$-0435]ddd;@\", \"Bi.\"},\n\t\t{\"43466.189571759256\", \"[$-0435]dddd;@\", \"ULwesibili\"},\n\t\t{\"43466.189571759256\", \"[$-zu-ZA]ddd;@\", \"Bi.\"},\n\t\t{\"43466.189571759256\", \"[$-zu-ZA]dddd;@\", \"ULwesibili\"},\n\t\t{\"44562.189571759256\", \"[$-36]mmm dd yyyy  h:mm AM/PM d\", \"Jan. 01 2022  4:32 vm. 1\"},\n\t\t{\"44562.189571759256\", \"[$-36]mmmm dd yyyy  h:mm AM/PM dd\", \"Januarie 01 2022  4:32 vm. 01\"},\n\t\t{\"44562.189571759256\", \"[$-36]mmmmm dd yyyy  h:mm AM/PM ddd\", \"J 01 2022  4:32 vm. Sa.\"},\n\t\t{\"44682.18957170139\", \"[$-36]mmm dd yyyy  h:mm AM/PM dddd\", \"Mei 01 2022  4:32 vm. Sondag\"},\n\t\t{\"44682.18957170139\", \"[$-36]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mei 01 2022  4:32 vm. So.\"},\n\t\t{\"44682.18957170139\", \"[$-36]mmmmm dd yyyy  h:mm AM/PM aaaa\", \"M 01 2022  4:32 vm. Sondag\"},\n\t\t{\"44562.189571759256\", \"[$-036]mmm dd yyyy  h:mm AM/PM d\", \"Jan. 01 2022  4:32 vm. 1\"},\n\t\t{\"44562.189571759256\", \"[$-036]mmmm dd yyyy  h:mm AM/PM dd\", \"Januarie 01 2022  4:32 vm. 01\"},\n\t\t{\"44562.189571759256\", \"[$-036]mmmmm dd yyyy  h:mm AM/PM ddd\", \"J 01 2022  4:32 vm. Sa.\"},\n\t\t{\"44682.18957170139\", \"[$-036]mmm dd yyyy  h:mm AM/PM dddd\", \"Mei 01 2022  4:32 vm. Sondag\"},\n\t\t{\"44682.18957170139\", \"[$-036]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mei 01 2022  4:32 vm. So.\"},\n\t\t{\"44682.18957170139\", \"[$-036]mmmmm dd yyyy  h:mm AM/PM aaaa\", \"M 01 2022  4:32 vm. Sondag\"},\n\t\t{\"44562.189571759256\", \"[$-0036]mmm dd yyyy  h:mm AM/PM d\", \"Jan. 01 2022  4:32 vm. 1\"},\n\t\t{\"44562.189571759256\", \"[$-0036]mmmm dd yyyy  h:mm AM/PM dd\", \"Januarie 01 2022  4:32 vm. 01\"},\n\t\t{\"44562.189571759256\", \"[$-0036]mmmmm dd yyyy  h:mm AM/PM ddd\", \"J 01 2022  4:32 vm. Sa.\"},\n\t\t{\"44682.18957170139\", \"[$-0036]mmm dd yyyy  h:mm AM/PM dddd\", \"Mei 01 2022  4:32 vm. Sondag\"},\n\t\t{\"44682.18957170139\", \"[$-0036]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mei 01 2022  4:32 vm. So.\"},\n\t\t{\"44682.18957170139\", \"[$-0036]mmmmm dd yyyy  h:mm AM/PM aaaa\", \"M 01 2022  4:32 vm. Sondag\"},\n\t\t{\"44562.189571759256\", \"[$-af]mmm dd yyyy  h:mm AM/PM d\", \"Jan. 01 2022  4:32 vm. 1\"},\n\t\t{\"44562.189571759256\", \"[$-af]mmmm dd yyyy  h:mm AM/PM dd\", \"Januarie 01 2022  4:32 vm. 01\"},\n\t\t{\"44562.189571759256\", \"[$-af]mmmmm dd yyyy  h:mm AM/PM ddd\", \"J 01 2022  4:32 vm. Sa.\"},\n\t\t{\"44682.18957170139\", \"[$-af]mmm dd yyyy  h:mm AM/PM dddd\", \"Mei 01 2022  4:32 vm. Sondag\"},\n\t\t{\"44682.18957170139\", \"[$-af]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mei 01 2022  4:32 vm. So.\"},\n\t\t{\"44682.18957170139\", \"[$-af]mmmmm dd yyyy  h:mm AM/PM aaaa\", \"M 01 2022  4:32 vm. Sondag\"},\n\t\t{\"44562.189571759256\", \"[$-436]mmm dd yyyy  h:mm AM/PM d\", \"Jan. 01 2022  4:32 vm. 1\"},\n\t\t{\"44562.189571759256\", \"[$-436]mmmm dd yyyy  h:mm AM/PM dd\", \"Januarie 01 2022  4:32 vm. 01\"},\n\t\t{\"44562.189571759256\", \"[$-436]mmmmm dd yyyy  h:mm AM/PM ddd\", \"J 01 2022  4:32 vm. Sa.\"},\n\t\t{\"44682.18957170139\", \"[$-436]mmm dd yyyy  h:mm AM/PM dddd\", \"Mei 01 2022  4:32 vm. Sondag\"},\n\t\t{\"44682.18957170139\", \"[$-436]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mei 01 2022  4:32 vm. So.\"},\n\t\t{\"44682.18957170139\", \"[$-436]mmmmm dd yyyy  h:mm AM/PM aaaa\", \"M 01 2022  4:32 vm. Sondag\"},\n\t\t{\"44562.189571759256\", \"[$-1C]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 p.d.\"},\n\t\t{\"44562.189571759256\", \"[$-1C]mmmm dd yyyy  h:mm AM/PM\", \"janar 01 2022  4:32 p.d.\"},\n\t\t{\"44562.189571759256\", \"[$-1C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 p.d.\"},\n\t\t{\"44562.189571759256\", \"[$-1C]mmmmmm dd yyyy  h:mm AM/PM\", \"janar 01 2022  4:32 p.d.\"},\n\t\t{\"43543.503206018519\", \"[$-1C]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 m.d.\"},\n\t\t{\"43543.503206018519\", \"[$-1C]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 m.d. mar\"},\n\t\t{\"43543.503206018519\", \"[$-1C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 m.d. mar\"},\n\t\t{\"43543.503206018519\", \"[$-1C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 m.d. e martë\"},\n\t\t{\"44562.189571759256\", \"[$-41C]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 p.d.\"},\n\t\t{\"44562.189571759256\", \"[$-41C]mmmm dd yyyy  h:mm AM/PM\", \"janar 01 2022  4:32 p.d.\"},\n\t\t{\"44562.189571759256\", \"[$-41C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 p.d.\"},\n\t\t{\"44562.189571759256\", \"[$-41C]mmmmmm dd yyyy  h:mm AM/PM\", \"janar 01 2022  4:32 p.d.\"},\n\t\t{\"43543.503206018519\", \"[$-41C]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 m.d.\"},\n\t\t{\"43543.503206018519\", \"[$-41C]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 m.d. mar\"},\n\t\t{\"43543.503206018519\", \"[$-41C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 m.d. mar\"},\n\t\t{\"43543.503206018519\", \"[$-41C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 m.d. e martë\"},\n\t\t{\"44562.189571759256\", \"[$-84]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 vorm.\"},\n\t\t{\"44562.189571759256\", \"[$-84]mmmm dd yyyy  h:mm AM/PM\", \"Januar 01 2022  4:32 vorm.\"},\n\t\t{\"44562.189571759256\", \"[$-84]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 vorm.\"},\n\t\t{\"44562.189571759256\", \"[$-84]mmmmmm dd yyyy  h:mm AM/PM\", \"Januar 01 2022  4:32 vorm.\"},\n\t\t{\"43543.503206018519\", \"[$-84]mmm dd yyyy  h:mm AM/PM\", \"Mär 19 2019  12:04 nam.\"},\n\t\t{\"43543.503206018519\", \"[$-84]mmmm dd yyyy  h:mm AM/PM aaa\", \"März 19 2019  12:04 nam. Zi.\"},\n\t\t{\"43543.503206018519\", \"[$-84]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 nam. Zi.\"},\n\t\t{\"43543.503206018519\", \"[$-84]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"März 19 2019  12:04 nam. Ziischtig\"},\n\t\t{\"44562.189571759256\", \"[$-484]mmm dd yyyy  h:mm AM/PM\", \"Jän. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-484]mmmm dd yyyy  h:mm AM/PM\", \"Jänner 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-484]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-484]mmmmmm dd yyyy  h:mm AM/PM\", \"Jänner 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-484]mmm dd yyyy  h:mm AM/PM\", \"März 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-484]mmmm dd yyyy  h:mm AM/PM aaa\", \"März 19 2019  12:04 PM Zi.\"},\n\t\t{\"43543.503206018519\", \"[$-484]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Zi.\"},\n\t\t{\"43543.503206018519\", \"[$-484]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"März 19 2019  12:04 PM Zischti\"},\n\t\t{\"44562.189571759256\", \"[$-5E]mmm dd yyyy  h:mm AM/PM\", \"\\u1303\\u1295\\u12E9 01 2022  4:32 \\u1325\\u12CB\\u1275\"},\n\t\t{\"44562.189571759256\", \"[$-5E]mmmm dd yyyy  h:mm AM/PM\", \"\\u1303\\u1295\\u12E9\\u12C8\\u122A 01 2022  4:32 \\u1325\\u12CB\\u1275\"},\n\t\t{\"44562.189571759256\", \"[$-5E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u1303 01 2022  4:32 \\u1325\\u12CB\\u1275\"},\n\t\t{\"44562.189571759256\", \"[$-5E]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u1303\\u1295\\u12E9\\u12C8\\u122A 01 2022  4:32 \\u1325\\u12CB\\u1275\"},\n\t\t{\"43543.503206018519\", \"[$-5E]mmm dd yyyy  h:mm AM/PM\", \"\\u121B\\u122D\\u127D 19 2019  12:04 \\u12A8\\u1230\\u12D3\\u1275\"},\n\t\t{\"43543.503206018519\", \"[$-5E]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u121B\\u122D\\u127D 19 2019  12:04 \\u12A8\\u1230\\u12D3\\u1275 \\u121B\\u12AD\\u1230\"},\n\t\t{\"43543.503206018519\", \"[$-5E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u121B 19 2019  12:04 \\u12A8\\u1230\\u12D3\\u1275 \\u121B\\u12AD\\u1230\"},\n\t\t{\"43543.503206018519\", \"[$-5E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u121B\\u122D\\u127D 19 2019  12:04 \\u12A8\\u1230\\u12D3\\u1275 \\u121B\\u12AD\\u1230\\u129E\"},\n\t\t{\"44562.189571759256\", \"[$-45E]mmm dd yyyy  h:mm AM/PM\", \"\\u1303\\u1295\\u12E9 01 2022  4:32 \\u1325\\u12CB\\u1275\"},\n\t\t{\"44562.189571759256\", \"[$-45E]mmmm dd yyyy  h:mm AM/PM\", \"\\u1303\\u1295\\u12E9\\u12C8\\u122A 01 2022  4:32 \\u1325\\u12CB\\u1275\"},\n\t\t{\"44562.189571759256\", \"[$-45E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u1303 01 2022  4:32 \\u1325\\u12CB\\u1275\"},\n\t\t{\"44562.189571759256\", \"[$-45E]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u1303\\u1295\\u12E9\\u12C8\\u122A 01 2022  4:32 \\u1325\\u12CB\\u1275\"},\n\t\t{\"43543.503206018519\", \"[$-45E]mmm dd yyyy  h:mm AM/PM\", \"\\u121B\\u122D\\u127D 19 2019  12:04 \\u12A8\\u1230\\u12D3\\u1275\"},\n\t\t{\"43543.503206018519\", \"[$-45E]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u121B\\u122D\\u127D 19 2019  12:04 \\u12A8\\u1230\\u12D3\\u1275 \\u121B\\u12AD\\u1230\"},\n\t\t{\"43543.503206018519\", \"[$-45E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u121B 19 2019  12:04 \\u12A8\\u1230\\u12D3\\u1275 \\u121B\\u12AD\\u1230\"},\n\t\t{\"43543.503206018519\", \"[$-45E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u121B\\u122D\\u127D 19 2019  12:04 \\u12A8\\u1230\\u12D3\\u1275 \\u121B\\u12AD\\u1230\\u129E\"},\n\t\t{\"44562.189571759256\", \"[$-1]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-1]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-1]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-1]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-1]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-1401]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1401]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1401]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1401]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-1401]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-1401]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-1401]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-1401]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-3C01]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-3C01]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-3C01]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-3C01]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-3C01]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-3C01]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-3C01]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-3C01]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-c01]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-c01]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-c01]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-c01]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-c01]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-c01]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-c01]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-c01]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-801]mmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-801]mmmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-801]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0643 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-801]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-801]mmm dd yyyy  h:mm AM/PM\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-801]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-801]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0622 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-801]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-2C01]mmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-2C01]mmmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-2C01]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0643 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-2C01]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-2C01]mmm dd yyyy  h:mm AM/PM\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-2C01]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-2C01]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0622 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-2C01]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-3401]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-3401]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-3401]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-3401]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-3401]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-3401]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-3401]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-3401]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-3001]mmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-3001]mmmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-3001]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0643 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-3001]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-3001]mmm dd yyyy  h:mm AM/PM\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-3001]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-3001]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0622 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-3001]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-1001]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1001]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1001]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1001]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-1001]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-1001]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-1001]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-1001]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-1801]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1801]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1801]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1801]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-1801]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-1801]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-1801]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-1801]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-2001]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-2001]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-2001]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-2001]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-2001]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-2001]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-2001]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-2001]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-4001]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-4001]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-4001]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-4001]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-4001]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-4001]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-4001]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-4001]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-401]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-401]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-401]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-401]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-401]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-401]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-401]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-401]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43466.189571759256\", \"[$-404]g\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u6C11\\u570B\\u5E74\\u0031\\u6708\\u0031\\u65E5\"},\n\t\t{\"43466.189571759256\", \"[$-404]e\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u0031\\u0030\\u0038\\u5E74\\u0031\\u6708\\u0031\\u65E5\"},\n\t\t{\"43466.189571759256\", \"[$-404]ge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u6C11\\u570B\\u0031\\u0030\\u0038\\u5E74\\u0031\\u6708\\u0031\\u65E5\"},\n\t\t{\"43466.189571759256\", \"[$-404]gge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u6C11\\u570B\\u0031\\u0030\\u0038\\u5E74\\u0031\\u6708\\u0031\\u65E5\"},\n\t\t{\"43466.189571759256\", \"[$-404]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4E2D\\u83EF\\u6C11\\u570B\\u0031\\u0030\\u0038\\u5E74\\u0031\\u6708\\u0031\\u65E5\"},\n\t\t{\"43466.189571759256\", \"[$-404]gggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4E2D\\u83EF\\u6C11\\u570B\\u0031\\u0030\\u0038\\u5E74\\u0031\\u6708\\u0031\\u65E5\"},\n\t\t{\"4385.5083333333332\", \"[$-404]ge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u6C11\\u570B\\u5143\\u5E74\\u0031\\u6708\\u0032\\u65E5\"},\n\t\t{\"4385.5083333333332\", \"[$-404]gge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u6C11\\u570B\\u5143\\u5E74\\u0031\\u6708\\u0032\\u65E5\"},\n\t\t{\"4385.5083333333332\", \"[$-404]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4E2D\\u83EF\\u6C11\\u570B\\u5143\\u5E74\\u0031\\u6708\\u0032\\u65E5\"},\n\t\t{\"4385.5083333333332\", \"[$-404]gggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4E2D\\u83EF\\u6C11\\u570B\\u5143\\u5E74\\u0031\\u6708\\u0032\\u65E5\"},\n\t\t{\"123\", \"[$-404]ge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u6C11\\u570B\\u524D\\u0031\\u0032\\u5E74\\u0035\\u6708\\u0032\\u65E5\"},\n\t\t{\"123\", \"[$-404]gge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u6C11\\u570B\\u524D\\u0031\\u0032\\u5E74\\u0035\\u6708\\u0032\\u65E5\"},\n\t\t{\"123\", \"[$-404]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4E2D\\u83EF\\u6C11\\u570B\\u524D\\u0031\\u0032\\u5E74\\u0035\\u6708\\u0032\\u65E5\"},\n\t\t{\"123\", \"[$-404]gggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4E2D\\u83EF\\u6C11\\u570B\\u524D\\u0031\\u0032\\u5E74\\u0035\\u6708\\u0032\\u65E5\"},\n\t\t{\"44562.189571759256\", \"[$-1010401]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1010401]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1010401]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1010401]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-1010401]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-1010401]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-1010401]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-1010401]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-2801]mmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-2801]mmmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-2801]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0643 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-2801]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-2801]mmm dd yyyy  h:mm AM/PM\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-2801]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-2801]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0622 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-2801]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-1C01]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1C01]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1C01]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-1C01]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-1C01]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-1C01]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-1C01]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-1C01]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-3801]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-3801]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-3801]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-3801]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-3801]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-3801]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-3801]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-3801]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-2401]mmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-2401]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-2401]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-2401]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0646\\u0627\\u064A\\u0631 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-2401]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-2401]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-2401]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-2401]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-2B]mmm dd yyyy  h:mm AM/PM\", \"\\u0540\\u0576\\u057E 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-2B]mmmm dd yyyy  h:mm AM/PM\", \"\\u0540\\u0578\\u0582\\u0576\\u057E\\u0561\\u0580 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-2B]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0540 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-2B]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0540\\u0578\\u0582\\u0576\\u057E\\u0561\\u0580 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-2B]mmm dd yyyy  h:mm AM/PM\", \"\\u0544\\u0580\\u057F 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-2B]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0544\\u0561\\u0580\\u057F 19 2019  12:04 PM \\u0535\\u0580\\u0584\"},\n\t\t{\"43543.503206018519\", \"[$-2B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0544 19 2019  12:04 PM \\u0535\\u0580\\u0584\"},\n\t\t{\"43543.503206018519\", \"[$-2B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0544\\u0561\\u0580\\u057F 19 2019  12:04 PM \\u0535\\u0580\\u0565\\u0584\\u0577\\u0561\\u0562\\u0569\\u056B\"},\n\t\t{\"44562.189571759256\", \"[$-42B]mmm dd yyyy  h:mm AM/PM\", \"\\u0540\\u0576\\u057E 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-42B]mmmm dd yyyy  h:mm AM/PM\", \"\\u0540\\u0578\\u0582\\u0576\\u057E\\u0561\\u0580 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-42B]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0540 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-42B]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0540\\u0578\\u0582\\u0576\\u057E\\u0561\\u0580 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-42B]mmm dd yyyy  h:mm AM/PM\", \"\\u0544\\u0580\\u057F 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-42B]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0544\\u0561\\u0580\\u057F 19 2019  12:04 PM \\u0535\\u0580\\u0584\"},\n\t\t{\"43543.503206018519\", \"[$-42B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0544 19 2019  12:04 PM \\u0535\\u0580\\u0584\"},\n\t\t{\"43543.503206018519\", \"[$-42B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0544\\u0561\\u0580\\u057F 19 2019  12:04 PM \\u0535\\u0580\\u0565\\u0584\\u0577\\u0561\\u0562\\u0569\\u056B\"},\n\t\t{\"44562.189571759256\", \"[$-4D]mmm dd yyyy  h:mm AM/PM\", \"\\u099C\\u09BE\\u09A8\\u09C1 01 2022  4:32 \\u09F0\\u09BE\\u09A4\\u09BF\\u09AA\\u09C1\"},\n\t\t{\"44562.189571759256\", \"[$-4D]mmmm dd yyyy  h:mm AM/PM\", \"\\u099C\\u09BE\\u09A8\\u09C1\\u09F1\\u09BE\\u09F0\\u09C0 01 2022  4:32 \\u09F0\\u09BE\\u09A4\\u09BF\\u09AA\\u09C1\"},\n\t\t{\"44562.189571759256\", \"[$-4D]mmmmm dd yyyy  h:mm AM/PM\", \"\\u099C 01 2022  4:32 \\u09F0\\u09BE\\u09A4\\u09BF\\u09AA\\u09C1\"},\n\t\t{\"44562.189571759256\", \"[$-4D]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u099C\\u09BE\\u09A8\\u09C1\\u09F1\\u09BE\\u09F0\\u09C0 01 2022  4:32 \\u09F0\\u09BE\\u09A4\\u09BF\\u09AA\\u09C1\"},\n\t\t{\"43543.503206018519\", \"[$-4D]mmm dd yyyy  h:mm AM/PM\", \"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A 19 2019  12:04 \\u0986\\u09AC\\u09C7\\u09B2\\u09BF\"},\n\t\t{\"43543.503206018519\", \"[$-4D]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A 19 2019  12:04 \\u0986\\u09AC\\u09C7\\u09B2\\u09BF \\u09AE\\u0999\\u09CD\\u0997\\u09B2.\"},\n\t\t{\"43543.503206018519\", \"[$-4D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u09AE 19 2019  12:04 \\u0986\\u09AC\\u09C7\\u09B2\\u09BF \\u09AE\\u0999\\u09CD\\u0997\\u09B2.\"},\n\t\t{\"43543.503206018519\", \"[$-4D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A 19 2019  12:04 \\u0986\\u09AC\\u09C7\\u09B2\\u09BF \\u09AE\\u0999\\u09CD\\u0997\\u09B2\\u09AC\\u09BE\\u09F0\"},\n\t\t{\"44562.189571759256\", \"[$-44D]mmm dd yyyy  h:mm AM/PM\", \"\\u099C\\u09BE\\u09A8\\u09C1 01 2022  4:32 \\u09F0\\u09BE\\u09A4\\u09BF\\u09AA\\u09C1\"},\n\t\t{\"44562.189571759256\", \"[$-44D]mmmm dd yyyy  h:mm AM/PM\", \"\\u099C\\u09BE\\u09A8\\u09C1\\u09F1\\u09BE\\u09F0\\u09C0 01 2022  4:32 \\u09F0\\u09BE\\u09A4\\u09BF\\u09AA\\u09C1\"},\n\t\t{\"44562.189571759256\", \"[$-44D]mmmmm dd yyyy  h:mm AM/PM\", \"\\u099C 01 2022  4:32 \\u09F0\\u09BE\\u09A4\\u09BF\\u09AA\\u09C1\"},\n\t\t{\"44562.189571759256\", \"[$-44D]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u099C\\u09BE\\u09A8\\u09C1\\u09F1\\u09BE\\u09F0\\u09C0 01 2022  4:32 \\u09F0\\u09BE\\u09A4\\u09BF\\u09AA\\u09C1\"},\n\t\t{\"43543.503206018519\", \"[$-44D]mmm dd yyyy  h:mm AM/PM\", \"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A 19 2019  12:04 \\u0986\\u09AC\\u09C7\\u09B2\\u09BF\"},\n\t\t{\"43543.503206018519\", \"[$-44D]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A 19 2019  12:04 \\u0986\\u09AC\\u09C7\\u09B2\\u09BF \\u09AE\\u0999\\u09CD\\u0997\\u09B2.\"},\n\t\t{\"43543.503206018519\", \"[$-44D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u09AE 19 2019  12:04 \\u0986\\u09AC\\u09C7\\u09B2\\u09BF \\u09AE\\u0999\\u09CD\\u0997\\u09B2.\"},\n\t\t{\"43543.503206018519\", \"[$-44D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A 19 2019  12:04 \\u0986\\u09AC\\u09C7\\u09B2\\u09BF \\u09AE\\u0999\\u09CD\\u0997\\u09B2\\u09AC\\u09BE\\u09F0\"},\n\t\t{\"44562.189571759256\", \"[$-742C]mmm dd yyyy  h:mm AM/PM\", \"\\u0408\\u0430\\u043D 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-742C]mmmm dd yyyy  h:mm AM/PM\", \"j\\u0430\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-742C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-742C]mmmmmm dd yyyy  h:mm AM/PM\", \"j\\u0430\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-742C]mmm dd yyyy  h:mm AM/PM\", \"\\u041C\\u0430\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-742C]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0427\\u0430\"},\n\t\t{\"43543.503206018519\", \"[$-742C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0427\\u0430\"},\n\t\t{\"43543.503206018519\", \"[$-742C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0447\\u04D9\\u0440\\u0448\\u04D9\\u043D\\u0431\\u04D9 \\u0430\\u0445\\u0448\\u0430\\u043C\\u044B\"},\n\t\t{\"44562.189571759256\", \"[$-82C]mmm dd yyyy  h:mm AM/PM\", \"\\u0408\\u0430\\u043D 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-82C]mmmm dd yyyy  h:mm AM/PM\", \"j\\u0430\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-82C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-82C]mmmmmm dd yyyy  h:mm AM/PM\", \"j\\u0430\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-82C]mmm dd yyyy  h:mm AM/PM\", \"\\u041C\\u0430\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-82C]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0427\\u0430\"},\n\t\t{\"43543.503206018519\", \"[$-82C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0427\\u0430\"},\n\t\t{\"43543.503206018519\", \"[$-82C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0447\\u04D9\\u0440\\u0448\\u04D9\\u043D\\u0431\\u04D9 \\u0430\\u0445\\u0448\\u0430\\u043C\\u044B\"},\n\t\t{\"44562.189571759256\", \"[$-2C]mmm dd yyyy  h:mm AM/PM\", \"yan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-2C]mmmm dd yyyy  h:mm AM/PM\", \"yanvar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-2C]mmmmm dd yyyy  h:mm AM/PM\", \"y 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-2C]mmmmmm dd yyyy  h:mm AM/PM\", \"yanvar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-2C]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-2C]mmmm dd yyyy  h:mm AM/PM aaa\", \"mart 19 2019  12:04 PM Ç.A.\"},\n\t\t{\"43543.503206018519\", \"[$-2C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM Ç.A.\"},\n\t\t{\"43543.503206018519\", \"[$-2C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mart 19 2019  12:04 PM ç\\u0259r\\u015F\\u0259nb\\u0259 ax\\u015Fam\\u0131\"},\n\t\t{\"44562.189571759256\", \"[$-782C]mmm dd yyyy  h:mm AM/PM\", \"yan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-782C]mmmm dd yyyy  h:mm AM/PM\", \"yanvar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-782C]mmmmm dd yyyy  h:mm AM/PM\", \"y 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-782C]mmmmmm dd yyyy  h:mm AM/PM\", \"yanvar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-782C]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-782C]mmmm dd yyyy  h:mm AM/PM aaa\", \"mart 19 2019  12:04 PM Ç.A.\"},\n\t\t{\"43543.503206018519\", \"[$-782C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM Ç.A.\"},\n\t\t{\"43543.503206018519\", \"[$-782C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mart 19 2019  12:04 PM ç\\u0259r\\u015F\\u0259nb\\u0259 ax\\u015Fam\\u0131\"},\n\t\t{\"44562.189571759256\", \"[$-42C]mmm dd yyyy  h:mm AM/PM\", \"yan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-42C]mmmm dd yyyy  h:mm AM/PM\", \"yanvar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-42C]mmmmm dd yyyy  h:mm AM/PM\", \"y 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-42C]mmmmmm dd yyyy  h:mm AM/PM\", \"yanvar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-42C]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-42C]mmmm dd yyyy  h:mm AM/PM aaa\", \"mart 19 2019  12:04 PM Ç.A.\"},\n\t\t{\"43543.503206018519\", \"[$-42C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM Ç.A.\"},\n\t\t{\"43543.503206018519\", \"[$-42C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mart 19 2019  12:04 PM ç\\u0259r\\u015F\\u0259nb\\u0259 ax\\u015Fam\\u0131\"},\n\t\t{\"43543.503206018519\", \"[$-45]mmm dd yyyy  h:mm AM/PM aaa\", \"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A 19 2019  12:04 PM \\u09AE\\u0999\\u09CD\\u0997\\u09B2.\"},\n\t\t{\"43543.503206018519\", \"[$-45]mmmm dd yyyy  h:mm AM/PM ddd\", \"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A 19 2019  12:04 PM \\u09AE\\u0999\\u09CD\\u0997\\u09B2.\"},\n\t\t{\"43543.503206018519\", \"[$-45]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u09AE 19 2019  12:04 PM \\u09AE\\u0999\\u09CD\\u0997\\u09B2\\u09AC\\u09BE\\u09B0\"},\n\t\t{\"43543.503206018519\", \"[$-845]mmm dd yyyy  h:mm AM/PM aaa\", \"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A 19 2019  12:04 PM \\u09AE\\u0999\\u09CD\\u0997\\u09B2.\"},\n\t\t{\"43543.503206018519\", \"[$-845]mmmm dd yyyy  h:mm AM/PM ddd\", \"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A 19 2019  12:04 PM \\u09AE\\u0999\\u09CD\\u0997\\u09B2.\"},\n\t\t{\"43543.503206018519\", \"[$-845]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u09AE 19 2019  12:04 PM \\u09AE\\u0999\\u09CD\\u0997\\u09B2\\u09AC\\u09BE\\u09B0\"},\n\t\t{\"43543.503206018519\", \"[$-445]mmm dd yyyy  h:mm AM/PM aaa\", \"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A 19 2019  12:04 PM \\u09AE\\u0999\\u09CD\\u0997\\u09B2.\"},\n\t\t{\"43543.503206018519\", \"[$-445]mmmm dd yyyy  h:mm AM/PM ddd\", \"\\u09AE\\u09BE\\u09B0\\u09CD\\u099A 19 2019  12:04 PM \\u09AE\\u0999\\u09CD\\u0997\\u09B2.\"},\n\t\t{\"43543.503206018519\", \"[$-445]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u09AE 19 2019  12:04 PM \\u09AE\\u0999\\u09CD\\u0997\\u09B2\\u09AC\\u09BE\\u09B0\"},\n\t\t{\"44562.189571759256\", \"[$-6D]mmm dd yyyy  h:mm AM/PM\", \"\\u0493\\u0438\\u043D 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6D]mmmm dd yyyy  h:mm AM/PM\", \"\\u0493\\u0438\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6D]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0493 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6D]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0493\\u0438\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-6D]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-6D]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0428\\u0448\"},\n\t\t{\"43543.503206018519\", \"[$-6D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0428\\u0448\"},\n\t\t{\"43543.503206018519\", \"[$-6D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0428\\u0438\\u0448\\u04D9\\u043C\\u0431\\u0435\"},\n\t\t{\"44562.189571759256\", \"[$-46D]mmm dd yyyy  h:mm AM/PM\", \"\\u0493\\u0438\\u043D 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-46D]mmmm dd yyyy  h:mm AM/PM\", \"\\u0493\\u0438\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-46D]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0493 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-46D]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0493\\u0438\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-46D]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-46D]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0428\\u0448\"},\n\t\t{\"43543.503206018519\", \"[$-46D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0428\\u0448\"},\n\t\t{\"43543.503206018519\", \"[$-46D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0428\\u0438\\u0448\\u04D9\\u043C\\u0431\\u0435\"},\n\t\t{\"44562.189571759256\", \"[$-2D]mmm dd yyyy  h:mm AM/PM\", \"urt. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-2D]mmmm dd yyyy  h:mm AM/PM\", \"urtarrila 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-2D]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-2D]mmmmmm dd yyyy  h:mm AM/PM\", \"urtarrila 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-2D]mmm dd yyyy  h:mm AM/PM\", \"mar. 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-2D]mmmm dd yyyy  h:mm AM/PM aaa\", \"martxoa 19 2019  12:04 PM ar.\"},\n\t\t{\"43543.503206018519\", \"[$-2D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM ar.\"},\n\t\t{\"43543.503206018519\", \"[$-2D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"martxoa 19 2019  12:04 PM asteartea\"},\n\t\t{\"44562.189571759256\", \"[$-42D]mmm dd yyyy  h:mm AM/PM\", \"urt. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-42D]mmmm dd yyyy  h:mm AM/PM\", \"urtarrila 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-42D]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-42D]mmmmmm dd yyyy  h:mm AM/PM\", \"urtarrila 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-42D]mmm dd yyyy  h:mm AM/PM\", \"mar. 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-42D]mmmm dd yyyy  h:mm AM/PM aaa\", \"martxoa 19 2019  12:04 PM ar.\"},\n\t\t{\"43543.503206018519\", \"[$-42D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM ar.\"},\n\t\t{\"43543.503206018519\", \"[$-42D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"martxoa 19 2019  12:04 PM asteartea\"},\n\t\t{\"44562.189571759256\", \"[$-23]mmm dd yyyy  h:mm AM/PM\", \"\\u0441\\u0442\\u0443\\u0434\\u0437 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-23]mmmm dd yyyy  h:mm AM/PM\", \"\\u0441\\u0442\\u0443\\u0434\\u0437\\u0435\\u043D\\u044C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-23]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0441 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-23]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0441\\u0442\\u0443\\u0434\\u0437\\u0435\\u043D\\u044C 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-23]mmm dd yyyy  h:mm AM/PM\", \"\\u0441\\u0430\\u043A 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-23]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0441\\u0430\\u043A\\u0430\\u0432\\u0456\\u043A 19 2019  12:04 PM \\u0430\\u045E\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-23]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0441 19 2019  12:04 PM \\u0430\\u045E\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-23]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0441\\u0430\\u043A\\u0430\\u0432\\u0456\\u043A 19 2019  12:04 PM \\u0430\\u045E\\u0442\\u043E\\u0440\\u0430\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-423]mmm dd yyyy  h:mm AM/PM\", \"\\u0441\\u0442\\u0443\\u0434\\u0437 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-423]mmmm dd yyyy  h:mm AM/PM\", \"\\u0441\\u0442\\u0443\\u0434\\u0437\\u0435\\u043D\\u044C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-423]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0441 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-423]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0441\\u0442\\u0443\\u0434\\u0437\\u0435\\u043D\\u044C 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-423]mmm dd yyyy  h:mm AM/PM\", \"\\u0441\\u0430\\u043A 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-423]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0441\\u0430\\u043A\\u0430\\u0432\\u0456\\u043A 19 2019  12:04 PM \\u0430\\u045E\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-423]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0441 19 2019  12:04 PM \\u0430\\u045E\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-423]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0441\\u0430\\u043A\\u0430\\u0432\\u0456\\u043A 19 2019  12:04 PM \\u0430\\u045E\\u0442\\u043E\\u0440\\u0430\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-641A]mmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-641A]mmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-641A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0458 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-641A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-641A]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-641A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442\\u043E\"},\n\t\t{\"43543.503206018519\", \"[$-641A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0443\\u0442\\u043E\"},\n\t\t{\"43543.503206018519\", \"[$-641A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442\\u043E\\u0440\\u0430\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-201A]mmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-201A]mmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-201A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0458 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-201A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-201A]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-201A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442\\u043E\"},\n\t\t{\"43543.503206018519\", \"[$-201A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0443\\u0442\\u043E\"},\n\t\t{\"43543.503206018519\", \"[$-201A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442\\u043E\\u0440\\u0430\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-681A]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-681A]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-681A]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-681A]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-681A]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-681A]mmmm dd yyyy  h:mm AM/PM aaa\", \"mart 19 2019  12:04 PM uto\"},\n\t\t{\"43543.503206018519\", \"[$-681A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM uto\"},\n\t\t{\"43543.503206018519\", \"[$-681A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mart 19 2019  12:04 PM utorak\"},\n\t\t{\"44562.189571759256\", \"[$-781A]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-781A]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-781A]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-781A]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-781A]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-781A]mmmm dd yyyy  h:mm AM/PM aaa\", \"mart 19 2019  12:04 PM uto\"},\n\t\t{\"43543.503206018519\", \"[$-781A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM uto\"},\n\t\t{\"43543.503206018519\", \"[$-781A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mart 19 2019  12:04 PM utorak\"},\n\t\t{\"44562.189571759256\", \"[$-141A]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-141A]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-141A]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-141A]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-141A]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-141A]mmmm dd yyyy  h:mm AM/PM aaa\", \"mart 19 2019  12:04 PM uto\"},\n\t\t{\"43543.503206018519\", \"[$-141A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM uto\"},\n\t\t{\"43543.503206018519\", \"[$-141A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mart 19 2019  12:04 PM utorak\"},\n\t\t{\"44562.189571759256\", \"[$-7E]mmm dd yyyy  h:mm AM/PM\", \"Gen. 01 2022  4:32 A.M.\"},\n\t\t{\"44562.189571759256\", \"[$-7E]mmmm dd yyyy  h:mm AM/PM\", \"Genver 01 2022  4:32 A.M.\"},\n\t\t{\"44562.189571759256\", \"[$-7E]mmmmm dd yyyy  h:mm AM/PM\", \"G 01 2022  4:32 A.M.\"},\n\t\t{\"44562.189571759256\", \"[$-7E]mmmmmm dd yyyy  h:mm AM/PM\", \"Genver 01 2022  4:32 A.M.\"},\n\t\t{\"43543.503206018519\", \"[$-7E]mmm dd yyyy  h:mm AM/PM\", \"Meur. 19 2019  12:04 G.M.\"},\n\t\t{\"43543.503206018519\", \"[$-7E]mmmm dd yyyy  h:mm AM/PM aaa\", \"Meurzh 19 2019  12:04 G.M. Meu.\"},\n\t\t{\"43543.503206018519\", \"[$-7E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 G.M. Meu.\"},\n\t\t{\"43543.503206018519\", \"[$-7E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Meurzh 19 2019  12:04 G.M. Meurzh\"},\n\t\t{\"44562.189571759256\", \"[$-47E]mmm dd yyyy  h:mm AM/PM\", \"Gen. 01 2022  4:32 A.M.\"},\n\t\t{\"44562.189571759256\", \"[$-47E]mmmm dd yyyy  h:mm AM/PM\", \"Genver 01 2022  4:32 A.M.\"},\n\t\t{\"44562.189571759256\", \"[$-47E]mmmmm dd yyyy  h:mm AM/PM\", \"G 01 2022  4:32 A.M.\"},\n\t\t{\"44562.189571759256\", \"[$-47E]mmmmmm dd yyyy  h:mm AM/PM\", \"Genver 01 2022  4:32 A.M.\"},\n\t\t{\"43543.503206018519\", \"[$-47E]mmm dd yyyy  h:mm AM/PM\", \"Meur. 19 2019  12:04 G.M.\"},\n\t\t{\"43543.503206018519\", \"[$-47E]mmmm dd yyyy  h:mm AM/PM aaa\", \"Meurzh 19 2019  12:04 G.M. Meu.\"},\n\t\t{\"43543.503206018519\", \"[$-47E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 G.M. Meu.\"},\n\t\t{\"43543.503206018519\", \"[$-47E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Meurzh 19 2019  12:04 G.M. Meurzh\"},\n\t\t{\"44562.189571759256\", \"[$-2]mmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0443 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-2]mmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0443\\u0430\\u0440\\u0438 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-2]mmmmm dd yyyy  h:mm AM/PM\", \"\\u044F 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-2]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0443\\u0430\\u0440\\u0438 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-2]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-2]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0432\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-2]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0432\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-2]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0432\\u0442\\u043E\\u0440\\u043D\\u0438\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-402]mmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0443 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-402]mmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0443\\u0430\\u0440\\u0438 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-402]mmmmm dd yyyy  h:mm AM/PM\", \"\\u044F 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-402]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0443\\u0430\\u0440\\u0438 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-402]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-402]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0432\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-402]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0432\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-402]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0432\\u0442\\u043E\\u0440\\u043D\\u0438\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-55]mmm dd yyyy  h:mm AM/PM\", \"\\u1007\\u1014\\u103A 01 2022  4:32 \\u1014\\u1036\\u1014\\u1000\\u103A\"},\n\t\t{\"44562.189571759256\", \"[$-55]mmmm dd yyyy  h:mm AM/PM\", \"\\u1007\\u1014\\u103A\\u1014\\u101D\\u102B\\u101B\\u102E 01 2022  4:32 \\u1014\\u1036\\u1014\\u1000\\u103A\"},\n\t\t{\"44562.189571759256\", \"[$-55]mmmmm dd yyyy  h:mm AM/PM\", \"\\u1007 01 2022  4:32 \\u1014\\u1036\\u1014\\u1000\\u103A\"},\n\t\t{\"44562.189571759256\", \"[$-55]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u1007\\u1014\\u103A\\u1014\\u101D\\u102B\\u101B\\u102E 01 2022  4:32 \\u1014\\u1036\\u1014\\u1000\\u103A\"},\n\t\t{\"43543.503206018519\", \"[$-55]mmm dd yyyy  h:mm AM/PM\", \"\\u1019\\u1010\\u103A 19 2019  12:04 \\u100A\\u1014\\u1031\"},\n\t\t{\"43543.503206018519\", \"[$-55]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u1019\\u1010\\u103A 19 2019  12:04 \\u100A\\u1014\\u1031 \\u1021\\u1004\\u103A\\u1039\\u1002\\u102B\"},\n\t\t{\"43543.503206018519\", \"[$-55]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u1019 19 2019  12:04 \\u100A\\u1014\\u1031 \\u1021\\u1004\\u103A\\u1039\\u1002\\u102B\"},\n\t\t{\"43543.503206018519\", \"[$-55]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u1019\\u1010\\u103A 19 2019  12:04 \\u100A\\u1014\\u1031 \\u1021\\u1004\\u103A\\u1039\\u1002\\u102B\"},\n\t\t{\"44562.189571759256\", \"[$-455]mmm dd yyyy  h:mm AM/PM\", \"\\u1007\\u1014\\u103A 01 2022  4:32 \\u1014\\u1036\\u1014\\u1000\\u103A\"},\n\t\t{\"44562.189571759256\", \"[$-455]mmmm dd yyyy  h:mm AM/PM\", \"\\u1007\\u1014\\u103A\\u1014\\u101D\\u102B\\u101B\\u102E 01 2022  4:32 \\u1014\\u1036\\u1014\\u1000\\u103A\"},\n\t\t{\"44562.189571759256\", \"[$-455]mmmmm dd yyyy  h:mm AM/PM\", \"\\u1007 01 2022  4:32 \\u1014\\u1036\\u1014\\u1000\\u103A\"},\n\t\t{\"44562.189571759256\", \"[$-455]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u1007\\u1014\\u103A\\u1014\\u101D\\u102B\\u101B\\u102E 01 2022  4:32 \\u1014\\u1036\\u1014\\u1000\\u103A\"},\n\t\t{\"43543.503206018519\", \"[$-455]mmm dd yyyy  h:mm AM/PM\", \"\\u1019\\u1010\\u103A 19 2019  12:04 \\u100A\\u1014\\u1031\"},\n\t\t{\"43543.503206018519\", \"[$-455]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u1019\\u1010\\u103A 19 2019  12:04 \\u100A\\u1014\\u1031 \\u1021\\u1004\\u103A\\u1039\\u1002\\u102B\"},\n\t\t{\"43543.503206018519\", \"[$-455]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u1019 19 2019  12:04 \\u100A\\u1014\\u1031 \\u1021\\u1004\\u103A\\u1039\\u1002\\u102B\"},\n\t\t{\"43543.503206018519\", \"[$-455]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u1019\\u1010\\u103A 19 2019  12:04 \\u100A\\u1014\\u1031 \\u1021\\u1004\\u103A\\u1039\\u1002\\u102B\"},\n\t\t{\"44562.189571759256\", \"[$-3]mmm dd yyyy  h:mm AM/PM\", \"gen. 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-3]mmmm dd yyyy  h:mm AM/PM\", \"gener 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-3]mmmmm dd yyyy  h:mm AM/PM\", \"g 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-3]mmmmmm dd yyyy  h:mm AM/PM\", \"gener 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-3]mmm dd yyyy  h:mm AM/PM\", \"març 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-3]mmmm dd yyyy  h:mm AM/PM aaa\", \"març 19 2019  12:04 p. m. dt.\"},\n\t\t{\"43543.503206018519\", \"[$-3]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 p. m. dt.\"},\n\t\t{\"43543.503206018519\", \"[$-3]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"març 19 2019  12:04 p. m. dimarts\"},\n\t\t{\"44562.189571759256\", \"[$-403]mmm dd yyyy  h:mm AM/PM\", \"gen. 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-403]mmmm dd yyyy  h:mm AM/PM\", \"gener 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-403]mmmmm dd yyyy  h:mm AM/PM\", \"g 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-403]mmmmmm dd yyyy  h:mm AM/PM\", \"gener 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-403]mmm dd yyyy  h:mm AM/PM\", \"març 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-403]mmmm dd yyyy  h:mm AM/PM aaa\", \"març 19 2019  12:04 p. m. dt.\"},\n\t\t{\"43543.503206018519\", \"[$-403]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 p. m. dt.\"},\n\t\t{\"43543.503206018519\", \"[$-403]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"març 19 2019  12:04 p. m. dimarts\"},\n\t\t{\"44562.189571759256\", \"[$-45F]mmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-45F]mmmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-45F]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0643 01 2022  4:32 \\u0635\"},\n\t\t{\"44562.189571759256\", \"[$-45F]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0643\\u0627\\u0646\\u0648\\u0646 \\u0627\\u0644\\u062B\\u0627\\u0646\\u064A 01 2022  4:32 \\u0635\"},\n\t\t{\"43543.503206018519\", \"[$-45F]mmm dd yyyy  h:mm AM/PM\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645\"},\n\t\t{\"43543.503206018519\", \"[$-45F]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-45F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0622 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"43543.503206018519\", \"[$-45F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0622\\u0630\\u0627\\u0631 19 2019  12:04 \\u0645 \\u0627\\u0644\\u062B\\u0644\\u0627\\u062B\\u0627\\u0621\"},\n\t\t{\"44562.189571759256\", \"[$-92]mmm dd yyyy  h:mm AM/PM\", \"\\u06A9\\u0627\\u0646\\u0648\\u0648\\u0646\\u06CC \\u062F\\u0648\\u0648\\u06D5\\u0645 01 2022  4:32 \\u067E.\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-92]mmmm dd yyyy  h:mm AM/PM\", \"\\u06A9\\u0627\\u0646\\u0648\\u0648\\u0646\\u06CC \\u062F\\u0648\\u0648\\u06D5\\u0645 01 2022  4:32 \\u067E.\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-92]mmmmm dd yyyy  h:mm AM/PM\", \"\\u06A9 01 2022  4:32 \\u067E.\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-92]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u06A9\\u0627\\u0646\\u0648\\u0648\\u0646\\u06CC \\u062F\\u0648\\u0648\\u06D5\\u0645 01 2022  4:32 \\u067E.\\u0646\"},\n\t\t{\"43543.503206018519\", \"[$-92]mmm dd yyyy  h:mm AM/PM\", \"\\u0626\\u0627\\u0632\\u0627\\u0631 19 2019  12:04 \\u062F.\\u0646\"},\n\t\t{\"43543.503206018519\", \"[$-92]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0626\\u0627\\u0632\\u0627\\u0631 19 2019  12:04 \\u062F.\\u0646 \\u0633\\u06CE\\u0634\\u06D5\\u0645\\u0645\\u06D5\"},\n\t\t{\"43543.503206018519\", \"[$-92]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0626 19 2019  12:04 \\u062F.\\u0646 \\u0633\\u06CE\\u0634\\u06D5\\u0645\\u0645\\u06D5\"},\n\t\t{\"43543.503206018519\", \"[$-92]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0626\\u0627\\u0632\\u0627\\u0631 19 2019  12:04 \\u062F.\\u0646 \\u0633\\u06CE\\u0634\\u06D5\\u0645\\u0645\\u06D5\"},\n\t\t{\"44562.189571759256\", \"[$-7C92]mmm dd yyyy  h:mm AM/PM\", \"\\u06A9\\u0627\\u0646\\u0648\\u0648\\u0646\\u06CC \\u062F\\u0648\\u0648\\u06D5\\u0645 01 2022  4:32 \\u067E.\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-7C92]mmmm dd yyyy  h:mm AM/PM\", \"\\u06A9\\u0627\\u0646\\u0648\\u0648\\u0646\\u06CC \\u062F\\u0648\\u0648\\u06D5\\u0645 01 2022  4:32 \\u067E.\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-7C92]mmmmm dd yyyy  h:mm AM/PM\", \"\\u06A9 01 2022  4:32 \\u067E.\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-7C92]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u06A9\\u0627\\u0646\\u0648\\u0648\\u0646\\u06CC \\u062F\\u0648\\u0648\\u06D5\\u0645 01 2022  4:32 \\u067E.\\u0646\"},\n\t\t{\"43543.503206018519\", \"[$-7C92]mmm dd yyyy  h:mm AM/PM\", \"\\u0626\\u0627\\u0632\\u0627\\u0631 19 2019  12:04 \\u062F.\\u0646\"},\n\t\t{\"43543.503206018519\", \"[$-7C92]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0626\\u0627\\u0632\\u0627\\u0631 19 2019  12:04 \\u062F.\\u0646 \\u0633\\u06CE\\u0634\\u06D5\\u0645\\u0645\\u06D5\"},\n\t\t{\"43543.503206018519\", \"[$-7C92]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0626 19 2019  12:04 \\u062F.\\u0646 \\u0633\\u06CE\\u0634\\u06D5\\u0645\\u0645\\u06D5\"},\n\t\t{\"43543.503206018519\", \"[$-7C92]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0626\\u0627\\u0632\\u0627\\u0631 19 2019  12:04 \\u062F.\\u0646 \\u0633\\u06CE\\u0634\\u06D5\\u0645\\u0645\\u06D5\"},\n\n\t\t{\"44562.189571759256\", \"[$-492]mmm dd yyyy  h:mm AM/PM\", \"\\u06A9\\u0627\\u0646\\u0648\\u0648\\u0646\\u06CC \\u062F\\u0648\\u0648\\u06D5\\u0645 01 2022  4:32 \\u067E.\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-492]mmmm dd yyyy  h:mm AM/PM\", \"\\u06A9\\u0627\\u0646\\u0648\\u0648\\u0646\\u06CC \\u062F\\u0648\\u0648\\u06D5\\u0645 01 2022  4:32 \\u067E.\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-492]mmmmm dd yyyy  h:mm AM/PM\", \"\\u06A9 01 2022  4:32 \\u067E.\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-492]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u06A9\\u0627\\u0646\\u0648\\u0648\\u0646\\u06CC \\u062F\\u0648\\u0648\\u06D5\\u0645 01 2022  4:32 \\u067E.\\u0646\"},\n\t\t{\"43543.503206018519\", \"[$-492]mmm dd yyyy  h:mm AM/PM\", \"\\u0626\\u0627\\u0632\\u0627\\u0631 19 2019  12:04 \\u062F.\\u0646\"},\n\t\t{\"43543.503206018519\", \"[$-492]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0626\\u0627\\u0632\\u0627\\u0631 19 2019  12:04 \\u062F.\\u0646 \\u0633\\u06CE\\u0634\\u06D5\\u0645\\u0645\\u06D5\"},\n\t\t{\"43543.503206018519\", \"[$-492]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0626 19 2019  12:04 \\u062F.\\u0646 \\u0633\\u06CE\\u0634\\u06D5\\u0645\\u0645\\u06D5\"},\n\t\t{\"43543.503206018519\", \"[$-492]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0626\\u0627\\u0632\\u0627\\u0631 19 2019  12:04 \\u062F.\\u0646 \\u0633\\u06CE\\u0634\\u06D5\\u0645\\u0645\\u06D5\"},\n\t\t{\"44562.189571759256\", \"[$-5C]mmm dd yyyy  h:mm AM/PM\", \"\\u13A4\\u13C3\\u13B8 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-5C]mmmm dd yyyy  h:mm AM/PM\", \"\\u13A4\\u13C3\\u13B8\\u13D4\\u13C5 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-5C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u13A4 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-5C]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u13A4\\u13C3\\u13B8\\u13D4\\u13C5 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-5C]mmm dd yyyy  h:mm AM/PM\", \"\\u13A0\\u13C5\\u13F1 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-5C]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u13A0\\u13C5\\u13F1 19 2019  12:04 PM \\u13D4\\u13B5\\u13C1\"},\n\t\t{\"43543.503206018519\", \"[$-5C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u13A0 19 2019  12:04 PM \\u13D4\\u13B5\\u13C1\"},\n\t\t{\"43543.503206018519\", \"[$-5C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u13A0\\u13C5\\u13F1 19 2019  12:04 PM \\u13D4\\u13B5\\u13C1\\u13A2\\u13A6\"},\n\t\t{\"44562.189571759256\", \"[$-7C5C]mmm dd yyyy  h:mm AM/PM\", \"\\u13A4\\u13C3\\u13B8 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C5C]mmmm dd yyyy  h:mm AM/PM\", \"\\u13A4\\u13C3\\u13B8\\u13D4\\u13C5 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C5C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u13A4 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C5C]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u13A4\\u13C3\\u13B8\\u13D4\\u13C5 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-7C5C]mmm dd yyyy  h:mm AM/PM\", \"\\u13A0\\u13C5\\u13F1 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-7C5C]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u13A0\\u13C5\\u13F1 19 2019  12:04 PM \\u13D4\\u13B5\\u13C1\"},\n\t\t{\"43543.503206018519\", \"[$-7C5C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u13A0 19 2019  12:04 PM \\u13D4\\u13B5\\u13C1\"},\n\t\t{\"43543.503206018519\", \"[$-7C5C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u13A0\\u13C5\\u13F1 19 2019  12:04 PM \\u13D4\\u13B5\\u13C1\\u13A2\\u13A6\"},\n\t\t{\"44562.189571759256\", \"[$-45C]mmm dd yyyy  h:mm AM/PM\", \"\\u13A4\\u13C3\\u13B8 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-45C]mmmm dd yyyy  h:mm AM/PM\", \"\\u13A4\\u13C3\\u13B8\\u13D4\\u13C5 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-45C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u13A4 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-45C]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u13A4\\u13C3\\u13B8\\u13D4\\u13C5 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-45C]mmm dd yyyy  h:mm AM/PM\", \"\\u13A0\\u13C5\\u13F1 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-45C]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u13A0\\u13C5\\u13F1 19 2019  12:04 PM \\u13D4\\u13B5\\u13C1\"},\n\t\t{\"43543.503206018519\", \"[$-45C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u13A0 19 2019  12:04 PM \\u13D4\\u13B5\\u13C1\"},\n\t\t{\"43543.503206018519\", \"[$-45C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u13A0\\u13C5\\u13F1 19 2019  12:04 PM \\u13D4\\u13B5\\u13C1\\u13A2\\u13A6\"},\n\t\t{\"43543.503206018519\", \"[$-4]mmm dd yyyy  h:mm AM/PM aaa\", \"3月 19 2019  12:04 下午 週二\"},\n\t\t{\"43543.503206018519\", \"[$-4]mmmm dd yyyy  h:mm AM/PM ddd\", \"三月 19 2019  12:04 下午 週二\"},\n\t\t{\"43543.503206018519\", \"[$-4]mmmmm dd yyyy  h:mm AM/PM dddd\", \"三 19 2019  12:04 下午 星期二\"},\n\t\t{\"43543.503206018519\", \"[$-7804]mmm dd yyyy  h:mm AM/PM aaa\", \"3月 19 2019  12:04 下午 週二\"},\n\t\t{\"43543.503206018519\", \"[$-7804]mmmm dd yyyy  h:mm AM/PM ddd\", \"三月 19 2019  12:04 下午 週二\"},\n\t\t{\"43543.503206018519\", \"[$-7804]mmmmm dd yyyy  h:mm AM/PM dddd\", \"三 19 2019  12:04 下午 星期二\"},\n\t\t{\"43543.503206018519\", \"[$-804]mmm dd yyyy  h:mm AM/PM aaa\", \"3月 19 2019  12:04 下午 周二\"},\n\t\t{\"43543.503206018519\", \"[$-804]mmmm dd yyyy  h:mm AM/PM ddd\", \"三月 19 2019  12:04 下午 周二\"},\n\t\t{\"43543.503206018519\", \"[$-804]mmmmm dd yyyy  h:mm AM/PM dddd\", \"三 19 2019  12:04 下午 星期二\"},\n\t\t{\"43543.503206018519\", \"[$-1004]mmm dd yyyy  h:mm AM/PM aaa\", \"三月 19 2019  12:04 下午 周二\"},\n\t\t{\"43543.503206018519\", \"[$-1004]mmmm dd yyyy  h:mm AM/PM ddd\", \"三月 19 2019  12:04 下午 周二\"},\n\t\t{\"43543.503206018519\", \"[$-1004]mmmmm dd yyyy  h:mm AM/PM dddd\", \"三 19 2019  12:04 下午 星期二\"},\n\t\t{\"43543.503206018519\", \"[$-7C04]mmm dd yyyy  h:mm AM/PM aaa\", \"3月 19 2019  12:04 下午 週二\"},\n\t\t{\"43543.503206018519\", \"[$-7C04]mmmm dd yyyy  h:mm AM/PM ddd\", \"3月 19 2019  12:04 下午 週二\"},\n\t\t{\"43543.503206018519\", \"[$-7C04]mmmmm dd yyyy  h:mm AM/PM dddd\", \"3 19 2019  12:04 下午 星期二\"},\n\t\t{\"43543.503206018519\", \"[$-C04]mmm dd yyyy  h:mm AM/PM aaa\", \"三月 19 2019  12:04 下午 週二\"},\n\t\t{\"43543.503206018519\", \"[$-C04]mmmm dd yyyy  h:mm AM/PM ddd\", \"三月 19 2019  12:04 下午 週二\"},\n\t\t{\"43543.503206018519\", \"[$-C04]mmmmm dd yyyy  h:mm AM/PM dddd\", \"三 19 2019  12:04 下午 星期二\"},\n\t\t{\"43543.503206018519\", \"[$-1404]mmm dd yyyy  h:mm AM/PM aaa\", \"3月 19 2019  12:04 下午 週二\"},\n\t\t{\"43543.503206018519\", \"[$-1404]mmmm dd yyyy  h:mm AM/PM ddd\", \"3月 19 2019  12:04 下午 週二\"},\n\t\t{\"43543.503206018519\", \"[$-1404]mmmmm dd yyyy  h:mm AM/PM dddd\", \"3 19 2019  12:04 下午 星期二\"},\n\t\t{\"43543.503206018519\", \"[$-404]mmm dd yyyy  h:mm AM/PM aaa\", \"3月 19 2019  12:04 下午 週二\"},\n\t\t{\"43543.503206018519\", \"[$-404]mmmm dd yyyy  h:mm AM/PM ddd\", \"3月 19 2019  12:04 下午 週二\"},\n\t\t{\"43543.503206018519\", \"[$-404]mmmmm dd yyyy  h:mm AM/PM dddd\", \"3 19 2019  12:04 下午 星期二\"},\n\t\t{\"44562.189571759256\", \"[$-83]mmm dd yyyy  h:mm AM/PM\", \"ghje 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-83]mmm dd yyyy  h:mm AM/PM\", \"ferr 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-83]mmm dd yyyy  h:mm AM/PM\", \"marz 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-83]mmm dd yyyy  h:mm AM/PM\", \"apri 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-83]mmm dd yyyy  h:mm AM/PM\", \"magh 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-83]mmm dd yyyy  h:mm AM/PM\", \"ghju 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-83]mmm dd yyyy  h:mm AM/PM\", \"lugl 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-83]mmm dd yyyy  h:mm AM/PM\", \"aost 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-83]mmm dd yyyy  h:mm AM/PM\", \"sett 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-83]mmm dd yyyy  h:mm AM/PM\", \"otto 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-83]mmm dd yyyy  h:mm AM/PM\", \"nuve 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-83]mmm dd yyyy  h:mm AM/PM\", \"dice 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-83]mmmm dd yyyy  h:mm AM/PM\", \"ghjennaghju 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-83]mmmm dd yyyy  h:mm AM/PM\", \"ferraghju 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-83]mmmm dd yyyy  h:mm AM/PM\", \"marzu 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-83]mmmm dd yyyy  h:mm AM/PM\", \"aprile 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-83]mmmm dd yyyy  h:mm AM/PM\", \"maghju 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-83]mmmm dd yyyy  h:mm AM/PM\", \"ghjunghju 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-83]mmmm dd yyyy  h:mm AM/PM\", \"lugliu 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-83]mmmm dd yyyy  h:mm AM/PM\", \"aostu 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-83]mmmm dd yyyy  h:mm AM/PM\", \"settembre 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-83]mmmm dd yyyy  h:mm AM/PM\", \"ottobre 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-83]mmmm dd yyyy  h:mm AM/PM\", \"nuvembre 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-83]mmmm dd yyyy  h:mm AM/PM\", \"dicembre 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-83]mmmmm dd yyyy  h:mm AM/PM\", \"g 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-83]mmmmm dd yyyy  h:mm AM/PM\", \"f 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-83]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-83]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-83]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-83]mmmmm dd yyyy  h:mm AM/PM\", \"g 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-83]mmmmm dd yyyy  h:mm AM/PM\", \"l 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-83]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-83]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-83]mmmmm dd yyyy  h:mm AM/PM aaa\", \"o 01 2022  4:32 AM sab.\"},\n\t\t{\"44866.18957170139\", \"[$-83]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 01 2022  4:32 AM mar.\"},\n\t\t{\"44896.18957170139\", \"[$-83]mmmmm dd yyyy  h:mm AM/PM dddd\", \"d 01 2022  4:32 AM ghjovi\"},\n\t\t{\"44562.189571759256\", \"[$-483]mmm dd yyyy  h:mm AM/PM\", \"ghje 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-483]mmm dd yyyy  h:mm AM/PM\", \"ferr 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-483]mmm dd yyyy  h:mm AM/PM\", \"marz 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-483]mmm dd yyyy  h:mm AM/PM\", \"apri 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-483]mmm dd yyyy  h:mm AM/PM\", \"magh 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-483]mmm dd yyyy  h:mm AM/PM\", \"ghju 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-483]mmm dd yyyy  h:mm AM/PM\", \"lugl 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-483]mmm dd yyyy  h:mm AM/PM\", \"aost 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-483]mmm dd yyyy  h:mm AM/PM\", \"sett 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-483]mmm dd yyyy  h:mm AM/PM\", \"otto 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-483]mmm dd yyyy  h:mm AM/PM\", \"nuve 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-483]mmm dd yyyy  h:mm AM/PM\", \"dice 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-483]mmmm dd yyyy  h:mm AM/PM\", \"ghjennaghju 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-483]mmmm dd yyyy  h:mm AM/PM\", \"ferraghju 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-483]mmmm dd yyyy  h:mm AM/PM\", \"marzu 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-483]mmmm dd yyyy  h:mm AM/PM\", \"aprile 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-483]mmmm dd yyyy  h:mm AM/PM\", \"maghju 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-483]mmmm dd yyyy  h:mm AM/PM\", \"ghjunghju 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-483]mmmm dd yyyy  h:mm AM/PM\", \"lugliu 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-483]mmmm dd yyyy  h:mm AM/PM\", \"aostu 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-483]mmmm dd yyyy  h:mm AM/PM\", \"settembre 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-483]mmmm dd yyyy  h:mm AM/PM\", \"ottobre 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-483]mmmm dd yyyy  h:mm AM/PM\", \"nuvembre 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-483]mmmm dd yyyy  h:mm AM/PM\", \"dicembre 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-483]mmmmm dd yyyy  h:mm AM/PM\", \"g 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-483]mmmmm dd yyyy  h:mm AM/PM\", \"f 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-483]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-483]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-483]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-483]mmmmm dd yyyy  h:mm AM/PM\", \"g 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-483]mmmmm dd yyyy  h:mm AM/PM\", \"l 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-483]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-483]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-483]mmmmm dd yyyy  h:mm AM/PM aaa\", \"o 01 2022  4:32 AM sab.\"},\n\t\t{\"44866.18957170139\", \"[$-483]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 01 2022  4:32 AM mar.\"},\n\t\t{\"44896.18957170139\", \"[$-483]mmmmm dd yyyy  h:mm AM/PM dddd\", \"d 01 2022  4:32 AM ghjovi\"},\n\t\t{\"44562.189571759256\", \"[$-1A]mmm dd yyyy  h:mm AM/PM\", \"sij 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-1A]mmm dd yyyy  h:mm AM/PM\", \"vlj 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-1A]mmm dd yyyy  h:mm AM/PM\", \"ožu 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-1A]mmm dd yyyy  h:mm AM/PM\", \"tra 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-1A]mmm dd yyyy  h:mm AM/PM\", \"svi 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-1A]mmm dd yyyy  h:mm AM/PM\", \"lip 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-1A]mmm dd yyyy  h:mm AM/PM\", \"srp 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-1A]mmm dd yyyy  h:mm AM/PM\", \"kol 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-1A]mmm dd yyyy  h:mm AM/PM\", \"ruj 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-1A]mmm dd yyyy  h:mm AM/PM\", \"lis 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-1A]mmm dd yyyy  h:mm AM/PM\", \"stu 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-1A]mmm dd yyyy  h:mm AM/PM\", \"pro 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1A]mmmm dd yyyy  h:mm AM/PM\", \"siječanj 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-1A]mmmm dd yyyy  h:mm AM/PM\", \"veljača 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-1A]mmmm dd yyyy  h:mm AM/PM\", \"ožujak 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-1A]mmmm dd yyyy  h:mm AM/PM\", \"travanj 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-1A]mmmm dd yyyy  h:mm AM/PM\", \"svibanj 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-1A]mmmm dd yyyy  h:mm AM/PM\", \"lipanj 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-1A]mmmm dd yyyy  h:mm AM/PM\", \"srpanj 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-1A]mmmm dd yyyy  h:mm AM/PM\", \"kolovoz 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-1A]mmmm dd yyyy  h:mm AM/PM\", \"rujan 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-1A]mmmm dd yyyy  h:mm AM/PM\", \"listopad 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-1A]mmmm dd yyyy  h:mm AM/PM\", \"studeni 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-1A]mmmm dd yyyy  h:mm AM/PM\", \"prosinac 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1A]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-1A]mmmmm dd yyyy  h:mm AM/PM\", \"v 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-1A]mmmmm dd yyyy  h:mm AM/PM\", \"o 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-1A]mmmmm dd yyyy  h:mm AM/PM\", \"t 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-1A]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-1A]mmmmm dd yyyy  h:mm AM/PM\", \"l 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-1A]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-1A]mmmmm dd yyyy  h:mm AM/PM\", \"k 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-1A]mmmmm dd yyyy  h:mm AM/PM\", \"r 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-1A]mmmmm dd yyyy  h:mm AM/PM aaa\", \"l 01 2022  4:32 AM sub\"},\n\t\t{\"44866.18957170139\", \"[$-1A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"s 01 2022  4:32 AM uto\"},\n\t\t{\"44896.18957170139\", \"[$-1A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"p 01 2022  4:32 AM četvrtak\"},\n\t\t{\"44562.189571759256\", \"[$-41A]mmm dd yyyy  h:mm AM/PM\", \"sij 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-41A]mmm dd yyyy  h:mm AM/PM\", \"vlj 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-41A]mmm dd yyyy  h:mm AM/PM\", \"ožu 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-41A]mmm dd yyyy  h:mm AM/PM\", \"tra 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-41A]mmm dd yyyy  h:mm AM/PM\", \"svi 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-41A]mmm dd yyyy  h:mm AM/PM\", \"lip 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-41A]mmm dd yyyy  h:mm AM/PM\", \"srp 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-41A]mmm dd yyyy  h:mm AM/PM\", \"kol 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-41A]mmm dd yyyy  h:mm AM/PM\", \"ruj 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-41A]mmm dd yyyy  h:mm AM/PM\", \"lis 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-41A]mmm dd yyyy  h:mm AM/PM\", \"stu 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-41A]mmm dd yyyy  h:mm AM/PM\", \"pro 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41A]mmmm dd yyyy  h:mm AM/PM\", \"siječanj 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-41A]mmmm dd yyyy  h:mm AM/PM\", \"veljača 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-41A]mmmm dd yyyy  h:mm AM/PM\", \"ožujak 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-41A]mmmm dd yyyy  h:mm AM/PM\", \"travanj 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-41A]mmmm dd yyyy  h:mm AM/PM\", \"svibanj 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-41A]mmmm dd yyyy  h:mm AM/PM\", \"lipanj 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-41A]mmmm dd yyyy  h:mm AM/PM\", \"srpanj 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-41A]mmmm dd yyyy  h:mm AM/PM\", \"kolovoz 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-41A]mmmm dd yyyy  h:mm AM/PM\", \"rujan 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-41A]mmmm dd yyyy  h:mm AM/PM\", \"listopad 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-41A]mmmm dd yyyy  h:mm AM/PM\", \"studeni 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-41A]mmmm dd yyyy  h:mm AM/PM\", \"prosinac 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41A]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-41A]mmmmm dd yyyy  h:mm AM/PM\", \"v 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-41A]mmmmm dd yyyy  h:mm AM/PM\", \"o 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-41A]mmmmm dd yyyy  h:mm AM/PM\", \"t 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-41A]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-41A]mmmmm dd yyyy  h:mm AM/PM\", \"l 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-41A]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-41A]mmmmm dd yyyy  h:mm AM/PM\", \"k 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-41A]mmmmm dd yyyy  h:mm AM/PM\", \"r 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-41A]mmmmm dd yyyy  h:mm AM/PM aaa\", \"l 01 2022  4:32 AM sub\"},\n\t\t{\"44866.18957170139\", \"[$-41A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"s 01 2022  4:32 AM uto\"},\n\t\t{\"44896.18957170139\", \"[$-41A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"p 01 2022  4:32 AM četvrtak\"},\n\t\t{\"44562.189571759256\", \"[$-101A]mmm dd yyyy  h:mm AM/PM\", \"sij 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-101A]mmm dd yyyy  h:mm AM/PM\", \"velj 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-101A]mmm dd yyyy  h:mm AM/PM\", \"ožu 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-101A]mmm dd yyyy  h:mm AM/PM\", \"tra 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-101A]mmm dd yyyy  h:mm AM/PM\", \"svi 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-101A]mmm dd yyyy  h:mm AM/PM\", \"lip 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-101A]mmm dd yyyy  h:mm AM/PM\", \"srp 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-101A]mmm dd yyyy  h:mm AM/PM\", \"kol 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-101A]mmm dd yyyy  h:mm AM/PM\", \"ruj 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-101A]mmm dd yyyy  h:mm AM/PM\", \"lis 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-101A]mmm dd yyyy  h:mm AM/PM\", \"stu 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-101A]mmm dd yyyy  h:mm AM/PM\", \"pro 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-101A]mmmm dd yyyy  h:mm AM/PM\", \"siječanj 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-101A]mmmm dd yyyy  h:mm AM/PM\", \"veljača 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-101A]mmmm dd yyyy  h:mm AM/PM\", \"ožujak 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-101A]mmmm dd yyyy  h:mm AM/PM\", \"travanj 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-101A]mmmm dd yyyy  h:mm AM/PM\", \"svibanj 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-101A]mmmm dd yyyy  h:mm AM/PM\", \"lipanj 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-101A]mmmm dd yyyy  h:mm AM/PM\", \"srpanj 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-101A]mmmm dd yyyy  h:mm AM/PM\", \"kolovoz 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-101A]mmmm dd yyyy  h:mm AM/PM\", \"rujan 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-101A]mmmm dd yyyy  h:mm AM/PM\", \"listopad 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-101A]mmmm dd yyyy  h:mm AM/PM\", \"studeni 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-101A]mmmm dd yyyy  h:mm AM/PM\", \"prosinac 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-101A]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-101A]mmmmm dd yyyy  h:mm AM/PM\", \"v 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-101A]mmmmm dd yyyy  h:mm AM/PM\", \"o 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-101A]mmmmm dd yyyy  h:mm AM/PM\", \"t 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-101A]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-101A]mmmmm dd yyyy  h:mm AM/PM\", \"l 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-101A]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-101A]mmmmm dd yyyy  h:mm AM/PM\", \"k 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-101A]mmmmm dd yyyy  h:mm AM/PM\", \"r 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-101A]mmmmm dd yyyy  h:mm AM/PM aaa\", \"l 01 2022  4:32 AM sub\"},\n\t\t{\"44866.18957170139\", \"[$-101A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"s 01 2022  4:32 AM uto\"},\n\t\t{\"44896.18957170139\", \"[$-101A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"p 01 2022  4:32 AM četvrtak\"},\n\t\t{\"44562.189571759256\", \"[$-5]mmm dd yyyy  h:mm AM/PM\", \"I 01 2022  4:32 dop.\"},\n\t\t{\"44593.189571759256\", \"[$-5]mmm dd yyyy  h:mm AM/PM\", \"II 01 2022  4:32 dop.\"},\n\t\t{\"44621.18957170139\", \"[$-5]mmm dd yyyy  h:mm AM/PM\", \"III 01 2022  4:32 dop.\"},\n\t\t{\"44652.18957170139\", \"[$-5]mmm dd yyyy  h:mm AM/PM\", \"IV 01 2022  4:32 dop.\"},\n\t\t{\"44682.18957170139\", \"[$-5]mmm dd yyyy  h:mm AM/PM\", \"V 01 2022  4:32 dop.\"},\n\t\t{\"44713.18957170139\", \"[$-5]mmm dd yyyy  h:mm AM/PM\", \"VI 01 2022  4:32 dop.\"},\n\t\t{\"44743.18957170139\", \"[$-5]mmm dd yyyy  h:mm AM/PM\", \"VII 01 2022  4:32 dop.\"},\n\t\t{\"44774.18957170139\", \"[$-5]mmm dd yyyy  h:mm AM/PM\", \"VIII 01 2022  4:32 dop.\"},\n\t\t{\"44805.18957170139\", \"[$-5]mmm dd yyyy  h:mm AM/PM\", \"IX 01 2022  4:32 dop.\"},\n\t\t{\"44835.18957170139\", \"[$-5]mmm dd yyyy  h:mm AM/PM\", \"X 01 2022  4:32 dop.\"},\n\t\t{\"44866.18957170139\", \"[$-5]mmm dd yyyy  h:mm AM/PM\", \"XI 01 2022  4:32 dop.\"},\n\t\t{\"44896.18957170139\", \"[$-5]mmm dd yyyy  h:mm AM/PM\", \"XII 01 2022  4:32 dop.\"},\n\t\t{\"44562.189571759256\", \"[$-5]mmmm dd yyyy  h:mm AM/PM\", \"leden 01 2022  4:32 dop.\"},\n\t\t{\"44593.189571759256\", \"[$-5]mmmm dd yyyy  h:mm AM/PM\", \"únor 01 2022  4:32 dop.\"},\n\t\t{\"44621.18957170139\", \"[$-5]mmmm dd yyyy  h:mm AM/PM\", \"březen 01 2022  4:32 dop.\"},\n\t\t{\"44652.18957170139\", \"[$-5]mmmm dd yyyy  h:mm AM/PM\", \"duben 01 2022  4:32 dop.\"},\n\t\t{\"44682.18957170139\", \"[$-5]mmmm dd yyyy  h:mm AM/PM\", \"květen 01 2022  4:32 dop.\"},\n\t\t{\"44713.18957170139\", \"[$-5]mmmm dd yyyy  h:mm AM/PM\", \"červen 01 2022  4:32 dop.\"},\n\t\t{\"44743.18957170139\", \"[$-5]mmmm dd yyyy  h:mm AM/PM\", \"červenec 01 2022  4:32 dop.\"},\n\t\t{\"44774.18957170139\", \"[$-5]mmmm dd yyyy  h:mm AM/PM\", \"srpen 01 2022  4:32 dop.\"},\n\t\t{\"44805.18957170139\", \"[$-5]mmmm dd yyyy  h:mm AM/PM\", \"září 01 2022  4:32 dop.\"},\n\t\t{\"44835.18957170139\", \"[$-5]mmmm dd yyyy  h:mm AM/PM\", \"říjen 01 2022  4:32 dop.\"},\n\t\t{\"44866.18957170139\", \"[$-5]mmmm dd yyyy  h:mm AM/PM\", \"listopad 01 2022  4:32 dop.\"},\n\t\t{\"44896.18957170139\", \"[$-5]mmmm dd yyyy  h:mm AM/PM\", \"prosinec 01 2022  4:32 dop.\"},\n\t\t{\"44562.189571759256\", \"[$-5]mmmmm dd yyyy  h:mm AM/PM\", \"l 01 2022  4:32 dop.\"},\n\t\t{\"44593.189571759256\", \"[$-5]mmmmm dd yyyy  h:mm AM/PM\", \"ú 01 2022  4:32 dop.\"},\n\t\t{\"44621.18957170139\", \"[$-5]mmmmm dd yyyy  h:mm AM/PM\", \"b 01 2022  4:32 dop.\"},\n\t\t{\"44652.18957170139\", \"[$-5]mmmmm dd yyyy  h:mm AM/PM\", \"d 01 2022  4:32 dop.\"},\n\t\t{\"44682.18957170139\", \"[$-5]mmmmm dd yyyy  h:mm AM/PM\", \"k 01 2022  4:32 dop.\"},\n\t\t{\"44713.18957170139\", \"[$-5]mmmmm dd yyyy  h:mm AM/PM\", \"č 01 2022  4:32 dop.\"},\n\t\t{\"44743.18957170139\", \"[$-5]mmmmm dd yyyy  h:mm AM/PM\", \"č 01 2022  4:32 dop.\"},\n\t\t{\"44774.18957170139\", \"[$-5]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 dop.\"},\n\t\t{\"44805.18957170139\", \"[$-5]mmmmm dd yyyy  h:mm AM/PM\", \"z 01 2022  4:32 dop.\"},\n\t\t{\"44835.18957170139\", \"[$-5]mmmmm dd yyyy  h:mm AM/PM aaa\", \"ř 01 2022  4:32 dop. so\"},\n\t\t{\"44866.18957170139\", \"[$-5]mmmmm dd yyyy  h:mm AM/PM ddd\", \"l 01 2022  4:32 dop. út\"},\n\t\t{\"44896.18957170139\", \"[$-5]mmmmm dd yyyy  h:mm AM/PM dddd\", \"p 01 2022  4:32 dop. čtvrtek\"},\n\t\t{\"44562.189571759256\", \"[$-405]mmm dd yyyy  h:mm AM/PM\", \"I 01 2022  4:32 dop.\"},\n\t\t{\"44593.189571759256\", \"[$-405]mmm dd yyyy  h:mm AM/PM\", \"II 01 2022  4:32 dop.\"},\n\t\t{\"44621.18957170139\", \"[$-405]mmm dd yyyy  h:mm AM/PM\", \"III 01 2022  4:32 dop.\"},\n\t\t{\"44652.18957170139\", \"[$-405]mmm dd yyyy  h:mm AM/PM\", \"IV 01 2022  4:32 dop.\"},\n\t\t{\"44682.18957170139\", \"[$-405]mmm dd yyyy  h:mm AM/PM\", \"V 01 2022  4:32 dop.\"},\n\t\t{\"44713.18957170139\", \"[$-405]mmm dd yyyy  h:mm AM/PM\", \"VI 01 2022  4:32 dop.\"},\n\t\t{\"44743.18957170139\", \"[$-405]mmm dd yyyy  h:mm AM/PM\", \"VII 01 2022  4:32 dop.\"},\n\t\t{\"44774.18957170139\", \"[$-405]mmm dd yyyy  h:mm AM/PM\", \"VIII 01 2022  4:32 dop.\"},\n\t\t{\"44805.18957170139\", \"[$-405]mmm dd yyyy  h:mm AM/PM\", \"IX 01 2022  4:32 dop.\"},\n\t\t{\"44835.18957170139\", \"[$-405]mmm dd yyyy  h:mm AM/PM\", \"X 01 2022  4:32 dop.\"},\n\t\t{\"44866.18957170139\", \"[$-405]mmm dd yyyy  h:mm AM/PM\", \"XI 01 2022  4:32 dop.\"},\n\t\t{\"44896.18957170139\", \"[$-405]mmm dd yyyy  h:mm AM/PM\", \"XII 01 2022  4:32 dop.\"},\n\t\t{\"44562.189571759256\", \"[$-405]mmmm dd yyyy  h:mm AM/PM\", \"leden 01 2022  4:32 dop.\"},\n\t\t{\"44593.189571759256\", \"[$-405]mmmm dd yyyy  h:mm AM/PM\", \"únor 01 2022  4:32 dop.\"},\n\t\t{\"44621.18957170139\", \"[$-405]mmmm dd yyyy  h:mm AM/PM\", \"březen 01 2022  4:32 dop.\"},\n\t\t{\"44652.18957170139\", \"[$-405]mmmm dd yyyy  h:mm AM/PM\", \"duben 01 2022  4:32 dop.\"},\n\t\t{\"44682.18957170139\", \"[$-405]mmmm dd yyyy  h:mm AM/PM\", \"květen 01 2022  4:32 dop.\"},\n\t\t{\"44713.18957170139\", \"[$-405]mmmm dd yyyy  h:mm AM/PM\", \"červen 01 2022  4:32 dop.\"},\n\t\t{\"44743.18957170139\", \"[$-405]mmmm dd yyyy  h:mm AM/PM\", \"červenec 01 2022  4:32 dop.\"},\n\t\t{\"44774.18957170139\", \"[$-405]mmmm dd yyyy  h:mm AM/PM\", \"srpen 01 2022  4:32 dop.\"},\n\t\t{\"44805.18957170139\", \"[$-405]mmmm dd yyyy  h:mm AM/PM\", \"září 01 2022  4:32 dop.\"},\n\t\t{\"44835.18957170139\", \"[$-405]mmmm dd yyyy  h:mm AM/PM\", \"říjen 01 2022  4:32 dop.\"},\n\t\t{\"44866.18957170139\", \"[$-405]mmmm dd yyyy  h:mm AM/PM\", \"listopad 01 2022  4:32 dop.\"},\n\t\t{\"44896.18957170139\", \"[$-405]mmmm dd yyyy  h:mm AM/PM\", \"prosinec 01 2022  4:32 dop.\"},\n\t\t{\"44562.189571759256\", \"[$-405]mmmmm dd yyyy  h:mm AM/PM\", \"l 01 2022  4:32 dop.\"},\n\t\t{\"44593.189571759256\", \"[$-405]mmmmm dd yyyy  h:mm AM/PM\", \"ú 01 2022  4:32 dop.\"},\n\t\t{\"44621.18957170139\", \"[$-405]mmmmm dd yyyy  h:mm AM/PM\", \"b 01 2022  4:32 dop.\"},\n\t\t{\"44652.18957170139\", \"[$-405]mmmmm dd yyyy  h:mm AM/PM\", \"d 01 2022  4:32 dop.\"},\n\t\t{\"44682.18957170139\", \"[$-405]mmmmm dd yyyy  h:mm AM/PM\", \"k 01 2022  4:32 dop.\"},\n\t\t{\"44713.18957170139\", \"[$-405]mmmmm dd yyyy  h:mm AM/PM\", \"č 01 2022  4:32 dop.\"},\n\t\t{\"44743.18957170139\", \"[$-405]mmmmm dd yyyy  h:mm AM/PM\", \"č 01 2022  4:32 dop.\"},\n\t\t{\"44774.18957170139\", \"[$-405]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 dop.\"},\n\t\t{\"44805.18957170139\", \"[$-405]mmmmm dd yyyy  h:mm AM/PM\", \"z 01 2022  4:32 dop.\"},\n\t\t{\"44835.18957170139\", \"[$-405]mmmmm dd yyyy  h:mm AM/PM aaa\", \"ř 01 2022  4:32 dop. so\"},\n\t\t{\"44866.18957170139\", \"[$-405]mmmmm dd yyyy  h:mm AM/PM ddd\", \"l 01 2022  4:32 dop. út\"},\n\t\t{\"44896.18957170139\", \"[$-405]mmmmm dd yyyy  h:mm AM/PM dddd\", \"p 01 2022  4:32 dop. čtvrtek\"},\n\t\t{\"44562.189571759256\", \"[$-6]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-6]mmm dd yyyy  h:mm AM/PM\", \"feb 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-6]mmm dd yyyy  h:mm AM/PM\", \"mar 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-6]mmm dd yyyy  h:mm AM/PM\", \"apr 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-6]mmm dd yyyy  h:mm AM/PM\", \"maj 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-6]mmm dd yyyy  h:mm AM/PM\", \"jun 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-6]mmm dd yyyy  h:mm AM/PM\", \"jul 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-6]mmm dd yyyy  h:mm AM/PM\", \"aug 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-6]mmm dd yyyy  h:mm AM/PM\", \"sep 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-6]mmm dd yyyy  h:mm AM/PM\", \"okt 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-6]mmm dd yyyy  h:mm AM/PM\", \"nov 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-6]mmm dd yyyy  h:mm AM/PM\", \"dec 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-6]mmmm dd yyyy  h:mm AM/PM\", \"februar 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-6]mmmm dd yyyy  h:mm AM/PM\", \"marts 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-6]mmmm dd yyyy  h:mm AM/PM\", \"april 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-6]mmmm dd yyyy  h:mm AM/PM\", \"maj 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-6]mmmm dd yyyy  h:mm AM/PM\", \"juni 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-6]mmmm dd yyyy  h:mm AM/PM\", \"juli 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-6]mmmm dd yyyy  h:mm AM/PM\", \"august 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-6]mmmm dd yyyy  h:mm AM/PM\", \"september 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-6]mmmm dd yyyy  h:mm AM/PM\", \"oktober 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-6]mmmm dd yyyy  h:mm AM/PM\", \"november 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-6]mmmm dd yyyy  h:mm AM/PM\", \"december 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-6]mmmmm dd yyyy  h:mm AM/PM\", \"f 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-6]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-6]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-6]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-6]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-6]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-6]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-6]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-6]mmmmm dd yyyy  h:mm AM/PM aaa\", \"o 01 2022  4:32 AM lø\"},\n\t\t{\"44866.18957170139\", \"[$-6]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 01 2022  4:32 AM ti\"},\n\t\t{\"44896.18957170139\", \"[$-6]mmmmm dd yyyy  h:mm AM/PM dddd\", \"d 01 2022  4:32 AM torsdag\"},\n\t\t{\"44562.189571759256\", \"[$-406]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-406]mmm dd yyyy  h:mm AM/PM\", \"feb 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-406]mmm dd yyyy  h:mm AM/PM\", \"mar 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-406]mmm dd yyyy  h:mm AM/PM\", \"apr 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-406]mmm dd yyyy  h:mm AM/PM\", \"maj 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-406]mmm dd yyyy  h:mm AM/PM\", \"jun 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-406]mmm dd yyyy  h:mm AM/PM\", \"jul 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-406]mmm dd yyyy  h:mm AM/PM\", \"aug 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-406]mmm dd yyyy  h:mm AM/PM\", \"sep 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-406]mmm dd yyyy  h:mm AM/PM\", \"okt 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-406]mmm dd yyyy  h:mm AM/PM\", \"nov 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-406]mmm dd yyyy  h:mm AM/PM\", \"dec 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-406]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-406]mmmm dd yyyy  h:mm AM/PM\", \"februar 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-406]mmmm dd yyyy  h:mm AM/PM\", \"marts 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-406]mmmm dd yyyy  h:mm AM/PM\", \"april 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-406]mmmm dd yyyy  h:mm AM/PM\", \"maj 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-406]mmmm dd yyyy  h:mm AM/PM\", \"juni 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-406]mmmm dd yyyy  h:mm AM/PM\", \"juli 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-406]mmmm dd yyyy  h:mm AM/PM\", \"august 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-406]mmmm dd yyyy  h:mm AM/PM\", \"september 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-406]mmmm dd yyyy  h:mm AM/PM\", \"oktober 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-406]mmmm dd yyyy  h:mm AM/PM\", \"november 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-406]mmmm dd yyyy  h:mm AM/PM\", \"december 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-406]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-406]mmmmm dd yyyy  h:mm AM/PM\", \"f 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-406]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-406]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-406]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-406]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-406]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-406]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-406]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-406]mmmmm dd yyyy  h:mm AM/PM aaa\", \"o 01 2022  4:32 AM lø\"},\n\t\t{\"44866.18957170139\", \"[$-406]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 01 2022  4:32 AM ti\"},\n\t\t{\"44896.18957170139\", \"[$-406]mmmmm dd yyyy  h:mm AM/PM dddd\", \"d 01 2022  4:32 AM torsdag\"},\n\t\t{\"44562.189571759256\", \"[$-8C]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u062F\\u06CC 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44593.189571759256\", \"[$-8C]mmm dd yyyy  h:mm AM/PM\", \"\\u062F\\u0644\\u0648 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44621.18957170139\", \"[$-8C]mmm dd yyyy  h:mm AM/PM\", \"\\u062D\\u0648\\u062A 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44652.18957170139\", \"[$-8C]mmm dd yyyy  h:mm AM/PM\", \"\\u062D\\u0645\\u0644 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44682.18957170139\", \"[$-8C]mmm dd yyyy  h:mm AM/PM\", \"\\u062B\\u0648\\u0631 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44713.18957170139\", \"[$-8C]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0648\\u0632\\u0627 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44743.18957170139\", \"[$-8C]mmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u0631\\u0637\\u0627\\u0646 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44774.18957170139\", \"[$-8C]mmm dd yyyy  h:mm AM/PM\", \"\\u0627\\u0633\\u062F 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44805.18957170139\", \"[$-8C]mmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u0646\\u0628\\u0644\\u0647 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44835.18957170139\", \"[$-8C]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u06CC\\u0632\\u0627\\u0646 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44866.18957170139\", \"[$-8C]mmm dd yyyy  h:mm AM/PM\", \"\\u0639\\u0642\\u0631\\u0628 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44896.18957170139\", \"[$-8C]mmm dd yyyy  h:mm AM/PM\", \"\\u0642\\u0648\\u0633 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44562.189571759256\", \"[$-8C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0698\\u0627\\u0646\\u0648\\u064A\\u0647 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44593.189571759256\", \"[$-8C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0641\\u0648\\u0631\\u064A\\u0647 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44621.18957170139\", \"[$-8C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44652.18957170139\", \"[$-8C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0622\\u0648\\u0631\\u064A\\u0644 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44682.18957170139\", \"[$-8C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0647 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44713.18957170139\", \"[$-8C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0698\\u0648\\u0626\\u0646 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44743.18957170139\", \"[$-8C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0698\\u0648\\u0626\\u064A\\u0647 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44774.18957170139\", \"[$-8C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0627\\u0648\\u062A 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44805.18957170139\", \"[$-8C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u067E\\u062A\\u0627\\u0645\\u0628\\u0631 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44835.18957170139\", \"[$-8C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0627\\u064F\\u0643\\u062A\\u0628\\u0631 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44866.18957170139\", \"[$-8C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0646\\u0648\\u0627\\u0645\\u0628\\u0631 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44896.18957170139\", \"[$-8C]mmmm dd yyyy  h:mm AM/PM\", \"\\u062F\\u0633\\u0627\\u0645\\u0628\\u0631 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44562.189571759256\", \"[$-8C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0698 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44593.189571759256\", \"[$-8C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0641 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44621.18957170139\", \"[$-8C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0645 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44652.18957170139\", \"[$-8C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0622 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44682.18957170139\", \"[$-8C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0645 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44713.18957170139\", \"[$-8C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0698 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44743.18957170139\", \"[$-8C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0698 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44774.18957170139\", \"[$-8C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0627 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44805.18957170139\", \"[$-8C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0633 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44835.18957170139\", \"[$-8C]mmmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0627 01 2022  4:32 \\u0642.\\u0638 \\u0634\\u0646\\u0628\\u0647\"},\n\t\t{\"44866.18957170139\", \"[$-8C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0646 01 2022  4:32 \\u0642.\\u0638 \\u0633\\u0647\\u200C \\u0634\\u0646\\u0628\\u0647\"},\n\t\t{\"44896.18957170139\", \"[$-8C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u062F 01 2022  4:32 \\u0642.\\u0638 \\u067E\\u0646\\u062C\\u0634\\u0646\\u0628\\u0647\"},\n\t\t{\"44562.189571759256\", \"[$-48C]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u062F\\u06CC 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44593.189571759256\", \"[$-48C]mmm dd yyyy  h:mm AM/PM\", \"\\u062F\\u0644\\u0648 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44621.18957170139\", \"[$-48C]mmm dd yyyy  h:mm AM/PM\", \"\\u062D\\u0648\\u062A 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44652.18957170139\", \"[$-48C]mmm dd yyyy  h:mm AM/PM\", \"\\u062D\\u0645\\u0644 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44682.18957170139\", \"[$-48C]mmm dd yyyy  h:mm AM/PM\", \"\\u062B\\u0648\\u0631 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44713.18957170139\", \"[$-48C]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0648\\u0632\\u0627 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44743.18957170139\", \"[$-48C]mmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u0631\\u0637\\u0627\\u0646 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44774.18957170139\", \"[$-48C]mmm dd yyyy  h:mm AM/PM\", \"\\u0627\\u0633\\u062F 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44805.18957170139\", \"[$-48C]mmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u0646\\u0628\\u0644\\u0647 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44835.18957170139\", \"[$-48C]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u06CC\\u0632\\u0627\\u0646 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44866.18957170139\", \"[$-48C]mmm dd yyyy  h:mm AM/PM\", \"\\u0639\\u0642\\u0631\\u0628 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44896.18957170139\", \"[$-48C]mmm dd yyyy  h:mm AM/PM\", \"\\u0642\\u0648\\u0633 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44562.189571759256\", \"[$-48C]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44593.189571759256\", \"[$-48C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0641\\u0628\\u0631\\u0648\\u0631\\u06CC 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44621.18957170139\", \"[$-48C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0686 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44652.18957170139\", \"[$-48C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0627\\u067E\\u0631\\u06CC\\u0644 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44682.18957170139\", \"[$-48C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u06CC 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44713.18957170139\", \"[$-48C]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0648\\u0646 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44743.18957170139\", \"[$-48C]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0648\\u0644\\u0627\\u06CC 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44774.18957170139\", \"[$-48C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0627\\u06AF\\u0633\\u062A 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44805.18957170139\", \"[$-48C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u067E\\u062A\\u0645\\u0628\\u0631 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44835.18957170139\", \"[$-48C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0627\\u06A9\\u062A\\u0648\\u0628\\u0631 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44866.18957170139\", \"[$-48C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0646\\u0648\\u0645\\u0628\\u0631 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44896.18957170139\", \"[$-48C]mmmm dd yyyy  h:mm AM/PM\", \"\\u062F\\u0633\\u0645\\u0628\\u0631 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44562.189571759256\", \"[$-48C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44593.189571759256\", \"[$-48C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0641 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44621.18957170139\", \"[$-48C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0645 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44652.18957170139\", \"[$-48C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0627 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44682.18957170139\", \"[$-48C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0645 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44713.18957170139\", \"[$-48C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44743.18957170139\", \"[$-48C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44774.18957170139\", \"[$-48C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0627 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44805.18957170139\", \"[$-48C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0633 01 2022  4:32 \\u0642.\\u0638.\"},\n\t\t{\"44835.18957170139\", \"[$-48C]mmmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0627 01 2022  4:32 \\u0642.\\u0638. \\u0634\\u0646\\u0628\\u0647\"},\n\t\t{\"44866.18957170139\", \"[$-48C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0646 01 2022  4:32 \\u0642.\\u0638. \\u0633\\u0647\\u200C \\u0634\\u0646\\u0628\\u0647\"},\n\t\t{\"44896.18957170139\", \"[$-48C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u062F 01 2022  4:32 \\u0642.\\u0638. \\u067E\\u0646\\u062C\\u0634\\u0646\\u0628\\u0647\"},\n\t\t{\"44562.189571759256\", \"[$-65]mmm dd yyyy  h:mm AM/PM\", \"\\u0796\\u07A6\\u0782\\u07A6\\u0788\\u07A6\\u0783\\u07A9 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44593.189571759256\", \"[$-65]mmm dd yyyy  h:mm AM/PM\", \"\\u078A\\u07AC\\u0784\\u07B0\\u0783\\u07AA\\u0787\\u07A6\\u0783\\u07A9 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44621.18957170139\", \"[$-65]mmm dd yyyy  h:mm AM/PM\", \"\\u0789\\u07A7\\u0783\\u0797\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44652.18957170139\", \"[$-65]mmm dd yyyy  h:mm AM/PM\", \"\\u0787\\u07AD\\u0795\\u07B0\\u0783\\u07A8\\u078D\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44682.18957170139\", \"[$-65]mmm dd yyyy  h:mm AM/PM\", \"\\u0789\\u07AC\\u0787\\u07A8 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44713.18957170139\", \"[$-65]mmm dd yyyy  h:mm AM/PM\", \"\\u0796\\u07AB\\u0782\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44743.18957170139\", \"[$-65]mmm dd yyyy  h:mm AM/PM\", \"\\u0796\\u07AA\\u078D\\u07A6\\u0787\\u07A8 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44774.18957170139\", \"[$-65]mmm dd yyyy  h:mm AM/PM\", \"\\u0787\\u07AE\\u078E\\u07A6\\u0790\\u07B0\\u0793\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44805.18957170139\", \"[$-65]mmm dd yyyy  h:mm AM/PM\", \"\\u0790\\u07AC\\u0795\\u07B0\\u0793\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44835.18957170139\", \"[$-65]mmm dd yyyy  h:mm AM/PM\", \"\\u0787\\u07AE\\u0786\\u07B0\\u0793\\u07AF\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44866.18957170139\", \"[$-65]mmm dd yyyy  h:mm AM/PM\", \"\\u0782\\u07AE\\u0788\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44896.18957170139\", \"[$-65]mmm dd yyyy  h:mm AM/PM\", \"\\u0791\\u07A8\\u0790\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44562.189571759256\", \"[$-65]mmmm dd yyyy  h:mm AM/PM\", \"\\u0796\\u07A6\\u0782\\u07A6\\u0788\\u07A6\\u0783\\u07A9 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44593.189571759256\", \"[$-65]mmmm dd yyyy  h:mm AM/PM\", \"\\u078A\\u07AC\\u0784\\u07B0\\u0783\\u07AA\\u0787\\u07A6\\u0783\\u07A9 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44621.18957170139\", \"[$-65]mmmm dd yyyy  h:mm AM/PM\", \"\\u0789\\u07A7\\u0783\\u0797\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44652.18957170139\", \"[$-65]mmmm dd yyyy  h:mm AM/PM\", \"\\u0787\\u07AD\\u0795\\u07B0\\u0783\\u07A8\\u078D\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44682.18957170139\", \"[$-65]mmmm dd yyyy  h:mm AM/PM\", \"\\u0789\\u07AC\\u0787\\u07A8 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44713.18957170139\", \"[$-65]mmmm dd yyyy  h:mm AM/PM\", \"\\u0796\\u07AB\\u0782\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44743.18957170139\", \"[$-65]mmmm dd yyyy  h:mm AM/PM\", \"\\u0796\\u07AA\\u078D\\u07A6\\u0787\\u07A8 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44774.18957170139\", \"[$-65]mmmm dd yyyy  h:mm AM/PM\", \"\\u0787\\u07AE\\u078E\\u07A6\\u0790\\u07B0\\u0793\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44805.18957170139\", \"[$-65]mmmm dd yyyy  h:mm AM/PM\", \"\\u0790\\u07AC\\u0795\\u07B0\\u0793\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44835.18957170139\", \"[$-65]mmmm dd yyyy  h:mm AM/PM\", \"\\u0787\\u07AE\\u0786\\u07B0\\u0793\\u07AF\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44866.18957170139\", \"[$-65]mmmm dd yyyy  h:mm AM/PM\", \"\\u0782\\u07AE\\u0788\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44896.18957170139\", \"[$-65]mmmm dd yyyy  h:mm AM/PM\", \"\\u0791\\u07A8\\u0790\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44562.189571759256\", \"[$-65]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0796 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44593.189571759256\", \"[$-65]mmmmm dd yyyy  h:mm AM/PM\", \"\\u078A 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44621.18957170139\", \"[$-65]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0789 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44652.18957170139\", \"[$-65]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0787 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44682.18957170139\", \"[$-65]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0789 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44713.18957170139\", \"[$-65]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0796 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44743.18957170139\", \"[$-65]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0796 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44774.18957170139\", \"[$-65]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0787 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44805.18957170139\", \"[$-65]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0790 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44835.18957170139\", \"[$-65]mmmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0787 01 2022  4:32 \\u0789\\u0786 \\u0780\\u07AE\\u0782\\u07A8\\u0780\\u07A8\\u0783\\u07AA\"},\n\t\t{\"44866.18957170139\", \"[$-65]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0782 01 2022  4:32 \\u0789\\u0786 \\u0787\\u07A6\\u0782\\u07B0\\u078E\\u07A7\\u0783\\u07A6\"},\n\t\t{\"44896.18957170139\", \"[$-65]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0791 01 2022  4:32 \\u0789\\u0786 \\u0784\\u07AA\\u0783\\u07A7\\u0790\\u07B0\\u078A\\u07A6\\u078C\\u07A8\"},\n\t\t{\"44562.189571759256\", \"[$-465]mmm dd yyyy  h:mm AM/PM\", \"\\u0796\\u07A6\\u0782\\u07A6\\u0788\\u07A6\\u0783\\u07A9 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44593.189571759256\", \"[$-465]mmm dd yyyy  h:mm AM/PM\", \"\\u078A\\u07AC\\u0784\\u07B0\\u0783\\u07AA\\u0787\\u07A6\\u0783\\u07A9 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44621.18957170139\", \"[$-465]mmm dd yyyy  h:mm AM/PM\", \"\\u0789\\u07A7\\u0783\\u0797\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44652.18957170139\", \"[$-465]mmm dd yyyy  h:mm AM/PM\", \"\\u0787\\u07AD\\u0795\\u07B0\\u0783\\u07A8\\u078D\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44682.18957170139\", \"[$-465]mmm dd yyyy  h:mm AM/PM\", \"\\u0789\\u07AC\\u0787\\u07A8 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44713.18957170139\", \"[$-465]mmm dd yyyy  h:mm AM/PM\", \"\\u0796\\u07AB\\u0782\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44743.18957170139\", \"[$-465]mmm dd yyyy  h:mm AM/PM\", \"\\u0796\\u07AA\\u078D\\u07A6\\u0787\\u07A8 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44774.18957170139\", \"[$-465]mmm dd yyyy  h:mm AM/PM\", \"\\u0787\\u07AE\\u078E\\u07A6\\u0790\\u07B0\\u0793\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44805.18957170139\", \"[$-465]mmm dd yyyy  h:mm AM/PM\", \"\\u0790\\u07AC\\u0795\\u07B0\\u0793\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44835.18957170139\", \"[$-465]mmm dd yyyy  h:mm AM/PM\", \"\\u0787\\u07AE\\u0786\\u07B0\\u0793\\u07AF\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44866.18957170139\", \"[$-465]mmm dd yyyy  h:mm AM/PM\", \"\\u0782\\u07AE\\u0788\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44896.18957170139\", \"[$-465]mmm dd yyyy  h:mm AM/PM\", \"\\u0791\\u07A8\\u0790\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44562.189571759256\", \"[$-465]mmmm dd yyyy  h:mm AM/PM\", \"\\u0796\\u07A6\\u0782\\u07A6\\u0788\\u07A6\\u0783\\u07A9 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44593.189571759256\", \"[$-465]mmmm dd yyyy  h:mm AM/PM\", \"\\u078A\\u07AC\\u0784\\u07B0\\u0783\\u07AA\\u0787\\u07A6\\u0783\\u07A9 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44621.18957170139\", \"[$-465]mmmm dd yyyy  h:mm AM/PM\", \"\\u0789\\u07A7\\u0783\\u0797\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44652.18957170139\", \"[$-465]mmmm dd yyyy  h:mm AM/PM\", \"\\u0787\\u07AD\\u0795\\u07B0\\u0783\\u07A8\\u078D\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44682.18957170139\", \"[$-465]mmmm dd yyyy  h:mm AM/PM\", \"\\u0789\\u07AC\\u0787\\u07A8 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44713.18957170139\", \"[$-465]mmmm dd yyyy  h:mm AM/PM\", \"\\u0796\\u07AB\\u0782\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44743.18957170139\", \"[$-465]mmmm dd yyyy  h:mm AM/PM\", \"\\u0796\\u07AA\\u078D\\u07A6\\u0787\\u07A8 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44774.18957170139\", \"[$-465]mmmm dd yyyy  h:mm AM/PM\", \"\\u0787\\u07AE\\u078E\\u07A6\\u0790\\u07B0\\u0793\\u07B0 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44805.18957170139\", \"[$-465]mmmm dd yyyy  h:mm AM/PM\", \"\\u0790\\u07AC\\u0795\\u07B0\\u0793\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44835.18957170139\", \"[$-465]mmmm dd yyyy  h:mm AM/PM\", \"\\u0787\\u07AE\\u0786\\u07B0\\u0793\\u07AF\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44866.18957170139\", \"[$-465]mmmm dd yyyy  h:mm AM/PM\", \"\\u0782\\u07AE\\u0788\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44896.18957170139\", \"[$-465]mmmm dd yyyy  h:mm AM/PM\", \"\\u0791\\u07A8\\u0790\\u07AC\\u0789\\u07B0\\u0784\\u07A6\\u0783 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44562.189571759256\", \"[$-465]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0796 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44593.189571759256\", \"[$-465]mmmmm dd yyyy  h:mm AM/PM\", \"\\u078A 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44621.18957170139\", \"[$-465]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0789 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44652.18957170139\", \"[$-465]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0787 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44682.18957170139\", \"[$-465]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0789 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44713.18957170139\", \"[$-465]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0796 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44743.18957170139\", \"[$-465]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0796 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44774.18957170139\", \"[$-465]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0787 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44805.18957170139\", \"[$-465]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0790 01 2022  4:32 \\u0789\\u0786\"},\n\t\t{\"44835.18957170139\", \"[$-465]mmmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0787 01 2022  4:32 \\u0789\\u0786 \\u0780\\u07AE\\u0782\\u07A8\\u0780\\u07A8\\u0783\\u07AA\"},\n\t\t{\"44866.18957170139\", \"[$-465]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0782 01 2022  4:32 \\u0789\\u0786 \\u0787\\u07A6\\u0782\\u07B0\\u078E\\u07A7\\u0783\\u07A6\"},\n\t\t{\"44896.18957170139\", \"[$-465]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0791 01 2022  4:32 \\u0789\\u0786 \\u0784\\u07AA\\u0783\\u07A7\\u0790\\u07B0\\u078A\\u07A6\\u078C\\u07A8\"},\n\t\t{\"44562.189571759256\", \"[$-13]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-13]mmm dd yyyy  h:mm AM/PM\", \"feb 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-13]mmm dd yyyy  h:mm AM/PM\", \"mrt 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-13]mmm dd yyyy  h:mm AM/PM\", \"apr 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-13]mmm dd yyyy  h:mm AM/PM\", \"mei 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-13]mmm dd yyyy  h:mm AM/PM\", \"jun 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-13]mmm dd yyyy  h:mm AM/PM\", \"jul 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-13]mmm dd yyyy  h:mm AM/PM\", \"aug 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-13]mmm dd yyyy  h:mm AM/PM\", \"sep 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-13]mmm dd yyyy  h:mm AM/PM\", \"okt 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-13]mmm dd yyyy  h:mm AM/PM\", \"nov 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-13]mmm dd yyyy  h:mm AM/PM\", \"dec 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-13]mmmm dd yyyy  h:mm AM/PM\", \"januari 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-13]mmmm dd yyyy  h:mm AM/PM\", \"februari 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-13]mmmm dd yyyy  h:mm AM/PM\", \"maart 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-13]mmmm dd yyyy  h:mm AM/PM\", \"april 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-13]mmmm dd yyyy  h:mm AM/PM\", \"mei 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-13]mmmm dd yyyy  h:mm AM/PM\", \"juni 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-13]mmmm dd yyyy  h:mm AM/PM\", \"juli 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-13]mmmm dd yyyy  h:mm AM/PM\", \"augustus 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-13]mmmm dd yyyy  h:mm AM/PM\", \"september 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-13]mmmm dd yyyy  h:mm AM/PM\", \"oktober 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-13]mmmm dd yyyy  h:mm AM/PM\", \"november 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-13]mmmm dd yyyy  h:mm AM/PM\", \"december 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-13]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-13]mmmmm dd yyyy  h:mm AM/PM\", \"f 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-13]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-13]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-13]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-13]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-13]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-13]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-13]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-13]mmmmm dd yyyy  h:mm AM/PM aaa\", \"o 01 2022  4:32 AM za\"},\n\t\t{\"44866.18957170139\", \"[$-13]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 01 2022  4:32 AM di\"},\n\t\t{\"44896.18957170139\", \"[$-13]mmmmm dd yyyy  h:mm AM/PM dddd\", \"d 01 2022  4:32 AM donderdag\"},\n\t\t{\"44562.189571759256\", \"[$-813]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-813]mmm dd yyyy  h:mm AM/PM\", \"feb 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-813]mmm dd yyyy  h:mm AM/PM\", \"mrt 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-813]mmm dd yyyy  h:mm AM/PM\", \"apr 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-813]mmm dd yyyy  h:mm AM/PM\", \"mei 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-813]mmm dd yyyy  h:mm AM/PM\", \"jun 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-813]mmm dd yyyy  h:mm AM/PM\", \"jul 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-813]mmm dd yyyy  h:mm AM/PM\", \"aug 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-813]mmm dd yyyy  h:mm AM/PM\", \"sep 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-813]mmm dd yyyy  h:mm AM/PM\", \"okt 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-813]mmm dd yyyy  h:mm AM/PM\", \"nov 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-813]mmm dd yyyy  h:mm AM/PM\", \"dec 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-813]mmmm dd yyyy  h:mm AM/PM\", \"januari 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-813]mmmm dd yyyy  h:mm AM/PM\", \"februari 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-813]mmmm dd yyyy  h:mm AM/PM\", \"maart 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-813]mmmm dd yyyy  h:mm AM/PM\", \"april 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-813]mmmm dd yyyy  h:mm AM/PM\", \"mei 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-813]mmmm dd yyyy  h:mm AM/PM\", \"juni 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-813]mmmm dd yyyy  h:mm AM/PM\", \"juli 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-813]mmmm dd yyyy  h:mm AM/PM\", \"augustus 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-813]mmmm dd yyyy  h:mm AM/PM\", \"september 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-813]mmmm dd yyyy  h:mm AM/PM\", \"oktober 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-813]mmmm dd yyyy  h:mm AM/PM\", \"november 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-813]mmmm dd yyyy  h:mm AM/PM\", \"december 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-813]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-813]mmmmm dd yyyy  h:mm AM/PM\", \"f 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-813]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-813]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-813]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-813]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-813]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-813]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-813]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-813]mmmmm dd yyyy  h:mm AM/PM aaa\", \"o 01 2022  4:32 AM za\"},\n\t\t{\"44866.18957170139\", \"[$-813]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 01 2022  4:32 AM di\"},\n\t\t{\"44896.18957170139\", \"[$-813]mmmmm dd yyyy  h:mm AM/PM dddd\", \"d 01 2022  4:32 AM donderdag\"},\n\t\t{\"44621.18957170139\", \"[$-413]mmm dd yyyy  h:mm AM/PM\", \"mrt 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-413]mmm dd yyyy  h:mm AM/PM\", \"apr 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-413]mmm dd yyyy  h:mm AM/PM\", \"mei 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-413]mmm dd yyyy  h:mm AM/PM\", \"jun 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-413]mmm dd yyyy  h:mm AM/PM\", \"jul 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-413]mmm dd yyyy  h:mm AM/PM\", \"aug 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-413]mmm dd yyyy  h:mm AM/PM\", \"sep 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-413]mmm dd yyyy  h:mm AM/PM\", \"okt 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-413]mmm dd yyyy  h:mm AM/PM\", \"nov 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-413]mmm dd yyyy  h:mm AM/PM\", \"dec 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-413]mmmm dd yyyy  h:mm AM/PM\", \"januari 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-413]mmmm dd yyyy  h:mm AM/PM\", \"februari 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-413]mmmm dd yyyy  h:mm AM/PM\", \"maart 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-413]mmmm dd yyyy  h:mm AM/PM\", \"april 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-413]mmmm dd yyyy  h:mm AM/PM\", \"mei 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-413]mmmm dd yyyy  h:mm AM/PM\", \"juni 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-413]mmmm dd yyyy  h:mm AM/PM\", \"juli 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-413]mmmm dd yyyy  h:mm AM/PM\", \"augustus 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-413]mmmm dd yyyy  h:mm AM/PM\", \"september 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-413]mmmm dd yyyy  h:mm AM/PM\", \"oktober 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-413]mmmm dd yyyy  h:mm AM/PM\", \"november 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-413]mmmm dd yyyy  h:mm AM/PM\", \"december 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-413]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-413]mmmmm dd yyyy  h:mm AM/PM\", \"f 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-413]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-413]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-413]mmmmm dd yyyy  h:mm AM/PM\", \"m 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-413]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-413]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-413]mmmmm dd yyyy  h:mm AM/PM\", \"a 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-413]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-413]mmmmm dd yyyy  h:mm AM/PM aaa\", \"o 01 2022  4:32 AM za\"},\n\t\t{\"44866.18957170139\", \"[$-413]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 01 2022  4:32 AM di\"},\n\t\t{\"44896.18957170139\", \"[$-413]mmmmm dd yyyy  h:mm AM/PM dddd\", \"d 01 2022  4:32 AM donderdag\"},\n\t\t{\"44562.189571759256\", \"[$-C51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F21 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44593.189571759256\", \"[$-C51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F22 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44621.18957170139\", \"[$-C51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F23 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44652.18957170139\", \"[$-C51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F24 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44682.18957170139\", \"[$-C51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F25 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44713.18957170139\", \"[$-C51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F26 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44743.18957170139\", \"[$-C51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F27 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44774.18957170139\", \"[$-C51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F28 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44805.18957170139\", \"[$-C51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F29 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44835.18957170139\", \"[$-C51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F21\\u0F20 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44866.18957170139\", \"[$-C51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F21\\u0F21 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44896.18957170139\", \"[$-C51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F21\\u0F22 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44562.189571759256\", \"[$-C51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F51\\u0F44\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44593.189571759256\", \"[$-C51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F42\\u0F49\\u0F72\\u0F66\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44621.18957170139\", \"[$-C51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F42\\u0F66\\u0F74\\u0F58\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44652.18957170139\", \"[$-C51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F5E\\u0F72\\u0F0B\\u0F54 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44682.18957170139\", \"[$-C51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F63\\u0F94\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44713.18957170139\", \"[$-C51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F51\\u0FB2\\u0F74\\u0F42\\u0F0B\\u0F54 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44743.18957170139\", \"[$-C51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F51\\u0F74\\u0F53\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44774.18957170139\", \"[$-C51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F62\\u0F92\\u0FB1\\u0F51\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44805.18957170139\", \"[$-C51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F51\\u0F42\\u0F74\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44835.18957170139\", \"[$-C51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F54\\u0F0D 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44866.18957170139\", \"[$-C51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F42\\u0F45\\u0F72\\u0F42\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44896.18957170139\", \"[$-C51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F42\\u0F49\\u0F72\\u0F66\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44562.189571759256\", \"[$-C51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44593.189571759256\", \"[$-C51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44621.18957170139\", \"[$-C51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44652.18957170139\", \"[$-C51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44682.18957170139\", \"[$-C51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44713.18957170139\", \"[$-C51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44743.18957170139\", \"[$-C51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44774.18957170139\", \"[$-C51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44805.18957170139\", \"[$-C51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B\"},\n\t\t{\"44835.18957170139\", \"[$-C51]mmmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B \\u0F49\\u0F72\\u0F0B\"},\n\t\t{\"44866.18957170139\", \"[$-C51]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B \\u0F63\\u0FB7\\u0F42\\u0F0B\"},\n\t\t{\"44896.18957170139\", \"[$-C51]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F46\\u0F0B \\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F54\\u0F0B\\u0F66\\u0F44\\u0F66\\u0F0B\"},\n\t\t{\"43543.503206018519\", \"[$-9]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-9]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-9]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-1000]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-1000]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-1000]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-C09]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 pm Tue\"},\n\t\t{\"43543.503206018519\", \"[$-C09]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 pm Tue\"},\n\t\t{\"43543.503206018519\", \"[$-C09]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 pm Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-c09]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 pm Tue\"},\n\t\t{\"43543.503206018519\", \"[$-c09]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 pm Tue\"},\n\t\t{\"43543.503206018519\", \"[$-c09]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 pm Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-2809]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-2809]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-2809]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-1009]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-1009]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-1009]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-2409]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-2409]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-2409]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-3C09]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-3C09]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-3C09]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-4009]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-4009]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-4009]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-1809]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 pm Tue\"},\n\t\t{\"43543.503206018519\", \"[$-1809]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 pm Tue\"},\n\t\t{\"43543.503206018519\", \"[$-1809]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 pm Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-2009]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-2009]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-2009]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-4409]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-4409]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-4409]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-1409]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-1409]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-1409]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-3409]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-3409]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-3409]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-4809]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-4809]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-4809]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-1C09]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-1C09]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-1C09]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-2C09]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-2C09]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-2C09]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-4C09]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-4C09]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-4C09]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-809]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 pm Tue\"},\n\t\t{\"43543.503206018519\", \"[$-809]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 pm Tue\"},\n\t\t{\"43543.503206018519\", \"[$-809]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 pm Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-3009]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-3009]mmmm dd yyyy  h:mm AM/PM ddd\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-3009]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Tuesday\"},\n\t\t{\"43543.503206018519\", \"[$-25]mmm dd yyyy  h:mm AM/PM aaa\", \"märts 19 2019  12:04 PM T\"},\n\t\t{\"43543.503206018519\", \"[$-25]mmmm dd yyyy  h:mm AM/PM ddd\", \"märts 19 2019  12:04 PM T\"},\n\t\t{\"43543.503206018519\", \"[$-25]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM teisipäev\"},\n\t\t{\"43543.503206018519\", \"[$-425]mmm dd yyyy  h:mm AM/PM aaa\", \"märts 19 2019  12:04 PM T\"},\n\t\t{\"43543.503206018519\", \"[$-425]mmmm dd yyyy  h:mm AM/PM ddd\", \"märts 19 2019  12:04 PM T\"},\n\t\t{\"43543.503206018519\", \"[$-425]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM teisipäev\"},\n\t\t{\"43543.503206018519\", \"[$-38]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 um sein. týs.\"},\n\t\t{\"43543.503206018519\", \"[$-38]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 um sein. týs.\"},\n\t\t{\"43543.503206018519\", \"[$-38]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 um sein. týsdagur\"},\n\t\t{\"43543.503206018519\", \"[$-438]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 um sein. týs.\"},\n\t\t{\"43543.503206018519\", \"[$-438]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 um sein. týs.\"},\n\t\t{\"43543.503206018519\", \"[$-438]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 um sein. týsdagur\"},\n\t\t{\"43543.503206018519\", \"[$-64]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar 19 2019  12:04 PM Mar\"},\n\t\t{\"43543.503206018519\", \"[$-64]mmmm dd yyyy  h:mm AM/PM ddd\", \"Marso 19 2019  12:04 PM Mar\"},\n\t\t{\"43543.503206018519\", \"[$-64]mmmmm dd yyyy  h:mm AM/PM dddd\", \"03 19 2019  12:04 PM Martes\"},\n\t\t{\"43543.503206018519\", \"[$-464]mmm dd yyyy  h:mm AM/PM ddd\", \"Mar 19 2019  12:04 PM Mar\"},\n\t\t{\"43543.503206018519\", \"[$-464]mmmm dd yyyy  h:mm AM/PM ddd\", \"Marso 19 2019  12:04 PM Mar\"},\n\t\t{\"43543.503206018519\", \"[$-464]mmmmm dd yyyy  h:mm AM/PM dddd\", \"03 19 2019  12:04 PM Martes\"},\n\t\t{\"43543.503206018519\", \"[$-B]mmm dd yyyy  h:mm AM/PM aaa\", \"maalis 19 2019  12:04 ip. ti\"},\n\t\t{\"43543.503206018519\", \"[$-B]mmmm dd yyyy  h:mm AM/PM ddd\", \"maaliskuu 19 2019  12:04 ip. ti\"},\n\t\t{\"43543.503206018519\", \"[$-B]mmmmm dd yyyy  h:mm AM/PM dddd\", \"03 19 2019  12:04 ip. tiistai\"},\n\t\t{\"43543.503206018519\", \"[$-40B]mmm dd yyyy  h:mm AM/PM aaa\", \"maalis 19 2019  12:04 ip. ti\"},\n\t\t{\"43543.503206018519\", \"[$-40B]mmmm dd yyyy  h:mm AM/PM ddd\", \"maaliskuu 19 2019  12:04 ip. ti\"},\n\t\t{\"43543.503206018519\", \"[$-40B]mmmmm dd yyyy  h:mm AM/PM dddd\", \"03 19 2019  12:04 ip. tiistai\"},\n\t\t{\"44562.189571759256\", \"[$-C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-80C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-80C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-80C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-80C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-80C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-80C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-2c0C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 mat.\"},\n\t\t{\"44562.189571759256\", \"[$-2c0C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 mat.\"},\n\t\t{\"44562.189571759256\", \"[$-2c0C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 mat.\"},\n\t\t{\"43543.503206018519\", \"[$-2c0C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 soir mar.\"},\n\t\t{\"43543.503206018519\", \"[$-2c0C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 soir mar.\"},\n\t\t{\"43543.503206018519\", \"[$-2c0C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 soir mardi\"},\n\t\t{\"44562.189571759256\", \"[$-c0C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-c0C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-c0C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-c0C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-c0C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-c0C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-1C0C]mmm dd yyyy  h:mm AM/PM\", \"Janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1C0C]mmmm dd yyyy  h:mm AM/PM\", \"Janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1C0C]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-1C0C]mmm dd yyyy  h:mm AM/PM aaa\", \"Mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-1C0C]mmmm dd yyyy  h:mm AM/PM ddd\", \"Mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-1C0C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-240C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-240C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-240C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-240C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-240C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-240C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-300C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-300C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-300C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-300C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-300C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-300C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-40C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-40C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-40C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-40C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-40C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-40C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-3c0C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-3c0C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-3c0C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-3c0C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-3c0C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-3c0C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-140C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-140C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-140C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-140C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-140C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-140C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-340C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-340C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-340C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-340C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-340C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-340C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-380C]mmm dd yyyy  h:mm AM/PM\", \"jan. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-380C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-380C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-380C]mmm dd yyyy  h:mm AM/PM aaa\", \"mar. 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-380C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-380C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-180C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-180C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-180C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-180C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-180C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-180C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-200C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-200C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-200C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-200C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-200C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-200C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-280C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-280C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-280C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-280C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-280C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-280C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-100C]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-100C]mmmm dd yyyy  h:mm AM/PM\", \"janvier 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-100C]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-100C]mmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-100C]mmmm dd yyyy  h:mm AM/PM ddd\", \"mars 19 2019  12:04 PM mar.\"},\n\t\t{\"43543.503206018519\", \"[$-100C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-62]m dd yyyy  h:mm AM/PM\", \"1 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-62]mm dd yyyy  h:mm AM/PM\", \"01 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-62]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-62]mmmm dd yyyy  h:mm AM/PM\", \"Jannewaris 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-62]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-62]mmmmmm dd yyyy  h:mm AM/PM\", \"Jannewaris 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-62]m dd yyyy  h:mm AM/PM\", \"3 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-62]mm dd yyyy  h:mm AM/PM\", \"03 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-62]mmm dd yyyy  h:mm AM/PM\", \"Mrt 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-62]mmmm dd yyyy  h:mm AM/PM aaa\", \"Maart 19 2019  12:04 PM tii\"},\n\t\t{\"43543.503206018519\", \"[$-62]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM tii\"},\n\t\t{\"43543.503206018519\", \"[$-62]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Maart 19 2019  12:04 PM tiisdei\"},\n\t\t{\"44562.189571759256\", \"[$-462]m dd yyyy  h:mm AM/PM\", \"1 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-462]mm dd yyyy  h:mm AM/PM\", \"01 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-462]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-462]mmmm dd yyyy  h:mm AM/PM\", \"Jannewaris 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-462]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-462]mmmmmm dd yyyy  h:mm AM/PM\", \"Jannewaris 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-462]m dd yyyy  h:mm AM/PM\", \"3 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-462]mm dd yyyy  h:mm AM/PM\", \"03 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-462]mmm dd yyyy  h:mm AM/PM\", \"Mrt 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-462]mmmm dd yyyy  h:mm AM/PM aaa\", \"Maart 19 2019  12:04 PM tii\"},\n\t\t{\"43543.503206018519\", \"[$-462]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM tii\"},\n\t\t{\"43543.503206018519\", \"[$-462]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Maart 19 2019  12:04 PM tiisdei\"},\n\t\t{\"44562.189571759256\", \"[$-67]mmm dd yyyy  h:mm AM/PM\", \"sii 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-67]mmmm dd yyyy  h:mm AM/PM\", \"siilo 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-67]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-67]mmmmmm dd yyyy  h:mm AM/PM\", \"siilo 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-67]mmm dd yyyy  h:mm AM/PM\", \"mbo 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-67]mmmm dd yyyy  h:mm AM/PM aaa\", \"mbooy 19 2019  12:04 PM maw\"},\n\t\t{\"43543.503206018519\", \"[$-67]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM maw\"},\n\t\t{\"43543.503206018519\", \"[$-67]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mbooy 19 2019  12:04 PM mawbaare\"},\n\t\t{\"44562.189571759256\", \"[$-7C67]mmm dd yyyy  h:mm AM/PM\", \"sii 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C67]mmmm dd yyyy  h:mm AM/PM\", \"siilo 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C67]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C67]mmmmmm dd yyyy  h:mm AM/PM\", \"siilo 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-7C67]mmm dd yyyy  h:mm AM/PM\", \"mbo 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-7C67]mmmm dd yyyy  h:mm AM/PM aaa\", \"mbooy 19 2019  12:04 PM maw\"},\n\t\t{\"43543.503206018519\", \"[$-7C67]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM maw\"},\n\t\t{\"43543.503206018519\", \"[$-7C67]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mbooy 19 2019  12:04 PM mawbaare\"},\n\t\t{\"44562.189571759256\", \"[$-467]mmm dd yyyy  h:mm AM/PM\", \"samw 01 2022  4:32 subaka\"},\n\t\t{\"44562.189571759256\", \"[$-467]mmmm dd yyyy  h:mm AM/PM\", \"samwiee 01 2022  4:32 subaka\"},\n\t\t{\"44562.189571759256\", \"[$-467]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 subaka\"},\n\t\t{\"44562.189571759256\", \"[$-467]mmmmmm dd yyyy  h:mm AM/PM\", \"samwiee 01 2022  4:32 subaka\"},\n\t\t{\"43543.503206018519\", \"[$-467]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 kikiiɗe\"},\n\t\t{\"43543.503206018519\", \"[$-467]mmmm dd yyyy  h:mm AM/PM aaa\", \"marsa 19 2019  12:04 kikiiɗe tal.\"},\n\t\t{\"43543.503206018519\", \"[$-467]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 kikiiɗe tal.\"},\n\t\t{\"43543.503206018519\", \"[$-467]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"marsa 19 2019  12:04 kikiiɗe talaata\"},\n\t\t{\"44562.189571759256\", \"[$-867]mmm dd yyyy  h:mm AM/PM\", \"samw 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-867]mmmm dd yyyy  h:mm AM/PM\", \"samwiee 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-867]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-867]mmmmmm dd yyyy  h:mm AM/PM\", \"samwiee 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-867]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-867]mmmm dd yyyy  h:mm AM/PM aaa\", \"marsa 19 2019  12:04 PM tal.\"},\n\t\t{\"43543.503206018519\", \"[$-867]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM tal.\"},\n\t\t{\"43543.503206018519\", \"[$-867]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"marsa 19 2019  12:04 PM talaata\"},\n\t\t{\"44562.189571759256\", \"[$-56]mmm dd yyyy  h:mm AM/PM\", \"Xan. 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-56]mmmm dd yyyy  h:mm AM/PM\", \"Xaneiro 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-56]mmmmm dd yyyy  h:mm AM/PM\", \"X 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-56]mmmmmm dd yyyy  h:mm AM/PM\", \"Xaneiro 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-56]mmm dd yyyy  h:mm AM/PM\", \"Mar. 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-56]mmmm dd yyyy  h:mm AM/PM\", \"Marzo 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-56]mmmmm dd yyyy  h:mm AM/PM\", \"M 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-56]mmmmmm dd yyyy  h:mm AM/PM\", \"Marzo 19 2019  12:04 p.m.\"},\n\t\t{\"44562.189571759256\", \"[$-56]mmm dd yyyy  h:mm AM/PM\", \"Xan. 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-56]mmmm dd yyyy  h:mm AM/PM\", \"Xaneiro 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-56]mmmmm dd yyyy  h:mm AM/PM\", \"X 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-56]mmmmmm dd yyyy  h:mm AM/PM\", \"Xaneiro 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-56]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar. 19 2019  12:04 p.m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-56]mmmm dd yyyy  h:mm AM/PM ddd\", \"Marzo 19 2019  12:04 p.m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-56]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 p.m. martes\"},\n\t\t{\"43543.503206018519\", \"[$-56]mmmmmm dd yyyy  h:mm AM/PM\", \"Marzo 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-456]mmm dd yyyy  h:mm AM/PM aaa\", \"Mar. 19 2019  12:04 p.m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-456]mmmm dd yyyy  h:mm AM/PM ddd\", \"Marzo 19 2019  12:04 p.m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-456]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 p.m. martes\"},\n\t\t{\"44562.189571759256\", \"[$-37]mmm dd yyyy  h:mm AM/PM\", \"\\u10D8\\u10D0\\u10DC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-37]mmmm dd yyyy  h:mm AM/PM\", \"\\u10D8\\u10D0\\u10DC\\u10D5\\u10D0\\u10E0\\u10D8 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-37]mmmmm dd yyyy  h:mm AM/PM\", \"\\u10D8 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-37]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u10D8\\u10D0\\u10DC\\u10D5\\u10D0\\u10E0\\u10D8 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-37]mmm dd yyyy  h:mm AM/PM\", \"\\u10DB\\u10D0\\u10E0 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-37]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u10DB\\u10D0\\u10E0\\u10E2\\u10D8 19 2019  12:04 PM \\u10E1\\u10D0\\u10DB\\u10E8.\"},\n\t\t{\"43543.503206018519\", \"[$-37]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u10DB 19 2019  12:04 PM \\u10E1\\u10D0\\u10DB\\u10E8.\"},\n\t\t{\"43543.503206018519\", \"[$-37]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u10DB\\u10D0\\u10E0\\u10E2\\u10D8 19 2019  12:04 PM \\u10E1\\u10D0\\u10DB\\u10E8\\u10D0\\u10D1\\u10D0\\u10D7\\u10D8\"},\n\t\t{\"44562.189571759256\", \"[$-437]mmm dd yyyy  h:mm AM/PM\", \"\\u10D8\\u10D0\\u10DC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-437]mmmm dd yyyy  h:mm AM/PM\", \"\\u10D8\\u10D0\\u10DC\\u10D5\\u10D0\\u10E0\\u10D8 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-437]mmmmm dd yyyy  h:mm AM/PM\", \"\\u10D8 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-437]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u10D8\\u10D0\\u10DC\\u10D5\\u10D0\\u10E0\\u10D8 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-437]mmm dd yyyy  h:mm AM/PM\", \"\\u10DB\\u10D0\\u10E0 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-437]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u10DB\\u10D0\\u10E0\\u10E2\\u10D8 19 2019  12:04 PM \\u10E1\\u10D0\\u10DB\\u10E8.\"},\n\t\t{\"43543.503206018519\", \"[$-437]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u10DB 19 2019  12:04 PM \\u10E1\\u10D0\\u10DB\\u10E8.\"},\n\t\t{\"43543.503206018519\", \"[$-437]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u10DB\\u10D0\\u10E0\\u10E2\\u10D8 19 2019  12:04 PM \\u10E1\\u10D0\\u10DB\\u10E8\\u10D0\\u10D1\\u10D0\\u10D7\\u10D8\"},\n\t\t{\"43543.503206018519\", \"[$-7]mmm dd yyyy  h:mm AM/PM aaa\", \"Mär 19 2019  12:04 PM Di\"},\n\t\t{\"43543.503206018519\", \"[$-7]mmmm dd yyyy  h:mm AM/PM ddd\", \"März 19 2019  12:04 PM Di\"},\n\t\t{\"43543.503206018519\", \"[$-7]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Dienstag\"},\n\t\t{\"44562.189571759256\", \"[$-C07]mmm dd yyyy  h:mm AM/PM aaa\", \"Jän 01 2022  4:32 AM Sa\"},\n\t\t{\"44562.189571759256\", \"[$-C07]mmmm dd yyyy  h:mm AM/PM ddd\", \"Jänner 01 2022  4:32 AM Sa\"},\n\t\t{\"44562.189571759256\", \"[$-C07]mmmmm dd yyyy  h:mm AM/PM dddd\", \"J 01 2022  4:32 AM Samstag\"},\n\t\t{\"43543.503206018519\", \"[$-407]mmm dd yyyy  h:mm AM/PM aaa\", \"Mär 19 2019  12:04 PM Di\"},\n\t\t{\"43543.503206018519\", \"[$-407]mmmm dd yyyy  h:mm AM/PM ddd\", \"März 19 2019  12:04 PM Di\"},\n\t\t{\"43543.503206018519\", \"[$-407]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Dienstag\"},\n\t\t{\"43543.503206018519\", \"[$-1407]mmm dd yyyy  h:mm AM/PM aaa\", \"Mär 19 2019  12:04 PM Di\"},\n\t\t{\"43543.503206018519\", \"[$-1407]mmmm dd yyyy  h:mm AM/PM ddd\", \"März 19 2019  12:04 PM Di\"},\n\t\t{\"43543.503206018519\", \"[$-1407]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Dienstag\"},\n\t\t{\"43543.503206018519\", \"[$-1007]mmm dd yyyy  h:mm AM/PM aaa\", \"Mär 19 2019  12:04 PM Di\"},\n\t\t{\"43543.503206018519\", \"[$-1007]mmmm dd yyyy  h:mm AM/PM ddd\", \"März 19 2019  12:04 PM Di\"},\n\t\t{\"43543.503206018519\", \"[$-1007]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Dienstag\"},\n\t\t{\"43543.503206018519\", \"[$-807]mmm dd yyyy  h:mm AM/PM aaa\", \"Mär 19 2019  12:04 PM Di\"},\n\t\t{\"43543.503206018519\", \"[$-807]mmmm dd yyyy  h:mm AM/PM ddd\", \"März 19 2019  12:04 PM Di\"},\n\t\t{\"43543.503206018519\", \"[$-807]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 19 2019  12:04 PM Dienstag\"},\n\t\t{\"44562.189571759256\", \"[$-8]mmm dd yyyy  h:mm AM/PM\", \"\\u0399\\u03B1\\u03BD 01 2022  4:32 \\u03C0\\u03BC\"},\n\t\t{\"44562.189571759256\", \"[$-8]mmmm dd yyyy  h:mm AM/PM\", \"\\u0399\\u03B1\\u03BD\\u03BF\\u03C5\\u03AC\\u03C1\\u03B9\\u03BF\\u03C2 01 2022  4:32 \\u03C0\\u03BC\"},\n\t\t{\"44562.189571759256\", \"[$-8]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0399 01 2022  4:32 \\u03C0\\u03BC\"},\n\t\t{\"44562.189571759256\", \"[$-8]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0399\\u03B1\\u03BD\\u03BF\\u03C5\\u03AC\\u03C1\\u03B9\\u03BF\\u03C2 01 2022  4:32 \\u03C0\\u03BC\"},\n\t\t{\"43543.503206018519\", \"[$-8]mmm dd yyyy  h:mm AM/PM\", \"\\u039C\\u03B1\\u03C1 19 2019  12:04 \\u03BC\\u03BC\"},\n\t\t{\"43543.503206018519\", \"[$-8]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u039C\\u03AC\\u03C1\\u03C4\\u03B9\\u03BF\\u03C2 19 2019  12:04 \\u03BC\\u03BC \\u03A4\\u03C1\\u03B9\"},\n\t\t{\"43543.503206018519\", \"[$-8]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u039C 19 2019  12:04 \\u03BC\\u03BC \\u03A4\\u03C1\\u03B9\"},\n\t\t{\"43543.503206018519\", \"[$-8]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u039C\\u03AC\\u03C1\\u03C4\\u03B9\\u03BF\\u03C2 19 2019  12:04 \\u03BC\\u03BC \\u03A4\\u03C1\\u03AF\\u03C4\\u03B7\"},\n\t\t{\"44562.189571759256\", \"[$-408]mmm dd yyyy  h:mm AM/PM\", \"\\u0399\\u03B1\\u03BD 01 2022  4:32 \\u03C0\\u03BC\"},\n\t\t{\"44562.189571759256\", \"[$-408]mmmm dd yyyy  h:mm AM/PM\", \"\\u0399\\u03B1\\u03BD\\u03BF\\u03C5\\u03AC\\u03C1\\u03B9\\u03BF\\u03C2 01 2022  4:32 \\u03C0\\u03BC\"},\n\t\t{\"44562.189571759256\", \"[$-408]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0399 01 2022  4:32 \\u03C0\\u03BC\"},\n\t\t{\"44562.189571759256\", \"[$-408]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0399\\u03B1\\u03BD\\u03BF\\u03C5\\u03AC\\u03C1\\u03B9\\u03BF\\u03C2 01 2022  4:32 \\u03C0\\u03BC\"},\n\t\t{\"43543.503206018519\", \"[$-408]mmm dd yyyy  h:mm AM/PM\", \"\\u039C\\u03B1\\u03C1 19 2019  12:04 \\u03BC\\u03BC\"},\n\t\t{\"43543.503206018519\", \"[$-408]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u039C\\u03AC\\u03C1\\u03C4\\u03B9\\u03BF\\u03C2 19 2019  12:04 \\u03BC\\u03BC \\u03A4\\u03C1\\u03B9\"},\n\t\t{\"43543.503206018519\", \"[$-408]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u039C 19 2019  12:04 \\u03BC\\u03BC \\u03A4\\u03C1\\u03B9\"},\n\t\t{\"43543.503206018519\", \"[$-408]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u039C\\u03AC\\u03C1\\u03C4\\u03B9\\u03BF\\u03C2 19 2019  12:04 \\u03BC\\u03BC \\u03A4\\u03C1\\u03AF\\u03C4\\u03B7\"},\n\t\t{\"44562.189571759256\", \"[$-6F]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6F]mmmm dd yyyy  h:mm AM/PM\", \"januaari 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6F]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6F]mmmmmm dd yyyy  h:mm AM/PM\", \"januaari 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-6F]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-6F]mmmm dd yyyy  h:mm AM/PM aaa\", \"marsi 19 2019  12:04 PM marl.\"},\n\t\t{\"43543.503206018519\", \"[$-6F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM marl.\"},\n\t\t{\"43543.503206018519\", \"[$-6F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"marsi 19 2019  12:04 PM marlunngorneq\"},\n\t\t{\"44562.189571759256\", \"[$-46F]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-46F]mmmm dd yyyy  h:mm AM/PM\", \"januaari 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-46F]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-46F]mmmmmm dd yyyy  h:mm AM/PM\", \"januaari 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-46F]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-46F]mmmm dd yyyy  h:mm AM/PM aaa\", \"marsi 19 2019  12:04 PM marl.\"},\n\t\t{\"43543.503206018519\", \"[$-46F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM marl.\"},\n\t\t{\"43543.503206018519\", \"[$-46F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"marsi 19 2019  12:04 PM marlunngorneq\"},\n\t\t{\"44562.189571759256\", \"[$-74]mmm dd yyyy  h:mm AM/PM\", \"jteĩ 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-74]mmmm dd yyyy  h:mm AM/PM\", \"jasyteĩ 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-74]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-74]mmmmmm dd yyyy  h:mm AM/PM\", \"jasyteĩ 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-74]mmm dd yyyy  h:mm AM/PM\", \"japy 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-74]mmmm dd yyyy  h:mm AM/PM aaa\", \"jasyapy 19 2019  12:04 p.m. apy\"},\n\t\t{\"43543.503206018519\", \"[$-74]mmmmm dd yyyy  h:mm AM/PM ddd\", \"j 19 2019  12:04 p.m. apy\"},\n\t\t{\"43543.503206018519\", \"[$-74]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"jasyapy 19 2019  12:04 p.m. araapy\"},\n\t\t{\"44562.189571759256\", \"[$-474]mmm dd yyyy  h:mm AM/PM\", \"jteĩ 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-474]mmmm dd yyyy  h:mm AM/PM\", \"jasyteĩ 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-474]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-474]mmmmmm dd yyyy  h:mm AM/PM\", \"jasyteĩ 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-474]mmm dd yyyy  h:mm AM/PM\", \"japy 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-474]mmmm dd yyyy  h:mm AM/PM aaa\", \"jasyapy 19 2019  12:04 p.m. apy\"},\n\t\t{\"43543.503206018519\", \"[$-474]mmmmm dd yyyy  h:mm AM/PM ddd\", \"j 19 2019  12:04 p.m. apy\"},\n\t\t{\"43543.503206018519\", \"[$-474]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"jasyapy 19 2019  12:04 p.m. araapy\"},\n\t\t{\"44562.189571759256\", \"[$-47]mmm dd yyyy  h:mm AM/PM\", \"\\u0A9C\\u0ABE\\u0AA8\\u0ACD\\u0AAF\\u0AC1 01 2022  4:32 \\u0AAA\\u0AC2\\u0AB0\\u0ACD\\u0AB5 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8\"},\n\t\t{\"44562.189571759256\", \"[$-47]mmmm dd yyyy  h:mm AM/PM\", \"\\u0A9C\\u0ABE\\u0AA8\\u0ACD\\u0AAF\\u0AC1\\u0A86\\u0AB0\\u0AC0 01 2022  4:32 \\u0AAA\\u0AC2\\u0AB0\\u0ACD\\u0AB5 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8\"},\n\t\t{\"44562.189571759256\", \"[$-47]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0A9C 01 2022  4:32 \\u0AAA\\u0AC2\\u0AB0\\u0ACD\\u0AB5 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8\"},\n\t\t{\"44562.189571759256\", \"[$-47]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0A9C\\u0ABE\\u0AA8\\u0ACD\\u0AAF\\u0AC1\\u0A86\\u0AB0\\u0AC0 01 2022  4:32 \\u0AAA\\u0AC2\\u0AB0\\u0ACD\\u0AB5 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8\"},\n\t\t{\"43543.503206018519\", \"[$-47]mmm dd yyyy  h:mm AM/PM\", \"\\u0AAE\\u0ABE\\u0AB0\\u0ACD\\u0A9A 19 2019  12:04 \\u0A89\\u0AA4\\u0ACD\\u0AA4\\u0AB0 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8\"},\n\t\t{\"43543.503206018519\", \"[$-47]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0AAE\\u0ABE\\u0AB0\\u0ACD\\u0A9A 19 2019  12:04 \\u0A89\\u0AA4\\u0ACD\\u0AA4\\u0AB0 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8 \\u0AAE\\u0A82\\u0A97\\u0AB3\"},\n\t\t{\"43543.503206018519\", \"[$-47]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0AAE 19 2019  12:04 \\u0A89\\u0AA4\\u0ACD\\u0AA4\\u0AB0 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8 \\u0AAE\\u0A82\\u0A97\\u0AB3\"},\n\t\t{\"43543.503206018519\", \"[$-47]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0AAE\\u0ABE\\u0AB0\\u0ACD\\u0A9A 19 2019  12:04 \\u0A89\\u0AA4\\u0ACD\\u0AA4\\u0AB0 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8 \\u0AAE\\u0A82\\u0A97\\u0AB3\\u0AB5\\u0ABE\\u0AB0\"},\n\t\t{\"44562.189571759256\", \"[$-447]mmm dd yyyy  h:mm AM/PM\", \"\\u0A9C\\u0ABE\\u0AA8\\u0ACD\\u0AAF\\u0AC1 01 2022  4:32 \\u0AAA\\u0AC2\\u0AB0\\u0ACD\\u0AB5 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8\"},\n\t\t{\"44562.189571759256\", \"[$-447]mmmm dd yyyy  h:mm AM/PM\", \"\\u0A9C\\u0ABE\\u0AA8\\u0ACD\\u0AAF\\u0AC1\\u0A86\\u0AB0\\u0AC0 01 2022  4:32 \\u0AAA\\u0AC2\\u0AB0\\u0ACD\\u0AB5 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8\"},\n\t\t{\"44562.189571759256\", \"[$-447]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0A9C 01 2022  4:32 \\u0AAA\\u0AC2\\u0AB0\\u0ACD\\u0AB5 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8\"},\n\t\t{\"44562.189571759256\", \"[$-447]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0A9C\\u0ABE\\u0AA8\\u0ACD\\u0AAF\\u0AC1\\u0A86\\u0AB0\\u0AC0 01 2022  4:32 \\u0AAA\\u0AC2\\u0AB0\\u0ACD\\u0AB5 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8\"},\n\t\t{\"43543.503206018519\", \"[$-447]mmm dd yyyy  h:mm AM/PM\", \"\\u0AAE\\u0ABE\\u0AB0\\u0ACD\\u0A9A 19 2019  12:04 \\u0A89\\u0AA4\\u0ACD\\u0AA4\\u0AB0 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8\"},\n\t\t{\"43543.503206018519\", \"[$-447]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0AAE\\u0ABE\\u0AB0\\u0ACD\\u0A9A 19 2019  12:04 \\u0A89\\u0AA4\\u0ACD\\u0AA4\\u0AB0 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8 \\u0AAE\\u0A82\\u0A97\\u0AB3\"},\n\t\t{\"43543.503206018519\", \"[$-447]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0AAE 19 2019  12:04 \\u0A89\\u0AA4\\u0ACD\\u0AA4\\u0AB0 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8 \\u0AAE\\u0A82\\u0A97\\u0AB3\"},\n\t\t{\"43543.503206018519\", \"[$-447]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0AAE\\u0ABE\\u0AB0\\u0ACD\\u0A9A 19 2019  12:04 \\u0A89\\u0AA4\\u0ACD\\u0AA4\\u0AB0 \\u0AAE\\u0AA7\\u0ACD\\u0AAF\\u0ABE\\u0AB9\\u0ACD\\u0AA8 \\u0AAE\\u0A82\\u0A97\\u0AB3\\u0AB5\\u0ABE\\u0AB0\"},\n\t\t{\"44562.189571759256\", \"[$-68]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-68]mmmm dd yyyy  h:mm AM/PM\", \"Janairu 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-68]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-68]mmmmmm dd yyyy  h:mm AM/PM\", \"Janairu 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-68]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-68]mmmm dd yyyy  h:mm AM/PM aaa\", \"Maris 19 2019  12:04 PM Tal\"},\n\t\t{\"43543.503206018519\", \"[$-68]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Tal\"},\n\t\t{\"43543.503206018519\", \"[$-68]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Maris 19 2019  12:04 PM Talata\"},\n\t\t{\"44562.189571759256\", \"[$-7C68]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C68]mmmm dd yyyy  h:mm AM/PM\", \"Janairu 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C68]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C68]mmmmmm dd yyyy  h:mm AM/PM\", \"Janairu 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-7C68]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-7C68]mmmm dd yyyy  h:mm AM/PM aaa\", \"Maris 19 2019  12:04 PM Tal\"},\n\t\t{\"43543.503206018519\", \"[$-7C68]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Tal\"},\n\t\t{\"43543.503206018519\", \"[$-7C68]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Maris 19 2019  12:04 PM Talata\"},\n\t\t{\"44562.189571759256\", \"[$-468]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-468]mmmm dd yyyy  h:mm AM/PM\", \"Janairu 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-468]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-468]mmmmmm dd yyyy  h:mm AM/PM\", \"Janairu 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-468]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-468]mmmm dd yyyy  h:mm AM/PM aaa\", \"Maris 19 2019  12:04 PM Tal\"},\n\t\t{\"43543.503206018519\", \"[$-468]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Tal\"},\n\t\t{\"43543.503206018519\", \"[$-468]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Maris 19 2019  12:04 PM Talata\"},\n\t\t{\"44562.189571759256\", \"[$-75]mmm dd yyyy  h:mm AM/PM\", \"Ian. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-75]mmmm dd yyyy  h:mm AM/PM\", \"Ianuali 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-75]mmmmm dd yyyy  h:mm AM/PM\", \"I 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-75]mmmmmm dd yyyy  h:mm AM/PM\", \"Ianuali 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-75]mmm dd yyyy  h:mm AM/PM\", \"Mal. 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-75]mmmm dd yyyy  h:mm AM/PM aaa\", \"Malaki 19 2019  12:04 PM P2\"},\n\t\t{\"43543.503206018519\", \"[$-75]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM P2\"},\n\t\t{\"43543.503206018519\", \"[$-75]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Malaki 19 2019  12:04 PM Po\\u02BBalua\"},\n\t\t{\"44562.189571759256\", \"[$-475]mmm dd yyyy  h:mm AM/PM\", \"Ian. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-475]mmmm dd yyyy  h:mm AM/PM\", \"Ianuali 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-475]mmmmm dd yyyy  h:mm AM/PM\", \"I 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-475]mmmmmm dd yyyy  h:mm AM/PM\", \"Ianuali 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-475]mmm dd yyyy  h:mm AM/PM\", \"Mal. 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-475]mmmm dd yyyy  h:mm AM/PM aaa\", \"Malaki 19 2019  12:04 PM P2\"},\n\t\t{\"43543.503206018519\", \"[$-475]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM P2\"},\n\t\t{\"43543.503206018519\", \"[$-475]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Malaki 19 2019  12:04 PM Po\\u02BBalua\"},\n\t\t{\"44562.189571759256\", \"[$-D]mmm dd yyyy  h:mm AM/PM\", \"\\u05D9\\u05E0\\u05D5 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-D]mmmm dd yyyy  h:mm AM/PM\", \"\\u05D9\\u05E0\\u05D5\\u05D0\\u05E8 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-D]mmmmm dd yyyy  h:mm AM/PM\", \"\\u05D9 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-D]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u05D9\\u05E0\\u05D5\\u05D0\\u05E8 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-D]mmm dd yyyy  h:mm AM/PM\", \"\\u05DE\\u05E8\\u05E5 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-D]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u05DE\\u05E8\\u05E5 19 2019  12:04 PM \\u05D9\\u05D5\\u05DD \\u05D2\"},\n\t\t{\"43543.503206018519\", \"[$-D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u05DE 19 2019  12:04 PM \\u05D9\\u05D5\\u05DD \\u05D2\"},\n\t\t{\"43543.503206018519\", \"[$-D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u05DE\\u05E8\\u05E5 19 2019  12:04 PM \\u05D9\\u05D5\\u05DD \\u05E9\\u05DC\\u05D9\\u05E9\\u05D9\"},\n\t\t{\"44562.189571759256\", \"[$-40D]mmm dd yyyy  h:mm AM/PM\", \"\\u05D9\\u05E0\\u05D5 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-40D]mmmm dd yyyy  h:mm AM/PM\", \"\\u05D9\\u05E0\\u05D5\\u05D0\\u05E8 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-40D]mmmmm dd yyyy  h:mm AM/PM\", \"\\u05D9 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-40D]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u05D9\\u05E0\\u05D5\\u05D0\\u05E8 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-40D]mmm dd yyyy  h:mm AM/PM\", \"\\u05DE\\u05E8\\u05E5 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-40D]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u05DE\\u05E8\\u05E5 19 2019  12:04 PM \\u05D9\\u05D5\\u05DD \\u05D2\"},\n\t\t{\"43543.503206018519\", \"[$-40D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u05DE 19 2019  12:04 PM \\u05D9\\u05D5\\u05DD \\u05D2\"},\n\t\t{\"43543.503206018519\", \"[$-40D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u05DE\\u05E8\\u05E5 19 2019  12:04 PM \\u05D9\\u05D5\\u05DD \\u05E9\\u05DC\\u05D9\\u05E9\\u05D9\"},\n\t\t{\"44562.189571759256\", \"[$-39]mmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-39]mmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-39]mmmmm dd yyyy  h:mm AM/PM\", \"\\u091C 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-39]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"43543.503206018519\", \"[$-39]mmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"43543.503206018519\", \"[$-39]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0902\\u0917\\u0932.\"},\n\t\t{\"43543.503206018519\", \"[$-39]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u092E 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0902\\u0917\\u0932.\"},\n\t\t{\"43543.503206018519\", \"[$-39]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0902\\u0917\\u0932\\u0935\\u093E\\u0930\"},\n\t\t{\"44562.189571759256\", \"[$-439]mmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-439]mmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-439]mmmmm dd yyyy  h:mm AM/PM\", \"\\u091C 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-439]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"43543.503206018519\", \"[$-439]mmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"43543.503206018519\", \"[$-439]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0902\\u0917\\u0932.\"},\n\t\t{\"43543.503206018519\", \"[$-439]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u092E 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0902\\u0917\\u0932.\"},\n\t\t{\"43543.503206018519\", \"[$-439]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0902\\u0917\\u0932\\u0935\\u093E\\u0930\"},\n\t\t{\"44562.189571759256\", \"[$-E]mmm dd yyyy  h:mm AM/PM\", \"jan. 01 2022  4:32 de.\"},\n\t\t{\"44562.189571759256\", \"[$-E]mmmm dd yyyy  h:mm AM/PM\", \"január 01 2022  4:32 de.\"},\n\t\t{\"44562.189571759256\", \"[$-E]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 de.\"},\n\t\t{\"44562.189571759256\", \"[$-E]mmmmmm dd yyyy  h:mm AM/PM\", \"január 01 2022  4:32 de.\"},\n\t\t{\"43543.503206018519\", \"[$-E]mmm dd yyyy  h:mm AM/PM\", \"márc. 19 2019  12:04 du.\"},\n\t\t{\"43543.503206018519\", \"[$-E]mmmm dd yyyy  h:mm AM/PM aaa\", \"március 19 2019  12:04 du. K\"},\n\t\t{\"43543.503206018519\", \"[$-E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 du. K\"},\n\t\t{\"43543.503206018519\", \"[$-E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"március 19 2019  12:04 du. kedd\"},\n\t\t{\"44562.189571759256\", \"[$-40E]mmm dd yyyy  h:mm AM/PM\", \"jan. 01 2022  4:32 de.\"},\n\t\t{\"44562.189571759256\", \"[$-40E]mmmm dd yyyy  h:mm AM/PM\", \"január 01 2022  4:32 de.\"},\n\t\t{\"44562.189571759256\", \"[$-40E]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 de.\"},\n\t\t{\"44562.189571759256\", \"[$-40E]mmmmmm dd yyyy  h:mm AM/PM\", \"január 01 2022  4:32 de.\"},\n\t\t{\"43543.503206018519\", \"[$-40E]mmm dd yyyy  h:mm AM/PM\", \"márc. 19 2019  12:04 du.\"},\n\t\t{\"43543.503206018519\", \"[$-40E]mmmm dd yyyy  h:mm AM/PM aaa\", \"március 19 2019  12:04 du. K\"},\n\t\t{\"43543.503206018519\", \"[$-40E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 du. K\"},\n\t\t{\"43543.503206018519\", \"[$-40E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"március 19 2019  12:04 du. kedd\"},\n\t\t{\"44562.189571759256\", \"[$-F]mmm dd yyyy  h:mm AM/PM\", \"jan. 01 2022  4:32 f.h.\"},\n\t\t{\"44562.189571759256\", \"[$-F]mmmm dd yyyy  h:mm AM/PM\", \"janúar 01 2022  4:32 f.h.\"},\n\t\t{\"44562.189571759256\", \"[$-F]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 f.h.\"},\n\t\t{\"44562.189571759256\", \"[$-F]mmmmmm dd yyyy  h:mm AM/PM\", \"janúar 01 2022  4:32 f.h.\"},\n\t\t{\"43543.503206018519\", \"[$-F]mmm dd yyyy  h:mm AM/PM\", \"mar. 19 2019  12:04 e.h.\"},\n\t\t{\"43543.503206018519\", \"[$-F]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 e.h. þri.\"},\n\t\t{\"43543.503206018519\", \"[$-F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 e.h. þri.\"},\n\t\t{\"43543.503206018519\", \"[$-F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 e.h. þriðjudagur\"},\n\t\t{\"44562.189571759256\", \"[$-40F]mmm dd yyyy  h:mm AM/PM\", \"jan. 01 2022  4:32 f.h.\"},\n\t\t{\"44562.189571759256\", \"[$-40F]mmmm dd yyyy  h:mm AM/PM\", \"janúar 01 2022  4:32 f.h.\"},\n\t\t{\"44562.189571759256\", \"[$-40F]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 f.h.\"},\n\t\t{\"44562.189571759256\", \"[$-40F]mmmmmm dd yyyy  h:mm AM/PM\", \"janúar 01 2022  4:32 f.h.\"},\n\t\t{\"43543.503206018519\", \"[$-40F]mmm dd yyyy  h:mm AM/PM\", \"mar. 19 2019  12:04 e.h.\"},\n\t\t{\"43543.503206018519\", \"[$-40F]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 e.h. þri.\"},\n\t\t{\"43543.503206018519\", \"[$-40F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 e.h. þri.\"},\n\t\t{\"43543.503206018519\", \"[$-40F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 e.h. þriðjudagur\"},\n\t\t{\"44562.189571759256\", \"[$-70]mmm dd yyyy  h:mm AM/PM\", \"Jen 01 2022  4:32 A.M.\"},\n\t\t{\"44562.189571759256\", \"[$-70]mmmm dd yyyy  h:mm AM/PM\", \"Jenụwarị 01 2022  4:32 A.M.\"},\n\t\t{\"44562.189571759256\", \"[$-70]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 A.M.\"},\n\t\t{\"44562.189571759256\", \"[$-70]mmmmmm dd yyyy  h:mm AM/PM\", \"Jenụwarị 01 2022  4:32 A.M.\"},\n\t\t{\"43543.503206018519\", \"[$-70]mmm dd yyyy  h:mm AM/PM\", \"Mac 19 2019  12:04 P.M.\"},\n\t\t{\"43543.503206018519\", \"[$-70]mmmm dd yyyy  h:mm AM/PM aaa\", \"Machị 19 2019  12:04 P.M. Tiu\"},\n\t\t{\"43543.503206018519\", \"[$-70]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 P.M. Tiu\"},\n\t\t{\"43543.503206018519\", \"[$-70]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Machị 19 2019  12:04 P.M. Tiuzdee\"},\n\t\t{\"44562.189571759256\", \"[$-470]mmm dd yyyy  h:mm AM/PM\", \"Jen 01 2022  4:32 A.M.\"},\n\t\t{\"44562.189571759256\", \"[$-470]mmmm dd yyyy  h:mm AM/PM\", \"Jenụwarị 01 2022  4:32 A.M.\"},\n\t\t{\"44562.189571759256\", \"[$-470]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 A.M.\"},\n\t\t{\"44562.189571759256\", \"[$-470]mmmmmm dd yyyy  h:mm AM/PM\", \"Jenụwarị 01 2022  4:32 A.M.\"},\n\t\t{\"43543.503206018519\", \"[$-470]mmm dd yyyy  h:mm AM/PM\", \"Mac 19 2019  12:04 P.M.\"},\n\t\t{\"43543.503206018519\", \"[$-470]mmmm dd yyyy  h:mm AM/PM aaa\", \"Machị 19 2019  12:04 P.M. Tiu\"},\n\t\t{\"43543.503206018519\", \"[$-470]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 P.M. Tiu\"},\n\t\t{\"43543.503206018519\", \"[$-470]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Machị 19 2019  12:04 P.M. Tiuzdee\"},\n\t\t{\"44562.189571759256\", \"[$-21]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-21]mmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-21]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-21]mmmmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-21]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-21]mmmm dd yyyy  h:mm AM/PM aaa\", \"Maret 19 2019  12:04 PM Sel\"},\n\t\t{\"43543.503206018519\", \"[$-21]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Sel\"},\n\t\t{\"43543.503206018519\", \"[$-21]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Maret 19 2019  12:04 PM Selasa\"},\n\t\t{\"44562.189571759256\", \"[$-421]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-421]mmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-421]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-421]mmmmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-421]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-421]mmmm dd yyyy  h:mm AM/PM aaa\", \"Maret 19 2019  12:04 PM Sel\"},\n\t\t{\"43543.503206018519\", \"[$-421]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Sel\"},\n\t\t{\"43543.503206018519\", \"[$-421]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Maret 19 2019  12:04 PM Selasa\"},\n\t\t{\"44562.189571759256\", \"[$-5D]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-5D]mmmm dd yyyy  h:mm AM/PM\", \"Jaannuari 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-5D]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-5D]mmmmmm dd yyyy  h:mm AM/PM\", \"Jaannuari 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-5D]mmm dd yyyy  h:mm AM/PM\", \"Mas 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-5D]mmmm dd yyyy  h:mm AM/PM aaa\", \"Maatsi 19 2019  12:04 PM Aip\"},\n\t\t{\"43543.503206018519\", \"[$-5D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Aip\"},\n\t\t{\"43543.503206018519\", \"[$-5D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Maatsi 19 2019  12:04 PM Aippiq\"},\n\t\t{\"44562.189571759256\", \"[$-7C5D]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C5D]mmmm dd yyyy  h:mm AM/PM\", \"Jaannuari 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C5D]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C5D]mmmmmm dd yyyy  h:mm AM/PM\", \"Jaannuari 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-7C5D]mmm dd yyyy  h:mm AM/PM\", \"Mas 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-7C5D]mmmm dd yyyy  h:mm AM/PM aaa\", \"Maatsi 19 2019  12:04 PM Aip\"},\n\t\t{\"43543.503206018519\", \"[$-7C5D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Aip\"},\n\t\t{\"43543.503206018519\", \"[$-7C5D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Maatsi 19 2019  12:04 PM Aippiq\"},\n\t\t{\"44562.189571759256\", \"[$-85D]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-85D]mmmm dd yyyy  h:mm AM/PM\", \"Jaannuari 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-85D]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-85D]mmmmmm dd yyyy  h:mm AM/PM\", \"Jaannuari 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-85D]mmm dd yyyy  h:mm AM/PM\", \"Mas 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-85D]mmmm dd yyyy  h:mm AM/PM aaa\", \"Maatsi 19 2019  12:04 PM Aip\"},\n\t\t{\"43543.503206018519\", \"[$-85D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Aip\"},\n\t\t{\"43543.503206018519\", \"[$-85D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Maatsi 19 2019  12:04 PM Aippiq\"},\n\t\t{\"44562.189571759256\", \"[$-785D]mmm dd yyyy  h:mm AM/PM\", \"\\u152E\\u14D0\\u14C4 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-785D]mmmm dd yyyy  h:mm AM/PM\", \"\\u152E\\u14D0\\u14C4\\u140A\\u1546 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-785D]mmmmm dd yyyy  h:mm AM/PM\", \"\\u152E 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-785D]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u152E\\u14D0\\u14C4\\u140A\\u1546 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-785D]mmm dd yyyy  h:mm AM/PM\", \"\\u14AB\\u1466\\u14EF 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-785D]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u14AB\\u1466\\u14EF 19 2019  12:04 PM \\u140A\\u1403\\u1449\\u1431\"},\n\t\t{\"43543.503206018519\", \"[$-785D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u14AB 19 2019  12:04 PM \\u140A\\u1403\\u1449\\u1431\"},\n\t\t{\"43543.503206018519\", \"[$-785D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u14AB\\u1466\\u14EF 19 2019  12:04 PM \\u140A\\u1403\\u1449\\u1431\\u1585\"},\n\t\t{\"44562.189571759256\", \"[$-45D]mmm dd yyyy  h:mm AM/PM\", \"\\u152E\\u14D0\\u14C4 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-45D]mmmm dd yyyy  h:mm AM/PM\", \"\\u152E\\u14D0\\u14C4\\u140A\\u1546 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-45D]mmmmm dd yyyy  h:mm AM/PM\", \"\\u152E 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-45D]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u152E\\u14D0\\u14C4\\u140A\\u1546 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-45D]mmm dd yyyy  h:mm AM/PM\", \"\\u14AB\\u1466\\u14EF 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-45D]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u14AB\\u1466\\u14EF 19 2019  12:04 PM \\u140A\\u1403\\u1449\\u1431\"},\n\t\t{\"43543.503206018519\", \"[$-45D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u14AB 19 2019  12:04 PM \\u140A\\u1403\\u1449\\u1431\"},\n\t\t{\"43543.503206018519\", \"[$-45D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u14AB\\u1466\\u14EF 19 2019  12:04 PM \\u140A\\u1403\\u1449\\u1431\\u1585\"},\n\t\t{\"44562.189571759256\", \"[$-3C]mmm dd yyyy  h:mm AM/PM\", \"Ean 01 2022  4:32 r.n.\"},\n\t\t{\"44593.189571759256\", \"[$-3C]mmm dd yyyy  h:mm AM/PM\", \"Feabh 01 2022  4:32 r.n.\"},\n\t\t{\"44621.18957170139\", \"[$-3C]mmm dd yyyy  h:mm AM/PM\", \"Márta 01 2022  4:32 r.n.\"},\n\t\t{\"44652.18957170139\", \"[$-3C]mmm dd yyyy  h:mm AM/PM\", \"Aib 01 2022  4:32 r.n.\"},\n\t\t{\"44682.18957170139\", \"[$-3C]mmm dd yyyy  h:mm AM/PM\", \"Beal 01 2022  4:32 r.n.\"},\n\t\t{\"44713.18957170139\", \"[$-3C]mmm dd yyyy  h:mm AM/PM\", \"Meith 01 2022  4:32 r.n.\"},\n\t\t{\"44743.18957170139\", \"[$-3C]mmm dd yyyy  h:mm AM/PM\", \"Iúil 01 2022  4:32 r.n.\"},\n\t\t{\"44774.18957170139\", \"[$-3C]mmm dd yyyy  h:mm AM/PM\", \"Lún 01 2022  4:32 r.n.\"},\n\t\t{\"44805.18957170139\", \"[$-3C]mmm dd yyyy  h:mm AM/PM\", \"MFómh 01 2022  4:32 r.n.\"},\n\t\t{\"44835.18957170139\", \"[$-3C]mmm dd yyyy  h:mm AM/PM\", \"DFómh 01 2022  4:32 r.n.\"},\n\t\t{\"44866.18957170139\", \"[$-3C]mmm dd yyyy  h:mm AM/PM\", \"Samh 01 2022  4:32 r.n.\"},\n\t\t{\"44896.18957170139\", \"[$-3C]mmm dd yyyy  h:mm AM/PM\", \"Noll 01 2022  4:32 r.n.\"},\n\t\t{\"44562.189571759256\", \"[$-3C]mmmm dd yyyy  h:mm AM/PM\", \"Eanáir 01 2022  4:32 r.n.\"},\n\t\t{\"44593.189571759256\", \"[$-3C]mmmm dd yyyy  h:mm AM/PM\", \"Feabhra 01 2022  4:32 r.n.\"},\n\t\t{\"44621.18957170139\", \"[$-3C]mmmm dd yyyy  h:mm AM/PM\", \"Márta 01 2022  4:32 r.n.\"},\n\t\t{\"44652.18957170139\", \"[$-3C]mmmm dd yyyy  h:mm AM/PM\", \"Aibreán 01 2022  4:32 r.n.\"},\n\t\t{\"44682.18957170139\", \"[$-3C]mmmm dd yyyy  h:mm AM/PM\", \"Bealtaine 01 2022  4:32 r.n.\"},\n\t\t{\"44713.18957170139\", \"[$-3C]mmmm dd yyyy  h:mm AM/PM\", \"Meitheamh 01 2022  4:32 r.n.\"},\n\t\t{\"44743.18957170139\", \"[$-3C]mmmm dd yyyy  h:mm AM/PM\", \"Iúil 01 2022  4:32 r.n.\"},\n\t\t{\"44774.18957170139\", \"[$-3C]mmmm dd yyyy  h:mm AM/PM\", \"Lúnasa 01 2022  4:32 r.n.\"},\n\t\t{\"44805.18957170139\", \"[$-3C]mmmm dd yyyy  h:mm AM/PM\", \"Meán Fómhair 01 2022  4:32 r.n.\"},\n\t\t{\"44835.18957170139\", \"[$-3C]mmmm dd yyyy  h:mm AM/PM\", \"Deireadh Fómhair 01 2022  4:32 r.n.\"},\n\t\t{\"44866.18957170139\", \"[$-3C]mmmm dd yyyy  h:mm AM/PM\", \"Samhain 01 2022  4:32 r.n.\"},\n\t\t{\"44896.18957170139\", \"[$-3C]mmmm dd yyyy  h:mm AM/PM\", \"Nollaig 01 2022  4:32 r.n.\"},\n\t\t{\"44562.189571759256\", \"[$-3C]mmmmm dd yyyy  h:mm AM/PM\", \"E 01 2022  4:32 r.n.\"},\n\t\t{\"44593.189571759256\", \"[$-3C]mmmmm dd yyyy  h:mm AM/PM\", \"F 01 2022  4:32 r.n.\"},\n\t\t{\"44621.18957170139\", \"[$-3C]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 r.n.\"},\n\t\t{\"44652.18957170139\", \"[$-3C]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 r.n.\"},\n\t\t{\"44682.18957170139\", \"[$-3C]mmmmm dd yyyy  h:mm AM/PM\", \"B 01 2022  4:32 r.n.\"},\n\t\t{\"44713.18957170139\", \"[$-3C]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 r.n.\"},\n\t\t{\"44743.18957170139\", \"[$-3C]mmmmm dd yyyy  h:mm AM/PM\", \"I 01 2022  4:32 r.n.\"},\n\t\t{\"44774.18957170139\", \"[$-3C]mmmmm dd yyyy  h:mm AM/PM\", \"L 01 2022  4:32 r.n.\"},\n\t\t{\"44805.18957170139\", \"[$-3C]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 r.n.\"},\n\t\t{\"44835.18957170139\", \"[$-3C]mmmmm dd yyyy  h:mm AM/PM aaa\", \"D 01 2022  4:32 r.n. Sath\"},\n\t\t{\"44866.18957170139\", \"[$-3C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"S 01 2022  4:32 r.n. Máirt\"},\n\t\t{\"44896.18957170139\", \"[$-3C]mmmmm dd yyyy  h:mm AM/PM dddd\", \"N 01 2022  4:32 r.n. Déardaoin\"},\n\t\t{\"44562.189571759256\", \"[$-83C]mmm dd yyyy  h:mm AM/PM\", \"Ean 01 2022  4:32 r.n.\"},\n\t\t{\"44593.189571759256\", \"[$-83C]mmm dd yyyy  h:mm AM/PM\", \"Feabh 01 2022  4:32 r.n.\"},\n\t\t{\"44621.18957170139\", \"[$-83C]mmm dd yyyy  h:mm AM/PM\", \"Márta 01 2022  4:32 r.n.\"},\n\t\t{\"44652.18957170139\", \"[$-83C]mmm dd yyyy  h:mm AM/PM\", \"Aib 01 2022  4:32 r.n.\"},\n\t\t{\"44682.18957170139\", \"[$-83C]mmm dd yyyy  h:mm AM/PM\", \"Beal 01 2022  4:32 r.n.\"},\n\t\t{\"44713.18957170139\", \"[$-83C]mmm dd yyyy  h:mm AM/PM\", \"Meith 01 2022  4:32 r.n.\"},\n\t\t{\"44743.18957170139\", \"[$-83C]mmm dd yyyy  h:mm AM/PM\", \"Iúil 01 2022  4:32 r.n.\"},\n\t\t{\"44774.18957170139\", \"[$-83C]mmm dd yyyy  h:mm AM/PM\", \"Lún 01 2022  4:32 r.n.\"},\n\t\t{\"44805.18957170139\", \"[$-83C]mmm dd yyyy  h:mm AM/PM\", \"MFómh 01 2022  4:32 r.n.\"},\n\t\t{\"44835.18957170139\", \"[$-83C]mmm dd yyyy  h:mm AM/PM\", \"DFómh 01 2022  4:32 r.n.\"},\n\t\t{\"44866.18957170139\", \"[$-83C]mmm dd yyyy  h:mm AM/PM\", \"Samh 01 2022  4:32 r.n.\"},\n\t\t{\"44896.18957170139\", \"[$-83C]mmm dd yyyy  h:mm AM/PM\", \"Noll 01 2022  4:32 r.n.\"},\n\t\t{\"44562.189571759256\", \"[$-83C]mmmm dd yyyy  h:mm AM/PM\", \"Eanáir 01 2022  4:32 r.n.\"},\n\t\t{\"44593.189571759256\", \"[$-83C]mmmm dd yyyy  h:mm AM/PM\", \"Feabhra 01 2022  4:32 r.n.\"},\n\t\t{\"44621.18957170139\", \"[$-83C]mmmm dd yyyy  h:mm AM/PM\", \"Márta 01 2022  4:32 r.n.\"},\n\t\t{\"44652.18957170139\", \"[$-83C]mmmm dd yyyy  h:mm AM/PM\", \"Aibreán 01 2022  4:32 r.n.\"},\n\t\t{\"44682.18957170139\", \"[$-83C]mmmm dd yyyy  h:mm AM/PM\", \"Bealtaine 01 2022  4:32 r.n.\"},\n\t\t{\"44713.18957170139\", \"[$-83C]mmmm dd yyyy  h:mm AM/PM\", \"Meitheamh 01 2022  4:32 r.n.\"},\n\t\t{\"44743.18957170139\", \"[$-83C]mmmm dd yyyy  h:mm AM/PM\", \"Iúil 01 2022  4:32 r.n.\"},\n\t\t{\"44774.18957170139\", \"[$-83C]mmmm dd yyyy  h:mm AM/PM\", \"Lúnasa 01 2022  4:32 r.n.\"},\n\t\t{\"44805.18957170139\", \"[$-83C]mmmm dd yyyy  h:mm AM/PM\", \"Meán Fómhair 01 2022  4:32 r.n.\"},\n\t\t{\"44835.18957170139\", \"[$-83C]mmmm dd yyyy  h:mm AM/PM aaa\", \"Deireadh Fómhair 01 2022  4:32 r.n. Sath\"},\n\t\t{\"44866.18957170139\", \"[$-83C]mmmm dd yyyy  h:mm AM/PM ddd\", \"Samhain 01 2022  4:32 r.n. Máirt\"},\n\t\t{\"44896.18957170139\", \"[$-83C]mmmm dd yyyy  h:mm AM/PM dddd\", \"Nollaig 01 2022  4:32 r.n. Déardaoin\"},\n\t\t{\"43543.503206018519\", \"[$-10]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 PM mar\"},\n\t\t{\"43543.503206018519\", \"[$-10]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 PM mar\"},\n\t\t{\"43543.503206018519\", \"[$-10]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM martedì\"},\n\t\t{\"43543.503206018519\", \"[$-410]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 PM mar\"},\n\t\t{\"43543.503206018519\", \"[$-410]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 PM mar\"},\n\t\t{\"43543.503206018519\", \"[$-410]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM martedì\"},\n\t\t{\"43543.503206018519\", \"[$-810]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 PM mar\"},\n\t\t{\"43543.503206018519\", \"[$-810]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 PM mar\"},\n\t\t{\"43543.503206018519\", \"[$-810]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM martedì\"},\n\t\t{\"43543.503206018519\", \"[$-11]mmm dd yyyy  h:mm AM/PM aaa\", \"3月 19 2019  12:04 午後 火\"},\n\t\t{\"43543.503206018519\", \"[$-11]mmmm dd yyyy  h:mm AM/PM ddd\", \"3月 19 2019  12:04 午後 火\"},\n\t\t{\"43543.503206018519\", \"[$-11]mmmmm dd yyyy  h:mm AM/PM dddd\", \"3 19 2019  12:04 午後 火曜日\"},\n\t\t{\"43543.503206018519\", \"[$-411]mmm dd yyyy  h:mm AM/PM aaa\", \"3月 19 2019  12:04 午後 火\"},\n\t\t{\"43543.503206018519\", \"[$-411]mmmm dd yyyy  h:mm AM/PM ddd\", \"3月 19 2019  12:04 午後 火\"},\n\t\t{\"43543.503206018519\", \"[$-411]mmmmm dd yyyy  h:mm AM/PM dddd\", \"3 19 2019  12:04 午後 火曜日\"},\n\t\t{\"44562.189571759256\", \"[$-4B]mmm dd yyyy  h:mm AM/PM\", \"\\u0C9C\\u0CA8\\u0CB5\\u0CB0\\u0CBF 01 2022  4:32 \\u0CAA\\u0CC2\\u0CB0\\u0CCD\\u0CB5\\u0CBE\\u0CB9\\u0CCD\\u0CA8\"},\n\t\t{\"44562.189571759256\", \"[$-4B]mmmm dd yyyy  h:mm AM/PM\", \"\\u0C9C\\u0CA8\\u0CB5\\u0CB0\\u0CBF 01 2022  4:32 \\u0CAA\\u0CC2\\u0CB0\\u0CCD\\u0CB5\\u0CBE\\u0CB9\\u0CCD\\u0CA8\"},\n\t\t{\"44562.189571759256\", \"[$-4B]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0C9C 01 2022  4:32 \\u0CAA\\u0CC2\\u0CB0\\u0CCD\\u0CB5\\u0CBE\\u0CB9\\u0CCD\\u0CA8\"},\n\t\t{\"44562.189571759256\", \"[$-4B]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0C9C\\u0CA8\\u0CB5\\u0CB0\\u0CBF 01 2022  4:32 \\u0CAA\\u0CC2\\u0CB0\\u0CCD\\u0CB5\\u0CBE\\u0CB9\\u0CCD\\u0CA8\"},\n\t\t{\"43543.503206018519\", \"[$-4B]mmm dd yyyy  h:mm AM/PM\", \"\\u0CAE\\u0CBE\\u0CB0\\u0CCD\\u0C9A\\u0CCD 19 2019  12:04 \\u0C85\\u0CAA\\u0CB0\\u0CBE\\u0CB9\\u0CCD\\u0CA8\"},\n\t\t{\"43543.503206018519\", \"[$-4B]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0CAE\\u0CBE\\u0CB0\\u0CCD\\u0C9A\\u0CCD 19 2019  12:04 \\u0C85\\u0CAA\\u0CB0\\u0CBE\\u0CB9\\u0CCD\\u0CA8 \\u0CAE\\u0C82\\u0C97\\u0CB3.\"},\n\t\t{\"43543.503206018519\", \"[$-4B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0CAE 19 2019  12:04 \\u0C85\\u0CAA\\u0CB0\\u0CBE\\u0CB9\\u0CCD\\u0CA8 \\u0CAE\\u0C82\\u0C97\\u0CB3.\"},\n\t\t{\"43543.503206018519\", \"[$-4B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0CAE\\u0CBE\\u0CB0\\u0CCD\\u0C9A\\u0CCD 19 2019  12:04 \\u0C85\\u0CAA\\u0CB0\\u0CBE\\u0CB9\\u0CCD\\u0CA8 \\u0CAE\\u0C82\\u0C97\\u0CB3\\u0CB5\\u0CBE\\u0CB0\"},\n\t\t{\"44562.189571759256\", \"[$-44B]mmm dd yyyy  h:mm AM/PM\", \"\\u0C9C\\u0CA8\\u0CB5\\u0CB0\\u0CBF 01 2022  4:32 \\u0CAA\\u0CC2\\u0CB0\\u0CCD\\u0CB5\\u0CBE\\u0CB9\\u0CCD\\u0CA8\"},\n\t\t{\"44562.189571759256\", \"[$-44B]mmmm dd yyyy  h:mm AM/PM\", \"\\u0C9C\\u0CA8\\u0CB5\\u0CB0\\u0CBF 01 2022  4:32 \\u0CAA\\u0CC2\\u0CB0\\u0CCD\\u0CB5\\u0CBE\\u0CB9\\u0CCD\\u0CA8\"},\n\t\t{\"44562.189571759256\", \"[$-44B]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0C9C 01 2022  4:32 \\u0CAA\\u0CC2\\u0CB0\\u0CCD\\u0CB5\\u0CBE\\u0CB9\\u0CCD\\u0CA8\"},\n\t\t{\"44562.189571759256\", \"[$-44B]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0C9C\\u0CA8\\u0CB5\\u0CB0\\u0CBF 01 2022  4:32 \\u0CAA\\u0CC2\\u0CB0\\u0CCD\\u0CB5\\u0CBE\\u0CB9\\u0CCD\\u0CA8\"},\n\t\t{\"43543.503206018519\", \"[$-44B]mmm dd yyyy  h:mm AM/PM\", \"\\u0CAE\\u0CBE\\u0CB0\\u0CCD\\u0C9A\\u0CCD 19 2019  12:04 \\u0C85\\u0CAA\\u0CB0\\u0CBE\\u0CB9\\u0CCD\\u0CA8\"},\n\t\t{\"43543.503206018519\", \"[$-44B]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0CAE\\u0CBE\\u0CB0\\u0CCD\\u0C9A\\u0CCD 19 2019  12:04 \\u0C85\\u0CAA\\u0CB0\\u0CBE\\u0CB9\\u0CCD\\u0CA8 \\u0CAE\\u0C82\\u0C97\\u0CB3.\"},\n\t\t{\"43543.503206018519\", \"[$-44B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0CAE 19 2019  12:04 \\u0C85\\u0CAA\\u0CB0\\u0CBE\\u0CB9\\u0CCD\\u0CA8 \\u0CAE\\u0C82\\u0C97\\u0CB3.\"},\n\t\t{\"43543.503206018519\", \"[$-44B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0CAE\\u0CBE\\u0CB0\\u0CCD\\u0C9A\\u0CCD 19 2019  12:04 \\u0C85\\u0CAA\\u0CB0\\u0CBE\\u0CB9\\u0CCD\\u0CA8 \\u0CAE\\u0C82\\u0C97\\u0CB3\\u0CB5\\u0CBE\\u0CB0\"},\n\t\t{\"44562.189571759256\", \"[$-471]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-471]mmmm dd yyyy  h:mm AM/PM\", \"January 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-471]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-471]mmmmmm dd yyyy  h:mm AM/PM\", \"January 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-471]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-471]mmmm dd yyyy  h:mm AM/PM aaa\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-471]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-471]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"March 19 2019  12:04 PM Tuesday\"},\n\t\t{\"44562.189571759256\", \"[$-60]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0624\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-60]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0624\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-60]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-60]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0624\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-60]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0655\\u0686 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-60]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0655\\u0686 19 2019  12:04 PM \\u0628\\u06C6\\u0645\\u0648\\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-60]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 PM \\u0628\\u06C6\\u0645\\u0648\\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-60]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0655\\u0686 19 2019  12:04 PM \\u0628\\u06C6\\u0645\\u0648\\u0627\\u0631\"},\n\t\t{\"44562.189571759256\", \"[$-460]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0624\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-460]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0624\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-460]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-460]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0624\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-460]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0655\\u0686 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-460]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0655\\u0686 19 2019  12:04 PM \\u0628\\u06C6\\u0645\\u0648\\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-460]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 PM \\u0628\\u06C6\\u0645\\u0648\\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-460]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0655\\u0686 19 2019  12:04 PM \\u0628\\u06C6\\u0645\\u0648\\u0627\\u0631\"},\n\t\t{\"44562.189571759256\", \"[$-860]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-860]mmmm dd yyyy  h:mm AM/PM\", \"January 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-860]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-860]mmmmmm dd yyyy  h:mm AM/PM\", \"January 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-860]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-860]mmmm dd yyyy  h:mm AM/PM aaa\", \"March 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-860]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-860]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"March 19 2019  12:04 PM Tuesday\"},\n\t\t{\"44562.189571759256\", \"[$-3F]mmm dd yyyy  h:mm AM/PM\", \"қаң 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-3F]mmmm dd yyyy  h:mm AM/PM\", \"Қаңтар 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-3F]mmmmm dd yyyy  h:mm AM/PM\", \"Қ 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-3F]mmmmmm dd yyyy  h:mm AM/PM\", \"Қаңтар 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-3F]mmm dd yyyy  h:mm AM/PM\", \"нау 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-3F]mmmm dd yyyy  h:mm AM/PM aaa\", \"Наурыз 19 2019  12:04 PM \\u0441\\u0435\\u0439\"},\n\t\t{\"43543.503206018519\", \"[$-3F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"Н 19 2019  12:04 PM \\u0441\\u0435\\u0439\"},\n\t\t{\"43543.503206018519\", \"[$-3F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Наурыз 19 2019  12:04 PM \\u0441\\u0435\\u0439\\u0441\\u0435\\u043D\\u0431\\u0456\"},\n\t\t{\"44562.189571759256\", \"[$-43F]mmm dd yyyy  h:mm AM/PM\", \"қаң 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-43F]mmmm dd yyyy  h:mm AM/PM\", \"Қаңтар 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-43F]mmmmm dd yyyy  h:mm AM/PM\", \"Қ 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-43F]mmmmmm dd yyyy  h:mm AM/PM\", \"Қаңтар 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-43F]mmm dd yyyy  h:mm AM/PM\", \"нау 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-43F]mmmm dd yyyy  h:mm AM/PM aaa\", \"Наурыз 19 2019  12:04 PM \\u0441\\u0435\\u0439\"},\n\t\t{\"43543.503206018519\", \"[$-43F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"Н 19 2019  12:04 PM \\u0441\\u0435\\u0439\"},\n\t\t{\"43543.503206018519\", \"[$-43F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Наурыз 19 2019  12:04 PM \\u0441\\u0435\\u0439\\u0441\\u0435\\u043D\\u0431\\u0456\"},\n\t\t{\"44562.189571759256\", \"[$-53]mmm dd yyyy  h:mm AM/PM\", \"\\u17E1 01 2022  4:32 \\u1796\\u17D2\\u179A\\u17B9\\u1780\"},\n\t\t{\"44562.189571759256\", \"[$-53]mmmm dd yyyy  h:mm AM/PM\", \"\\u1798\\u1780\\u179A\\u17B6 01 2022  4:32 \\u1796\\u17D2\\u179A\\u17B9\\u1780\"},\n\t\t{\"44562.189571759256\", \"[$-53]mmmmm dd yyyy  h:mm AM/PM\", \"\\u1798 01 2022  4:32 \\u1796\\u17D2\\u179A\\u17B9\\u1780\"},\n\t\t{\"44562.189571759256\", \"[$-53]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u1798\\u1780\\u179A\\u17B6 01 2022  4:32 \\u1796\\u17D2\\u179A\\u17B9\\u1780\"},\n\t\t{\"43543.503206018519\", \"[$-53]mmm dd yyyy  h:mm AM/PM\", \"\\u17E3 19 2019  12:04 \\u179B\\u17D2\\u1784\\u17B6\\u1785\"},\n\t\t{\"43543.503206018519\", \"[$-53]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u1798\\u17B7\\u1793\\u17B6 19 2019  12:04 \\u179B\\u17D2\\u1784\\u17B6\\u1785 \\u17A2.\"},\n\t\t{\"43543.503206018519\", \"[$-53]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u1798 19 2019  12:04 \\u179B\\u17D2\\u1784\\u17B6\\u1785 \\u17A2.\"},\n\t\t{\"43543.503206018519\", \"[$-53]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u1798\\u17B7\\u1793\\u17B6 19 2019  12:04 \\u179B\\u17D2\\u1784\\u17B6\\u1785 \\u1790\\u17D2\\u1784\\u17C3\\u17A2\\u1784\\u17D2\\u1782\\u17B6\\u179A\"},\n\t\t{\"44562.189571759256\", \"[$-453]mmm dd yyyy  h:mm AM/PM\", \"\\u17E1 01 2022  4:32 \\u1796\\u17D2\\u179A\\u17B9\\u1780\"},\n\t\t{\"44562.189571759256\", \"[$-453]mmmm dd yyyy  h:mm AM/PM\", \"\\u1798\\u1780\\u179A\\u17B6 01 2022  4:32 \\u1796\\u17D2\\u179A\\u17B9\\u1780\"},\n\t\t{\"44562.189571759256\", \"[$-453]mmmmm dd yyyy  h:mm AM/PM\", \"\\u1798 01 2022  4:32 \\u1796\\u17D2\\u179A\\u17B9\\u1780\"},\n\t\t{\"44562.189571759256\", \"[$-453]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u1798\\u1780\\u179A\\u17B6 01 2022  4:32 \\u1796\\u17D2\\u179A\\u17B9\\u1780\"},\n\t\t{\"43543.503206018519\", \"[$-453]mmm dd yyyy  h:mm AM/PM\", \"\\u17E3 19 2019  12:04 \\u179B\\u17D2\\u1784\\u17B6\\u1785\"},\n\t\t{\"43543.503206018519\", \"[$-453]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u1798\\u17B7\\u1793\\u17B6 19 2019  12:04 \\u179B\\u17D2\\u1784\\u17B6\\u1785 \\u17A2.\"},\n\t\t{\"43543.503206018519\", \"[$-453]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u1798 19 2019  12:04 \\u179B\\u17D2\\u1784\\u17B6\\u1785 \\u17A2.\"},\n\t\t{\"43543.503206018519\", \"[$-453]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u1798\\u17B7\\u1793\\u17B6 19 2019  12:04 \\u179B\\u17D2\\u1784\\u17B6\\u1785 \\u1790\\u17D2\\u1784\\u17C3\\u17A2\\u1784\\u17D2\\u1782\\u17B6\\u179A\"},\n\t\t{\"44562.189571759256\", \"[$-86]mmm dd yyyy  h:mm AM/PM\", \"nab'e 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-86]mmmm dd yyyy  h:mm AM/PM\", \"nab'e ik' 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-86]mmmmm dd yyyy  h:mm AM/PM\", \"n 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-86]mmmmmm dd yyyy  h:mm AM/PM\", \"nab'e ik' 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-86]mmm dd yyyy  h:mm AM/PM\", \"urox 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-86]mmmm dd yyyy  h:mm AM/PM aaa\", \"urox ik' 19 2019  12:04 p.m. oxq'\"},\n\t\t{\"43543.503206018519\", \"[$-86]mmmmm dd yyyy  h:mm AM/PM ddd\", \"u 19 2019  12:04 p.m. oxq'\"},\n\t\t{\"43543.503206018519\", \"[$-86]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"urox ik' 19 2019  12:04 p.m. oxq'ij\"},\n\t\t{\"44562.189571759256\", \"[$-486]mmm dd yyyy  h:mm AM/PM\", \"nab'e 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-486]mmmm dd yyyy  h:mm AM/PM\", \"nab'e ik' 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-486]mmmmm dd yyyy  h:mm AM/PM\", \"n 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-486]mmmmmm dd yyyy  h:mm AM/PM\", \"nab'e ik' 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-486]mmm dd yyyy  h:mm AM/PM\", \"urox 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-486]mmmm dd yyyy  h:mm AM/PM aaa\", \"urox ik' 19 2019  12:04 p.m. oxq'\"},\n\t\t{\"43543.503206018519\", \"[$-486]mmmmm dd yyyy  h:mm AM/PM ddd\", \"u 19 2019  12:04 p.m. oxq'\"},\n\t\t{\"43543.503206018519\", \"[$-486]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"urox ik' 19 2019  12:04 p.m. oxq'ij\"},\n\t\t{\"44562.189571759256\", \"[$-87]mmm dd yyyy  h:mm AM/PM\", \"mut. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-87]mmmm dd yyyy  h:mm AM/PM\", \"Mutarama 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-87]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-87]mmmmmm dd yyyy  h:mm AM/PM\", \"Mutarama 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-87]mmm dd yyyy  h:mm AM/PM\", \"wer. 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-87]mmmm dd yyyy  h:mm AM/PM aaa\", \"Werurwe 19 2019  12:04 PM kab.\"},\n\t\t{\"43543.503206018519\", \"[$-87]mmmmm dd yyyy  h:mm AM/PM ddd\", \"W 19 2019  12:04 PM kab.\"},\n\t\t{\"43543.503206018519\", \"[$-87]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Werurwe 19 2019  12:04 PM Ku wa kabiri\"},\n\t\t{\"44562.189571759256\", \"[$-487]mmm dd yyyy  h:mm AM/PM\", \"mut. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-487]mmmm dd yyyy  h:mm AM/PM\", \"Mutarama 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-487]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-487]mmmmmm dd yyyy  h:mm AM/PM\", \"Mutarama 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-487]mmm dd yyyy  h:mm AM/PM\", \"wer. 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-487]mmmm dd yyyy  h:mm AM/PM aaa\", \"Werurwe 19 2019  12:04 PM kab.\"},\n\t\t{\"43543.503206018519\", \"[$-487]mmmmm dd yyyy  h:mm AM/PM ddd\", \"W 19 2019  12:04 PM kab.\"},\n\t\t{\"43543.503206018519\", \"[$-487]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Werurwe 19 2019  12:04 PM Ku wa kabiri\"},\n\t\t{\"44562.189571759256\", \"[$-41]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41]mmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41]mmmmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-41]mmm dd yyyy  h:mm AM/PM\", \"Mac 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-41]mmmm dd yyyy  h:mm AM/PM aaa\", \"Machi 19 2019  12:04 PM Jnn\"},\n\t\t{\"43543.503206018519\", \"[$-41]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Jnn\"},\n\t\t{\"43543.503206018519\", \"[$-41]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Machi 19 2019  12:04 PM Jumanne\"},\n\t\t{\"44562.189571759256\", \"[$-441]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-441]mmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-441]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-441]mmmmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-441]mmm dd yyyy  h:mm AM/PM\", \"Mac 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-441]mmmm dd yyyy  h:mm AM/PM aaa\", \"Machi 19 2019  12:04 PM Jnn\"},\n\t\t{\"43543.503206018519\", \"[$-441]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Jnn\"},\n\t\t{\"43543.503206018519\", \"[$-441]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Machi 19 2019  12:04 PM Jumanne\"},\n\t\t{\"44562.189571759256\", \"[$-57]mmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u0947 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"44562.189571759256\", \"[$-57]mmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u0947\\u0935\\u093E\\u0930\\u0940 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"44562.189571759256\", \"[$-57]mmmmm dd yyyy  h:mm AM/PM\", \"\\u091C 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"44562.189571759256\", \"[$-57]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u0947\\u0935\\u093E\\u0930\\u0940 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"43543.503206018519\", \"[$-57]mmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902.\"},\n\t\t{\"43543.503206018519\", \"[$-57]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902. \\u092E\\u0902\\u0917\\u0933.\"},\n\t\t{\"43543.503206018519\", \"[$-57]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u092E 19 2019  12:04 \\u092E.\\u0928\\u0902. \\u092E\\u0902\\u0917\\u0933.\"},\n\t\t{\"43543.503206018519\", \"[$-57]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902. \\u092E\\u0902\\u0917\\u0933\\u093E\\u0930\"},\n\t\t{\"44562.189571759256\", \"[$-457]mmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u0947 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"44562.189571759256\", \"[$-457]mmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u0947\\u0935\\u093E\\u0930\\u0940 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"44562.189571759256\", \"[$-457]mmmmm dd yyyy  h:mm AM/PM\", \"\\u091C 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"44562.189571759256\", \"[$-457]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u0947\\u0935\\u093E\\u0930\\u0940 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"43543.503206018519\", \"[$-457]mmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902.\"},\n\t\t{\"43543.503206018519\", \"[$-457]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902. \\u092E\\u0902\\u0917\\u0933.\"},\n\t\t{\"43543.503206018519\", \"[$-457]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u092E 19 2019  12:04 \\u092E.\\u0928\\u0902. \\u092E\\u0902\\u0917\\u0933.\"},\n\t\t{\"43543.503206018519\", \"[$-457]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902. \\u092E\\u0902\\u0917\\u0933\\u093E\\u0930\"},\n\t\t{\"43543.503206018519\", \"[$-12]mmm dd yyyy  h:mm AM/PM aaa\", \"3 19 2019  12:04 오후 화\"},\n\t\t{\"43543.503206018519\", \"[$-12]mmmm dd yyyy  h:mm AM/PM ddd\", \"3월 19 2019  12:04 오후 화\"},\n\t\t{\"43543.503206018519\", \"[$-12]mmmmm dd yyyy  h:mm AM/PM dddd\", \"3 19 2019  12:04 오후 화요일\"},\n\t\t{\"43543.503206018519\", \"[$-412]mmm dd yyyy  h:mm AM/PM aaa\", \"3 19 2019  12:04 오후 화\"},\n\t\t{\"43543.503206018519\", \"[$-412]mmmm dd yyyy  h:mm AM/PM ddd\", \"3월 19 2019  12:04 오후 화\"},\n\t\t{\"43543.503206018519\", \"[$-412]mmmmm dd yyyy  h:mm AM/PM dddd\", \"3 19 2019  12:04 오후 화요일\"},\n\t\t{\"44562.189571759256\", \"[$-40]mmm dd yyyy  h:mm AM/PM\", \"\\u042F\\u043D\\u0432 01 2022  4:32 \\u0442\\u04A3\"},\n\t\t{\"44562.189571759256\", \"[$-40]mmmm dd yyyy  h:mm AM/PM\", \"\\u042F\\u043D\\u0432\\u0430\\u0440\\u044C 01 2022  4:32 \\u0442\\u04A3\"},\n\t\t{\"44562.189571759256\", \"[$-40]mmmmm dd yyyy  h:mm AM/PM\", \"\\u042F 01 2022  4:32 \\u0442\\u04A3\"},\n\t\t{\"44562.189571759256\", \"[$-40]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u042F\\u043D\\u0432\\u0430\\u0440\\u044C 01 2022  4:32 \\u0442\\u04A3\"},\n\t\t{\"43543.503206018519\", \"[$-40]mmm dd yyyy  h:mm AM/PM\", \"\\u041C\\u0430\\u0440 19 2019  12:04 \\u0442\\u043A\"},\n\t\t{\"43543.503206018519\", \"[$-40]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u041C\\u0430\\u0440\\u0442 19 2019  12:04 \\u0442\\u043A \\u0448\\u0435\\u0439\\u0448.\"},\n\t\t{\"43543.503206018519\", \"[$-40]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u041C 19 2019  12:04 \\u0442\\u043A \\u0448\\u0435\\u0439\\u0448.\"},\n\t\t{\"43543.503206018519\", \"[$-40]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u041C\\u0430\\u0440\\u0442 19 2019  12:04 \\u0442\\u043A \\u0448\\u0435\\u0439\\u0448\\u0435\\u043C\\u0431\\u0438\"},\n\t\t{\"44562.189571759256\", \"[$-440]mmm dd yyyy  h:mm AM/PM\", \"\\u042F\\u043D\\u0432 01 2022  4:32 \\u0442\\u04A3\"},\n\t\t{\"44562.189571759256\", \"[$-440]mmmm dd yyyy  h:mm AM/PM\", \"\\u042F\\u043D\\u0432\\u0430\\u0440\\u044C 01 2022  4:32 \\u0442\\u04A3\"},\n\t\t{\"44562.189571759256\", \"[$-440]mmmmm dd yyyy  h:mm AM/PM\", \"\\u042F 01 2022  4:32 \\u0442\\u04A3\"},\n\t\t{\"44562.189571759256\", \"[$-440]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u042F\\u043D\\u0432\\u0430\\u0440\\u044C 01 2022  4:32 \\u0442\\u04A3\"},\n\t\t{\"43543.503206018519\", \"[$-440]mmm dd yyyy  h:mm AM/PM\", \"\\u041C\\u0430\\u0440 19 2019  12:04 \\u0442\\u043A\"},\n\t\t{\"43543.503206018519\", \"[$-440]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u041C\\u0430\\u0440\\u0442 19 2019  12:04 \\u0442\\u043A \\u0448\\u0435\\u0439\\u0448.\"},\n\t\t{\"43543.503206018519\", \"[$-440]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u041C 19 2019  12:04 \\u0442\\u043A \\u0448\\u0435\\u0439\\u0448.\"},\n\t\t{\"43543.503206018519\", \"[$-440]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u041C\\u0430\\u0440\\u0442 19 2019  12:04 \\u0442\\u043A \\u0448\\u0435\\u0439\\u0448\\u0435\\u043C\\u0431\\u0438\"},\n\t\t{\"44562.189571759256\", \"[$-54]mmm dd yyyy  h:mm AM/PM\", \"\\u0EA1.\\u0E81. 01 2022  4:32 \\u0E81\\u0EC8\\u0EAD\\u0E99\\u0E97\\u0EC8\\u0EBD\\u0E87\"},\n\t\t{\"44562.189571759256\", \"[$-54]mmmm dd yyyy  h:mm AM/PM\", \"\\u0EA1\\u0EB1\\u0E87\\u0E81\\u0EAD\\u0E99 01 2022  4:32 \\u0E81\\u0EC8\\u0EAD\\u0E99\\u0E97\\u0EC8\\u0EBD\\u0E87\"},\n\t\t{\"44562.189571759256\", \"[$-54]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0EA1 01 2022  4:32 \\u0E81\\u0EC8\\u0EAD\\u0E99\\u0E97\\u0EC8\\u0EBD\\u0E87\"},\n\t\t{\"44562.189571759256\", \"[$-54]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0EA1\\u0EB1\\u0E87\\u0E81\\u0EAD\\u0E99 01 2022  4:32 \\u0E81\\u0EC8\\u0EAD\\u0E99\\u0E97\\u0EC8\\u0EBD\\u0E87\"},\n\t\t{\"43543.503206018519\", \"[$-54]mmm dd yyyy  h:mm AM/PM\", \"\\u0EA1.\\u0E99. 19 2019  12:04 \\u0EAB\\u0EBC\\u0EB1\\u0E87\\u0E97\\u0EC8\\u0EBD\\u0E87\"},\n\t\t{\"43543.503206018519\", \"[$-54]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0EA1\\u0EB5\\u0E99\\u0EB2 19 2019  12:04 \\u0EAB\\u0EBC\\u0EB1\\u0E87\\u0E97\\u0EC8\\u0EBD\\u0E87 \\u0EAD\\u0EB1\\u0E87\\u0E84\\u0EB2\\u0E99\"},\n\t\t{\"43543.503206018519\", \"[$-54]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0EA1 19 2019  12:04 \\u0EAB\\u0EBC\\u0EB1\\u0E87\\u0E97\\u0EC8\\u0EBD\\u0E87 \\u0EAD\\u0EB1\\u0E87\\u0E84\\u0EB2\\u0E99\"},\n\t\t{\"43543.503206018519\", \"[$-54]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0EA1\\u0EB5\\u0E99\\u0EB2 19 2019  12:04 \\u0EAB\\u0EBC\\u0EB1\\u0E87\\u0E97\\u0EC8\\u0EBD\\u0E87 \\u0EA7\\u0EB1\\u0E99\\u0EAD\\u0EB1\\u0E87\\u0E84\\u0EB2\\u0E99\"},\n\t\t{\"44562.189571759256\", \"[$-454]mmm dd yyyy  h:mm AM/PM\", \"\\u0EA1.\\u0E81. 01 2022  4:32 \\u0E81\\u0EC8\\u0EAD\\u0E99\\u0E97\\u0EC8\\u0EBD\\u0E87\"},\n\t\t{\"44562.189571759256\", \"[$-454]mmmm dd yyyy  h:mm AM/PM\", \"\\u0EA1\\u0EB1\\u0E87\\u0E81\\u0EAD\\u0E99 01 2022  4:32 \\u0E81\\u0EC8\\u0EAD\\u0E99\\u0E97\\u0EC8\\u0EBD\\u0E87\"},\n\t\t{\"44562.189571759256\", \"[$-454]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0EA1 01 2022  4:32 \\u0E81\\u0EC8\\u0EAD\\u0E99\\u0E97\\u0EC8\\u0EBD\\u0E87\"},\n\t\t{\"44562.189571759256\", \"[$-454]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0EA1\\u0EB1\\u0E87\\u0E81\\u0EAD\\u0E99 01 2022  4:32 \\u0E81\\u0EC8\\u0EAD\\u0E99\\u0E97\\u0EC8\\u0EBD\\u0E87\"},\n\t\t{\"43543.503206018519\", \"[$-454]mmm dd yyyy  h:mm AM/PM\", \"\\u0EA1.\\u0E99. 19 2019  12:04 \\u0EAB\\u0EBC\\u0EB1\\u0E87\\u0E97\\u0EC8\\u0EBD\\u0E87\"},\n\t\t{\"43543.503206018519\", \"[$-454]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0EA1\\u0EB5\\u0E99\\u0EB2 19 2019  12:04 \\u0EAB\\u0EBC\\u0EB1\\u0E87\\u0E97\\u0EC8\\u0EBD\\u0E87 \\u0EAD\\u0EB1\\u0E87\\u0E84\\u0EB2\\u0E99\"},\n\t\t{\"43543.503206018519\", \"[$-454]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0EA1 19 2019  12:04 \\u0EAB\\u0EBC\\u0EB1\\u0E87\\u0E97\\u0EC8\\u0EBD\\u0E87 \\u0EAD\\u0EB1\\u0E87\\u0E84\\u0EB2\\u0E99\"},\n\t\t{\"43543.503206018519\", \"[$-454]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0EA1\\u0EB5\\u0E99\\u0EB2 19 2019  12:04 \\u0EAB\\u0EBC\\u0EB1\\u0E87\\u0E97\\u0EC8\\u0EBD\\u0E87 \\u0EA7\\u0EB1\\u0E99\\u0EAD\\u0EB1\\u0E87\\u0E84\\u0EB2\\u0E99\"},\n\t\t{\"44562.189571759256\", \"[$-476]mmm dd yyyy  h:mm AM/PM\", \"Ian 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-476]mmmm dd yyyy  h:mm AM/PM\", \"Ianuarius 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-476]mmmmm dd yyyy  h:mm AM/PM\", \"I 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-476]mmmmmm dd yyyy  h:mm AM/PM\", \"Ianuarius 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-476]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-476]mmmm dd yyyy  h:mm AM/PM aaa\", \"Martius 19 2019  12:04 PM Mar\"},\n\t\t{\"43543.503206018519\", \"[$-476]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Mar\"},\n\t\t{\"43543.503206018519\", \"[$-476]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Martius 19 2019  12:04 PM Martis\"},\n\t\t{\"44562.189571759256\", \"[$-26]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 priekšp.\"},\n\t\t{\"44562.189571759256\", \"[$-26]mmmm dd yyyy  h:mm AM/PM\", \"janvāris 01 2022  4:32 priekšp.\"},\n\t\t{\"44562.189571759256\", \"[$-26]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 priekšp.\"},\n\t\t{\"44562.189571759256\", \"[$-26]mmmmmm dd yyyy  h:mm AM/PM\", \"janvāris 01 2022  4:32 priekšp.\"},\n\t\t{\"43543.503206018519\", \"[$-26]mmm dd yyyy  h:mm AM/PM\", \"marts 19 2019  12:04 pēcp.\"},\n\t\t{\"43543.503206018519\", \"[$-26]mmmm dd yyyy  h:mm AM/PM aaa\", \"marts 19 2019  12:04 pēcp. otrd.\"},\n\t\t{\"43543.503206018519\", \"[$-26]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 pēcp. otrd.\"},\n\t\t{\"43543.503206018519\", \"[$-26]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"marts 19 2019  12:04 pēcp. otrdiena\"},\n\t\t{\"44562.189571759256\", \"[$-426]mmm dd yyyy  h:mm AM/PM\", \"janv. 01 2022  4:32 priekšp.\"},\n\t\t{\"44562.189571759256\", \"[$-426]mmmm dd yyyy  h:mm AM/PM\", \"janvāris 01 2022  4:32 priekšp.\"},\n\t\t{\"44562.189571759256\", \"[$-426]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 priekšp.\"},\n\t\t{\"44562.189571759256\", \"[$-426]mmmmmm dd yyyy  h:mm AM/PM\", \"janvāris 01 2022  4:32 priekšp.\"},\n\t\t{\"43543.503206018519\", \"[$-426]mmm dd yyyy  h:mm AM/PM\", \"marts 19 2019  12:04 pēcp.\"},\n\t\t{\"43543.503206018519\", \"[$-426]mmmm dd yyyy  h:mm AM/PM aaa\", \"marts 19 2019  12:04 pēcp. otrd.\"},\n\t\t{\"43543.503206018519\", \"[$-426]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 pēcp. otrd.\"},\n\t\t{\"43543.503206018519\", \"[$-426]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"marts 19 2019  12:04 pēcp. otrdiena\"},\n\t\t{\"44562.189571759256\", \"[$-27]mmm dd yyyy  h:mm AM/PM\", \"saus. 01 2022  4:32 priešpiet\"},\n\t\t{\"44562.189571759256\", \"[$-27]mmmm dd yyyy  h:mm AM/PM\", \"sausis 01 2022  4:32 priešpiet\"},\n\t\t{\"44562.189571759256\", \"[$-27]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 priešpiet\"},\n\t\t{\"44562.189571759256\", \"[$-27]mmmmmm dd yyyy  h:mm AM/PM\", \"sausis 01 2022  4:32 priešpiet\"},\n\t\t{\"43543.503206018519\", \"[$-27]mmm dd yyyy  h:mm AM/PM\", \"kov. 19 2019  12:04 popiet\"},\n\t\t{\"43543.503206018519\", \"[$-27]mmmm dd yyyy  h:mm AM/PM aaa\", \"kovas 19 2019  12:04 popiet an\"},\n\t\t{\"43543.503206018519\", \"[$-27]mmmmm dd yyyy  h:mm AM/PM ddd\", \"k 19 2019  12:04 popiet an\"},\n\t\t{\"43543.503206018519\", \"[$-27]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"kovas 19 2019  12:04 popiet antradienis\"},\n\t\t{\"44562.189571759256\", \"[$-427]mmm dd yyyy  h:mm AM/PM\", \"saus. 01 2022  4:32 priešpiet\"},\n\t\t{\"44562.189571759256\", \"[$-427]mmmm dd yyyy  h:mm AM/PM\", \"sausis 01 2022  4:32 priešpiet\"},\n\t\t{\"44562.189571759256\", \"[$-427]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 priešpiet\"},\n\t\t{\"44562.189571759256\", \"[$-427]mmmmmm dd yyyy  h:mm AM/PM\", \"sausis 01 2022  4:32 priešpiet\"},\n\t\t{\"43543.503206018519\", \"[$-427]mmm dd yyyy  h:mm AM/PM\", \"kov. 19 2019  12:04 popiet\"},\n\t\t{\"43543.503206018519\", \"[$-427]mmmm dd yyyy  h:mm AM/PM aaa\", \"kovas 19 2019  12:04 popiet an\"},\n\t\t{\"43543.503206018519\", \"[$-427]mmmmm dd yyyy  h:mm AM/PM ddd\", \"k 19 2019  12:04 popiet an\"},\n\t\t{\"43543.503206018519\", \"[$-427]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"kovas 19 2019  12:04 popiet antradienis\"},\n\t\t{\"44562.189571759256\", \"[$-7C2E]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C2E]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C2E]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C2E]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-7C2E]mmm dd yyyy  h:mm AM/PM\", \"měr 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-7C2E]mmmm dd yyyy  h:mm AM/PM aaa\", \"měrc 19 2019  12:04 PM wa\\u0142\"},\n\t\t{\"43543.503206018519\", \"[$-7C2E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM wa\\u0142\"},\n\t\t{\"43543.503206018519\", \"[$-7C2E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"měrc 19 2019  12:04 PM wa\\u0142tora\"},\n\t\t{\"44562.189571759256\", \"[$-82E]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-82E]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-82E]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-82E]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-82E]mmm dd yyyy  h:mm AM/PM\", \"měr 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-82E]mmmm dd yyyy  h:mm AM/PM aaa\", \"měrc 19 2019  12:04 PM wa\\u0142\"},\n\t\t{\"43543.503206018519\", \"[$-82E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM wa\\u0142\"},\n\t\t{\"43543.503206018519\", \"[$-82E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"měrc 19 2019  12:04 PM wa\\u0142tora\"},\n\t\t{\"44562.189571759256\", \"[$-6E]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6E]mmmm dd yyyy  h:mm AM/PM\", \"Januar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6E]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6E]mmmmmm dd yyyy  h:mm AM/PM\", \"Januar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-6E]mmm dd yyyy  h:mm AM/PM\", \"Mäe 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-6E]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mäerz 19 2019  12:04 PM Dën\"},\n\t\t{\"43543.503206018519\", \"[$-6E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Dën\"},\n\t\t{\"43543.503206018519\", \"[$-6E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mäerz 19 2019  12:04 PM Dënschdeg\"},\n\t\t{\"44562.189571759256\", \"[$-46E]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-46E]mmmm dd yyyy  h:mm AM/PM\", \"Januar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-46E]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-46E]mmmmmm dd yyyy  h:mm AM/PM\", \"Januar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-46E]mmm dd yyyy  h:mm AM/PM\", \"Mäe 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-46E]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mäerz 19 2019  12:04 PM Dën\"},\n\t\t{\"43543.503206018519\", \"[$-46E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Dën\"},\n\t\t{\"43543.503206018519\", \"[$-46E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mäerz 19 2019  12:04 PM Dënschdeg\"},\n\t\t{\"44562.189571759256\", \"[$-2F]mmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D. 01 2022  4:32 \\u043F\\u0440\\u0435\\u0442\\u043F\\u043B.\"},\n\t\t{\"44562.189571759256\", \"[$-2F]mmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440\\u0438 01 2022  4:32 \\u043F\\u0440\\u0435\\u0442\\u043F\\u043B.\"},\n\t\t{\"44562.189571759256\", \"[$-2F]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0458 01 2022  4:32 \\u043F\\u0440\\u0435\\u0442\\u043F\\u043B.\"},\n\t\t{\"44562.189571759256\", \"[$-2F]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440\\u0438 01 2022  4:32 \\u043F\\u0440\\u0435\\u0442\\u043F\\u043B.\"},\n\t\t{\"43543.503206018519\", \"[$-2F]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440. 19 2019  12:04 \\u043F\\u043E\\u043F\\u043B.\"},\n\t\t{\"43543.503206018519\", \"[$-2F]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 \\u043F\\u043E\\u043F\\u043B. \\u0432\\u0442.\"},\n\t\t{\"43543.503206018519\", \"[$-2F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 \\u043F\\u043E\\u043F\\u043B. \\u0432\\u0442.\"},\n\t\t{\"43543.503206018519\", \"[$-2F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 \\u043F\\u043E\\u043F\\u043B. \\u0432\\u0442\\u043E\\u0440\\u043D\\u0438\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-42F]mmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D. 01 2022  4:32 \\u043F\\u0440\\u0435\\u0442\\u043F\\u043B.\"},\n\t\t{\"44562.189571759256\", \"[$-42F]mmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440\\u0438 01 2022  4:32 \\u043F\\u0440\\u0435\\u0442\\u043F\\u043B.\"},\n\t\t{\"44562.189571759256\", \"[$-42F]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0458 01 2022  4:32 \\u043F\\u0440\\u0435\\u0442\\u043F\\u043B.\"},\n\t\t{\"44562.189571759256\", \"[$-42F]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440\\u0438 01 2022  4:32 \\u043F\\u0440\\u0435\\u0442\\u043F\\u043B.\"},\n\t\t{\"43543.503206018519\", \"[$-42F]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440. 19 2019  12:04 \\u043F\\u043E\\u043F\\u043B.\"},\n\t\t{\"43543.503206018519\", \"[$-42F]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 \\u043F\\u043E\\u043F\\u043B. \\u0432\\u0442.\"},\n\t\t{\"43543.503206018519\", \"[$-42F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 \\u043F\\u043E\\u043F\\u043B. \\u0432\\u0442.\"},\n\t\t{\"43543.503206018519\", \"[$-42F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 \\u043F\\u043E\\u043F\\u043B. \\u0432\\u0442\\u043E\\u0440\\u043D\\u0438\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-3E]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 PG\"},\n\t\t{\"44562.189571759256\", \"[$-3E]mmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 PG\"},\n\t\t{\"44562.189571759256\", \"[$-3E]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 PG\"},\n\t\t{\"44562.189571759256\", \"[$-3E]mmmmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 PG\"},\n\t\t{\"43543.503206018519\", \"[$-3E]mmm dd yyyy  h:mm AM/PM\", \"Mac 19 2019  12:04 PTG\"},\n\t\t{\"43543.503206018519\", \"[$-3E]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mac 19 2019  12:04 PTG Sel\"},\n\t\t{\"43543.503206018519\", \"[$-3E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PTG Sel\"},\n\t\t{\"43543.503206018519\", \"[$-3E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mac 19 2019  12:04 PTG Selasa\"},\n\t\t{\"44562.189571759256\", \"[$-83E]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 PG\"},\n\t\t{\"44562.189571759256\", \"[$-83E]mmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 PG\"},\n\t\t{\"44562.189571759256\", \"[$-83E]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 PG\"},\n\t\t{\"44562.189571759256\", \"[$-83E]mmmmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 PG\"},\n\t\t{\"43543.503206018519\", \"[$-83E]mmm dd yyyy  h:mm AM/PM\", \"Mac 19 2019  12:04 PTG\"},\n\t\t{\"43543.503206018519\", \"[$-83E]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mac 19 2019  12:04 PTG Sel\"},\n\t\t{\"43543.503206018519\", \"[$-83E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PTG Sel\"},\n\t\t{\"43543.503206018519\", \"[$-83E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mac 19 2019  12:04 PTG Selasa\"},\n\t\t{\"44562.189571759256\", \"[$-43E]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 PG\"},\n\t\t{\"44562.189571759256\", \"[$-43E]mmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 PG\"},\n\t\t{\"44562.189571759256\", \"[$-43E]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 PG\"},\n\t\t{\"44562.189571759256\", \"[$-43E]mmmmmm dd yyyy  h:mm AM/PM\", \"Januari 01 2022  4:32 PG\"},\n\t\t{\"43543.503206018519\", \"[$-43E]mmm dd yyyy  h:mm AM/PM\", \"Mac 19 2019  12:04 PTG\"},\n\t\t{\"43543.503206018519\", \"[$-43E]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mac 19 2019  12:04 PTG Sel\"},\n\t\t{\"43543.503206018519\", \"[$-43E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PTG Sel\"},\n\t\t{\"43543.503206018519\", \"[$-43E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mac 19 2019  12:04 PTG Selasa\"},\n\t\t{\"44562.189571759256\", \"[$-4C]mmm dd yyyy  h:mm AM/PM\", \"\\u0D1C\\u0D28\\u0D41 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-4C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0D1C\\u0D28\\u0D41\\u0D35\\u0D30\\u0D3F 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-4C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0D1C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-4C]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0D1C\\u0D28\\u0D41\\u0D35\\u0D30\\u0D3F 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-4C]mmm dd yyyy  h:mm AM/PM\", \"\\u0D2E\\u0D3E\\u0D7C 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-4C]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0D2E\\u0D3E\\u0D30\\u0D4D\\u200D\\u200C\\u0D1A\\u0D4D\\u0D1A\\u0D4D 19 2019  12:04 PM \\u0D1A\\u0D4A\\u0D35\\u0D4D\\u0D35\"},\n\t\t{\"43543.503206018519\", \"[$-4C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0D2E 19 2019  12:04 PM \\u0D1A\\u0D4A\\u0D35\\u0D4D\\u0D35\"},\n\t\t{\"43543.503206018519\", \"[$-4C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0D2E\\u0D3E\\u0D30\\u0D4D\\u200D\\u200C\\u0D1A\\u0D4D\\u0D1A\\u0D4D 19 2019  12:04 PM \\u0D1A\\u0D4A\\u0D35\\u0D4D\\u0D35\\u0D3E\\u0D34\\u0D4D\\u0D1A\"},\n\t\t{\"44562.189571759256\", \"[$-44C]mmm dd yyyy  h:mm AM/PM\", \"\\u0D1C\\u0D28\\u0D41 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-44C]mmmm dd yyyy  h:mm AM/PM\", \"\\u0D1C\\u0D28\\u0D41\\u0D35\\u0D30\\u0D3F 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-44C]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0D1C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-44C]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0D1C\\u0D28\\u0D41\\u0D35\\u0D30\\u0D3F 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-44C]mmm dd yyyy  h:mm AM/PM\", \"\\u0D2E\\u0D3E\\u0D7C 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-44C]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0D2E\\u0D3E\\u0D30\\u0D4D\\u200D\\u200C\\u0D1A\\u0D4D\\u0D1A\\u0D4D 19 2019  12:04 PM \\u0D1A\\u0D4A\\u0D35\\u0D4D\\u0D35\"},\n\t\t{\"43543.503206018519\", \"[$-44C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0D2E 19 2019  12:04 PM \\u0D1A\\u0D4A\\u0D35\\u0D4D\\u0D35\"},\n\t\t{\"43543.503206018519\", \"[$-44C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0D2E\\u0D3E\\u0D30\\u0D4D\\u200D\\u200C\\u0D1A\\u0D4D\\u0D1A\\u0D4D 19 2019  12:04 PM \\u0D1A\\u0D4A\\u0D35\\u0D4D\\u0D35\\u0D3E\\u0D34\\u0D4D\\u0D1A\"},\n\t\t{\"44562.189571759256\", \"[$-3A]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-3A]mmmm dd yyyy  h:mm AM/PM\", \"Jannar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-3A]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-3A]mmmmmm dd yyyy  h:mm AM/PM\", \"Jannar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-3A]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-3A]mmmm dd yyyy  h:mm AM/PM aaa\", \"Marzu 19 2019  12:04 PM Tli\"},\n\t\t{\"43543.503206018519\", \"[$-3A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Tli\"},\n\t\t{\"43543.503206018519\", \"[$-3A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Marzu 19 2019  12:04 PM It-Tlieta\"},\n\t\t{\"44562.189571759256\", \"[$-43A]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-43A]mmmm dd yyyy  h:mm AM/PM\", \"Jannar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-43A]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-43A]mmmmmm dd yyyy  h:mm AM/PM\", \"Jannar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-43A]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-43A]mmmm dd yyyy  h:mm AM/PM aaa\", \"Marzu 19 2019  12:04 PM Tli\"},\n\t\t{\"43543.503206018519\", \"[$-43A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Tli\"},\n\t\t{\"43543.503206018519\", \"[$-43A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Marzu 19 2019  12:04 PM It-Tlieta\"},\n\t\t{\"44562.189571759256\", \"[$-81]mmm dd yyyy  h:mm AM/PM\", \"Kohi 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-81]mmmm dd yyyy  h:mm AM/PM\", \"Kohitātea 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-81]mmmmm dd yyyy  h:mm AM/PM\", \"K 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-81]mmmmmm dd yyyy  h:mm AM/PM\", \"Kohitātea 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-81]mmm dd yyyy  h:mm AM/PM\", \"Pou 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-81]mmmm dd yyyy  h:mm AM/PM aaa\", \"Poutūterangi 19 2019  12:04 p.m. Tū\"},\n\t\t{\"43543.503206018519\", \"[$-81]mmmmm dd yyyy  h:mm AM/PM ddd\", \"P 19 2019  12:04 p.m. Tū\"},\n\t\t{\"43543.503206018519\", \"[$-81]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Poutūterangi 19 2019  12:04 p.m. Rātū\"},\n\t\t{\"44562.189571759256\", \"[$-481]mmm dd yyyy  h:mm AM/PM\", \"Kohi 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-481]mmmm dd yyyy  h:mm AM/PM\", \"Kohitātea 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-481]mmmmm dd yyyy  h:mm AM/PM\", \"K 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-481]mmmmmm dd yyyy  h:mm AM/PM\", \"Kohitātea 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-481]mmm dd yyyy  h:mm AM/PM\", \"Pou 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-481]mmmm dd yyyy  h:mm AM/PM aaa\", \"Poutūterangi 19 2019  12:04 p.m. Tū\"},\n\t\t{\"43543.503206018519\", \"[$-481]mmmmm dd yyyy  h:mm AM/PM ddd\", \"P 19 2019  12:04 p.m. Tū\"},\n\t\t{\"43543.503206018519\", \"[$-481]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Poutūterangi 19 2019  12:04 p.m. Rātū\"},\n\t\t{\"44562.189571759256\", \"[$-7A]mmm dd yyyy  h:mm AM/PM\", \"Kiñe Tripantu 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7A]mmmm dd yyyy  h:mm AM/PM\", \"Kiñe Tripantu 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7A]mmmmm dd yyyy  h:mm AM/PM\", \"K 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7A]mmmmmm dd yyyy  h:mm AM/PM\", \"Kiñe Tripantu 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-7A]mmm dd yyyy  h:mm AM/PM\", \"Kila 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-7A]mmmm dd yyyy  h:mm AM/PM aaa\", \"Kila 19 2019  12:04 PM Kila\"},\n\t\t{\"43543.503206018519\", \"[$-7A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"K 19 2019  12:04 PM Kila\"},\n\t\t{\"43543.503206018519\", \"[$-7A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Kila 19 2019  12:04 PM Kila Ante\"},\n\t\t{\"44562.189571759256\", \"[$-47A]mmm dd yyyy  h:mm AM/PM\", \"Kiñe Tripantu 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-47A]mmmm dd yyyy  h:mm AM/PM\", \"Kiñe Tripantu 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-47A]mmmmm dd yyyy  h:mm AM/PM\", \"K 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-47A]mmmmmm dd yyyy  h:mm AM/PM\", \"Kiñe Tripantu 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-47A]mmm dd yyyy  h:mm AM/PM\", \"Kila 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-47A]mmmm dd yyyy  h:mm AM/PM aaa\", \"Kila 19 2019  12:04 PM Kila\"},\n\t\t{\"43543.503206018519\", \"[$-47A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"K 19 2019  12:04 PM Kila\"},\n\t\t{\"43543.503206018519\", \"[$-47A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Kila 19 2019  12:04 PM Kila Ante\"},\n\t\t{\"44562.189571759256\", \"[$-4E]mmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u0947. 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"44562.189571759256\", \"[$-4E]mmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u0947\\u0935\\u093E\\u0930\\u0940 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"44562.189571759256\", \"[$-4E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u091C 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"44562.189571759256\", \"[$-4E]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u0947\\u0935\\u093E\\u0930\\u0940 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"43543.503206018519\", \"[$-4E]mmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902.\"},\n\t\t{\"43543.503206018519\", \"[$-4E]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902. \\u092E\\u0902\\u0917\\u0933.\"},\n\t\t{\"43543.503206018519\", \"[$-4E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u092E 19 2019  12:04 \\u092E.\\u0928\\u0902. \\u092E\\u0902\\u0917\\u0933.\"},\n\t\t{\"43543.503206018519\", \"[$-4E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902. \\u092E\\u0902\\u0917\\u0933\\u0935\\u093E\\u0930\"},\n\t\t{\"44562.189571759256\", \"[$-44E]mmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u0947. 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"44562.189571759256\", \"[$-44E]mmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u0947\\u0935\\u093E\\u0930\\u0940 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"44562.189571759256\", \"[$-44E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u091C 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"44562.189571759256\", \"[$-44E]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u0947\\u0935\\u093E\\u0930\\u0940 01 2022  4:32 \\u092E.\\u092A\\u0942.\"},\n\t\t{\"43543.503206018519\", \"[$-44E]mmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902.\"},\n\t\t{\"43543.503206018519\", \"[$-44E]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902. \\u092E\\u0902\\u0917\\u0933.\"},\n\t\t{\"43543.503206018519\", \"[$-44E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u092E 19 2019  12:04 \\u092E.\\u0928\\u0902. \\u092E\\u0902\\u0917\\u0933.\"},\n\t\t{\"43543.503206018519\", \"[$-44E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902. \\u092E\\u0902\\u0917\\u0933\\u0935\\u093E\\u0930\"},\n\t\t{\"43543.503206018519\", \"[$-44E]mmmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902.\"},\n\t\t{\"43543.503206018519\", \"[$-44E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u092E 19 2019  12:04 \\u092E.\\u0928\\u0902.\"},\n\t\t{\"43543.503206018519\", \"[$-44E]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E.\\u0928\\u0902.\"},\n\t\t{\"44562.189571759256\", \"[$-7C]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C]mmmm dd yyyy  h:mm AM/PM\", \"Tsothohrkó:Wa 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C]mmmmm dd yyyy  h:mm AM/PM\", \"T 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C]mmmmmm dd yyyy  h:mm AM/PM\", \"Tsothohrkó:Wa 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-7C]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-7C]mmmm dd yyyy  h:mm AM/PM aaa\", \"Enniskó:Wa 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-7C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"E 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-7C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Enniskó:Wa 19 2019  12:04 PM Ratironhia'kehronòn:ke\"},\n\t\t{\"44562.189571759256\", \"[$-47C]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-47C]mmmm dd yyyy  h:mm AM/PM\", \"Tsothohrkó:Wa 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-47C]mmmmm dd yyyy  h:mm AM/PM\", \"T 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-47C]mmmmmm dd yyyy  h:mm AM/PM\", \"Tsothohrkó:Wa 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-47C]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-47C]mmmm dd yyyy  h:mm AM/PM aaa\", \"Enniskó:Wa 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-47C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"E 19 2019  12:04 PM Tue\"},\n\t\t{\"43543.503206018519\", \"[$-47C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Enniskó:Wa 19 2019  12:04 PM Ratironhia'kehronòn:ke\"},\n\t\t{\"44562.189571759256\", \"[$-50]mmm dd yyyy  h:mm AM/PM\", \"1-р сар 01 2022  4:32 \\u04AF.\\u04E9.\"},\n\t\t{\"44562.189571759256\", \"[$-50]mmmm dd yyyy  h:mm AM/PM\", \"\\u041D\\u044D\\u0433\\u0434\\u04AF\\u0433\\u044D\\u044D\\u0440 \\u0441\\u0430\\u0440 01 2022  4:32 \\u04AF.\\u04E9.\"},\n\t\t{\"44562.189571759256\", \"[$-50]mmmmm dd yyyy  h:mm AM/PM\", \"\\u041D 01 2022  4:32 \\u04AF.\\u04E9.\"},\n\t\t{\"44562.189571759256\", \"[$-50]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u041D\\u044D\\u0433\\u0434\\u04AF\\u0433\\u044D\\u044D\\u0440 \\u0441\\u0430\\u0440 01 2022  4:32 \\u04AF.\\u04E9.\"},\n\t\t{\"43543.503206018519\", \"[$-50]mmm dd yyyy  h:mm AM/PM\", \"3-р сар 19 2019  12:04 \\u04AF.\\u0445.\"},\n\t\t{\"43543.503206018519\", \"[$-50]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0413\\u0443\\u0440\\u0430\\u0432\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440 19 2019  12:04 \\u04AF.\\u0445. \\u041C\\u044F\"},\n\t\t{\"43543.503206018519\", \"[$-50]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0413 19 2019  12:04 \\u04AF.\\u0445. \\u041C\\u044F\"},\n\t\t{\"43543.503206018519\", \"[$-50]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0413\\u0443\\u0440\\u0430\\u0432\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440 19 2019  12:04 \\u04AF.\\u0445. \\u043C\\u044F\\u0433\\u043C\\u0430\\u0440\"},\n\t\t{\"44562.189571759256\", \"[$-7850]mmm dd yyyy  h:mm AM/PM\", \"1-р сар 01 2022  4:32 \\u04AF.\\u04E9.\"},\n\t\t{\"44562.189571759256\", \"[$-7850]mmmm dd yyyy  h:mm AM/PM\", \"\\u041D\\u044D\\u0433\\u0434\\u04AF\\u0433\\u044D\\u044D\\u0440 \\u0441\\u0430\\u0440 01 2022  4:32 \\u04AF.\\u04E9.\"},\n\t\t{\"44562.189571759256\", \"[$-7850]mmmmm dd yyyy  h:mm AM/PM\", \"\\u041D 01 2022  4:32 \\u04AF.\\u04E9.\"},\n\t\t{\"44562.189571759256\", \"[$-7850]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u041D\\u044D\\u0433\\u0434\\u04AF\\u0433\\u044D\\u044D\\u0440 \\u0441\\u0430\\u0440 01 2022  4:32 \\u04AF.\\u04E9.\"},\n\t\t{\"43543.503206018519\", \"[$-7850]mmm dd yyyy  h:mm AM/PM\", \"3-р сар 19 2019  12:04 \\u04AF.\\u0445.\"},\n\t\t{\"43543.503206018519\", \"[$-7850]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0413\\u0443\\u0440\\u0430\\u0432\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440 19 2019  12:04 \\u04AF.\\u0445. \\u041C\\u044F\"},\n\t\t{\"43543.503206018519\", \"[$-7850]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0413 19 2019  12:04 \\u04AF.\\u0445. \\u041C\\u044F\"},\n\t\t{\"43543.503206018519\", \"[$-7850]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0413\\u0443\\u0440\\u0430\\u0432\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440 19 2019  12:04 \\u04AF.\\u0445. \\u043C\\u044F\\u0433\\u043C\\u0430\\u0440\"},\n\t\t{\"44562.189571759256\", \"[$-450]mmm dd yyyy  h:mm AM/PM\", \"1-р сар 01 2022  4:32 \\u04AF.\\u04E9.\"},\n\t\t{\"44562.189571759256\", \"[$-450]mmmm dd yyyy  h:mm AM/PM\", \"\\u041D\\u044D\\u0433\\u0434\\u04AF\\u0433\\u044D\\u044D\\u0440 \\u0441\\u0430\\u0440 01 2022  4:32 \\u04AF.\\u04E9.\"},\n\t\t{\"44562.189571759256\", \"[$-450]mmmmm dd yyyy  h:mm AM/PM\", \"\\u041D 01 2022  4:32 \\u04AF.\\u04E9.\"},\n\t\t{\"44562.189571759256\", \"[$-450]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u041D\\u044D\\u0433\\u0434\\u04AF\\u0433\\u044D\\u044D\\u0440 \\u0441\\u0430\\u0440 01 2022  4:32 \\u04AF.\\u04E9.\"},\n\t\t{\"43543.503206018519\", \"[$-450]mmm dd yyyy  h:mm AM/PM\", \"3-р сар 19 2019  12:04 \\u04AF.\\u0445.\"},\n\t\t{\"43543.503206018519\", \"[$-450]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0413\\u0443\\u0440\\u0430\\u0432\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440 19 2019  12:04 \\u04AF.\\u0445. \\u041C\\u044F\"},\n\t\t{\"43543.503206018519\", \"[$-450]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0413 19 2019  12:04 \\u04AF.\\u0445. \\u041C\\u044F\"},\n\t\t{\"43543.503206018519\", \"[$-450]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0413\\u0443\\u0440\\u0430\\u0432\\u0434\\u0443\\u0433\\u0430\\u0430\\u0440 \\u0441\\u0430\\u0440 19 2019  12:04 \\u04AF.\\u0445. \\u043C\\u044F\\u0433\\u043C\\u0430\\u0440\"},\n\t\t{\"44562.189571759256\", \"[$-7C50]mmm dd yyyy  h:mm AM/PM\", \"M01 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-7C50]mmm dd yyyy  h:mm AM/PM\", \"M12 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C50]mmmm dd yyyy  h:mm AM/PM\", \"M01 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-7C50]mmmm dd yyyy  h:mm AM/PM aaa\", \"M12 01 2022  4:32 AM \\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u1833\\u1825\\u1837\\u182A\\u1821\\u1828\"},\n\t\t{\"44562.189571759256\", \"[$-7C50]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 01 2022  4:32 AM \\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u1835\\u1822\\u1837\\u182D\\u1824\\u182D\\u1820\\u1828\"},\n\t\t{\"44896.18957170139\", \"[$-7C50]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 01 2022  4:32 AM \\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u1833\\u1825\\u1837\\u182A\\u1821\\u1828\"},\n\t\t{\"44562.189571759256\", \"[$-850]mmm dd yyyy  h:mm AM/PM\", \"M01 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-850]mmm dd yyyy  h:mm AM/PM\", \"M12 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-850]mmmm dd yyyy  h:mm AM/PM\", \"M01 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-850]mmmm dd yyyy  h:mm AM/PM aaa\", \"M12 01 2022  4:32 AM \\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u1833\\u1825\\u1837\\u182A\\u1821\\u1828\"},\n\t\t{\"44562.189571759256\", \"[$-850]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 01 2022  4:32 AM \\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u1835\\u1822\\u1837\\u182D\\u1824\\u182D\\u1820\\u1828\"},\n\t\t{\"44896.18957170139\", \"[$-850]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 01 2022  4:32 AM \\u182D\\u1820\\u1837\\u1820\\u182D\\u202F\\u1824\\u1828 \\u1833\\u1825\\u1837\\u182A\\u1821\\u1828\"},\n\t\t{\"44562.189571759256\", \"[$-C50]mmm dd yyyy  h:mm AM/PM\", \"M01 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-C50]mmm dd yyyy  h:mm AM/PM\", \"M12 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-C50]mmmm dd yyyy  h:mm AM/PM\", \"M01 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-C50]mmmm dd yyyy  h:mm AM/PM aaa\", \"M12 01 2022  4:32 AM \\u182B\\u1826\\u1837\\u182A\\u1826\"},\n\t\t{\"44562.189571759256\", \"[$-C50]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 01 2022  4:32 AM \\u182A\\u1822\\u182E\\u182A\\u1820\"},\n\t\t{\"44896.18957170139\", \"[$-C50]mmmmm dd yyyy  h:mm AM/PM dddd\", \"M 01 2022  4:32 AM \\u182B\\u1826\\u1837\\u182A\\u1826\"},\n\t\t{\"44562.189571759256\", \"[$-61]mmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-61]mmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-61]mmmmm dd yyyy  h:mm AM/PM\", \"\\u091C 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-61]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"43543.503206018519\", \"[$-61]mmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"43543.503206018519\", \"[$-61]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0919\\u094D\\u0917\\u0932\"},\n\t\t{\"43543.503206018519\", \"[$-61]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u092E 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0919\\u094D\\u0917\\u0932\"},\n\t\t{\"43543.503206018519\", \"[$-61]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0919\\u094D\\u0917\\u0932\\u0935\\u093E\\u0930\"},\n\t\t{\"44562.189571759256\", \"[$-861]mmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-861]mmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-861]mmmmm dd yyyy  h:mm AM/PM\", \"\\u091C 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-861]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"43543.503206018519\", \"[$-861]mmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"43543.503206018519\", \"[$-861]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0919\\u094D\\u0917\\u0932\"},\n\t\t{\"43543.503206018519\", \"[$-861]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u092E 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0919\\u094D\\u0917\\u0932\"},\n\t\t{\"43543.503206018519\", \"[$-861]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0919\\u094D\\u0917\\u0932\\u092C\\u093E\\u0930\"},\n\t\t{\"44562.189571759256\", \"[$-461]mmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-461]mmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-461]mmmmm dd yyyy  h:mm AM/PM\", \"\\u091C 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"44562.189571759256\", \"[$-461]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u0928\\u0935\\u0930\\u0940 01 2022  4:32 \\u092A\\u0942\\u0930\\u094D\\u0935\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"43543.503206018519\", \"[$-461]mmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928\"},\n\t\t{\"43543.503206018519\", \"[$-461]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0919\\u094D\\u0917\\u0932\"},\n\t\t{\"43543.503206018519\", \"[$-461]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u092E 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0919\\u094D\\u0917\\u0932\"},\n\t\t{\"43543.503206018519\", \"[$-461]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u0905\\u092A\\u0930\\u093E\\u0939\\u094D\\u0928 \\u092E\\u0919\\u094D\\u0917\\u0932\\u0935\\u093E\\u0930\"},\n\t\t{\"44562.189571759256\", \"[$-14]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-14]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-14]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-14]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-14]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-14]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 p.m. tir.\"},\n\t\t{\"43543.503206018519\", \"[$-14]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 p.m. tir.\"},\n\t\t{\"43543.503206018519\", \"[$-14]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 p.m. tirsdag\"},\n\t\t{\"44562.189571759256\", \"[$-7C14]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-7C14]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-7C14]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-7C14]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-7C14]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-7C14]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 p.m. tir\"},\n\t\t{\"43543.503206018519\", \"[$-7C14]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 p.m. tir\"},\n\t\t{\"43543.503206018519\", \"[$-7C14]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 p.m. tirsdag\"},\n\t\t{\"44562.189571759256\", \"[$-414]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-414]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-414]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-414]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-414]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-414]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 p.m. tir\"},\n\t\t{\"43543.503206018519\", \"[$-414]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 p.m. tir\"},\n\t\t{\"43543.503206018519\", \"[$-414]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 p.m. tirsdag\"},\n\t\t{\"44562.189571759256\", \"[$-7814]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 f.m.\"},\n\t\t{\"44562.189571759256\", \"[$-7814]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 f.m.\"},\n\t\t{\"44562.189571759256\", \"[$-7814]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 f.m.\"},\n\t\t{\"44562.189571759256\", \"[$-7814]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 f.m.\"},\n\t\t{\"43543.503206018519\", \"[$-7814]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 e.m.\"},\n\t\t{\"43543.503206018519\", \"[$-7814]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 e.m. tys\"},\n\t\t{\"43543.503206018519\", \"[$-7814]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 e.m. tys\"},\n\t\t{\"43543.503206018519\", \"[$-7814]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 e.m. tysdag\"},\n\t\t{\"44562.189571759256\", \"[$-814]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 f.m.\"},\n\t\t{\"44562.189571759256\", \"[$-814]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 f.m.\"},\n\t\t{\"44562.189571759256\", \"[$-814]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 f.m.\"},\n\t\t{\"44562.189571759256\", \"[$-814]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 f.m.\"},\n\t\t{\"43543.503206018519\", \"[$-814]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 e.m.\"},\n\t\t{\"43543.503206018519\", \"[$-814]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 e.m. tys\"},\n\t\t{\"43543.503206018519\", \"[$-814]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 e.m. tys\"},\n\t\t{\"43543.503206018519\", \"[$-814]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 e.m. tysdag\"},\n\t\t{\"44562.189571759256\", \"[$-82]mmm dd yyyy  h:mm AM/PM\", \"gen. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-82]mmmm dd yyyy  h:mm AM/PM\", \"genièr 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-82]mmmmm dd yyyy  h:mm AM/PM\", \"g 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-82]mmmmmm dd yyyy  h:mm AM/PM\", \"genièr 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-82]mmm dd yyyy  h:mm AM/PM\", \"març 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-82]mmmm dd yyyy  h:mm AM/PM aaa\", \"març 19 2019  12:04 PM dma.\"},\n\t\t{\"43543.503206018519\", \"[$-82]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM dma.\"},\n\t\t{\"43543.503206018519\", \"[$-82]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"març 19 2019  12:04 PM dimarts\"},\n\t\t{\"44562.189571759256\", \"[$-482]mmm dd yyyy  h:mm AM/PM\", \"gen. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-482]mmmm dd yyyy  h:mm AM/PM\", \"genièr 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-482]mmmmm dd yyyy  h:mm AM/PM\", \"g 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-482]mmmmmm dd yyyy  h:mm AM/PM\", \"genièr 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-482]mmm dd yyyy  h:mm AM/PM\", \"març 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-482]mmmm dd yyyy  h:mm AM/PM aaa\", \"març 19 2019  12:04 PM dma.\"},\n\t\t{\"43543.503206018519\", \"[$-482]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM dma.\"},\n\t\t{\"43543.503206018519\", \"[$-482]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"març 19 2019  12:04 PM dimarts\"},\n\t\t{\"44562.189571759256\", \"[$-48]mmm dd yyyy  h:mm AM/PM\", \"\\u0B1C\\u0B3E\\u0B28\\u0B41\\u0B5F\\u0B3E\\u0B30\\u0B40 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-48]mmmm dd yyyy  h:mm AM/PM\", \"\\u0B1C\\u0B3E\\u0B28\\u0B41\\u0B5F\\u0B3E\\u0B30\\u0B40 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-48]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0B1C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-48]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0B1C\\u0B3E\\u0B28\\u0B41\\u0B5F\\u0B3E\\u0B30\\u0B40 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-48]mmm dd yyyy  h:mm AM/PM\", \"\\u0B2E\\u0B3E\\u0B30\\u0B4D\\u0B1A\\u0B4D\\u0B1A 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-48]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0B2E\\u0B3E\\u0B30\\u0B4D\\u0B1A\\u0B4D\\u0B1A 19 2019  12:04 PM \\u0B2E\\u0B19\\u0B4D\\u0B17\\u0B33.\"},\n\t\t{\"43543.503206018519\", \"[$-48]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0B2E 19 2019  12:04 PM \\u0B2E\\u0B19\\u0B4D\\u0B17\\u0B33.\"},\n\t\t{\"43543.503206018519\", \"[$-48]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0B2E\\u0B3E\\u0B30\\u0B4D\\u0B1A\\u0B4D\\u0B1A 19 2019  12:04 PM \\u0B2E\\u0B19\\u0B4D\\u0B17\\u0B33\\u0B2C\\u0B3E\\u0B30\"},\n\t\t{\"44562.189571759256\", \"[$-448]mmm dd yyyy  h:mm AM/PM\", \"\\u0B1C\\u0B3E\\u0B28\\u0B41\\u0B5F\\u0B3E\\u0B30\\u0B40 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-448]mmmm dd yyyy  h:mm AM/PM\", \"\\u0B1C\\u0B3E\\u0B28\\u0B41\\u0B5F\\u0B3E\\u0B30\\u0B40 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-448]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0B1C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-448]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0B1C\\u0B3E\\u0B28\\u0B41\\u0B5F\\u0B3E\\u0B30\\u0B40 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-448]mmm dd yyyy  h:mm AM/PM\", \"\\u0B2E\\u0B3E\\u0B30\\u0B4D\\u0B1A\\u0B4D\\u0B1A 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-448]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0B2E\\u0B3E\\u0B30\\u0B4D\\u0B1A\\u0B4D\\u0B1A 19 2019  12:04 PM \\u0B2E\\u0B19\\u0B4D\\u0B17\\u0B33.\"},\n\t\t{\"43543.503206018519\", \"[$-448]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0B2E 19 2019  12:04 PM \\u0B2E\\u0B19\\u0B4D\\u0B17\\u0B33.\"},\n\t\t{\"43543.503206018519\", \"[$-448]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0B2E\\u0B3E\\u0B30\\u0B4D\\u0B1A\\u0B4D\\u0B1A 19 2019  12:04 PM \\u0B2E\\u0B19\\u0B4D\\u0B17\\u0B33\\u0B2C\\u0B3E\\u0B30\"},\n\t\t{\"44562.189571759256\", \"[$-72]mmm dd yyyy  h:mm AM/PM\", \"Ama 01 2022  4:32 WD\"},\n\t\t{\"44562.189571759256\", \"[$-72]mmmm dd yyyy  h:mm AM/PM\", \"Amajjii 01 2022  4:32 WD\"},\n\t\t{\"44562.189571759256\", \"[$-72]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 WD\"},\n\t\t{\"44562.189571759256\", \"[$-72]mmmmmm dd yyyy  h:mm AM/PM\", \"Amajjii 01 2022  4:32 WD\"},\n\t\t{\"43543.503206018519\", \"[$-72]mmm dd yyyy  h:mm AM/PM\", \"Bit 19 2019  12:04 WB\"},\n\t\t{\"43543.503206018519\", \"[$-72]mmmm dd yyyy  h:mm AM/PM aaa\", \"Bitooteessa 19 2019  12:04 WB Qib\"},\n\t\t{\"43543.503206018519\", \"[$-72]mmmmm dd yyyy  h:mm AM/PM ddd\", \"B 19 2019  12:04 WB Qib\"},\n\t\t{\"43543.503206018519\", \"[$-72]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Bitooteessa 19 2019  12:04 WB Qibxata\"},\n\t\t{\"44562.189571759256\", \"[$-472]mmm dd yyyy  h:mm AM/PM\", \"Ama 01 2022  4:32 WD\"},\n\t\t{\"44562.189571759256\", \"[$-472]mmmm dd yyyy  h:mm AM/PM\", \"Amajjii 01 2022  4:32 WD\"},\n\t\t{\"44562.189571759256\", \"[$-472]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 WD\"},\n\t\t{\"44562.189571759256\", \"[$-472]mmmmmm dd yyyy  h:mm AM/PM\", \"Amajjii 01 2022  4:32 WD\"},\n\t\t{\"43543.503206018519\", \"[$-472]mmm dd yyyy  h:mm AM/PM\", \"Bit 19 2019  12:04 WB\"},\n\t\t{\"43543.503206018519\", \"[$-472]mmmm dd yyyy  h:mm AM/PM aaa\", \"Bitooteessa 19 2019  12:04 WB Qib\"},\n\t\t{\"43543.503206018519\", \"[$-472]mmmmm dd yyyy  h:mm AM/PM ddd\", \"B 19 2019  12:04 WB Qib\"},\n\t\t{\"43543.503206018519\", \"[$-472]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Bitooteessa 19 2019  12:04 WB Qibxata\"},\n\t\t{\"44562.189571759256\", \"[$-63]mmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u0644\\u0648\\u0627\\u063A\\u0647 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44562.189571759256\", \"[$-63]mmmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u0644\\u0648\\u0627\\u063A\\u0647 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44562.189571759256\", \"[$-63]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u0644\\u0648\\u0627\\u063A\\u0647 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44562.189571759256\", \"[$-63]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u0644\\u0648\\u0627\\u063A\\u0647 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44713.188888888886\", \"[$-63]mmm dd yyyy  h:mm AM/PM\", \"\\u0686\\u0646\\u06AB\\u0627 \\u069A 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44713.188888888886\", \"[$-63]mmmm dd yyyy  h:mm AM/PM\", \"\\u0686\\u0646\\u06AB\\u0627 \\u069A\\u0632\\u0645\\u0631\\u0649 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44713.188888888886\", \"[$-63]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0686\\u0646\\u06AB\\u0627 \\u069A\\u0632\\u0645\\u0631\\u0649 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44713.188888888886\", \"[$-63]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0686\\u0646\\u06AB\\u0627 \\u069A\\u0632\\u0645\\u0631\\u0649 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"43543.503206018519\", \"[$-63]mmm dd yyyy  h:mm AM/PM\", \"\\u0648\\u0631\\u0649 19 2019  12:04 \\u063A.\\u0648.\"},\n\t\t{\"43543.503206018519\", \"[$-63]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0648\\u0631\\u0649 19 2019  12:04 \\u063A.\\u0648. \\u062F\\u0631\\u06D0\\u0646\\u06CD\"},\n\t\t{\"43543.503206018519\", \"[$-63]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0648\\u0631\\u0649 19 2019  12:04 \\u063A.\\u0648. \\u062F\\u0631\\u06D0\\u0646\\u06CD\"},\n\t\t{\"43543.503206018519\", \"[$-63]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0648\\u0631\\u0649 19 2019  12:04 \\u063A.\\u0648. \\u062F\\u0631\\u06D0\\u0646\\u06CD\"},\n\t\t{\"44562.189571759256\", \"[$-463]mmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u0644\\u0648\\u0627\\u063A\\u0647 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44562.189571759256\", \"[$-463]mmmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u0644\\u0648\\u0627\\u063A\\u0647 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44562.189571759256\", \"[$-463]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u0644\\u0648\\u0627\\u063A\\u0647 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44562.189571759256\", \"[$-463]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0633\\u0644\\u0648\\u0627\\u063A\\u0647 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44713.188888888886\", \"[$-463]mmm dd yyyy  h:mm AM/PM\", \"\\u0686\\u0646\\u06AB\\u0627 \\u069A 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44713.188888888886\", \"[$-463]mmmm dd yyyy  h:mm AM/PM\", \"\\u0686\\u0646\\u06AB\\u0627 \\u069A\\u0632\\u0645\\u0631\\u0649 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44713.188888888886\", \"[$-463]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0686\\u0646\\u06AB\\u0627 \\u069A\\u0632\\u0645\\u0631\\u0649 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"44713.188888888886\", \"[$-463]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0686\\u0646\\u06AB\\u0627 \\u069A\\u0632\\u0645\\u0631\\u0649 01 2022  4:32 \\u063A.\\u0645.\"},\n\t\t{\"43543.503206018519\", \"[$-463]mmm dd yyyy  h:mm AM/PM\", \"\\u0648\\u0631\\u0649 19 2019  12:04 \\u063A.\\u0648.\"},\n\t\t{\"43543.503206018519\", \"[$-463]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0648\\u0631\\u0649 19 2019  12:04 \\u063A.\\u0648. \\u062F\\u0631\\u06D0\\u0646\\u06CD\"},\n\t\t{\"43543.503206018519\", \"[$-463]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0648\\u0631\\u0649 19 2019  12:04 \\u063A.\\u0648. \\u062F\\u0631\\u06D0\\u0646\\u06CD\"},\n\t\t{\"43543.503206018519\", \"[$-463]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0648\\u0631\\u0649 19 2019  12:04 \\u063A.\\u0648. \\u062F\\u0631\\u06D0\\u0646\\u06CD\"},\n\t\t{\"44562.189571759256\", \"[$-29]mmm dd yyyy  h:mm AM/PM\", \"\\u0698\\u0627\\u0646\\u0648\\u064A\\u0647 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44562.189571759256\", \"[$-29]mmmm dd yyyy  h:mm AM/PM\", \"\\u0698\\u0627\\u0646\\u0648\\u064A\\u0647 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44562.189571759256\", \"[$-29]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0698 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44562.189571759256\", \"[$-29]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0698\\u0627\\u0646\\u0648\\u064A\\u0647 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"43543.503206018519\", \"[$-29]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0628.\\u0638\"},\n\t\t{\"43543.503206018519\", \"[$-29]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0628.\\u0638 \\u0633\\u0647 \\u0634\\u0646\\u0628\\u0647\"},\n\t\t{\"43543.503206018519\", \"[$-29]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0628.\\u0638 \\u0633\\u0647 \\u0634\\u0646\\u0628\\u0647\"},\n\t\t{\"43543.503206018519\", \"[$-29]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0628.\\u0638 \\u0633\\u0647 \\u0634\\u0646\\u0628\\u0647\"},\n\t\t{\"44562.189571759256\", \"[$-429]mmm dd yyyy  h:mm AM/PM\", \"\\u0698\\u0627\\u0646\\u0648\\u064A\\u0647 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44562.189571759256\", \"[$-429]mmmm dd yyyy  h:mm AM/PM\", \"\\u0698\\u0627\\u0646\\u0648\\u064A\\u0647 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44562.189571759256\", \"[$-429]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0698 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"44562.189571759256\", \"[$-429]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0698\\u0627\\u0646\\u0648\\u064A\\u0647 01 2022  4:32 \\u0642.\\u0638\"},\n\t\t{\"43543.503206018519\", \"[$-429]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0628.\\u0638\"},\n\t\t{\"43543.503206018519\", \"[$-429]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0628.\\u0638 \\u0633\\u0647 \\u0634\\u0646\\u0628\\u0647\"},\n\t\t{\"43543.503206018519\", \"[$-429]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0628.\\u0638 \\u0633\\u0647 \\u0634\\u0646\\u0628\\u0647\"},\n\t\t{\"43543.503206018519\", \"[$-429]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0633 19 2019  12:04 \\u0628.\\u0638 \\u0633\\u0647 \\u0634\\u0646\\u0628\\u0647\"},\n\t\t{\"44562.189571759256\", \"[$-15]mmm dd yyyy  h:mm AM/PM\", \"sty 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-15]mmmm dd yyyy  h:mm AM/PM\", \"styczeń 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-15]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-15]mmmmmm dd yyyy  h:mm AM/PM\", \"styczeń 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-15]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-15]mmmm dd yyyy  h:mm AM/PM aaa\", \"marzec 19 2019  12:04 PM wt.\"},\n\t\t{\"43543.503206018519\", \"[$-15]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM wt.\"},\n\t\t{\"43543.503206018519\", \"[$-15]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"marzec 19 2019  12:04 PM wtorek\"},\n\t\t{\"44562.189571759256\", \"[$-415]mmm dd yyyy  h:mm AM/PM\", \"sty 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-415]mmmm dd yyyy  h:mm AM/PM\", \"styczeń 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-415]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-415]mmmmmm dd yyyy  h:mm AM/PM\", \"styczeń 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-415]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-415]mmmm dd yyyy  h:mm AM/PM aaa\", \"marzec 19 2019  12:04 PM wt.\"},\n\t\t{\"43543.503206018519\", \"[$-415]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM wt.\"},\n\t\t{\"43543.503206018519\", \"[$-415]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"marzec 19 2019  12:04 PM wtorek\"},\n\t\t{\"44562.189571759256\", \"[$-16]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-16]mmmm dd yyyy  h:mm AM/PM\", \"janeiro 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-16]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-16]mmmmmm dd yyyy  h:mm AM/PM\", \"janeiro 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-16]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-16]mmmm dd yyyy  h:mm AM/PM aaa\", \"março 19 2019  12:04 PM ter\"},\n\t\t{\"43543.503206018519\", \"[$-16]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM ter\"},\n\t\t{\"43543.503206018519\", \"[$-16]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"março 19 2019  12:04 PM terça-feira\"},\n\t\t{\"44562.189571759256\", \"[$-416]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-416]mmmm dd yyyy  h:mm AM/PM\", \"janeiro 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-416]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-416]mmmmmm dd yyyy  h:mm AM/PM\", \"janeiro 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-416]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-416]mmmm dd yyyy  h:mm AM/PM aaa\", \"março 19 2019  12:04 PM ter\"},\n\t\t{\"43543.503206018519\", \"[$-416]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM ter\"},\n\t\t{\"43543.503206018519\", \"[$-416]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"março 19 2019  12:04 PM terça-feira\"},\n\t\t{\"44562.189571759256\", \"[$-816]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-816]mmmm dd yyyy  h:mm AM/PM\", \"janeiro 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-816]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-816]mmmmmm dd yyyy  h:mm AM/PM\", \"janeiro 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-816]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-816]mmmm dd yyyy  h:mm AM/PM aaa\", \"março 19 2019  12:04 PM ter\"},\n\t\t{\"43543.503206018519\", \"[$-816]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM ter\"},\n\t\t{\"43543.503206018519\", \"[$-816]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"março 19 2019  12:04 PM terça-feira\"},\n\t\t{\"44562.189571759256\", \"[$-46]mmm dd yyyy  h:mm AM/PM\", \"\\u0A1C\\u0A28\\u0A35\\u0A30\\u0A40 01 2022  4:32 \\u0A38\\u0A35\\u0A47\\u0A30\"},\n\t\t{\"44562.189571759256\", \"[$-46]mmmm dd yyyy  h:mm AM/PM\", \"\\u0A1C\\u0A28\\u0A35\\u0A30\\u0A40 01 2022  4:32 \\u0A38\\u0A35\\u0A47\\u0A30\"},\n\t\t{\"44562.189571759256\", \"[$-46]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0A1C 01 2022  4:32 \\u0A38\\u0A35\\u0A47\\u0A30\"},\n\t\t{\"44562.189571759256\", \"[$-46]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0A1C\\u0A28\\u0A35\\u0A30\\u0A40 01 2022  4:32 \\u0A38\\u0A35\\u0A47\\u0A30\"},\n\t\t{\"43543.503206018519\", \"[$-46]mmm dd yyyy  h:mm AM/PM\", \"\\u0A2E\\u0A3E\\u0A30\\u0A1A 19 2019  12:04 \\u0A38\\u0A3C\\u0A3E\\u0A2E\"},\n\t\t{\"43543.503206018519\", \"[$-46]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0A2E\\u0A3E\\u0A30\\u0A1A 19 2019  12:04 \\u0A38\\u0A3C\\u0A3E\\u0A2E \\u0A2E\\u0A70\\u0A17\\u0A32.\"},\n\t\t{\"43543.503206018519\", \"[$-46]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0A2E 19 2019  12:04 \\u0A38\\u0A3C\\u0A3E\\u0A2E \\u0A2E\\u0A70\\u0A17\\u0A32.\"},\n\t\t{\"43543.503206018519\", \"[$-46]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0A2E\\u0A3E\\u0A30\\u0A1A 19 2019  12:04 \\u0A38\\u0A3C\\u0A3E\\u0A2E \\u0A2E\\u0A70\\u0A17\\u0A32\\u0A35\\u0A3E\\u0A30\"},\n\t\t{\"44562.189571759256\", \"[$-7C46]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C46]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C46]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C46]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-7C46]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-7C46]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0628\\u062F\\u06BE\"},\n\t\t{\"43543.503206018519\", \"[$-7C46]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 PM \\u0628\\u062F\\u06BE\"},\n\t\t{\"43543.503206018519\", \"[$-7C46]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0628\\u062F\\u06BE\"},\n\t\t{\"44562.189571759256\", \"[$-446]mmm dd yyyy  h:mm AM/PM\", \"\\u0A1C\\u0A28\\u0A35\\u0A30\\u0A40 01 2022  4:32 \\u0A38\\u0A35\\u0A47\\u0A30\"},\n\t\t{\"44562.189571759256\", \"[$-446]mmmm dd yyyy  h:mm AM/PM\", \"\\u0A1C\\u0A28\\u0A35\\u0A30\\u0A40 01 2022  4:32 \\u0A38\\u0A35\\u0A47\\u0A30\"},\n\t\t{\"44562.189571759256\", \"[$-446]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0A1C 01 2022  4:32 \\u0A38\\u0A35\\u0A47\\u0A30\"},\n\t\t{\"44562.189571759256\", \"[$-446]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0A1C\\u0A28\\u0A35\\u0A30\\u0A40 01 2022  4:32 \\u0A38\\u0A35\\u0A47\\u0A30\"},\n\t\t{\"43543.503206018519\", \"[$-446]mmm dd yyyy  h:mm AM/PM\", \"\\u0A2E\\u0A3E\\u0A30\\u0A1A 19 2019  12:04 \\u0A38\\u0A3C\\u0A3E\\u0A2E\"},\n\t\t{\"43543.503206018519\", \"[$-446]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0A2E\\u0A3E\\u0A30\\u0A1A 19 2019  12:04 \\u0A38\\u0A3C\\u0A3E\\u0A2E \\u0A2E\\u0A70\\u0A17\\u0A32.\"},\n\t\t{\"43543.503206018519\", \"[$-446]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0A2E 19 2019  12:04 \\u0A38\\u0A3C\\u0A3E\\u0A2E \\u0A2E\\u0A70\\u0A17\\u0A32.\"},\n\t\t{\"43543.503206018519\", \"[$-446]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0A2E\\u0A3E\\u0A30\\u0A1A 19 2019  12:04 \\u0A38\\u0A3C\\u0A3E\\u0A2E \\u0A2E\\u0A70\\u0A17\\u0A32\\u0A35\\u0A3E\\u0A30\"},\n\t\t{\"44562.189571759256\", \"[$-846]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-846]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-846]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-846]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-846]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-846]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0628\\u062F\\u06BE\"},\n\t\t{\"43543.503206018519\", \"[$-846]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 PM \\u0628\\u062F\\u06BE\"},\n\t\t{\"43543.503206018519\", \"[$-846]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0628\\u062F\\u06BE\"},\n\t\t{\"44562.189571759256\", \"[$-6B]mmm dd yyyy  h:mm AM/PM\", \"Qul 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-6B]mmmm dd yyyy  h:mm AM/PM\", \"Qulla puquy 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-6B]mmmmm dd yyyy  h:mm AM/PM\", \"Q 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-6B]mmmmmm dd yyyy  h:mm AM/PM\", \"Qulla puquy 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-6B]mmm dd yyyy  h:mm AM/PM\", \"Pau 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-6B]mmmm dd yyyy  h:mm AM/PM aaa\", \"Pauqar waray 19 2019  12:04 p.m. ati\"},\n\t\t{\"43543.503206018519\", \"[$-6B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"P 19 2019  12:04 p.m. ati\"},\n\t\t{\"43543.503206018519\", \"[$-6B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Pauqar waray 19 2019  12:04 p.m. atipachaw\"},\n\t\t{\"44562.189571759256\", \"[$-46B]mmm dd yyyy  h:mm AM/PM\", \"Qul 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-46B]mmmm dd yyyy  h:mm AM/PM\", \"Qulla puquy 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-46B]mmmmm dd yyyy  h:mm AM/PM\", \"Q 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-46B]mmmmmm dd yyyy  h:mm AM/PM\", \"Qulla puquy 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-46B]mmm dd yyyy  h:mm AM/PM\", \"Pau 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-46B]mmmm dd yyyy  h:mm AM/PM aaa\", \"Pauqar waray 19 2019  12:04 p.m. ati\"},\n\t\t{\"43543.503206018519\", \"[$-46B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"P 19 2019  12:04 p.m. ati\"},\n\t\t{\"43543.503206018519\", \"[$-46B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Pauqar waray 19 2019  12:04 p.m. atipachaw\"},\n\t\t{\"44562.189571759256\", \"[$-86B]mmm dd yyyy  h:mm AM/PM\", \"kull 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-86B]mmmm dd yyyy  h:mm AM/PM\", \"kulla 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-86B]mmmmm dd yyyy  h:mm AM/PM\", \"k 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-86B]mmmmmm dd yyyy  h:mm AM/PM\", \"kulla 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-86B]mmm dd yyyy  h:mm AM/PM\", \"paw 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-86B]mmmm dd yyyy  h:mm AM/PM aaa\", \"pawkar 19 2019  12:04 PM wan\"},\n\t\t{\"43543.503206018519\", \"[$-86B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"p 19 2019  12:04 PM wan\"},\n\t\t{\"43543.503206018519\", \"[$-86B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"pawkar 19 2019  12:04 PM wanra\"},\n\t\t{\"44562.189571759256\", \"[$-C6B]mmm dd yyyy  h:mm AM/PM\", \"Qul 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-C6B]mmmm dd yyyy  h:mm AM/PM\", \"Qulla puquy 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-C6B]mmmmm dd yyyy  h:mm AM/PM\", \"Q 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-C6B]mmmmmm dd yyyy  h:mm AM/PM\", \"Qulla puquy 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-C6B]mmm dd yyyy  h:mm AM/PM\", \"Pau 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-C6B]mmmm dd yyyy  h:mm AM/PM aaa\", \"Pauqar waray 19 2019  12:04 p.m. Mar\"},\n\t\t{\"43543.503206018519\", \"[$-C6B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"P 19 2019  12:04 p.m. Mar\"},\n\t\t{\"43543.503206018519\", \"[$-C6B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Pauqar waray 19 2019  12:04 p.m. Martes\"},\n\t\t{\"44562.189571759256\", \"[$-18]mmm dd yyyy  h:mm AM/PM\", \"ian. 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-18]mmmm dd yyyy  h:mm AM/PM\", \"ianuarie 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-18]mmmmm dd yyyy  h:mm AM/PM\", \"i 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-18]mmmmmm dd yyyy  h:mm AM/PM\", \"ianuarie 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-18]mmm dd yyyy  h:mm AM/PM\", \"mar. 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-18]mmmm dd yyyy  h:mm AM/PM aaa\", \"martie 19 2019  12:04 p.m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-18]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 p.m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-18]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"martie 19 2019  12:04 p.m. marți\"},\n\t\t{\"44562.189571759256\", \"[$-818]mmm dd yyyy  h:mm AM/PM\", \"ian. 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-818]mmmm dd yyyy  h:mm AM/PM\", \"ianuarie 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-818]mmmmm dd yyyy  h:mm AM/PM\", \"i 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-818]mmmmmm dd yyyy  h:mm AM/PM\", \"ianuarie 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-818]mmm dd yyyy  h:mm AM/PM\", \"mar. 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-818]mmmm dd yyyy  h:mm AM/PM aaa\", \"martie 19 2019  12:04 p.m. Mar\"},\n\t\t{\"43543.503206018519\", \"[$-818]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 p.m. Mar\"},\n\t\t{\"43543.503206018519\", \"[$-818]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"martie 19 2019  12:04 p.m. marți\"},\n\t\t{\"44562.189571759256\", \"[$-418]mmm dd yyyy  h:mm AM/PM\", \"ian. 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-418]mmmm dd yyyy  h:mm AM/PM\", \"ianuarie 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-418]mmmmm dd yyyy  h:mm AM/PM\", \"i 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-418]mmmmmm dd yyyy  h:mm AM/PM\", \"ianuarie 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-418]mmm dd yyyy  h:mm AM/PM\", \"mar. 19 2019  12:04 p.m.\"},\n\t\t{\"43543.503206018519\", \"[$-418]mmmm dd yyyy  h:mm AM/PM aaa\", \"martie 19 2019  12:04 p.m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-418]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 p.m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-418]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"martie 19 2019  12:04 p.m. marți\"},\n\t\t{\"44562.189571759256\", \"[$-17]mmm dd yyyy  h:mm AM/PM\", \"schan. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-17]mmmm dd yyyy  h:mm AM/PM\", \"schaner 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-17]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-17]mmmmmm dd yyyy  h:mm AM/PM\", \"schaner 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-17]mmm dd yyyy  h:mm AM/PM\", \"mars 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-17]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM ma\"},\n\t\t{\"43543.503206018519\", \"[$-17]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM ma\"},\n\t\t{\"43543.503206018519\", \"[$-17]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-417]mmm dd yyyy  h:mm AM/PM\", \"schan. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-417]mmmm dd yyyy  h:mm AM/PM\", \"schaner 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-417]mmmmm dd yyyy  h:mm AM/PM\", \"s 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-417]mmmmmm dd yyyy  h:mm AM/PM\", \"schaner 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-417]mmm dd yyyy  h:mm AM/PM\", \"mars 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-417]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM ma\"},\n\t\t{\"43543.503206018519\", \"[$-417]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM ma\"},\n\t\t{\"43543.503206018519\", \"[$-417]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 PM mardi\"},\n\t\t{\"44562.189571759256\", \"[$-19]mmm dd yyyy  h:mm AM/PM\", \"янв. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-19]mmmm dd yyyy  h:mm AM/PM\", \"январь 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-19]mmmmm dd yyyy  h:mm AM/PM\", \"я 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-19]mmm dd yyyy  h:mm AM/PM aaa\", \"март 19 2019  12:04 PM \\u0412\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-19]mmmm dd yyyy  h:mm AM/PM ddd\", \"март 19 2019  12:04 PM \\u0412\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-19]mmmmm dd yyyy  h:mm AM/PM dddd\", \"м 19 2019  12:04 PM \\u0432\\u0442\\u043E\\u0440\\u043D\\u0438\\u043A\"},\n\t\t{\"43543.503206018519\", \"[$-819]mmm dd yyyy  h:mm AM/PM aaa\", \"март 19 2019  12:04 PM \\u0412\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-819]mmmm dd yyyy  h:mm AM/PM ddd\", \"март 19 2019  12:04 PM \\u0412\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-819]mmmmm dd yyyy  h:mm AM/PM dddd\", \"м 19 2019  12:04 PM \\u0432\\u0442\\u043E\\u0440\\u043D\\u0438\\u043A\"},\n\t\t{\"43543.503206018519\", \"[$-419]mmm dd yyyy  h:mm AM/PM aaa\", \"март 19 2019  12:04 PM \\u0412\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-419]mmmm dd yyyy  h:mm AM/PM ddd\", \"март 19 2019  12:04 PM \\u0412\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-419]mmmmm dd yyyy  h:mm AM/PM dddd\", \"м 19 2019  12:04 PM \\u0432\\u0442\\u043E\\u0440\\u043D\\u0438\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-85]mmm dd yyyy  h:mm AM/PM\", \"\\u0422\\u0445\\u0441 01 2022  4:32 \\u041A\\u0418\"},\n\t\t{\"44562.189571759256\", \"[$-85]mmmm dd yyyy  h:mm AM/PM\", \"\\u0422\\u043E\\u0445\\u0441\\u0443\\u043D\\u043D\\u044C\\u0443 01 2022  4:32 \\u041A\\u0418\"},\n\t\t{\"44562.189571759256\", \"[$-85]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0422 01 2022  4:32 \\u041A\\u0418\"},\n\t\t{\"44562.189571759256\", \"[$-85]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0422\\u043E\\u0445\\u0441\\u0443\\u043D\\u043D\\u044C\\u0443 01 2022  4:32 \\u041A\\u0418\"},\n\t\t{\"43543.503206018519\", \"[$-85]mmm dd yyyy  h:mm AM/PM\", \"\\u041A\\u043B\\u043D 19 2019  12:04 \\u041A\\u041A\"},\n\t\t{\"43543.503206018519\", \"[$-85]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u041A\\u0443\\u043B\\u0443\\u043D \\u0442\\u0443\\u0442\\u0430\\u0440 19 2019  12:04 \\u041A\\u041A \\u043E\\u043F\"},\n\t\t{\"43543.503206018519\", \"[$-85]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u041A 19 2019  12:04 \\u041A\\u041A \\u043E\\u043F\"},\n\t\t{\"43543.503206018519\", \"[$-85]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u041A\\u0443\\u043B\\u0443\\u043D \\u0442\\u0443\\u0442\\u0430\\u0440 19 2019  12:04 \\u041A\\u041A \\u041E\\u043F\\u0442\\u0443\\u043E\\u0440\\u0443\\u043D\\u043D\\u044C\\u0443\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-485]mmm dd yyyy  h:mm AM/PM\", \"\\u0422\\u0445\\u0441 01 2022  4:32 \\u041A\\u0418\"},\n\t\t{\"44562.189571759256\", \"[$-485]mmmm dd yyyy  h:mm AM/PM\", \"\\u0422\\u043E\\u0445\\u0441\\u0443\\u043D\\u043D\\u044C\\u0443 01 2022  4:32 \\u041A\\u0418\"},\n\t\t{\"44562.189571759256\", \"[$-485]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0422 01 2022  4:32 \\u041A\\u0418\"},\n\t\t{\"44562.189571759256\", \"[$-485]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0422\\u043E\\u0445\\u0441\\u0443\\u043D\\u043D\\u044C\\u0443 01 2022  4:32 \\u041A\\u0418\"},\n\t\t{\"43543.503206018519\", \"[$-485]mmm dd yyyy  h:mm AM/PM\", \"\\u041A\\u043B\\u043D 19 2019  12:04 \\u041A\\u041A\"},\n\t\t{\"43543.503206018519\", \"[$-485]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u041A\\u0443\\u043B\\u0443\\u043D \\u0442\\u0443\\u0442\\u0430\\u0440 19 2019  12:04 \\u041A\\u041A \\u043E\\u043F\"},\n\t\t{\"43543.503206018519\", \"[$-485]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u041A 19 2019  12:04 \\u041A\\u041A \\u043E\\u043F\"},\n\t\t{\"43543.503206018519\", \"[$-485]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u041A\\u0443\\u043B\\u0443\\u043D \\u0442\\u0443\\u0442\\u0430\\u0440 19 2019  12:04 \\u041A\\u041A \\u041E\\u043F\\u0442\\u0443\\u043E\\u0440\\u0443\\u043D\\u043D\\u044C\\u0443\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-703B]mmm dd yyyy  h:mm AM/PM\", \"uđiv 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-703B]mmmm dd yyyy  h:mm AM/PM\", \"uđđâivemáánu 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-703B]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-703B]mmmmmm dd yyyy  h:mm AM/PM\", \"uđđâivemáánu 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-703B]mmm dd yyyy  h:mm AM/PM\", \"njuh 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-703B]mmmm dd yyyy  h:mm AM/PM aaa\", \"njuhčâmáánu 19 2019  12:04 PM maj\"},\n\t\t{\"43543.503206018519\", \"[$-703B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 19 2019  12:04 PM maj\"},\n\t\t{\"43543.503206018519\", \"[$-703B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"njuhčâmáánu 19 2019  12:04 PM majebargâ\"},\n\t\t{\"44562.189571759256\", \"[$-243B]mmm dd yyyy  h:mm AM/PM\", \"uđiv 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-243B]mmmm dd yyyy  h:mm AM/PM\", \"uđđâivemáánu 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-243B]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-243B]mmmmmm dd yyyy  h:mm AM/PM\", \"uđđâivemáánu 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-243B]mmm dd yyyy  h:mm AM/PM\", \"njuh 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-243B]mmmm dd yyyy  h:mm AM/PM aaa\", \"njuhčâmáánu 19 2019  12:04 PM maj\"},\n\t\t{\"43543.503206018519\", \"[$-243B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 19 2019  12:04 PM maj\"},\n\t\t{\"43543.503206018519\", \"[$-243B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"njuhčâmáánu 19 2019  12:04 PM majebargâ\"},\n\t\t{\"44562.189571759256\", \"[$-7C3B]mmm dd yyyy  h:mm AM/PM\", \"ådåj 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C3B]mmmm dd yyyy  h:mm AM/PM\", \"ådåjakmánno 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C3B]mmmmm dd yyyy  h:mm AM/PM\", \"å 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C3B]mmmmmm dd yyyy  h:mm AM/PM\", \"ådåjakmánno 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-7C3B]mmm dd yyyy  h:mm AM/PM\", \"snju 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-7C3B]mmmm dd yyyy  h:mm AM/PM aaa\", \"sjnjuktjamánno 19 2019  12:04 PM dis\"},\n\t\t{\"43543.503206018519\", \"[$-7C3B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"s 19 2019  12:04 PM dis\"},\n\t\t{\"43543.503206018519\", \"[$-7C3B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"sjnjuktjamánno 19 2019  12:04 PM dijstahka\"},\n\t\t{\"44562.189571759256\", \"[$-103B]mmm dd yyyy  h:mm AM/PM\", \"ådåj 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-103B]mmmm dd yyyy  h:mm AM/PM\", \"ådåjakmánno 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-103B]mmmmm dd yyyy  h:mm AM/PM\", \"å 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-103B]mmmmmm dd yyyy  h:mm AM/PM\", \"ådåjakmánno 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-103B]mmm dd yyyy  h:mm AM/PM\", \"snju 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-103B]mmmm dd yyyy  h:mm AM/PM aaa\", \"sjnjuktjamánno 19 2019  12:04 PM dis\"},\n\t\t{\"43543.503206018519\", \"[$-103B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"s 19 2019  12:04 PM dis\"},\n\t\t{\"43543.503206018519\", \"[$-103B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"sjnjuktjamánno 19 2019  12:04 PM dijstahka\"},\n\t\t{\"44562.189571759256\", \"[$-143B]mmm dd yyyy  h:mm AM/PM\", \"ådåj 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-143B]mmmm dd yyyy  h:mm AM/PM\", \"ådåjakmánno 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-143B]mmmmm dd yyyy  h:mm AM/PM\", \"å 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-143B]mmmmmm dd yyyy  h:mm AM/PM\", \"ådåjakmánno 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-143B]mmm dd yyyy  h:mm AM/PM\", \"snju 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-143B]mmmm dd yyyy  h:mm AM/PM aaa\", \"sjnjuktjamánno 19 2019  12:04 PM dis\"},\n\t\t{\"43543.503206018519\", \"[$-143B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"s 19 2019  12:04 PM dis\"},\n\t\t{\"43543.503206018519\", \"[$-143B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"sjnjuktjamánno 19 2019  12:04 PM dijstahka\"},\n\t\t{\"44562.189571759256\", \"[$-3B]mmm dd yyyy  h:mm AM/PM\", \"ođđj 01 2022  4:32 i.b.\"},\n\t\t{\"44562.189571759256\", \"[$-3B]mmmm dd yyyy  h:mm AM/PM\", \"ođđajagemánnu 01 2022  4:32 i.b.\"},\n\t\t{\"44562.189571759256\", \"[$-3B]mmmmm dd yyyy  h:mm AM/PM\", \"o 01 2022  4:32 i.b.\"},\n\t\t{\"44562.189571759256\", \"[$-3B]mmmmmm dd yyyy  h:mm AM/PM\", \"ođđajagemánnu 01 2022  4:32 i.b.\"},\n\t\t{\"43543.503206018519\", \"[$-3B]mmm dd yyyy  h:mm AM/PM\", \"njuk 19 2019  12:04 e.b.\"},\n\t\t{\"43543.503206018519\", \"[$-3B]mmmm dd yyyy  h:mm AM/PM aaa\", \"njukčamánnu 19 2019  12:04 e.b. maŋ\"},\n\t\t{\"43543.503206018519\", \"[$-3B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 19 2019  12:04 e.b. maŋ\"},\n\t\t{\"43543.503206018519\", \"[$-3B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"njukčamánnu 19 2019  12:04 e.b. maŋŋebárga\"},\n\t\t{\"44562.189571759256\", \"[$-C3B]mmm dd yyyy  h:mm AM/PM\", \"ođđj 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-C3B]mmmm dd yyyy  h:mm AM/PM\", \"ođđajagemánu 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-C3B]mmmmm dd yyyy  h:mm AM/PM\", \"o 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-C3B]mmmmmm dd yyyy  h:mm AM/PM\", \"ođđajagemánu 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-C3B]mmm dd yyyy  h:mm AM/PM\", \"njuk 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-C3B]mmmm dd yyyy  h:mm AM/PM aaa\", \"njukčamánnu 19 2019  12:04 PM di\"},\n\t\t{\"43543.503206018519\", \"[$-C3B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 19 2019  12:04 PM di\"},\n\t\t{\"43543.503206018519\", \"[$-C3B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"njukčamánnu 19 2019  12:04 PM maŋŋebárga\"},\n\t\t{\"44562.189571759256\", \"[$-43B]mmm dd yyyy  h:mm AM/PM\", \"ođđj 01 2022  4:32 i.b.\"},\n\t\t{\"44562.189571759256\", \"[$-43B]mmmm dd yyyy  h:mm AM/PM\", \"ođđajagemánnu 01 2022  4:32 i.b.\"},\n\t\t{\"44562.189571759256\", \"[$-43B]mmmmm dd yyyy  h:mm AM/PM\", \"o 01 2022  4:32 i.b.\"},\n\t\t{\"44562.189571759256\", \"[$-43B]mmmmmm dd yyyy  h:mm AM/PM\", \"ođđajagemánnu 01 2022  4:32 i.b.\"},\n\t\t{\"43543.503206018519\", \"[$-43B]mmm dd yyyy  h:mm AM/PM\", \"njuk 19 2019  12:04 e.b.\"},\n\t\t{\"43543.503206018519\", \"[$-43B]mmmm dd yyyy  h:mm AM/PM aaa\", \"njukčamánnu 19 2019  12:04 e.b. maŋ\"},\n\t\t{\"43543.503206018519\", \"[$-43B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 19 2019  12:04 e.b. maŋ\"},\n\t\t{\"43543.503206018519\", \"[$-43B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"njukčamánnu 19 2019  12:04 e.b. maŋŋebárga\"},\n\t\t{\"44562.189571759256\", \"[$-83B]mmm dd yyyy  h:mm AM/PM\", \"ođđj 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-83B]mmmm dd yyyy  h:mm AM/PM\", \"ođđajagemánnu 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-83B]mmmmm dd yyyy  h:mm AM/PM\", \"o 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-83B]mmmmmm dd yyyy  h:mm AM/PM\", \"ođđajagemánnu 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-83B]mmm dd yyyy  h:mm AM/PM\", \"njuk 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-83B]mmmm dd yyyy  h:mm AM/PM aaa\", \"njukčamánnu 19 2019  12:04 PM dis\"},\n\t\t{\"43543.503206018519\", \"[$-83B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 19 2019  12:04 PM dis\"},\n\t\t{\"43543.503206018519\", \"[$-83B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"njukčamánnu 19 2019  12:04 PM disdat\"},\n\t\t{\"44562.189571759256\", \"[$-743B]mmm dd yyyy  h:mm AM/PM\", \"ođđee\\u00B4jjmään 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-743B]mmmm dd yyyy  h:mm AM/PM\", \"ođđee\\u00B4jjmään 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-743B]mmmmm dd yyyy  h:mm AM/PM\", \"o 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-743B]mmmmmm dd yyyy  h:mm AM/PM\", \"ođđee\\u00B4jjmään 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-743B]mmm dd yyyy  h:mm AM/PM\", \"pâ\\u00B4zzlâšttam-mään 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-743B]mmmm dd yyyy  h:mm AM/PM aaa\", \"pâ\\u00B4zzlâšttam-mään 19 2019  12:04 PM mâ\"},\n\t\t{\"43543.503206018519\", \"[$-743B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"p 19 2019  12:04 PM mâ\"},\n\t\t{\"43543.503206018519\", \"[$-743B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"pâ\\u00B4zzlâšttam-mään 19 2019  12:04 PM mââibargg\"},\n\t\t{\"44562.189571759256\", \"[$-203B]mmm dd yyyy  h:mm AM/PM\", \"ođđee\\u00B4jjmään 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-203B]mmmm dd yyyy  h:mm AM/PM\", \"ođđee\\u00B4jjmään 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-203B]mmmmm dd yyyy  h:mm AM/PM\", \"o 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-203B]mmmmmm dd yyyy  h:mm AM/PM\", \"ođđee\\u00B4jjmään 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-203B]mmm dd yyyy  h:mm AM/PM\", \"pâ\\u00B4zzlâšttam-mään 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-203B]mmmm dd yyyy  h:mm AM/PM aaa\", \"pâ\\u00B4zzlâšttam-mään 19 2019  12:04 PM mâ\"},\n\t\t{\"43543.503206018519\", \"[$-203B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"p 19 2019  12:04 PM mâ\"},\n\t\t{\"43543.503206018519\", \"[$-203B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"pâ\\u00B4zzlâšttam-mään 19 2019  12:04 PM mââibargg\"},\n\t\t{\"44562.189571759256\", \"[$-783B]mmm dd yyyy  h:mm AM/PM\", \"tsïen 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-783B]mmmm dd yyyy  h:mm AM/PM\", \"tsïengele 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-783B]mmmmm dd yyyy  h:mm AM/PM\", \"t 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-783B]mmmmmm dd yyyy  h:mm AM/PM\", \"tsïengele 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-783B]mmm dd yyyy  h:mm AM/PM\", \"njok 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-783B]mmmm dd yyyy  h:mm AM/PM aaa\", \"njoktje 19 2019  12:04 PM dæj\"},\n\t\t{\"43543.503206018519\", \"[$-783B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 19 2019  12:04 PM dæj\"},\n\t\t{\"43543.503206018519\", \"[$-783B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"njoktje 19 2019  12:04 PM dæjsta\"},\n\t\t{\"44562.189571759256\", \"[$-183B]mmm dd yyyy  h:mm AM/PM\", \"tsïen 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-183B]mmmm dd yyyy  h:mm AM/PM\", \"tsïengele 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-183B]mmmmm dd yyyy  h:mm AM/PM\", \"t 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-183B]mmmmmm dd yyyy  h:mm AM/PM\", \"tsïengele 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-183B]mmm dd yyyy  h:mm AM/PM\", \"njok 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-183B]mmmm dd yyyy  h:mm AM/PM aaa\", \"njoktje 19 2019  12:04 PM dæj\"},\n\t\t{\"43543.503206018519\", \"[$-183B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 19 2019  12:04 PM dæj\"},\n\t\t{\"43543.503206018519\", \"[$-183B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"njoktje 19 2019  12:04 PM dæjsta\"},\n\t\t{\"44562.189571759256\", \"[$-1C3B]mmm dd yyyy  h:mm AM/PM\", \"tsïen 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1C3B]mmmm dd yyyy  h:mm AM/PM\", \"tsïengele 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1C3B]mmmmm dd yyyy  h:mm AM/PM\", \"t 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1C3B]mmmmmm dd yyyy  h:mm AM/PM\", \"tsïengele 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-1C3B]mmm dd yyyy  h:mm AM/PM\", \"njok 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-1C3B]mmmm dd yyyy  h:mm AM/PM aaa\", \"njoktje 19 2019  12:04 PM dæj\"},\n\t\t{\"43543.503206018519\", \"[$-1C3B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"n 19 2019  12:04 PM dæj\"},\n\t\t{\"43543.503206018519\", \"[$-1C3B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"njoktje 19 2019  12:04 PM dæjsta\"},\n\t\t{\"44562.189571759256\", \"[$-4F]mmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u094D\\u092F\\u0941\\u0905\\u0930\\u0940 01 2022  4:32 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u0942\\u0930\\u094D\\u0935\"},\n\t\t{\"44562.189571759256\", \"[$-4F]mmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u094D\\u092F\\u0941\\u0905\\u0930\\u0940 01 2022  4:32 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u0942\\u0930\\u094D\\u0935\"},\n\t\t{\"44562.189571759256\", \"[$-4F]mmmmm dd yyyy  h:mm AM/PM\", \"\\u091C 01 2022  4:32 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u0942\\u0930\\u094D\\u0935\"},\n\t\t{\"44562.189571759256\", \"[$-4F]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u094D\\u092F\\u0941\\u0905\\u0930\\u0940 01 2022  4:32 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u0942\\u0930\\u094D\\u0935\"},\n\t\t{\"43543.503206018519\", \"[$-4F]mmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u091A\\u094D\\u092F\\u093E\\u0924\"},\n\t\t{\"43543.503206018519\", \"[$-4F]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u091A\\u094D\\u092F\\u093E\\u0924 \\u092E\\u0919\\u094D\\u0917\"},\n\t\t{\"43543.503206018519\", \"[$-4F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u092E 19 2019  12:04 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u091A\\u094D\\u092F\\u093E\\u0924 \\u092E\\u0919\\u094D\\u0917\"},\n\t\t{\"43543.503206018519\", \"[$-4F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u091A\\u094D\\u092F\\u093E\\u0924 \\u092E\\u0902\\u0917\\u0932\\u0935\\u093E\\u0938\\u0930\\u0903\"},\n\t\t{\"44562.189571759256\", \"[$-44F]mmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u094D\\u092F\\u0941\\u0905\\u0930\\u0940 01 2022  4:32 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u0942\\u0930\\u094D\\u0935\"},\n\t\t{\"44562.189571759256\", \"[$-44F]mmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u094D\\u092F\\u0941\\u0905\\u0930\\u0940 01 2022  4:32 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u0942\\u0930\\u094D\\u0935\"},\n\t\t{\"44562.189571759256\", \"[$-44F]mmmmm dd yyyy  h:mm AM/PM\", \"\\u091C 01 2022  4:32 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u0942\\u0930\\u094D\\u0935\"},\n\t\t{\"44562.189571759256\", \"[$-44F]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u091C\\u093E\\u0928\\u094D\\u092F\\u0941\\u0905\\u0930\\u0940 01 2022  4:32 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u0942\\u0930\\u094D\\u0935\"},\n\t\t{\"43543.503206018519\", \"[$-44F]mmm dd yyyy  h:mm AM/PM\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u091A\\u094D\\u092F\\u093E\\u0924\"},\n\t\t{\"43543.503206018519\", \"[$-44F]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u091A\\u094D\\u092F\\u093E\\u0924 \\u092E\\u0919\\u094D\\u0917\"},\n\t\t{\"43543.503206018519\", \"[$-44F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u092E 19 2019  12:04 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u091A\\u094D\\u092F\\u093E\\u0924 \\u092E\\u0919\\u094D\\u0917\"},\n\t\t{\"43543.503206018519\", \"[$-44F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u092E\\u093E\\u0930\\u094D\\u091A 19 2019  12:04 \\u092E\\u0927\\u094D\\u092F\\u093E\\u0928\\u092A\\u091A\\u094D\\u092F\\u093E\\u0924 \\u092E\\u0902\\u0917\\u0932\\u0935\\u093E\\u0938\\u0930\\u0903\"},\n\t\t{\"44562.189571759256\", \"[$-91]mmm dd yyyy  h:mm AM/PM\", \"Faoi 01 2022  4:32 m\"},\n\t\t{\"44562.189571759256\", \"[$-91]mmmm dd yyyy  h:mm AM/PM\", \"Am Faoilleach 01 2022  4:32 m\"},\n\t\t{\"44562.189571759256\", \"[$-91]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 m\"},\n\t\t{\"44562.189571759256\", \"[$-91]mmmmmm dd yyyy  h:mm AM/PM\", \"Am Faoilleach 01 2022  4:32 m\"},\n\t\t{\"43543.503206018519\", \"[$-91]mmm dd yyyy  h:mm AM/PM\", \"Màrt 19 2019  12:04 f\"},\n\t\t{\"43543.503206018519\", \"[$-91]mmmm dd yyyy  h:mm AM/PM aaa\", \"Am Màrt 19 2019  12:04 f DiM\"},\n\t\t{\"43543.503206018519\", \"[$-91]mmmmm dd yyyy  h:mm AM/PM ddd\", \"A 19 2019  12:04 f DiM\"},\n\t\t{\"43543.503206018519\", \"[$-91]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Am Màrt 19 2019  12:04 f DiMàirt\"},\n\t\t{\"44562.189571759256\", \"[$-491]mmm dd yyyy  h:mm AM/PM\", \"Faoi 01 2022  4:32 m\"},\n\t\t{\"44562.189571759256\", \"[$-491]mmmm dd yyyy  h:mm AM/PM\", \"Am Faoilleach 01 2022  4:32 m\"},\n\t\t{\"44562.189571759256\", \"[$-491]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 m\"},\n\t\t{\"44562.189571759256\", \"[$-491]mmmmmm dd yyyy  h:mm AM/PM\", \"Am Faoilleach 01 2022  4:32 m\"},\n\t\t{\"43543.503206018519\", \"[$-491]mmm dd yyyy  h:mm AM/PM\", \"Màrt 19 2019  12:04 f\"},\n\t\t{\"43543.503206018519\", \"[$-491]mmmm dd yyyy  h:mm AM/PM aaa\", \"Am Màrt 19 2019  12:04 f DiM\"},\n\t\t{\"43543.503206018519\", \"[$-491]mmmmm dd yyyy  h:mm AM/PM ddd\", \"A 19 2019  12:04 f DiM\"},\n\t\t{\"43543.503206018519\", \"[$-491]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Am Màrt 19 2019  12:04 f DiMàirt\"},\n\t\t{\"44562.189571759256\", \"[$-6C1A]mmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6C1A]mmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6C1A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0458 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6C1A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-6C1A]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-6C1A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442.\"},\n\t\t{\"43543.503206018519\", \"[$-6C1A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0443\\u0442.\"},\n\t\t{\"43543.503206018519\", \"[$-6C1A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442\\u043E\\u0440\\u0430\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-1C1A]mmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1C1A]mmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1C1A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0458 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1C1A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-1C1A]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-1C1A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442\\u043E\"},\n\t\t{\"43543.503206018519\", \"[$-1C1A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0443\\u0442\\u043E\"},\n\t\t{\"43543.503206018519\", \"[$-1C1A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442\\u043E\\u0440\\u0430\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-301A]mmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-301A]mmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-301A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0458 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-301A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-301A]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-301A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442\\u043E\"},\n\t\t{\"43543.503206018519\", \"[$-301A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0443\\u0442\\u043E\"},\n\t\t{\"43543.503206018519\", \"[$-301A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442\\u043E\\u0440\\u0430\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-281A]mmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-281A]mmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-281A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0458 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-281A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-281A]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-281A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442.\"},\n\t\t{\"43543.503206018519\", \"[$-281A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0443\\u0442.\"},\n\t\t{\"43543.503206018519\", \"[$-281A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442\\u043E\\u0440\\u0430\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-C1A]mmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-C1A]mmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-C1A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0458 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-C1A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0458\\u0430\\u043D\\u0443\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-C1A]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-C1A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442.\"},\n\t\t{\"43543.503206018519\", \"[$-C1A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0443\\u0442.\"},\n\t\t{\"43543.503206018519\", \"[$-C1A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0443\\u0442\\u043E\\u0440\\u0430\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-701A]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 pre podne\"},\n\t\t{\"44562.189571759256\", \"[$-701A]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 pre podne\"},\n\t\t{\"44562.189571759256\", \"[$-701A]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 pre podne\"},\n\t\t{\"44562.189571759256\", \"[$-701A]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 pre podne\"},\n\t\t{\"43543.503206018519\", \"[$-701A]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 po podne\"},\n\t\t{\"43543.503206018519\", \"[$-701A]mmmm dd yyyy  h:mm AM/PM aaa\", \"mart 19 2019  12:04 po podne uto\"},\n\t\t{\"43543.503206018519\", \"[$-701A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 po podne uto\"},\n\t\t{\"43543.503206018519\", \"[$-701A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mart 19 2019  12:04 po podne utorak\"},\n\t\t{\"44562.189571759256\", \"[$-7C1A]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 pre podne\"},\n\t\t{\"44562.189571759256\", \"[$-7C1A]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 pre podne\"},\n\t\t{\"44562.189571759256\", \"[$-7C1A]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 pre podne\"},\n\t\t{\"44562.189571759256\", \"[$-7C1A]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 pre podne\"},\n\t\t{\"43543.503206018519\", \"[$-7C1A]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 po podne\"},\n\t\t{\"43543.503206018519\", \"[$-7C1A]mmmm dd yyyy  h:mm AM/PM aaa\", \"mart 19 2019  12:04 po podne uto\"},\n\t\t{\"43543.503206018519\", \"[$-7C1A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 po podne uto\"},\n\t\t{\"43543.503206018519\", \"[$-7C1A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mart 19 2019  12:04 po podne utorak\"},\n\t\t{\"44562.189571759256\", \"[$-181A]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 prije podne\"},\n\t\t{\"44562.189571759256\", \"[$-181A]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 prije podne\"},\n\t\t{\"44562.189571759256\", \"[$-181A]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 prije podne\"},\n\t\t{\"44562.189571759256\", \"[$-181A]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 prije podne\"},\n\t\t{\"43543.503206018519\", \"[$-181A]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 po podne\"},\n\t\t{\"43543.503206018519\", \"[$-181A]mmmm dd yyyy  h:mm AM/PM aaa\", \"mart 19 2019  12:04 po podne uto\"},\n\t\t{\"43543.503206018519\", \"[$-181A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 po podne uto\"},\n\t\t{\"43543.503206018519\", \"[$-181A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mart 19 2019  12:04 po podne utorak\"},\n\t\t{\"44562.189571759256\", \"[$-2C1A]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 prije podne\"},\n\t\t{\"44562.189571759256\", \"[$-2C1A]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 prije podne\"},\n\t\t{\"44562.189571759256\", \"[$-2C1A]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 prije podne\"},\n\t\t{\"44562.189571759256\", \"[$-2C1A]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 prije podne\"},\n\t\t{\"43543.503206018519\", \"[$-2C1A]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 po podne\"},\n\t\t{\"43543.503206018519\", \"[$-2C1A]mmmm dd yyyy  h:mm AM/PM aaa\", \"mart 19 2019  12:04 po podne uto\"},\n\t\t{\"43543.503206018519\", \"[$-2C1A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 po podne uto\"},\n\t\t{\"43543.503206018519\", \"[$-2C1A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mart 19 2019  12:04 po podne utorak\"},\n\t\t{\"44562.189571759256\", \"[$-241A]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 pre podne\"},\n\t\t{\"44562.189571759256\", \"[$-241A]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 pre podne\"},\n\t\t{\"44562.189571759256\", \"[$-241A]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 pre podne\"},\n\t\t{\"44562.189571759256\", \"[$-241A]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 pre podne\"},\n\t\t{\"43543.503206018519\", \"[$-241A]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 po podne\"},\n\t\t{\"43543.503206018519\", \"[$-241A]mmmm dd yyyy  h:mm AM/PM aaa\", \"mart 19 2019  12:04 po podne uto\"},\n\t\t{\"43543.503206018519\", \"[$-241A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 po podne uto\"},\n\t\t{\"43543.503206018519\", \"[$-241A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mart 19 2019  12:04 po podne utorak\"},\n\t\t{\"44562.189571759256\", \"[$-81A]mmm dd yyyy  h:mm AM/PM\", \"jan. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-81A]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-81A]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-81A]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-81A]mmm dd yyyy  h:mm AM/PM\", \"mart 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-81A]mmmm dd yyyy  h:mm AM/PM aaa\", \"mart 19 2019  12:04 PM uto.\"},\n\t\t{\"43543.503206018519\", \"[$-81A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM uto.\"},\n\t\t{\"43543.503206018519\", \"[$-81A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mart 19 2019  12:04 PM utorak\"},\n\t\t{\"44562.189571759256\", \"[$-6C]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6C]mmmm dd yyyy  h:mm AM/PM\", \"Janaware 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6C]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-6C]mmmmmm dd yyyy  h:mm AM/PM\", \"Janaware 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-6C]mmm dd yyyy  h:mm AM/PM\", \"Matš 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-6C]mmmm dd yyyy  h:mm AM/PM aaa\", \"Matšhe 19 2019  12:04 PM Lbb\"},\n\t\t{\"43543.503206018519\", \"[$-6C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Lbb\"},\n\t\t{\"43543.503206018519\", \"[$-6C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Matšhe 19 2019  12:04 PM Labobedi\"},\n\t\t{\"44562.189571759256\", \"[$-46C]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-46C]mmmm dd yyyy  h:mm AM/PM\", \"Janaware 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-46C]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-46C]mmmmmm dd yyyy  h:mm AM/PM\", \"Janaware 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-46C]mmm dd yyyy  h:mm AM/PM\", \"Matš 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-46C]mmmm dd yyyy  h:mm AM/PM aaa\", \"Matšhe 19 2019  12:04 PM Lbb\"},\n\t\t{\"43543.503206018519\", \"[$-46C]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Lbb\"},\n\t\t{\"43543.503206018519\", \"[$-46C]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Matšhe 19 2019  12:04 PM Labobedi\"},\n\t\t{\"44562.189571759256\", \"[$-32]mmm dd yyyy  h:mm AM/PM\", \"Fer. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-32]mmmm dd yyyy  h:mm AM/PM\", \"Ferikgong 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-32]mmmmm dd yyyy  h:mm AM/PM\", \"F 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-32]mmmmmm dd yyyy  h:mm AM/PM\", \"Ferikgong 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-32]mmm dd yyyy  h:mm AM/PM\", \"Mop. 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-32]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mopitlwe 19 2019  12:04 PM Lab.\"},\n\t\t{\"43543.503206018519\", \"[$-32]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Lab.\"},\n\t\t{\"43543.503206018519\", \"[$-32]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mopitlwe 19 2019  12:04 PM Labobedi\"},\n\t\t{\"44562.189571759256\", \"[$-832]mmm dd yyyy  h:mm AM/PM\", \"Fer. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-832]mmmm dd yyyy  h:mm AM/PM\", \"Ferikgong 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-832]mmmmm dd yyyy  h:mm AM/PM\", \"F 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-832]mmmmmm dd yyyy  h:mm AM/PM\", \"Ferikgong 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-832]mmm dd yyyy  h:mm AM/PM\", \"Mop. 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-832]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mopitlwe 19 2019  12:04 PM Lab.\"},\n\t\t{\"43543.503206018519\", \"[$-832]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Lab.\"},\n\t\t{\"43543.503206018519\", \"[$-832]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mopitlwe 19 2019  12:04 PM Labobedi\"},\n\t\t{\"44562.189571759256\", \"[$-432]mmm dd yyyy  h:mm AM/PM\", \"Fer. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-432]mmmm dd yyyy  h:mm AM/PM\", \"Ferikgong 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-432]mmmmm dd yyyy  h:mm AM/PM\", \"F 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-432]mmmmmm dd yyyy  h:mm AM/PM\", \"Ferikgong 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-432]mmm dd yyyy  h:mm AM/PM\", \"Mop. 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-432]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mopitlwe 19 2019  12:04 PM Lab.\"},\n\t\t{\"43543.503206018519\", \"[$-432]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Lab.\"},\n\t\t{\"43543.503206018519\", \"[$-432]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mopitlwe 19 2019  12:04 PM Labobedi\"},\n\t\t{\"44562.189571759256\", \"[$-59]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u064A 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-59]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u064A 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-59]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-59]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u064A 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-59]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-59]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-59]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 PM \\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-59]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0627\\u0631\\u0628\\u0639\"},\n\t\t{\"44562.189571759256\", \"[$-7C59]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u064A 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C59]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u064A 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C59]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C59]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u064A 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-7C59]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-7C59]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-7C59]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 PM \\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-7C59]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0627\\u0631\\u0628\\u0639\"},\n\t\t{\"44562.189571759256\", \"[$-859]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u064A 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-859]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u064A 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-859]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-859]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u064A 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-859]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-859]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-859]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 PM \\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-859]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0627\\u0631\\u0628\\u0639\"},\n\t\t{\"44562.189571759256\", \"[$-5B]mmm dd yyyy  h:mm AM/PM\", \"\\u0DA2\\u0DB1. 01 2022  4:32 \\u0DB4\\u0DD9.\\u0DC0.\"},\n\t\t{\"44562.189571759256\", \"[$-5B]mmmm dd yyyy  h:mm AM/PM\", \"\\u0DA2\\u0DB1\\u0DC0\\u0DCF\\u0DBB\\u0DD2 01 2022  4:32 \\u0DB4\\u0DD9.\\u0DC0.\"},\n\t\t{\"44562.189571759256\", \"[$-5B]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0DA2 01 2022  4:32 \\u0DB4\\u0DD9.\\u0DC0.\"},\n\t\t{\"44562.189571759256\", \"[$-5B]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0DA2\\u0DB1\\u0DC0\\u0DCF\\u0DBB\\u0DD2 01 2022  4:32 \\u0DB4\\u0DD9.\\u0DC0.\"},\n\t\t{\"43543.503206018519\", \"[$-5B]mmm dd yyyy  h:mm AM/PM\", \"\\u0DB8\\u0DCF\\u0DBB\\u0DCA\\u0DAD\\u0DD4. 19 2019  12:04 \\u0DB4.\\u0DC0.\"},\n\t\t{\"43543.503206018519\", \"[$-5B]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0DB8\\u0DCF\\u0DBB\\u0DCA\\u0DAD\\u0DD4 19 2019  12:04 \\u0DB4.\\u0DC0. \\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-5B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0DB8 19 2019  12:04 \\u0DB4.\\u0DC0. \\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-5B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0DB8\\u0DCF\\u0DBB\\u0DCA\\u0DAD\\u0DD4 19 2019  12:04 \\u0DB4.\\u0DC0. \\u0627\\u0631\\u0628\\u0639\"},\n\t\t{\"44562.189571759256\", \"[$-45B]mmm dd yyyy  h:mm AM/PM\", \"\\u0DA2\\u0DB1. 01 2022  4:32 \\u0DB4\\u0DD9.\\u0DC0.\"},\n\t\t{\"44562.189571759256\", \"[$-45B]mmmm dd yyyy  h:mm AM/PM\", \"\\u0DA2\\u0DB1\\u0DC0\\u0DCF\\u0DBB\\u0DD2 01 2022  4:32 \\u0DB4\\u0DD9.\\u0DC0.\"},\n\t\t{\"44562.189571759256\", \"[$-45B]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0DA2 01 2022  4:32 \\u0DB4\\u0DD9.\\u0DC0.\"},\n\t\t{\"44562.189571759256\", \"[$-45B]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0DA2\\u0DB1\\u0DC0\\u0DCF\\u0DBB\\u0DD2 01 2022  4:32 \\u0DB4\\u0DD9.\\u0DC0.\"},\n\t\t{\"43543.503206018519\", \"[$-45B]mmm dd yyyy  h:mm AM/PM\", \"\\u0DB8\\u0DCF\\u0DBB\\u0DCA\\u0DAD\\u0DD4. 19 2019  12:04 \\u0DB4.\\u0DC0.\"},\n\t\t{\"43543.503206018519\", \"[$-45B]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0DB8\\u0DCF\\u0DBB\\u0DCA\\u0DAD\\u0DD4 19 2019  12:04 \\u0DB4.\\u0DC0. \\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-45B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0DB8 19 2019  12:04 \\u0DB4.\\u0DC0. \\u0627\\u0631\"},\n\t\t{\"43543.503206018519\", \"[$-45B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0DB8\\u0DCF\\u0DBB\\u0DCA\\u0DAD\\u0DD4 19 2019  12:04 \\u0DB4.\\u0DC0. \\u0627\\u0631\\u0628\\u0639\"},\n\t\t{\"44562.189571759256\", \"[$-1B]mmm dd yyyy  h:mm AM/PM\", \"1 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1B]mmmm dd yyyy  h:mm AM/PM\", \"január 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1B]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1B]mmmmmm dd yyyy  h:mm AM/PM\", \"január 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-1B]mmm dd yyyy  h:mm AM/PM\", \"3 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-1B]mmmm dd yyyy  h:mm AM/PM aaa\", \"marec 19 2019  12:04 PM ut\"},\n\t\t{\"43543.503206018519\", \"[$-1B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM ut\"},\n\t\t{\"43543.503206018519\", \"[$-1B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"marec 19 2019  12:04 PM utorok\"},\n\t\t{\"44562.189571759256\", \"[$-41B]mmm dd yyyy  h:mm AM/PM\", \"1 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41B]mmmm dd yyyy  h:mm AM/PM\", \"január 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41B]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41B]mmmmmm dd yyyy  h:mm AM/PM\", \"január 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-41B]mmm dd yyyy  h:mm AM/PM\", \"3 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-41B]mmmm dd yyyy  h:mm AM/PM aaa\", \"marec 19 2019  12:04 PM ut\"},\n\t\t{\"43543.503206018519\", \"[$-41B]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM ut\"},\n\t\t{\"43543.503206018519\", \"[$-41B]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"marec 19 2019  12:04 PM utorok\"},\n\t\t{\"44562.189571759256\", \"[$-24]mmm dd yyyy  h:mm AM/PM\", \"jan. 01 2022  4:32 dop.\"},\n\t\t{\"44562.189571759256\", \"[$-24]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 dop.\"},\n\t\t{\"44562.189571759256\", \"[$-24]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 dop.\"},\n\t\t{\"44562.189571759256\", \"[$-24]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 dop.\"},\n\t\t{\"43543.503206018519\", \"[$-24]mmm dd yyyy  h:mm AM/PM\", \"mar. 19 2019  12:04 pop.\"},\n\t\t{\"43543.503206018519\", \"[$-24]mmmm dd yyyy  h:mm AM/PM aaa\", \"marec 19 2019  12:04 pop. tor.\"},\n\t\t{\"43543.503206018519\", \"[$-24]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 pop. tor.\"},\n\t\t{\"43543.503206018519\", \"[$-24]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"marec 19 2019  12:04 pop. torek\"},\n\t\t{\"44562.189571759256\", \"[$-424]mmm dd yyyy  h:mm AM/PM\", \"jan. 01 2022  4:32 dop.\"},\n\t\t{\"44562.189571759256\", \"[$-424]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 dop.\"},\n\t\t{\"44562.189571759256\", \"[$-424]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 dop.\"},\n\t\t{\"44562.189571759256\", \"[$-424]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 dop.\"},\n\t\t{\"43543.503206018519\", \"[$-424]mmm dd yyyy  h:mm AM/PM\", \"mar. 19 2019  12:04 pop.\"},\n\t\t{\"43543.503206018519\", \"[$-424]mmmm dd yyyy  h:mm AM/PM aaa\", \"marec 19 2019  12:04 pop. tor.\"},\n\t\t{\"43543.503206018519\", \"[$-424]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 pop. tor.\"},\n\t\t{\"43543.503206018519\", \"[$-424]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"marec 19 2019  12:04 pop. torek\"},\n\t\t{\"44562.189571759256\", \"[$-77]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 GH\"},\n\t\t{\"44562.189571759256\", \"[$-77]mmmm dd yyyy  h:mm AM/PM\", \"Jannaayo 01 2022  4:32 GH\"},\n\t\t{\"44562.189571759256\", \"[$-77]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 GH\"},\n\t\t{\"44562.189571759256\", \"[$-77]mmmmmm dd yyyy  h:mm AM/PM\", \"Jannaayo 01 2022  4:32 GH\"},\n\t\t{\"43543.503206018519\", \"[$-77]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 GD\"},\n\t\t{\"43543.503206018519\", \"[$-77]mmmm dd yyyy  h:mm AM/PM aaa\", \"Maarso 19 2019  12:04 GD Tldo\"},\n\t\t{\"43543.503206018519\", \"[$-77]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 GD Tldo\"},\n\t\t{\"43543.503206018519\", \"[$-77]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Maarso 19 2019  12:04 GD Talaado\"},\n\t\t{\"44562.189571759256\", \"[$-477]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 GH\"},\n\t\t{\"44562.189571759256\", \"[$-477]mmmm dd yyyy  h:mm AM/PM\", \"Jannaayo 01 2022  4:32 GH\"},\n\t\t{\"44562.189571759256\", \"[$-477]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 GH\"},\n\t\t{\"44562.189571759256\", \"[$-477]mmmmmm dd yyyy  h:mm AM/PM\", \"Jannaayo 01 2022  4:32 GH\"},\n\t\t{\"43543.503206018519\", \"[$-477]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 GD\"},\n\t\t{\"43543.503206018519\", \"[$-477]mmmm dd yyyy  h:mm AM/PM aaa\", \"Maarso 19 2019  12:04 GD Tldo\"},\n\t\t{\"43543.503206018519\", \"[$-477]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 GD Tldo\"},\n\t\t{\"43543.503206018519\", \"[$-477]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Maarso 19 2019  12:04 GD Talaado\"},\n\t\t{\"44562.189571759256\", \"[$-30]mmm dd yyyy  h:mm AM/PM\", \"Phe 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-30]mmmm dd yyyy  h:mm AM/PM\", \"Phesekgong 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-30]mmmmm dd yyyy  h:mm AM/PM\", \"P 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-30]mmmmmm dd yyyy  h:mm AM/PM\", \"Phesekgong 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-30]mmm dd yyyy  h:mm AM/PM\", \"Ube 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-30]mmmm dd yyyy  h:mm AM/PM aaa\", \"Hlakubele 19 2019  12:04 PM Bed\"},\n\t\t{\"43543.503206018519\", \"[$-30]mmmmm dd yyyy  h:mm AM/PM ddd\", \"H 19 2019  12:04 PM Bed\"},\n\t\t{\"43543.503206018519\", \"[$-30]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Hlakubele 19 2019  12:04 PM Labobedi\"},\n\t\t{\"44562.189571759256\", \"[$-430]mmm dd yyyy  h:mm AM/PM\", \"Phe 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-430]mmmm dd yyyy  h:mm AM/PM\", \"Phesekgong 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-430]mmmmm dd yyyy  h:mm AM/PM\", \"P 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-430]mmmmmm dd yyyy  h:mm AM/PM\", \"Phesekgong 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-430]mmm dd yyyy  h:mm AM/PM\", \"Ube 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-430]mmmm dd yyyy  h:mm AM/PM aaa\", \"Hlakubele 19 2019  12:04 PM Bed\"},\n\t\t{\"43543.503206018519\", \"[$-430]mmmmm dd yyyy  h:mm AM/PM ddd\", \"H 19 2019  12:04 PM Bed\"},\n\t\t{\"43543.503206018519\", \"[$-430]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Hlakubele 19 2019  12:04 PM Labobedi\"},\n\t\t{\"44562.189571759256\", \"[$-A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-A]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-A]mmmm dd yyyy  h:mm AM/PM\", \"marzo 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-A]mmmmm dd yyyy  h:mm AM/PM\", \"m 19 2019  12:04 p. m.\"},\n\t\t{\"44562.189571759256\", \"[$-A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-A]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-A]mmmm dd yyyy  h:mm AM/PM\", \"marzo 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-A]mmmmm dd yyyy  h:mm AM/PM\", \"m 19 2019  12:04 p. m.\"},\n\t\t{\"44562.189571759256\", \"[$-A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. ma.\"},\n\t\t{\"43543.503206018519\", \"[$-A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. ma.\"},\n\t\t{\"43543.503206018519\", \"[$-A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"44562.189571759256\", \"[$-2C0A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-2C0A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-2C0A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-2C0A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-2C0A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-2C0A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"44562.189571759256\", \"[$-200A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-200A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-200A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-200A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-200A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-200A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"44562.189571759256\", \"[$-400A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-400A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-400A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-400A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-400A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-400A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"44562.189571759256\", \"[$-340A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-340A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-340A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-340A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-340A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-340A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"44562.189571759256\", \"[$-240A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-240A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-240A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-240A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-240A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-240A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"44562.189571759256\", \"[$-140A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-140A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-140A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-140A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-140A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-140A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"44562.189571759256\", \"[$-5C0A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-5C0A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a.m.\"},\n\t\t{\"44562.189571759256\", \"[$-5C0A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a.m.\"},\n\t\t{\"43543.503206018519\", \"[$-5C0A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p.m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-5C0A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p.m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-5C0A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p.m. martes\"},\n\t\t{\"43543.503206018519\", \"[$-1C0A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-1C0A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-1C0A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"43543.503206018519\", \"[$-300A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-300A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-300A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"43543.503206018519\", \"[$-440A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-440A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-440A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"43543.503206018519\", \"[$-100A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-100A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-100A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"43543.503206018519\", \"[$-480A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-480A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-480A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"43543.503206018519\", \"[$-580A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p.m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-580A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p.m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-580A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p.m. martes\"},\n\t\t{\"44562.189571759256\", \"[$-80A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-80A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-80A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-80A]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-80A]mmmm dd yyyy  h:mm AM/PM\", \"marzo 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-80A]mmmmm dd yyyy  h:mm AM/PM\", \"m 19 2019  12:04 p. m.\"},\n\t\t{\"44562.189571759256\", \"[$-80A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-80A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-80A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-80A]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-80A]mmmm dd yyyy  h:mm AM/PM\", \"marzo 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-80A]mmmmm dd yyyy  h:mm AM/PM\", \"m 19 2019  12:04 p. m.\"},\n\t\t{\"44562.189571759256\", \"[$-80A]mmm dd yyyy  h:mm AM/PM\", \"ene 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-80A]mmmm dd yyyy  h:mm AM/PM\", \"enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-80A]mmmmm dd yyyy  h:mm AM/PM\", \"e 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-80A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-80A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-80A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"43543.503206018519\", \"[$-4C0A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-4C0A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-4C0A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"43543.503206018519\", \"[$-180A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-180A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-180A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"43543.503206018519\", \"[$-3C0A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-3C0A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-3C0A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"44562.189571759256\", \"[$-280A]mmm dd yyyy  h:mm AM/PM\", \"Ene. 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-280A]mmmm dd yyyy  h:mm AM/PM\", \"Enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-280A]mmmmm dd yyyy  h:mm AM/PM\", \"E 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-280A]mmmmmm dd yyyy  h:mm AM/PM\", \"Enero 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-280A]mmm dd yyyy  h:mm AM/PM\", \"Mar. 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-280A]mmmm dd yyyy  h:mm AM/PM aaa\", \"Marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-280A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-280A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Marzo 19 2019  12:04 p. m. martes\"},\n\t\t{\"43543.503206018519\", \"[$-500A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-500A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-500A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 p. m. martes\"},\n\t\t{\"43543.503206018519\", \"[$-40A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 PM ma.\"},\n\t\t{\"43543.503206018519\", \"[$-40A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 PM ma.\"},\n\t\t{\"43543.503206018519\", \"[$-40A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM martes\"},\n\t\t{\"43543.503206018519\", \"[$-C0A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 PM ma.\"},\n\t\t{\"43543.503206018519\", \"[$-C0A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 PM ma.\"},\n\t\t{\"43543.503206018519\", \"[$-C0A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM martes\"},\n\t\t{\"43543.503206018519\", \"[$-540A]mmm dd yyyy  h:mm AM/PM aaa\", \"mar 19 2019  12:04 PM mar\"},\n\t\t{\"43543.503206018519\", \"[$-540A]mmmm dd yyyy  h:mm AM/PM ddd\", \"marzo 19 2019  12:04 PM mar\"},\n\t\t{\"43543.503206018519\", \"[$-540A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"m 19 2019  12:04 PM martes\"},\n\t\t{\"44562.189571759256\", \"[$-380A]mmm dd yyyy  h:mm AM/PM\", \"Ene. 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-380A]mmmm dd yyyy  h:mm AM/PM\", \"Enero 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-380A]mmmmm dd yyyy  h:mm AM/PM\", \"E 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-380A]mmmmmm dd yyyy  h:mm AM/PM\", \"Enero 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-380A]mmm dd yyyy  h:mm AM/PM\", \"Mar. 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-380A]mmmm dd yyyy  h:mm AM/PM aaa\", \"Marzo 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-380A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 p. m. mar.\"},\n\t\t{\"43543.503206018519\", \"[$-380A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Marzo 19 2019  12:04 p. m. martes\"},\n\t\t{\"44562.189571759256\", \"[$-1D]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1D]mmmm dd yyyy  h:mm AM/PM\", \"januari 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1D]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1D]mmmmmm dd yyyy  h:mm AM/PM\", \"januari 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-1D]mmm dd yyyy  h:mm AM/PM\", \"mar 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-1D]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM tis\"},\n\t\t{\"43543.503206018519\", \"[$-1D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM tis\"},\n\t\t{\"43543.503206018519\", \"[$-1D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 PM tisdag\"},\n\t\t{\"44562.189571759256\", \"[$-81D]mmm dd yyyy  h:mm AM/PM\", \"jan. 01 2022  4:32 fm\"},\n\t\t{\"44562.189571759256\", \"[$-81D]mmmm dd yyyy  h:mm AM/PM\", \"januari 01 2022  4:32 fm\"},\n\t\t{\"44562.189571759256\", \"[$-81D]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 fm\"},\n\t\t{\"44562.189571759256\", \"[$-81D]mmmmmm dd yyyy  h:mm AM/PM\", \"januari 01 2022  4:32 fm\"},\n\t\t{\"43543.503206018519\", \"[$-81D]mmm dd yyyy  h:mm AM/PM\", \"mars 19 2019  12:04 em\"},\n\t\t{\"43543.503206018519\", \"[$-81D]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 em tis\"},\n\t\t{\"43543.503206018519\", \"[$-81D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 em tis\"},\n\t\t{\"43543.503206018519\", \"[$-81D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 em tisdag\"},\n\t\t{\"44562.189571759256\", \"[$-41D]mmm dd yyyy  h:mm AM/PM\", \"jan. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41D]mmmm dd yyyy  h:mm AM/PM\", \"januari 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41D]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41D]mmmmmm dd yyyy  h:mm AM/PM\", \"januari 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-41D]mmm dd yyyy  h:mm AM/PM\", \"mars 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-41D]mmmm dd yyyy  h:mm AM/PM aaa\", \"mars 19 2019  12:04 PM tis\"},\n\t\t{\"43543.503206018519\", \"[$-41D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 PM tis\"},\n\t\t{\"43543.503206018519\", \"[$-41D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"mars 19 2019  12:04 PM tisdag\"},\n\t\t{\"44562.189571759256\", \"[$-5A]mmm dd yyyy  h:mm AM/PM\", \"\\u071F\\u0722 \\u070F\\u0712 01 2022  4:32 \\u0729.\\u071B\"},\n\t\t{\"44562.189571759256\", \"[$-5A]mmmm dd yyyy  h:mm AM/PM\", \"\\u071F\\u0722\\u0718\\u0722 \\u0710\\u071A\\u072A\\u071D 01 2022  4:32 \\u0729.\\u071B\"},\n\t\t{\"44562.189571759256\", \"[$-5A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u071F 01 2022  4:32 \\u0729.\\u071B\"},\n\t\t{\"44562.189571759256\", \"[$-5A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u071F\\u0722\\u0718\\u0722 \\u0710\\u071A\\u072A\\u071D 01 2022  4:32 \\u0729.\\u071B\"},\n\t\t{\"43543.503206018519\", \"[$-5A]mmm dd yyyy  h:mm AM/PM\", \"\\u0710\\u0715\\u072A 19 2019  12:04 \\u0712.\\u071B\"},\n\t\t{\"43543.503206018519\", \"[$-5A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0710\\u0715\\u072A 19 2019  12:04 \\u0712.\\u071B \\u070F\\u0713 \\u070F\\u0712\\u072B\"},\n\t\t{\"43543.503206018519\", \"[$-5A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0710 19 2019  12:04 \\u0712.\\u071B \\u070F\\u0713 \\u070F\\u0712\\u072B\"},\n\t\t{\"43543.503206018519\", \"[$-5A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0710\\u0715\\u072A 19 2019  12:04 \\u0712.\\u071B \\u072C\\u0720\\u072C\\u0710 \\u0712\\u072B\\u0712\\u0710\"},\n\t\t{\"44562.189571759256\", \"[$-45A]mmm dd yyyy  h:mm AM/PM\", \"\\u071F\\u0722 \\u070F\\u0712 01 2022  4:32 \\u0729.\\u071B\"},\n\t\t{\"44562.189571759256\", \"[$-45A]mmmm dd yyyy  h:mm AM/PM\", \"\\u071F\\u0722\\u0718\\u0722 \\u0710\\u071A\\u072A\\u071D 01 2022  4:32 \\u0729.\\u071B\"},\n\t\t{\"44562.189571759256\", \"[$-45A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u071F 01 2022  4:32 \\u0729.\\u071B\"},\n\t\t{\"44562.189571759256\", \"[$-45A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u071F\\u0722\\u0718\\u0722 \\u0710\\u071A\\u072A\\u071D 01 2022  4:32 \\u0729.\\u071B\"},\n\t\t{\"43543.503206018519\", \"[$-45A]mmm dd yyyy  h:mm AM/PM\", \"\\u0710\\u0715\\u072A 19 2019  12:04 \\u0712.\\u071B\"},\n\t\t{\"43543.503206018519\", \"[$-45A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0710\\u0715\\u072A 19 2019  12:04 \\u0712.\\u071B \\u070F\\u0713 \\u070F\\u0712\\u072B\"},\n\t\t{\"43543.503206018519\", \"[$-45A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0710 19 2019  12:04 \\u0712.\\u071B \\u070F\\u0713 \\u070F\\u0712\\u072B\"},\n\t\t{\"43543.503206018519\", \"[$-45A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0710\\u0715\\u072A 19 2019  12:04 \\u0712.\\u071B \\u072C\\u0720\\u072C\\u0710 \\u0712\\u072B\\u0712\\u0710\"},\n\t\t{\"44562.189571759256\", \"[$-28]mmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-28]mmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-28]mmmmm dd yyyy  h:mm AM/PM\", \"\\u044F 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-28]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-28]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-28]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0441\\u0448\\u0431\"},\n\t\t{\"43543.503206018519\", \"[$-28]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0441\\u0448\\u0431\"},\n\t\t{\"43543.503206018519\", \"[$-28]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0441\\u0435\\u0448\\u0430\\u043D\\u0431\\u0435\"},\n\t\t{\"44562.189571759256\", \"[$-7C28]mmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C28]mmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C28]mmmmm dd yyyy  h:mm AM/PM\", \"\\u044F 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C28]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-7C28]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-7C28]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0441\\u0448\\u0431\"},\n\t\t{\"43543.503206018519\", \"[$-7C28]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0441\\u0448\\u0431\"},\n\t\t{\"43543.503206018519\", \"[$-7C28]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0441\\u0435\\u0448\\u0430\\u043D\\u0431\\u0435\"},\n\t\t{\"44562.189571759256\", \"[$-428]mmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-428]mmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-428]mmmmm dd yyyy  h:mm AM/PM\", \"\\u044F 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-428]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-428]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-428]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0441\\u0448\\u0431\"},\n\t\t{\"43543.503206018519\", \"[$-428]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0441\\u0448\\u0431\"},\n\t\t{\"43543.503206018519\", \"[$-428]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0441\\u0435\\u0448\\u0430\\u043D\\u0431\\u0435\"},\n\t\t{\"44562.189571759256\", \"[$-5F]mmm dd yyyy  h:mm AM/PM\", \"Yen 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-5F]mmmm dd yyyy  h:mm AM/PM\", \"Yennayer 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-5F]mmmmm dd yyyy  h:mm AM/PM\", \"Y 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-5F]mmmmmm dd yyyy  h:mm AM/PM\", \"Yennayer 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-5F]mmm dd yyyy  h:mm AM/PM\", \"Megh 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-5F]mmmm dd yyyy  h:mm AM/PM aaa\", \"Meghres 19 2019  12:04 PM ttl\"},\n\t\t{\"43543.503206018519\", \"[$-5F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM ttl\"},\n\t\t{\"43543.503206018519\", \"[$-5F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Meghres 19 2019  12:04 PM ttlata\"},\n\t\t{\"44562.189571759256\", \"[$-7C5F]mmm dd yyyy  h:mm AM/PM\", \"Yen 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C5F]mmmm dd yyyy  h:mm AM/PM\", \"Yennayer 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C5F]mmmmm dd yyyy  h:mm AM/PM\", \"Y 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-7C5F]mmmmmm dd yyyy  h:mm AM/PM\", \"Yennayer 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-7C5F]mmm dd yyyy  h:mm AM/PM\", \"Megh 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-7C5F]mmmm dd yyyy  h:mm AM/PM aaa\", \"Meghres 19 2019  12:04 PM ttl\"},\n\t\t{\"43543.503206018519\", \"[$-7C5F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM ttl\"},\n\t\t{\"43543.503206018519\", \"[$-7C5F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Meghres 19 2019  12:04 PM ttlata\"},\n\t\t{\"44562.189571759256\", \"[$-85F]mmm dd yyyy  h:mm AM/PM\", \"Yen 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-85F]mmmm dd yyyy  h:mm AM/PM\", \"Yennayer 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-85F]mmmmm dd yyyy  h:mm AM/PM\", \"Y 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-85F]mmmmmm dd yyyy  h:mm AM/PM\", \"Yennayer 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-85F]mmm dd yyyy  h:mm AM/PM\", \"Megh 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-85F]mmmm dd yyyy  h:mm AM/PM aaa\", \"Meghres 19 2019  12:04 PM ttl\"},\n\t\t{\"43543.503206018519\", \"[$-85F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM ttl\"},\n\t\t{\"43543.503206018519\", \"[$-85F]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Meghres 19 2019  12:04 PM ttlata\"},\n\t\t{\"44562.189571759256\", \"[$-49]mmm dd yyyy  h:mm AM/PM\", \"\\u0B9C\\u0BA9\\u0BB5\\u0BB0\\u0BBF 01 2022  4:32 \\u0B95\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"44562.189571759256\", \"[$-49]mmmm dd yyyy  h:mm AM/PM\", \"\\u0B9C\\u0BA9\\u0BB5\\u0BB0\\u0BBF 01 2022  4:32 \\u0B95\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"44562.189571759256\", \"[$-49]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0B9C 01 2022  4:32 \\u0B95\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"44562.189571759256\", \"[$-49]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0B9C\\u0BA9\\u0BB5\\u0BB0\\u0BBF 01 2022  4:32 \\u0B95\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"43543.503206018519\", \"[$-49]mmm dd yyyy  h:mm AM/PM\", \"\\u0BAE\\u0BBE\\u0BB0\\u0BCD\\u0B9A\\u0BCD 19 2019  12:04 \\u0BAE\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"43543.503206018519\", \"[$-49]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0BAE\\u0BBE\\u0BB0\\u0BCD\\u0B9A\\u0BCD 19 2019  12:04 \\u0BAE\\u0BBE\\u0BB2\\u0BC8 \\u0B9A\\u0BC6\\u0BB5\\u0BCD\\u0BB5\\u0BBE\\u0BAF\\u0BCD\"},\n\t\t{\"43543.503206018519\", \"[$-49]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0BAE 19 2019  12:04 \\u0BAE\\u0BBE\\u0BB2\\u0BC8 \\u0B9A\\u0BC6\\u0BB5\\u0BCD\\u0BB5\\u0BBE\\u0BAF\\u0BCD\"},\n\t\t{\"43543.503206018519\", \"[$-49]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0BAE\\u0BBE\\u0BB0\\u0BCD\\u0B9A\\u0BCD 19 2019  12:04 \\u0BAE\\u0BBE\\u0BB2\\u0BC8 \\u0B9A\\u0BC6\\u0BB5\\u0BCD\\u0BB5\\u0BBE\\u0BAF\\u0BCD\\u0B95\\u0BCD\\u0B95\\u0BBF\\u0BB4\\u0BAE\\u0BC8\"},\n\t\t{\"44562.189571759256\", \"[$-449]mmm dd yyyy  h:mm AM/PM\", \"\\u0B9C\\u0BA9\\u0BB5\\u0BB0\\u0BBF 01 2022  4:32 \\u0B95\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"44562.189571759256\", \"[$-449]mmmm dd yyyy  h:mm AM/PM\", \"\\u0B9C\\u0BA9\\u0BB5\\u0BB0\\u0BBF 01 2022  4:32 \\u0B95\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"44562.189571759256\", \"[$-449]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0B9C 01 2022  4:32 \\u0B95\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"44562.189571759256\", \"[$-449]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0B9C\\u0BA9\\u0BB5\\u0BB0\\u0BBF 01 2022  4:32 \\u0B95\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"43543.503206018519\", \"[$-449]mmm dd yyyy  h:mm AM/PM\", \"\\u0BAE\\u0BBE\\u0BB0\\u0BCD\\u0B9A\\u0BCD 19 2019  12:04 \\u0BAE\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"43543.503206018519\", \"[$-449]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0BAE\\u0BBE\\u0BB0\\u0BCD\\u0B9A\\u0BCD 19 2019  12:04 \\u0BAE\\u0BBE\\u0BB2\\u0BC8 \\u0B9A\\u0BC6\\u0BB5\\u0BCD\\u0BB5\\u0BBE\\u0BAF\\u0BCD\"},\n\t\t{\"43543.503206018519\", \"[$-449]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0BAE 19 2019  12:04 \\u0BAE\\u0BBE\\u0BB2\\u0BC8 \\u0B9A\\u0BC6\\u0BB5\\u0BCD\\u0BB5\\u0BBE\\u0BAF\\u0BCD\"},\n\t\t{\"43543.503206018519\", \"[$-449]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0BAE\\u0BBE\\u0BB0\\u0BCD\\u0B9A\\u0BCD 19 2019  12:04 \\u0BAE\\u0BBE\\u0BB2\\u0BC8 \\u0B9A\\u0BC6\\u0BB5\\u0BCD\\u0BB5\\u0BBE\\u0BAF\\u0BCD\\u0B95\\u0BCD\\u0B95\\u0BBF\\u0BB4\\u0BAE\\u0BC8\"},\n\t\t{\"44562.189571759256\", \"[$-849]mmm dd yyyy  h:mm AM/PM\", \"\\u0B9C\\u0BA9. 01 2022  4:32 \\u0B95\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"44562.189571759256\", \"[$-849]mmmm dd yyyy  h:mm AM/PM\", \"\\u0B9C\\u0BA9\\u0BB5\\u0BB0\\u0BBF 01 2022  4:32 \\u0B95\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"44562.189571759256\", \"[$-849]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0B9C 01 2022  4:32 \\u0B95\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"44562.189571759256\", \"[$-849]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0B9C\\u0BA9\\u0BB5\\u0BB0\\u0BBF 01 2022  4:32 \\u0B95\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"43543.503206018519\", \"[$-849]mmm dd yyyy  h:mm AM/PM\", \"\\u0BAE\\u0BBE\\u0BB0\\u0BCD. 19 2019  12:04 \\u0BAE\\u0BBE\\u0BB2\\u0BC8\"},\n\t\t{\"43543.503206018519\", \"[$-849]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0BAE\\u0BBE\\u0BB0\\u0BCD\\u0B9A\\u0BCD 19 2019  12:04 \\u0BAE\\u0BBE\\u0BB2\\u0BC8 \\u0B9A\\u0BC6\\u0BB5\\u0BCD.\"},\n\t\t{\"43543.503206018519\", \"[$-849]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0BAE 19 2019  12:04 \\u0BAE\\u0BBE\\u0BB2\\u0BC8 \\u0B9A\\u0BC6\\u0BB5\\u0BCD.\"},\n\t\t{\"43543.503206018519\", \"[$-849]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0BAE\\u0BBE\\u0BB0\\u0BCD\\u0B9A\\u0BCD 19 2019  12:04 \\u0BAE\\u0BBE\\u0BB2\\u0BC8 \\u0B9A\\u0BC6\\u0BB5\\u0BCD\\u0BB5\\u0BBE\\u0BAF\\u0BCD\"},\n\t\t{\"44562.189571759256\", \"[$-44]mmm dd yyyy  h:mm AM/PM\", \"\\u0433\\u044B\\u0439\\u043D. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-44]mmmm dd yyyy  h:mm AM/PM\", \"\\u0433\\u044B\\u0439\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-44]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0433 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-44]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0433\\u044B\\u0439\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-44]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440. 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-44]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0441\\u0438\\u0448.\"},\n\t\t{\"43543.503206018519\", \"[$-44]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0441\\u0438\\u0448.\"},\n\t\t{\"43543.503206018519\", \"[$-44]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0441\\u0438\\u0448\\u04D9\\u043C\\u0431\\u0435\"},\n\t\t{\"44562.189571759256\", \"[$-444]mmm dd yyyy  h:mm AM/PM\", \"\\u0433\\u044B\\u0439\\u043D. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-444]mmmm dd yyyy  h:mm AM/PM\", \"\\u0433\\u044B\\u0439\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-444]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0433 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-444]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0433\\u044B\\u0439\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-444]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440. 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-444]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0441\\u0438\\u0448.\"},\n\t\t{\"43543.503206018519\", \"[$-444]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 PM \\u0441\\u0438\\u0448.\"},\n\t\t{\"43543.503206018519\", \"[$-444]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 PM \\u0441\\u0438\\u0448\\u04D9\\u043C\\u0431\\u0435\"},\n\t\t{\"44562.189571759256\", \"[$-4A]mmm dd yyyy  h:mm AM/PM\", \"\\u0C1C\\u0C28 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-4A]mmmm dd yyyy  h:mm AM/PM\", \"\\u0C1C\\u0C28\\u0C35\\u0C30\\u0C3F 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-4A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0C1C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-4A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0C1C\\u0C28\\u0C35\\u0C30\\u0C3F 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-4A]mmm dd yyyy  h:mm AM/PM\", \"\\u0C2E\\u0C3E\\u0C30\\u0C4D\\u0C1A\\u0C3F 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-4A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0C2E\\u0C3E\\u0C30\\u0C4D\\u0C1A\\u0C3F 19 2019  12:04 PM \\u0C2E\\u0C02\\u0C17\\u0C33\"},\n\t\t{\"43543.503206018519\", \"[$-4A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0C2E 19 2019  12:04 PM \\u0C2E\\u0C02\\u0C17\\u0C33\"},\n\t\t{\"43543.503206018519\", \"[$-4A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0C2E\\u0C3E\\u0C30\\u0C4D\\u0C1A\\u0C3F 19 2019  12:04 PM \\u0C2E\\u0C02\\u0C17\\u0C33\\u0C35\\u0C3E\\u0C30\\u0C02\"},\n\t\t{\"44562.189571759256\", \"[$-44A]mmm dd yyyy  h:mm AM/PM\", \"\\u0C1C\\u0C28 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-44A]mmmm dd yyyy  h:mm AM/PM\", \"\\u0C1C\\u0C28\\u0C35\\u0C30\\u0C3F 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-44A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0C1C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-44A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0C1C\\u0C28\\u0C35\\u0C30\\u0C3F 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-44A]mmm dd yyyy  h:mm AM/PM\", \"\\u0C2E\\u0C3E\\u0C30\\u0C4D\\u0C1A\\u0C3F 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-44A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0C2E\\u0C3E\\u0C30\\u0C4D\\u0C1A\\u0C3F 19 2019  12:04 PM \\u0C2E\\u0C02\\u0C17\\u0C33\"},\n\t\t{\"43543.503206018519\", \"[$-44A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0C2E 19 2019  12:04 PM \\u0C2E\\u0C02\\u0C17\\u0C33\"},\n\t\t{\"43543.503206018519\", \"[$-44A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0C2E\\u0C3E\\u0C30\\u0C4D\\u0C1A\\u0C3F 19 2019  12:04 PM \\u0C2E\\u0C02\\u0C17\\u0C33\\u0C35\\u0C3E\\u0C30\\u0C02\"},\n\t\t{\"44562.189571759256\", \"[$-1E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E21.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-1E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E01.\\u0E18. 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-1E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E21.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-1E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E40.\\u0E22. 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-1E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E1E.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-1E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E21.\\u0E22. 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-1E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E01.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-1E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E2A.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-1E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E01.\\u0E22. 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-1E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E15.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-1E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E1E.\\u0E22. 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-1E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E18.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E21\\u0E01\\u0E23\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-1E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E01\\u0E38\\u0E21\\u0E20\\u0E32\\u0E1E\\u0E31\\u0E19\\u0E18\\u0E4C 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-1E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E21\\u0E35\\u0E19\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-1E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E40\\u0E21\\u0E29\\u0E32\\u0E22\\u0E19 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-1E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E1E\\u0E24\\u0E29\\u0E20\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-1E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E21\\u0E34\\u0E16\\u0E38\\u0E19\\u0E32\\u0E22\\u0E19 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-1E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E01\\u0E23\\u0E01\\u0E0E\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-1E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E2A\\u0E34\\u0E07\\u0E2B\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-1E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E01\\u0E31\\u0E19\\u0E22\\u0E32\\u0E22\\u0E19 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-1E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E15\\u0E38\\u0E25\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-1E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E1E\\u0E24\\u0E28\\u0E08\\u0E34\\u0E01\\u0E32\\u0E22\\u0E19 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-1E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E18\\u0E31\\u0E19\\u0E27\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-1E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-1E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E01 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-1E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-1E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E40 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-1E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E1E 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-1E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-1E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E01 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-1E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E2A 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-1E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E01 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-1E]mmmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0E15 01 2022  4:32 AM \\u0E2A.\"},\n\t\t{\"44866.18957170139\", \"[$-1E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0E1E 01 2022  4:32 AM \\u0E2D.\"},\n\t\t{\"44896.18957170139\", \"[$-1E]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0E18 01 2022  4:32 AM \\u0E1E\\u0E24\\u0E2B\\u0E31\\u0E2A\\u0E1A\\u0E14\\u0E35\"},\n\t\t{\"44562.189571759256\", \"[$-41E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E21.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-41E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E01.\\u0E18. 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-41E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E21.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-41E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E40.\\u0E22. 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-41E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E1E.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-41E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E21.\\u0E22. 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-41E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E01.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-41E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E2A.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-41E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E01.\\u0E22. 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-41E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E15.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-41E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E1E.\\u0E22. 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-41E]mmm dd yyyy  h:mm AM/PM\", \"\\u0E18.\\u0E04. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E21\\u0E01\\u0E23\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-41E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E01\\u0E38\\u0E21\\u0E20\\u0E32\\u0E1E\\u0E31\\u0E19\\u0E18\\u0E4C 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-41E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E21\\u0E35\\u0E19\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-41E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E40\\u0E21\\u0E29\\u0E32\\u0E22\\u0E19 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-41E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E1E\\u0E24\\u0E29\\u0E20\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-41E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E21\\u0E34\\u0E16\\u0E38\\u0E19\\u0E32\\u0E22\\u0E19 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-41E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E01\\u0E23\\u0E01\\u0E0E\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-41E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E2A\\u0E34\\u0E07\\u0E2B\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-41E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E01\\u0E31\\u0E19\\u0E22\\u0E32\\u0E22\\u0E19 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-41E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E15\\u0E38\\u0E25\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-41E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E1E\\u0E24\\u0E28\\u0E08\\u0E34\\u0E01\\u0E32\\u0E22\\u0E19 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-41E]mmmm dd yyyy  h:mm AM/PM\", \"\\u0E18\\u0E31\\u0E19\\u0E27\\u0E32\\u0E04\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-41E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-41E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E01 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-41E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-41E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E40 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-41E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E1E 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-41E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E21 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-41E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E01 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-41E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E2A 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-41E]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0E01 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-41E]mmmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0E15 01 2022  4:32 AM \\u0E2A.\"},\n\t\t{\"44866.18957170139\", \"[$-41E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0E1E 01 2022  4:32 AM \\u0E2D.\"},\n\t\t{\"44896.18957170139\", \"[$-41E]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0E18 01 2022  4:32 AM \\u0E1E\\u0E24\\u0E2B\\u0E31\\u0E2A\\u0E1A\\u0E14\\u0E35\"},\n\t\t{\"100\", \"g\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"年4月9日\"},\n\t\t{\"100\", \"e\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"1900年4月9日\"},\n\t\t{\"100\", \"ge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"1900年4月9日\"},\n\t\t{\"100\", \"[$-411]ge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"1900年4月9日\"},\n\t\t{\"43709\", \"[$-411]ge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"R1年9月1日\"},\n\t\t{\"43709\", \"[$-411]gge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE41年9月1日\"},\n\t\t{\"43709\", \"[$-411]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE4\\u548C1年9月1日\"},\n\t\t{\"43709\", \"[$-411]gee\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"R01年9月1日\"},\n\t\t{\"43709\", \"[$-411]ggee\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE401年9月1日\"},\n\t\t{\"43709\", \"[$-411]gggee\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE4\\u548C01年9月1日\"},\n\t\t{\"43709\", \"[$-ja-JP-x-gannen]ge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"R1年9月1日\"},\n\t\t{\"43709\", \"[$-ja-JP-x-gannen]gge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE41年9月1日\"},\n\t\t{\"43709\", \"[$-ja-JP-x-gannen]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE4\\u548C1年9月1日\"},\n\t\t{\"43709\", \"[$-ja-JP-x-gannen]gee\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"R01年9月1日\"},\n\t\t{\"43709\", \"[$-ja-JP-x-gannen]ggee\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE401年9月1日\"},\n\t\t{\"43709\", \"[$-ja-JP-x-gannen]gggee\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE4\\u548C01年9月1日\"},\n\t\t{\"43709\", \"[$-ja-JP-x-gannen,80]ge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"R1年9月1日\"},\n\t\t{\"43709\", \"[$-ja-JP-x-gannen,80]gge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE4\\u5143年9月1日\"},\n\t\t{\"43709\", \"[$-ja-JP-x-gannen,80]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE4\\u548C\\u5143年9月1日\"},\n\t\t{\"43709\", \"[$-ja-JP-x-gannen,80]gee\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"R01年9月1日\"},\n\t\t{\"43709\", \"[$-ja-JP-x-gannen,80]ggee\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE4\\u5143年9月1日\"},\n\t\t{\"43709\", \"[$-ja-JP-x-gannen,80]gggee\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE4\\u548C\\u5143年9月1日\"},\n\t\t{\"43466.189571759256\", \"[$-411]ge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"H31年1月1日\"},\n\t\t{\"43466.189571759256\", \"[$-411]gge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u5E7331年1月1日\"},\n\t\t{\"43466.189571759256\", \"[$-411]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u5E73\\u621031年1月1日\"},\n\t\t{\"44896.18957170139\", \"[$-411]ge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"R4年12月1日\"},\n\t\t{\"44896.18957170139\", \"[$-411]gge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE44年12月1日\"},\n\t\t{\"44896.18957170139\", \"[$-411]ggge\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"\\u4EE4\\u548C4年12月1日\"},\n\t\t{\"44562.189571759256\", \"[$-51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F21 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44593.189571759256\", \"[$-51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F22 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44621.18957170139\", \"[$-51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F23 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44652.18957170139\", \"[$-51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F24 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44682.18957170139\", \"[$-51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F25 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44713.18957170139\", \"[$-51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F26 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44743.18957170139\", \"[$-51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F27 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44774.18957170139\", \"[$-51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F28 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44805.18957170139\", \"[$-51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F29 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44835.18957170139\", \"[$-51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F21\\u0F20 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44866.18957170139\", \"[$-51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F21\\u0F21 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44896.18957170139\", \"[$-51]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F21\\u0F22 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44562.189571759256\", \"[$-51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F51\\u0F44\\u0F0B\\u0F54\\u0F7C\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44593.189571759256\", \"[$-51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F42\\u0F49\\u0F72\\u0F66\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44621.18957170139\", \"[$-51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F42\\u0F66\\u0F74\\u0F58\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44652.18957170139\", \"[$-51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F5E\\u0F72\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44682.18957170139\", \"[$-51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F63\\u0F94\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44713.18957170139\", \"[$-51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F51\\u0FB2\\u0F74\\u0F42\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44743.18957170139\", \"[$-51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F51\\u0F74\\u0F53\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44774.18957170139\", \"[$-51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F62\\u0F92\\u0FB1\\u0F51\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44805.18957170139\", \"[$-51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F51\\u0F42\\u0F74\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44835.18957170139\", \"[$-51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F54\\u0F0D 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44866.18957170139\", \"[$-51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F42\\u0F45\\u0F72\\u0F42\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44896.18957170139\", \"[$-51]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F42\\u0F49\\u0F72\\u0F66\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44562.189571759256\", \"[$-51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44593.189571759256\", \"[$-51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44621.18957170139\", \"[$-51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44652.18957170139\", \"[$-51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44682.18957170139\", \"[$-51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44713.18957170139\", \"[$-51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44743.18957170139\", \"[$-51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44774.18957170139\", \"[$-51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44805.18957170139\", \"[$-51]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44835.18957170139\", \"[$-51]mmmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B \\u0F66\\u0FA4\\u0F7A\\u0F53\\u0F0B\\u0F54\\u0F0D\"},\n\t\t{\"44866.18957170139\", \"[$-51]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B \\u0F58\\u0F72\\u0F42\\u0F0B\\u0F51\\u0F58\\u0F62\\u0F0D\"},\n\t\t{\"44896.18957170139\", \"[$-51]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B \\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F55\\u0F74\\u0F62\\u0F0B\\u0F56\\u0F74\\u0F0D\"},\n\t\t{\"44562.189571759256\", \"[$-451]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F21 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44593.189571759256\", \"[$-451]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F22 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44621.18957170139\", \"[$-451]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F23 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44652.18957170139\", \"[$-451]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F24 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44682.18957170139\", \"[$-451]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F25 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44713.18957170139\", \"[$-451]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F26 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44743.18957170139\", \"[$-451]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F27 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44774.18957170139\", \"[$-451]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F28 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44805.18957170139\", \"[$-451]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F29 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44835.18957170139\", \"[$-451]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F21\\u0F20 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44866.18957170139\", \"[$-451]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F21\\u0F21 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44896.18957170139\", \"[$-451]mmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B \\u0F21\\u0F22 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44562.189571759256\", \"[$-451]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F51\\u0F44\\u0F0B\\u0F54\\u0F7C\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44593.189571759256\", \"[$-451]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F42\\u0F49\\u0F72\\u0F66\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44621.18957170139\", \"[$-451]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F42\\u0F66\\u0F74\\u0F58\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44652.18957170139\", \"[$-451]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F5E\\u0F72\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44682.18957170139\", \"[$-451]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F63\\u0F94\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44713.18957170139\", \"[$-451]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F51\\u0FB2\\u0F74\\u0F42\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44743.18957170139\", \"[$-451]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F51\\u0F74\\u0F53\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44774.18957170139\", \"[$-451]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F62\\u0F92\\u0FB1\\u0F51\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44805.18957170139\", \"[$-451]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F51\\u0F42\\u0F74\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44835.18957170139\", \"[$-451]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F66\\u0FA4\\u0FB1\\u0F72\\u0F0B\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F54\\u0F0D 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44866.18957170139\", \"[$-451]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F42\\u0F45\\u0F72\\u0F42\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44896.18957170139\", \"[$-451]mmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F\\u0FB3\\u0F0B\\u0F56\\u0F0B\\u0F56\\u0F45\\u0F74\\u0F0B\\u0F42\\u0F49\\u0F72\\u0F66\\u0F0B\\u0F54\\u0F0B 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44562.189571759256\", \"[$-451]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44593.189571759256\", \"[$-451]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44621.18957170139\", \"[$-451]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44652.18957170139\", \"[$-451]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44682.18957170139\", \"[$-451]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44713.18957170139\", \"[$-451]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44743.18957170139\", \"[$-451]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44774.18957170139\", \"[$-451]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44805.18957170139\", \"[$-451]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B\"},\n\t\t{\"44835.18957170139\", \"[$-451]mmmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0F66 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B \\u0F66\\u0FA4\\u0F7A\\u0F53\\u0F0B\\u0F54\\u0F0D\"},\n\t\t{\"44866.18957170139\", \"[$-451]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B \\u0F58\\u0F72\\u0F42\\u0F0B\\u0F51\\u0F58\\u0F62\\u0F0D\"},\n\t\t{\"44896.18957170139\", \"[$-451]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0F5F 01 2022  4:32 \\u0F66\\u0F94\\u0F0B\\u0F51\\u0FB2\\u0F7C\\u0F0B \\u0F42\\u0F5F\\u0F60\\u0F0B\\u0F55\\u0F74\\u0F62\\u0F0B\\u0F56\\u0F74\\u0F0D\"},\n\t\t{\"44562.189571759256\", \"[$-73]mmm dd yyyy  h:mm AM/PM\", \"\\u1325\\u122A 01 2022  4:32 \\u1295\\u1309\\u1206\"},\n\t\t{\"44562.189571759256\", \"[$-73]mmmm dd yyyy  h:mm AM/PM\", \"\\u1325\\u122A 01 2022  4:32 \\u1295\\u1309\\u1206\"},\n\t\t{\"44562.189571759256\", \"[$-73]mmmmm dd yyyy  h:mm AM/PM\", \"\\u1325 01 2022  4:32 \\u1295\\u1309\\u1206\"},\n\t\t{\"44562.189571759256\", \"[$-73]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u1325\\u122A 01 2022  4:32 \\u1295\\u1309\\u1206\"},\n\t\t{\"43543.503206018519\", \"[$-73]mmm dd yyyy  h:mm AM/PM\", \"\\u1218\\u130B 19 2019  12:04 \\u12F5\\u1215\\u122A \\u1250\\u1275\\u122A\"},\n\t\t{\"43543.503206018519\", \"[$-73]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u1218\\u130B\\u1262\\u1275 19 2019  12:04 \\u12F5\\u1215\\u122A \\u1250\\u1275\\u122A \\u1230\\u1209\"},\n\t\t{\"43543.503206018519\", \"[$-73]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u1218 19 2019  12:04 \\u12F5\\u1215\\u122A \\u1250\\u1275\\u122A \\u1230\\u1209\"},\n\t\t{\"43543.503206018519\", \"[$-73]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u1218\\u130B\\u1262\\u1275 19 2019  12:04 \\u12F5\\u1215\\u122A \\u1250\\u1275\\u122A \\u1220\\u1209\\u1235\"},\n\t\t{\"44562.189571759256\", \"[$-873]mmm dd yyyy  h:mm AM/PM\", \"\\u1325\\u122A 01 2022  4:32 \\u1295\\u1309\\u1206 \\u1230\\u12D3\\u1270\"},\n\t\t{\"44562.189571759256\", \"[$-873]mmmm dd yyyy  h:mm AM/PM\", \"\\u1325\\u122A 01 2022  4:32 \\u1295\\u1309\\u1206 \\u1230\\u12D3\\u1270\"},\n\t\t{\"44562.189571759256\", \"[$-873]mmmmm dd yyyy  h:mm AM/PM\", \"\\u1325 01 2022  4:32 \\u1295\\u1309\\u1206 \\u1230\\u12D3\\u1270\"},\n\t\t{\"44562.189571759256\", \"[$-873]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u1325\\u122A 01 2022  4:32 \\u1295\\u1309\\u1206 \\u1230\\u12D3\\u1270\"},\n\t\t{\"43543.503206018519\", \"[$-873]mmm dd yyyy  h:mm AM/PM\", \"\\u1218\\u130B 19 2019  12:04 \\u12F5\\u1215\\u122D \\u1230\\u12D3\\u1275\"},\n\t\t{\"43543.503206018519\", \"[$-873]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u1218\\u130B\\u1262\\u1275 19 2019  12:04 \\u12F5\\u1215\\u122D \\u1230\\u12D3\\u1275 \\u1230\\u1209\"},\n\t\t{\"43543.503206018519\", \"[$-873]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u1218 19 2019  12:04 \\u12F5\\u1215\\u122D \\u1230\\u12D3\\u1275 \\u1230\\u1209\"},\n\t\t{\"43543.503206018519\", \"[$-873]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u1218\\u130B\\u1262\\u1275 19 2019  12:04 \\u12F5\\u1215\\u122D \\u1230\\u12D3\\u1275 \\u1220\\u1209\\u1235\"},\n\t\t{\"44562.189571759256\", \"[$-473]mmm dd yyyy  h:mm AM/PM\", \"\\u1325\\u122A 01 2022  4:32 \\u1295\\u1309\\u1206\"},\n\t\t{\"44562.189571759256\", \"[$-473]mmmm dd yyyy  h:mm AM/PM\", \"\\u1325\\u122A 01 2022  4:32 \\u1295\\u1309\\u1206\"},\n\t\t{\"44562.189571759256\", \"[$-473]mmmmm dd yyyy  h:mm AM/PM\", \"\\u1325 01 2022  4:32 \\u1295\\u1309\\u1206\"},\n\t\t{\"44562.189571759256\", \"[$-473]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u1325\\u122A 01 2022  4:32 \\u1295\\u1309\\u1206\"},\n\t\t{\"43543.503206018519\", \"[$-473]mmm dd yyyy  h:mm AM/PM\", \"\\u1218\\u130B 19 2019  12:04 \\u12F5\\u1215\\u122A \\u1250\\u1275\\u122A\"},\n\t\t{\"43543.503206018519\", \"[$-473]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u1218\\u130B\\u1262\\u1275 19 2019  12:04 \\u12F5\\u1215\\u122A \\u1250\\u1275\\u122A \\u1230\\u1209\"},\n\t\t{\"43543.503206018519\", \"[$-473]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u1218 19 2019  12:04 \\u12F5\\u1215\\u122A \\u1250\\u1275\\u122A \\u1230\\u1209\"},\n\t\t{\"43543.503206018519\", \"[$-473]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u1218\\u130B\\u1262\\u1275 19 2019  12:04 \\u12F5\\u1215\\u122A \\u1250\\u1275\\u122A \\u1220\\u1209\\u1235\"},\n\t\t{\"44562.189571759256\", \"[$-31]mmm dd yyyy  h:mm AM/PM\", \"Sun 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-31]mmmm dd yyyy  h:mm AM/PM\", \"Sunguti 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-31]mmmmm dd yyyy  h:mm AM/PM\", \"S 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-31]mmmmmm dd yyyy  h:mm AM/PM\", \"Sunguti 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-31]mmm dd yyyy  h:mm AM/PM\", \"Kul 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-31]mmmm dd yyyy  h:mm AM/PM aaa\", \"Nyenyankulu 19 2019  12:04 PM Bir\"},\n\t\t{\"43543.503206018519\", \"[$-31]mmmmm dd yyyy  h:mm AM/PM ddd\", \"N 19 2019  12:04 PM Bir\"},\n\t\t{\"43543.503206018519\", \"[$-31]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Nyenyankulu 19 2019  12:04 PM Ravumbirhi\"},\n\t\t{\"44562.189571759256\", \"[$-431]mmm dd yyyy  h:mm AM/PM\", \"Sun 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-431]mmmm dd yyyy  h:mm AM/PM\", \"Sunguti 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-431]mmmmm dd yyyy  h:mm AM/PM\", \"S 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-431]mmmmmm dd yyyy  h:mm AM/PM\", \"Sunguti 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-431]mmm dd yyyy  h:mm AM/PM\", \"Kul 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-431]mmmm dd yyyy  h:mm AM/PM aaa\", \"Nyenyankulu 19 2019  12:04 PM Bir\"},\n\t\t{\"43543.503206018519\", \"[$-431]mmmmm dd yyyy  h:mm AM/PM ddd\", \"N 19 2019  12:04 PM Bir\"},\n\t\t{\"43543.503206018519\", \"[$-431]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Nyenyankulu 19 2019  12:04 PM Ravumbirhi\"},\n\t\t{\"44562.189571759256\", \"[$-1F]mmm dd yyyy  h:mm AM/PM\", \"Oca 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44593.189571759256\", \"[$-1F]mmm dd yyyy  h:mm AM/PM\", \"Şub 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44621.18957170139\", \"[$-1F]mmm dd yyyy  h:mm AM/PM\", \"Mar 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44652.18957170139\", \"[$-1F]mmm dd yyyy  h:mm AM/PM\", \"Nis 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44682.18957170139\", \"[$-1F]mmm dd yyyy  h:mm AM/PM\", \"May 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44713.18957170139\", \"[$-1F]mmm dd yyyy  h:mm AM/PM\", \"Haz 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44743.18957170139\", \"[$-1F]mmm dd yyyy  h:mm AM/PM\", \"Tem 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44774.18957170139\", \"[$-1F]mmm dd yyyy  h:mm AM/PM\", \"Ağu 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44805.18957170139\", \"[$-1F]mmm dd yyyy  h:mm AM/PM\", \"Eyl 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44835.18957170139\", \"[$-1F]mmm dd yyyy  h:mm AM/PM\", \"Eki 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44866.18957170139\", \"[$-1F]mmm dd yyyy  h:mm AM/PM\", \"Kas 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44896.18957170139\", \"[$-1F]mmm dd yyyy  h:mm AM/PM\", \"Ara 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44562.189571759256\", \"[$-1F]mmmm dd yyyy  h:mm AM/PM\", \"Ocak 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44593.189571759256\", \"[$-1F]mmmm dd yyyy  h:mm AM/PM\", \"Şubat 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44621.18957170139\", \"[$-1F]mmmm dd yyyy  h:mm AM/PM\", \"Mart 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44652.18957170139\", \"[$-1F]mmmm dd yyyy  h:mm AM/PM\", \"Nisan 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44682.18957170139\", \"[$-1F]mmmm dd yyyy  h:mm AM/PM\", \"Mayıs 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44713.18957170139\", \"[$-1F]mmmm dd yyyy  h:mm AM/PM\", \"Haziran 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44743.18957170139\", \"[$-1F]mmmm dd yyyy  h:mm AM/PM\", \"Temmuz 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44774.18957170139\", \"[$-1F]mmmm dd yyyy  h:mm AM/PM\", \"Ağustos 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44805.18957170139\", \"[$-1F]mmmm dd yyyy  h:mm AM/PM\", \"Eylül 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44835.18957170139\", \"[$-1F]mmmm dd yyyy  h:mm AM/PM\", \"Ekim 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44866.18957170139\", \"[$-1F]mmmm dd yyyy  h:mm AM/PM\", \"Kasım 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44896.18957170139\", \"[$-1F]mmmm dd yyyy  h:mm AM/PM\", \"Aralık 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44562.189571759256\", \"[$-1F]mmmmm dd yyyy  h:mm AM/PM\", \"O 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44593.189571759256\", \"[$-1F]mmmmm dd yyyy  h:mm AM/PM\", \"Ş 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44621.18957170139\", \"[$-1F]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44652.18957170139\", \"[$-1F]mmmmm dd yyyy  h:mm AM/PM\", \"N 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44682.18957170139\", \"[$-1F]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44713.18957170139\", \"[$-1F]mmmmm dd yyyy  h:mm AM/PM\", \"H 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44743.18957170139\", \"[$-1F]mmmmm dd yyyy  h:mm AM/PM\", \"T 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44774.18957170139\", \"[$-1F]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44805.18957170139\", \"[$-1F]mmmmm dd yyyy  h:mm AM/PM\", \"E 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44835.18957170139\", \"[$-1F]mmmmm dd yyyy  h:mm AM/PM aaa\", \"E 01 2022  4:32 \\u00F6\\u00F6 Cmt\"},\n\t\t{\"44866.18957170139\", \"[$-1F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"K 01 2022  4:32 \\u00F6\\u00F6 Sal\"},\n\t\t{\"44896.18957170139\", \"[$-1F]mmmmm dd yyyy  h:mm AM/PM dddd\", \"A 01 2022  4:32 \\u00F6\\u00F6 Perşembe\"},\n\t\t{\"44562.189571759256\", \"[$-41F]mmm dd yyyy  h:mm AM/PM\", \"Oca 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44593.189571759256\", \"[$-41F]mmm dd yyyy  h:mm AM/PM\", \"Şub 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44621.18957170139\", \"[$-41F]mmm dd yyyy  h:mm AM/PM\", \"Mar 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44652.18957170139\", \"[$-41F]mmm dd yyyy  h:mm AM/PM\", \"Nis 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44682.18957170139\", \"[$-41F]mmm dd yyyy  h:mm AM/PM\", \"May 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44713.18957170139\", \"[$-41F]mmm dd yyyy  h:mm AM/PM\", \"Haz 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44743.18957170139\", \"[$-41F]mmm dd yyyy  h:mm AM/PM\", \"Tem 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44774.18957170139\", \"[$-41F]mmm dd yyyy  h:mm AM/PM\", \"Ağu 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44805.18957170139\", \"[$-41F]mmm dd yyyy  h:mm AM/PM\", \"Eyl 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44835.18957170139\", \"[$-41F]mmm dd yyyy  h:mm AM/PM\", \"Eki 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44866.18957170139\", \"[$-41F]mmm dd yyyy  h:mm AM/PM\", \"Kas 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44896.18957170139\", \"[$-41F]mmm dd yyyy  h:mm AM/PM\", \"Ara 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44562.189571759256\", \"[$-41F]mmmm dd yyyy  h:mm AM/PM\", \"Ocak 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44593.189571759256\", \"[$-41F]mmmm dd yyyy  h:mm AM/PM\", \"Şubat 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44621.18957170139\", \"[$-41F]mmmm dd yyyy  h:mm AM/PM\", \"Mart 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44652.18957170139\", \"[$-41F]mmmm dd yyyy  h:mm AM/PM\", \"Nisan 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44682.18957170139\", \"[$-41F]mmmm dd yyyy  h:mm AM/PM\", \"Mayıs 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44713.18957170139\", \"[$-41F]mmmm dd yyyy  h:mm AM/PM\", \"Haziran 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44743.18957170139\", \"[$-41F]mmmm dd yyyy  h:mm AM/PM\", \"Temmuz 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44774.18957170139\", \"[$-41F]mmmm dd yyyy  h:mm AM/PM\", \"Ağustos 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44805.18957170139\", \"[$-41F]mmmm dd yyyy  h:mm AM/PM\", \"Eylül 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44835.18957170139\", \"[$-41F]mmmm dd yyyy  h:mm AM/PM\", \"Ekim 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44866.18957170139\", \"[$-41F]mmmm dd yyyy  h:mm AM/PM\", \"Kasım 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44896.18957170139\", \"[$-41F]mmmm dd yyyy  h:mm AM/PM\", \"Aralık 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44562.189571759256\", \"[$-41F]mmmmm dd yyyy  h:mm AM/PM\", \"O 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44593.189571759256\", \"[$-41F]mmmmm dd yyyy  h:mm AM/PM\", \"Ş 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44621.18957170139\", \"[$-41F]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44652.18957170139\", \"[$-41F]mmmmm dd yyyy  h:mm AM/PM\", \"N 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44682.18957170139\", \"[$-41F]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44713.18957170139\", \"[$-41F]mmmmm dd yyyy  h:mm AM/PM\", \"H 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44743.18957170139\", \"[$-41F]mmmmm dd yyyy  h:mm AM/PM\", \"T 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44774.18957170139\", \"[$-41F]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44805.18957170139\", \"[$-41F]mmmmm dd yyyy  h:mm AM/PM\", \"E 01 2022  4:32 \\u00F6\\u00F6\"},\n\t\t{\"44835.18957170139\", \"[$-41F]mmmmm dd yyyy  h:mm AM/PM aaa\", \"E 01 2022  4:32 \\u00F6\\u00F6 Cmt\"},\n\t\t{\"44866.18957170139\", \"[$-41F]mmmmm dd yyyy  h:mm AM/PM ddd\", \"K 01 2022  4:32 \\u00F6\\u00F6 Sal\"},\n\t\t{\"44896.18957170139\", \"[$-41F]mmmmm dd yyyy  h:mm AM/PM dddd\", \"A 01 2022  4:32 \\u00F6\\u00F6 Perşembe\"},\n\t\t{\"44562.189571759256\", \"[$-42]mmm dd yyyy  h:mm AM/PM\", \"Ýan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-42]mmmm dd yyyy  h:mm AM/PM\", \"Ýanwar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-42]mmmmm dd yyyy  h:mm AM/PM\", \"Ý 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-42]mmmmmm dd yyyy  h:mm AM/PM\", \"Ýanwar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-42]mmm dd yyyy  h:mm AM/PM\", \"Mart 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-42]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mart 19 2019  12:04 PM Sb\"},\n\t\t{\"43543.503206018519\", \"[$-42]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Sb\"},\n\t\t{\"43543.503206018519\", \"[$-42]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mart 19 2019  12:04 PM Sişenbe\"},\n\t\t{\"44562.189571759256\", \"[$-442]mmm dd yyyy  h:mm AM/PM\", \"Ýan 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-442]mmmm dd yyyy  h:mm AM/PM\", \"Ýanwar 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-442]mmmmm dd yyyy  h:mm AM/PM\", \"Ý 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-442]mmmmmm dd yyyy  h:mm AM/PM\", \"Ýanwar 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-442]mmm dd yyyy  h:mm AM/PM\", \"Mart 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-442]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mart 19 2019  12:04 PM Sb\"},\n\t\t{\"43543.503206018519\", \"[$-442]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 PM Sb\"},\n\t\t{\"43543.503206018519\", \"[$-442]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mart 19 2019  12:04 PM Sişenbe\"},\n\t\t{\"44562.189571759256\", \"[$-22]mmm dd yyyy  h:mm AM/PM\", \"\\u0421\\u0456\\u0447 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-22]mmmm dd yyyy  h:mm AM/PM\", \"\\u0441\\u0456\\u0447\\u0435\\u043D\\u044C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-22]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0441 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-22]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0441\\u0456\\u0447\\u0435\\u043D\\u044C 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-22]mmm dd yyyy  h:mm AM/PM\", \"\\u0411\\u0435\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-22]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0431\\u0435\\u0440\\u0435\\u0437\\u0435\\u043D\\u044C 19 2019  12:04 PM \\u0412\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-22]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0431 19 2019  12:04 PM \\u0412\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-22]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0431\\u0435\\u0440\\u0435\\u0437\\u0435\\u043D\\u044C 19 2019  12:04 PM \\u0432\\u0456\\u0432\\u0442\\u043E\\u0440\\u043E\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-422]mmm dd yyyy  h:mm AM/PM\", \"\\u0421\\u0456\\u0447 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-422]mmmm dd yyyy  h:mm AM/PM\", \"\\u0441\\u0456\\u0447\\u0435\\u043D\\u044C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-422]mmmmm dd yyyy  h:mm AM/PM\", \"\\u0441 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-422]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u0441\\u0456\\u0447\\u0435\\u043D\\u044C 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-422]mmm dd yyyy  h:mm AM/PM\", \"\\u0411\\u0435\\u0440 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-422]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0431\\u0435\\u0440\\u0435\\u0437\\u0435\\u043D\\u044C 19 2019  12:04 PM \\u0412\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-422]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0431 19 2019  12:04 PM \\u0412\\u0442\"},\n\t\t{\"43543.503206018519\", \"[$-422]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0431\\u0435\\u0440\\u0435\\u0437\\u0435\\u043D\\u044C 19 2019  12:04 PM \\u0432\\u0456\\u0432\\u0442\\u043E\\u0440\\u043E\\u043A\"},\n\t\t{\"44562.189571759256\", \"[$-2E]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 dopołdnja\"},\n\t\t{\"44562.189571759256\", \"[$-2E]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 dopołdnja\"},\n\t\t{\"44562.189571759256\", \"[$-2E]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 dopołdnja\"},\n\t\t{\"44562.189571759256\", \"[$-2E]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 dopołdnja\"},\n\t\t{\"43543.503206018519\", \"[$-2E]mmm dd yyyy  h:mm AM/PM\", \"měr 19 2019  12:04 popołdnju\"},\n\t\t{\"43543.503206018519\", \"[$-2E]mmmm dd yyyy  h:mm AM/PM aaa\", \"měrc 19 2019  12:04 popołdnju wut\"},\n\t\t{\"43543.503206018519\", \"[$-2E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 popołdnju wut\"},\n\t\t{\"43543.503206018519\", \"[$-2E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"měrc 19 2019  12:04 popołdnju wutora\"},\n\t\t{\"44562.189571759256\", \"[$-42E]mmm dd yyyy  h:mm AM/PM\", \"jan 01 2022  4:32 dopołdnja\"},\n\t\t{\"44562.189571759256\", \"[$-42E]mmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 dopołdnja\"},\n\t\t{\"44562.189571759256\", \"[$-42E]mmmmm dd yyyy  h:mm AM/PM\", \"j 01 2022  4:32 dopołdnja\"},\n\t\t{\"44562.189571759256\", \"[$-42E]mmmmmm dd yyyy  h:mm AM/PM\", \"januar 01 2022  4:32 dopołdnja\"},\n\t\t{\"43543.503206018519\", \"[$-42E]mmm dd yyyy  h:mm AM/PM\", \"měr 19 2019  12:04 popołdnju\"},\n\t\t{\"43543.503206018519\", \"[$-42E]mmmm dd yyyy  h:mm AM/PM aaa\", \"měrc 19 2019  12:04 popołdnju wut\"},\n\t\t{\"43543.503206018519\", \"[$-42E]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 popołdnju wut\"},\n\t\t{\"43543.503206018519\", \"[$-42E]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"měrc 19 2019  12:04 popołdnju wutora\"},\n\t\t{\"44562.189571759256\", \"[$-20]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-20]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-20]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-20]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-20]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-20]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0645\\u0646\\u06AF\\u0644\"},\n\t\t{\"43543.503206018519\", \"[$-20]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 PM \\u0645\\u0646\\u06AF\\u0644\"},\n\t\t{\"43543.503206018519\", \"[$-20]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0645\\u0646\\u06AF\\u0644\"},\n\t\t{\"44562.189571759256\", \"[$-820]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 \\u062F\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-820]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 \\u062F\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-820]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 \\u062F\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-820]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 \\u062F\\u0646\"},\n\t\t{\"43543.503206018519\", \"[$-820]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 \\u0631\\u0627\\u062A\"},\n\t\t{\"43543.503206018519\", \"[$-820]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 \\u0631\\u0627\\u062A \\u0645\\u0646\\u06AF\\u0644\"},\n\t\t{\"43543.503206018519\", \"[$-820]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0631\\u0627\\u062A \\u0645\\u0646\\u06AF\\u0644\"},\n\t\t{\"43543.503206018519\", \"[$-820]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 \\u0631\\u0627\\u062A \\u0645\\u0646\\u06AF\\u0644\"},\n\t\t{\"44562.189571759256\", \"[$-420]mmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-420]mmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-420]mmmmm dd yyyy  h:mm AM/PM\", \"\\u062C 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-420]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u062C\\u0646\\u0648\\u0631\\u06CC 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-420]mmm dd yyyy  h:mm AM/PM\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-420]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0645\\u0646\\u06AF\\u0644\"},\n\t\t{\"43543.503206018519\", \"[$-420]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 PM \\u0645\\u0646\\u06AF\\u0644\"},\n\t\t{\"43543.503206018519\", \"[$-420]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u0686 19 2019  12:04 PM \\u0645\\u0646\\u06AF\\u0644\"},\n\t\t{\"44562.189571759256\", \"[$-80]mmm dd yyyy  h:mm AM/PM\", \"1-\\u0626\\u0627\\u064A 01 2022  4:32 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0628\\u06C7\\u0631\\u06C7\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-80]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0627\\u0646\\u06CB\\u0627\\u0631 01 2022  4:32 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0628\\u06C7\\u0631\\u06C7\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-80]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0628\\u06C7\\u0631\\u06C7\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-80]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0627\\u0646\\u06CB\\u0627\\u0631 01 2022  4:32 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0628\\u06C7\\u0631\\u06C7\\u0646\"},\n\t\t{\"43543.503206018519\", \"[$-80]mmm dd yyyy  h:mm AM/PM\", \"3-\\u0626\\u0627\\u064A 19 2019  12:04 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0643\\u06D0\\u064A\\u0649\\u0646\"},\n\t\t{\"43543.503206018519\", \"[$-80]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u062A 19 2019  12:04 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0643\\u06D0\\u064A\\u0649\\u0646 \\u0633\\u06D5\"},\n\t\t{\"43543.503206018519\", \"[$-80]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0643\\u06D0\\u064A\\u0649\\u0646 \\u0633\\u06D5\"},\n\t\t{\"43543.503206018519\", \"[$-80]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u062A 19 2019  12:04 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0643\\u06D0\\u064A\\u0649\\u0646 \\u0633\\u06D5\\u064A\\u0634\\u06D5\\u0646\\u0628\\u06D5\"},\n\t\t{\"44562.189571759256\", \"[$-480]mmm dd yyyy  h:mm AM/PM\", \"1-\\u0626\\u0627\\u064A 01 2022  4:32 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0628\\u06C7\\u0631\\u06C7\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-480]mmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0627\\u0646\\u06CB\\u0627\\u0631 01 2022  4:32 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0628\\u06C7\\u0631\\u06C7\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-480]mmmmm dd yyyy  h:mm AM/PM\", \"\\u064A 01 2022  4:32 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0628\\u06C7\\u0631\\u06C7\\u0646\"},\n\t\t{\"44562.189571759256\", \"[$-480]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u064A\\u0627\\u0646\\u06CB\\u0627\\u0631 01 2022  4:32 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0628\\u06C7\\u0631\\u06C7\\u0646\"},\n\t\t{\"43543.503206018519\", \"[$-480]mmm dd yyyy  h:mm AM/PM\", \"3-\\u0626\\u0627\\u064A 19 2019  12:04 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0643\\u06D0\\u064A\\u0649\\u0646\"},\n\t\t{\"43543.503206018519\", \"[$-480]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u0645\\u0627\\u0631\\u062A 19 2019  12:04 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0643\\u06D0\\u064A\\u0649\\u0646 \\u0633\\u06D5\"},\n\t\t{\"43543.503206018519\", \"[$-480]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u0645 19 2019  12:04 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0643\\u06D0\\u064A\\u0649\\u0646 \\u0633\\u06D5\"},\n\t\t{\"43543.503206018519\", \"[$-480]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u0645\\u0627\\u0631\\u062A 19 2019  12:04 \\u0686\\u06C8\\u0634\\u062A\\u0649\\u0646 \\u0643\\u06D0\\u064A\\u0649\\u0646 \\u0633\\u06D5\\u064A\\u0634\\u06D5\\u0646\\u0628\\u06D5\"},\n\t\t{\"44562.189571759256\", \"[$-7843]mmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432 01 2022  4:32 \\u0422\\u041E\"},\n\t\t{\"44562.189571759256\", \"[$-7843]mmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 \\u0422\\u041E\"},\n\t\t{\"44562.189571759256\", \"[$-7843]mmmmm dd yyyy  h:mm AM/PM\", \"\\u044F 01 2022  4:32 \\u0422\\u041E\"},\n\t\t{\"44562.189571759256\", \"[$-7843]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 \\u0422\\u041E\"},\n\t\t{\"43543.503206018519\", \"[$-7843]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440 19 2019  12:04 \\u0422\\u041A\"},\n\t\t{\"43543.503206018519\", \"[$-7843]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 \\u0422\\u041A \\u0441\\u0435\\u0448\"},\n\t\t{\"43543.503206018519\", \"[$-7843]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 \\u0422\\u041A \\u0441\\u0435\\u0448\"},\n\t\t{\"43543.503206018519\", \"[$-7843]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 \\u0422\\u041A \\u0441\\u0435\\u0448\\u0430\\u043D\\u0431\\u0430\"},\n\t\t{\"44562.189571759256\", \"[$-843]mmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432 01 2022  4:32 \\u0422\\u041E\"},\n\t\t{\"44562.189571759256\", \"[$-843]mmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 \\u0422\\u041E\"},\n\t\t{\"44562.189571759256\", \"[$-843]mmmmm dd yyyy  h:mm AM/PM\", \"\\u044F 01 2022  4:32 \\u0422\\u041E\"},\n\t\t{\"44562.189571759256\", \"[$-843]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u044F\\u043D\\u0432\\u0430\\u0440 01 2022  4:32 \\u0422\\u041E\"},\n\t\t{\"43543.503206018519\", \"[$-843]mmm dd yyyy  h:mm AM/PM\", \"\\u043C\\u0430\\u0440 19 2019  12:04 \\u0422\\u041A\"},\n\t\t{\"43543.503206018519\", \"[$-843]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 \\u0422\\u041A \\u0441\\u0435\\u0448\"},\n\t\t{\"43543.503206018519\", \"[$-843]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u043C 19 2019  12:04 \\u0422\\u041A \\u0441\\u0435\\u0448\"},\n\t\t{\"43543.503206018519\", \"[$-843]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u043C\\u0430\\u0440\\u0442 19 2019  12:04 \\u0422\\u041A \\u0441\\u0435\\u0448\\u0430\\u043D\\u0431\\u0430\"},\n\t\t{\"44562.189571759256\", \"[$-43]mmm dd yyyy  h:mm AM/PM\", \"Yan 01 2022  4:32 TO\"},\n\t\t{\"44562.189571759256\", \"[$-43]mmmm dd yyyy  h:mm AM/PM\", \"Yanvar 01 2022  4:32 TO\"},\n\t\t{\"44562.189571759256\", \"[$-43]mmmmm dd yyyy  h:mm AM/PM\", \"Y 01 2022  4:32 TO\"},\n\t\t{\"44562.189571759256\", \"[$-43]mmmmmm dd yyyy  h:mm AM/PM\", \"Yanvar 01 2022  4:32 TO\"},\n\t\t{\"43543.503206018519\", \"[$-43]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 TK\"},\n\t\t{\"43543.503206018519\", \"[$-43]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mart 19 2019  12:04 TK Sesh\"},\n\t\t{\"43543.503206018519\", \"[$-43]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 TK Sesh\"},\n\t\t{\"43543.503206018519\", \"[$-43]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mart 19 2019  12:04 TK seshanba\"},\n\t\t{\"44562.189571759256\", \"[$-7C43]mmm dd yyyy  h:mm AM/PM\", \"Yan 01 2022  4:32 TO\"},\n\t\t{\"44562.189571759256\", \"[$-7C43]mmmm dd yyyy  h:mm AM/PM\", \"Yanvar 01 2022  4:32 TO\"},\n\t\t{\"44562.189571759256\", \"[$-7C43]mmmmm dd yyyy  h:mm AM/PM\", \"Y 01 2022  4:32 TO\"},\n\t\t{\"44562.189571759256\", \"[$-7C43]mmmmmm dd yyyy  h:mm AM/PM\", \"Yanvar 01 2022  4:32 TO\"},\n\t\t{\"43543.503206018519\", \"[$-7C43]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 TK\"},\n\t\t{\"43543.503206018519\", \"[$-7C43]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mart 19 2019  12:04 TK Sesh\"},\n\t\t{\"43543.503206018519\", \"[$-7C43]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 TK Sesh\"},\n\t\t{\"43543.503206018519\", \"[$-7C43]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mart 19 2019  12:04 TK seshanba\"},\n\t\t{\"44562.189571759256\", \"[$-443]mmm dd yyyy  h:mm AM/PM\", \"Yan 01 2022  4:32 TO\"},\n\t\t{\"44562.189571759256\", \"[$-443]mmmm dd yyyy  h:mm AM/PM\", \"Yanvar 01 2022  4:32 TO\"},\n\t\t{\"44562.189571759256\", \"[$-443]mmmmm dd yyyy  h:mm AM/PM\", \"Y 01 2022  4:32 TO\"},\n\t\t{\"44562.189571759256\", \"[$-443]mmmmmm dd yyyy  h:mm AM/PM\", \"Yanvar 01 2022  4:32 TO\"},\n\t\t{\"43543.503206018519\", \"[$-443]mmm dd yyyy  h:mm AM/PM\", \"Mar 19 2019  12:04 TK\"},\n\t\t{\"43543.503206018519\", \"[$-443]mmmm dd yyyy  h:mm AM/PM aaa\", \"Mart 19 2019  12:04 TK Sesh\"},\n\t\t{\"43543.503206018519\", \"[$-443]mmmmm dd yyyy  h:mm AM/PM ddd\", \"M 19 2019  12:04 TK Sesh\"},\n\t\t{\"43543.503206018519\", \"[$-443]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Mart 19 2019  12:04 TK seshanba\"},\n\t\t{\"44562.189571759256\", \"[$-803]mmm dd yyyy  h:mm AM/PM\", \"gen. 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-803]mmmm dd yyyy  h:mm AM/PM\", \"gener 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-803]mmmmm dd yyyy  h:mm AM/PM\", \"g 01 2022  4:32 a. m.\"},\n\t\t{\"44562.189571759256\", \"[$-803]mmmmmm dd yyyy  h:mm AM/PM\", \"gener 01 2022  4:32 a. m.\"},\n\t\t{\"43543.503206018519\", \"[$-803]mmm dd yyyy  h:mm AM/PM\", \"març 19 2019  12:04 p. m.\"},\n\t\t{\"43543.503206018519\", \"[$-803]mmmm dd yyyy  h:mm AM/PM aaa\", \"març 19 2019  12:04 p. m. dt.\"},\n\t\t{\"43543.503206018519\", \"[$-803]mmmmm dd yyyy  h:mm AM/PM ddd\", \"m 19 2019  12:04 p. m. dt.\"},\n\t\t{\"43543.503206018519\", \"[$-803]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"març 19 2019  12:04 p. m. dimarts\"},\n\t\t{\"44562.189571759256\", \"[$-33]mmm dd yyyy  h:mm AM/PM\", \"Pha 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-33]mmmm dd yyyy  h:mm AM/PM\", \"Phando 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-33]mmmmm dd yyyy  h:mm AM/PM\", \"P 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-33]mmmmmm dd yyyy  h:mm AM/PM\", \"Phando 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-33]mmm dd yyyy  h:mm AM/PM\", \"Ṱhf 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-33]mmmm dd yyyy  h:mm AM/PM aaa\", \"Ṱhafamuhwe 19 2019  12:04 PM Vhi\"},\n\t\t{\"43543.503206018519\", \"[$-33]mmmmm dd yyyy  h:mm AM/PM ddd\", \"Ṱ 19 2019  12:04 PM Vhi\"},\n\t\t{\"43543.503206018519\", \"[$-33]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Ṱhafamuhwe 19 2019  12:04 PM Ḽavhuvhili\"},\n\t\t{\"44562.189571759256\", \"[$-433]mmm dd yyyy  h:mm AM/PM\", \"Pha 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-433]mmmm dd yyyy  h:mm AM/PM\", \"Phando 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-433]mmmmm dd yyyy  h:mm AM/PM\", \"P 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-433]mmmmmm dd yyyy  h:mm AM/PM\", \"Phando 01 2022  4:32 AM\"},\n\t\t{\"43543.503206018519\", \"[$-433]mmm dd yyyy  h:mm AM/PM\", \"Ṱhf 19 2019  12:04 PM\"},\n\t\t{\"43543.503206018519\", \"[$-433]mmmm dd yyyy  h:mm AM/PM aaa\", \"Ṱhafamuhwe 19 2019  12:04 PM Vhi\"},\n\t\t{\"43543.503206018519\", \"[$-433]mmmmm dd yyyy  h:mm AM/PM ddd\", \"Ṱ 19 2019  12:04 PM Vhi\"},\n\t\t{\"43543.503206018519\", \"[$-433]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"Ṱhafamuhwe 19 2019  12:04 PM Ḽavhuvhili\"},\n\t\t{\"44562.189571759256\", \"[$-2A]mmm dd yyyy  h:mm AM/PM\", \"Thg 1 01 2022  4:32 SA\"},\n\t\t{\"44593.189571759256\", \"[$-2A]mmm dd yyyy  h:mm AM/PM\", \"Thg 2 01 2022  4:32 SA\"},\n\t\t{\"44621.18957170139\", \"[$-2A]mmm dd yyyy  h:mm AM/PM\", \"Thg 3 01 2022  4:32 SA\"},\n\t\t{\"44652.18957170139\", \"[$-2A]mmm dd yyyy  h:mm AM/PM\", \"Thg 4 01 2022  4:32 SA\"},\n\t\t{\"44682.18957170139\", \"[$-2A]mmm dd yyyy  h:mm AM/PM\", \"Thg 5 01 2022  4:32 SA\"},\n\t\t{\"44713.18957170139\", \"[$-2A]mmm dd yyyy  h:mm AM/PM\", \"Thg 6 01 2022  4:32 SA\"},\n\t\t{\"44743.18957170139\", \"[$-2A]mmm dd yyyy  h:mm AM/PM\", \"Thg 7 01 2022  4:32 SA\"},\n\t\t{\"44774.18957170139\", \"[$-2A]mmm dd yyyy  h:mm AM/PM\", \"Thg 8 01 2022  4:32 SA\"},\n\t\t{\"44805.18957170139\", \"[$-2A]mmm dd yyyy  h:mm AM/PM\", \"Thg 9 01 2022  4:32 SA\"},\n\t\t{\"44835.18957170139\", \"[$-2A]mmm dd yyyy  h:mm AM/PM\", \"Thg 10 01 2022  4:32 SA\"},\n\t\t{\"44866.18957170139\", \"[$-2A]mmm dd yyyy  h:mm AM/PM\", \"Thg 11 01 2022  4:32 SA\"},\n\t\t{\"44896.18957170139\", \"[$-2A]mmm dd yyyy  h:mm AM/PM\", \"Thg 12 01 2022  4:32 SA\"},\n\t\t{\"44562.189571759256\", \"[$-2A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 1 01 2022  4:32 SA\"},\n\t\t{\"44593.189571759256\", \"[$-2A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 2 01 2022  4:32 SA\"},\n\t\t{\"44621.18957170139\", \"[$-2A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 3 01 2022  4:32 SA\"},\n\t\t{\"44652.18957170139\", \"[$-2A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 4 01 2022  4:32 SA\"},\n\t\t{\"44682.18957170139\", \"[$-2A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 5 01 2022  4:32 SA\"},\n\t\t{\"44713.18957170139\", \"[$-2A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 6 01 2022  4:32 SA\"},\n\t\t{\"44743.18957170139\", \"[$-2A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 7 01 2022  4:32 SA\"},\n\t\t{\"44774.18957170139\", \"[$-2A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 8 01 2022  4:32 SA\"},\n\t\t{\"44805.18957170139\", \"[$-2A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 9 01 2022  4:32 SA\"},\n\t\t{\"44835.18957170139\", \"[$-2A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 10 01 2022  4:32 SA\"},\n\t\t{\"44866.18957170139\", \"[$-2A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 11 01 2022  4:32 SA\"},\n\t\t{\"44896.18957170139\", \"[$-2A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 12 01 2022  4:32 SA\"},\n\t\t{\"44562.189571759256\", \"[$-2A]mmmmm dd yyyy  h:mm AM/PM\", \"T 1 01 2022  4:32 SA\"},\n\t\t{\"44593.189571759256\", \"[$-2A]mmmmm dd yyyy  h:mm AM/PM\", \"T 2 01 2022  4:32 SA\"},\n\t\t{\"44621.18957170139\", \"[$-2A]mmmmm dd yyyy  h:mm AM/PM\", \"T 3 01 2022  4:32 SA\"},\n\t\t{\"44652.18957170139\", \"[$-2A]mmmmm dd yyyy  h:mm AM/PM\", \"T 4 01 2022  4:32 SA\"},\n\t\t{\"44682.18957170139\", \"[$-2A]mmmmm dd yyyy  h:mm AM/PM\", \"T 5 01 2022  4:32 SA\"},\n\t\t{\"44713.18957170139\", \"[$-2A]mmmmm dd yyyy  h:mm AM/PM\", \"T 6 01 2022  4:32 SA\"},\n\t\t{\"44743.18957170139\", \"[$-2A]mmmmm dd yyyy  h:mm AM/PM\", \"T 7 01 2022  4:32 SA\"},\n\t\t{\"44774.18957170139\", \"[$-2A]mmmmm dd yyyy  h:mm AM/PM\", \"T 8 01 2022  4:32 SA\"},\n\t\t{\"44805.18957170139\", \"[$-2A]mmmmm dd yyyy  h:mm AM/PM\", \"T 9 01 2022  4:32 SA\"},\n\t\t{\"44835.18957170139\", \"[$-2A]mmmmm dd yyyy  h:mm AM/PM aaa\", \"T 10 01 2022  4:32 SA T7\"},\n\t\t{\"44866.18957170139\", \"[$-2A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"T 11 01 2022  4:32 SA T3\"},\n\t\t{\"44896.18957170139\", \"[$-2A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"T 12 01 2022  4:32 SA Th\\u01B0\\u0301 N\\u0103m\"},\n\t\t{\"44562.189571759256\", \"[$-42A]mmm dd yyyy  h:mm AM/PM\", \"Thg 1 01 2022  4:32 SA\"},\n\t\t{\"44593.189571759256\", \"[$-42A]mmm dd yyyy  h:mm AM/PM\", \"Thg 2 01 2022  4:32 SA\"},\n\t\t{\"44621.18957170139\", \"[$-42A]mmm dd yyyy  h:mm AM/PM\", \"Thg 3 01 2022  4:32 SA\"},\n\t\t{\"44652.18957170139\", \"[$-42A]mmm dd yyyy  h:mm AM/PM\", \"Thg 4 01 2022  4:32 SA\"},\n\t\t{\"44682.18957170139\", \"[$-42A]mmm dd yyyy  h:mm AM/PM\", \"Thg 5 01 2022  4:32 SA\"},\n\t\t{\"44713.18957170139\", \"[$-42A]mmm dd yyyy  h:mm AM/PM\", \"Thg 6 01 2022  4:32 SA\"},\n\t\t{\"44743.18957170139\", \"[$-42A]mmm dd yyyy  h:mm AM/PM\", \"Thg 7 01 2022  4:32 SA\"},\n\t\t{\"44774.18957170139\", \"[$-42A]mmm dd yyyy  h:mm AM/PM\", \"Thg 8 01 2022  4:32 SA\"},\n\t\t{\"44805.18957170139\", \"[$-42A]mmm dd yyyy  h:mm AM/PM\", \"Thg 9 01 2022  4:32 SA\"},\n\t\t{\"44835.18957170139\", \"[$-42A]mmm dd yyyy  h:mm AM/PM\", \"Thg 10 01 2022  4:32 SA\"},\n\t\t{\"44866.18957170139\", \"[$-42A]mmm dd yyyy  h:mm AM/PM\", \"Thg 11 01 2022  4:32 SA\"},\n\t\t{\"44896.18957170139\", \"[$-42A]mmm dd yyyy  h:mm AM/PM\", \"Thg 12 01 2022  4:32 SA\"},\n\t\t{\"44562.189571759256\", \"[$-42A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 1 01 2022  4:32 SA\"},\n\t\t{\"44593.189571759256\", \"[$-42A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 2 01 2022  4:32 SA\"},\n\t\t{\"44621.18957170139\", \"[$-42A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 3 01 2022  4:32 SA\"},\n\t\t{\"44652.18957170139\", \"[$-42A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 4 01 2022  4:32 SA\"},\n\t\t{\"44682.18957170139\", \"[$-42A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 5 01 2022  4:32 SA\"},\n\t\t{\"44713.18957170139\", \"[$-42A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 6 01 2022  4:32 SA\"},\n\t\t{\"44743.18957170139\", \"[$-42A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 7 01 2022  4:32 SA\"},\n\t\t{\"44774.18957170139\", \"[$-42A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 8 01 2022  4:32 SA\"},\n\t\t{\"44805.18957170139\", \"[$-42A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 9 01 2022  4:32 SA\"},\n\t\t{\"44835.18957170139\", \"[$-42A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 10 01 2022  4:32 SA\"},\n\t\t{\"44866.18957170139\", \"[$-42A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 11 01 2022  4:32 SA\"},\n\t\t{\"44896.18957170139\", \"[$-42A]mmmm dd yyyy  h:mm AM/PM\", \"Tháng 12 01 2022  4:32 SA\"},\n\t\t{\"44562.189571759256\", \"[$-42A]mmmmm dd yyyy  h:mm AM/PM\", \"T 1 01 2022  4:32 SA\"},\n\t\t{\"44593.189571759256\", \"[$-42A]mmmmm dd yyyy  h:mm AM/PM\", \"T 2 01 2022  4:32 SA\"},\n\t\t{\"44621.18957170139\", \"[$-42A]mmmmm dd yyyy  h:mm AM/PM\", \"T 3 01 2022  4:32 SA\"},\n\t\t{\"44652.18957170139\", \"[$-42A]mmmmm dd yyyy  h:mm AM/PM\", \"T 4 01 2022  4:32 SA\"},\n\t\t{\"44682.18957170139\", \"[$-42A]mmmmm dd yyyy  h:mm AM/PM\", \"T 5 01 2022  4:32 SA\"},\n\t\t{\"44713.18957170139\", \"[$-42A]mmmmm dd yyyy  h:mm AM/PM\", \"T 6 01 2022  4:32 SA\"},\n\t\t{\"44743.18957170139\", \"[$-42A]mmmmm dd yyyy  h:mm AM/PM\", \"T 7 01 2022  4:32 SA\"},\n\t\t{\"44774.18957170139\", \"[$-42A]mmmmm dd yyyy  h:mm AM/PM\", \"T 8 01 2022  4:32 SA\"},\n\t\t{\"44805.18957170139\", \"[$-42A]mmmmm dd yyyy  h:mm AM/PM\", \"T 9 01 2022  4:32 SA\"},\n\t\t{\"44835.18957170139\", \"[$-42A]mmmmm dd yyyy  h:mm AM/PM aaa\", \"T 10 01 2022  4:32 SA T7\"},\n\t\t{\"44866.18957170139\", \"[$-42A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"T 11 01 2022  4:32 SA T3\"},\n\t\t{\"44896.18957170139\", \"[$-42A]mmmmm dd yyyy  h:mm AM/PM dddd\", \"T 12 01 2022  4:32 SA Th\\u01B0\\u0301 N\\u0103m\"},\n\t\t{\"44562.189571759256\", \"[$-52]mmm dd yyyy  h:mm AM/PM\", \"Ion 01 2022  4:32 yb\"},\n\t\t{\"44593.189571759256\", \"[$-52]mmm dd yyyy  h:mm AM/PM\", \"Chwef 01 2022  4:32 yb\"},\n\t\t{\"44621.18957170139\", \"[$-52]mmm dd yyyy  h:mm AM/PM\", \"Maw 01 2022  4:32 yb\"},\n\t\t{\"44652.18957170139\", \"[$-52]mmm dd yyyy  h:mm AM/PM\", \"Ebr 01 2022  4:32 yb\"},\n\t\t{\"44682.18957170139\", \"[$-52]mmm dd yyyy  h:mm AM/PM\", \"Mai 01 2022  4:32 yb\"},\n\t\t{\"44713.18957170139\", \"[$-52]mmm dd yyyy  h:mm AM/PM\", \"Meh 01 2022  4:32 yb\"},\n\t\t{\"44743.18957170139\", \"[$-52]mmm dd yyyy  h:mm AM/PM\", \"Gorff 01 2022  4:32 yb\"},\n\t\t{\"44774.18957170139\", \"[$-52]mmm dd yyyy  h:mm AM/PM\", \"Awst 01 2022  4:32 yb\"},\n\t\t{\"44805.18957170139\", \"[$-52]mmm dd yyyy  h:mm AM/PM\", \"Medi 01 2022  4:32 yb\"},\n\t\t{\"44835.18957170139\", \"[$-52]mmm dd yyyy  h:mm AM/PM\", \"Hyd 01 2022  4:32 yb\"},\n\t\t{\"44866.18957170139\", \"[$-52]mmm dd yyyy  h:mm AM/PM\", \"Tach 01 2022  4:32 yb\"},\n\t\t{\"44896.18957170139\", \"[$-52]mmm dd yyyy  h:mm AM/PM\", \"Rhag 01 2022  4:32 yb\"},\n\t\t{\"44562.189571759256\", \"[$-52]mmmm dd yyyy  h:mm AM/PM\", \"Ionawr 01 2022  4:32 yb\"},\n\t\t{\"44593.189571759256\", \"[$-52]mmmm dd yyyy  h:mm AM/PM\", \"Chwefror 01 2022  4:32 yb\"},\n\t\t{\"44621.18957170139\", \"[$-52]mmmm dd yyyy  h:mm AM/PM\", \"Mawrth 01 2022  4:32 yb\"},\n\t\t{\"44652.18957170139\", \"[$-52]mmmm dd yyyy  h:mm AM/PM\", \"Ebrill 01 2022  4:32 yb\"},\n\t\t{\"44682.18957170139\", \"[$-52]mmmm dd yyyy  h:mm AM/PM\", \"Mai 01 2022  4:32 yb\"},\n\t\t{\"44713.18957170139\", \"[$-52]mmmm dd yyyy  h:mm AM/PM\", \"Mehefin 01 2022  4:32 yb\"},\n\t\t{\"44743.18957170139\", \"[$-52]mmmm dd yyyy  h:mm AM/PM\", \"Gorffennaf 01 2022  4:32 yb\"},\n\t\t{\"44774.18957170139\", \"[$-52]mmmm dd yyyy  h:mm AM/PM\", \"Awst 01 2022  4:32 yb\"},\n\t\t{\"44805.18957170139\", \"[$-52]mmmm dd yyyy  h:mm AM/PM\", \"Medi 01 2022  4:32 yb\"},\n\t\t{\"44835.18957170139\", \"[$-52]mmmm dd yyyy  h:mm AM/PM\", \"Hydref 01 2022  4:32 yb\"},\n\t\t{\"44866.18957170139\", \"[$-52]mmmm dd yyyy  h:mm AM/PM\", \"Tachwedd 01 2022  4:32 yb\"},\n\t\t{\"44896.18957170139\", \"[$-52]mmmm dd yyyy  h:mm AM/PM\", \"Rhagfyr 01 2022  4:32 yb\"},\n\t\t{\"44562.189571759256\", \"[$-52]mmmmm dd yyyy  h:mm AM/PM\", \"I 01 2022  4:32 yb\"},\n\t\t{\"44593.189571759256\", \"[$-52]mmmmm dd yyyy  h:mm AM/PM\", \"C 01 2022  4:32 yb\"},\n\t\t{\"44621.18957170139\", \"[$-52]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 yb\"},\n\t\t{\"44652.18957170139\", \"[$-52]mmmmm dd yyyy  h:mm AM/PM\", \"E 01 2022  4:32 yb\"},\n\t\t{\"44682.18957170139\", \"[$-52]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 yb\"},\n\t\t{\"44713.18957170139\", \"[$-52]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 yb\"},\n\t\t{\"44743.18957170139\", \"[$-52]mmmmm dd yyyy  h:mm AM/PM\", \"G 01 2022  4:32 yb\"},\n\t\t{\"44774.18957170139\", \"[$-52]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 yb\"},\n\t\t{\"44805.18957170139\", \"[$-52]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 yb\"},\n\t\t{\"44835.18957170139\", \"[$-52]mmmmm dd yyyy  h:mm AM/PM aaa\", \"H 01 2022  4:32 yb Sad\"},\n\t\t{\"44866.18957170139\", \"[$-52]mmmmm dd yyyy  h:mm AM/PM ddd\", \"T 01 2022  4:32 yb Maw\"},\n\t\t{\"44896.18957170139\", \"[$-52]mmmmm dd yyyy  h:mm AM/PM dddd\", \"R 01 2022  4:32 yb Dydd Iau\"},\n\t\t{\"44562.189571759256\", \"[$-452]mmm dd yyyy  h:mm AM/PM\", \"Ion 01 2022  4:32 yb\"},\n\t\t{\"44593.189571759256\", \"[$-452]mmm dd yyyy  h:mm AM/PM\", \"Chwef 01 2022  4:32 yb\"},\n\t\t{\"44621.18957170139\", \"[$-452]mmm dd yyyy  h:mm AM/PM\", \"Maw 01 2022  4:32 yb\"},\n\t\t{\"44652.18957170139\", \"[$-452]mmm dd yyyy  h:mm AM/PM\", \"Ebr 01 2022  4:32 yb\"},\n\t\t{\"44682.18957170139\", \"[$-452]mmm dd yyyy  h:mm AM/PM\", \"Mai 01 2022  4:32 yb\"},\n\t\t{\"44713.18957170139\", \"[$-452]mmm dd yyyy  h:mm AM/PM\", \"Meh 01 2022  4:32 yb\"},\n\t\t{\"44743.18957170139\", \"[$-452]mmm dd yyyy  h:mm AM/PM\", \"Gorff 01 2022  4:32 yb\"},\n\t\t{\"44774.18957170139\", \"[$-452]mmm dd yyyy  h:mm AM/PM\", \"Awst 01 2022  4:32 yb\"},\n\t\t{\"44805.18957170139\", \"[$-452]mmm dd yyyy  h:mm AM/PM\", \"Medi 01 2022  4:32 yb\"},\n\t\t{\"44835.18957170139\", \"[$-452]mmm dd yyyy  h:mm AM/PM\", \"Hyd 01 2022  4:32 yb\"},\n\t\t{\"44866.18957170139\", \"[$-452]mmm dd yyyy  h:mm AM/PM\", \"Tach 01 2022  4:32 yb\"},\n\t\t{\"44896.18957170139\", \"[$-452]mmm dd yyyy  h:mm AM/PM\", \"Rhag 01 2022  4:32 yb\"},\n\t\t{\"44562.189571759256\", \"[$-452]mmmm dd yyyy  h:mm AM/PM\", \"Ionawr 01 2022  4:32 yb\"},\n\t\t{\"44593.189571759256\", \"[$-452]mmmm dd yyyy  h:mm AM/PM\", \"Chwefror 01 2022  4:32 yb\"},\n\t\t{\"44621.18957170139\", \"[$-452]mmmm dd yyyy  h:mm AM/PM\", \"Mawrth 01 2022  4:32 yb\"},\n\t\t{\"44652.18957170139\", \"[$-452]mmmm dd yyyy  h:mm AM/PM\", \"Ebrill 01 2022  4:32 yb\"},\n\t\t{\"44682.18957170139\", \"[$-452]mmmm dd yyyy  h:mm AM/PM\", \"Mai 01 2022  4:32 yb\"},\n\t\t{\"44713.18957170139\", \"[$-452]mmmm dd yyyy  h:mm AM/PM\", \"Mehefin 01 2022  4:32 yb\"},\n\t\t{\"44743.18957170139\", \"[$-452]mmmm dd yyyy  h:mm AM/PM\", \"Gorffennaf 01 2022  4:32 yb\"},\n\t\t{\"44774.18957170139\", \"[$-452]mmmm dd yyyy  h:mm AM/PM\", \"Awst 01 2022  4:32 yb\"},\n\t\t{\"44805.18957170139\", \"[$-452]mmmm dd yyyy  h:mm AM/PM\", \"Medi 01 2022  4:32 yb\"},\n\t\t{\"44835.18957170139\", \"[$-452]mmmm dd yyyy  h:mm AM/PM\", \"Hydref 01 2022  4:32 yb\"},\n\t\t{\"44866.18957170139\", \"[$-452]mmmm dd yyyy  h:mm AM/PM\", \"Tachwedd 01 2022  4:32 yb\"},\n\t\t{\"44896.18957170139\", \"[$-452]mmmm dd yyyy  h:mm AM/PM\", \"Rhagfyr 01 2022  4:32 yb\"},\n\t\t{\"44562.189571759256\", \"[$-452]mmmmm dd yyyy  h:mm AM/PM\", \"I 01 2022  4:32 yb\"},\n\t\t{\"44593.189571759256\", \"[$-452]mmmmm dd yyyy  h:mm AM/PM\", \"C 01 2022  4:32 yb\"},\n\t\t{\"44621.18957170139\", \"[$-452]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 yb\"},\n\t\t{\"44652.18957170139\", \"[$-452]mmmmm dd yyyy  h:mm AM/PM\", \"E 01 2022  4:32 yb\"},\n\t\t{\"44682.18957170139\", \"[$-452]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 yb\"},\n\t\t{\"44713.18957170139\", \"[$-452]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 yb\"},\n\t\t{\"44743.18957170139\", \"[$-452]mmmmm dd yyyy  h:mm AM/PM\", \"G 01 2022  4:32 yb\"},\n\t\t{\"44774.18957170139\", \"[$-452]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 yb\"},\n\t\t{\"44805.18957170139\", \"[$-452]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 yb\"},\n\t\t{\"44835.18957170139\", \"[$-452]mmmmm dd yyyy  h:mm AM/PM aaa\", \"H 01 2022  4:32 yb Sad\"},\n\t\t{\"44866.18957170139\", \"[$-452]mmmmm dd yyyy  h:mm AM/PM ddd\", \"T 01 2022  4:32 yb Maw\"},\n\t\t{\"44896.18957170139\", \"[$-452]mmmmm dd yyyy  h:mm AM/PM dddd\", \"R 01 2022  4:32 yb Dydd Iau\"},\n\t\t{\"44562.189571759256\", \"[$-88]mmm dd yyyy  h:mm AM/PM\", \"Sam. 01 2022  4:32 Sub\"},\n\t\t{\"44593.189571759256\", \"[$-88]mmm dd yyyy  h:mm AM/PM\", \"Few. 01 2022  4:32 Sub\"},\n\t\t{\"44621.18957170139\", \"[$-88]mmm dd yyyy  h:mm AM/PM\", \"Maa 01 2022  4:32 Sub\"},\n\t\t{\"44652.18957170139\", \"[$-88]mmm dd yyyy  h:mm AM/PM\", \"Awr. 01 2022  4:32 Sub\"},\n\t\t{\"44682.18957170139\", \"[$-88]mmm dd yyyy  h:mm AM/PM\", \"Me 01 2022  4:32 Sub\"},\n\t\t{\"44713.18957170139\", \"[$-88]mmm dd yyyy  h:mm AM/PM\", \"Suw 01 2022  4:32 Sub\"},\n\t\t{\"44743.18957170139\", \"[$-88]mmm dd yyyy  h:mm AM/PM\", \"Sul. 01 2022  4:32 Sub\"},\n\t\t{\"44774.18957170139\", \"[$-88]mmm dd yyyy  h:mm AM/PM\", \"Ut 01 2022  4:32 Sub\"},\n\t\t{\"44805.18957170139\", \"[$-88]mmm dd yyyy  h:mm AM/PM\", \"Sept. 01 2022  4:32 Sub\"},\n\t\t{\"44835.18957170139\", \"[$-88]mmm dd yyyy  h:mm AM/PM\", \"Okt. 01 2022  4:32 Sub\"},\n\t\t{\"44866.18957170139\", \"[$-88]mmm dd yyyy  h:mm AM/PM\", \"Now. 01 2022  4:32 Sub\"},\n\t\t{\"44896.18957170139\", \"[$-88]mmm dd yyyy  h:mm AM/PM\", \"Des. 01 2022  4:32 Sub\"},\n\t\t{\"44562.189571759256\", \"[$-88]mmmm dd yyyy  h:mm AM/PM\", \"Samwiye 01 2022  4:32 Sub\"},\n\t\t{\"44593.189571759256\", \"[$-88]mmmm dd yyyy  h:mm AM/PM\", \"Fewriye 01 2022  4:32 Sub\"},\n\t\t{\"44621.18957170139\", \"[$-88]mmmm dd yyyy  h:mm AM/PM\", \"Maars 01 2022  4:32 Sub\"},\n\t\t{\"44652.18957170139\", \"[$-88]mmmm dd yyyy  h:mm AM/PM\", \"Awril 01 2022  4:32 Sub\"},\n\t\t{\"44682.18957170139\", \"[$-88]mmmm dd yyyy  h:mm AM/PM\", \"Me 01 2022  4:32 Sub\"},\n\t\t{\"44713.18957170139\", \"[$-88]mmmm dd yyyy  h:mm AM/PM\", \"Suwe 01 2022  4:32 Sub\"},\n\t\t{\"44743.18957170139\", \"[$-88]mmmm dd yyyy  h:mm AM/PM\", \"Sullet 01 2022  4:32 Sub\"},\n\t\t{\"44774.18957170139\", \"[$-88]mmmm dd yyyy  h:mm AM/PM\", \"Ut 01 2022  4:32 Sub\"},\n\t\t{\"44805.18957170139\", \"[$-88]mmmm dd yyyy  h:mm AM/PM\", \"Septàmbar 01 2022  4:32 Sub\"},\n\t\t{\"44835.18957170139\", \"[$-88]mmmm dd yyyy  h:mm AM/PM\", \"Oktoobar 01 2022  4:32 Sub\"},\n\t\t{\"44866.18957170139\", \"[$-88]mmmm dd yyyy  h:mm AM/PM\", \"Noowàmbar 01 2022  4:32 Sub\"},\n\t\t{\"44896.18957170139\", \"[$-88]mmmm dd yyyy  h:mm AM/PM\", \"Desàmbar 01 2022  4:32 Sub\"},\n\t\t{\"44562.189571759256\", \"[$-88]mmmmm dd yyyy  h:mm AM/PM\", \"S 01 2022  4:32 Sub\"},\n\t\t{\"44593.189571759256\", \"[$-88]mmmmm dd yyyy  h:mm AM/PM\", \"F 01 2022  4:32 Sub\"},\n\t\t{\"44621.18957170139\", \"[$-88]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 Sub\"},\n\t\t{\"44652.18957170139\", \"[$-88]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 Sub\"},\n\t\t{\"44682.18957170139\", \"[$-88]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 Sub\"},\n\t\t{\"44713.18957170139\", \"[$-88]mmmmm dd yyyy  h:mm AM/PM\", \"S 01 2022  4:32 Sub\"},\n\t\t{\"44743.18957170139\", \"[$-88]mmmmm dd yyyy  h:mm AM/PM\", \"S 01 2022  4:32 Sub\"},\n\t\t{\"44774.18957170139\", \"[$-88]mmmmm dd yyyy  h:mm AM/PM\", \"U 01 2022  4:32 Sub\"},\n\t\t{\"44805.18957170139\", \"[$-88]mmmmm dd yyyy  h:mm AM/PM\", \"S 01 2022  4:32 Sub\"},\n\t\t{\"44835.18957170139\", \"[$-88]mmmmm dd yyyy  h:mm AM/PM aaa\", \"O 01 2022  4:32 Sub Gaa.\"},\n\t\t{\"44866.18957170139\", \"[$-88]mmmmm dd yyyy  h:mm AM/PM ddd\", \"N 01 2022  4:32 Sub Tal.\"},\n\t\t{\"44896.18957170139\", \"[$-88]mmmmm dd yyyy  h:mm AM/PM dddd\", \"D 01 2022  4:32 Sub Alxames\"},\n\t\t{\"44562.189571759256\", \"[$-488]mmm dd yyyy  h:mm AM/PM\", \"Sam. 01 2022  4:32 Sub\"},\n\t\t{\"44593.189571759256\", \"[$-488]mmm dd yyyy  h:mm AM/PM\", \"Few. 01 2022  4:32 Sub\"},\n\t\t{\"44621.18957170139\", \"[$-488]mmm dd yyyy  h:mm AM/PM\", \"Maa 01 2022  4:32 Sub\"},\n\t\t{\"44652.18957170139\", \"[$-488]mmm dd yyyy  h:mm AM/PM\", \"Awr. 01 2022  4:32 Sub\"},\n\t\t{\"44682.18957170139\", \"[$-488]mmm dd yyyy  h:mm AM/PM\", \"Me 01 2022  4:32 Sub\"},\n\t\t{\"44713.18957170139\", \"[$-488]mmm dd yyyy  h:mm AM/PM\", \"Suw 01 2022  4:32 Sub\"},\n\t\t{\"44743.18957170139\", \"[$-488]mmm dd yyyy  h:mm AM/PM\", \"Sul. 01 2022  4:32 Sub\"},\n\t\t{\"44774.18957170139\", \"[$-488]mmm dd yyyy  h:mm AM/PM\", \"Ut 01 2022  4:32 Sub\"},\n\t\t{\"44805.18957170139\", \"[$-488]mmm dd yyyy  h:mm AM/PM\", \"Sept. 01 2022  4:32 Sub\"},\n\t\t{\"44835.18957170139\", \"[$-488]mmm dd yyyy  h:mm AM/PM\", \"Okt. 01 2022  4:32 Sub\"},\n\t\t{\"44866.18957170139\", \"[$-488]mmm dd yyyy  h:mm AM/PM\", \"Now. 01 2022  4:32 Sub\"},\n\t\t{\"44896.18957170139\", \"[$-488]mmm dd yyyy  h:mm AM/PM\", \"Des. 01 2022  4:32 Sub\"},\n\t\t{\"44562.189571759256\", \"[$-488]mmmm dd yyyy  h:mm AM/PM\", \"Samwiye 01 2022  4:32 Sub\"},\n\t\t{\"44593.189571759256\", \"[$-488]mmmm dd yyyy  h:mm AM/PM\", \"Fewriye 01 2022  4:32 Sub\"},\n\t\t{\"44621.18957170139\", \"[$-488]mmmm dd yyyy  h:mm AM/PM\", \"Maars 01 2022  4:32 Sub\"},\n\t\t{\"44652.18957170139\", \"[$-488]mmmm dd yyyy  h:mm AM/PM\", \"Awril 01 2022  4:32 Sub\"},\n\t\t{\"44682.18957170139\", \"[$-488]mmmm dd yyyy  h:mm AM/PM\", \"Me 01 2022  4:32 Sub\"},\n\t\t{\"44713.18957170139\", \"[$-488]mmmm dd yyyy  h:mm AM/PM\", \"Suwe 01 2022  4:32 Sub\"},\n\t\t{\"44743.18957170139\", \"[$-488]mmmm dd yyyy  h:mm AM/PM\", \"Sullet 01 2022  4:32 Sub\"},\n\t\t{\"44774.18957170139\", \"[$-488]mmmm dd yyyy  h:mm AM/PM\", \"Ut 01 2022  4:32 Sub\"},\n\t\t{\"44805.18957170139\", \"[$-488]mmmm dd yyyy  h:mm AM/PM\", \"Septàmbar 01 2022  4:32 Sub\"},\n\t\t{\"44835.18957170139\", \"[$-488]mmmm dd yyyy  h:mm AM/PM\", \"Oktoobar 01 2022  4:32 Sub\"},\n\t\t{\"44866.18957170139\", \"[$-488]mmmm dd yyyy  h:mm AM/PM\", \"Noowàmbar 01 2022  4:32 Sub\"},\n\t\t{\"44896.18957170139\", \"[$-488]mmmm dd yyyy  h:mm AM/PM\", \"Desàmbar 01 2022  4:32 Sub\"},\n\t\t{\"44562.189571759256\", \"[$-488]mmmmm dd yyyy  h:mm AM/PM\", \"S 01 2022  4:32 Sub\"},\n\t\t{\"44593.189571759256\", \"[$-488]mmmmm dd yyyy  h:mm AM/PM\", \"F 01 2022  4:32 Sub\"},\n\t\t{\"44621.18957170139\", \"[$-488]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 Sub\"},\n\t\t{\"44652.18957170139\", \"[$-488]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 Sub\"},\n\t\t{\"44682.18957170139\", \"[$-488]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 Sub\"},\n\t\t{\"44713.18957170139\", \"[$-488]mmmmm dd yyyy  h:mm AM/PM\", \"S 01 2022  4:32 Sub\"},\n\t\t{\"44743.18957170139\", \"[$-488]mmmmm dd yyyy  h:mm AM/PM\", \"S 01 2022  4:32 Sub\"},\n\t\t{\"44774.18957170139\", \"[$-488]mmmmm dd yyyy  h:mm AM/PM\", \"U 01 2022  4:32 Sub\"},\n\t\t{\"44805.18957170139\", \"[$-488]mmmmm dd yyyy  h:mm AM/PM\", \"S 01 2022  4:32 Sub\"},\n\t\t{\"44835.18957170139\", \"[$-488]mmmmm dd yyyy  h:mm AM/PM aaa\", \"O 01 2022  4:32 Sub Gaa.\"},\n\t\t{\"44866.18957170139\", \"[$-488]mmmmm dd yyyy  h:mm AM/PM ddd\", \"N 01 2022  4:32 Sub Tal.\"},\n\t\t{\"44896.18957170139\", \"[$-488]mmmmm dd yyyy  h:mm AM/PM dddd\", \"D 01 2022  4:32 Sub Alxames\"},\n\t\t{\"44562.189571759256\", \"[$-34]mmm dd yyyy  h:mm AM/PM\", \"uJan. 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-34]mmm dd yyyy  h:mm AM/PM\", \"uFeb. 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-34]mmm dd yyyy  h:mm AM/PM\", \"uMat. 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-34]mmm dd yyyy  h:mm AM/PM\", \"uEpr. 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-34]mmm dd yyyy  h:mm AM/PM\", \"uMey. 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-34]mmm dd yyyy  h:mm AM/PM\", \"uJun. 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-34]mmm dd yyyy  h:mm AM/PM\", \"uJul. 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-34]mmm dd yyyy  h:mm AM/PM\", \"uAg. 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-34]mmm dd yyyy  h:mm AM/PM\", \"uSep. 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-34]mmm dd yyyy  h:mm AM/PM\", \"uOkt. 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-34]mmm dd yyyy  h:mm AM/PM\", \"uNov. 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-34]mmm dd yyyy  h:mm AM/PM\", \"uDis. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-34]mmmm dd yyyy  h:mm AM/PM\", \"uJanuwari 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-34]mmmm dd yyyy  h:mm AM/PM\", \"uFebuwari 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-34]mmmm dd yyyy  h:mm AM/PM\", \"uMatshi 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-34]mmmm dd yyyy  h:mm AM/PM\", \"uAprili 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-34]mmmm dd yyyy  h:mm AM/PM\", \"uMeyi 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-34]mmmm dd yyyy  h:mm AM/PM\", \"uJuni 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-34]mmmm dd yyyy  h:mm AM/PM\", \"uJulayi 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-34]mmmm dd yyyy  h:mm AM/PM\", \"uAgasti 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-34]mmmm dd yyyy  h:mm AM/PM\", \"uSeptemba 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-34]mmmm dd yyyy  h:mm AM/PM\", \"uOktobha 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-34]mmmm dd yyyy  h:mm AM/PM\", \"uNovemba 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-34]mmmm dd yyyy  h:mm AM/PM\", \"uDisemba 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-34]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-34]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-34]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-34]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-34]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-34]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-34]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-34]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-34]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-34]mmmmm dd yyyy  h:mm AM/PM aaa\", \"u 01 2022  4:32 AM uMgq.\"},\n\t\t{\"44866.18957170139\", \"[$-34]mmmmm dd yyyy  h:mm AM/PM ddd\", \"u 01 2022  4:32 AM uLwesib.\"},\n\t\t{\"44896.18957170139\", \"[$-34]mmmmm dd yyyy  h:mm AM/PM dddd\", \"u 01 2022  4:32 AM Lwesine\"},\n\t\t{\"44562.189571759256\", \"[$-434]mmm dd yyyy  h:mm AM/PM\", \"uJan. 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-434]mmm dd yyyy  h:mm AM/PM\", \"uFeb. 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-434]mmm dd yyyy  h:mm AM/PM\", \"uMat. 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-434]mmm dd yyyy  h:mm AM/PM\", \"uEpr. 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-434]mmm dd yyyy  h:mm AM/PM\", \"uMey. 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-434]mmm dd yyyy  h:mm AM/PM\", \"uJun. 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-434]mmm dd yyyy  h:mm AM/PM\", \"uJul. 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-434]mmm dd yyyy  h:mm AM/PM\", \"uAg. 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-434]mmm dd yyyy  h:mm AM/PM\", \"uSep. 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-434]mmm dd yyyy  h:mm AM/PM\", \"uOkt. 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-434]mmm dd yyyy  h:mm AM/PM\", \"uNov. 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-434]mmm dd yyyy  h:mm AM/PM\", \"uDis. 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-434]mmmm dd yyyy  h:mm AM/PM\", \"uJanuwari 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-434]mmmm dd yyyy  h:mm AM/PM\", \"uFebuwari 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-434]mmmm dd yyyy  h:mm AM/PM\", \"uMatshi 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-434]mmmm dd yyyy  h:mm AM/PM\", \"uAprili 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-434]mmmm dd yyyy  h:mm AM/PM\", \"uMeyi 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-434]mmmm dd yyyy  h:mm AM/PM\", \"uJuni 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-434]mmmm dd yyyy  h:mm AM/PM\", \"uJulayi 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-434]mmmm dd yyyy  h:mm AM/PM\", \"uAgasti 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-434]mmmm dd yyyy  h:mm AM/PM\", \"uSeptemba 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-434]mmmm dd yyyy  h:mm AM/PM\", \"uOktobha 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-434]mmmm dd yyyy  h:mm AM/PM\", \"uNovemba 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-434]mmmm dd yyyy  h:mm AM/PM\", \"uDisemba 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-434]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-434]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-434]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-434]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-434]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-434]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-434]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-434]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-434]mmmmm dd yyyy  h:mm AM/PM\", \"u 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-434]mmmmm dd yyyy  h:mm AM/PM aaa\", \"u 01 2022  4:32 AM uMgq.\"},\n\t\t{\"44866.18957170139\", \"[$-434]mmmmm dd yyyy  h:mm AM/PM ddd\", \"u 01 2022  4:32 AM uLwesib.\"},\n\t\t{\"44896.18957170139\", \"[$-434]mmmmm dd yyyy  h:mm AM/PM dddd\", \"u 01 2022  4:32 AM Lwesine\"},\n\t\t{\"44562.189571759256\", \"[$-78]mmm dd yyyy  h:mm AM/PM\", \"\\uA2CD\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44593.189571759256\", \"[$-78]mmm dd yyyy  h:mm AM/PM\", \"\\uA44D\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44621.18957170139\", \"[$-78]mmm dd yyyy  h:mm AM/PM\", \"\\uA315\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44652.18957170139\", \"[$-78]mmm dd yyyy  h:mm AM/PM\", \"\\uA1D6\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44682.18957170139\", \"[$-78]mmm dd yyyy  h:mm AM/PM\", \"\\uA26C\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44713.18957170139\", \"[$-78]mmm dd yyyy  h:mm AM/PM\", \"\\uA0D8\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44743.18957170139\", \"[$-78]mmm dd yyyy  h:mm AM/PM\", \"\\uA3C3\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44774.18957170139\", \"[$-78]mmm dd yyyy  h:mm AM/PM\", \"\\uA246\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44805.18957170139\", \"[$-78]mmm dd yyyy  h:mm AM/PM\", \"\\uA22C\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44835.18957170139\", \"[$-78]mmm dd yyyy  h:mm AM/PM\", \"\\uA2B0\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44866.18957170139\", \"[$-78]mmm dd yyyy  h:mm AM/PM\", \"\\uA2B0\\uA2AA\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44896.18957170139\", \"[$-78]mmm dd yyyy  h:mm AM/PM\", \"\\uA2B0\\uA44B\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44562.189571759256\", \"[$-78]mmmm dd yyyy  h:mm AM/PM\", \"\\uA2CD\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44593.189571759256\", \"[$-78]mmmm dd yyyy  h:mm AM/PM\", \"\\uA44D\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44621.18957170139\", \"[$-78]mmmm dd yyyy  h:mm AM/PM\", \"\\uA315\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44652.18957170139\", \"[$-78]mmmm dd yyyy  h:mm AM/PM\", \"\\uA1D6\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44682.18957170139\", \"[$-78]mmmm dd yyyy  h:mm AM/PM\", \"\\uA26C\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44713.18957170139\", \"[$-78]mmmm dd yyyy  h:mm AM/PM\", \"\\uA0D8\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44743.18957170139\", \"[$-78]mmmm dd yyyy  h:mm AM/PM\", \"\\uA3C3\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44774.18957170139\", \"[$-78]mmmm dd yyyy  h:mm AM/PM\", \"\\uA246\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44805.18957170139\", \"[$-78]mmmm dd yyyy  h:mm AM/PM\", \"\\uA22C\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44835.18957170139\", \"[$-78]mmmm dd yyyy  h:mm AM/PM\", \"\\uA2B0\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44866.18957170139\", \"[$-78]mmmm dd yyyy  h:mm AM/PM\", \"\\uA2B0\\uA2AA\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44896.18957170139\", \"[$-78]mmmm dd yyyy  h:mm AM/PM\", \"\\uA2B0\\uA44B\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44562.189571759256\", \"[$-78]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA2CD 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44593.189571759256\", \"[$-78]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA44D 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44621.18957170139\", \"[$-78]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA315 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44652.18957170139\", \"[$-78]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA1D6 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44682.18957170139\", \"[$-78]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA26C 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44713.18957170139\", \"[$-78]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA0D8 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44743.18957170139\", \"[$-78]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA3C3 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44774.18957170139\", \"[$-78]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA246 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44805.18957170139\", \"[$-78]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA22C 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44835.18957170139\", \"[$-78]mmmmm dd yyyy  h:mm AM/PM aaa\", \"\\uA2B0 01 2022  4:32 \\uA3B8\\uA111 \\uA18F\\uA0D8\"},\n\t\t{\"44866.18957170139\", \"[$-78]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\uA2B0 01 2022  4:32 \\uA3B8\\uA111 \\uA18F\\uA44D\"},\n\t\t{\"44896.18957170139\", \"[$-78]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\uA2B0 01 2022  4:32 \\uA3B8\\uA111 \\uA18F\\uA282\\uA1D6\"},\n\t\t{\"44562.189571759256\", \"[$-478]mmm dd yyyy  h:mm AM/PM\", \"\\uA2CD\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44593.189571759256\", \"[$-478]mmm dd yyyy  h:mm AM/PM\", \"\\uA44D\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44621.18957170139\", \"[$-478]mmm dd yyyy  h:mm AM/PM\", \"\\uA315\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44652.18957170139\", \"[$-478]mmm dd yyyy  h:mm AM/PM\", \"\\uA1D6\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44682.18957170139\", \"[$-478]mmm dd yyyy  h:mm AM/PM\", \"\\uA26C\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44713.18957170139\", \"[$-478]mmm dd yyyy  h:mm AM/PM\", \"\\uA0D8\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44743.18957170139\", \"[$-478]mmm dd yyyy  h:mm AM/PM\", \"\\uA3C3\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44774.18957170139\", \"[$-478]mmm dd yyyy  h:mm AM/PM\", \"\\uA246\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44805.18957170139\", \"[$-478]mmm dd yyyy  h:mm AM/PM\", \"\\uA22C\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44835.18957170139\", \"[$-478]mmm dd yyyy  h:mm AM/PM\", \"\\uA2B0\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44866.18957170139\", \"[$-478]mmm dd yyyy  h:mm AM/PM\", \"\\uA2B0\\uA2AA\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44896.18957170139\", \"[$-478]mmm dd yyyy  h:mm AM/PM\", \"\\uA2B0\\uA44B\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44562.189571759256\", \"[$-478]mmmm dd yyyy  h:mm AM/PM\", \"\\uA2CD\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44593.189571759256\", \"[$-478]mmmm dd yyyy  h:mm AM/PM\", \"\\uA44D\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44621.18957170139\", \"[$-478]mmmm dd yyyy  h:mm AM/PM\", \"\\uA315\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44652.18957170139\", \"[$-478]mmmm dd yyyy  h:mm AM/PM\", \"\\uA1D6\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44682.18957170139\", \"[$-478]mmmm dd yyyy  h:mm AM/PM\", \"\\uA26C\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44713.18957170139\", \"[$-478]mmmm dd yyyy  h:mm AM/PM\", \"\\uA0D8\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44743.18957170139\", \"[$-478]mmmm dd yyyy  h:mm AM/PM\", \"\\uA3C3\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44774.18957170139\", \"[$-478]mmmm dd yyyy  h:mm AM/PM\", \"\\uA246\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44805.18957170139\", \"[$-478]mmmm dd yyyy  h:mm AM/PM\", \"\\uA22C\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44835.18957170139\", \"[$-478]mmmm dd yyyy  h:mm AM/PM\", \"\\uA2B0\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44866.18957170139\", \"[$-478]mmmm dd yyyy  h:mm AM/PM\", \"\\uA2B0\\uA2AA\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44896.18957170139\", \"[$-478]mmmm dd yyyy  h:mm AM/PM\", \"\\uA2B0\\uA44B\\uA1AA 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44562.189571759256\", \"[$-478]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA2CD 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44593.189571759256\", \"[$-478]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA44D 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44621.18957170139\", \"[$-478]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA315 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44652.18957170139\", \"[$-478]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA1D6 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44682.18957170139\", \"[$-478]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA26C 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44713.18957170139\", \"[$-478]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA0D8 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44743.18957170139\", \"[$-478]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA3C3 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44774.18957170139\", \"[$-478]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA246 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44805.18957170139\", \"[$-478]mmmmm dd yyyy  h:mm AM/PM\", \"\\uA22C 01 2022  4:32 \\uA3B8\\uA111\"},\n\t\t{\"44835.18957170139\", \"[$-478]mmmmm dd yyyy  h:mm AM/PM aaa\", \"\\uA2B0 01 2022  4:32 \\uA3B8\\uA111 \\uA18F\\uA0D8\"},\n\t\t{\"44866.18957170139\", \"[$-478]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\uA2B0 01 2022  4:32 \\uA3B8\\uA111 \\uA18F\\uA44D\"},\n\t\t{\"44896.18957170139\", \"[$-478]mmmmm dd yyyy  h:mm AM/PM dddd\", \"\\uA2B0 01 2022  4:32 \\uA3B8\\uA111 \\uA18F\\uA282\\uA1D6\"},\n\t\t{\"44562.189571759256\", \"[$-43D]mmm dd yyyy  h:mm AM/PM\", \"\\u05D9\\u05D0\\u05B7\\u05E0 01 2022  4:32 \\u05E4\\u05BF\\u05D0\\u05B7\\u05E8\\u05DE\\u05D9\\u05D8\\u05D0\\u05B8\\u05D2\"},\n\t\t{\"44562.189571759256\", \"[$-43D]mmmm dd yyyy  h:mm AM/PM\", \"\\u05D9\\u05D0\\u05B7\\u05E0\\u05D5\\u05D0\\u05B7\\u05E8 01 2022  4:32 \\u05E4\\u05BF\\u05D0\\u05B7\\u05E8\\u05DE\\u05D9\\u05D8\\u05D0\\u05B8\\u05D2\"},\n\t\t{\"44562.189571759256\", \"[$-43D]mmmmm dd yyyy  h:mm AM/PM\", \"\\u05D9 01 2022  4:32 \\u05E4\\u05BF\\u05D0\\u05B7\\u05E8\\u05DE\\u05D9\\u05D8\\u05D0\\u05B8\\u05D2\"},\n\t\t{\"44562.189571759256\", \"[$-43D]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u05D9\\u05D0\\u05B7\\u05E0\\u05D5\\u05D0\\u05B7\\u05E8 01 2022  4:32 \\u05E4\\u05BF\\u05D0\\u05B7\\u05E8\\u05DE\\u05D9\\u05D8\\u05D0\\u05B8\\u05D2\"},\n\t\t{\"43543.503206018519\", \"[$-43D]mmm dd yyyy  h:mm AM/PM\", \"\\u05DE\\u05E2\\u05E8\\u05E5 19 2019  12:04 \\u05E0\\u05D0\\u05B8\\u05DB\\u05DE\\u05D9\\u05D8\\u05D0\\u05B8\\u05D2\"},\n\t\t{\"43543.503206018519\", \"[$-43D]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u05DE\\u05E2\\u05E8\\u05E5 19 2019  12:04 \\u05E0\\u05D0\\u05B8\\u05DB\\u05DE\\u05D9\\u05D8\\u05D0\\u05B8\\u05D2 \\u05D9\\u05D5\\u05DD \\u05D2\"},\n\t\t{\"43543.503206018519\", \"[$-43D]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u05DE 19 2019  12:04 \\u05E0\\u05D0\\u05B8\\u05DB\\u05DE\\u05D9\\u05D8\\u05D0\\u05B8\\u05D2 \\u05D9\\u05D5\\u05DD \\u05D2\"},\n\t\t{\"43543.503206018519\", \"[$-43D]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u05DE\\u05E2\\u05E8\\u05E5 19 2019  12:04 \\u05E0\\u05D0\\u05B8\\u05DB\\u05DE\\u05D9\\u05D8\\u05D0\\u05B8\\u05D2 \\u05D3\\u05D9\\u05E0\\u05E1\\u05D8\\u05D9\\u05E7\"},\n\t\t{\"44562.189571759256\", \"[$-6A]mmm dd yyyy  h:mm AM/PM\", \"\\u1E62\\u1EB9\\u0301 01 2022  4:32 Àár\\u1ECD\\u0300\"},\n\t\t{\"44562.189571759256\", \"[$-6A]mmmm dd yyyy  h:mm AM/PM\", \"\\u1E62\\u1EB9\\u0301r\\u1EB9\\u0301 01 2022  4:32 Àár\\u1ECD\\u0300\"},\n\t\t{\"44562.189571759256\", \"[$-6A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u1E62 01 2022  4:32 Àár\\u1ECD\\u0300\"},\n\t\t{\"44562.189571759256\", \"[$-6A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u1E62\\u1EB9\\u0301r\\u1EB9\\u0301 01 2022  4:32 Àár\\u1ECD\\u0300\"},\n\t\t{\"43543.503206018519\", \"[$-6A]mmm dd yyyy  h:mm AM/PM\", \"\\u1EB8r 19 2019  12:04 \\u1ECC\\u0300sán\"},\n\t\t{\"43543.503206018519\", \"[$-6A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u1EB8r\\u1EB9\\u0300nà 19 2019  12:04 \\u1ECC\\u0300sán Ì\\u1E63g\"},\n\t\t{\"43543.503206018519\", \"[$-6A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u1EB8 19 2019  12:04 \\u1ECC\\u0300sán Ì\\u1E63g\"},\n\t\t{\"43543.503206018519\", \"[$-6A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u1EB8r\\u1EB9\\u0300nà 19 2019  12:04 \\u1ECC\\u0300sán \\u1ECCj\\u1ECD\\u0301 Ìs\\u1EB9\\u0301gun\"},\n\t\t{\"44562.189571759256\", \"[$-46A]mmm dd yyyy  h:mm AM/PM\", \"\\u1E62\\u1EB9\\u0301 01 2022  4:32 Àár\\u1ECD\\u0300\"},\n\t\t{\"44562.189571759256\", \"[$-46A]mmmm dd yyyy  h:mm AM/PM\", \"\\u1E62\\u1EB9\\u0301r\\u1EB9\\u0301 01 2022  4:32 Àár\\u1ECD\\u0300\"},\n\t\t{\"44562.189571759256\", \"[$-46A]mmmmm dd yyyy  h:mm AM/PM\", \"\\u1E62 01 2022  4:32 Àár\\u1ECD\\u0300\"},\n\t\t{\"44562.189571759256\", \"[$-46A]mmmmmm dd yyyy  h:mm AM/PM\", \"\\u1E62\\u1EB9\\u0301r\\u1EB9\\u0301 01 2022  4:32 Àár\\u1ECD\\u0300\"},\n\t\t{\"43543.503206018519\", \"[$-46A]mmm dd yyyy  h:mm AM/PM\", \"\\u1EB8r 19 2019  12:04 \\u1ECC\\u0300sán\"},\n\t\t{\"43543.503206018519\", \"[$-46A]mmmm dd yyyy  h:mm AM/PM aaa\", \"\\u1EB8r\\u1EB9\\u0300nà 19 2019  12:04 \\u1ECC\\u0300sán Ì\\u1E63g\"},\n\t\t{\"43543.503206018519\", \"[$-46A]mmmmm dd yyyy  h:mm AM/PM ddd\", \"\\u1EB8 19 2019  12:04 \\u1ECC\\u0300sán Ì\\u1E63g\"},\n\t\t{\"43543.503206018519\", \"[$-46A]mmmmmm dd yyyy  h:mm AM/PM dddd\", \"\\u1EB8r\\u1EB9\\u0300nà 19 2019  12:04 \\u1ECC\\u0300sán \\u1ECCj\\u1ECD\\u0301 Ìs\\u1EB9\\u0301gun\"},\n\t\t{\"44562.189571759256\", \"[$-35]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-35]mmm dd yyyy  h:mm AM/PM\", \"Feb 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-35]mmm dd yyyy  h:mm AM/PM\", \"Mas 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-35]mmm dd yyyy  h:mm AM/PM\", \"Eph 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-35]mmm dd yyyy  h:mm AM/PM\", \"Mey 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-35]mmm dd yyyy  h:mm AM/PM\", \"Jun 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-35]mmm dd yyyy  h:mm AM/PM\", \"Jul 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-35]mmm dd yyyy  h:mm AM/PM\", \"Agas 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-35]mmm dd yyyy  h:mm AM/PM\", \"Sep 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-35]mmm dd yyyy  h:mm AM/PM\", \"Okt 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-35]mmm dd yyyy  h:mm AM/PM\", \"Nov 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-35]mmm dd yyyy  h:mm AM/PM\", \"Dis 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-35]mmmm dd yyyy  h:mm AM/PM\", \"Januwari 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-35]mmmm dd yyyy  h:mm AM/PM\", \"Febhuwari 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-35]mmmm dd yyyy  h:mm AM/PM\", \"Mashi 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-35]mmmm dd yyyy  h:mm AM/PM\", \"Ephreli 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-35]mmmm dd yyyy  h:mm AM/PM\", \"Meyi 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-35]mmmm dd yyyy  h:mm AM/PM\", \"Juni 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-35]mmmm dd yyyy  h:mm AM/PM\", \"Julayi 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-35]mmmm dd yyyy  h:mm AM/PM\", \"Agasti 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-35]mmmm dd yyyy  h:mm AM/PM\", \"Septemba 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-35]mmmm dd yyyy  h:mm AM/PM\", \"Okthoba 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-35]mmmm dd yyyy  h:mm AM/PM\", \"Novemba 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-35]mmmm dd yyyy  h:mm AM/PM\", \"Disemba 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-35]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-35]mmmmm dd yyyy  h:mm AM/PM\", \"F 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-35]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-35]mmmmm dd yyyy  h:mm AM/PM\", \"E 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-35]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-35]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-35]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-35]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-35]mmmmm dd yyyy  h:mm AM/PM\", \"S 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-35]mmmmm dd yyyy  h:mm AM/PM aaa\", \"O 01 2022  4:32 AM Mgq.\"},\n\t\t{\"44866.18957170139\", \"[$-35]mmmmm dd yyyy  h:mm AM/PM ddd\", \"N 01 2022  4:32 AM Bi.\"},\n\t\t{\"44896.18957170139\", \"[$-35]mmmmm dd yyyy  h:mm AM/PM dddd\", \"D 01 2022  4:32 AM ULwesine\"},\n\t\t{\"44562.189571759256\", \"[$-435]mmm dd yyyy  h:mm AM/PM\", \"Jan 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-435]mmm dd yyyy  h:mm AM/PM\", \"Feb 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-435]mmm dd yyyy  h:mm AM/PM\", \"Mas 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-435]mmm dd yyyy  h:mm AM/PM\", \"Eph 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-435]mmm dd yyyy  h:mm AM/PM\", \"Mey 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-435]mmm dd yyyy  h:mm AM/PM\", \"Jun 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-435]mmm dd yyyy  h:mm AM/PM\", \"Jul 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-435]mmm dd yyyy  h:mm AM/PM\", \"Agas 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-435]mmm dd yyyy  h:mm AM/PM\", \"Sep 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-435]mmm dd yyyy  h:mm AM/PM\", \"Okt 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-435]mmm dd yyyy  h:mm AM/PM\", \"Nov 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-435]mmm dd yyyy  h:mm AM/PM\", \"Dis 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-435]mmmm dd yyyy  h:mm AM/PM\", \"Januwari 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-435]mmmm dd yyyy  h:mm AM/PM\", \"Febhuwari 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-435]mmmm dd yyyy  h:mm AM/PM\", \"Mashi 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-435]mmmm dd yyyy  h:mm AM/PM\", \"Ephreli 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-435]mmmm dd yyyy  h:mm AM/PM\", \"Meyi 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-435]mmmm dd yyyy  h:mm AM/PM\", \"Juni 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-435]mmmm dd yyyy  h:mm AM/PM\", \"Julayi 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-435]mmmm dd yyyy  h:mm AM/PM\", \"Agasti 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-435]mmmm dd yyyy  h:mm AM/PM\", \"Septemba 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-435]mmmm dd yyyy  h:mm AM/PM\", \"Okthoba 01 2022  4:32 AM\"},\n\t\t{\"44866.18957170139\", \"[$-435]mmmm dd yyyy  h:mm AM/PM\", \"Novemba 01 2022  4:32 AM\"},\n\t\t{\"44896.18957170139\", \"[$-435]mmmm dd yyyy  h:mm AM/PM\", \"Disemba 01 2022  4:32 AM\"},\n\t\t{\"44562.189571759256\", \"[$-435]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44593.189571759256\", \"[$-435]mmmmm dd yyyy  h:mm AM/PM\", \"F 01 2022  4:32 AM\"},\n\t\t{\"44621.18957170139\", \"[$-435]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 AM\"},\n\t\t{\"44652.18957170139\", \"[$-435]mmmmm dd yyyy  h:mm AM/PM\", \"E 01 2022  4:32 AM\"},\n\t\t{\"44682.18957170139\", \"[$-435]mmmmm dd yyyy  h:mm AM/PM\", \"M 01 2022  4:32 AM\"},\n\t\t{\"44713.18957170139\", \"[$-435]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44743.18957170139\", \"[$-435]mmmmm dd yyyy  h:mm AM/PM\", \"J 01 2022  4:32 AM\"},\n\t\t{\"44774.18957170139\", \"[$-435]mmmmm dd yyyy  h:mm AM/PM\", \"A 01 2022  4:32 AM\"},\n\t\t{\"44805.18957170139\", \"[$-435]mmmmm dd yyyy  h:mm AM/PM\", \"S 01 2022  4:32 AM\"},\n\t\t{\"44835.18957170139\", \"[$-435]mmmmm dd yyyy  h:mm AM/PM aaa\", \"O 01 2022  4:32 AM Mgq.\"},\n\t\t{\"44866.18957170139\", \"[$-435]mmmmm dd yyyy  h:mm AM/PM ddd\", \"N 01 2022  4:32 AM Bi.\"},\n\t\t{\"44896.18957170139\", \"[$-435]mmmmm dd yyyy  h:mm AM/PM dddd\", \"D 01 2022  4:32 AM ULwesine\"},\n\t\t{\"43543.503206018519\", \"[$-F800]dddd, mmmm dd, yyyy\", \"Tuesday, March 19, 2019\"},\n\t\t{\"43543.503206018519\", \"[$-F400]h:mm:ss AM/PM\", \"12:04:37 PM\"},\n\t\t{\"text_\", \"General\", \"text_\"},\n\t\t{\"text_\", \"\\\"=====\\\"@@@\\\"--\\\"@\\\"----\\\"\", \"=====text_text_text_--text_----\"},\n\t\t{\"0.0450685976001E+21\", \"0_);[Red]\\\\(0\\\\)\", \"45068597600100000000 \"},\n\t\t{\"8.0450685976001E+21\", \"0_);[Red]\\\\(0\\\\)\", \"8045068597600100000000 \"},\n\t\t{\"8.0450685976001E-21\", \"0_);[Red]\\\\(0\\\\)\", \"0 \"},\n\t\t{\"8.04506\", \"0_);[Red]\\\\(0\\\\)\", \"8 \"},\n\t\t{\"-0.0450685976001E+21\", \"0_);[Red]\\\\(0\\\\)\", \"(45068597600100000000)\"},\n\t\t{\"-8.0450685976001E+21\", \"0_);[Red]\\\\(0\\\\)\", \"(8045068597600100000000)\"},\n\t\t{\"-8.0450685976001E-21\", \"0_);[Red]\\\\(0\\\\)\", \"(0)\"},\n\t\t{\"-8.04506\", \"0_);[Red]\\\\(0\\\\)\", \"(8)\"},\n\t\t{\"-8.04506\", \"$#,##0.00_);[Red]($#,##0.00)\", \"($8.05)\"},\n\t\t{\"43543.5448726851\", `_(\"$\"* #,##0.00_);_(\"$\"* \\(#,##0.00\\);_(\"$\"* \"-\"??_);_(@_)`, \" $43,543.54 \"},\n\t\t{\"1234.5678\", \"0\", \"1235\"},\n\t\t{\"1234.125\", \"0.00\", \"1234.13\"},\n\t\t{\"1234.5678\", \"0.00\", \"1234.57\"},\n\t\t{\"1234.5678\", \"#,##0\", \"1,235\"},\n\t\t{\"1234.5678\", \"#,##0.00\", \"1,234.57\"},\n\t\t{\"1234.5678\", \"0%\", \"123457%\"},\n\t\t{\"1234.5678\", \"#,##0 ;(#,##0)\", \"1,235 \"},\n\t\t{\"1234.5678\", \"#,##0 ;[red](#,##0)\", \"1,235 \"},\n\t\t{\"1234.5678\", \"#,##0.00;(#,##0.00)\", \"1,234.57\"},\n\t\t{\"1234.5678\", \"#,##0.00;[red](#,##0.00)\", \"1,234.57\"},\n\t\t{\"1234.5678\", \"#\", \"1235\"},\n\t\t{\"1234.5678\", \"#0\", \"1235\"},\n\t\t{\"1234.5678\", \"##\", \"1235\"},\n\t\t{\"1234.5678\", \"00000.00#\", \"01234.568\"},\n\t\t{\"1234.5678\", \"00000####\", \"000001235\"},\n\t\t{\"1234.5678\", \"00000######\", \"000001235\"},\n\t\t{\"-1234.5678\", \"0.00\", \"-1234.57\"},\n\t\t{\"-1234.5678\", \"0.00;-0.00\", \"-1234.57\"},\n\t\t{\"-1234.5678\", \"0.00%%\", \"-12345678.00%%\"},\n\t\t{\"2.1\", \"mmss.0000\", \"2400.000\"},\n\t\t{\"0.007\", \"[h]:mm:ss.0\", \"0:10:04.8\"},\n\t\t{\"0.007\", \"[h]:mm:ss.00\", \"0:10:04.80\"},\n\t\t{\"0.007\", \"[h]:mm:ss.000\", \"0:10:04.800\"},\n\t\t{\"0.007\", \"[h]:mm:ss.0000\", \"0:10:04.800\"},\n\t\t{\"0.3270833333\", \"[h]:mm\", \"7:51\"},\n\t\t{\"0.5347222222\", \"[h]:mm\", \"12:50\"},\n\t\t{\"0.5833333333\", \"[h]:mm\", \"14:00\"},\n\t\t{\"0.5833333333\", \"hh\", \"14\"},\n\t\t{\"123\", \"[h]:mm,:ss.0\", \"2952:00,:00.0\"},\n\t\t{\"123\", \"yy-.dd\", \"00-.02\"},\n\t\t{\"123\", \"[DBNum1][$-804]yyyy\\\"年\\\"m\\\"月\\\";@\", \"\\u4E00\\u4E5D\\u25CB\\u25CB\\u5E74\\u4E94\\u6708\"},\n\t\t{\"123\", \"[DBNum2][$-804]yyyy\\\"年\\\"m\\\"月\\\";@\", \"\\u58F9\\u7396\\u96F6\\u96F6\\u5E74\\u4F0D\\u6708\"},\n\t\t{\"123\", \"[DBNum3][$-804]yyyy\\\"年\\\"m\\\"月\\\";@\", \"\\uFF11\\uFF19\\uFF10\\uFF10\\u5E74\\uFF15\\u6708\"},\n\t\t{\"1234567890\", \"[DBNum1][$-804]0.00\", \"\\u4E00\\u4E8C\\u4E09\\u56DB\\u4E94\\u516D\\u4E03\\u516B\\u4E5D\\u25CB.\\u25CB\\u25CB\"},\n\t\t{\"1234567890\", \"[DBNum2][$-804]0.00\", \"\\u58F9\\u8D30\\u53C1\\u8086\\u4F0D\\u9646\\u67D2\\u634C\\u7396\\u96F6.\\u96F6\\u96F6\"},\n\t\t{\"1234567890\", \"[DBNum3][$-804]0.00\", \"\\uFF11\\uFF12\\uFF13\\uFF14\\uFF15\\uFF16\\uFF17\\uFF18\\uFF19\\uFF10.\\uFF10\\uFF10\"},\n\t\t{\"1234.5678\", \"0.00###\", \"1234.5678\"},\n\t\t{\"1234.5678\", \"00000.00###\", \"01234.5678\"},\n\t\t{\"-1234.5678\", \"00000.00###;;\", \"\"},\n\t\t{\"1234.5678\", \"0.00000\", \"1234.56780\"},\n\t\t{\"8.8888666665555487\", \"0.00000\", \"8.88887\"},\n\t\t{\"8.8888666665555493e+19\", \"#,000.00\", \"88,888,666,665,555,500,000.00\"},\n\t\t{\"8.8888666665555493e+19\", \"0.00000\", \"88888666665555500000.00000\"},\n\t\t{\"37947.7500001\", \"0.00000000E+00\", \"3.79477500E+04\"},\n\t\t{\"2312312321.1231198\", \"0.00E+00\", \"2.31E+09\"},\n\t\t{\"3.2234623764278598E+33\", \"0.00E+00\", \"3.22E+33\"},\n\t\t{\"1.234E-16\", \"0.00000000000000000000\", \"0.00000000000000012340\"},\n\t\t{\"1.234E-16\", \"0.000000000000000000\", \"0.000000000000000123\"},\n\t\t{\"1.234E-16\", \"0.000000000000000000%\", \"0.000000000000012340%\"},\n\t\t{\"1.234E-16\", \"0.000000000000000000%%%%\", \"0.000000000000012340%\"},\n\t\t{\"-123.4567\", \"# ?/?\", \"-123 1/2\"},\n\t\t{\"123.4567\", \"# ??/??\", \"123 37/81\"},\n\t\t{\"123.4567\", \"#\\\\ ???/???\", \"123  58/127\"},\n\t\t{\"123.4567\", \"#\\\\ ?/2\", \"123 1/2\"},\n\t\t{\"123.4567\", \"#\\\\ ?/4\", \"123 2/4\"},\n\t\t{\"123.4567\", \"#\\\\ ?/8\", \"123 4/8\"},\n\t\t{\"123.4567\", \"#\\\\ ?/16\", \"123 7/16\"},\n\t\t{\"123.4567\", \"#\\\\ ?/10\", \"123 5/10\"},\n\t\t{\"-123.4567\", \"#\\\\ ?/100\", \"-123 46/100\"},\n\t\t{\"123.4567\", \"#\\\\ ?/1000\", \"123 457/1000\"},\n\t\t{\"1234.5678\", \"[$$-409]#,##0.00\", \"$1,234.57\"},\n\t\t{\"123\", \"[$x.-unknown]#,##0.00\", \"x.123.00\"},\n\t\t{\"123\", \"[$x.-unknown]MM/DD/YYYY\", \"x.05/02/1900\"},\n\t\t{\"1234.5678\", \"0.0xxx00\", \"1234.5xxx68\"},\n\t\t{\"80145.899999999994\", \"[$¥-8004]\\\" \\\"#\\\" \\\"####\\\"\\\"\", \"¥ 8 0146\"},\n\t\t{\"1\", \"?\", \"1\"},\n\t\t{\"1\", \"???\", \"  1\"},\n\t\t{\"0\", \"# ?/?\", \"0    \"},\n\t\t{\"0\", \"# ??/??\", \"0      \"},\n\t\t{\"0\", \"# ??/???\", \"0       \"},\n\t\t{\"31.69\", \"?#???\", \"  32\"},\n\t\t{\"314159.26535\", \"#,### ??/??\", \"314,159 13/49\"},\n\t\t{\"314159.26535\", \"# ??? ????/??\", \"314 159   13/49\"},\n\t\t{\"-3.14159265358979323\", \"# ?/?\", \"-3 1/7\"},\n\t\t{\"-3.14159265358979323\", \"# ??/??\", \"-3  1/7 \"},\n\t\t{\"-3.14159265358979323\", \"# ???/???\", \"-3  16/113\"},\n\t\t{\"5.81999\", `???? ????`, \"        6\"},\n\t\t{\"4.6\", `# #### ####`, \"  5\"},\n\t\t// Unsupported number format\n\t\t{\"37947.7500001\", \"0.00000000E+000\", \"37947.7500001\"},\n\t\t{\"123\", \"[DBNum4][$-804]yyyy\\\"年\\\"m\\\"月\\\";@\", \"123\"},\n\t\t// Invalid number format\n\t\t{\"123\", \"x0.00s\", \"123\"},\n\t\t{\"123\", \"[h]:m00m:ss\", \"123\"},\n\t\t{\"123\", \"yy-00dd\", \"123\"},\n\t\t{\"123\", \"yy-##dd\", \"123\"},\n\t\t{\"123\", \"xx[h]:mm,:ss.0xx\", \"xx2952:00,:00.0xx\"},\n\t\t{\"-123\", \"x0.00s\", \"-123\"},\n\t\t{\"-1234.5678\", \";E+;\", \"-1234.5678\"},\n\t\t{\"1234.5678\", \"E+;\", \"1234.5678\"},\n\t\t{\"1234.5678\", \"00000.00###s\", \"1234.5678\"},\n\t\t{\"-1234.5678\", \"00000.00###;s;\", \"-1234.5678\"},\n\t} {\n\t\tresult := format(item[0], item[1], false, CellTypeNumber, nil)\n\t\tassert.Equal(t, item[2], result, item)\n\t}\n\t// Test format number with specified date and time format code\n\tfor _, item := range [][]string{\n\t\t{\"43543.503206018519\", \"[$-F800]dddd, mmmm dd, yyyy\", \"2019年3月19日\"},\n\t\t{\"43543.503206018519\", \"[$-x-sysdate]dddd, mmmm dd, yyyy\", \"2019年3月19日\"},\n\t\t{\"43543.503206018519\", \"[$-F400]h:mm:ss AM/PM\", \"12:04:37\"},\n\t\t{\"43543.503206018519\", \"[$-x-systime]h:mm:ss AM/PM\", \"12:04:37\"},\n\t} {\n\t\tresult := format(item[0], item[1], false, CellTypeNumber, &Options{\n\t\t\tShortDatePattern: \"yyyy/m/d\",\n\t\t\tLongDatePattern:  \"yyyy\\\"年\\\"M\\\"月\\\"d\\\"日\\\"\",\n\t\t\tLongTimePattern:  \"H:mm:ss\",\n\t\t})\n\t\tassert.Equal(t, item[2], result, item)\n\t}\n\t// Test format number with string data type cell value\n\tfor _, cellType := range []CellType{CellTypeSharedString, CellTypeInlineString} {\n\t\tfor _, item := range [][]string{\n\t\t\t{\"1234.5678\", \"General\", \"1234.5678\"},\n\t\t\t{\"1234.5678\", \"yyyy\\\"年\\\"m\\\"月\\\"d\\\"日\\\";@\", \"1234.5678\"},\n\t\t\t{\"1234.5678\", \"h\\\"时\\\"mm\\\"分\\\"ss\\\"秒\\\";@\", \"1234.5678\"},\n\t\t\t{\"1234.5678\", \"\\\"¥\\\"#,##0.00_);\\\\(\\\"¥\\\"#,##0.00\\\\)\", \"1234.5678\"},\n\t\t\t{\"1234.5678\", \"0_);[Red]\\\\(0\\\\)\", \"1234.5678\"},\n\t\t\t{\"1234.5678\", \"\\\"text\\\"@\", \"text1234.5678\"},\n\t\t} {\n\t\t\tresult := format(item[0], item[1], false, cellType, nil)\n\t\t\tassert.Equal(t, item[2], result, item)\n\t\t}\n\t}\n\tnf := numberFormat{}\n\tchangeNumFmtCode, err := nf.currencyLanguageHandler(nfp.Token{Parts: []nfp.Part{{}}})\n\tassert.Equal(t, ErrUnsupportedNumberFormat, err)\n\tassert.False(t, changeNumFmtCode)\n}\n\nfunc BenchmarkNumFmtPlaceHolder(b *testing.B) {\n\titems := [][]string{\n\t\t{\"1\", \"?\", \"1\"},\n\t\t{\"1\", \"???\", \"  1\"},\n\t\t{\"0\", \"# ?/?\", \"0    \"},\n\t\t{\"0\", \"# ??/??\", \"0      \"},\n\t\t{\"0\", \"# ??/???\", \"0       \"},\n\t\t{\"31.69\", \"?#???\", \"  32\"},\n\t\t{\"314159.26535\", \"#,### ??/??\", \"314,159 13/49\"},\n\t\t{\"314159.26535\", \"# ??? ????/??\", \"314 159   13/49\"},\n\t\t{\"-3.14159265358979323\", \"# ?/?\", \"-3 1/7\"},\n\t\t{\"-3.14159265358979323\", \"# ??/??\", \"-3  1/7 \"},\n\t\t{\"123.4567\", \"#\\\\ ?/10\", \"123 5/10\"},\n\t}\n\tb.ReportAllocs()\n\tfor i := 0; i < b.N; i++ {\n\t\tfor _, item := range items {\n\t\t\t_ = format(item[0], item[1], false, CellTypeNumber, nil)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "picture.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"image\"\n\t\"io\"\n\t\"math\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// PictureInsertType defines the type of the picture has been inserted into the\n// worksheet.\ntype PictureInsertType byte\n\n// Insert picture types.\nconst (\n\tPictureInsertTypePlaceOverCells PictureInsertType = iota\n\tPictureInsertTypePlaceInCell\n\tPictureInsertTypeIMAGE\n\tPictureInsertTypeDISPIMG\n)\n\n// parseGraphicOptions provides a function to parse the format settings of\n// the picture with default value.\nfunc (opts *GraphicOptions) parseGraphicOptions(defaults *GraphicOptions) (*GraphicOptions, error) {\n\tif opts == nil {\n\t\treturn defaults, nil\n\t}\n\tif countUTF16String(opts.AltText) > MaxGraphicAltTextLength {\n\t\treturn defaults, ErrMaxGraphicAltTextLength\n\t}\n\tif countUTF16String(opts.Name) > MaxGraphicNameLength {\n\t\treturn defaults, ErrMaxGraphicNameLength\n\t}\n\tif opts.PrintObject == nil {\n\t\topts.PrintObject = boolPtr(true)\n\t}\n\tif opts.Locked == nil {\n\t\topts.Locked = boolPtr(true)\n\t}\n\tif opts.ScaleX == 0 {\n\t\topts.ScaleX = defaultDrawingScale\n\t}\n\tif opts.ScaleY == 0 {\n\t\topts.ScaleY = defaultDrawingScale\n\t}\n\tif opts.Positioning != \"\" && inStrSlice(supportedPositioning, opts.Positioning, true) == -1 {\n\t\treturn defaults, newInvalidOptionalValue(\"Positioning\", opts.Positioning, supportedPositioning)\n\t}\n\treturn opts, nil\n}\n\n// parsePictureOptions provides a function to parse the picture options with\n// default value.\nfunc parsePictureOptions(pic *Picture) (*GraphicOptions, error) {\n\tif pic.InsertType != PictureInsertTypePlaceOverCells {\n\t\treturn nil, ErrParameterInvalid\n\t}\n\treturn pic.Format.parseGraphicOptions(&GraphicOptions{\n\t\tPrintObject: boolPtr(true),\n\t\tLocked:      boolPtr(true),\n\t\tScaleX:      defaultDrawingScale,\n\t\tScaleY:      defaultDrawingScale,\n\t})\n}\n\n// AddPicture provides the method to add picture in a sheet by given picture\n// format set (such as offset, scale, aspect ratio setting and print settings)\n// and file path, supported image types: BMP, EMF, EMZ, GIF, ICO, JPEG, JPG,\n// PNG, SVG, TIF, TIFF, WMF, and WMZ. This function is concurrency-safe. Note\n// that this function only supports adding pictures placed over the cells\n// currently, and doesn't support adding pictures placed in cells or creating\n// the Kingsoft WPS Office embedded image cells. For example:\n//\n//\tpackage main\n//\n//\timport (\n//\t    \"fmt\"\n//\t    _ \"image/gif\"\n//\t    _ \"image/jpeg\"\n//\t    _ \"image/png\"\n//\n//\t    \"github.com/xuri/excelize/v2\"\n//\t)\n//\n//\tfunc main() {\n//\t    f := excelize.NewFile()\n//\t    defer func() {\n//\t        if err := f.Close(); err != nil {\n//\t            fmt.Println(err)\n//\t        }\n//\t    }()\n//\t    // Insert a picture.\n//\t    if err := f.AddPicture(\"Sheet1\", \"A2\", \"image.jpg\", nil); err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    // Insert a picture scaling in the cell with location hyperlink.\n//\t    enable := true\n//\t    if err := f.AddPicture(\"Sheet1\", \"D2\", \"image.png\",\n//\t        &excelize.GraphicOptions{\n//\t            ScaleX:        0.5,\n//\t            ScaleY:        0.5,\n//\t            Hyperlink:     \"#Sheet2!D8\",\n//\t            HyperlinkType: \"Location\",\n//\t        },\n//\t    ); err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    // Insert a picture offset in the cell with external hyperlink, printing and positioning support.\n//\t    if err := f.AddPicture(\"Sheet1\", \"H2\", \"image.gif\",\n//\t        &excelize.GraphicOptions{\n//\t            PrintObject:     &enable,\n//\t            LockAspectRatio: false,\n//\t            OffsetX:         15,\n//\t            OffsetY:         10,\n//\t            Hyperlink:       \"https://github.com/xuri/excelize\",\n//\t            HyperlinkType:   \"External\",\n//\t            Positioning:     \"oneCell\",\n//\t        },\n//\t    ); err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    if err := f.SaveAs(\"Book1.xlsx\"); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t}\n//\n// The optional parameter \"AltText\" is used to add alternative text to a graph\n// object.\n//\n// The optional parameter \"PrintObject\" indicates whether the graph object is\n// printed when the worksheet is printed, the default value of that is 'true'.\n//\n// The optional parameter \"Locked\" indicates whether lock the graph object.\n// Locking an object has no effect unless the sheet is protected.\n//\n// The optional parameter \"LockAspectRatio\" indicates whether lock aspect ratio\n// for the graph object, the default value of that is 'false'.\n//\n// The optional parameter \"AutoFit\" specifies if you make graph object size\n// auto-fits the cell, the default value of that is 'false'.\n//\n// The optional parameter \"AutoFitIgnoreAspect\" specifies if fill the cell with\n// the image and ignore its aspect ratio, the default value of that is 'false'.\n// This option only works when the \"AutoFit\" is enabled.\n//\n// The optional parameter \"OffsetX\" specifies the horizontal offset of the graph\n// object with the cell, the default value of that is 0.\n//\n// The optional parameter \"OffsetY\" specifies the vertical offset of the graph\n// object with the cell, the default value of that is 0.\n//\n// The optional parameter \"ScaleX\" specifies the horizontal scale of graph\n// object. The value of ScaleX must be a floating-point number greater than 0\n// with a precision of two decimal places. The default value of that is 1.0\n// which presents 100%.\n//\n// The optional parameter \"ScaleY\" specifies the vertical scale of graph object.\n// The value of ScaleY must be a floating-point number greater than 0 with a\n// precision of two decimal places. The default value of that is 1.0 which\n// presents 100%.\n//\n// The optional parameter \"Hyperlink\" specifies the hyperlink of the graph\n// object.\n//\n// The optional parameter \"HyperlinkType\" defines two types of\n// hyperlink \"External\" for website or \"Location\" for moving to one of the\n// cells in this workbook. When the \"HyperlinkType\" is \"Location\",\n// coordinates need to start with \"#\".\n//\n// The optional parameter \"Positioning\" defines 3 types of the position of a\n// graph object in a spreadsheet: \"oneCell\" (Move but don't size with\n// cells), \"twoCell\" (Move and size with cells), and \"absolute\" (Don't move or\n// size with cells). If you don't set this parameter, the default positioning\n// is to move and size with cells.\nfunc (f *File) AddPicture(sheet, cell, name string, opts *GraphicOptions) error {\n\tvar err error\n\t// Check picture exists first.\n\tif _, err = os.Stat(name); os.IsNotExist(err) {\n\t\treturn err\n\t}\n\text, ok := supportedImageTypes[strings.ToLower(path.Ext(name))]\n\tif !ok {\n\t\treturn ErrImgExt\n\t}\n\tfile, _ := os.ReadFile(filepath.Clean(name))\n\treturn f.AddPictureFromBytes(sheet, cell, &Picture{Extension: ext, File: file, Format: opts})\n}\n\n// AddPictureFromBytes provides the method to add picture in a sheet by given\n// picture format set (such as offset, scale, aspect ratio setting and print\n// settings), file base name, extension name and file bytes, supported image\n// types: EMF, EMZ, GIF, ICO, JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ. Note\n// that this function only supports adding pictures placed over the cells\n// currently, and doesn't support adding pictures placed in cells or creating\n// the Kingsoft WPS Office embedded image cells. For example:\n//\n//\tpackage main\n//\n//\timport (\n//\t    \"fmt\"\n//\t    _ \"image/jpeg\"\n//\t    \"os\"\n//\n//\t    \"github.com/xuri/excelize/v2\"\n//\t)\n//\n//\tfunc main() {\n//\t    f := excelize.NewFile()\n//\t    defer func() {\n//\t        if err := f.Close(); err != nil {\n//\t            fmt.Println(err)\n//\t        }\n//\t    }()\n//\t    file, err := os.ReadFile(\"image.jpg\")\n//\t    if err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    if err := f.AddPictureFromBytes(\"Sheet1\", \"A2\", &excelize.Picture{\n//\t        Extension: \".jpg\",\n//\t        File:      file,\n//\t        Format:    &excelize.GraphicOptions{AltText: \"Excel Logo\"},\n//\t    }); err != nil {\n//\t        fmt.Println(err)\n//\t        return\n//\t    }\n//\t    if err := f.SaveAs(\"Book1.xlsx\"); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t}\nfunc (f *File) AddPictureFromBytes(sheet, cell string, pic *Picture) error {\n\tvar drawingHyperlinkRID int\n\tvar hyperlinkType string\n\text, ok := supportedImageTypes[strings.ToLower(pic.Extension)]\n\tif !ok {\n\t\treturn ErrImgExt\n\t}\n\toptions, err := parsePictureOptions(pic)\n\tif err != nil {\n\t\treturn err\n\t}\n\timg, _, err := image.DecodeConfig(bytes.NewReader(pic.File))\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Read sheet data\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\t// Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.\n\tdrawingID := f.countDrawings() + 1\n\tdrawingXML := \"xl/drawings/drawing\" + strconv.Itoa(drawingID) + \".xml\"\n\tdrawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML)\n\tdrawingRels := \"xl/drawings/_rels/drawing\" + strconv.Itoa(drawingID) + \".xml.rels\"\n\tmediaStr := \"..\" + strings.TrimPrefix(f.addMedia(pic.File, ext), \"xl\")\n\tvar drawingRID int\n\tif rels, _ := f.relsReader(drawingRels); rels != nil {\n\t\tfor _, rel := range rels.Relationships {\n\t\t\tif rel.Type == SourceRelationshipImage && rel.Target == mediaStr {\n\t\t\t\tdrawingRID, _ = strconv.Atoi(strings.TrimPrefix(rel.ID, \"rId\"))\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tif drawingRID == 0 {\n\t\tdrawingRID = f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType)\n\t}\n\t// Add picture with hyperlink.\n\tif options.Hyperlink != \"\" && options.HyperlinkType != \"\" {\n\t\tif options.HyperlinkType == \"External\" {\n\t\t\thyperlinkType = options.HyperlinkType\n\t\t}\n\t\tdrawingHyperlinkRID = f.addRels(drawingRels, SourceRelationshipHyperLink, options.Hyperlink, hyperlinkType)\n\t}\n\tws.mu.Unlock()\n\terr = f.addDrawingPicture(sheet, drawingXML, cell, ext, drawingRID, drawingHyperlinkRID, img, options)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = f.addContentTypePart(drawingID, \"drawings\"); err != nil {\n\t\treturn err\n\t}\n\tf.addSheetNameSpace(sheet, SourceRelationship)\n\treturn err\n}\n\n// addSheetLegacyDrawing provides a function to add legacy drawing element to\n// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.\nfunc (f *File) addSheetLegacyDrawing(sheet string, rID int) {\n\tws, _ := f.workSheetReader(sheet)\n\tws.LegacyDrawing = &xlsxLegacyDrawing{\n\t\tRID: \"rId\" + strconv.Itoa(rID),\n\t}\n}\n\n// addSheetLegacyDrawingHF provides a function to add legacy drawing\n// header/footer element to xl/worksheets/sheet%d.xml by given\n// worksheet name and relationship index.\nfunc (f *File) addSheetLegacyDrawingHF(sheet string, rID int) {\n\tws, _ := f.workSheetReader(sheet)\n\tws.LegacyDrawingHF = &xlsxLegacyDrawingHF{\n\t\tRID: \"rId\" + strconv.Itoa(rID),\n\t}\n}\n\n// addSheetDrawing provides a function to add drawing element to\n// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.\nfunc (f *File) addSheetDrawing(sheet string, rID int) {\n\tws, _ := f.workSheetReader(sheet)\n\tws.Drawing = &xlsxDrawing{\n\t\tRID: \"rId\" + strconv.Itoa(rID),\n\t}\n}\n\n// addSheetPicture provides a function to add picture element to\n// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.\nfunc (f *File) addSheetPicture(sheet string, rID int) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.Picture = &xlsxPicture{\n\t\tRID: \"rId\" + strconv.Itoa(rID),\n\t}\n\treturn err\n}\n\n// countDrawings provides a function to get drawing files count storage in the\n// folder xl/drawings.\nfunc (f *File) countDrawings() int {\n\tdrawings := map[string]struct{}{}\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/drawings/drawing\") {\n\t\t\tdrawings[k.(string)] = struct{}{}\n\t\t}\n\t\treturn true\n\t})\n\tf.Drawings.Range(func(rel, value interface{}) bool {\n\t\tif strings.Contains(rel.(string), \"xl/drawings/drawing\") {\n\t\t\tdrawings[rel.(string)] = struct{}{}\n\t\t}\n\t\treturn true\n\t})\n\treturn len(drawings)\n}\n\n// addDrawingPicture provides a function to add picture by given sheet,\n// drawingXML, cell, file name, width, height relationship index and format\n// sets.\nfunc (f *File) addDrawingPicture(sheet, drawingXML, cell, ext string, rID, hyperlinkRID int, img image.Config, opts *GraphicOptions) error {\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif opts.Positioning != \"\" && inStrSlice(supportedPositioning, opts.Positioning, true) == -1 {\n\t\treturn newInvalidOptionalValue(\"Positioning\", opts.Positioning, supportedPositioning)\n\t}\n\twidth, height := img.Width, img.Height\n\tif opts.AutoFit {\n\t\tif width, height, col, row, err = f.drawingResize(sheet, cell, float64(width), float64(height), opts); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\twidth = int(float64(width) * opts.ScaleX)\n\t\theight = int(float64(height) * opts.ScaleY)\n\t}\n\tcolStart, rowStart, colEnd, rowEnd, x1, y1, x2, y2 := f.positionObjectPixels(sheet, col, row, width, height, opts)\n\tcontent, cNvPrID, err := f.drawingParser(drawingXML)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcellAnchor := xdrCellAnchor{}\n\tfrom := xlsxFrom{}\n\tfrom.Col = colStart\n\tfrom.ColOff = x1 * EMU\n\tfrom.Row = rowStart\n\tfrom.RowOff = y1 * EMU\n\tcellAnchor.From = &from\n\n\tif opts.Positioning != \"oneCell\" {\n\t\tto := xlsxTo{}\n\t\tto.Col = colEnd\n\t\tto.ColOff = x2 * EMU\n\t\tto.Row = rowEnd\n\t\tto.RowOff = y2 * EMU\n\t\tcellAnchor.To = &to\n\t\tcellAnchor.EditAs = opts.Positioning\n\t}\n\n\tpic := xlsxPic{}\n\tpic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect = opts.LockAspectRatio\n\tpic.NvPicPr.CNvPr.ID = cNvPrID\n\tpic.NvPicPr.CNvPr.Descr = opts.AltText\n\tpic.NvPicPr.CNvPr.Name = \"Picture \" + strconv.Itoa(cNvPrID)\n\tif len(opts.Name) > 0 {\n\t\tpic.NvPicPr.CNvPr.Name = opts.Name\n\t}\n\tif hyperlinkRID != 0 {\n\t\tpic.NvPicPr.CNvPr.HlinkClick = &xlsxHlinkClick{\n\t\t\tR:   SourceRelationship.Value,\n\t\t\tRID: \"rId\" + strconv.Itoa(hyperlinkRID),\n\t\t}\n\t}\n\tpic.BlipFill.Blip.R = SourceRelationship.Value\n\tpic.BlipFill.Blip.Embed = \"rId\" + strconv.Itoa(rID)\n\tif ext == \".svg\" {\n\t\tpic.BlipFill.Blip.ExtList = &xlsxEGOfficeArtExtensionList{\n\t\t\tExt: []xlsxCTOfficeArtExtension{\n\t\t\t\t{\n\t\t\t\t\tURI: ExtURISVG,\n\t\t\t\t\tSVGBlip: xlsxCTSVGBlip{\n\t\t\t\t\t\tXMLNSaAVG: NameSpaceDrawing2016SVG.Value,\n\t\t\t\t\t\tEmbed:     pic.BlipFill.Blip.Embed,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t}\n\tpic.SpPr.PrstGeom.Prst = \"rect\"\n\tpic.SpPr.Xfrm.Ext.Cx = width * EMU\n\tpic.SpPr.Xfrm.Ext.Cy = height * EMU\n\n\tif opts.Positioning == \"oneCell\" {\n\t\tcx := x2 * EMU\n\t\tcy := y2 * EMU\n\t\tcellAnchor.Ext = &xlsxPositiveSize2D{\n\t\t\tCx: cx,\n\t\t\tCy: cy,\n\t\t}\n\t\tpic.SpPr.Xfrm.Ext.Cx = cx\n\t\tpic.SpPr.Xfrm.Ext.Cy = cy\n\t}\n\n\tcellAnchor.Pic = &pic\n\tcellAnchor.ClientData = &xdrClientData{\n\t\tFLocksWithSheet:  *opts.Locked,\n\t\tFPrintsWithSheet: *opts.PrintObject,\n\t}\n\tcontent.mu.Lock()\n\tdefer content.mu.Unlock()\n\tif opts.Positioning == \"oneCell\" {\n\t\tcontent.OneCellAnchor = append(content.OneCellAnchor, &cellAnchor)\n\t} else {\n\t\tcontent.TwoCellAnchor = append(content.TwoCellAnchor, &cellAnchor)\n\t}\n\tf.Drawings.Store(drawingXML, content)\n\treturn err\n}\n\n// countMedia provides a function to get media files count storage in the\n// folder xl/media/image.\nfunc (f *File) countMedia() int {\n\tcount := 0\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/media/image\") {\n\t\t\tcount++\n\t\t}\n\t\treturn true\n\t})\n\treturn count\n}\n\n// addMedia provides a function to add a picture into folder xl/media/image by\n// given file and extension name. Duplicate images are only actually stored once\n// and drawings that use it will reference the same image.\nfunc (f *File) addMedia(file []byte, ext string) string {\n\tcount := f.countMedia()\n\tvar name string\n\tf.Pkg.Range(func(k, existing interface{}) bool {\n\t\tif !strings.HasPrefix(k.(string), \"xl/media/image\") {\n\t\t\treturn true\n\t\t}\n\t\tif bytes.Equal(file, existing.([]byte)) {\n\t\t\tname = k.(string)\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t})\n\tif name != \"\" {\n\t\treturn name\n\t}\n\tmedia := \"xl/media/image\" + strconv.Itoa(count+1) + ext\n\tf.Pkg.Store(media, file)\n\treturn media\n}\n\n// GetPictures provides a function to get picture meta info and raw content\n// embed in spreadsheet by given worksheet and cell name. This function\n// returns the image contents as []byte data types. This function is\n// concurrency safe. Note that this function currently does not support\n// retrieving all properties from the image's Format property, and the value of\n// the ScaleX and ScaleY property is a floating-point number greater than 0 with\n// a precision of two decimal places. For example:\n//\n//\tf, err := excelize.OpenFile(\"Book1.xlsx\")\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\tdefer func() {\n//\t    if err := f.Close(); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t}()\n//\tpics, err := f.GetPictures(\"Sheet1\", \"A2\")\n//\tif err != nil {\n//\t\tfmt.Println(err)\n//\t}\n//\tfor idx, pic := range pics {\n//\t    name := fmt.Sprintf(\"image%d%s\", idx+1, pic.Extension)\n//\t    if err := os.WriteFile(name, pic.File, 0644); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t}\nfunc (f *File) GetPictures(sheet, cell string) ([]Picture, error) {\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcol--\n\trow--\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn nil, err\n\t}\n\tf.mu.Unlock()\n\tif ws.Drawing == nil {\n\t\treturn f.getCellImages(sheet, cell)\n\t}\n\ttarget := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)\n\tdrawingXML := strings.TrimPrefix(strings.ReplaceAll(target, \"..\", \"xl\"), \"/\")\n\tdrawingRelationships := strings.ReplaceAll(\n\t\tstrings.ReplaceAll(drawingXML, \"xl/drawings\", \"xl/drawings/_rels\"), \".xml\", \".xml.rels\")\n\timgs, err := f.getCellImages(sheet, cell)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpics, err := f.getPicture(row, col, drawingXML, drawingRelationships)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn append(imgs, pics...), err\n}\n\n// GetPictureCells returns all picture cell references in a worksheet by a\n// specific worksheet name.\nfunc (f *File) GetPictureCells(sheet string) ([]string, error) {\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn nil, err\n\t}\n\tf.mu.Unlock()\n\tif ws.Drawing == nil {\n\t\treturn f.getImageCells(sheet)\n\t}\n\ttarget := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)\n\tdrawingXML := strings.TrimPrefix(strings.ReplaceAll(target, \"..\", \"xl\"), \"/\")\n\tdrawingRelationships := strings.ReplaceAll(\n\t\tstrings.ReplaceAll(drawingXML, \"xl/drawings\", \"xl/drawings/_rels\"), \".xml\", \".xml.rels\")\n\n\tembeddedImageCells, err := f.getImageCells(sheet)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\timageCells, err := f.getPictureCells(drawingXML, drawingRelationships)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn append(embeddedImageCells, imageCells...), err\n}\n\n// DeletePicture provides a function to delete all pictures in a cell by given\n// worksheet name and cell reference.\nfunc (f *File) DeletePicture(sheet, cell string) error {\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcol--\n\trow--\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif ws.Drawing == nil {\n\t\treturn err\n\t}\n\tdrawingXML := strings.ReplaceAll(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), \"..\", \"xl\")\n\tdrawingRels := \"xl/drawings/_rels/\" + filepath.Base(drawingXML) + \".rels\"\n\trIDs, err := f.deleteDrawing(col, row, drawingXML, \"Pic\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, rID := range rIDs {\n\t\trels := f.getDrawingRelationships(drawingRels, rID)\n\t\tif rels == nil {\n\t\t\treturn err\n\t\t}\n\t\tvar used bool\n\t\tcheckPicRef := func(k, v interface{}) bool {\n\t\t\tif strings.Contains(k.(string), \"xl/drawings/_rels/drawing\") {\n\t\t\t\tif k.(string) == drawingRels {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tr, err := f.relsReader(k.(string))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tfor _, rel := range r.Relationships {\n\t\t\t\t\tif rel.Type == SourceRelationshipImage &&\n\t\t\t\t\t\tfilepath.Base(rel.Target) == filepath.Base(rels.Target) {\n\t\t\t\t\t\tused = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\t\tf.Relationships.Range(checkPicRef)\n\t\tf.Pkg.Range(checkPicRef)\n\t\tif !used {\n\t\t\tf.Pkg.Delete(strings.ReplaceAll(rels.Target, \"../\", \"xl/\"))\n\t\t}\n\t\tf.deleteDrawingRels(drawingRels, rID)\n\t}\n\treturn err\n}\n\n// getPicture provides a function to get picture base name and raw content\n// embed in spreadsheet by given coordinates and drawing relationships.\nfunc (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (pics []Picture, err error) {\n\tvar wsDr *xlsxWsDr\n\tif wsDr, _, err = f.drawingParser(drawingXML); err != nil {\n\t\treturn\n\t}\n\twsDr.mu.Lock()\n\tdefer wsDr.mu.Unlock()\n\tcond := func(from *xlsxFrom) bool { return from.Col == col && from.Row == row }\n\tcond2 := func(from *decodeFrom) bool { return from.Col == col && from.Row == row }\n\tcb := func(a *xdrCellAnchor, r *xlsxRelationship, drawingRelationships string) {\n\t\tif pic := f.extractPictureFromAnchor(drawingRelationships, a, r); pic != nil {\n\t\t\tpics = append(pics, *pic)\n\t\t}\n\t}\n\tcb2 := func(a *decodeCellAnchor, r *xlsxRelationship, drawingRelationships string) {\n\t\tif pic := f.extractPictureFromDecodeAnchor(drawingRelationships, a, r); pic != nil {\n\t\t\tpics = append(pics, *pic)\n\t\t}\n\t}\n\tfor _, anchor := range wsDr.TwoCellAnchor {\n\t\tf.extractCellAnchor(anchor, drawingRelationships, cond, cb, cond2, cb2)\n\t}\n\tfor _, anchor := range wsDr.OneCellAnchor {\n\t\tf.extractCellAnchor(anchor, drawingRelationships, cond, cb, cond2, cb2)\n\t}\n\treturn\n}\n\n// extractPictureFromAnchor extracts picture data from a cell anchor and\n// relationship.\nfunc (f *File) extractPictureFromAnchor(drawingRelationships string, a *xdrCellAnchor, r *xlsxRelationship) *Picture {\n\tvar (\n\t\tcx, cy int\n\t\tpic    *Picture\n\t)\n\tif buffer, _ := f.Pkg.Load(filepath.ToSlash(filepath.Clean(\"xl/drawings/\" + r.Target))); buffer != nil {\n\t\tpic = &Picture{\n\t\t\tExtension: filepath.Ext(r.Target),\n\t\t\tFile:      buffer.([]byte),\n\t\t\tFormat:    &GraphicOptions{ScaleX: defaultDrawingScale, ScaleY: defaultDrawingScale},\n\t\t}\n\t\tif a.ClientData != nil {\n\t\t\tpic.Format.Locked = &a.ClientData.FLocksWithSheet\n\t\t\tpic.Format.PrintObject = &a.ClientData.FPrintsWithSheet\n\t\t}\n\t\tif a.To == nil {\n\t\t\tpic.Format.Positioning = \"oneCell\"\n\t\t}\n\t\tif a.Pic != nil {\n\t\t\tcx, cy = a.Pic.SpPr.Xfrm.Ext.Cx, a.Pic.SpPr.Xfrm.Ext.Cy\n\t\t\tif a.From != nil {\n\t\t\t\tpic.Format.OffsetX = int(a.From.ColOff / EMU)\n\t\t\t\tpic.Format.OffsetY = int(a.From.RowOff / EMU)\n\t\t\t}\n\t\t\tpic.Format.LockAspectRatio = a.Pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect\n\t\t\tpic.Format.AltText = a.Pic.NvPicPr.CNvPr.Descr\n\t\t\tpic.Format.Name = a.Pic.NvPicPr.CNvPr.Name\n\t\t\tif a.Pic.NvPicPr.CNvPr.HlinkClick != nil {\n\t\t\t\tif drawRel := f.getDrawingRelationships(drawingRelationships, a.Pic.NvPicPr.CNvPr.HlinkClick.RID); drawRel != nil {\n\t\t\t\t\tpic.Format.Hyperlink = drawRel.Target\n\t\t\t\t\tpic.Format.HyperlinkType = \"Location\"\n\t\t\t\t\tif drawRel.TargetMode == \"External\" {\n\t\t\t\t\t\tpic.Format.HyperlinkType = \"External\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tf.calculatePictureScale(pic, cx, cy)\n\t}\n\treturn pic\n}\n\n// calculatePictureScale calculates and sets the scale factors for a picture.\nfunc (f *File) calculatePictureScale(pic *Picture, cx, cy int) {\n\timgCfg, _, err := image.DecodeConfig(bytes.NewReader(pic.File))\n\tif err != nil || imgCfg.Width <= 0 || imgCfg.Height <= 0 || cx <= 0 || cy <= 0 {\n\t\treturn\n\t}\n\tpic.Format.ScaleX = math.Round(float64(cx)/float64(EMU)/float64(imgCfg.Width)*100) / 100\n\tpic.Format.ScaleY = math.Round(float64(cy)/float64(EMU)/float64(imgCfg.Height)*100) / 100\n}\n\n// extractPictureFromDecodeAnchor extracts picture data from a decoded cell\n// anchor and relationship.\nfunc (f *File) extractPictureFromDecodeAnchor(drawingRelationships string, a *decodeCellAnchor, r *xlsxRelationship) *Picture {\n\tvar (\n\t\tcx, cy int\n\t\tpic    *Picture\n\t\ttarget string\n\t)\n\tif strings.HasPrefix(r.Target, \"/\") {\n\t\ttarget = strings.TrimPrefix(r.Target, \"/\")\n\t} else {\n\t\ttarget = filepath.ToSlash(filepath.Clean(\"xl/drawings/\" + r.Target))\n\t}\n\tif buffer, _ := f.Pkg.Load(target); buffer != nil {\n\t\tpic = &Picture{\n\t\t\tExtension: filepath.Ext(target),\n\t\t\tFile:      buffer.([]byte),\n\t\t\tFormat:    &GraphicOptions{ScaleX: defaultDrawingScale, ScaleY: defaultDrawingScale},\n\t\t}\n\t\tif a.ClientData != nil {\n\t\t\tpic.Format.Locked = &a.ClientData.FLocksWithSheet\n\t\t\tpic.Format.PrintObject = &a.ClientData.FPrintsWithSheet\n\t\t}\n\t\tif a.To == nil {\n\t\t\tpic.Format.Positioning = \"oneCell\"\n\t\t}\n\t\tif a.Pic != nil {\n\t\t\tcx, cy = a.Pic.SpPr.Xfrm.Ext.Cx, a.Pic.SpPr.Xfrm.Ext.Cy\n\t\t\tif a.From != nil {\n\t\t\t\tpic.Format.OffsetX = int(a.From.ColOff / EMU)\n\t\t\t\tpic.Format.OffsetY = int(a.From.RowOff / EMU)\n\t\t\t}\n\t\t\tpic.Format.LockAspectRatio = a.Pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect\n\t\t\tpic.Format.AltText = a.Pic.NvPicPr.CNvPr.Descr\n\t\t\tpic.Format.Name = a.Pic.NvPicPr.CNvPr.Name\n\t\t\tif a.Pic.NvPicPr.CNvPr.HlinkClick != nil {\n\t\t\t\tif drawRel := f.getDrawingRelationships(drawingRelationships, a.Pic.NvPicPr.CNvPr.HlinkClick.RID); drawRel != nil {\n\t\t\t\t\tpic.Format.Hyperlink = drawRel.Target\n\t\t\t\t\tpic.Format.HyperlinkType = \"Location\"\n\t\t\t\t\tif drawRel.TargetMode == \"External\" {\n\t\t\t\t\t\tpic.Format.HyperlinkType = \"External\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tf.calculatePictureScale(pic, cx, cy)\n\t}\n\treturn pic\n}\n\n// extractCellAnchor extract drawing object from cell anchor by giving drawing\n// cell anchor, drawing relationships part path, conditional and callback\n// function.\nfunc (f *File) extractCellAnchor(anchor *xdrCellAnchor, drawingRelationships string,\n\tcond func(from *xlsxFrom) bool,\n\tcb func(anchor *xdrCellAnchor, rels *xlsxRelationship, drawingRelationships string),\n\tcond2 func(from *decodeFrom) bool,\n\tcb2 func(anchor *decodeCellAnchor, rels *xlsxRelationship, drawingRelationships string),\n) {\n\tvar drawRel *xlsxRelationship\n\tif anchor.GraphicFrame == \"\" {\n\t\tif anchor.From != nil && anchor.Pic != nil {\n\t\t\tif cond(anchor.From) {\n\t\t\t\tif drawRel = f.getDrawingRelationships(drawingRelationships,\n\t\t\t\t\tanchor.Pic.BlipFill.Blip.Embed); drawRel != nil {\n\t\t\t\t\tif _, ok := supportedImageTypes[strings.ToLower(filepath.Ext(drawRel.Target))]; ok {\n\t\t\t\t\t\tcb(anchor, drawRel, drawingRelationships)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\tf.extractDecodeCellAnchor(anchor, drawingRelationships, cond2, cb2)\n}\n\n// extractDecodeCellAnchor extract drawing object from cell anchor by giving\n// decoded drawing cell anchor, drawing relationships part path, conditional and\n// callback function.\nfunc (f *File) extractDecodeCellAnchor(anchor *xdrCellAnchor, drawingRelationships string,\n\tcond func(from *decodeFrom) bool, cb func(anchor *decodeCellAnchor, rels *xlsxRelationship, drawingRelationships string),\n) {\n\tvar (\n\t\tdrawRel      *xlsxRelationship\n\t\tdeCellAnchor = new(decodeCellAnchor)\n\t)\n\t_ = f.xmlNewDecoder(strings.NewReader(\"<decodeCellAnchor>\" + anchor.GraphicFrame + \"</decodeCellAnchor>\")).Decode(&deCellAnchor)\n\tif deCellAnchor.From != nil && deCellAnchor.Pic != nil {\n\t\tif cond(deCellAnchor.From) {\n\t\t\tif drawRel = f.getDrawingRelationships(drawingRelationships, deCellAnchor.Pic.BlipFill.Blip.Embed); drawRel != nil {\n\t\t\t\tif _, ok := supportedImageTypes[strings.ToLower(filepath.Ext(drawRel.Target))]; ok {\n\t\t\t\t\tcb(deCellAnchor, drawRel, drawingRelationships)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// getDrawingRelationships provides a function to get drawing relationships\n// from xl/drawings/_rels/drawing%s.xml.rels by given file name and\n// relationship ID.\nfunc (f *File) getDrawingRelationships(rels, rID string) *xlsxRelationship {\n\tif drawingRels, _ := f.relsReader(rels); drawingRels != nil {\n\t\tdrawingRels.mu.Lock()\n\t\tdefer drawingRels.mu.Unlock()\n\t\tfor _, v := range drawingRels.Relationships {\n\t\t\tif v.ID == rID {\n\t\t\t\treturn &v\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// drawingsWriter provides a function to save xl/drawings/drawing%d.xml after\n// serialize structure.\nfunc (f *File) drawingsWriter() {\n\tf.Drawings.Range(func(path, d interface{}) bool {\n\t\tif d != nil {\n\t\t\tv, _ := xml.Marshal(d.(*xlsxWsDr))\n\t\t\tf.saveFileList(path.(string), v)\n\t\t}\n\t\treturn true\n\t})\n}\n\n// drawingResize calculate the height and width after resizing.\nfunc (f *File) drawingResize(sheet, cell string, width, height float64, opts *GraphicOptions) (w, h, c, r int, err error) {\n\tvar mergeCells []MergeCell\n\tmergeCells, err = f.GetMergeCells(sheet)\n\tif err != nil {\n\t\treturn\n\t}\n\tvar rng []int\n\tvar inMergeCell bool\n\tif c, r, err = CellNameToCoordinates(cell); err != nil {\n\t\treturn\n\t}\n\tcellWidth, cellHeight := f.getColWidth(sheet, c), f.getRowHeight(sheet, r)\n\tfor _, mergeCell := range mergeCells {\n\t\tif inMergeCell {\n\t\t\tcontinue\n\t\t}\n\t\tif inMergeCell, err = f.checkCellInRangeRef(cell, mergeCell[0]); err == nil {\n\t\t\trng, _ = cellRefsToCoordinates(mergeCell.GetStartAxis(), mergeCell.GetEndAxis())\n\t\t\t_ = sortCoordinates(rng)\n\t\t}\n\t}\n\tif inMergeCell {\n\t\tcellWidth, cellHeight = 0, 0\n\t\tc, r = rng[0], rng[1]\n\t\tfor col := rng[0]; col <= rng[2]; col++ {\n\t\t\tcellWidth += f.getColWidth(sheet, col)\n\t\t}\n\t\tfor row := rng[1]; row <= rng[3]; row++ {\n\t\t\tcellHeight += f.getRowHeight(sheet, row)\n\t\t}\n\t}\n\tif float64(cellWidth) < width || float64(cellHeight) < height {\n\t\taspWidth := float64(cellWidth) / width\n\t\taspHeight := float64(cellHeight) / height\n\t\tasp := min(aspWidth, aspHeight)\n\t\twidth, height = width*asp, height*asp\n\t}\n\tif opts.AutoFitIgnoreAspect {\n\t\twidth, height = float64(cellWidth), float64(cellHeight)\n\t}\n\tw, h = int(width*opts.ScaleX), int(height*opts.ScaleY)\n\treturn\n}\n\n// getPictureCells provides a function to get all picture cell references in a\n// worksheet by given drawing part path and drawing relationships path.\nfunc (f *File) getPictureCells(drawingXML, drawingRelationships string) ([]string, error) {\n\tvar (\n\t\tcells []string\n\t\terr   error\n\t\twsDr  *xlsxWsDr\n\t)\n\tif wsDr, _, err = f.drawingParser(drawingXML); err != nil {\n\t\treturn cells, err\n\t}\n\twsDr.mu.Lock()\n\tdefer wsDr.mu.Unlock()\n\tcond := func(from *xlsxFrom) bool { return true }\n\tcond2 := func(from *decodeFrom) bool { return true }\n\tcb := func(a *xdrCellAnchor, r *xlsxRelationship, drawingRelationships string) {\n\t\tif _, ok := f.Pkg.Load(filepath.ToSlash(filepath.Clean(\"xl/drawings/\" + r.Target))); ok {\n\t\t\tif cell, err := CoordinatesToCellName(a.From.Col+1, a.From.Row+1); err == nil && inStrSlice(cells, cell, true) == -1 {\n\t\t\t\tcells = append(cells, cell)\n\t\t\t}\n\t\t}\n\t}\n\tcb2 := func(a *decodeCellAnchor, r *xlsxRelationship, drawingRelationships string) {\n\t\tvar target string\n\t\tif strings.HasPrefix(r.Target, \"/\") {\n\t\t\ttarget = strings.TrimPrefix(r.Target, \"/\")\n\t\t} else {\n\t\t\ttarget = filepath.ToSlash(filepath.Clean(\"xl/drawings/\" + r.Target))\n\t\t}\n\n\t\tif _, ok := f.Pkg.Load(target); ok {\n\t\t\tif cell, err := CoordinatesToCellName(a.From.Col+1, a.From.Row+1); err == nil && inStrSlice(cells, cell, true) == -1 {\n\t\t\t\tcells = append(cells, cell)\n\t\t\t}\n\t\t}\n\t}\n\tfor _, anchor := range wsDr.TwoCellAnchor {\n\t\tf.extractCellAnchor(anchor, drawingRelationships, cond, cb, cond2, cb2)\n\t}\n\tfor _, anchor := range wsDr.OneCellAnchor {\n\t\tf.extractCellAnchor(anchor, drawingRelationships, cond, cb, cond2, cb2)\n\t}\n\treturn cells, err\n}\n\n// cellImagesReader provides a function to get the pointer to the structure\n// after deserialization of xl/cellimages.xml.\nfunc (f *File) cellImagesReader() (*decodeCellImages, error) {\n\tif f.DecodeCellImages == nil {\n\t\tf.DecodeCellImages = new(decodeCellImages)\n\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathCellImages)))).\n\t\t\tDecode(f.DecodeCellImages); err != nil && err != io.EOF {\n\t\t\treturn f.DecodeCellImages, err\n\t\t}\n\t}\n\treturn f.DecodeCellImages, nil\n}\n\n// getImageCells returns all the cell images and the Kingsoft WPS\n// Office embedded image cells reference by given worksheet name.\nfunc (f *File) getImageCells(sheet string) ([]string, error) {\n\tvar (\n\t\terr   error\n\t\tcells []string\n\t)\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn cells, err\n\t}\n\tfor _, row := range ws.SheetData.Row {\n\t\tfor _, c := range row.C {\n\t\t\tif c.F != nil && c.F.Content != \"\" &&\n\t\t\t\tstrings.HasPrefix(strings.TrimPrefix(strings.TrimPrefix(c.F.Content, \"=\"), \"_xlfn.\"), \"DISPIMG\") {\n\t\t\t\tif _, err = f.CalcCellValue(sheet, c.R); err != nil {\n\t\t\t\t\treturn cells, err\n\t\t\t\t}\n\t\t\t\tcells = append(cells, c.R)\n\t\t\t}\n\t\t\tr, err := f.getImageCellRel(&c, &Picture{Format: &GraphicOptions{}})\n\t\t\tif err != nil {\n\t\t\t\treturn cells, err\n\t\t\t}\n\t\t\tif r != nil {\n\t\t\t\tcells = append(cells, c.R)\n\t\t\t}\n\n\t\t}\n\t}\n\treturn cells, err\n}\n\n// getRichDataRichValueRel returns relationship of the cell image by given meta\n// blocks value.\nfunc (f *File) getRichDataRichValueRel(val string) (*xlsxRelationship, error) {\n\tvar r *xlsxRelationship\n\tidx, err := strconv.Atoi(val)\n\tif err != nil {\n\t\treturn r, err\n\t}\n\trichValueRel, err := f.richValueRelReader()\n\tif err != nil {\n\t\treturn r, err\n\t}\n\tif idx >= len(richValueRel.Rels) {\n\t\treturn r, err\n\t}\n\trID := richValueRel.Rels[idx].ID\n\tif r = f.getRichDataRichValueRelRelationships(rID); r != nil && r.Type != SourceRelationshipImage {\n\t\treturn nil, err\n\t}\n\treturn r, err\n}\n\n// getRichDataWebImagesRel returns relationship of a web image by given meta\n// blocks value.\nfunc (f *File) getRichDataWebImagesRel(val string) (*xlsxRelationship, error) {\n\tvar r *xlsxRelationship\n\tidx, err := strconv.Atoi(val)\n\tif err != nil {\n\t\treturn r, err\n\t}\n\trichValueWebImages, err := f.richValueWebImageReader()\n\tif err != nil {\n\t\treturn r, err\n\t}\n\tif idx >= len(richValueWebImages.WebImageSrd) {\n\t\treturn r, err\n\t}\n\trID := richValueWebImages.WebImageSrd[idx].Blip.RID\n\tif r = f.getRichValueWebImageRelationships(rID); r != nil && r.Type != SourceRelationshipImage {\n\t\treturn nil, err\n\t}\n\treturn r, err\n}\n\n// getImageCellRel returns the cell image relationship.\nfunc (f *File) getImageCellRel(c *xlsxC, pic *Picture) (*xlsxRelationship, error) {\n\tvar r *xlsxRelationship\n\tif c.Vm == nil || c.V != formulaErrorVALUE {\n\t\treturn r, nil\n\t}\n\tmetaData, err := f.metadataReader()\n\tif err != nil {\n\t\treturn r, err\n\t}\n\tvmd := metaData.ValueMetadata\n\tif vmd == nil || int(*c.Vm) > len(vmd.Bk) || len(vmd.Bk[*c.Vm-1].Rc) == 0 {\n\t\treturn r, err\n\t}\n\trichValueIdx := vmd.Bk[*c.Vm-1].Rc[0].V\n\trichValue, err := f.richValueReader()\n\tif err != nil {\n\t\treturn r, err\n\t}\n\tif richValueIdx >= len(richValue.Rv) {\n\t\treturn r, err\n\t}\n\trv := richValue.Rv[richValueIdx]\n\trvStructures, err := f.richValueStructuresReader()\n\tif err != nil {\n\t\treturn r, err\n\t}\n\tif rv.S >= len(rvStructures.S) {\n\t\treturn r, err\n\t}\n\trvStruct := rvStructures.S[rv.S]\n\tif len(rvStruct.K) != len(rv.V) {\n\t\treturn r, err\n\t}\n\tif idx := rvStruct.getRichDataValueIdx(\"Text\"); idx != -1 {\n\t\tpic.Format.AltText = rv.V[idx]\n\t}\n\tif idx := rvStruct.getRichDataValueIdx(\"_rvRel:LocalImageIdentifier\"); idx != -1 {\n\t\tpic.InsertType = PictureInsertTypePlaceInCell\n\t\treturn f.getRichDataRichValueRel(rv.V[idx])\n\t}\n\t// cell image inserted by IMAGE formula function\n\tif idx := rvStruct.getRichDataValueIdx(\"WebImageIdentifier\"); idx != -1 {\n\t\tpic.InsertType = PictureInsertTypeIMAGE\n\t\treturn f.getRichDataWebImagesRel(rv.V[idx])\n\t}\n\treturn r, err\n}\n\n// getRichDataValueIdx provides a function to get the index of rich data value\n// structure by given name and rich data value.\nfunc (s *xlsxRichValueStructure) getRichDataValueIdx(n string) int {\n\tfor idx, k := range s.K {\n\t\tif k.N == n {\n\t\t\treturn idx\n\t\t}\n\t}\n\treturn -1\n}\n\n// getCellImages provides a function to get the cell images and\n// the Kingsoft WPS Office embedded cell images by given worksheet name and cell\n// reference.\nfunc (f *File) getCellImages(sheet, cell string) ([]Picture, error) {\n\tpics, err := f.getDispImages(sheet, cell)\n\tif err != nil {\n\t\treturn pics, err\n\t}\n\t_, err = f.getCellStringFunc(sheet, cell, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {\n\t\tpic := Picture{Format: &GraphicOptions{}, InsertType: PictureInsertTypePlaceInCell}\n\t\tr, err := f.getImageCellRel(c, &pic)\n\t\tif err != nil || r == nil {\n\t\t\treturn \"\", true, err\n\t\t}\n\t\tpic.Extension = filepath.Ext(r.Target)\n\t\tif buffer, _ := f.Pkg.Load(strings.TrimPrefix(strings.ReplaceAll(r.Target, \"..\", \"xl\"), \"/\")); buffer != nil {\n\t\t\tpic.File = buffer.([]byte)\n\t\t\tpics = append(pics, pic)\n\t\t}\n\t\treturn \"\", true, nil\n\t})\n\treturn pics, err\n}\n\n// getDispImages provides a function to get the Kingsoft WPS Office embedded\n// cell images by given worksheet name and cell reference.\nfunc (f *File) getDispImages(sheet, cell string) ([]Picture, error) {\n\tformula, err := f.GetCellFormula(sheet, cell)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !strings.HasPrefix(strings.TrimPrefix(strings.TrimPrefix(formula, \"=\"), \"_xlfn.\"), \"DISPIMG\") {\n\t\treturn nil, err\n\t}\n\timgID, err := f.CalcCellValue(sheet, cell)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcellImages, err := f.cellImagesReader()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trels, err := f.relsReader(defaultXMLPathCellImagesRels)\n\tif rels == nil {\n\t\treturn nil, err\n\t}\n\tvar pics []Picture\n\tfor _, cellImg := range cellImages.CellImage {\n\t\tif cellImg.Pic.NvPicPr.CNvPr.Name == imgID {\n\t\t\tfor _, r := range rels.Relationships {\n\t\t\t\tif r.ID == cellImg.Pic.BlipFill.Blip.Embed {\n\t\t\t\t\tpic := Picture{Extension: filepath.Ext(r.Target), Format: &GraphicOptions{}, InsertType: PictureInsertTypeDISPIMG}\n\t\t\t\t\tif buffer, _ := f.Pkg.Load(\"xl/\" + r.Target); buffer != nil {\n\t\t\t\t\t\tpic.File = buffer.([]byte)\n\t\t\t\t\t\tpic.Format.AltText = cellImg.Pic.NvPicPr.CNvPr.Descr\n\t\t\t\t\t\tpic.Format.Name = cellImg.Pic.NvPicPr.CNvPr.Name\n\t\t\t\t\t\tpics = append(pics, pic)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn pics, err\n}\n"
  },
  {
    "path": "picture_test.go",
    "content": "package excelize\n\nimport (\n\t\"fmt\"\n\t\"image\"\n\t_ \"image/gif\"\n\t_ \"image/jpeg\"\n\t_ \"image/png\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t_ \"golang.org/x/image/bmp\"\n\t_ \"golang.org/x/image/tiff\"\n)\n\nfunc BenchmarkAddPictureFromBytes(b *testing.B) {\n\tf := NewFile()\n\timgFile, err := os.ReadFile(filepath.Join(\"test\", \"images\", \"excel.png\"))\n\tif err != nil {\n\t\tb.Error(\"unable to load image for benchmark\")\n\t}\n\tb.ResetTimer()\n\tfor i := 1; i <= b.N; i++ {\n\t\tif err := f.AddPictureFromBytes(\"Sheet1\", fmt.Sprint(\"A\", i), &Picture{Extension: \".png\", File: imgFile, Format: &GraphicOptions{AltText: \"Excel\"}}); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc TestAddPicture(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\n\t// Test add picture to worksheet with offset and location hyperlink\n\topts := []GraphicOptions{\n\t\t{Name: \"Picture 4\", Hyperlink: \"#Sheet2!D8\", HyperlinkType: \"Location\"},\n\t\t{Name: \"Picture 4\", OffsetX: 10, OffsetY: 10, ScaleX: 0.5, ScaleY: 0.5, Hyperlink: \"https://github.com/xuri/excelize\", HyperlinkType: \"External\", Positioning: \"oneCell\"},\n\t\t{Name: \"Picture 5\", OffsetX: 10, OffsetY: 10, ScaleX: 0.88, ScaleY: 0.88, Hyperlink: \"https://github.com/xuri/excelize\", HyperlinkType: \"External\"},\n\t\t{Name: \"Picture 4\", PrintObject: boolPtr(true), Locked: boolPtr(true), OffsetX: 200, ScaleX: 1, ScaleY: 1, Positioning: \"oneCell\"},\n\t\t{Name: \"Picture 9\", PrintObject: boolPtr(true), Locked: boolPtr(true), AltText: \"Excel Logo\", LockAspectRatio: true, ScaleX: 1, ScaleY: 1},\n\t}\n\tassert.NoError(t, f.AddPicture(\"Sheet2\", \"I9\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), &opts[0]))\n\t// Test add picture to worksheet with offset, external hyperlink and positioning\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"F21\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), &opts[1]))\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"H21\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), &opts[2]))\n\n\t// Test add pictures to single cell with offsets\n\tassert.NoError(t, f.AddPicture(\"Sheet2\", \"K22\", filepath.Join(\"test\", \"images\", \"excel.jpg\"),\n\t\t&GraphicOptions{Positioning: \"oneCell\"}))\n\tassert.NoError(t, f.AddPicture(\"Sheet2\", \"K22\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), &opts[3]))\n\tassert.NoError(t, f.AddPicture(\"Sheet2\", \"K22\", filepath.Join(\"test\", \"images\", \"excel.jpg\"),\n\t\t&GraphicOptions{OffsetX: 400, Positioning: \"oneCell\"}))\n\tassert.NoError(t, f.AddPicture(\"Sheet2\", \"K22\", filepath.Join(\"test\", \"images\", \"excel.jpg\"),\n\t\t&GraphicOptions{OffsetX: 600, Positioning: \"oneCell\"}))\n\n\tfile, err := os.ReadFile(filepath.Join(\"test\", \"images\", \"excel.png\"))\n\tassert.NoError(t, err)\n\n\t// Test add picture to worksheet with autofit\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"A30\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), &GraphicOptions{AutoFit: true}))\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"B30\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), &GraphicOptions{OffsetX: 10, OffsetY: 10, AutoFit: true}))\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"C30\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), &GraphicOptions{AutoFit: true, AutoFitIgnoreAspect: true}))\n\t_, err = f.NewSheet(\"AddPicture\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetRowHeight(\"AddPicture\", 10, 30))\n\tassert.NoError(t, f.MergeCell(\"AddPicture\", \"B3\", \"D9\"))\n\tassert.NoError(t, f.MergeCell(\"AddPicture\", \"B1\", \"D1\"))\n\tassert.NoError(t, f.AddPicture(\"AddPicture\", \"C6\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), &GraphicOptions{AutoFit: true}))\n\tassert.NoError(t, f.AddPicture(\"AddPicture\", \"A1\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), &GraphicOptions{AutoFit: true}))\n\n\t// Test add picture to worksheet from bytes\n\tassert.NoError(t, f.AddPictureFromBytes(\"Sheet1\", \"Q1\", &Picture{Extension: \".png\", File: file, Format: &opts[4]}))\n\t// Test add picture to worksheet from bytes with unsupported insert type\n\tassert.Equal(t, ErrParameterInvalid, f.AddPictureFromBytes(\"Sheet1\", \"Q1\", &Picture{Extension: \".png\", File: file, Format: &GraphicOptions{AltText: \"Excel Logo\"}, InsertType: PictureInsertTypePlaceInCell}))\n\t// Test add picture to worksheet from bytes with illegal cell reference\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.AddPictureFromBytes(\"Sheet1\", \"A\", &Picture{Extension: \".png\", File: file, Format: &GraphicOptions{AltText: \"Excel Logo\"}}))\n\n\tfor _, preset := range [][]string{{\"Q8\", \"gif\"}, {\"Q15\", \"jpg\"}, {\"Q22\", \"tif\"}, {\"Q28\", \"bmp\"}} {\n\t\tassert.NoError(t, f.AddPicture(\"Sheet1\", preset[0], filepath.Join(\"test\", \"images\", fmt.Sprintf(\"excel.%s\", preset[1])), nil))\n\t}\n\t// Test get one cell anchor pictures from worksheet which added pictures with offset\n\tpics, err := f.GetPictures(\"Sheet2\", \"K22\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 4)\n\tassert.Equal(t, opts[3], *pics[1].Format)\n\t// Test get one cell anchor pictures from worksheet which added pictures with offset and scale\n\tpics, err = f.GetPictures(\"Sheet1\", \"F21\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 1)\n\tassert.Equal(t, opts[1], *pics[0].Format)\n\t// Test get two cell anchor pictures from worksheet which added pictures with offset and scale\n\tpics, err = f.GetPictures(\"Sheet1\", \"H21\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 1)\n\tassert.Equal(t, opts[2], *pics[0].Format)\n\t// Test get two cell anchor pictures from worksheet which added pictures with alternative text\n\tpics, err = f.GetPictures(\"Sheet1\", \"Q1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 1)\n\tassert.Equal(t, opts[4], *pics[0].Format)\n\t// Test get two cell anchor pictures from worksheet which added pictures with location hyperlink\n\tpics, err = f.GetPictures(\"Sheet2\", \"I9\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 1)\n\tassert.Equal(t, opts[0], *pics[0].Format)\n\n\t// Test write file to given path\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddPicture1.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\t// Test get pictures after inserting a new picture from a workbook which contains existing pictures\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestAddPicture1.xlsx\"))\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"A30\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), nil))\n\tpics, err = f.GetPictures(\"Sheet1\", \"A30\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 2)\n\n\t// Test get one cell anchor pictures from worksheet which already contains pictures\n\tpics, err = f.GetPictures(\"Sheet2\", \"K22\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 4)\n\tassert.Equal(t, opts[3], *pics[1].Format)\n\t// Test get one cell anchor pictures from worksheet which already contains pictures with offset and scale\n\tpics, err = f.GetPictures(\"Sheet1\", \"F21\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 1)\n\tassert.Equal(t, opts[1], *pics[0].Format)\n\t// Test get two cell anchor pictures from worksheet which already contains pictures\n\tpics, err = f.GetPictures(\"Sheet1\", \"H21\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 1)\n\tassert.Equal(t, opts[2], *pics[0].Format)\n\t// Test get two cell anchor pictures from worksheet which already contains pictures with alternative text\n\tpics, err = f.GetPictures(\"Sheet1\", \"Q1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 1)\n\tassert.Equal(t, opts[4], *pics[0].Format)\n\t// Test get two cell anchor pictures from worksheet which already contains pictures with location hyperlink\n\tpics, err = f.GetPictures(\"Sheet2\", \"I9\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 1)\n\tassert.Equal(t, opts[0], *pics[0].Format)\n\n\t// Test get picture cells\n\tcells, err := f.GetPictureCells(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"H21\", \"A30\", \"B30\", \"C30\", \"Q1\", \"Q8\", \"Q15\", \"Q22\", \"Q28\", \"F21\"}, cells)\n\tassert.NoError(t, f.Close())\n\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestAddPicture1.xlsx\"))\n\tassert.NoError(t, err)\n\tpath := \"xl/drawings/drawing1.xml\"\n\tf.Drawings.Delete(path)\n\tcells, err = f.GetPictureCells(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"H21\", \"A30\", \"B30\", \"C30\", \"Q1\", \"Q8\", \"Q15\", \"Q22\", \"Q28\", \"F21\"}, cells)\n\t// Test get picture cells with unsupported charset\n\tf.Drawings.Delete(path)\n\tf.Pkg.Store(path, MacintoshCyrillicCharset)\n\t_, err = f.GetPictureCells(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestAddPicture1.xlsx\"))\n\tassert.NoError(t, err)\n\t// Test get picture cells with unsupported charset\n\tf.Pkg.Store(path, MacintoshCyrillicCharset)\n\t_, err = f.GetPictureCells(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\n\t// Test add picture with unsupported charset content types\n\tf = NewFile()\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddPictureFromBytes(\"Sheet1\", \"Q1\", &Picture{Extension: \".png\", File: file, Format: &GraphicOptions{AltText: \"Excel Logo\"}}), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test add picture with invalid sheet name\n\tassert.EqualError(t, f.AddPicture(\"Sheet:1\", \"A1\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), nil), ErrSheetNameInvalid.Error())\n}\n\nfunc TestAddPictureErrors(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\n\t// Test add picture to worksheet with invalid file path\n\tassert.Error(t, f.AddPicture(\"Sheet1\", \"G21\", filepath.Join(\"test\", \"not_exists_dir\", \"not_exists.icon\"), nil))\n\n\t// Test add picture to worksheet with unsupported file type\n\tassert.EqualError(t, f.AddPicture(\"Sheet1\", \"G21\", filepath.Join(\"test\", \"Book1.xlsx\"), nil), ErrImgExt.Error())\n\n\tassert.EqualError(t, f.AddPictureFromBytes(\"Sheet1\", \"G21\", &Picture{Extension: \"jpg\", File: make([]byte, 1), Format: &GraphicOptions{AltText: \"Excel Logo\"}}), ErrImgExt.Error())\n\n\t// Test add picture to worksheet with invalid file data\n\tassert.EqualError(t, f.AddPictureFromBytes(\"Sheet1\", \"G21\", &Picture{Extension: \".jpg\", File: make([]byte, 1), Format: &GraphicOptions{AltText: \"Excel Logo\"}}), image.ErrFormat.Error())\n\n\t// Test add picture to worksheet with name length exceeds the limit\n\tassert.EqualError(t, f.AddPicture(\"Sheet1\", \"Q25\", \"excelize.svg\", &GraphicOptions{Name: strings.Repeat(\"s\", MaxGraphicNameLength+1)}), ErrMaxGraphicNameLength.Error())\n\n\t// Test add picture to worksheet with alt text length exceeds the limit\n\tassert.EqualError(t, f.AddPicture(\"Sheet1\", \"Q25\", \"excelize.svg\", &GraphicOptions{AltText: strings.Repeat(\"s\", MaxGraphicAltTextLength+1)}), ErrMaxGraphicAltTextLength.Error())\n\n\t// Test add picture with custom image decoder and encoder\n\tdecode := func(r io.Reader) (image.Image, error) { return nil, nil }\n\tdecodeConfig := func(r io.Reader) (image.Config, error) { return image.Config{Height: 100, Width: 90}, nil }\n\tfor cell, ext := range map[string]string{\"Q1\": \"emf\", \"Q7\": \"wmf\", \"Q13\": \"emz\", \"Q19\": \"wmz\"} {\n\t\timage.RegisterFormat(ext, \"\", decode, decodeConfig)\n\t\tassert.NoError(t, f.AddPicture(\"Sheet1\", cell, filepath.Join(\"test\", \"images\", fmt.Sprintf(\"excel.%s\", ext)), nil))\n\t}\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"Q25\", \"excelize.svg\", &GraphicOptions{ScaleX: 2.8}))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddPicture2.xlsx\")))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestGetPicture(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"A1\", filepath.Join(\"test\", \"images\", \"excel.png\"), nil))\n\tpics, err := f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics[0].File, 4718)\n\tassert.Empty(t, pics[0].Format.AltText)\n\tassert.Equal(t, \"Picture 2\", pics[0].Format.Name)\n\tassert.Equal(t, PictureInsertTypePlaceOverCells, pics[0].InsertType)\n\n\tf, err = prepareTestBook1()\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\n\tpics, err = f.GetPictures(\"Sheet1\", \"F21\")\n\tassert.NoError(t, err)\n\tif !assert.NotEmpty(t, filepath.Join(\"test\", fmt.Sprintf(\"image1%s\", pics[0].Extension))) || !assert.NotEmpty(t, pics[0].File) ||\n\t\t!assert.NoError(t, os.WriteFile(filepath.Join(\"test\", fmt.Sprintf(\"image1%s\", pics[0].Extension)), pics[0].File, 0o644)) {\n\t\tt.FailNow()\n\t}\n\n\t// Try to get picture from a worksheet with illegal cell reference\n\t_, err = f.GetPictures(\"Sheet1\", \"A\")\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), err)\n\n\t// Try to get picture from a worksheet that doesn't contain any images\n\tpics, err = f.GetPictures(\"Sheet3\", \"I9\")\n\tassert.EqualError(t, err, \"sheet Sheet3 does not exist\")\n\tassert.Len(t, pics, 0)\n\n\t// Try to get picture from a cell that doesn't contain an image\n\tpics, err = f.GetPictures(\"Sheet2\", \"A2\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 0)\n\n\t// Test get picture with invalid sheet name\n\t_, err = f.GetPictures(\"Sheet:1\", \"A2\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\n\tf.getDrawingRelationships(\"xl/worksheets/_rels/sheet1.xml.rels\", \"rId8\")\n\tf.getDrawingRelationships(\"\", \"\")\n\tf.getSheetRelationshipsTargetByID(\"\", \"\")\n\tf.deleteSheetRelationships(\"\", \"\")\n\n\t// Try to get picture from a local storage file.\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestGetPicture.xlsx\")))\n\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestGetPicture.xlsx\"))\n\tassert.NoError(t, err)\n\n\tpics, err = f.GetPictures(\"Sheet1\", \"F21\")\n\tassert.NoError(t, err)\n\tif !assert.NotEmpty(t, filepath.Join(\"test\", fmt.Sprintf(\"image1%s\", pics[0].Extension))) || !assert.NotEmpty(t, pics[0].File) ||\n\t\t!assert.NoError(t, os.WriteFile(filepath.Join(\"test\", fmt.Sprintf(\"image1%s\", pics[0].Extension)), pics[0].File, 0o644)) {\n\t\tt.FailNow()\n\t}\n\n\t// Try to get picture from a local storage file that doesn't contain an image\n\tpics, err = f.GetPictures(\"Sheet1\", \"F22\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 0)\n\tassert.NoError(t, f.Close())\n\n\t// Try to get picture with one cell anchor\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestGetPicture.xlsx\"))\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/drawings/drawing2.xml\", []byte(`<xdr:wsDr xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:xdr=\"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"><xdr:oneCellAnchor><xdr:from><xdr:col>10</xdr:col><xdr:row>15</xdr:row></xdr:from><xdr:to><xdr:col>13</xdr:col><xdr:row>22</xdr:row></xdr:to><xdr:pic><xdr:nvPicPr><xdr:cNvPr id=\"2\"></xdr:cNvPr></xdr:nvPicPr><xdr:blipFill><a:blip r:embed=\"rId1\"></a:blip></xdr:blipFill></xdr:pic></xdr:oneCellAnchor></xdr:wsDr>`))\n\tpics, err = f.GetPictures(\"Sheet2\", \"K16\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 1)\n\t// Try to get picture cells with one cell anchor\n\tcells, err := f.GetPictureCells(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"K16\"}, cells)\n\n\t// Try to get picture cells with absolute target path in the drawing relationship\n\trels, err := f.relsReader(\"xl/drawings/_rels/drawing2.xml.rels\")\n\tassert.NoError(t, err)\n\trels.Relationships[0].Target = \"/xl/media/image2.jpeg\"\n\tcells, err = f.GetPictureCells(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"K16\"}, cells)\n\t// Try to get pictures with absolute target path in the drawing relationship\n\tpics, err = f.GetPictures(\"Sheet2\", \"K16\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 1)\n\n\tassert.NoError(t, f.Close())\n\n\t// Test get picture from none drawing worksheet\n\tf = NewFile()\n\tpics, err = f.GetPictures(\"Sheet1\", \"F22\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 0)\n\tf, err = prepareTestBook1()\n\tassert.NoError(t, err)\n\n\t// Test get pictures with unsupported charset\n\tpath := \"xl/drawings/drawing1.xml\"\n\tf.Drawings.Delete(path)\n\tf.Pkg.Store(path, MacintoshCyrillicCharset)\n\t_, err = f.GetPictures(\"Sheet1\", \"F21\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t_, err = f.getPicture(20, 5, path, \"xl/drawings/_rels/drawing2.xml.rels\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tf.Drawings.Delete(path)\n\t_, err = f.getPicture(20, 5, path, \"xl/drawings/_rels/drawing2.xml.rels\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\n\t// Test get embedded cell pictures\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestGetPicture.xlsx\"))\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"F21\", \"_xlfn.DISPIMG(\\\"ID_********************************\\\",1)\"))\n\tf.Pkg.Store(defaultXMLPathCellImages, []byte(`<etc:cellImages xmlns:etc=\"http://www.wps.cn/officeDocument/2017/etCustomData\"><etc:cellImage><xdr:pic><xdr:nvPicPr><xdr:cNvPr id=\"1\" name=\"ID_********************************\" descr=\"CellImage1\"/></xdr:nvPicPr><xdr:blipFill><a:blip r:embed=\"rId1\"/></xdr:blipFill></xdr:pic></etc:cellImage></etc:cellImages>`))\n\tf.Pkg.Store(defaultXMLPathCellImagesRels, []byte(fmt.Sprintf(`<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"%s\" Target=\"media/image1.jpeg\"/></Relationships>`, SourceRelationshipImage)))\n\tpics, err = f.GetPictures(\"Sheet1\", \"F21\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pics, 2)\n\tassert.Equal(t, \"CellImage1\", pics[0].Format.AltText)\n\tassert.Equal(t, PictureInsertTypeDISPIMG, pics[0].InsertType)\n\n\t// Test get embedded cell pictures with invalid formula\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", \"_xlfn.DISPIMG()\"))\n\t_, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, \"DISPIMG requires 2 numeric arguments\")\n\n\t// Test get embedded cell pictures with unsupported charset\n\tf.Relationships.Delete(defaultXMLPathCellImagesRels)\n\tf.Pkg.Store(defaultXMLPathCellImagesRels, MacintoshCyrillicCharset)\n\t_, err = f.GetPictures(\"Sheet1\", \"F21\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tf.Pkg.Store(defaultXMLPathCellImages, MacintoshCyrillicCharset)\n\tf.DecodeCellImages = nil\n\t_, err = f.GetPictures(\"Sheet1\", \"F21\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestAddDrawingPicture(t *testing.T) {\n\t// Test addDrawingPicture with illegal cell reference\n\tf := NewFile()\n\topts := &GraphicOptions{PrintObject: boolPtr(true), Locked: boolPtr(false)}\n\tassert.EqualError(t, f.addDrawingPicture(\"sheet1\", \"\", \"A\", \"\", 0, 0, image.Config{}, opts), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\t// Test addDrawingPicture with invalid positioning types\n\tassert.Equal(t, newInvalidOptionalValue(\"Positioning\", \"x\", supportedPositioning),\n\t\tf.addDrawingPicture(\"sheet1\", \"\", \"A1\", \"\", 0, 0, image.Config{}, &GraphicOptions{Positioning: \"x\"}))\n\n\tpath := \"xl/drawings/drawing1.xml\"\n\tf.Pkg.Store(path, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.addDrawingPicture(\"sheet1\", path, \"A1\", \"\", 0, 0, image.Config{}, opts), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestAddPictureFromBytes(t *testing.T) {\n\tf := NewFile()\n\timgFile, err := os.ReadFile(\"logo.png\")\n\tassert.NoError(t, err, \"Unable to load logo for test\")\n\n\tassert.NoError(t, f.AddPictureFromBytes(\"Sheet1\", \"A1\", &Picture{Extension: \".png\", File: imgFile, Format: &GraphicOptions{AltText: strings.Repeat(\"\\u4e00\", MaxGraphicAltTextLength), Name: strings.Repeat(\"\\u4e00\", MaxGraphicNameLength)}}))\n\tassert.NoError(t, f.AddPictureFromBytes(\"Sheet1\", \"A50\", &Picture{Extension: \".png\", File: imgFile, Format: &GraphicOptions{AltText: \"logo\"}}))\n\timageCount := 0\n\tf.Pkg.Range(func(fileName, v interface{}) bool {\n\t\tif strings.Contains(fileName.(string), \"media/image\") {\n\t\t\timageCount++\n\t\t}\n\t\treturn true\n\t})\n\tassert.Equal(t, 1, imageCount, \"Duplicate image should only be stored once.\")\n\tassert.EqualError(t, f.AddPictureFromBytes(\"SheetN\", \"A1\", &Picture{Extension: \".png\", File: imgFile}), \"sheet SheetN does not exist\")\n\t// Test add picture from bytes with invalid sheet name\n\tassert.EqualError(t, f.AddPictureFromBytes(\"Sheet:1\", \"A1\", &Picture{Extension: \".png\", File: imgFile}), ErrSheetNameInvalid.Error())\n\t// Test add picture from bytes with invalid positioning types\n\tassert.Equal(t, f.AddPictureFromBytes(\"Sheet1\", \"A1\", &Picture{Extension: \".png\", File: imgFile, Format: &GraphicOptions{Positioning: \"x\"}}), newInvalidOptionalValue(\"Positioning\", \"x\", supportedPositioning))\n}\n\nfunc TestDeletePicture(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\t// Test delete picture on a worksheet which does not contains any pictures\n\tassert.NoError(t, f.DeletePicture(\"Sheet1\", \"A1\"))\n\t// Add same pictures on different worksheets\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"F20\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), nil))\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"I20\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), nil))\n\tassert.NoError(t, f.AddPicture(\"Sheet2\", \"F1\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), nil))\n\t// Test delete picture on a worksheet, the images should be preserved\n\tassert.NoError(t, f.DeletePicture(\"Sheet1\", \"F20\"))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestDeletePicture.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestDeletePicture.xlsx\"))\n\tassert.NoError(t, err)\n\t// Test delete same picture on different worksheet, the images should be removed\n\tassert.NoError(t, f.DeletePicture(\"Sheet1\", \"F20\"))\n\tassert.NoError(t, f.DeletePicture(\"Sheet1\", \"I20\"))\n\tassert.NoError(t, f.DeletePicture(\"Sheet2\", \"F1\"))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestDeletePicture2.xlsx\")))\n\n\t// Test delete picture on not exists worksheet\n\tassert.EqualError(t, f.DeletePicture(\"SheetN\", \"A1\"), \"sheet SheetN does not exist\")\n\t// Test delete picture with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.DeletePicture(\"Sheet:1\", \"A1\"))\n\t// Test delete picture with invalid coordinates\n\tassert.Equal(t, newCellNameToCoordinatesError(\"\", newInvalidCellNameError(\"\")), f.DeletePicture(\"Sheet1\", \"\"))\n\tassert.NoError(t, f.Close())\n\t// Test delete picture on no chart worksheet\n\tassert.NoError(t, NewFile().DeletePicture(\"Sheet1\", \"A1\"))\n\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestDeletePicture.xlsx\"))\n\tassert.NoError(t, err)\n\t// Test delete picture with unsupported charset drawing\n\tf.Pkg.Store(\"xl/drawings/drawing1.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.DeletePicture(\"Sheet1\", \"F10\"), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestDeletePicture.xlsx\"))\n\tassert.NoError(t, err)\n\t// Test delete picture with unsupported charset drawing relationships\n\tf.Relationships.Delete(\"xl/drawings/_rels/drawing1.xml.rels\")\n\tf.Pkg.Store(\"xl/drawings/_rels/drawing1.xml.rels\", MacintoshCyrillicCharset)\n\tassert.NoError(t, f.DeletePicture(\"Sheet2\", \"F1\"))\n\tassert.NoError(t, f.Close())\n\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestDeletePicture.xlsx\"))\n\tassert.NoError(t, err)\n\t// Test delete picture without drawing relationships\n\tf.Relationships.Delete(\"xl/drawings/_rels/drawing1.xml.rels\")\n\tf.Pkg.Delete(\"xl/drawings/_rels/drawing1.xml.rels\")\n\tassert.NoError(t, f.DeletePicture(\"Sheet1\", \"I20\"))\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"A1\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), nil))\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"G1\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), nil))\n\tdrawing, ok := f.Drawings.Load(\"xl/drawings/drawing1.xml\")\n\tassert.True(t, ok)\n\t// Made two picture reference the same drawing relationship ID\n\tdrawing.(*xlsxWsDr).TwoCellAnchor[1].Pic.BlipFill.Blip.Embed = \"rId1\"\n\tassert.NoError(t, f.DeletePicture(\"Sheet1\", \"A1\"))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestDrawingResize(t *testing.T) {\n\tf := NewFile()\n\t// Test calculate drawing resize on not exists worksheet\n\t_, _, _, _, err := f.drawingResize(\"SheetN\", \"A1\", 1, 1, nil)\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test calculate drawing resize with invalid coordinates\n\t_, _, _, _, err = f.drawingResize(\"Sheet1\", \"\", 1, 1, nil)\n\tassert.Equal(t, newCellNameToCoordinatesError(\"\", newInvalidCellNameError(\"\")), err)\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: \"A:A\"}}}\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.AddPicture(\"Sheet1\", \"A1\", filepath.Join(\"test\", \"images\", \"excel.jpg\"), &GraphicOptions{AutoFit: true}))\n}\n\nfunc TestSetContentTypePartRelsExtensions(t *testing.T) {\n\tf := NewFile()\n\tf.ContentTypes = &xlsxTypes{}\n\tassert.NoError(t, f.setContentTypePartRelsExtensions())\n\n\t// Test set content type part relationships extensions with unsupported charset content types\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.setContentTypePartRelsExtensions(), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestSetContentTypePartImageExtensions(t *testing.T) {\n\tf := NewFile()\n\t// Test set content type part image extensions with unsupported charset content types\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.setContentTypePartImageExtensions(), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestSetContentTypePartVMLExtensions(t *testing.T) {\n\tf := NewFile()\n\t// Test set content type part VML extensions with unsupported charset content types\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.setContentTypePartVMLExtensions(), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestAddContentTypePart(t *testing.T) {\n\tf := NewFile()\n\t// Test add content type part with unsupported charset content types\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.addContentTypePart(0, \"unknown\"), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestGetPictureCells(t *testing.T) {\n\tf := NewFile()\n\t// Test get picture cells on a worksheet which not contains any pictures\n\tcells, err := f.GetPictureCells(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, cells)\n\t// Test get picture cells on not exists worksheet\n\t_, err = f.GetPictureCells(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\tassert.NoError(t, f.Close())\n\n\t// Test get embedded picture cells\n\tf = NewFile()\n\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"A1\", filepath.Join(\"test\", \"images\", \"excel.png\"), nil))\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A2\", \"_xlfn.DISPIMG(\\\"ID_********************************\\\",1)\"))\n\tcells, err = f.GetPictureCells(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"A2\", \"A1\"}, cells)\n\n\t// Test get embedded cell pictures with invalid formula\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A2\", \"_xlfn.DISPIMG()\"))\n\t_, err = f.GetPictureCells(\"Sheet1\")\n\tassert.EqualError(t, err, \"DISPIMG requires 2 numeric arguments\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestExtractDecodeCellAnchor(t *testing.T) {\n\tf := NewFile()\n\tcond := func(a *decodeFrom) bool { return true }\n\tcb := func(a *decodeCellAnchor, r *xlsxRelationship, drawingRelationships string) {}\n\tf.extractDecodeCellAnchor(&xdrCellAnchor{GraphicFrame: string(MacintoshCyrillicCharset)}, \"\", cond, cb)\n}\n\nfunc TestGetCellImages(t *testing.T) {\n\tf := NewFile()\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", MacintoshCyrillicCharset)\n\t_, err := f.getCellImages(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\n\t// Test get the cell images\n\tprepareWorkbook := func() *File {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.AddPicture(\"Sheet1\", \"A1\", filepath.Join(\"test\", \"images\", \"excel.png\"), nil))\n\t\tf.Pkg.Store(defaultXMLMetadata, []byte(`<metadata><valueMetadata count=\"1\"><bk><rc t=\"1\" v=\"0\"/></bk></valueMetadata></metadata>`))\n\t\tf.Pkg.Store(defaultXMLRdRichValue, []byte(`<rvData count=\"1\"><rv s=\"0\"><v>0</v><v>5</v><v>logo</v></rv></rvData>`))\n\t\tf.Pkg.Store(defaultXMLRdRichValueRel, []byte(`<richValueRels><rel r:id=\"rId1\"/></richValueRels>`))\n\t\tf.Pkg.Store(defaultXMLRdRichValueRelRels, []byte(fmt.Sprintf(`<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"%s\" Target=\"../media/image1.png\"/></Relationships>`, SourceRelationshipImage)))\n\t\tf.Pkg.Store(defaultXMLRdRichValueStructure, []byte(`<rvStructures><s t=\"_localImage\"><k n=\"_rvRel:LocalImageIdentifier\" t=\"i\"/><k n=\"CalcOrigin\" t=\"i\"/><k n=\"Text\" t=\"s\"/></s></rvStructures>`))\n\t\tf.Sheet.Store(\"xl/worksheets/sheet1.xml\", &xlsxWorksheet{\n\t\t\tSheetData: xlsxSheetData{Row: []xlsxRow{\n\t\t\t\t{R: 1, C: []xlsxC{{R: \"A1\", T: \"e\", V: formulaErrorVALUE, Vm: uintPtr(1)}}},\n\t\t\t}},\n\t\t})\n\t\treturn f\n\t}\n\tf = prepareWorkbook()\n\tpics, err := f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, 1, len(pics))\n\tassert.Equal(t, PictureInsertTypePlaceInCell, pics[0].InsertType)\n\tassert.Equal(t, \"logo\", pics[0].Format.AltText)\n\tcells, err := f.GetPictureCells(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"A1\"}, cells)\n\n\t// Test get the cell images without image relationships parts\n\tf.Relationships.Delete(defaultXMLRdRichValueRelRels)\n\tf.Pkg.Store(defaultXMLRdRichValueRelRels, []byte(fmt.Sprintf(`<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"%s\" Target=\"../media/image1.png\"/></Relationships>`, SourceRelationshipHyperLink)))\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, pics)\n\t// Test get the cell images with unsupported charset rich data rich value relationships\n\tf.Relationships.Delete(defaultXMLRdRichValueRelRels)\n\tf.Pkg.Store(defaultXMLRdRichValueRelRels, MacintoshCyrillicCharset)\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, pics)\n\t// Test get the cell images with unsupported charset rich data rich value\n\tf.Pkg.Store(defaultXMLRdRichValueRel, MacintoshCyrillicCharset)\n\t_, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get the image cells without block of metadata records\n\tcells, err = f.GetPictureCells(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.Empty(t, cells)\n\t// Test get the cell images with rich data rich value relationships\n\tf.Pkg.Store(defaultXMLMetadata, []byte(`<metadata><valueMetadata count=\"1\"><bk><rc t=\"1\" v=\"0\"/></bk></valueMetadata></metadata>`))\n\tf.Pkg.Store(defaultXMLRdRichValueRel, []byte(`<richValueRels/>`))\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, pics)\n\t// Test get the cell images with unsupported charset meta data\n\tf.Pkg.Store(defaultXMLMetadata, MacintoshCyrillicCharset)\n\t_, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get the cell images without block of metadata records\n\tf.Pkg.Store(defaultXMLMetadata, []byte(`<metadata><valueMetadata/></metadata>`))\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, pics)\n\n\tf = prepareWorkbook()\n\t// Test get the cell images with empty image cell rich value\n\tf.Pkg.Store(defaultXMLRdRichValue, []byte(`<rvData count=\"1\"><rv s=\"0\"><v></v><v>5</v><v>logo</v></rv></rvData>`))\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, \"strconv.Atoi: parsing \\\"\\\": invalid syntax\")\n\tassert.Empty(t, pics)\n\t// Test get the cell images without image cell rich value\n\tf.Pkg.Store(defaultXMLRdRichValue, []byte(`<rvData count=\"1\"><rv s=\"0\"><v>0</v><v>1</v></rv></rvData>`))\n\tf.Pkg.Delete(defaultXMLRdRichValueStructure)\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, pics)\n\t// Test get the cell images with unsupported charset rich value\n\tf.Pkg.Store(defaultXMLRdRichValue, MacintoshCyrillicCharset)\n\t_, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\n\tf = prepareWorkbook()\n\t// Test get the cell images with invalid rich value index\n\tf.Pkg.Store(defaultXMLMetadata, []byte(`<metadata><valueMetadata count=\"1\"><bk><rc t=\"1\" v=\"1\"/></bk></valueMetadata></metadata>`))\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, pics)\n\n\tf = prepareWorkbook()\n\t// Test get the cell images inserted by IMAGE formula function\n\tf.Pkg.Store(defaultXMLRdRichValue, []byte(`<rvData count=\"1\"><rv s=\"1\"><v>0</v><v>1</v><v>0</v><v>0</v></rv></rvData>`))\n\tf.Pkg.Store(defaultXMLRdRichValueStructure, []byte(`<rvStructures><s t=\"_localImage\"><k n=\"_rvRel:LocalImageIdentifier\" t=\"i\"/><k n=\"CalcOrigin\" t=\"i\"/></s><s t=\"_webimage\"><k n=\"WebImageIdentifier\" t=\"i\"/><k n=\"CalcOrigin\" t=\"i\"/><k n=\"ComputedImage\" t=\"b\"/><k n=\"ImageSizing\" t=\"i\"/></s></rvStructures>`))\n\tf.Pkg.Store(defaultXMLRdRichValueWebImage, []byte(`<webImagesSrd xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"><webImageSrd><address r:id=\"rId1\"/><blip r:id=\"rId2\"/></webImageSrd>\n\t</webImagesSrd>`))\n\tf.Pkg.Store(defaultXMLRdRichValueWebImageRels, []byte(fmt.Sprintf(`<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"%s\" Target=\"https://github.com/xuri/excelize\" TargetMode=\"External\"/><Relationship Id=\"rId2\" Type=\"%s\" Target=\"../media/image1.png\"/></Relationships>`, SourceRelationshipHyperLink, SourceRelationshipImage)))\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, 1, len(pics))\n\tassert.Equal(t, PictureInsertTypeIMAGE, pics[0].InsertType)\n\n\t// Test get the cell images inserted by IMAGE formula function with unsupported charset web images relationships\n\tf.Relationships.Delete(defaultXMLRdRichValueWebImageRels)\n\tf.Pkg.Store(defaultXMLRdRichValueWebImageRels, MacintoshCyrillicCharset)\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, pics)\n\n\t// Test get the cell images inserted by IMAGE formula function without image part\n\tf.Relationships.Delete(defaultXMLRdRichValueWebImageRels)\n\tf.Pkg.Store(defaultXMLRdRichValueWebImageRels, []byte(fmt.Sprintf(`<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"%s\" Target=\"https://github.com/xuri/excelize\" TargetMode=\"External\"/><Relationship Id=\"rId2\" Type=\"%s\" Target=\"../media/image1.png\"/></Relationships>`, SourceRelationshipHyperLink, SourceRelationshipHyperLink)))\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, pics)\n\t// Test get the cell images inserted by IMAGE formula function with unsupported charset web images part\n\tf.Pkg.Store(defaultXMLRdRichValueWebImage, MacintoshCyrillicCharset)\n\t_, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get the cell images inserted by IMAGE formula function with empty charset web images part\n\tf.Pkg.Store(defaultXMLRdRichValueWebImage, []byte(`<webImagesSrd xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" />`))\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, pics)\n\t// Test get the cell images inserted by IMAGE formula function with invalid rich value index\n\tf.Pkg.Store(defaultXMLRdRichValue, []byte(`<rvData count=\"1\"><rv s=\"1\"><v></v><v>1</v><v>0</v><v>0</v></rv></rvData>`))\n\t_, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, \"strconv.Atoi: parsing \\\"\\\": invalid syntax\")\n\n\tf = prepareWorkbook()\n\t// Test get the cell images inserted by IMAGE formula function with not matched rich value and structure keys\n\tf.Pkg.Store(defaultXMLRdRichValue, []byte(`<rvData count=\"1\"><rv s=\"0\"><v>0</v></rv></rvData>`))\n\tf.Pkg.Store(defaultXMLRdRichValueStructure, []byte(`<rvStructures><s t=\"_localImage\"/></rvStructures>`))\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, pics)\n\t// Test get the cell images inserted by IMAGE formula function without _rvRel:WebImageIdentifier in rich value structure\n\tf.Pkg.Store(defaultXMLRdRichValueStructure, []byte(`<rvStructures><s t=\"_localImage\"><k n=\"CalcOrigin\" t=\"i\"/></s></rvStructures>`))\n\tpics, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, pics)\n\t// Test get the cell images inserted by IMAGE formula function with unsupported charset rich value structures\n\tf.Pkg.Store(defaultXMLRdRichValueStructure, MacintoshCyrillicCharset)\n\t_, err = f.GetPictures(\"Sheet1\", \"A1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestGetImageCells(t *testing.T) {\n\tf := NewFile()\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", MacintoshCyrillicCharset)\n\t_, err := f.getImageCells(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n}\n"
  },
  {
    "path": "pivotTable.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"golang.org/x/text/cases\"\n\t\"golang.org/x/text/language\"\n)\n\n// PivotTableOptions directly maps the format settings of the pivot table.\n//\n// PivotTableStyleName: The built-in pivot table style names\n//\n//\tPivotStyleLight1 - PivotStyleLight28\n//\tPivotStyleMedium1 - PivotStyleMedium28\n//\tPivotStyleDark1 - PivotStyleDark28\ntype PivotTableOptions struct {\n\tpivotTableXML       string\n\tpivotCacheXML       string\n\tpivotSheetName      string\n\tpivotDataRange      string\n\tnamedDataRange      bool\n\tDataRange           string\n\tPivotTableRange     string\n\tName                string\n\tRows                []PivotTableField\n\tColumns             []PivotTableField\n\tData                []PivotTableField\n\tFilter              []PivotTableField\n\tRowGrandTotals      bool\n\tColGrandTotals      bool\n\tShowDrill           bool\n\tUseAutoFormatting   bool\n\tPageOverThenDown    bool\n\tMergeItem           bool\n\tClassicLayout       bool\n\tCompactData         bool\n\tShowError           bool\n\tShowRowHeaders      bool\n\tShowColHeaders      bool\n\tShowRowStripes      bool\n\tShowColStripes      bool\n\tShowLastColumn      bool\n\tFieldPrintTitles    bool\n\tItemPrintTitles     bool\n\tPivotTableStyleName string\n}\n\n// PivotTableField directly maps the field settings of the pivot table.\n//\n// Name specifies the name of the data field. Maximum 255 characters\n// are allowed in data field name, excess characters will be truncated.\n//\n// Subtotal specifies the aggregation function that applies to this data\n// field. The default value is sum. The possible values for this attribute\n// are:\n//\n//\tAverage\n//\tCount\n//\tCountNums\n//\tMax\n//\tMin\n//\tProduct\n//\tStdDev\n//\tStdDevp\n//\tSum\n//\tVar\n//\tVarp\n//\n// NumFmt specifies the number format ID of the data field, this filed only\n// accepts built-in number format ID and does not support custom number format\n// expression currently.\ntype PivotTableField struct {\n\tCompact         bool\n\tData            string\n\tName            string\n\tOutline         bool\n\tShowAll         bool\n\tInsertBlankRow  bool\n\tSubtotal        string\n\tDefaultSubtotal bool\n\tNumFmt          int\n}\n\n// AddPivotTable provides the method to add pivot table by given pivot table\n// options. Note that the same fields can not in Columns, Rows and Filter\n// fields at the same time.\n//\n// For example, create a pivot table on the range reference Sheet1!G2:M34 with\n// the range reference Sheet1!A1:E31 as the data source, summarize by sum for\n// sales:\n//\n//\tpackage main\n//\n//\timport (\n//\t    \"fmt\"\n//\t    \"math/rand\"\n//\n//\t    \"github.com/xuri/excelize/v2\"\n//\t)\n//\n//\tfunc main() {\n//\t    f := excelize.NewFile()\n//\t    defer func() {\n//\t        if err := f.Close(); err != nil {\n//\t            fmt.Println(err)\n//\t        }\n//\t    }()\n//\t    // Create some data in a sheet\n//\t    month := []string{\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"}\n//\t    year := []int{2017, 2018, 2019}\n//\t    types := []string{\"Meat\", \"Dairy\", \"Beverages\", \"Produce\"}\n//\t    region := []string{\"East\", \"West\", \"North\", \"South\"}\n//\t    f.SetSheetRow(\"Sheet1\", \"A1\", &[]string{\"Month\", \"Year\", \"Type\", \"Sales\", \"Region\"})\n//\t    for row := 2; row < 32; row++ {\n//\t        f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"A%d\", row), month[rand.Intn(12)])\n//\t        f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"B%d\", row), year[rand.Intn(3)])\n//\t        f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"C%d\", row), types[rand.Intn(4)])\n//\t        f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"D%d\", row), rand.Intn(5000))\n//\t        f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"E%d\", row), region[rand.Intn(4)])\n//\t    }\n//\t    if err := f.AddPivotTable(&excelize.PivotTableOptions{\n//\t        DataRange:       \"Sheet1!A1:E31\",\n//\t        PivotTableRange: \"Sheet1!G2:M34\",\n//\t        Rows:            []excelize.PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n//\t        Filter:          []excelize.PivotTableField{{Data: \"Region\"}},\n//\t        Columns:         []excelize.PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n//\t        Data:            []excelize.PivotTableField{{Data: \"Sales\", Name: \"Summarize\", Subtotal: \"Sum\"}},\n//\t        RowGrandTotals:  true,\n//\t        ColGrandTotals:  true,\n//\t        ShowDrill:       true,\n//\t        ShowRowHeaders:  true,\n//\t        ShowColHeaders:  true,\n//\t        ShowLastColumn:  true,\n//\t    }); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t    if err := f.SaveAs(\"Book1.xlsx\"); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t}\nfunc (f *File) AddPivotTable(opts *PivotTableOptions) error {\n\t// parameter validation\n\t_, pivotTableSheetPath, err := f.parseFormatPivotTableSet(opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.clearCalcCache()\n\tpivotTableID := f.countPivotTables() + 1\n\tpivotCacheID := f.countPivotCache() + 1\n\n\tsheetRelationshipsPivotTableXML := \"../pivotTables/pivotTable\" + strconv.Itoa(pivotTableID) + \".xml\"\n\topts.pivotTableXML = strings.ReplaceAll(sheetRelationshipsPivotTableXML, \"..\", \"xl\")\n\topts.pivotCacheXML = \"xl/pivotCache/pivotCacheDefinition\" + strconv.Itoa(pivotCacheID) + \".xml\"\n\tif err = f.addPivotCache(opts); err != nil {\n\t\treturn err\n\t}\n\n\t// workbook pivot cache\n\tworkBookPivotCacheRID := f.addRels(f.getWorkbookRelsPath(), SourceRelationshipPivotCache, strings.TrimPrefix(opts.pivotCacheXML, \"xl/\"), \"\")\n\tcacheID := f.addWorkbookPivotCache(workBookPivotCacheRID)\n\n\tpivotCacheRels := \"xl/pivotTables/_rels/pivotTable\" + strconv.Itoa(pivotTableID) + \".xml.rels\"\n\t// rId not used\n\t_ = f.addRels(pivotCacheRels, SourceRelationshipPivotCache, fmt.Sprintf(\"../pivotCache/pivotCacheDefinition%d.xml\", pivotCacheID), \"\")\n\tif err = f.addPivotTable(cacheID, pivotTableID, opts); err != nil {\n\t\treturn err\n\t}\n\tpivotTableSheetRels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(pivotTableSheetPath, \"xl/worksheets/\") + \".rels\"\n\tf.addRels(pivotTableSheetRels, SourceRelationshipPivotTable, sheetRelationshipsPivotTableXML, \"\")\n\tif err = f.addContentTypePart(pivotTableID, \"pivotTable\"); err != nil {\n\t\treturn err\n\t}\n\treturn f.addContentTypePart(pivotCacheID, \"pivotCache\")\n}\n\n// parseFormatPivotTableSet provides a function to validate pivot table\n// properties.\nfunc (f *File) parseFormatPivotTableSet(opts *PivotTableOptions) (*xlsxWorksheet, string, error) {\n\tif opts == nil {\n\t\treturn nil, \"\", ErrParameterRequired\n\t}\n\tpivotTableSheetName, _, err := f.adjustRange(opts.PivotTableRange)\n\tif err != nil {\n\t\treturn nil, \"\", newPivotTableRangeError(err.Error())\n\t}\n\tif countUTF16String(opts.Name) > MaxFieldLength {\n\t\treturn nil, \"\", ErrNameLength\n\t}\n\topts.pivotSheetName = pivotTableSheetName\n\tif err = f.getPivotTableDataRange(opts); err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tdataSheetName, _, err := f.adjustRange(opts.pivotDataRange)\n\tif err != nil {\n\t\treturn nil, \"\", newPivotTableDataRangeError(err.Error())\n\t}\n\tdataSheet, err := f.workSheetReader(dataSheetName)\n\tif err != nil {\n\t\treturn dataSheet, \"\", err\n\t}\n\tpivotTableSheetPath, ok := f.getSheetXMLPath(pivotTableSheetName)\n\tif !ok {\n\t\treturn dataSheet, pivotTableSheetPath, ErrSheetNotExist{pivotTableSheetName}\n\t}\n\tif opts.CompactData && opts.ClassicLayout {\n\t\treturn nil, \"\", ErrPivotTableClassicLayout\n\t}\n\tvar colDataFields, rowDataFields []string\n\tfor _, f := range opts.Filter {\n\t\tif inPivotTableField(opts.Columns, f.Data) != -1 {\n\t\t\tcolDataFields = append(colDataFields, f.Data)\n\t\t}\n\t\tif inPivotTableField(opts.Rows, f.Data) != -1 {\n\t\t\trowDataFields = append(rowDataFields, f.Data)\n\t\t}\n\t}\n\tif len(colDataFields) > 0 {\n\t\treturn dataSheet, pivotTableSheetPath, newPivotTableColFieldsError(colDataFields)\n\t}\n\tif len(rowDataFields) > 0 {\n\t\treturn dataSheet, pivotTableSheetPath, newPivotTableRowFieldsError(rowDataFields)\n\t}\n\treturn dataSheet, pivotTableSheetPath, err\n}\n\n// adjustRange adjust range, for example: adjust Sheet1!$E$31:$A$1 to Sheet1!$A$1:$E$31\nfunc (f *File) adjustRange(rangeStr string) (string, []int, error) {\n\tif len(rangeStr) < 1 {\n\t\treturn \"\", []int{}, ErrParameterRequired\n\t}\n\trng := strings.Split(rangeStr, \"!\")\n\tif len(rng) != 2 {\n\t\treturn \"\", []int{}, ErrParameterInvalid\n\t}\n\ttrimRng := strings.ReplaceAll(rng[1], \"$\", \"\")\n\tcoordinates, err := rangeRefToCoordinates(trimRng)\n\tif err != nil {\n\t\treturn rng[0], []int{}, err\n\t}\n\tx1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]\n\tif x1 == x2 && y1 == y2 {\n\t\treturn rng[0], []int{}, ErrParameterInvalid\n\t}\n\n\t// Correct the range, such correct C1:B3 to B1:C3.\n\tif x2 < x1 {\n\t\tx1, x2 = x2, x1\n\t}\n\n\tif y2 < y1 {\n\t\ty1, y2 = y2, y1\n\t}\n\treturn rng[0], []int{x1, y1, x2, y2}, nil\n}\n\n// getTableFieldsOrder provides a function to get order list of pivot table\n// fields.\nfunc (f *File) getTableFieldsOrder(opts *PivotTableOptions) ([]string, error) {\n\tvar order []string\n\tif err := f.getPivotTableDataRange(opts); err != nil {\n\t\treturn order, err\n\t}\n\tdataSheet, coordinates, err := f.adjustRange(opts.pivotDataRange)\n\tif err != nil {\n\t\treturn order, newPivotTableDataRangeError(err.Error())\n\t}\n\tfor col := coordinates[0]; col <= coordinates[2]; col++ {\n\t\tcoordinate, _ := CoordinatesToCellName(col, coordinates[1])\n\t\tname, err := f.GetCellValue(dataSheet, coordinate)\n\t\tif err != nil {\n\t\t\treturn order, err\n\t\t}\n\t\tif name == \"\" {\n\t\t\treturn order, ErrParameterInvalid\n\t\t}\n\t\torder = append(order, name)\n\t}\n\treturn order, nil\n}\n\n// addPivotCache provides a function to create a pivot cache by given properties.\nfunc (f *File) addPivotCache(opts *PivotTableOptions) error {\n\t// validate data range\n\tdataSheet, coordinates, err := f.adjustRange(opts.pivotDataRange)\n\tif err != nil {\n\t\treturn newPivotTableDataRangeError(err.Error())\n\t}\n\torder, err := f.getTableFieldsOrder(opts)\n\tif err != nil {\n\t\treturn newPivotTableDataRangeError(err.Error())\n\t}\n\ttopLeftCell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])\n\tbottomRightCell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])\n\tpc := xlsxPivotCacheDefinition{\n\t\tSaveData:              false,\n\t\tRefreshOnLoad:         true,\n\t\tCreatedVersion:        pivotTableVersion,\n\t\tRefreshedVersion:      pivotTableRefreshedVersion,\n\t\tMinRefreshableVersion: pivotTableVersion,\n\t\tCacheSource: &xlsxCacheSource{\n\t\t\tType: \"worksheet\",\n\t\t\tWorksheetSource: &xlsxWorksheetSource{\n\t\t\t\tRef:   topLeftCell + \":\" + bottomRightCell,\n\t\t\t\tSheet: dataSheet,\n\t\t\t},\n\t\t},\n\t\tCacheFields: &xlsxCacheFields{},\n\t}\n\tif opts.namedDataRange {\n\t\tpc.CacheSource.WorksheetSource = &xlsxWorksheetSource{Name: opts.DataRange}\n\t}\n\tfor _, name := range order {\n\t\tpc.CacheFields.CacheField = append(pc.CacheFields.CacheField, &xlsxCacheField{\n\t\t\tName:        name,\n\t\t\tSharedItems: &xlsxSharedItems{ContainsBlank: true, M: []xlsxMissing{{}}},\n\t\t})\n\t}\n\tpc.CacheFields.Count = len(pc.CacheFields.CacheField)\n\tpivotCache, err := xml.Marshal(pc)\n\tf.saveFileList(opts.pivotCacheXML, pivotCache)\n\treturn err\n}\n\n// addPivotTable provides a function to create a pivot table by given pivot\n// table ID and properties.\nfunc (f *File) addPivotTable(cacheID, pivotTableID int, opts *PivotTableOptions) error {\n\t// validate pivot table range\n\t_, coordinates, err := f.adjustRange(opts.PivotTableRange)\n\tif err != nil {\n\t\treturn newPivotTableRangeError(err.Error())\n\t}\n\n\ttopLeftCell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])\n\tbottomRightCell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])\n\n\tpivotTableStyle := func() string {\n\t\tif opts.PivotTableStyleName == \"\" {\n\t\t\treturn \"PivotStyleLight16\"\n\t\t}\n\t\treturn opts.PivotTableStyleName\n\t}\n\tpt := xlsxPivotTableDefinition{\n\t\tName:                  opts.Name,\n\t\tCacheID:               cacheID,\n\t\tRowGrandTotals:        &opts.RowGrandTotals,\n\t\tColGrandTotals:        &opts.ColGrandTotals,\n\t\tUpdatedVersion:        pivotTableRefreshedVersion,\n\t\tMinRefreshableVersion: pivotTableVersion,\n\t\tShowDrill:             &opts.ShowDrill,\n\t\tUseAutoFormatting:     &opts.UseAutoFormatting,\n\t\tPageOverThenDown:      &opts.PageOverThenDown,\n\t\tMergeItem:             &opts.MergeItem,\n\t\tCreatedVersion:        pivotTableVersion,\n\t\tCompactData:           &opts.CompactData,\n\t\tGridDropZones:         opts.ClassicLayout,\n\t\tShowError:             &opts.ShowError,\n\t\tFieldPrintTitles:      opts.FieldPrintTitles,\n\t\tItemPrintTitles:       opts.ItemPrintTitles,\n\t\tDataCaption:           \"Values\",\n\t\tLocation: &xlsxLocation{\n\t\t\tRef:            topLeftCell + \":\" + bottomRightCell,\n\t\t\tFirstDataCol:   1,\n\t\t\tFirstDataRow:   1,\n\t\t\tFirstHeaderRow: 1,\n\t\t},\n\t\tPivotFields: &xlsxPivotFields{},\n\t\tRowItems: &xlsxRowItems{\n\t\t\tCount: 1,\n\t\t\tI: []*xlsxI{\n\t\t\t\t{\n\t\t\t\t\t[]*xlsxX{{}},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tColItems: &xlsxColItems{\n\t\t\tCount: 1,\n\t\t\tI:     []*xlsxI{{}},\n\t\t},\n\t\tPivotTableStyleInfo: &xlsxPivotTableStyleInfo{\n\t\t\tName:           pivotTableStyle(),\n\t\t\tShowRowHeaders: opts.ShowRowHeaders,\n\t\t\tShowColHeaders: opts.ShowColHeaders,\n\t\t\tShowRowStripes: opts.ShowRowStripes,\n\t\t\tShowColStripes: opts.ShowColStripes,\n\t\t\tShowLastColumn: opts.ShowLastColumn,\n\t\t},\n\t}\n\tif pt.Name == \"\" {\n\t\tpt.Name = fmt.Sprintf(\"PivotTable%d\", pivotTableID)\n\t}\n\n\t// set classic layout\n\tif opts.ClassicLayout {\n\t\tpt.Compact, pt.CompactData = boolPtr(false), boolPtr(false)\n\t}\n\n\t// pivot fields\n\t_ = f.addPivotFields(&pt, opts)\n\n\t// count pivot fields\n\tpt.PivotFields.Count = len(pt.PivotFields.PivotField)\n\n\t// data range has been checked\n\t_ = f.addPivotRowFields(&pt, opts)\n\t_ = f.addPivotColFields(&pt, opts)\n\t_ = f.addPivotPageFields(&pt, opts)\n\t_ = f.addPivotDataFields(&pt, opts)\n\n\tpivotTable, err := xml.Marshal(pt)\n\tf.saveFileList(opts.pivotTableXML, pivotTable)\n\treturn err\n}\n\n// addPivotRowFields provides a method to add row fields for pivot table by\n// given pivot table options.\nfunc (f *File) addPivotRowFields(pt *xlsxPivotTableDefinition, opts *PivotTableOptions) error {\n\t// row fields\n\trowFieldsIndex, err := f.getPivotFieldsIndex(opts.Rows, opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, fieldIdx := range rowFieldsIndex {\n\t\tif pt.RowFields == nil {\n\t\t\tpt.RowFields = &xlsxRowFields{}\n\t\t}\n\t\tpt.RowFields.Field = append(pt.RowFields.Field, &xlsxField{\n\t\t\tX: fieldIdx,\n\t\t})\n\t}\n\n\t// count row fields\n\tif pt.RowFields != nil {\n\t\tpt.RowFields.Count = len(pt.RowFields.Field)\n\t}\n\treturn err\n}\n\n// addPivotPageFields provides a method to add page fields for pivot table by\n// given pivot table options.\nfunc (f *File) addPivotPageFields(pt *xlsxPivotTableDefinition, opts *PivotTableOptions) error {\n\t// page fields\n\tpageFieldsIndex, err := f.getPivotFieldsIndex(opts.Filter, opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpageFieldsName := f.getPivotTableFieldsName(opts.Filter)\n\tfor idx, pageField := range pageFieldsIndex {\n\t\tif pt.PageFields == nil {\n\t\t\tpt.PageFields = &xlsxPageFields{}\n\t\t}\n\t\tpt.PageFields.PageField = append(pt.PageFields.PageField, &xlsxPageField{\n\t\t\tName: pageFieldsName[idx],\n\t\t\tFld:  pageField,\n\t\t})\n\t}\n\n\t// count page fields\n\tif pt.PageFields != nil {\n\t\tpt.PageFields.Count = len(pt.PageFields.PageField)\n\t}\n\treturn err\n}\n\n// addPivotDataFields provides a method to add data fields for pivot table by\n// given pivot table options.\nfunc (f *File) addPivotDataFields(pt *xlsxPivotTableDefinition, opts *PivotTableOptions) error {\n\t// data fields\n\tdataFieldsIndex, err := f.getPivotFieldsIndex(opts.Data, opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdataFieldsSubtotals := f.getPivotTableFieldsSubtotal(opts.Data)\n\tdataFieldsName := f.getPivotTableFieldsName(opts.Data)\n\tdataFieldsNumFmtID := f.getPivotTableFieldsNumFmtID(opts.Data)\n\tfor idx, dataField := range dataFieldsIndex {\n\t\tif pt.DataFields == nil {\n\t\t\tpt.DataFields = &xlsxDataFields{}\n\t\t}\n\t\tpt.DataFields.DataField = append(pt.DataFields.DataField, &xlsxDataField{\n\t\t\tName:     dataFieldsName[idx],\n\t\t\tFld:      dataField,\n\t\t\tSubtotal: dataFieldsSubtotals[idx],\n\t\t\tNumFmtID: dataFieldsNumFmtID[idx],\n\t\t})\n\t}\n\n\t// count data fields\n\tif pt.DataFields != nil {\n\t\tpt.DataFields.Count = len(pt.DataFields.DataField)\n\t}\n\treturn err\n}\n\n// inPivotTableField provides a method to check if an element is present in\n// pivot table fields list, and return the index of its location, otherwise\n// return -1.\nfunc inPivotTableField(a []PivotTableField, x string) int {\n\tfor idx, n := range a {\n\t\tif x == n.Data {\n\t\t\treturn idx\n\t\t}\n\t}\n\treturn -1\n}\n\n// addPivotColFields create pivot column fields by given pivot table\n// definition and option.\nfunc (f *File) addPivotColFields(pt *xlsxPivotTableDefinition, opts *PivotTableOptions) error {\n\tif len(opts.Columns) == 0 {\n\t\tif len(opts.Data) <= 1 {\n\t\t\treturn nil\n\t\t}\n\t\tpt.ColFields = &xlsxColFields{}\n\t\t// in order to create pivot table in case there is no input from Columns\n\t\tpt.ColFields.Count = 1\n\t\tpt.ColFields.Field = append(pt.ColFields.Field, &xlsxField{\n\t\t\tX: -2,\n\t\t})\n\t\treturn nil\n\t}\n\n\tpt.ColFields = &xlsxColFields{}\n\n\t// col fields\n\tcolFieldsIndex, err := f.getPivotFieldsIndex(opts.Columns, opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, fieldIdx := range colFieldsIndex {\n\t\tpt.ColFields.Field = append(pt.ColFields.Field, &xlsxField{\n\t\t\tX: fieldIdx,\n\t\t})\n\t}\n\n\t// in order to create pivot in case there is many Columns and Data\n\tif len(opts.Data) > 1 {\n\t\tpt.ColFields.Field = append(pt.ColFields.Field, &xlsxField{\n\t\t\tX: -2,\n\t\t})\n\t}\n\n\t// count col fields\n\tpt.ColFields.Count = len(pt.ColFields.Field)\n\treturn err\n}\n\n// setClassicLayout provides a method to set classic layout for pivot table by\n// setting Compact and Outline to false.\nfunc (fld *xlsxPivotField) setClassicLayout(classicLayout bool) {\n\tif classicLayout {\n\t\tfld.Compact, fld.Outline = boolPtr(false), boolPtr(false)\n\t}\n}\n\n// addPivotFields create pivot fields based on the column order of the first\n// row in the data region by given pivot table definition and option.\nfunc (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opts *PivotTableOptions) error {\n\torder, err := f.getTableFieldsOrder(opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\tx := 0\n\tfor _, name := range order {\n\t\tif inPivotTableField(opts.Rows, name) != -1 {\n\t\t\trowOptions, ok := f.getPivotTableFieldOptions(name, opts.Rows)\n\t\t\tvar items []*xlsxItem\n\t\t\tif !ok || !rowOptions.DefaultSubtotal {\n\t\t\t\titems = append(items, &xlsxItem{X: &x})\n\t\t\t} else {\n\t\t\t\titems = append(items, &xlsxItem{T: \"default\"})\n\t\t\t}\n\t\t\tfld := &xlsxPivotField{\n\t\t\t\tName:            f.getPivotTableFieldName(name, opts.Rows),\n\t\t\t\tAxis:            \"axisRow\",\n\t\t\t\tDataField:       inPivotTableField(opts.Data, name) != -1,\n\t\t\t\tCompact:         &rowOptions.Compact,\n\t\t\t\tOutline:         &rowOptions.Outline,\n\t\t\t\tShowAll:         rowOptions.ShowAll,\n\t\t\t\tInsertBlankRow:  rowOptions.InsertBlankRow,\n\t\t\t\tDefaultSubtotal: &rowOptions.DefaultSubtotal,\n\t\t\t\tItems: &xlsxItems{\n\t\t\t\t\tCount: len(items),\n\t\t\t\t\tItem:  items,\n\t\t\t\t},\n\t\t\t}\n\t\t\tfld.setClassicLayout(opts.ClassicLayout)\n\t\t\tpt.PivotFields.PivotField = append(pt.PivotFields.PivotField, fld)\n\t\t\tcontinue\n\t\t}\n\t\tif inPivotTableField(opts.Filter, name) != -1 {\n\t\t\tfld := &xlsxPivotField{\n\t\t\t\tAxis:      \"axisPage\",\n\t\t\t\tDataField: inPivotTableField(opts.Data, name) != -1,\n\t\t\t\tName:      f.getPivotTableFieldName(name, opts.Columns),\n\t\t\t\tItems: &xlsxItems{\n\t\t\t\t\tCount: 1,\n\t\t\t\t\tItem: []*xlsxItem{\n\t\t\t\t\t\t{T: \"default\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\tfld.setClassicLayout(opts.ClassicLayout)\n\t\t\tpt.PivotFields.PivotField = append(pt.PivotFields.PivotField, fld)\n\t\t\tcontinue\n\t\t}\n\t\tif inPivotTableField(opts.Columns, name) != -1 {\n\t\t\tcolumnOptions, ok := f.getPivotTableFieldOptions(name, opts.Columns)\n\t\t\tvar items []*xlsxItem\n\t\t\tif !ok || !columnOptions.DefaultSubtotal {\n\t\t\t\titems = append(items, &xlsxItem{X: &x})\n\t\t\t} else {\n\t\t\t\titems = append(items, &xlsxItem{T: \"default\"})\n\t\t\t}\n\t\t\tfld := &xlsxPivotField{\n\t\t\t\tName:            f.getPivotTableFieldName(name, opts.Columns),\n\t\t\t\tAxis:            \"axisCol\",\n\t\t\t\tDataField:       inPivotTableField(opts.Data, name) != -1,\n\t\t\t\tCompact:         &columnOptions.Compact,\n\t\t\t\tOutline:         &columnOptions.Outline,\n\t\t\t\tShowAll:         columnOptions.ShowAll,\n\t\t\t\tInsertBlankRow:  columnOptions.InsertBlankRow,\n\t\t\t\tDefaultSubtotal: &columnOptions.DefaultSubtotal,\n\t\t\t\tItems: &xlsxItems{\n\t\t\t\t\tCount: len(items),\n\t\t\t\t\tItem:  items,\n\t\t\t\t},\n\t\t\t}\n\t\t\tfld.setClassicLayout(opts.ClassicLayout)\n\t\t\tpt.PivotFields.PivotField = append(pt.PivotFields.PivotField, fld)\n\t\t\tcontinue\n\t\t}\n\t\tif inPivotTableField(opts.Data, name) != -1 {\n\t\t\tfld := &xlsxPivotField{\n\t\t\t\tDataField: true,\n\t\t\t}\n\t\t\tfld.setClassicLayout(opts.ClassicLayout)\n\t\t\tpt.PivotFields.PivotField = append(pt.PivotFields.PivotField, fld)\n\t\t\tcontinue\n\t\t}\n\t\tfld := &xlsxPivotField{}\n\t\tfld.setClassicLayout(opts.ClassicLayout)\n\t\tpt.PivotFields.PivotField = append(pt.PivotFields.PivotField, fld)\n\t}\n\treturn err\n}\n\n// countPivotTables provides a function to get pivot table files count storage\n// in the folder xl/pivotTables.\nfunc (f *File) countPivotTables() int {\n\tcount := 0\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/pivotTables/pivotTable\") {\n\t\t\tcount++\n\t\t}\n\t\treturn true\n\t})\n\treturn count\n}\n\n// countPivotCache provides a function to get pivot table cache definition files\n// count storage in the folder xl/pivotCache.\nfunc (f *File) countPivotCache() int {\n\tcount := 0\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/pivotCache/pivotCacheDefinition\") {\n\t\t\tcount++\n\t\t}\n\t\treturn true\n\t})\n\treturn count\n}\n\n// getPivotFieldsIndex convert the column of the first row in the data region\n// to a sequential index by given fields and pivot option.\nfunc (f *File) getPivotFieldsIndex(fields []PivotTableField, opts *PivotTableOptions) ([]int, error) {\n\tvar pivotFieldsIndex []int\n\torders, err := f.getTableFieldsOrder(opts)\n\tif err != nil {\n\t\treturn pivotFieldsIndex, err\n\t}\n\tfor _, field := range fields {\n\t\tif pos := inStrSlice(orders, field.Data, true); pos != -1 {\n\t\t\tpivotFieldsIndex = append(pivotFieldsIndex, pos)\n\t\t}\n\t}\n\treturn pivotFieldsIndex, nil\n}\n\n// getPivotTableFieldsSubtotal prepare fields subtotal by given pivot table fields.\nfunc (f *File) getPivotTableFieldsSubtotal(fields []PivotTableField) []string {\n\tfield := make([]string, len(fields))\n\tenums := []string{\"average\", \"count\", \"countNums\", \"max\", \"min\", \"product\", \"stdDev\", \"stdDevp\", \"sum\", \"var\", \"varp\"}\n\tinEnums := func(enums []string, val string) string {\n\t\tfor _, enum := range enums {\n\t\t\tif strings.EqualFold(enum, val) {\n\t\t\t\treturn enum\n\t\t\t}\n\t\t}\n\t\treturn \"sum\"\n\t}\n\tfor idx, fld := range fields {\n\t\tfield[idx] = inEnums(enums, fld.Subtotal)\n\t}\n\treturn field\n}\n\n// getPivotTableFieldsName prepare fields name list by given pivot table\n// fields.\nfunc (f *File) getPivotTableFieldsName(fields []PivotTableField) []string {\n\tfield := make([]string, len(fields))\n\tfor idx, fld := range fields {\n\t\tif countUTF16String(fld.Name) > MaxFieldLength {\n\t\t\tfield[idx] = truncateUTF16Units(fld.Name, MaxFieldLength)\n\t\t\tcontinue\n\t\t}\n\t\tfield[idx] = fld.Name\n\t}\n\treturn field\n}\n\n// getPivotTableFieldName prepare field name by given pivot table fields.\nfunc (f *File) getPivotTableFieldName(name string, fields []PivotTableField) string {\n\tfieldsName := f.getPivotTableFieldsName(fields)\n\tfor idx, field := range fields {\n\t\tif field.Data == name {\n\t\t\treturn fieldsName[idx]\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// getPivotTableFieldsNumFmtID prepare fields number format ID by given pivot\n// table fields.\nfunc (f *File) getPivotTableFieldsNumFmtID(fields []PivotTableField) []int {\n\tfield := make([]int, len(fields))\n\tfor idx, fld := range fields {\n\t\tif _, ok := builtInNumFmt[fld.NumFmt]; ok {\n\t\t\tfield[idx] = fld.NumFmt\n\t\t\tcontinue\n\t\t}\n\t\tif (27 <= fld.NumFmt && fld.NumFmt <= 36) || (50 <= fld.NumFmt && fld.NumFmt <= 81) {\n\t\t\tfield[idx] = fld.NumFmt\n\t\t}\n\t}\n\treturn field\n}\n\n// getPivotTableFieldOptions return options for specific field by given field name.\nfunc (f *File) getPivotTableFieldOptions(name string, fields []PivotTableField) (options PivotTableField, ok bool) {\n\tfor _, field := range fields {\n\t\tif field.Data == name {\n\t\t\toptions, ok = field, true\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\n// addWorkbookPivotCache add the association ID of the pivot cache in workbook.xml.\nfunc (f *File) addWorkbookPivotCache(RID int) int {\n\twb, _ := f.workbookReader()\n\tif wb.PivotCaches == nil {\n\t\twb.PivotCaches = &xlsxPivotCaches{}\n\t}\n\tcacheID := 1\n\tfor _, pivotCache := range wb.PivotCaches.PivotCache {\n\t\tif pivotCache.CacheID > cacheID {\n\t\t\tcacheID = pivotCache.CacheID\n\t\t}\n\t}\n\tcacheID++\n\twb.PivotCaches.PivotCache = append(wb.PivotCaches.PivotCache, xlsxPivotCache{\n\t\tCacheID: cacheID,\n\t\tRID:     fmt.Sprintf(\"rId%d\", RID),\n\t})\n\treturn cacheID\n}\n\n// GetPivotTables returns all pivot table definitions in a worksheet by given\n// worksheet name. Currently only support get pivot table cache with worksheet\n// source type, and doesn't support source types: external, consolidation\n// and scenario.\nfunc (f *File) GetPivotTables(sheet string) ([]PivotTableOptions, error) {\n\tvar pivotTables []PivotTableOptions\n\tname, ok := f.getSheetXMLPath(sheet)\n\tif !ok {\n\t\treturn pivotTables, ErrSheetNotExist{sheet}\n\t}\n\trels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(name, \"xl/worksheets/\") + \".rels\"\n\tsheetRels, err := f.relsReader(rels)\n\tif err != nil {\n\t\treturn pivotTables, err\n\t}\n\tif sheetRels == nil {\n\t\tsheetRels = &xlsxRelationships{}\n\t}\n\tfor _, v := range sheetRels.Relationships {\n\t\tif v.Type == SourceRelationshipPivotTable {\n\t\t\tpivotTableXML := strings.ReplaceAll(v.Target, \"..\", \"xl\")\n\t\t\tpivotCacheRels := \"xl/pivotTables/_rels/\" + filepath.Base(v.Target) + \".rels\"\n\t\t\tpivotTable, err := f.getPivotTable(sheet, pivotTableXML, pivotCacheRels)\n\t\t\tif err != nil {\n\t\t\t\treturn pivotTables, err\n\t\t\t}\n\t\t\tpivotTables = append(pivotTables, pivotTable)\n\t\t}\n\t}\n\treturn pivotTables, nil\n}\n\n// getPivotTableDataRange checking given if data range is a cell reference or\n// named reference (defined name or table name), and set pivot table data range.\nfunc (f *File) getPivotTableDataRange(opts *PivotTableOptions) error {\n\tif opts.DataRange == \"\" {\n\t\treturn newPivotTableDataRangeError(ErrParameterRequired.Error())\n\t}\n\tif opts.pivotDataRange != \"\" {\n\t\treturn nil\n\t}\n\tif strings.Contains(opts.DataRange, \"!\") {\n\t\topts.pivotDataRange = opts.DataRange\n\t\treturn nil\n\t}\n\ttbls, err := f.getTables()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor sheetName, tables := range tbls {\n\t\tfor _, table := range tables {\n\t\t\tif table.Name == opts.DataRange {\n\t\t\t\topts.pivotDataRange, opts.namedDataRange = fmt.Sprintf(\"%s!%s\", sheetName, table.Range), true\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\tif !opts.namedDataRange {\n\t\topts.pivotDataRange = f.getDefinedNameRefTo(opts.DataRange, opts.pivotSheetName)\n\t\tif opts.pivotDataRange != \"\" {\n\t\t\topts.namedDataRange = true\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn newPivotTableDataRangeError(ErrParameterInvalid.Error())\n}\n\n// getPivotTable provides a function to get a pivot table definition by given\n// worksheet name, pivot table XML path and pivot cache relationship XML path.\nfunc (f *File) getPivotTable(sheet, pivotTableXML, pivotCacheRels string) (PivotTableOptions, error) {\n\tvar opts PivotTableOptions\n\trels, err := f.relsReader(pivotCacheRels)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\tvar pivotCacheXML string\n\tfor _, v := range rels.Relationships {\n\t\tif v.Type == SourceRelationshipPivotCache {\n\t\t\tpivotCacheXML = strings.ReplaceAll(v.Target, \"..\", \"xl\")\n\t\t\tbreak\n\t\t}\n\t}\n\tpc, err := f.pivotCacheReader(pivotCacheXML)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\tpt, err := f.pivotTableReader(pivotTableXML)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\tif pc.CacheSource.WorksheetSource == nil {\n\t\treturn opts, newUnsupportedPivotCacheSourceType(pc.CacheSource.Type)\n\t}\n\topts = PivotTableOptions{\n\t\tpivotTableXML:    pivotTableXML,\n\t\tpivotCacheXML:    pivotCacheXML,\n\t\tpivotSheetName:   sheet,\n\t\tDataRange:        fmt.Sprintf(\"%s!%s\", pc.CacheSource.WorksheetSource.Sheet, pc.CacheSource.WorksheetSource.Ref),\n\t\tPivotTableRange:  fmt.Sprintf(\"%s!%s\", sheet, pt.Location.Ref),\n\t\tName:             pt.Name,\n\t\tClassicLayout:    pt.GridDropZones,\n\t\tFieldPrintTitles: pt.FieldPrintTitles,\n\t\tItemPrintTitles:  pt.ItemPrintTitles,\n\t}\n\tif pc.CacheSource.WorksheetSource.Name != \"\" {\n\t\topts.DataRange = pc.CacheSource.WorksheetSource.Name\n\t\t_ = f.getPivotTableDataRange(&opts)\n\t}\n\tsetPtrFieldsVal([]string{\n\t\t\"RowGrandTotals\", \"ColGrandTotals\", \"ShowDrill\",\n\t\t\"UseAutoFormatting\", \"PageOverThenDown\", \"MergeItem\", \"CompactData\", \"ShowError\",\n\t}, reflect.ValueOf(*pt), reflect.ValueOf(&opts).Elem())\n\tif si := pt.PivotTableStyleInfo; si != nil {\n\t\topts.ShowRowHeaders = si.ShowRowHeaders\n\t\topts.ShowColHeaders = si.ShowColHeaders\n\t\topts.ShowRowStripes = si.ShowRowStripes\n\t\topts.ShowColStripes = si.ShowColStripes\n\t\topts.ShowLastColumn = si.ShowLastColumn\n\t\topts.PivotTableStyleName = si.Name\n\t}\n\tif err = f.getPivotTableDataRange(&opts); err != nil {\n\t\treturn opts, err\n\t}\n\tf.extractPivotTableFields(pc.getPivotCacheFieldsName(), pt, &opts)\n\treturn opts, err\n}\n\n// getPivotCacheFieldsName returns pivot table fields name list by order from\n// pivot cache fields.\nfunc (pc *xlsxPivotCacheDefinition) getPivotCacheFieldsName() []string {\n\tvar order []string\n\tif pc.CacheFields != nil {\n\t\tfor _, cf := range pc.CacheFields.CacheField {\n\t\t\tif cf != nil {\n\t\t\t\torder = append(order, cf.Name)\n\t\t\t}\n\t\t}\n\t}\n\treturn order\n}\n\n// pivotTableReader provides a function to get the pointer to the structure\n// after deserialization of xl/pivotTables/pivotTable%d.xml.\nfunc (f *File) pivotTableReader(path string) (*xlsxPivotTableDefinition, error) {\n\tcontent, ok := f.Pkg.Load(path)\n\tpivotTable := &xlsxPivotTableDefinition{}\n\tif ok && content != nil {\n\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).\n\t\t\tDecode(pivotTable); err != nil && err != io.EOF {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn pivotTable, nil\n}\n\n// pivotCacheReader provides a function to get the pointer to the structure\n// after deserialization of xl/pivotCache/pivotCacheDefinition%d.xml.\nfunc (f *File) pivotCacheReader(path string) (*xlsxPivotCacheDefinition, error) {\n\tcontent, ok := f.Pkg.Load(path)\n\tpivotCache := &xlsxPivotCacheDefinition{}\n\tif ok && content != nil {\n\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).\n\t\t\tDecode(pivotCache); err != nil && err != io.EOF {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn pivotCache, nil\n}\n\n// extractPivotTableFields provides a function to extract all pivot table fields\n// settings by given pivot table fields.\nfunc (f *File) extractPivotTableFields(order []string, pt *xlsxPivotTableDefinition, opts *PivotTableOptions) {\n\tfor fieldIdx, field := range pt.PivotFields.PivotField {\n\t\tif field.Axis == \"axisRow\" {\n\t\t\topts.Rows = append(opts.Rows, extractPivotTableField(order[fieldIdx], field))\n\t\t}\n\t\tif field.Axis == \"axisCol\" {\n\t\t\topts.Columns = append(opts.Columns, extractPivotTableField(order[fieldIdx], field))\n\t\t}\n\t\tif field.Axis == \"axisPage\" {\n\t\t\topts.Filter = append(opts.Filter, extractPivotTableField(order[fieldIdx], field))\n\t\t}\n\t}\n\tif pt.DataFields != nil {\n\t\tfor _, field := range pt.DataFields.DataField {\n\t\t\topts.Data = append(opts.Data, PivotTableField{\n\t\t\t\tData:     order[field.Fld],\n\t\t\t\tName:     field.Name,\n\t\t\t\tSubtotal: cases.Title(language.English).String(field.Subtotal),\n\t\t\t\tNumFmt:   field.NumFmtID,\n\t\t\t})\n\t\t}\n\t}\n}\n\n// extractPivotTableField provides a function to extract pivot table field\n// settings by given pivot table fields.\nfunc extractPivotTableField(data string, fld *xlsxPivotField) PivotTableField {\n\tpivotTableField := PivotTableField{\n\t\tData:           data,\n\t\tShowAll:        fld.ShowAll,\n\t\tInsertBlankRow: fld.InsertBlankRow,\n\t}\n\tsetPtrFieldsVal([]string{\"Compact\", \"Outline\", \"DefaultSubtotal\"},\n\t\treflect.ValueOf(*fld), reflect.ValueOf(&pivotTableField).Elem())\n\treturn pivotTableField\n}\n\n// genPivotCacheDefinitionID generates a unique pivot table cache definition ID.\nfunc (f *File) genPivotCacheDefinitionID() int {\n\tvar (\n\t\tID                            int\n\t\tdecodeExtLst                  = new(decodeExtLst)\n\t\tdecodeX14PivotCacheDefinition = new(decodeX14PivotCacheDefinition)\n\t)\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/pivotCache/pivotCacheDefinition\") {\n\t\t\tpc, err := f.pivotCacheReader(k.(string))\n\t\t\tif err != nil {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif pc.ExtLst != nil {\n\t\t\t\t_ = f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + pc.ExtLst.Ext + \"</extLst>\")).Decode(decodeExtLst)\n\t\t\t\tfor _, ext := range decodeExtLst.Ext {\n\t\t\t\t\tif ext.URI == ExtURIPivotCacheDefinition {\n\t\t\t\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(decodeX14PivotCacheDefinition)\n\t\t\t\t\t\tif ID < decodeX14PivotCacheDefinition.PivotCacheID {\n\t\t\t\t\t\t\tID = decodeX14PivotCacheDefinition.PivotCacheID\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\treturn ID + 1\n}\n\n// deleteWorkbookPivotCache remove workbook pivot cache and pivot cache\n// relationships.\nfunc (f *File) deleteWorkbookPivotCache(opt PivotTableOptions) error {\n\trID, err := f.deleteWorkbookRels(SourceRelationshipPivotCache, strings.TrimPrefix(strings.TrimPrefix(opt.pivotCacheXML, \"/\"), \"xl/\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif wb.PivotCaches != nil {\n\t\tfor i, pivotCache := range wb.PivotCaches.PivotCache {\n\t\t\tif pivotCache.RID == rID {\n\t\t\t\twb.PivotCaches.PivotCache = append(wb.PivotCaches.PivotCache[:i], wb.PivotCaches.PivotCache[i+1:]...)\n\t\t\t}\n\t\t}\n\t\tif len(wb.PivotCaches.PivotCache) == 0 {\n\t\t\twb.PivotCaches = nil\n\t\t}\n\t}\n\treturn err\n}\n\n// DeletePivotTable delete a pivot table by giving the worksheet name and pivot\n// table name. Note that this function does not clean cell values in the pivot\n// table range.\nfunc (f *File) DeletePivotTable(sheet, name string) error {\n\tsheetXML, ok := f.getSheetXMLPath(sheet)\n\tif !ok {\n\t\treturn ErrSheetNotExist{sheet}\n\t}\n\trels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(sheetXML, \"xl/worksheets/\") + \".rels\"\n\tsheetRels, err := f.relsReader(rels)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif sheetRels == nil {\n\t\tsheetRels = &xlsxRelationships{}\n\t}\n\topts, err := f.GetPivotTables(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.clearCalcCache()\n\tpivotTableCaches := map[string]int{}\n\tpivotTables, _ := f.getPivotTables()\n\tfor _, sheetPivotTables := range pivotTables {\n\t\tfor _, sheetPivotTable := range sheetPivotTables {\n\t\t\tpivotTableCaches[sheetPivotTable.pivotCacheXML]++\n\t\t}\n\t}\n\tfor _, v := range sheetRels.Relationships {\n\t\tfor _, opt := range opts {\n\t\t\tif v.Type == SourceRelationshipPivotTable {\n\t\t\t\tpivotTableXML := strings.ReplaceAll(v.Target, \"..\", \"xl\")\n\t\t\t\tif opt.Name == name && opt.pivotTableXML == pivotTableXML {\n\t\t\t\t\tif pivotTableCaches[opt.pivotCacheXML] == 1 {\n\t\t\t\t\t\terr = f.deleteWorkbookPivotCache(opt)\n\t\t\t\t\t}\n\t\t\t\t\tf.deleteSheetRelationships(sheet, v.ID)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn newNoExistTableError(name)\n}\n\n// getPivotTables provides a function to get all pivot tables in a workbook.\nfunc (f *File) getPivotTables() (map[string][]PivotTableOptions, error) {\n\tpivotTables := map[string][]PivotTableOptions{}\n\tfor _, sheetName := range f.GetSheetList() {\n\t\tpts, err := f.GetPivotTables(sheetName)\n\t\te := ErrSheetNotExist{sheetName}\n\t\tif err != nil && err.Error() != newNotWorksheetError(sheetName).Error() && err.Error() != e.Error() {\n\t\t\treturn pivotTables, err\n\t\t}\n\t\tpivotTables[sheetName] = append(pivotTables[sheetName], pts...)\n\t}\n\treturn pivotTables, nil\n}\n"
  },
  {
    "path": "pivotTable_test.go",
    "content": "package excelize\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestPivotTable(t *testing.T) {\n\tf := NewFile()\n\t// Create some data in a sheet\n\tmonth := []string{\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"}\n\tyear := []int{2017, 2018, 2019}\n\ttypes := []string{\"Meat\", \"Dairy\", \"Beverages\", \"Produce\"}\n\tregion := []string{\"East\", \"West\", \"North\", \"South\"}\n\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A1\", &[]string{\"Month\", \"Year\", \"Type\", \"Sales\", \"Region\"}))\n\tfor row := 2; row < 32; row++ {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"A%d\", row), month[rand.Intn(12)]))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"B%d\", row), year[rand.Intn(3)]))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"C%d\", row), types[rand.Intn(4)]))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"D%d\", row), rand.Intn(5000)))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"E%d\", row), region[rand.Intn(4)]))\n\t}\n\texpected := &PivotTableOptions{\n\t\tpivotTableXML:       \"xl/pivotTables/pivotTable1.xml\",\n\t\tpivotCacheXML:       \"xl/pivotCache/pivotCacheDefinition1.xml\",\n\t\tDataRange:           \"Sheet1!A1:E31\",\n\t\tPivotTableRange:     \"Sheet1!G2:M34\",\n\t\tName:                \"PivotTable1\",\n\t\tRows:                []PivotTableField{{Data: \"Month\", ShowAll: true, DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tFilter:              []PivotTableField{{Data: \"Region\"}},\n\t\tColumns:             []PivotTableField{{Data: \"Type\", ShowAll: true, InsertBlankRow: true, DefaultSubtotal: true}},\n\t\tData:                []PivotTableField{{Data: \"Sales\", Subtotal: \"Sum\", Name: \"Summarize by Sum\", NumFmt: 38}},\n\t\tRowGrandTotals:      true,\n\t\tColGrandTotals:      true,\n\t\tShowDrill:           true,\n\t\tClassicLayout:       true,\n\t\tShowError:           true,\n\t\tShowRowHeaders:      true,\n\t\tShowColHeaders:      true,\n\t\tShowLastColumn:      true,\n\t\tFieldPrintTitles:    true,\n\t\tItemPrintTitles:     true,\n\t\tPivotTableStyleName: \"PivotStyleLight16\",\n\t}\n\tassert.NoError(t, f.AddPivotTable(expected))\n\t// Test get pivot table\n\tpivotTables, err := f.GetPivotTables(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pivotTables, 1)\n\tassert.Equal(t, *expected, pivotTables[0])\n\t// Use different order of coordinate tests\n\tassert.NoError(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"Sheet1!U34:O2\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\", Subtotal: \"Average\", Name: \"Summarize by Average\"}},\n\t\tRowGrandTotals:  true,\n\t\tColGrandTotals:  true,\n\t\tShowDrill:       true,\n\t\tShowRowHeaders:  true,\n\t\tShowColHeaders:  true,\n\t\tShowLastColumn:  true,\n\t}))\n\t// Test get pivot table with default style name\n\tpivotTables, err = f.GetPivotTables(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pivotTables, 2)\n\tassert.Equal(t, \"PivotStyleLight16\", pivotTables[1].PivotTableStyleName)\n\n\tassert.NoError(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"Sheet1!W2:AC34\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Region\"}},\n\t\tData:            []PivotTableField{{Data: \"Sales\", Subtotal: \"Count\", Name: \"Summarize by Count\"}},\n\t\tRowGrandTotals:  true,\n\t\tColGrandTotals:  true,\n\t\tShowDrill:       true,\n\t\tShowRowHeaders:  true,\n\t\tShowColHeaders:  true,\n\t\tShowLastColumn:  true,\n\t}))\n\tassert.NoError(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"Sheet1!G42:W55\",\n\t\tRows:            []PivotTableField{{Data: \"Month\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Region\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tData:            []PivotTableField{{Data: \"Sales\", Subtotal: \"CountNums\", Name: \"Summarize by CountNums\"}},\n\t\tRowGrandTotals:  true,\n\t\tColGrandTotals:  true,\n\t\tShowDrill:       true,\n\t\tShowRowHeaders:  true,\n\t\tShowColHeaders:  true,\n\t\tShowLastColumn:  true,\n\t}))\n\tassert.NoError(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"Sheet1!AE2:AG33\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tData:            []PivotTableField{{Data: \"Sales\", Subtotal: \"Max\", Name: \"Summarize by Max\"}, {Data: \"Sales\", Subtotal: \"Average\", Name: \"Average of Sales\"}},\n\t\tRowGrandTotals:  true,\n\t\tColGrandTotals:  true,\n\t\tShowDrill:       true,\n\t\tShowRowHeaders:  true,\n\t\tShowColHeaders:  true,\n\t\tShowLastColumn:  true,\n\t}))\n\t// Create pivot table with empty subtotal field name and specified style\n\tassert.NoError(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:           \"Sheet1!A1:E31\",\n\t\tPivotTableRange:     \"Sheet1!AJ2:AP135\",\n\t\tRows:                []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tFilter:              []PivotTableField{{Data: \"Region\"}},\n\t\tColumns:             []PivotTableField{},\n\t\tData:                []PivotTableField{{Subtotal: \"Sum\", Name: \"Summarize by Sum\"}},\n\t\tRowGrandTotals:      true,\n\t\tColGrandTotals:      true,\n\t\tShowDrill:           true,\n\t\tShowRowHeaders:      true,\n\t\tShowColHeaders:      true,\n\t\tShowLastColumn:      true,\n\t\tPivotTableStyleName: \"PivotStyleLight19\",\n\t}))\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"Sheet2!A1:AN17\",\n\t\tRows:            []PivotTableField{{Data: \"Month\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Region\", DefaultSubtotal: true}, {Data: \"Type\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tData:            []PivotTableField{{Data: \"Sales\", Subtotal: \"Min\", Name: \"Summarize by Min\", NumFmt: 32}},\n\t\tRowGrandTotals:  true,\n\t\tColGrandTotals:  true,\n\t\tShowDrill:       true,\n\t\tShowRowHeaders:  true,\n\t\tShowColHeaders:  true,\n\t\tShowLastColumn:  true,\n\t}))\n\n\t// Test get pivot table with across worksheet data range\n\tpivotTables, err = f.GetPivotTables(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pivotTables, 1)\n\tassert.Equal(t, \"Sheet1!A1:E31\", pivotTables[0].DataRange)\n\n\tassert.NoError(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"Sheet2!A20:AR60\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Type\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Region\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tData:            []PivotTableField{{Data: \"Sales\", Subtotal: \"Product\", Name: \"Summarize by Product\", NumFmt: 32}},\n\t\tRowGrandTotals:  true,\n\t\tColGrandTotals:  true,\n\t\tShowDrill:       true,\n\t\tShowRowHeaders:  true,\n\t\tShowColHeaders:  true,\n\t\tShowLastColumn:  true,\n\t}))\n\t// Create pivot table with many data, many rows, many cols and defined name\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     \"dataRange\",\n\t\tRefersTo: \"Sheet1!A1:E31\",\n\t\tComment:  \"Pivot Table Data Range\",\n\t\tScope:    \"Sheet2\",\n\t}))\n\tassert.NoError(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"dataRange\",\n\t\tPivotTableRange: \"Sheet2!A65:AJ100\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Region\", DefaultSubtotal: true}, {Data: \"Type\"}},\n\t\tData:            []PivotTableField{{Data: \"Sales\", Subtotal: \"Sum\", Name: \"Sum of Sales\", NumFmt: -1}, {Data: \"Sales\", Subtotal: \"Average\", Name: \"Average of Sales\", NumFmt: 38}},\n\t\tRowGrandTotals:  true,\n\t\tColGrandTotals:  true,\n\t\tShowDrill:       true,\n\t\tShowRowHeaders:  true,\n\t\tShowColHeaders:  true,\n\t\tShowLastColumn:  true,\n\t}))\n\n\t// Test empty pivot table options\n\tassert.Equal(t, ErrParameterRequired, f.AddPivotTable(nil))\n\t// Test add pivot table with custom name which exceeds the max characters limit\n\tassert.Equal(t, ErrNameLength, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"dataRange\",\n\t\tPivotTableRange: \"Sheet2!A65:AJ100\",\n\t\tName:            strings.Repeat(\"c\", MaxFieldLength+1),\n\t}))\n\t// Test invalid data range\n\tassert.Equal(t, newPivotTableDataRangeError(\"parameter is invalid\"), f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:A1\",\n\t\tPivotTableRange: \"Sheet1!U34:O2\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\"}},\n\t}))\n\t// Test the data range of the worksheet that is not declared\n\tassert.Equal(t, newPivotTableDataRangeError(\"parameter is invalid\"), f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"A1:E31\",\n\t\tPivotTableRange: \"Sheet1!U34:O2\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\"}},\n\t}))\n\t// Test the worksheet declared in the data range does not exist\n\tassert.Equal(t, ErrSheetNotExist{\"SheetN\"}, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"SheetN!A1:E31\",\n\t\tPivotTableRange: \"Sheet1!U34:O2\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\"}},\n\t}))\n\t// Test the pivot table range of the worksheet that is not declared\n\tassert.Equal(t, newPivotTableRangeError(\"parameter is invalid\"), f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"U34:O2\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\"}},\n\t}))\n\t// Test the worksheet declared in the pivot table range does not exist\n\tassert.Equal(t, ErrSheetNotExist{\"SheetN\"}, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"SheetN!U34:O2\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\"}},\n\t}))\n\t// Test not exists worksheet in data range\n\tassert.Equal(t, ErrSheetNotExist{\"SheetN\"}, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"SheetN!A1:E31\",\n\t\tPivotTableRange: \"Sheet1!U34:O2\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\"}},\n\t}))\n\t// Test invalid row number in data range\n\tassert.Equal(t, newPivotTableDataRangeError(newCellNameToCoordinatesError(\"A0\", newInvalidCellNameError(\"A0\")).Error()), f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A0:E31\",\n\t\tPivotTableRange: \"Sheet1!U34:O2\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\"}},\n\t}))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddPivotTable1.xlsx\")))\n\t// Test with field names that exceed the length limit and invalid subtotal\n\tassert.NoError(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"Sheet1!G2:M34\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\", Subtotal: \"-\", Name: strings.Repeat(\"s\", MaxFieldLength+1)}},\n\t}))\n\t// Test with same data field appears both in the pivot table column fields and filter fields\n\tassert.Equal(t, newPivotTableColFieldsError([]string{\"Type\"}), f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"Sheet1!G2:M34\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\"}},\n\t\tFilter:          []PivotTableField{{Data: \"Type\"}},\n\t}))\n\t// Test with same data field appears both in the pivot table row fields and filter fields\n\tassert.Equal(t, newPivotTableRowFieldsError([]string{\"Month\"}), f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"Sheet1!G2:M34\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\"}},\n\t\tFilter:          []PivotTableField{{Data: \"Month\"}},\n\t}))\n\t// Test delete pivot table\n\tpivotTables, err = f.GetPivotTables(\"Sheet1\")\n\tassert.Len(t, pivotTables, 7)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.DeletePivotTable(\"Sheet1\", \"PivotTable1\"))\n\tpivotTables, err = f.GetPivotTables(\"Sheet1\")\n\tassert.Len(t, pivotTables, 6)\n\tassert.NoError(t, err)\n\n\t// Test add pivot table with invalid sheet name\n\tassert.Error(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet:1!A1:E31\",\n\t\tPivotTableRange: \"Sheet:1!G2:M34\",\n\t\tRows:            []PivotTableField{{Data: \"Year\"}},\n\t}), ErrSheetNameInvalid)\n\t// Test add pivot table with enable ClassicLayout and CompactData in the same time\n\tassert.Error(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"Sheet1!G2:M34\",\n\t\tCompactData:     true,\n\t\tClassicLayout:   true,\n\t}), ErrPivotTableClassicLayout)\n\t// Test delete pivot table with not exists worksheet\n\tassert.EqualError(t, f.DeletePivotTable(\"SheetN\", \"PivotTable1\"), \"sheet SheetN does not exist\")\n\t// Test delete pivot table with not exists pivot table name\n\tassert.EqualError(t, f.DeletePivotTable(\"Sheet1\", \"PivotTableN\"), \"table PivotTableN does not exist\")\n\t// Test adjust range with invalid range\n\t_, _, err = f.adjustRange(\"\")\n\tassert.Error(t, err, ErrParameterRequired)\n\t// Test adjust range with incorrect range\n\t_, _, err = f.adjustRange(\"sheet1!\")\n\tassert.EqualError(t, err, \"parameter is invalid\")\n\t// Test get table fields order with empty data range\n\t_, err = f.getTableFieldsOrder(&PivotTableOptions{})\n\tassert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`)\n\t// Test add pivot cache with empty data range\n\tassert.EqualError(t, f.addPivotCache(&PivotTableOptions{}), \"parameter 'DataRange' parsing error: parameter is required\")\n\t// Test add pivot table with empty options\n\tassert.EqualError(t, f.addPivotTable(0, 0, &PivotTableOptions{}), \"parameter 'PivotTableRange' parsing error: parameter is required\")\n\t// Test add pivot table with invalid data range\n\tassert.EqualError(t, f.addPivotTable(0, 0, &PivotTableOptions{}), \"parameter 'PivotTableRange' parsing error: parameter is required\")\n\t// Test add pivot fields with empty data range\n\tassert.EqualError(t, f.addPivotFields(nil, &PivotTableOptions{\n\t\tDataRange:       \"A1:E31\",\n\t\tPivotTableRange: \"Sheet1!U34:O2\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\"}},\n\t}), `parameter 'DataRange' parsing error: parameter is invalid`)\n\t// Test get pivot fields index with empty data range\n\t_, err = f.getPivotFieldsIndex([]PivotTableField{}, &PivotTableOptions{})\n\tassert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`)\n\t// Test add pivot table with unsupported charset content types.\n\tf = NewFile()\n\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A1\", &[]string{\"Month\", \"Year\", \"Type\", \"Sales\", \"Region\"}))\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\tPivotTableRange: \"Sheet1!G2:M34\",\n\t\tRows:            []PivotTableField{{Data: \"Year\"}},\n\t}), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\n\t// Test get pivot table without pivot table\n\tf = NewFile()\n\tpivotTables, err = f.GetPivotTables(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, pivotTables, 0)\n\t// Test get pivot table with not exists worksheet\n\t_, err = f.GetPivotTables(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get pivot table with unsupported charset worksheet relationships\n\tf.Pkg.Store(\"xl/worksheets/_rels/sheet1.xml.rels\", MacintoshCyrillicCharset)\n\t_, err = f.GetPivotTables(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\t// Test get pivot table with unsupported charset pivot cache definition\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestAddPivotTable1.xlsx\"))\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/pivotCache/pivotCacheDefinition1.xml\", MacintoshCyrillicCharset)\n\t_, err = f.GetPivotTables(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\t// Test get pivot table with unsupported charset pivot table relationships\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestAddPivotTable1.xlsx\"))\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/pivotTables/_rels/pivotTable1.xml.rels\", MacintoshCyrillicCharset)\n\t_, err = f.GetPivotTables(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\t// Test get pivot table with unsupported charset pivot table\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestAddPivotTable1.xlsx\"))\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/pivotTables/pivotTable1.xml\", MacintoshCyrillicCharset)\n\t_, err = f.GetPivotTables(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t_, err = f.getPivotTables()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\t// Test get pivot table with unsupported pivot table cache source type\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestAddPivotTable1.xlsx\"))\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/pivotCache/pivotCacheDefinition1.xml\", fmt.Appendf(nil, `<pivotCacheDefinition  xmlns=\"%s\"><cacheSource type=\"external\" connectionId=\"1\"/></pivotCacheDefinition>`, NameSpaceSpreadSheet.Value))\n\t_, err = f.GetPivotTables(\"Sheet1\")\n\tassert.Equal(t, err, newUnsupportedPivotCacheSourceType(\"external\"))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestPivotTableDataRange(t *testing.T) {\n\tf := NewFile()\n\t// Create table in a worksheet\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{\n\t\tName:  \"Table1\",\n\t\tRange: \"A1:D5\",\n\t}))\n\tfor row := 2; row < 6; row++ {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"A%d\", row), rand.Intn(10)))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"B%d\", row), rand.Intn(10)))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"C%d\", row), rand.Intn(10)))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"D%d\", row), rand.Intn(10)))\n\t}\n\t// Test add pivot table with table data range\n\topts := PivotTableOptions{\n\t\tDataRange:           \"Table1\",\n\t\tPivotTableRange:     \"Sheet1!G2:K7\",\n\t\tRows:                []PivotTableField{{Data: \"Column1\"}},\n\t\tColumns:             []PivotTableField{{Data: \"Column2\"}},\n\t\tRowGrandTotals:      true,\n\t\tColGrandTotals:      true,\n\t\tShowDrill:           true,\n\t\tShowRowHeaders:      true,\n\t\tShowColHeaders:      true,\n\t\tShowLastColumn:      true,\n\t\tShowError:           true,\n\t\tPivotTableStyleName: \"PivotStyleLight16\",\n\t}\n\tassert.NoError(t, f.AddPivotTable(&opts))\n\tassert.NoError(t, f.DeletePivotTable(\"Sheet1\", \"PivotTable1\"))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddPivotTable2.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\tassert.NoError(t, f.AddPivotTable(&opts))\n\n\t// Test delete pivot table with unsupported table relationships charset\n\tf.Pkg.Store(\"xl/tables/table1.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.DeletePivotTable(\"Sheet1\", \"PivotTable1\"), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test delete pivot table with unsupported worksheet relationships charset\n\tf.Relationships.Delete(\"xl/worksheets/_rels/sheet1.xml.rels\")\n\tf.Pkg.Store(\"xl/worksheets/_rels/sheet1.xml.rels\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.DeletePivotTable(\"Sheet1\", \"PivotTable1\"), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test delete pivot table without worksheet relationships\n\tf.Relationships.Delete(\"xl/worksheets/_rels/sheet1.xml.rels\")\n\tf.Pkg.Delete(\"xl/worksheets/_rels/sheet1.xml.rels\")\n\tassert.EqualError(t, f.DeletePivotTable(\"Sheet1\", \"PivotTable1\"), \"table PivotTable1 does not exist\")\n\n\tt.Run(\"data_range_with_empty_column\", func(t *testing.T) {\n\t\t// Test add pivot table with data range doesn't organized as a list with labeled columns\n\t\tf := NewFile()\n\t\t// Create some data in a sheet\n\t\tmonth := []string{\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"}\n\t\ttypes := []string{\"Meat\", \"Dairy\", \"Beverages\", \"Produce\"}\n\t\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A1\", &[]string{\"Month\", \"\", \"Type\"}))\n\t\tfor row := 2; row < 32; row++ {\n\t\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"A%d\", row), month[rand.Intn(12)]))\n\t\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", fmt.Sprintf(\"C%d\", row), types[rand.Intn(4)]))\n\t\t}\n\t\tassert.Equal(t, newPivotTableDataRangeError(\"parameter is invalid\"), f.AddPivotTable(&PivotTableOptions{\n\t\t\tDataRange:       \"Sheet1!A1:E31\",\n\t\t\tPivotTableRange: \"Sheet1!G2:M34\",\n\t\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}},\n\t\t\tData:            []PivotTableField{{Data: \"Type\"}},\n\t\t}))\n\t})\n}\n\nfunc TestParseFormatPivotTableSet(t *testing.T) {\n\tf := NewFile()\n\t// Create table in a worksheet\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{\n\t\tName:  \"Table1\",\n\t\tRange: \"A1:D5\",\n\t}))\n\t// Test parse format pivot table options with unsupported table relationships charset\n\tf.Pkg.Store(\"xl/tables/table1.xml\", MacintoshCyrillicCharset)\n\t_, _, err := f.parseFormatPivotTableSet(&PivotTableOptions{\n\t\tDataRange:       \"Table1\",\n\t\tPivotTableRange: \"Sheet1!G2:K7\",\n\t\tRows:            []PivotTableField{{Data: \"Column1\"}},\n\t})\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestAddPivotRowFields(t *testing.T) {\n\tf := NewFile()\n\t// Test invalid data range\n\tassert.EqualError(t, f.addPivotRowFields(&xlsxPivotTableDefinition{}, &PivotTableOptions{\n\t\tDataRange: \"Sheet1!A1:A1\",\n\t}), `parameter 'DataRange' parsing error: parameter is invalid`)\n}\n\nfunc TestAddPivotPageFields(t *testing.T) {\n\tf := NewFile()\n\t// Test invalid data range\n\tassert.EqualError(t, f.addPivotPageFields(&xlsxPivotTableDefinition{}, &PivotTableOptions{\n\t\tDataRange: \"Sheet1!A1:A1\",\n\t}), `parameter 'DataRange' parsing error: parameter is invalid`)\n}\n\nfunc TestAddPivotDataFields(t *testing.T) {\n\tf := NewFile()\n\t// Test invalid data range\n\tassert.EqualError(t, f.addPivotDataFields(&xlsxPivotTableDefinition{}, &PivotTableOptions{\n\t\tDataRange: \"Sheet1!A1:A1\",\n\t}), `parameter 'DataRange' parsing error: parameter is invalid`)\n}\n\nfunc TestAddPivotColFields(t *testing.T) {\n\tf := NewFile()\n\t// Test invalid data range\n\tassert.EqualError(t, f.addPivotColFields(&xlsxPivotTableDefinition{}, &PivotTableOptions{\n\t\tDataRange: \"Sheet1!A1:A1\",\n\t\tColumns:   []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t}), `parameter 'DataRange' parsing error: parameter is invalid`)\n}\n\nfunc TestGetPivotFieldsOrder(t *testing.T) {\n\tf := NewFile()\n\t// Test get table fields order with not exist worksheet\n\t_, err := f.getTableFieldsOrder(&PivotTableOptions{DataRange: \"SheetN!A1:E31\"})\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Create table in a worksheet\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{\n\t\tName:  \"Table1\",\n\t\tRange: \"A1:D5\",\n\t}))\n\t// Test get table fields order with unsupported table relationships charset\n\tf.Pkg.Store(\"xl/tables/table1.xml\", MacintoshCyrillicCharset)\n\t_, err = f.getTableFieldsOrder(&PivotTableOptions{DataRange: \"Table\"})\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestGetPivotTableFieldName(t *testing.T) {\n\tf := NewFile()\n\tassert.Empty(t, f.getPivotTableFieldName(\"-\", []PivotTableField{}))\n}\n\nfunc TestGetPivotTableFieldOptions(t *testing.T) {\n\tf := NewFile()\n\t_, ok := f.getPivotTableFieldOptions(\"-\", []PivotTableField{})\n\tassert.False(t, ok)\n}\n\nfunc TestGenPivotCacheDefinitionID(t *testing.T) {\n\tf := NewFile()\n\t// Test generate pivot table cache definition ID with unsupported charset\n\tf.Pkg.Store(\"xl/pivotCache/pivotCacheDefinition1.xml\", MacintoshCyrillicCharset)\n\tassert.Equal(t, 1, f.genPivotCacheDefinitionID())\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestDeleteWorkbookPivotCache(t *testing.T) {\n\tf := NewFile()\n\t// Test delete workbook pivot table cache with unsupported workbook charset\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.deleteWorkbookPivotCache(PivotTableOptions{pivotCacheXML: \"pivotCache/pivotCacheDefinition1.xml\"}), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test delete workbook pivot table cache with unsupported workbook relationships charset\n\tf.Relationships.Delete(\"xl/_rels/workbook.xml.rels\")\n\tf.Pkg.Store(\"xl/_rels/workbook.xml.rels\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.deleteWorkbookPivotCache(PivotTableOptions{pivotCacheXML: \"pivotCache/pivotCacheDefinition1.xml\"}), \"XML syntax error on line 1: invalid UTF-8\")\n}\n"
  },
  {
    "path": "rows.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"io\"\n\t\"math\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/tiendc/go-deepcopy\"\n)\n\n// duplicateHelperFunc defines functions to duplicate helper.\nvar duplicateHelperFunc = [3]func(*File, *xlsxWorksheet, string, int, int) error{\n\tfunc(f *File, ws *xlsxWorksheet, sheet string, row, row2 int) error {\n\t\treturn f.duplicateConditionalFormat(ws, sheet, row, row2)\n\t},\n\tfunc(f *File, ws *xlsxWorksheet, sheet string, row, row2 int) error {\n\t\treturn f.duplicateDataValidations(ws, sheet, row, row2)\n\t},\n\tfunc(f *File, ws *xlsxWorksheet, sheet string, row, row2 int) error {\n\t\treturn f.duplicateMergeCells(ws, sheet, row, row2)\n\t},\n}\n\n// GetRows return all the rows in a sheet by given worksheet name, returned as\n// a two-dimensional array, where the value of the cell is converted to the\n// string type. If the cell format can be applied to the value of the cell,\n// the applied value will be used, otherwise the original value will be used.\n// GetRows fetched the rows with value or formula cells, the continually blank\n// cells in the tail of each row will be skipped, so the length of each row\n// may be inconsistent.\n//\n// For example, get and traverse the value of all cells by rows on a worksheet\n// named 'Sheet1':\n//\n//\trows, err := f.GetRows(\"Sheet1\")\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\tfor _, row := range rows {\n//\t    for _, colCell := range row {\n//\t        fmt.Print(colCell, \"\\t\")\n//\t    }\n//\t    fmt.Println()\n//\t}\nfunc (f *File) GetRows(sheet string, opts ...Options) ([][]string, error) {\n\trows, err := f.Rows(sheet)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresults, cur, maxVal := make([][]string, 0, 64), 0, 0\n\tfor rows.Next() {\n\t\tcur++\n\t\trow, err := rows.Columns(opts...)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tif len(row) > 0 {\n\t\t\tif emptyRows := cur - maxVal - 1; emptyRows > 0 {\n\t\t\t\tresults = append(results, make([][]string, emptyRows)...)\n\t\t\t}\n\t\t\tresults = append(results, row)\n\t\t\tmaxVal = cur\n\t\t}\n\t}\n\treturn results[:maxVal], rows.Close()\n}\n\n// Rows defines an iterator to a sheet.\ntype Rows struct {\n\terr                     error\n\tcurRow, seekRow         int\n\tneedClose, rawCellValue bool\n\tsheet                   string\n\tf                       *File\n\ttempFile                *os.File\n\tsst                     *xlsxSST\n\tdecoder                 *xml.Decoder\n\ttoken                   xml.Token\n\tcurRowOpts, seekRowOpts RowOpts\n}\n\n// Next will return true if it finds the next row element.\nfunc (rows *Rows) Next() bool {\n\trows.seekRow++\n\tif rows.curRow >= rows.seekRow {\n\t\trows.curRowOpts = rows.seekRowOpts\n\t\treturn true\n\t}\n\tfor {\n\t\ttoken, _ := rows.decoder.Token()\n\t\tif token == nil {\n\t\t\treturn false\n\t\t}\n\t\tswitch xmlElement := token.(type) {\n\t\tcase xml.StartElement:\n\t\t\tif xmlElement.Name.Local == \"row\" {\n\t\t\t\trows.curRow++\n\t\t\t\tif rowNum, _ := attrValToInt(\"r\", xmlElement.Attr); rowNum != 0 {\n\t\t\t\t\trows.curRow = rowNum\n\t\t\t\t}\n\t\t\t\trows.token = token\n\t\t\t\trows.curRowOpts = extractRowOpts(xmlElement.Attr)\n\t\t\t\treturn true\n\t\t\t}\n\t\tcase xml.EndElement:\n\t\t\tif xmlElement.Name.Local == \"sheetData\" {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n}\n\n// GetRowOpts will return the RowOpts of the current row.\nfunc (rows *Rows) GetRowOpts() RowOpts {\n\treturn rows.curRowOpts\n}\n\n// Error will return the error when the error occurs.\nfunc (rows *Rows) Error() error {\n\treturn rows.err\n}\n\n// Close closes the open worksheet XML file in the system temporary\n// directory.\nfunc (rows *Rows) Close() error {\n\ttempFile := rows.tempFile\n\trows.tempFile = nil\n\tif tempFile != nil {\n\t\treturn tempFile.Close()\n\t}\n\treturn nil\n}\n\n// Columns return the current row's column values. This fetches the worksheet\n// data as a stream, returns each cell in a row as is, and will not skip empty\n// rows in the tail of the worksheet.\nfunc (rows *Rows) Columns(opts ...Options) ([]string, error) {\n\tif rows.curRow > rows.seekRow {\n\t\treturn nil, nil\n\t}\n\tvar rowIterator rowXMLIterator\n\tvar token xml.Token\n\trows.rawCellValue = rows.f.getOptions(opts...).RawCellValue\n\tif rows.sst, rowIterator.err = rows.f.sharedStringsReader(); rowIterator.err != nil {\n\t\treturn rowIterator.cells, rowIterator.err\n\t}\n\tfor {\n\t\tif rows.token != nil {\n\t\t\ttoken = rows.token\n\t\t} else if token, _ = rows.decoder.Token(); token == nil {\n\t\t\tbreak\n\t\t}\n\t\tswitch xmlElement := token.(type) {\n\t\tcase xml.StartElement:\n\t\t\trowIterator.inElement = xmlElement.Name.Local\n\t\t\tif rowIterator.inElement == \"row\" {\n\t\t\t\trowNum := 0\n\t\t\t\tif rowNum, rowIterator.err = attrValToInt(\"r\", xmlElement.Attr); rowNum != 0 {\n\t\t\t\t\trows.curRow = rowNum\n\t\t\t\t} else if rows.token == nil {\n\t\t\t\t\trows.curRow++\n\t\t\t\t}\n\t\t\t\trows.token = token\n\t\t\t\trows.seekRowOpts = extractRowOpts(xmlElement.Attr)\n\t\t\t\tif rows.curRow > rows.seekRow {\n\t\t\t\t\trows.token = nil\n\t\t\t\t\treturn rowIterator.cells, rowIterator.err\n\t\t\t\t}\n\t\t\t}\n\t\t\tif rows.rowXMLHandler(&rowIterator, &xmlElement, rows.rawCellValue); rowIterator.err != nil {\n\t\t\t\trows.token = nil\n\t\t\t\treturn rowIterator.cells, rowIterator.err\n\t\t\t}\n\t\t\trows.token = nil\n\t\tcase xml.EndElement:\n\t\t\tif xmlElement.Name.Local == \"sheetData\" {\n\t\t\t\treturn rowIterator.cells, rowIterator.err\n\t\t\t}\n\t\t}\n\t}\n\treturn rowIterator.cells, rowIterator.err\n}\n\n// extractRowOpts extract row element attributes.\nfunc extractRowOpts(attrs []xml.Attr) RowOpts {\n\trowOpts := RowOpts{Height: defaultRowHeight}\n\tif styleID, err := attrValToInt(\"s\", attrs); err == nil && styleID > 0 && styleID < MaxCellStyles {\n\t\trowOpts.StyleID = styleID\n\t}\n\tif hidden, err := attrValToBool(\"hidden\", attrs); err == nil {\n\t\trowOpts.Hidden = hidden\n\t}\n\tif height, err := attrValToFloat(\"ht\", attrs); err == nil {\n\t\trowOpts.Height = height\n\t}\n\treturn rowOpts\n}\n\n// appendSpace append blank characters to slice by given length and source slice.\nfunc appendSpace(l int, s []string) []string {\n\tfor i := 1; i < l; i++ {\n\t\ts = append(s, \"\")\n\t}\n\treturn s\n}\n\n// rowXMLIterator defined runtime use field for the worksheet row SAX parser.\ntype rowXMLIterator struct {\n\terr              error\n\tinElement        string\n\tcellCol, cellRow int\n\tcells            []string\n}\n\n// rowXMLHandler parse the row XML element of the worksheet.\nfunc (rows *Rows) rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.StartElement, raw bool) {\n\tif rowIterator.inElement == \"c\" {\n\t\trowIterator.cellCol++\n\t\tcolCell := xlsxC{}\n\t\t_ = colCell.cellXMLHandler(rows.decoder, xmlElement)\n\t\tif colCell.R != \"\" {\n\t\t\tif rowIterator.cellCol, _, rowIterator.err = CellNameToCoordinates(colCell.R); rowIterator.err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tblank := rowIterator.cellCol - len(rowIterator.cells)\n\t\tif val, _ := colCell.getValueFrom(rows.f, rows.sst, raw); val != \"\" || colCell.F != nil {\n\t\t\trowIterator.cells = append(appendSpace(blank, rowIterator.cells), val)\n\t\t}\n\t}\n}\n\n// cellXMLAttrHandler parse the cell XML element attributes of the worksheet.\nfunc (cell *xlsxC) cellXMLAttrHandler(start *xml.StartElement) error {\n\tfor _, attr := range start.Attr {\n\t\tswitch attr.Name.Local {\n\t\tcase \"r\":\n\t\t\tcell.R = attr.Value\n\t\tcase \"s\":\n\t\t\tval, err := strconv.ParseInt(attr.Value, 10, 64)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif math.MinInt <= val && val <= math.MaxInt {\n\t\t\t\tcell.S = int(val)\n\t\t\t}\n\t\tcase \"t\":\n\t\t\tcell.T = attr.Value\n\t\tdefault:\n\t\t}\n\t}\n\treturn nil\n}\n\n// cellXMLHandler parse the cell XML element of the worksheet.\nfunc (cell *xlsxC) cellXMLHandler(decoder *xml.Decoder, start *xml.StartElement) error {\n\tcell.XMLName = start.Name\n\terr := cell.cellXMLAttrHandler(start)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor {\n\t\ttok, err := decoder.Token()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar se xml.StartElement\n\t\tswitch el := tok.(type) {\n\t\tcase xml.StartElement:\n\t\t\tse = el\n\t\t\tswitch se.Name.Local {\n\t\t\tcase \"v\":\n\t\t\t\terr = decoder.DecodeElement(&cell.V, &se)\n\t\t\tcase \"f\":\n\t\t\t\terr = decoder.DecodeElement(&cell.F, &se)\n\t\t\tcase \"is\":\n\t\t\t\terr = decoder.DecodeElement(&cell.IS, &se)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase xml.EndElement:\n\t\t\tif el == start.End() {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Rows returns a rows iterator, used for streaming reading data for a\n// worksheet with a large data. This function is concurrency safe. For\n// example:\n//\n//\trows, err := f.Rows(\"Sheet1\")\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\tfor rows.Next() {\n//\t    row, err := rows.Columns()\n//\t    if err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t    for _, colCell := range row {\n//\t        fmt.Print(colCell, \"\\t\")\n//\t    }\n//\t    fmt.Println()\n//\t}\n//\tif err = rows.Close(); err != nil {\n//\t    fmt.Println(err)\n//\t}\nfunc (f *File) Rows(sheet string) (*Rows, error) {\n\tif err := checkSheetName(sheet); err != nil {\n\t\treturn nil, err\n\t}\n\tname, ok := f.getSheetXMLPath(sheet)\n\tif !ok {\n\t\treturn nil, ErrSheetNotExist{sheet}\n\t}\n\tif worksheet, ok := f.Sheet.Load(name); ok && worksheet != nil {\n\t\tws := worksheet.(*xlsxWorksheet)\n\t\tws.mu.Lock()\n\t\tdefer ws.mu.Unlock()\n\t\t// Flush data\n\t\toutput, _ := xml.Marshal(ws)\n\t\tf.saveFileList(name, f.replaceNameSpaceBytes(name, output))\n\t}\n\tvar err error\n\trows := Rows{f: f, sheet: name}\n\trows.needClose, rows.decoder, rows.tempFile, err = f.xmlDecoder(name)\n\treturn &rows, err\n}\n\n// getFromStringItem build shared string item offset list from system temporary\n// file at one time, and return value by given to string index.\nfunc (f *File) getFromStringItem(index int) string {\n\tif f.sharedStringTemp != nil {\n\t\tif len(f.sharedStringItem) <= index {\n\t\t\treturn strconv.Itoa(index)\n\t\t}\n\t\toffsetRange := f.sharedStringItem[index]\n\t\tif len(offsetRange) != 2 || offsetRange[0] > offsetRange[1] {\n\t\t\treturn strconv.Itoa(index)\n\t\t}\n\t\tbuf := make([]byte, offsetRange[1]-offsetRange[0])\n\t\tif _, err := f.sharedStringTemp.ReadAt(buf, int64(offsetRange[0])); err != nil {\n\t\t\treturn strconv.Itoa(index)\n\t\t}\n\t\treturn string(buf)\n\t}\n\tneedClose, decoder, tempFile, err := f.xmlDecoder(defaultXMLPathSharedStrings)\n\tif needClose && err == nil {\n\t\tdefer func() {\n\t\t\terr = tempFile.Close()\n\t\t}()\n\t}\n\tf.sharedStringItem = [][]uint{}\n\tf.sharedStringTemp, _ = os.CreateTemp(f.options.TmpDir, \"excelize-\")\n\tf.tempFiles.Store(defaultTempFileSST, f.sharedStringTemp.Name())\n\tvar (\n\t\tinElement string\n\t\ti, offset uint\n\t)\n\tfor {\n\t\ttoken, _ := decoder.Token()\n\t\tif token == nil {\n\t\t\tbreak\n\t\t}\n\t\tswitch xmlElement := token.(type) {\n\t\tcase xml.StartElement:\n\t\t\tinElement = xmlElement.Name.Local\n\t\t\tif inElement == \"si\" {\n\t\t\t\tsi := xlsxSI{}\n\t\t\t\t_ = decoder.DecodeElement(&si, &xmlElement)\n\n\t\t\t\tstartIdx := offset\n\t\t\t\tn, _ := f.sharedStringTemp.WriteString(si.String())\n\t\t\t\toffset += uint(n)\n\t\t\t\tf.sharedStringItem = append(f.sharedStringItem, []uint{startIdx, offset})\n\t\t\t\ti++\n\t\t\t}\n\t\t}\n\t}\n\treturn f.getFromStringItem(index)\n}\n\n// xmlDecoder creates XML decoder by given path in the zip from memory data\n// or system temporary file.\nfunc (f *File) xmlDecoder(name string) (bool, *xml.Decoder, *os.File, error) {\n\tvar (\n\t\tcontent  []byte\n\t\terr      error\n\t\ttempFile *os.File\n\t)\n\tif content = f.readXML(name); len(content) > 0 {\n\t\treturn false, f.xmlNewDecoder(bytes.NewReader(content)), tempFile, err\n\t}\n\ttempFile, err = f.readTemp(name)\n\treturn true, f.xmlNewDecoder(tempFile), tempFile, err\n}\n\n// SetRowHeight provides a function to set the height of a single row. If the\n// value of height is 0, will hide the specified row, if the value of height is\n// -1, will unset the custom row height. For example, set the height of the\n// first row in Sheet1:\n//\n//\terr := f.SetRowHeight(\"Sheet1\", 1, 50)\nfunc (f *File) SetRowHeight(sheet string, row int, height float64) error {\n\tif row < 1 {\n\t\treturn newInvalidRowNumberError(row)\n\t}\n\tif height > MaxRowHeight {\n\t\treturn ErrMaxRowHeight\n\t}\n\tif height < -1 {\n\t\treturn ErrParameterInvalid\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tws.prepareSheetXML(0, row)\n\n\trowIdx := row - 1\n\tif height == -1 {\n\t\tws.SheetData.Row[rowIdx].Ht = nil\n\t\tws.SheetData.Row[rowIdx].CustomHeight = false\n\t\treturn err\n\t}\n\tws.SheetData.Row[rowIdx].Ht = float64Ptr(height)\n\tws.SheetData.Row[rowIdx].CustomHeight = true\n\treturn err\n}\n\n// getRowHeight provides a function to get row height in pixels by given sheet\n// name and row number.\nfunc (f *File) getRowHeight(sheet string, row int) int {\n\tws, _ := f.workSheetReader(sheet)\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\theight := -1.0\n\tfor i := range ws.SheetData.Row {\n\t\tv := &ws.SheetData.Row[i]\n\t\tif v.R == row && v.Ht != nil {\n\t\t\theight = *v.Ht\n\t\t\tbreak\n\t\t}\n\t}\n\tif height != -1.0 {\n\t\treturn int(convertRowHeightToPixels(height))\n\t}\n\tif ws.SheetFormatPr != nil && ws.SheetFormatPr.DefaultRowHeight > 0 {\n\t\treturn int(convertRowHeightToPixels(ws.SheetFormatPr.DefaultRowHeight))\n\t}\n\t// Optimization for when the row heights haven't changed.\n\treturn int(defaultRowHeightPixels)\n}\n\n// GetRowHeight provides a function to get row height by given worksheet name\n// and row number. For example, get the height of the first row in Sheet1:\n//\n//\theight, err := f.GetRowHeight(\"Sheet1\", 1)\nfunc (f *File) GetRowHeight(sheet string, row int) (float64, error) {\n\tif row < 1 {\n\t\treturn defaultRowHeight, newInvalidRowNumberError(row)\n\t}\n\tht := defaultRowHeight\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn ht, err\n\t}\n\tif ws.SheetFormatPr != nil && ws.SheetFormatPr.CustomHeight {\n\t\tht = ws.SheetFormatPr.DefaultRowHeight\n\t}\n\tif row > len(ws.SheetData.Row) {\n\t\treturn ht, nil // it will be better to use 0, but we take care with BC\n\t}\n\tfor _, v := range ws.SheetData.Row {\n\t\tif v.R == row && v.Ht != nil {\n\t\t\treturn *v.Ht, nil\n\t\t}\n\t}\n\t// Optimization for when the row heights haven't changed.\n\treturn ht, nil\n}\n\n// sharedStringsReader provides a function to get the pointer to the structure\n// after deserialization of xl/sharedStrings.xml.\nfunc (f *File) sharedStringsReader() (*xlsxSST, error) {\n\tvar err error\n\tf.mu.Lock()\n\tdefer f.mu.Unlock()\n\trelPath := f.getWorkbookRelsPath()\n\tif f.SharedStrings == nil {\n\t\tvar sharedStrings xlsxSST\n\t\tss := f.readXML(defaultXMLPathSharedStrings)\n\t\tif err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).\n\t\t\tDecode(&sharedStrings); err != nil && err != io.EOF {\n\t\t\treturn f.SharedStrings, err\n\t\t}\n\t\tif sharedStrings.Count == 0 {\n\t\t\tsharedStrings.Count = len(sharedStrings.SI)\n\t\t}\n\t\tif sharedStrings.UniqueCount == 0 {\n\t\t\tsharedStrings.UniqueCount = sharedStrings.Count\n\t\t}\n\t\tf.SharedStrings = &sharedStrings\n\t\tfor i := range sharedStrings.SI {\n\t\t\tif sharedStrings.SI[i].T != nil {\n\t\t\t\tf.sharedStringsMap[sharedStrings.SI[i].T.Val] = i\n\t\t\t}\n\t\t}\n\t\tif err = f.addContentTypePart(0, \"sharedStrings\"); err != nil {\n\t\t\treturn f.SharedStrings, err\n\t\t}\n\t\trels, err := f.relsReader(relPath)\n\t\tif err != nil {\n\t\t\treturn f.SharedStrings, err\n\t\t}\n\t\tfor _, rel := range rels.Relationships {\n\t\t\tif rel.Target == \"/xl/sharedStrings.xml\" {\n\t\t\t\treturn f.SharedStrings, nil\n\t\t\t}\n\t\t}\n\t\t// Update workbook.xml.rels\n\t\tf.addRels(relPath, SourceRelationshipSharedStrings, \"/xl/sharedStrings.xml\", \"\")\n\t}\n\n\treturn f.SharedStrings, nil\n}\n\n// SetRowVisible provides a function to set visible of a single row by given\n// worksheet name and Excel row number. For example, hide row 2 in Sheet1:\n//\n//\terr := f.SetRowVisible(\"Sheet1\", 2, false)\nfunc (f *File) SetRowVisible(sheet string, row int, visible bool) error {\n\tif row < 1 {\n\t\treturn newInvalidRowNumberError(row)\n\t}\n\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.prepareSheetXML(0, row)\n\tws.SheetData.Row[row-1].Hidden = !visible\n\treturn nil\n}\n\n// GetRowVisible provides a function to get visible of a single row by given\n// worksheet name and Excel row number. For example, get visible state of row\n// 2 in Sheet1:\n//\n//\tvisible, err := f.GetRowVisible(\"Sheet1\", 2)\nfunc (f *File) GetRowVisible(sheet string, row int) (bool, error) {\n\tif row < 1 {\n\t\treturn false, newInvalidRowNumberError(row)\n\t}\n\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif row > len(ws.SheetData.Row) {\n\t\treturn false, nil\n\t}\n\treturn !ws.SheetData.Row[row-1].Hidden, nil\n}\n\n// SetRowOutlineLevel provides a function to set outline level number of a\n// single row by given worksheet name and row number. The range of 'level'\n// parameter value from 1 to 7. For example, outline row 2 in Sheet1 to level 1:\n//\n//\terr := f.SetRowOutlineLevel(\"Sheet1\", 2, 1)\nfunc (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) error {\n\tif row < 1 {\n\t\treturn newInvalidRowNumberError(row)\n\t}\n\tif level > 7 || level < 1 {\n\t\treturn ErrOutlineLevel\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.prepareSheetXML(0, row)\n\tws.SheetData.Row[row-1].OutlineLevel = level\n\treturn nil\n}\n\n// GetRowOutlineLevel provides a function to get outline level number of a\n// single row by given worksheet name and Excel row number. For example, get\n// outline number of row 2 in Sheet1:\n//\n//\tlevel, err := f.GetRowOutlineLevel(\"Sheet1\", 2)\nfunc (f *File) GetRowOutlineLevel(sheet string, row int) (uint8, error) {\n\tif row < 1 {\n\t\treturn 0, newInvalidRowNumberError(row)\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif row > len(ws.SheetData.Row) {\n\t\treturn 0, nil\n\t}\n\treturn ws.SheetData.Row[row-1].OutlineLevel, nil\n}\n\n// RemoveRow provides a function to remove single row by given worksheet name\n// and Excel row number. For example, remove row 3 in Sheet1:\n//\n//\terr := f.RemoveRow(\"Sheet1\", 3)\n//\n// Use this method with caution, which will affect changes in references such\n// as formulas, charts, and so on. If there is any referenced value of the\n// worksheet, it will cause a file error when you open it. The excelize only\n// partially updates these references currently.\nfunc (f *File) RemoveRow(sheet string, row int) error {\n\tif row < 1 {\n\t\treturn newInvalidRowNumberError(row)\n\t}\n\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.formulaSI.Clear()\n\tif row > len(ws.SheetData.Row) {\n\t\treturn f.adjustHelper(sheet, rows, row, -1)\n\t}\n\tfor rowIdx := range ws.SheetData.Row {\n\t\tv := &ws.SheetData.Row[rowIdx]\n\t\tif v.R == row {\n\t\t\tfor _, c := range v.C {\n\t\t\t\tif err := f.removeFormula(&c, ws, sheet); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tkeep := 0\n\tfor rowIdx := range ws.SheetData.Row {\n\t\tv := &ws.SheetData.Row[rowIdx]\n\t\tif v.R != row {\n\t\t\tws.SheetData.Row[keep] = *v\n\t\t\tkeep++\n\t\t}\n\t}\n\tws.SheetData.Row = ws.SheetData.Row[:keep]\n\treturn f.adjustHelper(sheet, rows, row, -1)\n}\n\n// InsertRows provides a function to insert new rows after the given Excel row\n// number starting from 1 and number of rows. For example, create two rows\n// before row 3 in Sheet1:\n//\n//\terr := f.InsertRows(\"Sheet1\", 3, 2)\n//\n// Use this method with caution, which will affect changes in references such\n// as formulas, charts, and so on. If there is any referenced value of the\n// worksheet, it will cause a file error when you open it. The excelize only\n// partially updates these references currently.\nfunc (f *File) InsertRows(sheet string, row, n int) error {\n\tif row < 1 {\n\t\treturn newInvalidRowNumberError(row)\n\t}\n\tif row >= TotalRows || n >= TotalRows {\n\t\treturn ErrMaxRows\n\t}\n\tif n < 1 {\n\t\treturn ErrParameterInvalid\n\t}\n\treturn f.adjustHelper(sheet, rows, row, n)\n}\n\n// DuplicateRow inserts a copy of specified row (by its Excel row number) below\n//\n//\terr := f.DuplicateRow(\"Sheet1\", 2)\n//\n// Use this method with caution, which will affect changes in references such\n// as formulas, charts, and so on. If there is any referenced value of the\n// worksheet, it will cause a file error when you open it. The excelize only\n// partially updates these references currently.\nfunc (f *File) DuplicateRow(sheet string, row int) error {\n\treturn f.DuplicateRowTo(sheet, row, row+1)\n}\n\n// DuplicateRowTo inserts a copy of specified row by it Excel number\n// to specified row position moving down exists rows after target position\n//\n//\terr := f.DuplicateRowTo(\"Sheet1\", 2, 7)\n//\n// Use this method with caution, which will affect changes in references such\n// as formulas, charts, and so on. If there is any referenced value of the\n// worksheet, it will cause a file error when you open it. The excelize only\n// partially updates these references currently.\nfunc (f *File) DuplicateRowTo(sheet string, row, row2 int) error {\n\tif row < 1 {\n\t\treturn newInvalidRowNumberError(row)\n\t}\n\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif row2 < 1 || row == row2 {\n\t\treturn err\n\t}\n\n\tvar ok bool\n\tvar rowCopy xlsxRow\n\n\tfor i, r := range ws.SheetData.Row {\n\t\tif r.R == row {\n\t\t\terr = deepcopy.Copy(&rowCopy, ws.SheetData.Row[i])\n\t\t\tok = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif err := f.adjustHelper(sheet, rows, row2, 1); err != nil {\n\t\treturn err\n\t}\n\n\tif !ok {\n\t\treturn err\n\t}\n\n\tidx2 := -1\n\tfor i, r := range ws.SheetData.Row {\n\t\tif r.R == row2 {\n\t\t\tidx2 = i\n\t\t\tbreak\n\t\t}\n\t}\n\trowCopy.C = append(make([]xlsxC, 0, len(rowCopy.C)), rowCopy.C...)\n\trowCopy.adjustSingleRowDimensions(row2 - row)\n\t_ = f.adjustSingleRowFormulas(sheet, sheet, &rowCopy, row, row2-row, true)\n\n\tif idx2 != -1 {\n\t\tws.SheetData.Row[idx2] = rowCopy\n\t} else {\n\t\tws.SheetData.Row = append(ws.SheetData.Row, rowCopy)\n\t}\n\tfor _, fn := range duplicateHelperFunc {\n\t\tif err := fn(f, ws, sheet, row, row2); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn err\n}\n\n// duplicateSQRefHelper provides a function to adjust conditional formatting and\n// data validations cell reference when duplicate rows.\nfunc duplicateSQRefHelper(row, row2 int, ref string) (string, error) {\n\tif !strings.Contains(ref, \":\") {\n\t\tref += \":\" + ref\n\t}\n\tabs := strings.Contains(ref, \"$\")\n\tcoordinates, err := rangeRefToCoordinates(ref)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tx1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]\n\tif y1 == y2 && y1 == row {\n\t\tif ref, err = coordinatesToRangeRef([]int{x1, row2, x2, row2}, abs); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn ref, err\n\t}\n\treturn \"\", err\n}\n\n// duplicateConditionalFormat create conditional formatting for the destination\n// row if there are conditional formats in the copied row.\nfunc (f *File) duplicateConditionalFormat(ws *xlsxWorksheet, sheet string, row, row2 int) error {\n\tvar cfs []*xlsxConditionalFormatting\n\tfor _, cf := range ws.ConditionalFormatting {\n\t\tif cf != nil {\n\t\t\tvar SQRef []string\n\t\t\tfor _, ref := range strings.Split(cf.SQRef, \" \") {\n\t\t\t\tcoordinates, err := duplicateSQRefHelper(row, row2, ref)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif coordinates != \"\" {\n\t\t\t\t\tSQRef = append(SQRef, coordinates)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(SQRef) > 0 {\n\t\t\t\tvar cfCopy xlsxConditionalFormatting\n\t\t\t\t_ = deepcopy.Copy(&cfCopy, *cf)\n\t\t\t\tcfCopy.SQRef = strings.Join(SQRef, \" \")\n\t\t\t\tcfs = append(cfs, &cfCopy)\n\t\t\t}\n\t\t}\n\t}\n\tws.ConditionalFormatting = append(ws.ConditionalFormatting, cfs...)\n\treturn nil\n}\n\n// duplicateDataValidations create data validations for the destination row if\n// there are data validation rules in the copied row.\nfunc (f *File) duplicateDataValidations(ws *xlsxWorksheet, sheet string, row, row2 int) error {\n\tif ws.DataValidations == nil {\n\t\treturn nil\n\t}\n\tvar dvs []*xlsxDataValidation\n\tfor _, dv := range ws.DataValidations.DataValidation {\n\t\tif dv != nil {\n\t\t\tvar SQRef []string\n\t\t\tfor _, ref := range strings.Split(dv.Sqref, \" \") {\n\t\t\t\tcoordinates, err := duplicateSQRefHelper(row, row2, ref)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif coordinates != \"\" {\n\t\t\t\t\tSQRef = append(SQRef, coordinates)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(SQRef) > 0 {\n\t\t\t\tvar dvCopy xlsxDataValidation\n\t\t\t\t_ = deepcopy.Copy(&dvCopy, *dv)\n\t\t\t\tdvCopy.Sqref = strings.Join(SQRef, \" \")\n\t\t\t\tdvs = append(dvs, &dvCopy)\n\t\t\t}\n\t\t}\n\t}\n\tws.DataValidations.DataValidation = append(ws.DataValidations.DataValidation, dvs...)\n\treturn nil\n}\n\n// duplicateMergeCells merge cells in the destination row if there are single\n// row merged cells in the copied row.\nfunc (f *File) duplicateMergeCells(ws *xlsxWorksheet, sheet string, row, row2 int) error {\n\tif ws.MergeCells == nil {\n\t\treturn nil\n\t}\n\tif row > row2 {\n\t\trow++\n\t}\n\tfor _, rng := range ws.MergeCells.Cells {\n\t\tcoordinates, err := rangeRefToCoordinates(rng.Ref)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif coordinates[1] < row2 && row2 < coordinates[3] {\n\t\t\treturn nil\n\t\t}\n\t}\n\tfor i := 0; i < len(ws.MergeCells.Cells); i++ {\n\t\tmergedCells := ws.MergeCells.Cells[i]\n\t\tcoordinates, _ := rangeRefToCoordinates(mergedCells.Ref)\n\t\tx1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]\n\t\tif y1 == y2 && y1 == row {\n\t\t\tfrom, _ := CoordinatesToCellName(x1, row2)\n\t\t\tto, _ := CoordinatesToCellName(x2, row2)\n\t\t\tif err := f.MergeCell(sheet, from, to); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// checkRow provides a function to check and fill each column element for all\n// rows and make that is continuous in a worksheet of XML. For example:\n//\n//\t<row r=\"15\">\n//\t    <c r=\"A15\" s=\"2\" />\n//\t    <c r=\"B15\" s=\"2\" />\n//\t    <c r=\"F15\" s=\"1\" />\n//\t    <c r=\"G15\" s=\"1\" />\n//\t</row>\n//\n// in this case, we should to change it to\n//\n//\t<row r=\"15\">\n//\t    <c r=\"A15\" s=\"2\" />\n//\t    <c r=\"B15\" s=\"2\" />\n//\t    <c r=\"C15\" s=\"2\" />\n//\t    <c r=\"D15\" s=\"2\" />\n//\t    <c r=\"E15\" s=\"2\" />\n//\t    <c r=\"F15\" s=\"1\" />\n//\t    <c r=\"G15\" s=\"1\" />\n//\t</row>\n//\n// Notice: this method could be very slow for large spreadsheets (more than\n// 3000 rows one sheet).\nfunc (ws *xlsxWorksheet) checkRow() error {\n\tfor rowIdx := range ws.SheetData.Row {\n\t\trowData := &ws.SheetData.Row[rowIdx]\n\n\t\tcolCount := len(rowData.C)\n\t\tif colCount == 0 {\n\t\t\tcontinue\n\t\t}\n\t\t// check and fill the cell without r attribute in a row element\n\t\trCount := 0\n\t\tfor idx, cell := range rowData.C {\n\t\t\trCount++\n\t\t\tif cell.R != \"\" {\n\t\t\t\tlastR, _, err := CellNameToCoordinates(cell.R)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif lastR > rCount {\n\t\t\t\t\trCount = lastR\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\trowData.C[idx].R, _ = CoordinatesToCellName(rCount, rowIdx+1)\n\t\t}\n\t\tlastCol, _, err := CellNameToCoordinates(rowData.C[colCount-1].R)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif colCount < lastCol {\n\t\t\tsourceList := rowData.C\n\t\t\ttargetList := make([]xlsxC, 0, lastCol)\n\n\t\t\trowData.C = ws.SheetData.Row[rowIdx].C[:0]\n\n\t\t\tfor colIdx := 0; colIdx < lastCol; colIdx++ {\n\t\t\t\tcellName, err := CoordinatesToCellName(colIdx+1, rowIdx+1)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\ttargetList = append(targetList, xlsxC{R: cellName})\n\t\t\t}\n\n\t\t\trowData.C = targetList\n\n\t\t\tfor colIdx := range sourceList {\n\t\t\t\tcolData := &sourceList[colIdx]\n\t\t\t\tcolNum, _, err := CellNameToCoordinates(colData.R)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tws.SheetData.Row[rowIdx].C[colNum-1] = *colData\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// hasAttr determine if row non-default attributes.\nfunc (r *xlsxRow) hasAttr() bool {\n\treturn r.Spans != \"\" || r.S != 0 || r.CustomFormat || r.Ht != nil ||\n\t\tr.Hidden || r.CustomHeight || r.OutlineLevel != 0 || r.Collapsed ||\n\t\tr.ThickTop || r.ThickBot || r.Ph\n}\n\n// SetRowStyle provides a function to set the style of rows by given worksheet\n// name, row range, and style ID. Note that this will overwrite the existing\n// styles for the rows, it won't append or merge style with existing styles.\n//\n// For example set style of row 1 on Sheet1:\n//\n//\terr := f.SetRowStyle(\"Sheet1\", 1, 1, styleID)\n//\n// Set style of rows 1 to 10 on Sheet1:\n//\n//\terr := f.SetRowStyle(\"Sheet1\", 1, 10, styleID)\nfunc (f *File) SetRowStyle(sheet string, start, end, styleID int) error {\n\tif end < start {\n\t\tstart, end = end, start\n\t}\n\tif start < 1 {\n\t\treturn newInvalidRowNumberError(start)\n\t}\n\tif end > TotalRows {\n\t\treturn ErrMaxRows\n\t}\n\ts, err := f.stylesReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\tif styleID < 0 || s.CellXfs == nil || len(s.CellXfs.Xf) <= styleID {\n\t\treturn newInvalidStyleID(styleID)\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tws.prepareSheetXML(0, end)\n\tfor row := start - 1; row < end; row++ {\n\t\tws.SheetData.Row[row].S = styleID\n\t\tws.SheetData.Row[row].CustomFormat = true\n\t\tfor i := range ws.SheetData.Row[row].C {\n\t\t\tif _, rowNum, err := CellNameToCoordinates(ws.SheetData.Row[row].C[i].R); err == nil && rowNum-1 == row {\n\t\t\t\tws.SheetData.Row[row].C[i].S = styleID\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// convertRowHeightToPixels provides a function to convert the height of a\n// cell from user's units to pixels. If the height hasn't been set by the user\n// we use the default value. If the row is hidden it has a value of zero.\nfunc convertRowHeightToPixels(height float64) float64 {\n\tif height == 0 {\n\t\treturn 0\n\t}\n\treturn math.Ceil(4.0 / 3.4 * height)\n}\n"
  },
  {
    "path": "rows_test.go",
    "content": "package excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestGetRows(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", \"A1\"))\n\t// Test get rows with unsupported charset shared strings table\n\tf.SharedStrings = nil\n\tf.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)\n\t_, err := f.GetRows(\"Sheet1\")\n\tassert.NoError(t, err)\n}\n\nfunc TestRows(t *testing.T) {\n\tconst sheet2 = \"Sheet2\"\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\n\t// Test get rows with invalid sheet name\n\t_, err = f.Rows(\"Sheet:1\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\n\trows, err := f.Rows(sheet2)\n\tassert.NoError(t, err)\n\tvar collectedRows [][]string\n\tfor rows.Next() {\n\t\tcolumns, err := rows.Columns()\n\t\tassert.NoError(t, err)\n\t\tcollectedRows = append(collectedRows, trimSliceSpace(columns))\n\t}\n\tif !assert.NoError(t, rows.Error()) {\n\t\tt.FailNow()\n\t}\n\tassert.NoError(t, rows.Close())\n\n\treturnedRows, err := f.GetRows(sheet2)\n\tassert.NoError(t, err)\n\tfor i := range returnedRows {\n\t\treturnedRows[i] = trimSliceSpace(returnedRows[i])\n\t}\n\tif !assert.Equal(t, collectedRows, returnedRows) {\n\t\tt.FailNow()\n\t}\n\tassert.NoError(t, f.Close())\n\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", nil)\n\t_, err = f.Rows(\"Sheet1\")\n\tassert.NoError(t, err)\n\n\t// Test reload the file to memory from system temporary directory\n\tf, err = OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"), Options{UnzipXMLSizeLimit: 128})\n\tassert.NoError(t, err)\n\tvalue, err := f.GetCellValue(\"Sheet1\", \"A19\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Total:\", value)\n\t// Test load shared string table to memory\n\terr = f.SetCellValue(\"Sheet1\", \"A19\", \"A19\")\n\tassert.NoError(t, err)\n\tvalue, err = f.GetCellValue(\"Sheet1\", \"A19\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A19\", value)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetRow.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\t// Test rows iterator with unsupported charset shared strings table\n\tf.SharedStrings = nil\n\tf.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)\n\trows, err = f.Rows(sheet2)\n\tassert.NoError(t, err)\n\t_, err = rows.Columns()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestRowsIterator(t *testing.T) {\n\tsheetName, rowCount, expectedNumRow := \"Sheet2\", 0, 11\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\trequire.NoError(t, err)\n\n\trows, err := f.Rows(sheetName)\n\trequire.NoError(t, err)\n\n\tfor rows.Next() {\n\t\trowCount++\n\t\trequire.True(t, rowCount <= expectedNumRow, \"rowCount is greater than expected\")\n\t}\n\tassert.Equal(t, expectedNumRow, rowCount)\n\tassert.NoError(t, rows.Close())\n\tassert.NoError(t, f.Close())\n\n\t// Valued cell sparse distribution test\n\tf, sheetName, rowCount, expectedNumRow = NewFile(), \"Sheet1\", 0, 3\n\tcells := []string{\"C1\", \"E1\", \"A3\", \"B3\", \"C3\", \"D3\", \"E3\"}\n\tfor _, cell := range cells {\n\t\tassert.NoError(t, f.SetCellValue(sheetName, cell, 1))\n\t}\n\trows, err = f.Rows(sheetName)\n\trequire.NoError(t, err)\n\tfor rows.Next() {\n\t\trowCount++\n\t\trequire.True(t, rowCount <= expectedNumRow, \"rowCount is greater than expected\")\n\t}\n\tassert.Equal(t, expectedNumRow, rowCount)\n}\n\nfunc TestRowsGetRowOpts(t *testing.T) {\n\tsheetName := \"Sheet2\"\n\texpectedRowStyleID1 := RowOpts{Height: 17.0, Hidden: false, StyleID: 1}\n\texpectedRowStyleID2 := RowOpts{Height: 17.0, Hidden: false, StyleID: 0}\n\texpectedRowStyleID3 := RowOpts{Height: 17.0, Hidden: false, StyleID: 2}\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\trequire.NoError(t, err)\n\n\trows, err := f.Rows(sheetName)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, true, rows.Next())\n\t_, err = rows.Columns()\n\trequire.NoError(t, err)\n\trowOpts := rows.GetRowOpts()\n\tassert.Equal(t, expectedRowStyleID1, rowOpts)\n\tassert.Equal(t, true, rows.Next())\n\trowOpts = rows.GetRowOpts()\n\tassert.Equal(t, expectedRowStyleID2, rowOpts)\n\tassert.Equal(t, true, rows.Next())\n\t_, err = rows.Columns()\n\trequire.NoError(t, err)\n\trowOpts = rows.GetRowOpts()\n\tassert.Equal(t, expectedRowStyleID3, rowOpts)\n}\n\nfunc TestRowsError(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\t_, err = f.Rows(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestRowHeight(t *testing.T) {\n\tf := NewFile()\n\tsheet1 := f.GetSheetName(0)\n\n\tassert.EqualError(t, f.SetRowHeight(sheet1, 0, defaultRowHeightPixels+1.0), newInvalidRowNumberError(0).Error())\n\n\t_, err := f.GetRowHeight(\"Sheet1\", 0)\n\tassert.EqualError(t, err, newInvalidRowNumberError(0).Error())\n\n\tassert.NoError(t, f.SetRowHeight(sheet1, 1, 111.0))\n\theight, err := f.GetRowHeight(sheet1, 1)\n\tassert.NoError(t, err)\n\tassert.Equal(t, 111.0, height)\n\n\t// Test set row height overflow max row height limit\n\tassert.EqualError(t, f.SetRowHeight(sheet1, 4, MaxRowHeight+1), ErrMaxRowHeight.Error())\n\n\t// Test get row height that rows index over exists rows\n\theight, err = f.GetRowHeight(sheet1, 5)\n\tassert.NoError(t, err)\n\tassert.Equal(t, defaultRowHeight, height)\n\n\t// Test get row height that rows heights haven't changed\n\theight, err = f.GetRowHeight(sheet1, 3)\n\tassert.NoError(t, err)\n\tassert.Equal(t, defaultRowHeight, height)\n\n\t// Test set and get row height on not exists worksheet\n\tassert.EqualError(t, f.SetRowHeight(\"SheetN\", 1, 111.0), \"sheet SheetN does not exist\")\n\t_, err = f.GetRowHeight(\"SheetN\", 3)\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\n\t// Test set row height with invalid sheet name\n\tassert.EqualError(t, f.SetRowHeight(\"Sheet:1\", 1, 10.0), ErrSheetNameInvalid.Error())\n\n\t// Test get row height with invalid sheet name\n\t_, err = f.GetRowHeight(\"Sheet:1\", 3)\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\n\t// Test get row height with custom default row height\n\tassert.NoError(t, f.SetSheetProps(sheet1, &SheetPropsOptions{\n\t\tDefaultRowHeight: float64Ptr(30.0),\n\t\tCustomHeight:     boolPtr(true),\n\t}))\n\theight, err = f.GetRowHeight(sheet1, 100)\n\tassert.NoError(t, err)\n\tassert.Equal(t, 30.0, height)\n\n\t// Test set row height with custom default row height with prepare XML\n\tassert.NoError(t, f.SetCellValue(sheet1, \"A10\", \"A10\"))\n\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"A2\", true))\n\theight, err = f.GetRowHeight(\"Sheet2\", 1)\n\tassert.NoError(t, err)\n\tassert.Equal(t, 15.0, height)\n\n\terr = f.SaveAs(filepath.Join(\"test\", \"TestRowHeight.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\n\tassert.Equal(t, 0.0, convertColWidthToPixels(0))\n}\n\nfunc TestColumns(t *testing.T) {\n\tf := NewFile()\n\trows, err := f.Rows(\"Sheet1\")\n\tassert.NoError(t, err)\n\n\trows.decoder = f.xmlNewDecoder(bytes.NewReader([]byte(`<worksheet><sheetData><row r=\"2\"><c r=\"A1\" t=\"s\"><v>1</v></c></row></sheetData></worksheet>`)))\n\t_, err = rows.Columns()\n\tassert.NoError(t, err)\n\trows.decoder = f.xmlNewDecoder(bytes.NewReader([]byte(`<worksheet><sheetData><row r=\"2\"><c r=\"A1\" t=\"s\"><v>1</v></c></row></sheetData></worksheet>`)))\n\trows.curRow = 1\n\t_, err = rows.Columns()\n\tassert.NoError(t, err)\n\n\trows.decoder = f.xmlNewDecoder(bytes.NewReader([]byte(`<worksheet><sheetData><row r=\"A\"><c r=\"A1\" t=\"s\"><v>1</v></c></row><row r=\"A\"><c r=\"2\" t=\"inlineStr\"><is><t>B</t></is></c></row></sheetData></worksheet>`)))\n\tassert.True(t, rows.Next())\n\t_, err = rows.Columns()\n\tassert.EqualError(t, err, `strconv.Atoi: parsing \"A\": invalid syntax`)\n\n\trows.decoder = f.xmlNewDecoder(bytes.NewReader([]byte(`<worksheet><sheetData><row r=\"1\"><c r=\"A1\" t=\"s\"><v>1</v></c></row><row r=\"A\"><c r=\"2\" t=\"inlineStr\"><is><t>B</t></is></c></row></sheetData></worksheet>`)))\n\t_, err = rows.Columns()\n\tassert.NoError(t, err)\n\n\trows.decoder = f.xmlNewDecoder(bytes.NewReader([]byte(`<worksheet><sheetData><row r=\"1\"><c r=\"A\" t=\"s\"><v>1</v></c></row></sheetData></worksheet>`)))\n\tassert.True(t, rows.Next())\n\t_, err = rows.Columns()\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), err)\n\n\t// Test token is nil\n\trows.decoder = f.xmlNewDecoder(bytes.NewReader(nil))\n\t_, err = rows.Columns()\n\tassert.NoError(t, err)\n}\n\nfunc TestSharedStringsReader(t *testing.T) {\n\tf := NewFile()\n\t// Test read shared string with unsupported charset\n\tf.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)\n\t_, err := f.sharedStringsReader()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test read shared strings with unsupported charset content types\n\tf = NewFile()\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\t_, err = f.sharedStringsReader()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test read shared strings with unsupported charset workbook relationships\n\tf = NewFile()\n\tf.Relationships.Delete(defaultXMLPathWorkbookRels)\n\tf.Pkg.Store(defaultXMLPathWorkbookRels, MacintoshCyrillicCharset)\n\t_, err = f.sharedStringsReader()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestRowVisibility(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\t_, err = f.NewSheet(\"Sheet3\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetRowVisible(\"Sheet3\", 2, false))\n\tassert.NoError(t, f.SetRowVisible(\"Sheet3\", 2, true))\n\tvisible, err := f.GetRowVisible(\"Sheet3\", 2)\n\tassert.Equal(t, true, visible)\n\tassert.NoError(t, err)\n\tvisible, err = f.GetRowVisible(\"Sheet3\", 25)\n\tassert.Equal(t, false, visible)\n\tassert.NoError(t, err)\n\tassert.EqualError(t, f.SetRowVisible(\"Sheet3\", 0, true), newInvalidRowNumberError(0).Error())\n\tassert.EqualError(t, f.SetRowVisible(\"SheetN\", 2, false), \"sheet SheetN does not exist\")\n\t// Test set row visibility with invalid sheet name\n\tassert.EqualError(t, f.SetRowVisible(\"Sheet:1\", 1, false), ErrSheetNameInvalid.Error())\n\n\tvisible, err = f.GetRowVisible(\"Sheet3\", 0)\n\tassert.Equal(t, false, visible)\n\tassert.EqualError(t, err, newInvalidRowNumberError(0).Error())\n\t_, err = f.GetRowVisible(\"SheetN\", 1)\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get row visibility with invalid sheet name\n\t_, err = f.GetRowVisible(\"Sheet:1\", 1)\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestRowVisibility.xlsx\")))\n}\n\nfunc TestRemoveRow(t *testing.T) {\n\tf := NewFile()\n\tsheet1 := f.GetSheetName(0)\n\tr, err := f.workSheetReader(sheet1)\n\tassert.NoError(t, err)\n\tconst (\n\t\tcolCount = 10\n\t\trowCount = 10\n\t)\n\tassert.NoError(t, fillCells(f, sheet1, colCount, rowCount))\n\n\tassert.NoError(t, f.SetCellHyperLink(sheet1, \"A5\", \"https://github.com/xuri/excelize\", \"External\"))\n\n\tassert.EqualError(t, f.RemoveRow(sheet1, -1), newInvalidRowNumberError(-1).Error())\n\n\tassert.EqualError(t, f.RemoveRow(sheet1, 0), newInvalidRowNumberError(0).Error())\n\n\tassert.NoError(t, f.RemoveRow(sheet1, 4))\n\tassert.Len(t, r.SheetData.Row, rowCount-1)\n\n\tassert.NoError(t, f.MergeCell(sheet1, \"B3\", \"B5\"))\n\n\tassert.NoError(t, f.RemoveRow(sheet1, 2))\n\tassert.Len(t, r.SheetData.Row, rowCount-2)\n\n\tassert.NoError(t, f.RemoveRow(sheet1, 4))\n\tassert.Len(t, r.SheetData.Row, rowCount-3)\n\n\terr = f.AutoFilter(sheet1, \"A2:A2\", []AutoFilterOptions{{Column: \"A\", Expression: \"x != blanks\"}})\n\tassert.NoError(t, err)\n\n\tassert.NoError(t, f.RemoveRow(sheet1, 1))\n\tassert.Len(t, r.SheetData.Row, rowCount-4)\n\n\tassert.NoError(t, f.RemoveRow(sheet1, 2))\n\tassert.Len(t, r.SheetData.Row, rowCount-5)\n\n\tassert.NoError(t, f.RemoveRow(sheet1, 1))\n\tassert.Len(t, r.SheetData.Row, rowCount-6)\n\n\tassert.NoError(t, f.RemoveRow(sheet1, 10))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestRemoveRow.xlsx\")))\n\n\tf = NewFile()\n\tassert.NoError(t, f.MergeCell(\"Sheet1\", \"A1\", \"C1\"))\n\tassert.NoError(t, f.MergeCell(\"Sheet1\", \"A2\", \"C2\"))\n\tassert.NoError(t, f.RemoveRow(\"Sheet1\", 1))\n\tmergedCells, err := f.GetMergeCells(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A1\", mergedCells[0].GetStartAxis())\n\tassert.Equal(t, \"C1\", mergedCells[0].GetEndAxis())\n\n\t// Test remove row on not exist worksheet\n\tassert.EqualError(t, f.RemoveRow(\"SheetN\", 1), \"sheet SheetN does not exist\")\n\t// Test remove row with invalid sheet name\n\tassert.EqualError(t, f.RemoveRow(\"Sheet:1\", 1), ErrSheetNameInvalid.Error())\n\n\tf = NewFile()\n\tformulaType, ref := STCellFormulaTypeShared, \"C1:C5\"\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"C1\", \"A1+B1\",\n\t\tFormulaOpts{Ref: &ref, Type: &formulaType}))\n\tf.CalcChain = nil\n\tf.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.RemoveRow(\"Sheet1\", 1), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestInsertRows(t *testing.T) {\n\tf := NewFile()\n\tsheet1 := f.GetSheetName(0)\n\tr, err := f.workSheetReader(sheet1)\n\tassert.NoError(t, err)\n\tconst (\n\t\tcolCount = 10\n\t\trowCount = 10\n\t)\n\tassert.NoError(t, fillCells(f, sheet1, colCount, rowCount))\n\n\tassert.NoError(t, f.SetCellHyperLink(sheet1, \"A5\", \"https://github.com/xuri/excelize\", \"External\"))\n\n\tassert.NoError(t, f.InsertRows(sheet1, 1, 1))\n\tassert.Len(t, r.SheetData.Row, rowCount+1)\n\n\tassert.NoError(t, f.InsertRows(sheet1, 4, 1))\n\tassert.Len(t, r.SheetData.Row, rowCount+2)\n\n\tassert.NoError(t, f.InsertRows(sheet1, 4, 2))\n\tassert.Len(t, r.SheetData.Row, rowCount+4)\n\t// Test insert rows with invalid sheet name\n\tassert.EqualError(t, f.InsertRows(\"Sheet:1\", 1, 1), ErrSheetNameInvalid.Error())\n\n\tassert.EqualError(t, f.InsertRows(sheet1, -1, 1), newInvalidRowNumberError(-1).Error())\n\tassert.EqualError(t, f.InsertRows(sheet1, 0, 1), newInvalidRowNumberError(0).Error())\n\tassert.EqualError(t, f.InsertRows(sheet1, 4, 0), ErrParameterInvalid.Error())\n\tassert.EqualError(t, f.InsertRows(sheet1, 4, TotalRows), ErrMaxRows.Error())\n\tassert.EqualError(t, f.InsertRows(sheet1, 4, TotalRows-5), ErrMaxRows.Error())\n\tassert.EqualError(t, f.InsertRows(sheet1, TotalRows, 1), ErrMaxRows.Error())\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestInsertRows.xlsx\")))\n}\n\n// Test internal structure state after insert operations. It is important\n// for insert workflow to be constant to avoid side effect with functions\n// related to internal structure.\nfunc TestInsertRowsInEmptyFile(t *testing.T) {\n\tf := NewFile()\n\tsheet1 := f.GetSheetName(0)\n\tr, err := f.workSheetReader(sheet1)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.InsertRows(sheet1, 1, 1))\n\tassert.Len(t, r.SheetData.Row, 0)\n\tassert.NoError(t, f.InsertRows(sheet1, 2, 1))\n\tassert.Len(t, r.SheetData.Row, 0)\n\tassert.NoError(t, f.InsertRows(sheet1, 99, 1))\n\tassert.Len(t, r.SheetData.Row, 0)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestInsertRowInEmptyFile.xlsx\")))\n}\n\nfunc prepareTestBook2() (*File, error) {\n\tf := NewFile()\n\tfor cell, val := range map[string]string{\n\t\t\"A1\": \"A1 Value\",\n\t\t\"A2\": \"A2 Value\",\n\t\t\"A3\": \"A3 Value\",\n\t\t\"B1\": \"B1 Value\",\n\t\t\"B2\": \"B2 Value\",\n\t\t\"B3\": \"B3 Value\",\n\t} {\n\t\tif err := f.SetCellStr(\"Sheet1\", cell, val); err != nil {\n\t\t\treturn f, err\n\t\t}\n\t}\n\treturn f, nil\n}\n\nfunc TestDuplicateRowFromSingleRow(t *testing.T) {\n\tconst sheet = \"Sheet1\"\n\toutFile := filepath.Join(\"test\", \"TestDuplicateRow.%s.xlsx\")\n\n\tcells := map[string]string{\n\t\t\"A1\": \"A1 Value\",\n\t\t\"A2\": \"A2 Value\",\n\t\t\"A3\": \"A3 Value\",\n\t\t\"B1\": \"B1 Value\",\n\t\t\"B2\": \"B2 Value\",\n\t\t\"B3\": \"B3 Value\",\n\t}\n\n\tt.Run(\"FromSingleRow\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetCellStr(sheet, \"A1\", cells[\"A1\"]))\n\t\tassert.NoError(t, f.SetCellStr(sheet, \"B1\", cells[\"B1\"]))\n\n\t\tassert.NoError(t, f.DuplicateRow(sheet, 1))\n\t\tif !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, \"FromSingleRow_1\"))) {\n\t\t\tt.FailNow()\n\t\t}\n\t\texpect := map[string]string{\n\t\t\t\"A1\": cells[\"A1\"], \"B1\": cells[\"B1\"],\n\t\t\t\"A2\": cells[\"A1\"], \"B2\": cells[\"B1\"],\n\t\t}\n\t\tfor cell, val := range expect {\n\t\t\tv, err := f.GetCellValue(sheet, cell)\n\t\t\tassert.NoError(t, err)\n\t\t\tif !assert.Equal(t, val, v, cell) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t}\n\n\t\tassert.NoError(t, f.DuplicateRow(sheet, 2))\n\t\tif !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, \"FromSingleRow_2\"))) {\n\t\t\tt.FailNow()\n\t\t}\n\t\texpect = map[string]string{\n\t\t\t\"A1\": cells[\"A1\"], \"B1\": cells[\"B1\"],\n\t\t\t\"A2\": cells[\"A1\"], \"B2\": cells[\"B1\"],\n\t\t\t\"A3\": cells[\"A1\"], \"B3\": cells[\"B1\"],\n\t\t}\n\t\tfor cell, val := range expect {\n\t\t\tv, err := f.GetCellValue(sheet, cell)\n\t\t\tassert.NoError(t, err)\n\t\t\tif !assert.Equal(t, val, v, cell) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestDuplicateRowUpdateDuplicatedRows(t *testing.T) {\n\tconst sheet = \"Sheet1\"\n\toutFile := filepath.Join(\"test\", \"TestDuplicateRow.%s.xlsx\")\n\n\tcells := map[string]string{\n\t\t\"A1\": \"A1 Value\",\n\t\t\"A2\": \"A2 Value\",\n\t\t\"A3\": \"A3 Value\",\n\t\t\"B1\": \"B1 Value\",\n\t\t\"B2\": \"B2 Value\",\n\t\t\"B3\": \"B3 Value\",\n\t}\n\n\tt.Run(\"UpdateDuplicatedRows\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tassert.NoError(t, f.SetCellStr(sheet, \"A1\", cells[\"A1\"]))\n\t\tassert.NoError(t, f.SetCellStr(sheet, \"B1\", cells[\"B1\"]))\n\n\t\tassert.NoError(t, f.DuplicateRow(sheet, 1))\n\n\t\tassert.NoError(t, f.SetCellStr(sheet, \"A2\", cells[\"A2\"]))\n\t\tassert.NoError(t, f.SetCellStr(sheet, \"B2\", cells[\"B2\"]))\n\n\t\tif !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, \"UpdateDuplicatedRows\"))) {\n\t\t\tt.FailNow()\n\t\t}\n\t\texpect := map[string]string{\n\t\t\t\"A1\": cells[\"A1\"], \"B1\": cells[\"B1\"],\n\t\t\t\"A2\": cells[\"A2\"], \"B2\": cells[\"B2\"],\n\t\t}\n\t\tfor cell, val := range expect {\n\t\t\tv, err := f.GetCellValue(sheet, cell)\n\t\t\tassert.NoError(t, err)\n\t\t\tif !assert.Equal(t, val, v, cell) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestDuplicateRowFirstOfMultipleRows(t *testing.T) {\n\tconst sheet = \"Sheet1\"\n\toutFile := filepath.Join(\"test\", \"TestDuplicateRow.%s.xlsx\")\n\tcells := map[string]string{\n\t\t\"A1\": \"A1 Value\",\n\t\t\"A2\": \"A2 Value\",\n\t\t\"A3\": \"A3 Value\",\n\t\t\"B1\": \"B1 Value\",\n\t\t\"B2\": \"B2 Value\",\n\t\t\"B3\": \"B3 Value\",\n\t}\n\tt.Run(\"FirstOfMultipleRows\", func(t *testing.T) {\n\t\tf, err := prepareTestBook2()\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.DuplicateRow(sheet, 1))\n\n\t\tif !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, \"FirstOfMultipleRows\"))) {\n\t\t\tt.FailNow()\n\t\t}\n\t\texpect := map[string]string{\n\t\t\t\"A1\": cells[\"A1\"], \"B1\": cells[\"B1\"],\n\t\t\t\"A2\": cells[\"A1\"], \"B2\": cells[\"B1\"],\n\t\t\t\"A3\": cells[\"A2\"], \"B3\": cells[\"B2\"],\n\t\t\t\"A4\": cells[\"A3\"], \"B4\": cells[\"B3\"],\n\t\t}\n\t\tfor cell, val := range expect {\n\t\t\tv, err := f.GetCellValue(sheet, cell)\n\t\t\tassert.NoError(t, err)\n\t\t\tif !assert.Equal(t, val, v, cell) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestDuplicateRowZeroWithNoRows(t *testing.T) {\n\tconst sheet = \"Sheet1\"\n\toutFile := filepath.Join(\"test\", \"TestDuplicateRow.%s.xlsx\")\n\n\tt.Run(\"ZeroWithNoRows\", func(t *testing.T) {\n\t\tf := NewFile()\n\n\t\tassert.EqualError(t, f.DuplicateRow(sheet, 0), newInvalidRowNumberError(0).Error())\n\n\t\tif !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, \"ZeroWithNoRows\"))) {\n\t\t\tt.FailNow()\n\t\t}\n\n\t\tval, err := f.GetCellValue(sheet, \"A1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Empty(t, val)\n\t\tval, err = f.GetCellValue(sheet, \"B1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Empty(t, val)\n\t\tval, err = f.GetCellValue(sheet, \"A2\")\n\t\tassert.NoError(t, err)\n\t\tassert.Empty(t, val)\n\t\tval, err = f.GetCellValue(sheet, \"B2\")\n\t\tassert.NoError(t, err)\n\t\tassert.Empty(t, val)\n\n\t\tassert.NoError(t, err)\n\t\texpect := map[string]string{\n\t\t\t\"A1\": \"\", \"B1\": \"\",\n\t\t\t\"A2\": \"\", \"B2\": \"\",\n\t\t}\n\n\t\tfor cell, val := range expect {\n\t\t\tv, err := f.GetCellValue(sheet, cell)\n\t\t\tassert.NoError(t, err)\n\t\t\tif !assert.Equal(t, val, v, cell) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestDuplicateRowMiddleRowOfEmptyFile(t *testing.T) {\n\tconst sheet = \"Sheet1\"\n\toutFile := filepath.Join(\"test\", \"TestDuplicateRow.%s.xlsx\")\n\n\tt.Run(\"MiddleRowOfEmptyFile\", func(t *testing.T) {\n\t\tf := NewFile()\n\n\t\tassert.NoError(t, f.DuplicateRow(sheet, 99))\n\n\t\tif !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, \"MiddleRowOfEmptyFile\"))) {\n\t\t\tt.FailNow()\n\t\t}\n\t\texpect := map[string]string{\n\t\t\t\"A98\":  \"\",\n\t\t\t\"A99\":  \"\",\n\t\t\t\"A100\": \"\",\n\t\t}\n\t\tfor cell, val := range expect {\n\t\t\tv, err := f.GetCellValue(sheet, cell)\n\t\t\tassert.NoError(t, err)\n\t\t\tif !assert.Equal(t, val, v, cell) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestDuplicateRowWithLargeOffsetToMiddleOfData(t *testing.T) {\n\tconst sheet = \"Sheet1\"\n\toutFile := filepath.Join(\"test\", \"TestDuplicateRow.%s.xlsx\")\n\n\tcells := map[string]string{\n\t\t\"A1\": \"A1 Value\",\n\t\t\"A2\": \"A2 Value\",\n\t\t\"A3\": \"A3 Value\",\n\t\t\"B1\": \"B1 Value\",\n\t\t\"B2\": \"B2 Value\",\n\t\t\"B3\": \"B3 Value\",\n\t}\n\tt.Run(\"WithLargeOffsetToMiddleOfData\", func(t *testing.T) {\n\t\tf, err := prepareTestBook2()\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.DuplicateRowTo(sheet, 1, 3))\n\n\t\tif !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, \"WithLargeOffsetToMiddleOfData\"))) {\n\t\t\tt.FailNow()\n\t\t}\n\t\texpect := map[string]string{\n\t\t\t\"A1\": cells[\"A1\"], \"B1\": cells[\"B1\"],\n\t\t\t\"A2\": cells[\"A2\"], \"B2\": cells[\"B2\"],\n\t\t\t\"A3\": cells[\"A1\"], \"B3\": cells[\"B1\"],\n\t\t\t\"A4\": cells[\"A3\"], \"B4\": cells[\"B3\"],\n\t\t}\n\t\tfor cell, val := range expect {\n\t\t\tv, err := f.GetCellValue(sheet, cell)\n\t\t\tassert.NoError(t, err)\n\t\t\tif !assert.Equal(t, val, v, cell) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestDuplicateRowWithLargeOffsetToEmptyRows(t *testing.T) {\n\tconst sheet = \"Sheet1\"\n\toutFile := filepath.Join(\"test\", \"TestDuplicateRow.%s.xlsx\")\n\tcells := map[string]string{\n\t\t\"A1\": \"A1 Value\",\n\t\t\"A2\": \"A2 Value\",\n\t\t\"A3\": \"A3 Value\",\n\t\t\"B1\": \"B1 Value\",\n\t\t\"B2\": \"B2 Value\",\n\t\t\"B3\": \"B3 Value\",\n\t}\n\tt.Run(\"WithLargeOffsetToEmptyRows\", func(t *testing.T) {\n\t\tf, err := prepareTestBook2()\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.DuplicateRowTo(sheet, 1, 7))\n\n\t\tif !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, \"WithLargeOffsetToEmptyRows\"))) {\n\t\t\tt.FailNow()\n\t\t}\n\t\texpect := map[string]string{\n\t\t\t\"A1\": cells[\"A1\"], \"B1\": cells[\"B1\"],\n\t\t\t\"A2\": cells[\"A2\"], \"B2\": cells[\"B2\"],\n\t\t\t\"A3\": cells[\"A3\"], \"B3\": cells[\"B3\"],\n\t\t\t\"A7\": cells[\"A1\"], \"B7\": cells[\"B1\"],\n\t\t}\n\t\tfor cell, val := range expect {\n\t\t\tv, err := f.GetCellValue(sheet, cell)\n\t\t\tassert.NoError(t, err)\n\t\t\tif !assert.Equal(t, val, v, cell) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestDuplicateRowInsertBefore(t *testing.T) {\n\tconst sheet = \"Sheet1\"\n\toutFile := filepath.Join(\"test\", \"TestDuplicateRow.%s.xlsx\")\n\tcells := map[string]string{\n\t\t\"A1\": \"A1 Value\",\n\t\t\"A2\": \"A2 Value\",\n\t\t\"A3\": \"A3 Value\",\n\t\t\"B1\": \"B1 Value\",\n\t\t\"B2\": \"B2 Value\",\n\t\t\"B3\": \"B3 Value\",\n\t}\n\tt.Run(\"InsertBefore\", func(t *testing.T) {\n\t\tf, err := prepareTestBook2()\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.DuplicateRowTo(sheet, 2, 1))\n\t\tassert.NoError(t, f.DuplicateRowTo(sheet, 10, 4))\n\n\t\tif !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, \"InsertBefore\"))) {\n\t\t\tt.FailNow()\n\t\t}\n\n\t\texpect := map[string]string{\n\t\t\t\"A1\": cells[\"A2\"], \"B1\": cells[\"B2\"],\n\t\t\t\"A2\": cells[\"A1\"], \"B2\": cells[\"B1\"],\n\t\t\t\"A3\": cells[\"A2\"], \"B3\": cells[\"B2\"],\n\t\t\t\"A5\": cells[\"A3\"], \"B5\": cells[\"B3\"],\n\t\t}\n\t\tfor cell, val := range expect {\n\t\t\tv, err := f.GetCellValue(sheet, cell)\n\t\t\tassert.NoError(t, err)\n\t\t\tif !assert.Equal(t, val, v, cell) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestDuplicateRowInsertBeforeWithLargeOffset(t *testing.T) {\n\tconst sheet = \"Sheet1\"\n\toutFile := filepath.Join(\"test\", \"TestDuplicateRow.%s.xlsx\")\n\tcells := map[string]string{\n\t\t\"A1\": \"A1 Value\",\n\t\t\"A2\": \"A2 Value\",\n\t\t\"A3\": \"A3 Value\",\n\t\t\"B1\": \"B1 Value\",\n\t\t\"B2\": \"B2 Value\",\n\t\t\"B3\": \"B3 Value\",\n\t}\n\tt.Run(\"InsertBeforeWithLargeOffset\", func(t *testing.T) {\n\t\tf, err := prepareTestBook2()\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.DuplicateRowTo(sheet, 3, 1))\n\n\t\tif !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, \"InsertBeforeWithLargeOffset\"))) {\n\t\t\tt.FailNow()\n\t\t}\n\n\t\texpect := map[string]string{\n\t\t\t\"A1\": cells[\"A3\"], \"B1\": cells[\"B3\"],\n\t\t\t\"A2\": cells[\"A1\"], \"B2\": cells[\"B1\"],\n\t\t\t\"A3\": cells[\"A2\"], \"B3\": cells[\"B2\"],\n\t\t\t\"A4\": cells[\"A3\"], \"B4\": cells[\"B3\"],\n\t\t}\n\t\tfor cell, val := range expect {\n\t\t\tv, err := f.GetCellValue(sheet, cell)\n\t\t\tassert.NoError(t, err)\n\t\t\tif !assert.Equal(t, val, v) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestDuplicateRowInsertBeforeWithMergeCells(t *testing.T) {\n\tconst sheet = \"Sheet1\"\n\toutFile := filepath.Join(\"test\", \"TestDuplicateRow.%s.xlsx\")\n\tt.Run(\"InsertBeforeWithLargeOffset\", func(t *testing.T) {\n\t\tf, err := prepareTestBook2()\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, f.MergeCell(sheet, \"B2\", \"C2\"))\n\t\tassert.NoError(t, f.MergeCell(sheet, \"C6\", \"C8\"))\n\n\t\tassert.NoError(t, f.DuplicateRowTo(sheet, 2, 1))\n\t\tassert.NoError(t, f.DuplicateRowTo(sheet, 1, 8))\n\n\t\tif !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, \"InsertBeforeWithMergeCells\"))) {\n\t\t\tt.FailNow()\n\t\t}\n\n\t\texpect := []MergeCell{\n\t\t\t{\"B3:C3\", \"B2 Value\"},\n\t\t\t{\"C7:C10\", \"\"},\n\t\t\t{\"B1:C1\", \"B2 Value\"},\n\t\t}\n\n\t\tmergeCells, err := f.GetMergeCells(sheet)\n\t\tassert.NoError(t, err)\n\t\tfor idx, val := range expect {\n\t\t\tif !assert.Equal(t, val, mergeCells[idx]) {\n\t\t\t\tt.FailNow()\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestDuplicateRowInvalidRowNum(t *testing.T) {\n\tconst sheet = \"Sheet1\"\n\toutFile := filepath.Join(\"test\", \"TestDuplicateRow.InvalidRowNum.%s.xlsx\")\n\n\tcells := map[string]string{\n\t\t\"A1\": \"A1 Value\",\n\t\t\"A2\": \"A2 Value\",\n\t\t\"A3\": \"A3 Value\",\n\t\t\"B1\": \"B1 Value\",\n\t\t\"B2\": \"B2 Value\",\n\t\t\"B3\": \"B3 Value\",\n\t}\n\n\tinvalidIndexes := []int{-100, -2, -1, 0}\n\n\tfor _, row := range invalidIndexes {\n\t\tname := fmt.Sprintf(\"%d\", row)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tf := NewFile()\n\t\t\tfor col, val := range cells {\n\t\t\t\tassert.NoError(t, f.SetCellStr(sheet, col, val))\n\t\t\t}\n\n\t\t\tassert.EqualError(t, f.DuplicateRow(sheet, row), newInvalidRowNumberError(row).Error())\n\n\t\t\tfor col, val := range cells {\n\t\t\t\tv, err := f.GetCellValue(sheet, col)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tif !assert.Equal(t, val, v) {\n\t\t\t\t\tt.FailNow()\n\t\t\t\t}\n\t\t\t}\n\t\t\tassert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, name)))\n\t\t})\n\t}\n\n\tfor _, row1 := range invalidIndexes {\n\t\tfor _, row2 := range invalidIndexes {\n\t\t\tname := fmt.Sprintf(\"[%d,%d]\", row1, row2)\n\t\t\tt.Run(name, func(t *testing.T) {\n\t\t\t\tf := NewFile()\n\t\t\t\tfor col, val := range cells {\n\t\t\t\t\tassert.NoError(t, f.SetCellStr(sheet, col, val))\n\t\t\t\t}\n\n\t\t\t\tassert.EqualError(t, f.DuplicateRowTo(sheet, row1, row2), newInvalidRowNumberError(row1).Error())\n\n\t\t\t\tfor col, val := range cells {\n\t\t\t\t\tv, err := f.GetCellValue(sheet, col)\n\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t\tif !assert.Equal(t, val, v) {\n\t\t\t\t\t\tt.FailNow()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tassert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, name)))\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc TestDuplicateRow(t *testing.T) {\n\tf := NewFile()\n\t// Test duplicate row with invalid sheet name\n\tassert.EqualError(t, f.DuplicateRowTo(\"Sheet:1\", 1, 2), ErrSheetNameInvalid.Error())\n\n\tf = NewFile()\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     \"Amount\",\n\t\tRefersTo: \"Sheet1!$B$1\",\n\t}))\n\tassert.NoError(t, f.SetCellFormula(\"Sheet1\", \"A1\", \"Amount+C1\"))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A10\", \"A10\"))\n\n\tformat, err := f.NewConditionalStyle(&Style{Font: &Font{Color: \"9A0511\"}, Fill: Fill{Type: \"pattern\", Color: []string{\"FEC7CE\"}, Pattern: 1}})\n\tassert.NoError(t, err)\n\n\texpected := []ConditionalFormatOptions{\n\t\t{Type: \"cell\", Criteria: \"greater than\", Format: &format, Value: \"0\"},\n\t}\n\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"A1\", expected))\n\n\tdv := NewDataValidation(true)\n\tdv.Sqref = \"A1\"\n\tassert.NoError(t, dv.SetDropList([]string{\"1\", \"2\", \"3\"}))\n\tassert.NoError(t, f.AddDataValidation(\"Sheet1\", dv))\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).DataValidations.DataValidation[0].Sqref = \"A1\"\n\n\tassert.NoError(t, f.DuplicateRowTo(\"Sheet1\", 1, 10))\n\tformula, err := f.GetCellFormula(\"Sheet1\", \"A10\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Amount+C10\", formula)\n\tvalue, err := f.GetCellValue(\"Sheet1\", \"A11\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A10\", value)\n\n\tcfs, err := f.GetConditionalFormats(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, cfs, 2)\n\tassert.Equal(t, expected, cfs[\"A10:A10\"])\n\n\tdvs, err := f.GetDataValidations(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, dvs, 2)\n\tassert.Equal(t, \"A10:A10\", dvs[1].Sqref)\n\n\t// Test duplicate data validation with row number exceeds maximum limit\n\tassert.Equal(t, ErrMaxRows, f.duplicateDataValidations(ws.(*xlsxWorksheet), \"Sheet1\", 1, TotalRows+1))\n\t// Test duplicate data validation with invalid range reference\n\tws.(*xlsxWorksheet).DataValidations.DataValidation[0].Sqref = \"A\"\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.duplicateDataValidations(ws.(*xlsxWorksheet), \"Sheet1\", 1, 10))\n\n\t// Test duplicate conditional formatting with row number exceeds maximum limit\n\tassert.Equal(t, ErrMaxRows, f.duplicateConditionalFormat(ws.(*xlsxWorksheet), \"Sheet1\", 1, TotalRows+1))\n\t// Test duplicate conditional formatting with invalid range reference\n\tws.(*xlsxWorksheet).ConditionalFormatting[0].SQRef = \"A\"\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.duplicateConditionalFormat(ws.(*xlsxWorksheet), \"Sheet1\", 1, 10))\n}\n\nfunc TestDuplicateRowTo(t *testing.T) {\n\tf, sheetName := NewFile(), \"Sheet1\"\n\t// Test duplicate row with invalid target row number\n\tassert.Equal(t, nil, f.DuplicateRowTo(sheetName, 1, 0))\n\t// Test duplicate row with equal source and target row number\n\tassert.Equal(t, nil, f.DuplicateRowTo(sheetName, 1, 1))\n\t// Test duplicate row on the blank worksheet\n\tassert.Equal(t, nil, f.DuplicateRowTo(sheetName, 1, 2))\n\t// Test duplicate row on the worksheet with illegal cell reference\n\tf.Sheet.Store(\"xl/worksheets/sheet1.xml\", &xlsxWorksheet{\n\t\tMergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: \"A:B1\"}}},\n\t})\n\tassert.EqualError(t, f.DuplicateRowTo(sheetName, 1, 2), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\t// Test duplicate row on not exists worksheet\n\tassert.EqualError(t, f.DuplicateRowTo(\"SheetN\", 1, 2), \"sheet SheetN does not exist\")\n\t// Test duplicate row with invalid sheet name\n\tassert.EqualError(t, f.DuplicateRowTo(\"Sheet:1\", 1, 2), ErrSheetNameInvalid.Error())\n}\n\nfunc TestDuplicateMergeCells(t *testing.T) {\n\tf := File{}\n\tws := &xlsxWorksheet{MergeCells: &xlsxMergeCells{\n\t\tCells: []*xlsxMergeCell{{Ref: \"A1:-\"}},\n\t}}\n\tassert.EqualError(t, f.duplicateMergeCells(ws, \"Sheet1\", 0, 0), `cannot convert cell \"-\" to coordinates: invalid cell name \"-\"`)\n\tws.MergeCells.Cells[0].Ref = \"A1:B1\"\n\tassert.EqualError(t, f.duplicateMergeCells(ws, \"SheetN\", 1, 2), \"sheet SheetN does not exist\")\n}\n\nfunc TestGetValueFromInlineStr(t *testing.T) {\n\tc := &xlsxC{T: \"inlineStr\"}\n\tf := NewFile()\n\td := &xlsxSST{}\n\tval, err := c.getValueFrom(f, d, false)\n\tassert.NoError(t, err)\n\tassert.Empty(t, val)\n}\n\nfunc TestGetValueFromNumber(t *testing.T) {\n\tc := &xlsxC{T: \"n\"}\n\tf := NewFile()\n\td := &xlsxSST{}\n\tfor input, expected := range map[string]string{\n\t\t\"2.2.\":                     \"2.2.\",\n\t\t\"1.1000000000000001\":       \"1.1\",\n\t\t\"2.2200000000000002\":       \"2.22\",\n\t\t\"28.552\":                   \"28.552\",\n\t\t\"27.399000000000001\":       \"27.399\",\n\t\t\"26.245999999999999\":       \"26.246\",\n\t\t\"2422.3000000000002\":       \"2422.3\",\n\t\t\"2.220000ddsf0000000002-r\": \"2.220000ddsf0000000002-r\",\n\t} {\n\t\tc.V = input\n\t\tval, err := c.getValueFrom(f, d, false)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, val)\n\t}\n}\n\nfunc TestErrSheetNotExistError(t *testing.T) {\n\tassert.Equal(t, \"sheet Sheet1 does not exist\", ErrSheetNotExist{\"Sheet1\"}.Error())\n}\n\nfunc TestCheckRow(t *testing.T) {\n\tf := NewFile()\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(xml.Header+`<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" ><sheetData><row r=\"2\"><c><v>1</v></c><c r=\"F2\"><v>2</v></c><c><v>3</v></c><c><v>4</v></c><c r=\"M2\"><v>5</v></c></row></sheetData></worksheet>`))\n\t_, err := f.GetRows(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", false))\n\tf = NewFile()\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(xml.Header+`<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" ><sheetData><row r=\"2\"><c><v>1</v></c><c r=\"-\"><v>2</v></c><c><v>3</v></c><c><v>4</v></c><c r=\"M2\"><v>5</v></c></row></sheetData></worksheet>`))\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.checked.Delete(\"xl/worksheets/sheet1.xml\")\n\tassert.EqualError(t, f.SetCellValue(\"Sheet1\", \"A1\", false), newCellNameToCoordinatesError(\"-\", newInvalidCellNameError(\"-\")).Error())\n}\n\nfunc TestSetRowStyle(t *testing.T) {\n\tf := NewFile()\n\tstyle1, err := f.NewStyle(&Style{Fill: Fill{Type: \"pattern\", Color: []string{\"63BE7B\"}, Pattern: 1}})\n\tassert.NoError(t, err)\n\tstyle2, err := f.NewStyle(&Style{Fill: Fill{Type: \"pattern\", Color: []string{\"E0EBF5\"}, Pattern: 1}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"B2\", \"B2\", style1))\n\tassert.EqualError(t, f.SetRowStyle(\"Sheet1\", 5, -1, style2), newInvalidRowNumberError(-1).Error())\n\tassert.EqualError(t, f.SetRowStyle(\"Sheet1\", 1, TotalRows+1, style2), ErrMaxRows.Error())\n\t// Test set row style with invalid style ID\n\tassert.EqualError(t, f.SetRowStyle(\"Sheet1\", 1, 1, -1), newInvalidStyleID(-1).Error())\n\t// Test set row style with not exists style ID\n\tassert.EqualError(t, f.SetRowStyle(\"Sheet1\", 1, 1, 10), newInvalidStyleID(10).Error())\n\tassert.EqualError(t, f.SetRowStyle(\"SheetN\", 1, 1, style2), \"sheet SheetN does not exist\")\n\t// Test set row style with invalid sheet name\n\tassert.EqualError(t, f.SetRowStyle(\"Sheet:1\", 1, 1, 0), ErrSheetNameInvalid.Error())\n\tassert.NoError(t, f.SetRowStyle(\"Sheet1\", 5, 1, style2))\n\tcellStyleID, err := f.GetCellStyle(\"Sheet1\", \"B2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, style2, cellStyleID)\n\t// Test cell inheritance rows style\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"C1\", nil))\n\tcellStyleID, err = f.GetCellStyle(\"Sheet1\", \"C1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, style2, cellStyleID)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetRowStyle.xlsx\")))\n\t// Test set row style with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetRowStyle(\"Sheet1\", 1, 1, cellStyleID), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestSetRowHeight(t *testing.T) {\n\tf := NewFile()\n\t// Test hidden row by set row height to 0\n\tassert.NoError(t, f.SetRowHeight(\"Sheet1\", 2, 0))\n\tht, err := f.GetRowHeight(\"Sheet1\", 2)\n\tassert.NoError(t, err)\n\tassert.Empty(t, ht)\n\t// Test unset custom row height\n\tassert.NoError(t, f.SetRowHeight(\"Sheet1\", 2, -1))\n\tht, err = f.GetRowHeight(\"Sheet1\", 2)\n\tassert.NoError(t, err)\n\tassert.Equal(t, defaultRowHeight, ht)\n\t// Test set row height with invalid height value\n\tassert.Equal(t, ErrParameterInvalid, f.SetRowHeight(\"Sheet1\", 2, -2))\n}\n\nfunc TestNumberFormats(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tcells := make([][]string, 0)\n\tcols, err := f.Cols(\"Sheet2\")\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tfor cols.Next() {\n\t\tcol, err := cols.Rows()\n\t\tassert.NoError(t, err)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tcells = append(cells, col)\n\t}\n\tassert.Equal(t, []string{\"\", \"200\", \"450\", \"200\", \"510\", \"315\", \"127\", \"89\", \"348\", \"53\", \"37\"}, cells[3])\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tnumFmt1, err := f.NewStyle(&Style{NumFmt: 1})\n\tassert.NoError(t, err)\n\tnumFmt2, err := f.NewStyle(&Style{NumFmt: 2})\n\tassert.NoError(t, err)\n\tnumFmt3, err := f.NewStyle(&Style{NumFmt: 3})\n\tassert.NoError(t, err)\n\tnumFmt9, err := f.NewStyle(&Style{NumFmt: 9})\n\tassert.NoError(t, err)\n\tnumFmt10, err := f.NewStyle(&Style{NumFmt: 10})\n\tassert.NoError(t, err)\n\tnumFmt21, err := f.NewStyle(&Style{NumFmt: 21})\n\tassert.NoError(t, err)\n\tnumFmt37, err := f.NewStyle(&Style{NumFmt: 37})\n\tassert.NoError(t, err)\n\tnumFmt38, err := f.NewStyle(&Style{NumFmt: 38})\n\tassert.NoError(t, err)\n\tnumFmt39, err := f.NewStyle(&Style{NumFmt: 39})\n\tassert.NoError(t, err)\n\tnumFmt40, err := f.NewStyle(&Style{NumFmt: 40})\n\tassert.NoError(t, err)\n\tfor _, cases := range [][]interface{}{\n\t\t{\"A1\", numFmt1, 8.8888666665555493e+19, \"88888666665555500000\"},\n\t\t{\"A2\", numFmt1, 8.8888666665555487, \"9\"},\n\t\t{\"A3\", numFmt2, 8.8888666665555493e+19, \"88888666665555500000.00\"},\n\t\t{\"A4\", numFmt2, 8.8888666665555487, \"8.89\"},\n\t\t{\"A5\", numFmt3, 8.8888666665555493e+19, \"88,888,666,665,555,500,000\"},\n\t\t{\"A6\", numFmt3, 8.8888666665555487, \"9\"},\n\t\t{\"A7\", numFmt3, 123, \"123\"},\n\t\t{\"A8\", numFmt3, -1234, \"-1,234\"},\n\t\t{\"A9\", numFmt9, 8.8888666665555493e+19, \"8888866666555550000000%\"},\n\t\t{\"A10\", numFmt9, -8.8888666665555493e+19, \"-8888866666555550000000%\"},\n\t\t{\"A11\", numFmt9, 8.8888666665555487, \"889%\"},\n\t\t{\"A12\", numFmt9, -8.8888666665555487, \"-889%\"},\n\t\t{\"A13\", numFmt10, 8.8888666665555493e+19, \"8888866666555550000000.00%\"},\n\t\t{\"A14\", numFmt10, -8.8888666665555493e+19, \"-8888866666555550000000.00%\"},\n\t\t{\"A15\", numFmt10, 8.8888666665555487, \"888.89%\"},\n\t\t{\"A16\", numFmt10, -8.8888666665555487, \"-888.89%\"},\n\t\t{\"A17\", numFmt37, 8.8888666665555493e+19, \"88,888,666,665,555,500,000 \"},\n\t\t{\"A18\", numFmt37, -8.8888666665555493e+19, \"(88,888,666,665,555,500,000)\"},\n\t\t{\"A19\", numFmt37, 8.8888666665555487, \"9 \"},\n\t\t{\"A20\", numFmt37, -8.8888666665555487, \"(9)\"},\n\t\t{\"A21\", numFmt38, 8.8888666665555493e+19, \"88,888,666,665,555,500,000 \"},\n\t\t{\"A22\", numFmt38, -8.8888666665555493e+19, \"(88,888,666,665,555,500,000)\"},\n\t\t{\"A23\", numFmt38, 8.8888666665555487, \"9 \"},\n\t\t{\"A24\", numFmt38, -8.8888666665555487, \"(9)\"},\n\t\t{\"A25\", numFmt39, 8.8888666665555493e+19, \"88,888,666,665,555,500,000.00 \"},\n\t\t{\"A26\", numFmt39, -8.8888666665555493e+19, \"(88,888,666,665,555,500,000.00)\"},\n\t\t{\"A27\", numFmt39, 8.8888666665555487, \"8.89 \"},\n\t\t{\"A28\", numFmt39, -8.8888666665555487, \"(8.89)\"},\n\t\t{\"A29\", numFmt40, 8.8888666665555493e+19, \"88,888,666,665,555,500,000.00 \"},\n\t\t{\"A30\", numFmt40, -8.8888666665555493e+19, \"(88,888,666,665,555,500,000.00)\"},\n\t\t{\"A31\", numFmt40, 8.8888666665555487, \"8.89 \"},\n\t\t{\"A32\", numFmt40, -8.8888666665555487, \"(8.89)\"},\n\t\t{\"A33\", numFmt21, 44729.999988368058, \"23:59:59\"},\n\t\t{\"A34\", numFmt21, 44944.375005787035, \"09:00:00\"},\n\t\t{\"A35\", numFmt21, 44944.375005798611, \"09:00:01\"},\n\t} {\n\t\tcell, styleID, value, expected := cases[0].(string), cases[1].(int), cases[2], cases[3].(string)\n\t\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", cell, cell, styleID))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", cell, value))\n\t\tresult, err := f.GetCellValue(\"Sheet1\", cell)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, result, cell)\n\t}\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestNumberFormats.xlsx\")))\n\n\tf = NewFile(Options{ShortDatePattern: \"yyyy/m/d\"})\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 43543.503206018519))\n\tnumFmt14, err := f.NewStyle(&Style{NumFmt: 14})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A1\", \"A1\", numFmt14))\n\tresult, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"2019/3/19\", result, \"A1\")\n}\n\nfunc TestCellXMLHandler(t *testing.T) {\n\tvar (\n\t\tcontent      = []byte(fmt.Sprintf(`<worksheet xmlns=\"%s\"><sheetData><row r=\"1\"><c r=\"A1\" t=\"s\"><v>10</v></c><c r=\"B1\"><is><t>String</t></is></c></row><row r=\"2\"><c r=\"A2\" s=\"4\" t=\"str\"><f>2*A1</f><v>0</v></c><c r=\"C2\" s=\"1\"><f>A3</f><v>2422.3000000000002</v></c><c r=\"D2\" t=\"d\"><v>2022-10-22T15:05:29Z</v></c><c r=\"F2\"></c><c r=\"G2\"></c></row></sheetData></worksheet>`, NameSpaceSpreadSheet.Value))\n\t\texpected, ws xlsxWorksheet\n\t\trow          *xlsxRow\n\t)\n\tassert.NoError(t, xml.Unmarshal(content, &expected))\n\tdecoder := xml.NewDecoder(bytes.NewReader(content))\n\trows := Rows{decoder: decoder}\n\tfor {\n\t\ttoken, _ := decoder.Token()\n\t\tif token == nil {\n\t\t\tbreak\n\t\t}\n\t\tswitch element := token.(type) {\n\t\tcase xml.StartElement:\n\t\t\tif element.Name.Local == \"row\" {\n\t\t\t\tr, err := strconv.Atoi(element.Attr[0].Value)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tws.SheetData.Row = append(ws.SheetData.Row, xlsxRow{R: r})\n\t\t\t\trow = &ws.SheetData.Row[len(ws.SheetData.Row)-1]\n\t\t\t}\n\t\t\tif element.Name.Local == \"c\" {\n\t\t\t\tcolCell := xlsxC{}\n\t\t\t\tassert.NoError(t, colCell.cellXMLHandler(rows.decoder, &element))\n\t\t\t\trow.C = append(row.C, colCell)\n\t\t\t}\n\t\t}\n\t}\n\tassert.Equal(t, expected.SheetData.Row, ws.SheetData.Row)\n\n\tfor _, rowXML := range []string{\n\t\t`<row spans=\"1:17\" r=\"1\"><c r=\"A1\" t=\"s\" s=\"A\"><v>10</v></c></row></sheetData></worksheet>`, // s need number\n\t\t`<row spans=\"1:17\" r=\"1\"><c r=\"A1\"><v>10</v>    </row></sheetData></worksheet>`,             // missing </c>\n\t\t`<row spans=\"1:17\" r=\"1\"><c r=\"B1\"><is><t>`,                                                 // incorrect data\n\t} {\n\t\tws := xlsxWorksheet{}\n\t\tcontent := []byte(fmt.Sprintf(`<worksheet xmlns=\"%s\"><sheetData>%s</sheetData></worksheet>`, NameSpaceSpreadSheet.Value, rowXML))\n\t\texpected := xml.Unmarshal(content, &ws)\n\t\tassert.Error(t, expected)\n\t\tdecoder := xml.NewDecoder(bytes.NewReader(content))\n\t\trows := Rows{decoder: decoder}\n\t\tfor {\n\t\t\ttoken, _ := decoder.Token()\n\t\t\tif token == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tswitch element := token.(type) {\n\t\t\tcase xml.StartElement:\n\t\t\t\tif element.Name.Local == \"c\" {\n\t\t\t\t\tcolCell := xlsxC{}\n\t\t\t\t\terr := colCell.cellXMLHandler(rows.decoder, &element)\n\t\t\t\t\tassert.Error(t, err)\n\t\t\t\t\tassert.Equal(t, expected, err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkRows(b *testing.B) {\n\tf, _ := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tfor i := 0; i < b.N; i++ {\n\t\trows, _ := f.Rows(\"Sheet2\")\n\t\tfor rows.Next() {\n\t\t\trow, _ := rows.Columns()\n\t\t\tfor i := range row {\n\t\t\t\tif i >= 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif err := rows.Close(); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n\tif err := f.Close(); err != nil {\n\t\tb.Error(err)\n\t}\n}\n\n// trimSliceSpace trim continually blank element in the tail of slice.\nfunc trimSliceSpace(s []string) []string {\n\tfor {\n\t\tif len(s) > 0 && s[len(s)-1] == \"\" {\n\t\t\ts = s[:len(s)-1]\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn s\n}\n"
  },
  {
    "path": "shape.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n)\n\n// parseShapeOptions provides a function to parse the format settings of the\n// shape with default value.\nfunc parseShapeOptions(opts *Shape) (*Shape, error) {\n\tif opts == nil {\n\t\treturn nil, ErrParameterInvalid\n\t}\n\tif opts.Type == \"\" {\n\t\treturn nil, ErrParameterInvalid\n\t}\n\tif opts.Width == 0 {\n\t\topts.Width = defaultShapeSize\n\t}\n\tif opts.Height == 0 {\n\t\topts.Height = defaultShapeSize\n\t}\n\tif opts.Line.Width == nil {\n\t\topts.Line.Width = float64Ptr(defaultShapeLineWidth)\n\t}\n\tif opts.Fill.Transparency < 0 || 100 < opts.Fill.Transparency {\n\t\treturn opts, ErrTransparency\n\t}\n\tformat := opts.Format\n\tgraphicOptions, err := format.parseGraphicOptions(nil)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\topts.Format = *graphicOptions\n\treturn opts, err\n}\n\n// AddShape provides the method to add shape in a sheet by given worksheet\n// name and shape format set (such as offset, scale, aspect ratio setting and\n// print settings). For example, add text box (rect shape) in Sheet1:\n//\n//\tlineWidth := 1.2\n//\terr := f.AddShape(\"Sheet1\",\n//\t    &excelize.Shape{\n//\t        Cell: \"G6\",\n//\t        Type: \"rect\",\n//\t        Line: excelize.ShapeLine{Color: \"4286F4\", Width: &lineWidth},\n//\t        Fill: excelize.Fill{Color: []string{\"8EB9FF\"}, Pattern: 1},\n//\t        Paragraph: []excelize.RichTextRun{\n//\t            {\n//\t                Text: \"Rectangle Shape\",\n//\t                Font: &excelize.Font{\n//\t                    Bold:      true,\n//\t                    Italic:    true,\n//\t                    Family:    \"Times New Roman\",\n//\t                    Size:      18,\n//\t                    Color:     \"777777\",\n//\t                    Underline: \"sng\",\n//\t                },\n//\t            },\n//\t        },\n//\t        Width:  180,\n//\t        Height: 40,\n//\t    },\n//\t)\n//\n// The following shows the type of shape supported by excelize:\n//\n//\taccentBorderCallout1 (Callout 1 with Border and Accent Shape)\n//\taccentBorderCallout2 (Callout 2 with Border and Accent Shape)\n//\taccentBorderCallout3 (Callout 3 with Border and Accent Shape)\n//\taccentCallout1 (Callout 1 Shape)\n//\taccentCallout2 (Callout 2 Shape)\n//\taccentCallout3 (Callout 3 Shape)\n//\tactionButtonBackPrevious (Back or Previous Button Shape)\n//\tactionButtonBeginning (Beginning Button Shape)\n//\tactionButtonBlank (Blank Button Shape)\n//\tactionButtonDocument (Document Button Shape)\n//\tactionButtonEnd (End Button Shape)\n//\tactionButtonForwardNext (Forward or Next Button Shape)\n//\tactionButtonHelp (Help Button Shape)\n//\tactionButtonHome (Home Button Shape)\n//\tactionButtonInformation (Information Button Shape)\n//\tactionButtonMovie (Movie Button Shape)\n//\tactionButtonReturn (Return Button Shape)\n//\tactionButtonSound (Sound Button Shape)\n//\tarc (Curved Arc Shape)\n//\tbentArrow (Bent Arrow Shape)\n//\tbentConnector2 (Bent Connector 2 Shape)\n//\tbentConnector3 (Bent Connector 3 Shape)\n//\tbentConnector4 (Bent Connector 4 Shape)\n//\tbentConnector5 (Bent Connector 5 Shape)\n//\tbentUpArrow (Bent Up Arrow Shape)\n//\tbevel (Bevel Shape)\n//\tblockArc (Block Arc Shape)\n//\tborderCallout1 (Callout 1 with Border Shape)\n//\tborderCallout2 (Callout 2 with Border Shape)\n//\tborderCallout3 (Callout 3 with Border Shape)\n//\tbracePair (Brace Pair Shape)\n//\tbracketPair (Bracket Pair Shape)\n//\tcallout1 (Callout 1 Shape)\n//\tcallout2 (Callout 2 Shape)\n//\tcallout3 (Callout 3 Shape)\n//\tcan (Can Shape)\n//\tchartPlus (Chart Plus Shape)\n//\tchartStar (Chart Star Shape)\n//\tchartX (Chart X Shape)\n//\tchevron (Chevron Shape)\n//\tchord (Chord Shape)\n//\tcircularArrow (Circular Arrow Shape)\n//\tcloud (Cloud Shape)\n//\tcloudCallout (Callout Cloud Shape)\n//\tcorner (Corner Shape)\n//\tcornerTabs (Corner Tabs Shape)\n//\tcube (Cube Shape)\n//\tcurvedConnector2 (Curved Connector 2 Shape)\n//\tcurvedConnector3 (Curved Connector 3 Shape)\n//\tcurvedConnector4 (Curved Connector 4 Shape)\n//\tcurvedConnector5 (Curved Connector 5 Shape)\n//\tcurvedDownArrow (Curved Down Arrow Shape)\n//\tcurvedLeftArrow (Curved Left Arrow Shape)\n//\tcurvedRightArrow (Curved Right Arrow Shape)\n//\tcurvedUpArrow (Curved Up Arrow Shape)\n//\tdecagon (Decagon Shape)\n//\tdiagStripe (Diagonal Stripe Shape)\n//\tdiamond (Diamond Shape)\n//\tdodecagon (Dodecagon Shape)\n//\tdonut (Donut Shape)\n//\tdoubleWave (Double Wave Shape)\n//\tdownArrow (Down Arrow Shape)\n//\tdownArrowCallout (Callout Down Arrow Shape)\n//\tellipse (Ellipse Shape)\n//\tellipseRibbon (Ellipse Ribbon Shape)\n//\tellipseRibbon2 (Ellipse Ribbon 2 Shape)\n//\tflowChartAlternateProcess (Alternate Process Flow Shape)\n//\tflowChartCollate (Collate Flow Shape)\n//\tflowChartConnector (Connector Flow Shape)\n//\tflowChartDecision (Decision Flow Shape)\n//\tflowChartDelay (Delay Flow Shape)\n//\tflowChartDisplay (Display Flow Shape)\n//\tflowChartDocument (Document Flow Shape)\n//\tflowChartExtract (Extract Flow Shape)\n//\tflowChartInputOutput (Input Output Flow Shape)\n//\tflowChartInternalStorage (Internal Storage Flow Shape)\n//\tflowChartMagneticDisk (Magnetic Disk Flow Shape)\n//\tflowChartMagneticDrum (Magnetic Drum Flow Shape)\n//\tflowChartMagneticTape (Magnetic Tape Flow Shape)\n//\tflowChartManualInput (Manual Input Flow Shape)\n//\tflowChartManualOperation (Manual Operation Flow Shape)\n//\tflowChartMerge (Merge Flow Shape)\n//\tflowChartMultidocument (Multi-Document Flow Shape)\n//\tflowChartOfflineStorage (Offline Storage Flow Shape)\n//\tflowChartOffpageConnector (Off-Page Connector Flow Shape)\n//\tflowChartOnlineStorage (Online Storage Flow Shape)\n//\tflowChartOr (Or Flow Shape)\n//\tflowChartPredefinedProcess (Predefined Process Flow Shape)\n//\tflowChartPreparation (Preparation Flow Shape)\n//\tflowChartProcess (Process Flow Shape)\n//\tflowChartPunchedCard (Punched Card Flow Shape)\n//\tflowChartPunchedTape (Punched Tape Flow Shape)\n//\tflowChartSort (Sort Flow Shape)\n//\tflowChartSummingJunction (Summing Junction Flow Shape)\n//\tflowChartTerminator (Terminator Flow Shape)\n//\tfoldedCorner (Folded Corner Shape)\n//\tframe (Frame Shape)\n//\tfunnel (Funnel Shape)\n//\tgear6 (Gear 6 Shape)\n//\tgear9 (Gear 9 Shape)\n//\thalfFrame (Half Frame Shape)\n//\theart (Heart Shape)\n//\theptagon (Heptagon Shape)\n//\thexagon (Hexagon Shape)\n//\thomePlate (Home Plate Shape)\n//\thorizontalScroll (Horizontal Scroll Shape)\n//\tirregularSeal1 (Irregular Seal 1 Shape)\n//\tirregularSeal2 (Irregular Seal 2 Shape)\n//\tleftArrow (Left Arrow Shape)\n//\tleftArrowCallout (Callout Left Arrow Shape)\n//\tleftBrace (Left Brace Shape)\n//\tleftBracket (Left Bracket Shape)\n//\tleftCircularArrow (Left Circular Arrow Shape)\n//\tleftRightArrow (Left Right Arrow Shape)\n//\tleftRightArrowCallout (Callout Left Right Arrow Shape)\n//\tleftRightCircularArrow (Left Right Circular Arrow Shape)\n//\tleftRightRibbon (Left Right Ribbon Shape)\n//\tleftRightUpArrow (Left Right Up Arrow Shape)\n//\tleftUpArrow (Left Up Arrow Shape)\n//\tlightningBolt (Lightning Bolt Shape)\n//\tline (Line Shape)\n//\tlineInv (Line Inverse Shape)\n//\tmathDivide (Divide Math Shape)\n//\tmathEqual (Equal Math Shape)\n//\tmathMinus (Minus Math Shape)\n//\tmathMultiply (Multiply Math Shape)\n//\tmathNotEqual (Not Equal Math Shape)\n//\tmathPlus (Plus Math Shape)\n//\tmoon (Moon Shape)\n//\tnonIsoscelesTrapezoid (Non-Isosceles Trapezoid Shape)\n//\tnoSmoking (No Smoking Shape)\n//\tnotchedRightArrow (Notched Right Arrow Shape)\n//\toctagon (Octagon Shape)\n//\tparallelogram (Parallelogram Shape)\n//\tpentagon (Pentagon Shape)\n//\tpie (Pie Shape)\n//\tpieWedge (Pie Wedge Shape)\n//\tplaque (Plaque Shape)\n//\tplaqueTabs (Plaque Tabs Shape)\n//\tplus (Plus Shape)\n//\tquadArrow (Quad-Arrow Shape)\n//\tquadArrowCallout (Callout Quad-Arrow Shape)\n//\trect (Rectangle Shape)\n//\tribbon (Ribbon Shape)\n//\tribbon2 (Ribbon 2 Shape)\n//\trightArrow (Right Arrow Shape)\n//\trightArrowCallout (Callout Right Arrow Shape)\n//\trightBrace (Right Brace Shape)\n//\trightBracket (Right Bracket Shape)\n//\tround1Rect (One Round Corner Rectangle Shape)\n//\tround2DiagRect (Two Diagonal Round Corner Rectangle Shape)\n//\tround2SameRect (Two Same-side Round Corner Rectangle Shape)\n//\troundRect (Round Corner Rectangle Shape)\n//\trtTriangle (Right Triangle Shape)\n//\tsmileyFace (Smiley Face Shape)\n//\tsnip1Rect (One Snip Corner Rectangle Shape)\n//\tsnip2DiagRect (Two Diagonal Snip Corner Rectangle Shape)\n//\tsnip2SameRect (Two Same-side Snip Corner Rectangle Shape)\n//\tsnipRoundRect (One Snip One Round Corner Rectangle Shape)\n//\tsquareTabs (Square Tabs Shape)\n//\tstar10 (Ten Pointed Star Shape)\n//\tstar12 (Twelve Pointed Star Shape)\n//\tstar16 (Sixteen Pointed Star Shape)\n//\tstar24 (Twenty Four Pointed Star Shape)\n//\tstar32 (Thirty Two Pointed Star Shape)\n//\tstar4 (Four Pointed Star Shape)\n//\tstar5 (Five Pointed Star Shape)\n//\tstar6 (Six Pointed Star Shape)\n//\tstar7 (Seven Pointed Star Shape)\n//\tstar8 (Eight Pointed Star Shape)\n//\tstraightConnector1 (Straight Connector 1 Shape)\n//\tstripedRightArrow (Striped Right Arrow Shape)\n//\tsun (Sun Shape)\n//\tswooshArrow (Swoosh Arrow Shape)\n//\tteardrop (Teardrop Shape)\n//\ttrapezoid (Trapezoid Shape)\n//\ttriangle (Triangle Shape)\n//\tupArrow (Up Arrow Shape)\n//\tupArrowCallout (Callout Up Arrow Shape)\n//\tupDownArrow (Up Down Arrow Shape)\n//\tupDownArrowCallout (Callout Up Down Arrow Shape)\n//\tuturnArrow (U-Turn Arrow Shape)\n//\tverticalScroll (Vertical Scroll Shape)\n//\twave (Wave Shape)\n//\twedgeEllipseCallout (Callout Wedge Ellipse Shape)\n//\twedgeRectCallout (Callout Wedge Rectangle Shape)\n//\twedgeRoundRectCallout (Callout Wedge Round Rectangle Shape)\n//\n// The following shows the type of text underline supported by excelize:\n//\n//\tnone\n//\twords\n//\tsng\n//\tdbl\n//\theavy\n//\tdotted\n//\tdottedHeavy\n//\tdash\n//\tdashHeavy\n//\tdashLong\n//\tdashLongHeavy\n//\tdotDash\n//\tdotDashHeavy\n//\tdotDotDash\n//\tdotDotDashHeavy\n//\twavy\n//\twavyHeavy\n//\twavyDbl\nfunc (f *File) AddShape(sheet string, opts *Shape) error {\n\toptions, err := parseShapeOptions(opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Read sheet data\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Add first shape for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.\n\tdrawingID := f.countDrawings() + 1\n\tdrawingXML := \"xl/drawings/drawing\" + strconv.Itoa(drawingID) + \".xml\"\n\tsheetRelationshipsDrawingXML := \"../drawings/drawing\" + strconv.Itoa(drawingID) + \".xml\"\n\n\tif ws.Drawing != nil {\n\t\t// The worksheet already has a shape or chart relationships, use the relationships drawing ../drawings/drawing%d.xml.\n\t\tsheetRelationshipsDrawingXML = f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)\n\t\tdrawingID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingXML, \"../drawings/drawing\"), \".xml\"))\n\t\tdrawingXML = strings.ReplaceAll(sheetRelationshipsDrawingXML, \"..\", \"xl\")\n\t} else {\n\t\t// Add first shape for given sheet.\n\t\tsheetXMLPath, _ := f.getSheetXMLPath(sheet)\n\t\tsheetRels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(sheetXMLPath, \"xl/worksheets/\") + \".rels\"\n\t\trID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, \"\")\n\t\tf.addSheetDrawing(sheet, rID)\n\t\tf.addSheetNameSpace(sheet, SourceRelationship)\n\t}\n\tif err = f.addDrawingShape(sheet, drawingXML, opts.Cell, options); err != nil {\n\t\treturn err\n\t}\n\treturn f.addContentTypePart(drawingID, \"drawings\")\n}\n\n// cellAnchorShape create a two cell anchor shape size placeholder for a\n// group, a shape, or a drawing element.\nfunc (f *File) cellAnchorShape(sheet, drawingXML, cell string, width, height uint, format GraphicOptions) (*xlsxWsDr, *xdrCellAnchor, int, error) {\n\tfromCol, fromRow, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn nil, nil, 0, err\n\t}\n\tw := int(float64(width) * format.ScaleX)\n\th := int(float64(height) * format.ScaleY)\n\tcolStart, rowStart, colEnd, rowEnd, x1, y1, x2, y2 := f.positionObjectPixels(sheet, fromCol, fromRow, w, h, &format)\n\tcontent, cNvPrID, err := f.drawingParser(drawingXML)\n\tif err != nil {\n\t\treturn content, nil, cNvPrID, err\n\t}\n\tcellAnchor := xdrCellAnchor{}\n\tfrom := xlsxFrom{}\n\tfrom.Col = colStart\n\tfrom.ColOff = x1 * EMU\n\tfrom.Row = rowStart\n\tfrom.RowOff = y1 * EMU\n\tcellAnchor.From = &from\n\tif format.Positioning != \"oneCell\" {\n\t\tto := xlsxTo{}\n\t\tto.Col = colEnd\n\t\tto.ColOff = x2 * EMU\n\t\tto.Row = rowEnd\n\t\tto.RowOff = y2 * EMU\n\t\tcellAnchor.To = &to\n\t\tcellAnchor.EditAs = format.Positioning\n\t}\n\tif format.Positioning == \"oneCell\" {\n\t\tcellAnchor.Ext = &xlsxPositiveSize2D{\n\t\t\tCx: x2 * EMU,\n\t\t\tCy: y2 * EMU,\n\t\t}\n\t}\n\treturn content, &cellAnchor, cNvPrID, err\n}\n\n// addDrawingShape provides a function to add preset geometry by given sheet,\n// drawingXML and format sets.\nfunc (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *Shape) error {\n\tcontent, cellAnchor, cNvPrID, err := f.cellAnchorShape(\n\t\tsheet, drawingXML, cell, opts.Width, opts.Height, opts.Format)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar solidColor string\n\tif len(opts.Fill.Color) == 1 {\n\t\tsolidColor = strings.ReplaceAll(strings.ToUpper(opts.Fill.Color[0]), \"#\", \"\")\n\t}\n\tshape := xdrSp{\n\t\tMacro: opts.Macro,\n\t\tNvSpPr: &xdrNvSpPr{\n\t\t\tCNvPr: &xlsxCNvPr{\n\t\t\t\tID:    cNvPrID,\n\t\t\t\tName:  \"Shape \" + strconv.Itoa(cNvPrID),\n\t\t\t\tDescr: opts.Format.AltText,\n\t\t\t},\n\t\t\tCNvSpPr: &xdrCNvSpPr{\n\t\t\t\tTxBox: true,\n\t\t\t},\n\t\t},\n\t\tSpPr: &xlsxSpPr{\n\t\t\tXfrm: xlsxXfrm{\n\t\t\t\tExt: xlsxPositiveSize2D{\n\t\t\t\t\tCx: int(opts.Width) * EMU,\n\t\t\t\t\tCy: int(opts.Height) * EMU,\n\t\t\t\t},\n\t\t\t},\n\t\t\tPrstGeom: xlsxPrstGeom{\n\t\t\t\tPrst: opts.Type,\n\t\t\t},\n\t\t},\n\t\tStyle: &xdrStyle{\n\t\t\tLnRef:     setShapeRef(opts.Line.Color, 2),\n\t\t\tFillRef:   setShapeRef(solidColor, 1),\n\t\t\tEffectRef: setShapeRef(\"\", 0),\n\t\t\tFontRef: &aFontRef{\n\t\t\t\tIdx: \"minor\",\n\t\t\t\tSchemeClr: &attrValString{\n\t\t\t\t\tVal: stringPtr(\"tx1\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tTxBody: &xdrTxBody{\n\t\t\tBodyPr: &aBodyPr{\n\t\t\t\tVertOverflow: \"clip\",\n\t\t\t\tHorzOverflow: \"clip\",\n\t\t\t\tWrap:         \"none\",\n\t\t\t\tRtlCol:       false,\n\t\t\t\tAnchor:       \"t\",\n\t\t\t},\n\t\t},\n\t}\n\tif len(opts.Format.Name) > 0 {\n\t\tshape.NvSpPr.CNvPr.Name = opts.Format.Name\n\t}\n\tif *opts.Line.Width != 1 {\n\t\tshape.SpPr.Ln = xlsxLineProperties{\n\t\t\tW: f.ptToEMUs(*opts.Line.Width),\n\t\t}\n\t}\n\tif opts.Fill.Transparency > 0 {\n\t\tval := (100 - opts.Fill.Transparency) * 1000\n\t\tshape.SpPr.SolidFill = &aSolidFill{SrgbClr: &aSrgbClr{\n\t\t\tVal:   stringPtr(solidColor),\n\t\t\tAlpha: &attrValInt{Val: &val},\n\t\t}}\n\t}\n\tdefaultFont, err := f.GetDefaultFont()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(opts.Paragraph) < 1 {\n\t\topts.Paragraph = []RichTextRun{\n\t\t\t{\n\t\t\t\tFont: &Font{\n\t\t\t\t\tBold:      false,\n\t\t\t\t\tItalic:    false,\n\t\t\t\t\tUnderline: \"none\",\n\t\t\t\t\tFamily:    defaultFont,\n\t\t\t\t\tSize:      11,\n\t\t\t\t\tColor:     \"000000\",\n\t\t\t\t},\n\t\t\t\tText: \" \",\n\t\t\t},\n\t\t}\n\t}\n\tfor _, p := range opts.Paragraph {\n\t\tu := \"none\"\n\t\tfont := &Font{}\n\t\tif p.Font != nil {\n\t\t\tfont = p.Font\n\t\t}\n\t\tif idx := inStrSlice(supportedDrawingUnderlineTypes, font.Underline, true); idx != -1 {\n\t\t\tu = supportedDrawingUnderlineTypes[idx]\n\t\t}\n\t\ttext := p.Text\n\t\tif text == \"\" {\n\t\t\ttext = \" \"\n\t\t}\n\t\tparagraph := &aP{\n\t\t\tR: &aR{\n\t\t\t\tRPr: aRPr{\n\t\t\t\t\tI:       font.Italic,\n\t\t\t\t\tB:       font.Bold,\n\t\t\t\t\tLang:    \"en-US\",\n\t\t\t\t\tAltLang: \"en-US\",\n\t\t\t\t\tU:       u,\n\t\t\t\t\tSz:      font.Size * 100,\n\t\t\t\t\tLatin:   &xlsxCTTextFont{Typeface: font.Family},\n\t\t\t\t\tEa:      &xlsxCTTextFont{Typeface: font.Family},\n\t\t\t\t\tCs:      &xlsxCTTextFont{Typeface: font.Family},\n\t\t\t\t},\n\t\t\t\tT: text,\n\t\t\t},\n\t\t\tEndParaRPr: &aEndParaRPr{\n\t\t\t\tLang: \"en-US\",\n\t\t\t},\n\t\t}\n\t\tsrgbClr := strings.ReplaceAll(strings.ToUpper(font.Color), \"#\", \"\")\n\t\tif len(srgbClr) == 6 {\n\t\t\tparagraph.R.RPr.SolidFill = &aSolidFill{\n\t\t\t\tSrgbClr: &aSrgbClr{\n\t\t\t\t\tVal: stringPtr(srgbClr),\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t\tshape.TxBody.P = append(shape.TxBody.P, paragraph)\n\t}\n\tcellAnchor.Sp = &shape\n\tcellAnchor.ClientData = &xdrClientData{\n\t\tFLocksWithSheet:  *opts.Format.Locked,\n\t\tFPrintsWithSheet: *opts.Format.PrintObject,\n\t}\n\tif opts.Format.Positioning == \"oneCell\" {\n\t\tcontent.OneCellAnchor = append(content.OneCellAnchor, cellAnchor)\n\t} else {\n\t\tcontent.TwoCellAnchor = append(content.TwoCellAnchor, cellAnchor)\n\t}\n\tf.Drawings.Store(drawingXML, content)\n\treturn err\n}\n\n// setShapeRef provides a function to set color with hex model by given actual\n// color value.\nfunc setShapeRef(color string, i int) *aRef {\n\tif color == \"\" {\n\t\treturn &aRef{\n\t\t\tIdx: 0,\n\t\t\tScrgbClr: &aScrgbClr{\n\t\t\t\tR: 0,\n\t\t\t\tG: 0,\n\t\t\t\tB: 0,\n\t\t\t},\n\t\t}\n\t}\n\treturn &aRef{\n\t\tIdx: i,\n\t\tSrgbClr: &attrValString{\n\t\t\tVal: stringPtr(strings.ReplaceAll(strings.ToUpper(color), \"#\", \"\")),\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "shape_test.go",
    "content": "package excelize\n\nimport (\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestAddShape(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\tassert.NoError(t, f.AddShape(\"Sheet1\", &Shape{\n\t\tCell: \"A30\",\n\t\tType: \"rect\",\n\t\tParagraph: []RichTextRun{\n\t\t\t{Text: \"Rectangle\", Font: &Font{Color: \"CD5C5C\"}},\n\t\t\t{Text: \"Shape\", Font: &Font{Bold: true, Color: \"2980B9\"}},\n\t\t},\n\t}))\n\tassert.NoError(t, f.AddShape(\"Sheet1\", &Shape{Cell: \"B30\", Type: \"rect\", Paragraph: []RichTextRun{{Text: \"Rectangle\"}, {}}}))\n\tshape1 := Shape{Cell: \"C30\", Type: \"rect\", Width: 160, Height: 160}\n\tassert.NoError(t, f.AddShape(\"Sheet1\", &shape1))\n\t// Test add shape with invalid positioning types\n\tassert.Equal(t, newInvalidOptionalValue(\"Positioning\", \"x\", supportedPositioning), f.AddShape(\"Sheet1\", &Shape{Cell: \"C30\", Type: \"rect\", Format: GraphicOptions{Positioning: \"x\"}}))\n\tassert.EqualError(t, f.AddShape(\"Sheet3\", &Shape{Cell: \"C30\", Type: \"rect\"}), \"sheet Sheet3 does not exist\")\n\tassert.Equal(t, ErrParameterInvalid, f.AddShape(\"Sheet3\", nil))\n\tassert.Equal(t, ErrParameterInvalid, f.AddShape(\"Sheet1\", &Shape{Cell: \"A1\"}))\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.AddShape(\"Sheet1\", &Shape{\n\t\tCell: \"A\",\n\t\tType: \"rect\",\n\t\tParagraph: []RichTextRun{\n\t\t\t{Text: \"Rectangle\", Font: &Font{Color: \"CD5C5C\"}},\n\t\t\t{Text: \"Shape\", Font: &Font{Bold: true, Color: \"2980B9\"}},\n\t\t},\n\t}))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddShape1.xlsx\")))\n\n\t// Test add first shape for given sheet\n\tf = NewFile()\n\tlineWidth := 1.2\n\tshape2 := Shape{\n\t\tCell: \"A1\",\n\t\tType: \"ellipseRibbon\",\n\t\tLine: ShapeLine{Color: \"4286F4\", Width: &lineWidth},\n\t\tFill: Fill{Color: []string{\"8EB9FF\"}, Transparency: 60},\n\t\tFormat: GraphicOptions{\n\t\t\tAltText:     \"Shape\",\n\t\t\tName:        \"Shape 1\",\n\t\t\tPrintObject: boolPtr(true),\n\t\t\tLocked:      boolPtr(false),\n\t\t\tScaleX:      0.8,\n\t\t\tScaleY:      0.8,\n\t\t\tPositioning: \"oneCell\",\n\t\t},\n\t\tParagraph: []RichTextRun{\n\t\t\t{\n\t\t\t\tFont: &Font{\n\t\t\t\t\tBold:      true,\n\t\t\t\t\tItalic:    true,\n\t\t\t\t\tFamily:    \"Times New Roman\",\n\t\t\t\t\tSize:      18,\n\t\t\t\t\tColor:     \"777777\",\n\t\t\t\t\tUnderline: \"sng\",\n\t\t\t\t},\n\t\t\t\tText: \"Shape\",\n\t\t\t},\n\t\t},\n\t\tHeight: 90,\n\t}\n\tassert.NoError(t, f.AddShape(\"Sheet1\", &shape2))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddShape2.xlsx\")))\n\t// Test add shape with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.AddShape(\"Sheet:1\", &Shape{\n\t\tCell: \"A30\",\n\t\tType: \"rect\",\n\t\tParagraph: []RichTextRun{\n\t\t\t{Text: \"Rectangle\", Font: &Font{Color: \"CD5C5C\"}},\n\t\t\t{Text: \"Shape\", Font: &Font{Bold: true, Color: \"2980B9\"}},\n\t\t},\n\t}))\n\t// Test add shape with transparency value exceeds limit\n\tassert.Equal(t, ErrTransparency, f.AddShape(\"Sheet1\", &Shape{Cell: \"B30\", Type: \"rect\", Fill: Fill{Color: []string{\"8EB9FF\"}, Transparency: 110}}))\n\t// Test add shape with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddShape(\"Sheet1\", &Shape{Cell: \"B30\", Type: \"rect\", Paragraph: []RichTextRun{{Text: \"Rectangle\"}, {}}}), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test add shape with unsupported charset content types\n\tf = NewFile()\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddShape(\"Sheet1\", &Shape{Cell: \"B30\", Type: \"rect\", Paragraph: []RichTextRun{{Text: \"Rectangle\"}, {}}}), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestAddDrawingShape(t *testing.T) {\n\tf := NewFile()\n\tpath := \"xl/drawings/drawing1.xml\"\n\tf.Pkg.Store(path, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.addDrawingShape(\"sheet1\", path, \"A1\",\n\t\t&Shape{\n\t\t\tWidth:  defaultShapeSize,\n\t\t\tHeight: defaultShapeSize,\n\t\t\tFormat: GraphicOptions{\n\t\t\t\tPrintObject: boolPtr(true),\n\t\t\t\tLocked:      boolPtr(false),\n\t\t\t},\n\t\t},\n\t), \"XML syntax error on line 1: invalid UTF-8\")\n}\n"
  },
  {
    "path": "sheet.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/tiendc/go-deepcopy\"\n)\n\n// IgnoredErrorsType is the type of ignored errors.\ntype IgnoredErrorsType byte\n\n// Ignored errors types enumeration.\nconst (\n\tIgnoredErrorsEvalError = iota\n\tIgnoredErrorsTwoDigitTextYear\n\tIgnoredErrorsNumberStoredAsText\n\tIgnoredErrorsFormula\n\tIgnoredErrorsFormulaRange\n\tIgnoredErrorsUnlockedFormula\n\tIgnoredErrorsEmptyCellReference\n\tIgnoredErrorsListDataValidation\n\tIgnoredErrorsCalculatedColumn\n)\n\n// NewSheet provides the function to create a new sheet by given a worksheet\n// name and returns the index of the sheets in the workbook after it appended.\n// Note that when creating a new workbook, the default worksheet named\n// `Sheet1` will be created.\nfunc (f *File) NewSheet(sheet string) (int, error) {\n\tvar err error\n\tif err = checkSheetName(sheet); err != nil {\n\t\treturn -1, err\n\t}\n\t// Check if the worksheet already exists\n\tindex, err := f.GetSheetIndex(sheet)\n\tif index != -1 {\n\t\treturn index, err\n\t}\n\t_ = f.DeleteSheet(sheet)\n\tf.SheetCount++\n\twb, _ := f.workbookReader()\n\tsheetID := 0\n\tfor _, v := range wb.Sheets.Sheet {\n\t\tif v.SheetID > sheetID {\n\t\t\tsheetID = v.SheetID\n\t\t}\n\t}\n\tsheetID++\n\t// Update [Content_Types].xml\n\t_ = f.setContentTypes(\"/xl/worksheets/sheet\"+strconv.Itoa(sheetID)+\".xml\", ContentTypeSpreadSheetMLWorksheet)\n\t// Create new sheet /xl/worksheets/sheet%d.xml\n\tf.setSheet(sheetID, sheet)\n\t// Update workbook.xml.rels\n\trID := f.addRels(f.getWorkbookRelsPath(), SourceRelationshipWorkSheet, fmt.Sprintf(\"worksheets/sheet%d.xml\", sheetID), \"\")\n\t// Update workbook.xml\n\tf.setWorkbook(sheet, sheetID, rID)\n\treturn f.GetSheetIndex(sheet)\n}\n\n// contentTypesReader provides a function to get the pointer to the\n// [Content_Types].xml structure after deserialization.\nfunc (f *File) contentTypesReader() (*xlsxTypes, error) {\n\tif f.ContentTypes == nil {\n\t\tf.ContentTypes = new(xlsxTypes)\n\t\tf.ContentTypes.mu.Lock()\n\t\tdefer f.ContentTypes.mu.Unlock()\n\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathContentTypes)))).\n\t\t\tDecode(f.ContentTypes); err != nil && err != io.EOF {\n\t\t\treturn f.ContentTypes, err\n\t\t}\n\t}\n\treturn f.ContentTypes, nil\n}\n\n// contentTypesWriter provides a function to save [Content_Types].xml after\n// serialize structure.\nfunc (f *File) contentTypesWriter() {\n\tif f.ContentTypes != nil {\n\t\toutput, _ := xml.Marshal(f.ContentTypes)\n\t\tf.saveFileList(defaultXMLPathContentTypes, output)\n\t}\n}\n\n// getWorksheetPath construct a target XML as xl/worksheets/sheet%d by split\n// path, compatible with different types of relative paths in\n// workbook.xml.rels, for example: worksheets/sheet%d.xml\n// and /xl/worksheets/sheet%d.xml\nfunc (f *File) getWorksheetPath(relTarget string) (path string) {\n\tpath = filepath.ToSlash(strings.TrimPrefix(\n\t\tstrings.ReplaceAll(filepath.Clean(fmt.Sprintf(\"%s/%s\", filepath.Dir(f.getWorkbookPath()), relTarget)), \"\\\\\", \"/\"), \"/\"))\n\tif strings.HasPrefix(relTarget, \"/\") {\n\t\tpath = filepath.ToSlash(strings.TrimPrefix(strings.ReplaceAll(filepath.Clean(relTarget), \"\\\\\", \"/\"), \"/\"))\n\t}\n\treturn path\n}\n\n// mergeExpandedCols merge expanded columns.\nfunc (f *File) mergeExpandedCols(ws *xlsxWorksheet) {\n\tsort.Slice(ws.Cols.Col, func(i, j int) bool {\n\t\treturn ws.Cols.Col[i].Min < ws.Cols.Col[j].Min\n\t})\n\tvar columns []xlsxCol\n\tfor i, n := 0, len(ws.Cols.Col); i < n; {\n\t\tleft := i\n\t\tfor i++; i < n && reflect.DeepEqual(\n\t\t\txlsxCol{\n\t\t\t\tBestFit:      ws.Cols.Col[i-1].BestFit,\n\t\t\t\tCollapsed:    ws.Cols.Col[i-1].Collapsed,\n\t\t\t\tCustomWidth:  ws.Cols.Col[i-1].CustomWidth,\n\t\t\t\tHidden:       ws.Cols.Col[i-1].Hidden,\n\t\t\t\tMax:          ws.Cols.Col[i-1].Max + 1,\n\t\t\t\tMin:          ws.Cols.Col[i-1].Min + 1,\n\t\t\t\tOutlineLevel: ws.Cols.Col[i-1].OutlineLevel,\n\t\t\t\tPhonetic:     ws.Cols.Col[i-1].Phonetic,\n\t\t\t\tStyle:        ws.Cols.Col[i-1].Style,\n\t\t\t\tWidth:        ws.Cols.Col[i-1].Width,\n\t\t\t}, ws.Cols.Col[i]); i++ {\n\t\t}\n\t\tvar column xlsxCol\n\t\t_ = deepcopy.Copy(&column, ws.Cols.Col[left])\n\t\tif left < i-1 {\n\t\t\tcolumn.Max = ws.Cols.Col[i-1].Min\n\t\t}\n\t\tcolumns = append(columns, column)\n\t}\n\tws.Cols.Col = columns\n}\n\n// workSheetWriter provides a function to save xl/worksheets/sheet%d.xml after\n// serialize structure.\nfunc (f *File) workSheetWriter() {\n\tvar (\n\t\tarr     []byte\n\t\tbuffer  = bytes.NewBuffer(arr)\n\t\tencoder = xml.NewEncoder(buffer)\n\t)\n\tf.Sheet.Range(func(p, ws interface{}) bool {\n\t\tif ws != nil {\n\t\t\tsheet := ws.(*xlsxWorksheet)\n\t\t\tif sheet.MergeCells != nil && len(sheet.MergeCells.Cells) > 0 {\n\t\t\t\t_ = sheet.mergeOverlapCells()\n\t\t\t}\n\t\t\tif sheet.Cols != nil && len(sheet.Cols.Col) > 0 {\n\t\t\t\tf.mergeExpandedCols(sheet)\n\t\t\t}\n\t\t\tsheet.SheetData.Row = trimRow(&sheet.SheetData)\n\t\t\tif sheet.SheetPr != nil || sheet.Drawing != nil || sheet.Hyperlinks != nil || sheet.Picture != nil || sheet.TableParts != nil {\n\t\t\t\tf.addNameSpaces(p.(string), SourceRelationship)\n\t\t\t}\n\t\t\tif sheet.DecodeAlternateContent != nil {\n\t\t\t\tsheet.AlternateContent = &xlsxAlternateContent{\n\t\t\t\t\tContent: sheet.DecodeAlternateContent.Content,\n\t\t\t\t\tXMLNSMC: SourceRelationshipCompatibility.Value,\n\t\t\t\t}\n\t\t\t}\n\t\t\tsheet.DecodeAlternateContent = nil\n\t\t\t// reusing buffer\n\t\t\t_ = encoder.Encode(sheet)\n\t\t\tf.saveFileList(p.(string), replaceRelationshipsBytes(f.replaceNameSpaceBytes(p.(string), buffer.Bytes())))\n\t\t\t_, ok := f.checked.Load(p.(string))\n\t\t\tif ok {\n\t\t\t\tf.Sheet.Delete(p.(string))\n\t\t\t\tf.checked.Delete(p.(string))\n\t\t\t}\n\t\t\tbuffer.Reset()\n\t\t}\n\t\treturn true\n\t})\n}\n\n// trimRow provides a function to trim empty rows.\nfunc trimRow(sheetData *xlsxSheetData) []xlsxRow {\n\tvar (\n\t\trow xlsxRow\n\t\ti   int\n\t)\n\n\tfor k := 0; k < len(sheetData.Row); k++ {\n\t\trow = sheetData.Row[k]\n\t\tif row = trimCell(row); len(row.C) != 0 || row.hasAttr() {\n\t\t\tsheetData.Row[i] = row\n\t\t\ti++\n\t\t\tcontinue\n\t\t}\n\t\tsheetData.Row = append(sheetData.Row[:k], sheetData.Row[k+1:]...)\n\t\tk--\n\t}\n\treturn sheetData.Row[:i]\n}\n\n// trimCell provides a function to trim blank cells which created by fillColumns.\nfunc trimCell(row xlsxRow) xlsxRow {\n\tcolumn := row.C\n\trowFull := true\n\tfor i := range column {\n\t\trowFull = column[i].hasValue() && rowFull\n\t}\n\tif rowFull {\n\t\treturn row\n\t}\n\ti := 0\n\tfor _, c := range column {\n\t\tif c.hasValue() {\n\t\t\trow.C[i] = c\n\t\t\ti++\n\t\t}\n\t}\n\trow.C = row.C[:i]\n\treturn row\n}\n\n// setContentTypes provides a function to read and update property of contents\n// type of the spreadsheet.\nfunc (f *File) setContentTypes(partName, contentType string) error {\n\tcontent, err := f.contentTypesReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tcontent.mu.Lock()\n\tdefer content.mu.Unlock()\n\tcontent.Overrides = append(content.Overrides, xlsxOverride{\n\t\tPartName:    partName,\n\t\tContentType: contentType,\n\t})\n\treturn err\n}\n\n// setSheet provides a function to update sheet property by given index.\nfunc (f *File) setSheet(index int, name string) {\n\tws := xlsxWorksheet{\n\t\tDimension: &xlsxDimension{Ref: \"A1\"},\n\t\tSheetViews: &xlsxSheetViews{\n\t\t\tSheetView: []xlsxSheetView{{WorkbookViewID: 0}},\n\t\t},\n\t}\n\tsheetXMLPath := \"xl/worksheets/sheet\" + strconv.Itoa(index) + \".xml\"\n\tf.sheetMap[name] = sheetXMLPath\n\tf.Sheet.Store(sheetXMLPath, &ws)\n\tf.xmlAttr.Store(sheetXMLPath, []xml.Attr{NameSpaceSpreadSheet})\n}\n\n// relsWriter provides a function to save relationships after\n// serialize structure.\nfunc (f *File) relsWriter() {\n\tf.Relationships.Range(func(path, rel interface{}) bool {\n\t\tif rel != nil {\n\t\t\toutput, _ := xml.Marshal(rel.(*xlsxRelationships))\n\t\t\tif strings.HasPrefix(path.(string), \"xl/worksheets/sheet/rels/sheet\") {\n\t\t\t\toutput = f.replaceNameSpaceBytes(path.(string), output)\n\t\t\t}\n\t\t\tf.saveFileList(path.(string), replaceRelationshipsBytes(output))\n\t\t}\n\t\treturn true\n\t})\n}\n\n// replaceRelationshipsBytes; Some tools that read spreadsheet files have very\n// strict requirements about the structure of the input XML. This function is\n// a horrible hack to fix that after the XML marshalling is completed.\nfunc replaceRelationshipsBytes(content []byte) []byte {\n\tsourceXmlns := []byte(`xmlns:relationships=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" relationships`)\n\ttargetXmlns := []byte(\"r\")\n\treturn bytesReplace(content, sourceXmlns, targetXmlns, -1)\n}\n\n// SetActiveSheet provides a function to set the default active sheet of the\n// workbook by a given index. Note that the active index is different from the\n// ID returned by function GetSheetMap(). It should be greater than or equal to 0\n// and less than the total worksheet numbers.\nfunc (f *File) SetActiveSheet(index int) {\n\tif index < 0 {\n\t\tindex = 0\n\t}\n\twb, _ := f.workbookReader()\n\tfor activeTab := range wb.Sheets.Sheet {\n\t\tif activeTab == index {\n\t\t\tif wb.BookViews == nil {\n\t\t\t\twb.BookViews = &xlsxBookViews{}\n\t\t\t}\n\t\t\tif len(wb.BookViews.WorkBookView) > 0 {\n\t\t\t\twb.BookViews.WorkBookView[0].ActiveTab = activeTab\n\t\t\t} else {\n\t\t\t\twb.BookViews.WorkBookView = append(wb.BookViews.WorkBookView, xlsxWorkBookView{\n\t\t\t\t\tActiveTab: activeTab,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\tfor idx, name := range f.GetSheetList() {\n\t\tws, err := f.workSheetReader(name)\n\t\tif err != nil {\n\t\t\t// Chartsheet, macrosheet or dialogsheet\n\t\t\treturn\n\t\t}\n\t\tif ws.SheetViews == nil {\n\t\t\tws.SheetViews = &xlsxSheetViews{\n\t\t\t\tSheetView: []xlsxSheetView{{WorkbookViewID: 0}},\n\t\t\t}\n\t\t}\n\t\tif len(ws.SheetViews.SheetView) > 0 {\n\t\t\tws.SheetViews.SheetView[0].TabSelected = false\n\t\t}\n\t\tif index == idx {\n\t\t\tif len(ws.SheetViews.SheetView) > 0 {\n\t\t\t\tws.SheetViews.SheetView[0].TabSelected = true\n\t\t\t} else {\n\t\t\t\tws.SheetViews.SheetView = append(ws.SheetViews.SheetView, xlsxSheetView{\n\t\t\t\t\tTabSelected: true,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n}\n\n// GetActiveSheetIndex provides a function to get active sheet index of the\n// spreadsheet. If not found the active sheet will be return integer 0.\nfunc (f *File) GetActiveSheetIndex() (index int) {\n\tsheetID := f.getActiveSheetID()\n\twb, _ := f.workbookReader()\n\tif wb != nil {\n\t\tfor idx, sheet := range wb.Sheets.Sheet {\n\t\t\tif sheet.SheetID == sheetID {\n\t\t\t\tindex = idx\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// getActiveSheetID provides a function to get active sheet ID of the\n// spreadsheet. If not found the active sheet will be return integer 0.\nfunc (f *File) getActiveSheetID() int {\n\twb, _ := f.workbookReader()\n\tif wb != nil {\n\t\tif wb.BookViews != nil && len(wb.BookViews.WorkBookView) > 0 {\n\t\t\tactiveTab := wb.BookViews.WorkBookView[0].ActiveTab\n\t\t\tif len(wb.Sheets.Sheet) > activeTab && wb.Sheets.Sheet[activeTab].SheetID != 0 {\n\t\t\t\treturn wb.Sheets.Sheet[activeTab].SheetID\n\t\t\t}\n\t\t}\n\t\tif len(wb.Sheets.Sheet) >= 1 {\n\t\t\treturn wb.Sheets.Sheet[0].SheetID\n\t\t}\n\t}\n\treturn 0\n}\n\n// SetSheetName provides a function to set the worksheet name by given source and\n// target worksheet names. Maximum 31 characters are allowed in sheet title and\n// this function only changes the name of the sheet and will not update the\n// sheet name in the formula or reference associated with the cell. So there\n// may be problem formula error or reference missing.\nfunc (f *File) SetSheetName(source, target string) error {\n\tvar err error\n\tif err = checkSheetName(source); err != nil {\n\t\treturn err\n\t}\n\tif err = checkSheetName(target); err != nil {\n\t\treturn err\n\t}\n\tif target == source {\n\t\treturn err\n\t}\n\tf.clearCalcCache()\n\twb, _ := f.workbookReader()\n\tfor k, v := range wb.Sheets.Sheet {\n\t\tif v.Name == source {\n\t\t\twb.Sheets.Sheet[k].Name = target\n\t\t\tf.sheetMap[target] = f.sheetMap[source]\n\t\t\tdelete(f.sheetMap, source)\n\t\t}\n\t}\n\tif wb.DefinedNames == nil {\n\t\treturn err\n\t}\n\tfor i, dn := range wb.DefinedNames.DefinedName {\n\t\twb.DefinedNames.DefinedName[i].Data = adjustRangeSheetName(dn.Data, source, target)\n\t}\n\treturn err\n}\n\n// GetSheetName provides a function to get the sheet name of the workbook by\n// the given sheet index. If the given sheet index is invalid, it will return\n// an empty string.\nfunc (f *File) GetSheetName(index int) (name string) {\n\tfor idx, sheet := range f.GetSheetList() {\n\t\tif idx == index {\n\t\t\tname = sheet\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\n// getSheetID provides a function to get worksheet ID of the spreadsheet by\n// given sheet name. If given worksheet name is invalid, will return an\n// integer type value -1.\nfunc (f *File) getSheetID(sheet string) int {\n\tfor sheetID, name := range f.GetSheetMap() {\n\t\tif strings.EqualFold(name, sheet) {\n\t\t\treturn sheetID\n\t\t}\n\t}\n\treturn -1\n}\n\n// GetSheetIndex provides a function to get a sheet index of the workbook by\n// the given sheet name. If the given sheet name is invalid or sheet doesn't\n// exist, it will return an integer type value -1.\nfunc (f *File) GetSheetIndex(sheet string) (int, error) {\n\tif err := checkSheetName(sheet); err != nil {\n\t\treturn -1, err\n\t}\n\tfor index, name := range f.GetSheetList() {\n\t\tif strings.EqualFold(name, sheet) {\n\t\t\treturn index, nil\n\t\t}\n\t}\n\treturn -1, nil\n}\n\n// GetSheetMap provides a function to get worksheets, chart sheets, dialog\n// sheets ID and name map of the workbook. For example:\n//\n//\tf, err := excelize.OpenFile(\"Book1.xlsx\")\n//\tif err != nil {\n//\t    return\n//\t}\n//\tdefer func() {\n//\t    if err := f.Close(); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t}()\n//\tfor index, name := range f.GetSheetMap() {\n//\t    fmt.Println(index, name)\n//\t}\nfunc (f *File) GetSheetMap() map[int]string {\n\twb, _ := f.workbookReader()\n\tsheetMap := map[int]string{}\n\tif wb != nil {\n\t\tfor _, sheet := range wb.Sheets.Sheet {\n\t\t\tsheetMap[sheet.SheetID] = sheet.Name\n\t\t}\n\t}\n\treturn sheetMap\n}\n\n// GetSheetList provides a function to get worksheets, chart sheets, and\n// dialog sheets name list of the workbook.\nfunc (f *File) GetSheetList() (list []string) {\n\twb, _ := f.workbookReader()\n\tif wb != nil {\n\t\tfor _, sheet := range wb.Sheets.Sheet {\n\t\t\tlist = append(list, sheet.Name)\n\t\t}\n\t}\n\treturn\n}\n\n// getSheetMap provides a function to get worksheet name and XML file path map\n// of the spreadsheet.\nfunc (f *File) getSheetMap() (map[string]string, error) {\n\tmaps := map[string]string{}\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trels, err := f.relsReader(f.getWorkbookRelsPath())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif rels == nil {\n\t\treturn maps, nil\n\t}\n\tfor _, v := range wb.Sheets.Sheet {\n\t\tfor _, rel := range rels.Relationships {\n\t\t\tif rel.ID == v.ID {\n\t\t\t\tsheetXMLPath := f.getWorksheetPath(rel.Target)\n\t\t\t\tif _, ok := f.Pkg.Load(sheetXMLPath); ok {\n\t\t\t\t\tmaps[v.Name] = sheetXMLPath\n\t\t\t\t}\n\t\t\t\tif _, ok := f.tempFiles.Load(sheetXMLPath); ok {\n\t\t\t\t\tmaps[v.Name] = sheetXMLPath\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn maps, nil\n}\n\n// getSheetXMLPath provides a function to get XML file path by given sheet\n// name.\nfunc (f *File) getSheetXMLPath(sheet string) (string, bool) {\n\tvar (\n\t\tname string\n\t\tok   bool\n\t)\n\tfor sheetName, filePath := range f.sheetMap {\n\t\tif strings.EqualFold(sheetName, sheet) {\n\t\t\tname, ok = filePath, true\n\t\t\tbreak\n\t\t}\n\t}\n\treturn name, ok\n}\n\n// SetSheetBackground provides a function to set background picture by given\n// worksheet name and file path. Supported image types: BMP, EMF, EMZ, GIF, ICO,\n// JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ.\nfunc (f *File) SetSheetBackground(sheet, picture string) error {\n\tvar err error\n\t// Check picture exists first.\n\tif _, err = os.Stat(picture); os.IsNotExist(err) {\n\t\treturn err\n\t}\n\tfile, _ := os.ReadFile(filepath.Clean(picture))\n\treturn f.setSheetBackground(sheet, path.Ext(picture), file)\n}\n\n// SetSheetBackgroundFromBytes provides a function to set background picture by\n// given worksheet name, extension name and image data. Supported image types:\n// BMP, EMF, EMZ, GIF, ICO, JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ.\nfunc (f *File) SetSheetBackgroundFromBytes(sheet, extension string, picture []byte) error {\n\tif len(picture) == 0 {\n\t\treturn ErrParameterInvalid\n\t}\n\treturn f.setSheetBackground(sheet, extension, picture)\n}\n\n// setSheetBackground provides a function to set background picture by given\n// worksheet name, file name extension and image data.\nfunc (f *File) setSheetBackground(sheet, extension string, file []byte) error {\n\timageType, ok := supportedImageTypes[strings.ToLower(extension)]\n\tif !ok {\n\t\treturn ErrImgExt\n\t}\n\tname := f.addMedia(file, imageType)\n\tsheetXMLPath, _ := f.getSheetXMLPath(sheet)\n\tsheetRels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(sheetXMLPath, \"xl/worksheets/\") + \".rels\"\n\trID := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, \"xl\", \"..\", 1), \"\")\n\tif err := f.addSheetPicture(sheet, rID); err != nil {\n\t\treturn err\n\t}\n\tf.addSheetNameSpace(sheet, SourceRelationship)\n\treturn f.setContentTypePartImageExtensions()\n}\n\n// DeleteSheet provides a function to delete worksheet in a workbook by given\n// worksheet name. Use this method with caution, which will affect changes in\n// references such as formulas, charts, and so on. If there is any referenced\n// value of the deleted worksheet, it will cause a file error when you open\n// it. This function will be invalid when only one worksheet is left.\nfunc (f *File) DeleteSheet(sheet string) error {\n\tif err := checkSheetName(sheet); err != nil {\n\t\treturn err\n\t}\n\tif idx, _ := f.GetSheetIndex(sheet); f.SheetCount == 1 || idx == -1 {\n\t\treturn nil\n\t}\n\tf.clearCalcCache()\n\twb, _ := f.workbookReader()\n\twbRels, _ := f.relsReader(f.getWorkbookRelsPath())\n\tactiveSheetName := f.GetSheetName(f.GetActiveSheetIndex())\n\tdeleteLocalSheetID, _ := f.GetSheetIndex(sheet)\n\tdeleteAndAdjustDefinedNames(wb, deleteLocalSheetID)\n\n\tfor idx, v := range wb.Sheets.Sheet {\n\t\tif !strings.EqualFold(v.Name, sheet) {\n\t\t\tcontinue\n\t\t}\n\n\t\twb.Sheets.Sheet = append(wb.Sheets.Sheet[:idx], wb.Sheets.Sheet[idx+1:]...)\n\t\tvar sheetXML, rels string\n\t\tif wbRels != nil {\n\t\t\tfor _, rel := range wbRels.Relationships {\n\t\t\t\tif rel.ID == v.ID {\n\t\t\t\t\tsheetXML = f.getWorksheetPath(rel.Target)\n\t\t\t\t\tsheetXMLPath, _ := f.getSheetXMLPath(sheet)\n\t\t\t\t\trels = \"xl/worksheets/_rels/\" + strings.TrimPrefix(sheetXMLPath, \"xl/worksheets/\") + \".rels\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttarget := f.deleteSheetFromWorkbookRels(v.ID)\n\t\t_ = f.removeContentTypesPart(ContentTypeSpreadSheetMLWorksheet, target)\n\t\t_ = f.deleteCalcChain(f.getSheetID(sheet), \"\")\n\t\tdelete(f.sheetMap, v.Name)\n\t\tf.Pkg.Delete(sheetXML)\n\t\tf.Pkg.Delete(rels)\n\t\tf.Relationships.Delete(rels)\n\t\tf.Sheet.Delete(sheetXML)\n\t\tf.xmlAttr.Delete(sheetXML)\n\t\tf.SheetCount--\n\t}\n\tindex, err := f.GetSheetIndex(activeSheetName)\n\tf.SetActiveSheet(index)\n\treturn err\n}\n\n// MoveSheet moves a sheet to a specified position in the workbook. The function\n// moves the source sheet before the target sheet. After moving, other sheets\n// will be shifted to the left or right. If the sheet is already at the target\n// position, the function will not perform any action. Not that this function\n// will be ungroup all sheets after moving. For example, move Sheet2 before\n// Sheet1:\n//\n//\terr := f.MoveSheet(\"Sheet2\", \"Sheet1\")\nfunc (f *File) MoveSheet(source, target string) error {\n\tif strings.EqualFold(source, target) {\n\t\treturn nil\n\t}\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tsourceIdx, err := f.GetSheetIndex(source)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttargetIdx, err := f.GetSheetIndex(target)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif sourceIdx < 0 {\n\t\treturn ErrSheetNotExist{source}\n\t}\n\tif targetIdx < 0 {\n\t\treturn ErrSheetNotExist{target}\n\t}\n\t_ = f.UngroupSheets()\n\tactiveSheetName := f.GetSheetName(f.GetActiveSheetIndex())\n\tsourceSheet := wb.Sheets.Sheet[sourceIdx]\n\twb.Sheets.Sheet = append(wb.Sheets.Sheet[:sourceIdx], wb.Sheets.Sheet[sourceIdx+1:]...)\n\tif targetIdx > sourceIdx {\n\t\ttargetIdx--\n\t}\n\twb.Sheets.Sheet = append(wb.Sheets.Sheet[:targetIdx], append([]xlsxSheet{sourceSheet}, wb.Sheets.Sheet[targetIdx:]...)...)\n\tactiveSheetIdx, _ := f.GetSheetIndex(activeSheetName)\n\tf.SetActiveSheet(activeSheetIdx)\n\treturn err\n}\n\n// deleteAndAdjustDefinedNames delete and adjust defined name in the workbook\n// by given worksheet ID.\nfunc deleteAndAdjustDefinedNames(wb *xlsxWorkbook, deleteLocalSheetID int) {\n\tif wb == nil || wb.DefinedNames == nil {\n\t\treturn\n\t}\n\tfor idx := 0; idx < len(wb.DefinedNames.DefinedName); idx++ {\n\t\tdn := wb.DefinedNames.DefinedName[idx]\n\t\tif dn.LocalSheetID != nil {\n\t\t\tlocalSheetID := *dn.LocalSheetID\n\t\t\tif localSheetID == deleteLocalSheetID {\n\t\t\t\twb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName[:idx], wb.DefinedNames.DefinedName[idx+1:]...)\n\t\t\t\tidx--\n\t\t\t} else if localSheetID > deleteLocalSheetID {\n\t\t\t\twb.DefinedNames.DefinedName[idx].LocalSheetID = intPtr(*dn.LocalSheetID - 1)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// deleteSheetFromWorkbookRels provides a function to remove worksheet\n// relationships by given relationships ID in the file workbook.xml.rels.\nfunc (f *File) deleteSheetFromWorkbookRels(rID string) string {\n\trels, _ := f.relsReader(f.getWorkbookRelsPath())\n\trels.mu.Lock()\n\tdefer rels.mu.Unlock()\n\tfor k, v := range rels.Relationships {\n\t\tif v.ID == rID {\n\t\t\trels.Relationships = append(rels.Relationships[:k], rels.Relationships[k+1:]...)\n\t\t\treturn v.Target\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// deleteSheetRelationships provides a function to delete relationships in\n// xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and\n// relationship index.\nfunc (f *File) deleteSheetRelationships(sheet, rID string) {\n\tname, ok := f.getSheetXMLPath(sheet)\n\tif !ok {\n\t\tname = strings.ToLower(sheet) + \".xml\"\n\t}\n\trels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(name, \"xl/worksheets/\") + \".rels\"\n\tsheetRels, _ := f.relsReader(rels)\n\tif sheetRels == nil {\n\t\tsheetRels = &xlsxRelationships{}\n\t}\n\tsheetRels.mu.Lock()\n\tdefer sheetRels.mu.Unlock()\n\tfor k, v := range sheetRels.Relationships {\n\t\tif v.ID == rID {\n\t\t\tsheetRels.Relationships = append(sheetRels.Relationships[:k], sheetRels.Relationships[k+1:]...)\n\t\t}\n\t}\n\tf.Relationships.Store(rels, sheetRels)\n}\n\n// getSheetRelationshipsTargetByID provides a function to get Target attribute\n// value in xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and\n// relationship index.\nfunc (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {\n\tname, ok := f.getSheetXMLPath(sheet)\n\tif !ok {\n\t\tname = strings.ToLower(sheet) + \".xml\"\n\t}\n\trels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(name, \"xl/worksheets/\") + \".rels\"\n\tsheetRels, _ := f.relsReader(rels)\n\tif sheetRels == nil {\n\t\tsheetRels = &xlsxRelationships{}\n\t}\n\tsheetRels.mu.Lock()\n\tdefer sheetRels.mu.Unlock()\n\tfor _, v := range sheetRels.Relationships {\n\t\tif v.ID == rID {\n\t\t\treturn v.Target\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// CopySheet provides a function to duplicate a worksheet by gave source and\n// target worksheet index. Note that currently doesn't support duplicate\n// workbooks that contain tables, charts or pictures. For Example:\n//\n//\t// Sheet1 already exists...\n//\tindex, err := f.NewSheet(\"Sheet2\")\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\terr = f.CopySheet(0, index)\nfunc (f *File) CopySheet(from, to int) error {\n\tif from < 0 || to < 0 || from == to || f.GetSheetName(from) == \"\" || f.GetSheetName(to) == \"\" {\n\t\treturn ErrSheetIdx\n\t}\n\treturn f.copySheet(from, to)\n}\n\n// copySheet provides a function to duplicate a worksheet by gave source and\n// target worksheet name.\nfunc (f *File) copySheet(from, to int) error {\n\tfromSheet := f.GetSheetName(from)\n\tsheet, err := f.workSheetReader(fromSheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.clearCalcCache()\n\tworksheet := &xlsxWorksheet{}\n\terr = deepcopy.Copy(worksheet, sheet)\n\ttoSheetID := strconv.Itoa(f.getSheetID(f.GetSheetName(to)))\n\tsheetXMLPath := \"xl/worksheets/sheet\" + toSheetID + \".xml\"\n\tif len(worksheet.SheetViews.SheetView) > 0 {\n\t\tworksheet.SheetViews.SheetView[0].TabSelected = false\n\t}\n\tworksheet.Drawing = nil\n\tworksheet.TableParts = nil\n\tworksheet.PageSetUp = nil\n\tf.Sheet.Store(sheetXMLPath, worksheet)\n\ttoRels := \"xl/worksheets/_rels/sheet\" + toSheetID + \".xml.rels\"\n\tfromRels := \"xl/worksheets/_rels/sheet\" + strconv.Itoa(f.getSheetID(fromSheet)) + \".xml.rels\"\n\tif rels, ok := f.Pkg.Load(fromRels); ok && rels != nil {\n\t\tf.Pkg.Store(toRels, rels.([]byte))\n\t}\n\tfromSheetXMLPath, _ := f.getSheetXMLPath(fromSheet)\n\tfromSheetAttr, _ := f.xmlAttr.Load(fromSheetXMLPath)\n\tf.xmlAttr.Store(sheetXMLPath, fromSheetAttr)\n\treturn err\n}\n\n// getSheetState returns sheet visible enumeration by given hidden status.\nfunc getSheetState(visible bool, veryHidden []bool) string {\n\tstate := \"hidden\"\n\tif !visible && len(veryHidden) > 0 && veryHidden[0] {\n\t\tstate = \"veryHidden\"\n\t}\n\treturn state\n}\n\n// SetSheetVisible provides a function to set worksheet visible by given\n// worksheet name. A workbook must contain at least one visible worksheet. If\n// the given worksheet has been activated, this setting will be invalidated.\n// The third optional veryHidden parameter only works when visible was false.\n//\n// For example, hide Sheet1:\n//\n//\terr := f.SetSheetVisible(\"Sheet1\", false)\nfunc (f *File) SetSheetVisible(sheet string, visible bool, veryHidden ...bool) error {\n\tif err := checkSheetName(sheet); err != nil {\n\t\treturn err\n\t}\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif visible {\n\t\tfor k, v := range wb.Sheets.Sheet {\n\t\t\tif strings.EqualFold(v.Name, sheet) {\n\t\t\t\twb.Sheets.Sheet[k].State = \"\"\n\t\t\t}\n\t\t}\n\t\treturn err\n\t}\n\tcount, state := 0, getSheetState(visible, veryHidden)\n\tfor _, v := range wb.Sheets.Sheet {\n\t\tif v.State != state {\n\t\t\tcount++\n\t\t}\n\t}\n\tfor k, v := range wb.Sheets.Sheet {\n\t\tws, err := f.workSheetReader(v.Name)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttabSelected := false\n\t\tif ws.SheetViews == nil {\n\t\t\tws.SheetViews = &xlsxSheetViews{\n\t\t\t\tSheetView: []xlsxSheetView{{WorkbookViewID: 0}},\n\t\t\t}\n\t\t}\n\t\tif len(ws.SheetViews.SheetView) > 0 {\n\t\t\ttabSelected = ws.SheetViews.SheetView[0].TabSelected\n\t\t}\n\t\tif strings.EqualFold(v.Name, sheet) && count > 1 && !tabSelected {\n\t\t\twb.Sheets.Sheet[k].State = state\n\t\t}\n\t}\n\treturn err\n}\n\n// setPanes set create freeze panes and split panes by given options.\nfunc (ws *xlsxWorksheet) setPanes(panes *Panes) error {\n\tif panes == nil {\n\t\treturn ErrParameterInvalid\n\t}\n\tp := &xlsxPane{\n\t\tActivePane:  panes.ActivePane,\n\t\tTopLeftCell: panes.TopLeftCell,\n\t\tXSplit:      float64(panes.XSplit),\n\t\tYSplit:      float64(panes.YSplit),\n\t}\n\tif panes.Freeze {\n\t\tp.State = \"frozen\"\n\t}\n\tif ws.SheetViews == nil {\n\t\tws.SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{{}}}\n\t}\n\tws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Pane = p\n\tif !(panes.Freeze) && !(panes.Split) {\n\t\tif len(ws.SheetViews.SheetView) > 0 {\n\t\t\tws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Pane = nil\n\t\t}\n\t}\n\tvar s []*xlsxSelection\n\tfor _, p := range panes.Selection {\n\t\ts = append(s, &xlsxSelection{\n\t\t\tActiveCell: p.ActiveCell,\n\t\t\tPane:       p.Pane,\n\t\t\tSQRef:      p.SQRef,\n\t\t})\n\t}\n\tws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Selection = s\n\treturn nil\n}\n\n// SetPanes provides a function to create and remove freeze panes and split panes\n// by given worksheet name and panes options.\n//\n// ActivePane defines the pane that is active. The possible values for this\n// attribute are defined in the following table:\n//\n//\t Enumeration Value               | Description\n//\t---------------------------------+-------------------------------------------------------------\n//\t bottomLeft (Bottom Left Pane)   | Bottom left pane, when both vertical and horizontal\n//\t                                 | splits are applied.\n//\t                                 |\n//\t                                 | This value is also used when only a horizontal split has\n//\t                                 | been applied, dividing the pane into upper and lower\n//\t                                 | regions. In that case, this value specifies the bottom\n//\t                                 | pane.\n//\t                                 |\n//\t bottomRight (Bottom Right Pane) | Bottom right pane, when both vertical and horizontal\n//\t                                 | splits are applied.\n//\t                                 |\n//\t topLeft (Top Left Pane)         | Top left pane, when both vertical and horizontal splits\n//\t                                 | are applied.\n//\t                                 |\n//\t                                 | This value is also used when only a horizontal split has\n//\t                                 | been applied, dividing the pane into upper and lower\n//\t                                 | regions. In that case, this value specifies the top pane.\n//\t                                 |\n//\t                                 | This value is also used when only a vertical split has\n//\t                                 | been applied, dividing the pane into right and left\n//\t                                 | regions. In that case, this value specifies the left pane\n//\t                                 |\n//\t topRight (Top Right Pane)       | Top right pane, when both vertical and horizontal\n//\t                                 | splits are applied.\n//\t                                 |\n//\t                                 | This value is also used when only a vertical split has\n//\t                                 | been applied, dividing the pane into right and left\n//\t                                 | regions. In that case, this value specifies the right\n//\t                                 | pane.\n//\n// Pane state type is restricted to the values supported currently listed in the following table:\n//\n//\t Enumeration Value               | Description\n//\t---------------------------------+-------------------------------------------------------------\n//\t frozen (Frozen)                 | Panes are frozen, but were not split being frozen. In\n//\t                                 | this state, when the panes are unfrozen again, a single\n//\t                                 | pane results, with no split.\n//\t                                 |\n//\t                                 | In this state, the split bars are not adjustable.\n//\t                                 |\n//\t split (Split)                   | Panes are split, but not frozen. In this state, the split\n//\t                                 | bars are adjustable by the user.\n//\n// XSplit (Horizontal Split Position): Horizontal position of the split, in\n// 1/20th of a point; 0 (zero) if none. If the pane is frozen, this value\n// indicates the number of columns visible in the top pane.\n//\n// YSplit (Vertical Split Position): Vertical position of the split, in 1/20th\n// of a point; 0 (zero) if none. If the pane is frozen, this value indicates the\n// number of rows visible in the left pane. The possible values for this\n// attribute are defined by the W3C XML Schema double datatype.\n//\n// TopLeftCell: Location of the top left visible cell in the bottom right pane\n// (when in Left-To-Right mode).\n//\n// SQRef (Sequence of References): Range of the selection. Can be non-contiguous\n// set of ranges.\n//\n// An example of how to freeze column A in the Sheet1 and set the active cell on\n// Sheet1!K16:\n//\n//\terr := f.SetPanes(\"Sheet1\", &excelize.Panes{\n//\t    Freeze:      true,\n//\t    Split:       false,\n//\t    XSplit:      1,\n//\t    YSplit:      0,\n//\t    TopLeftCell: \"B1\",\n//\t    ActivePane:  \"topRight\",\n//\t    Selection: []excelize.Selection{\n//\t        {SQRef: \"K16\", ActiveCell: \"K16\", Pane: \"topRight\"},\n//\t    },\n//\t})\n//\n// An example of how to freeze rows 1 to 9 in the Sheet1 and set the active cell\n// ranges on Sheet1!A11:XFD11:\n//\n//\terr := f.SetPanes(\"Sheet1\", &excelize.Panes{\n//\t    Freeze:      true,\n//\t    Split:       false,\n//\t    XSplit:      0,\n//\t    YSplit:      9,\n//\t    TopLeftCell: \"A34\",\n//\t    ActivePane:  \"bottomLeft\",\n//\t    Selection: []excelize.Selection{\n//\t        {SQRef: \"A11:XFD11\", ActiveCell: \"A11\", Pane: \"bottomLeft\"},\n//\t    },\n//\t})\n//\n// An example of how to create split panes in the Sheet1 and set the active cell\n// on Sheet1!J60:\n//\n//\terr := f.SetPanes(\"Sheet1\", &excelize.Panes{\n//\t    Freeze:      false,\n//\t    Split:       true,\n//\t    XSplit:      3270,\n//\t    YSplit:      1800,\n//\t    TopLeftCell: \"N57\",\n//\t    ActivePane:  \"bottomLeft\",\n//\t    Selection: []excelize.Selection{\n//\t        {SQRef: \"I36\", ActiveCell: \"I36\"},\n//\t        {SQRef: \"G33\", ActiveCell: \"G33\", Pane: \"topRight\"},\n//\t        {SQRef: \"J60\", ActiveCell: \"J60\", Pane: \"bottomLeft\"},\n//\t        {SQRef: \"O60\", ActiveCell: \"O60\", Pane: \"bottomRight\"},\n//\t    },\n//\t})\n//\n// An example of how to unfreeze and remove all panes on Sheet1:\n//\n//\terr := f.SetPanes(\"Sheet1\", &excelize.Panes{Freeze: false, Split: false})\nfunc (f *File) SetPanes(sheet string, panes *Panes) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn ws.setPanes(panes)\n}\n\n// getPanes returns freeze panes, split panes, and views of the worksheet.\nfunc (ws *xlsxWorksheet) getPanes() Panes {\n\tvar (\n\t\tpanes   Panes\n\t\tsection []Selection\n\t)\n\tif ws.SheetViews == nil || len(ws.SheetViews.SheetView) < 1 {\n\t\treturn panes\n\t}\n\tsw := ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1]\n\tfor _, s := range sw.Selection {\n\t\tif s != nil {\n\t\t\tsection = append(section, Selection{\n\t\t\t\tSQRef:      s.SQRef,\n\t\t\t\tActiveCell: s.ActiveCell,\n\t\t\t\tPane:       s.Pane,\n\t\t\t})\n\t\t}\n\t}\n\tpanes.Selection = section\n\tif sw.Pane == nil {\n\t\treturn panes\n\t}\n\tpanes.ActivePane = sw.Pane.ActivePane\n\tif sw.Pane.State == \"frozen\" {\n\t\tpanes.Freeze = true\n\t}\n\tpanes.TopLeftCell = sw.Pane.TopLeftCell\n\tpanes.XSplit = int(sw.Pane.XSplit)\n\tpanes.YSplit = int(sw.Pane.YSplit)\n\treturn panes\n}\n\n// GetPanes provides a function to get freeze panes, split panes, and worksheet\n// views by given worksheet name.\nfunc (f *File) GetPanes(sheet string) (Panes, error) {\n\tvar panes Panes\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn panes, err\n\t}\n\treturn ws.getPanes(), err\n}\n\n// GetSheetVisible provides a function to get worksheet visible by given worksheet\n// name. For example, get visible state of Sheet1:\n//\n//\tvisible, err := f.GetSheetVisible(\"Sheet1\")\nfunc (f *File) GetSheetVisible(sheet string) (bool, error) {\n\tvar visible bool\n\tif err := checkSheetName(sheet); err != nil {\n\t\treturn visible, err\n\t}\n\twb, _ := f.workbookReader()\n\tfor k, v := range wb.Sheets.Sheet {\n\t\tif strings.EqualFold(v.Name, sheet) {\n\t\t\tif wb.Sheets.Sheet[k].State == \"\" || wb.Sheets.Sheet[k].State == \"visible\" {\n\t\t\t\tvisible = true\n\t\t\t}\n\t\t}\n\t}\n\treturn visible, nil\n}\n\n// SearchSheet provides a function to get cell reference by given worksheet name,\n// cell value, and regular expression. The function doesn't support searching\n// on the calculated result, formatted numbers and conditional lookup\n// currently. If it is a merged cell, it will return the cell reference of the\n// upper left cell of the merged range reference.\n//\n// An example of search the cell reference of the value of \"100\" on Sheet1:\n//\n//\tresult, err := f.SearchSheet(\"Sheet1\", \"100\")\n//\n// An example of search the cell reference where the numerical value in the range\n// of \"0-9\" of Sheet1 is described:\n//\n//\tresult, err := f.SearchSheet(\"Sheet1\", \"[0-9]\", true)\nfunc (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) {\n\tvar (\n\t\tregSearch bool\n\t\tresult    []string\n\t)\n\tif err := checkSheetName(sheet); err != nil {\n\t\treturn result, err\n\t}\n\tfor _, r := range reg {\n\t\tregSearch = r\n\t}\n\tname, ok := f.getSheetXMLPath(sheet)\n\tif !ok {\n\t\treturn result, ErrSheetNotExist{sheet}\n\t}\n\tif ws, ok := f.Sheet.Load(name); ok && ws != nil {\n\t\t// Flush data\n\t\toutput, _ := xml.Marshal(ws.(*xlsxWorksheet))\n\t\tf.saveFileList(name, f.replaceNameSpaceBytes(name, output))\n\t}\n\treturn f.searchSheet(name, value, regSearch)\n}\n\n// searchSheet provides a function to get cell reference by given worksheet\n// name, cell value, and regular expression.\nfunc (f *File) searchSheet(name, value string, regSearch bool) (result []string, err error) {\n\tvar (\n\t\tcellName, inElement string\n\t\tcellCol, row        int\n\t\tsst                 *xlsxSST\n\t)\n\n\tif sst, err = f.sharedStringsReader(); err != nil {\n\t\treturn\n\t}\n\tregex := regexp.MustCompile(value)\n\tdecoder := f.xmlNewDecoder(bytes.NewReader(f.readBytes(name)))\n\tfor {\n\t\tvar token xml.Token\n\t\ttoken, err = decoder.Token()\n\t\tif err != nil || token == nil {\n\t\t\tif err == io.EOF {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tswitch xmlElement := token.(type) {\n\t\tcase xml.StartElement:\n\t\t\tinElement = xmlElement.Name.Local\n\t\t\tif inElement == \"row\" {\n\t\t\t\trow, err = attrValToInt(\"r\", xmlElement.Attr)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tif inElement == \"c\" {\n\t\t\t\tcolCell := xlsxC{}\n\t\t\t\t_ = decoder.DecodeElement(&colCell, &xmlElement)\n\t\t\t\tval, _ := colCell.getValueFrom(f, sst, false)\n\t\t\t\tif regSearch {\n\t\t\t\t\tif !regex.MatchString(val) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif val != value {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcellCol, _, err = CellNameToCoordinates(colCell.R)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn result, err\n\t\t\t\t}\n\t\t\t\tcellName, err = CoordinatesToCellName(cellCol, row)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn result, err\n\t\t\t\t}\n\t\t\t\tresult = append(result, cellName)\n\t\t\t}\n\t\tdefault:\n\t\t}\n\t}\n\treturn\n}\n\n// attrValToInt provides a function to convert the local names to an integer\n// by given XML attributes and specified names.\nfunc attrValToInt(name string, attrs []xml.Attr) (val int, err error) {\n\tfor _, attr := range attrs {\n\t\tif attr.Name.Local == name {\n\t\t\tval, err = strconv.Atoi(attr.Value)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// attrValToFloat provides a function to convert the local names to a float64\n// by given XML attributes and specified names.\nfunc attrValToFloat(name string, attrs []xml.Attr) (val float64, err error) {\n\tfor _, attr := range attrs {\n\t\tif attr.Name.Local == name {\n\t\t\tval, err = strconv.ParseFloat(attr.Value, 64)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// attrValToBool provides a function to convert the local names to a boolean\n// by given XML attributes and specified names.\nfunc attrValToBool(name string, attrs []xml.Attr) (val bool, err error) {\n\tfor _, attr := range attrs {\n\t\tif attr.Name.Local == name {\n\t\t\tval, err = strconv.ParseBool(attr.Value)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// SetHeaderFooter provides a function to set headers and footers by given\n// worksheet name and the control characters.\n//\n// Headers and footers are specified using the following settings fields:\n//\n//\t Fields           | Description\n//\t------------------+-----------------------------------------------------------\n//\t AlignWithMargins | Align header footer margins with page margins\n//\t DifferentFirst   | Different first-page header and footer indicator\n//\t DifferentOddEven | Different odd and even page headers and footers indicator\n//\t ScaleWithDoc     | Scale header and footer with document scaling\n//\t OddFooter        | Odd Page Footer, or primary Page Footer if 'DifferentOddEven' is 'false'\n//\t OddHeader        | Odd Header, or primary Page Header if 'DifferentOddEven' is 'false'\n//\t EvenFooter       | Even Page Footer\n//\t EvenHeader       | Even Page Header\n//\t FirstFooter      | First Page Footer\n//\t FirstHeader      | First Page Header\n//\n// The following formatting codes can be used in 6 string type fields:\n// OddHeader, OddFooter, EvenHeader, EvenFooter, FirstFooter, FirstHeader\n//\n//\t Formatting Code        | Description\n//\t------------------------+-------------------------------------------------------------------------\n//\t &&                     | The character \"&\"\n//\t                        |\n//\t &font-size             | Size of the text font, where font-size is a decimal font size in points\n//\t                        |\n//\t &\"font name,font type\" | A text font-name string, font name, and a text font-type string,\n//\t                        | font type\n//\t                        |\n//\t &\"-,Regular\"           | Regular text format. Toggles bold and italic modes to off\n//\t                        |\n//\t &A                     | Current worksheet's tab name\n//\t                        |\n//\t &B or &\"-,Bold\"        | Bold text format, from off to on, or vice versa. The default mode is off\n//\t                        |\n//\t &D                     | Current date\n//\t                        |\n//\t &C                     | Center section\n//\t                        |\n//\t &E                     | Double-underline text format\n//\t                        |\n//\t &F                     | Current workbook's file name\n//\t                        |\n//\t &G                     | Drawing object as background (Use AddHeaderFooterImage)\n//\t                        |\n//\t &H                     | Shadow text format\n//\t                        |\n//\t &I or &\"-,Italic\"      | Italic text format\n//\t                        |\n//\t &K                     | Text font color\n//\t                        |\n//\t                        | An RGB Color is specified as RRGGBB\n//\t                        |\n//\t                        | A Theme Color is specified as TTSNNN where TT is the theme color Id,\n//\t                        | S is either \"+\" or \"-\" of the tint/shade value, and NNN is the\n//\t                        | tint/shade value\n//\t                        |\n//\t &L                     | Left section\n//\t                        |\n//\t &N                     | Total number of pages\n//\t                        |\n//\t &O                     | Outline text format\n//\t                        |\n//\t &P[[+|-]n]             | Without the optional suffix, the current page number in decimal\n//\t                        |\n//\t &R                     | Right section\n//\t                        |\n//\t &S                     | Strike through text format\n//\t                        |\n//\t &T                     | Current time\n//\t                        |\n//\t &U                     | Single-underline text format. If double-underline mode is on, the next\n//\t                        | occurrence in a section specifier toggles double-underline mode to off;\n//\t                        | otherwise, it toggles single-underline mode, from off to on, or vice\n//\t                        | versa. The default mode is off\n//\t                        |\n//\t &X                     | Superscript text format\n//\t                        |\n//\t &Y                     | Subscript text format\n//\t                        |\n//\t &Z                     | Current workbook's file path\n//\n// For example:\n//\n//\terr := f.SetHeaderFooter(\"Sheet1\", &excelize.HeaderFooterOptions{\n//\t    DifferentFirst:   true,\n//\t    DifferentOddEven: true,\n//\t    OddHeader:        \"&R&P\",\n//\t    OddFooter:        \"&C&F\",\n//\t    EvenHeader:       \"&L&P\",\n//\t    EvenFooter:       \"&L&D&R&T\",\n//\t    FirstHeader:      `&CCenter &\"-,Bold\"Bold&\"-,Regular\"HeaderU+000A&D`,\n//\t})\n//\n// This example shows:\n//\n// - The first page has its own header and footer\n//\n// - Odd and even-numbered pages have different headers and footers\n//\n// - Current page number in the right section of odd-page headers\n//\n// - Current workbook's file name in the center section of odd-page footers\n//\n// - Current page number in the left section of even-page headers\n//\n// - Current date in the left section and the current time in the right section\n// of even-page footers\n//\n// - The text \"Center Bold Header\" on the first line of the center section of\n// the first page, and the date on the second line of the center section of\n// that same page\n//\n// - No footer on the first page\nfunc (f *File) SetHeaderFooter(sheet string, opts *HeaderFooterOptions) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif opts == nil {\n\t\tws.HeaderFooter = nil\n\t\treturn err\n\t}\n\n\tv := reflect.ValueOf(*opts)\n\t// Check 6 string type fields: OddHeader, OddFooter, EvenHeader, EvenFooter,\n\t// FirstFooter, FirstHeader\n\tfor i := 4; i < v.NumField()-1; i++ {\n\t\tif countUTF16String(v.Field(i).String()) > MaxFieldLength {\n\t\t\treturn newFieldLengthError(v.Type().Field(i).Name)\n\t\t}\n\t}\n\tws.HeaderFooter = &xlsxHeaderFooter{\n\t\tAlignWithMargins: opts.AlignWithMargins,\n\t\tDifferentFirst:   opts.DifferentFirst,\n\t\tDifferentOddEven: opts.DifferentOddEven,\n\t\tScaleWithDoc:     opts.ScaleWithDoc,\n\t\tOddHeader:        opts.OddHeader,\n\t\tOddFooter:        opts.OddFooter,\n\t\tEvenHeader:       opts.EvenHeader,\n\t\tEvenFooter:       opts.EvenFooter,\n\t\tFirstFooter:      opts.FirstFooter,\n\t\tFirstHeader:      opts.FirstHeader,\n\t}\n\treturn err\n}\n\n// GetHeaderFooter provides a function to get worksheet header and footer by\n// given worksheet name.\nfunc (f *File) GetHeaderFooter(sheet string) (*HeaderFooterOptions, error) {\n\tvar opts *HeaderFooterOptions\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\tif ws.HeaderFooter == nil {\n\t\treturn opts, err\n\t}\n\topts = &HeaderFooterOptions{\n\t\tAlignWithMargins: ws.HeaderFooter.AlignWithMargins,\n\t\tDifferentFirst:   ws.HeaderFooter.DifferentFirst,\n\t\tDifferentOddEven: ws.HeaderFooter.DifferentOddEven,\n\t\tScaleWithDoc:     ws.HeaderFooter.ScaleWithDoc,\n\t\tOddHeader:        ws.HeaderFooter.OddHeader,\n\t\tOddFooter:        ws.HeaderFooter.OddFooter,\n\t\tEvenHeader:       ws.HeaderFooter.EvenHeader,\n\t\tEvenFooter:       ws.HeaderFooter.EvenFooter,\n\t\tFirstHeader:      ws.HeaderFooter.FirstHeader,\n\t\tFirstFooter:      ws.HeaderFooter.FirstFooter,\n\t}\n\treturn opts, err\n}\n\n// ProtectSheet provides a function to prevent other users from accidentally or\n// deliberately changing, moving, or deleting data in a worksheet. The\n// optional field AlgorithmName specified hash algorithm, support XOR, MD4,\n// MD5, SHA-1, SHA2-56, SHA-384, and SHA-512 currently, if no hash algorithm\n// specified, will be using the XOR algorithm as default. For example, protect\n// Sheet1 with protection settings:\n//\n//\terr := f.ProtectSheet(\"Sheet1\", &excelize.SheetProtectionOptions{\n//\t    AlgorithmName:       \"SHA-512\",\n//\t    Password:            \"password\",\n//\t    SelectLockedCells:   true,\n//\t    SelectUnlockedCells: true,\n//\t    EditScenarios:       true,\n//\t})\nfunc (f *File) ProtectSheet(sheet string, opts *SheetProtectionOptions) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif opts == nil {\n\t\treturn ErrParameterInvalid\n\t}\n\tws.SheetProtection = &xlsxSheetProtection{\n\t\tAutoFilter:          !opts.AutoFilter,\n\t\tDeleteColumns:       !opts.DeleteColumns,\n\t\tDeleteRows:          !opts.DeleteRows,\n\t\tFormatCells:         !opts.FormatCells,\n\t\tFormatColumns:       !opts.FormatColumns,\n\t\tFormatRows:          !opts.FormatRows,\n\t\tInsertColumns:       !opts.InsertColumns,\n\t\tInsertHyperlinks:    !opts.InsertHyperlinks,\n\t\tInsertRows:          !opts.InsertRows,\n\t\tObjects:             !opts.EditObjects,\n\t\tPivotTables:         !opts.PivotTables,\n\t\tScenarios:           !opts.EditScenarios,\n\t\tSelectLockedCells:   !opts.SelectLockedCells,\n\t\tSelectUnlockedCells: !opts.SelectUnlockedCells,\n\t\tSheet:               true,\n\t\tSort:                !opts.Sort,\n\t}\n\tif opts.Password != \"\" {\n\t\tif opts.AlgorithmName == \"\" {\n\t\t\tws.SheetProtection.Password = genSheetPasswd(opts.Password)\n\t\t\treturn err\n\t\t}\n\t\thashValue, saltValue, err := genISOPasswdHash(opts.Password, opts.AlgorithmName, \"\", int(sheetProtectionSpinCount))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tws.SheetProtection.Password = \"\"\n\t\tws.SheetProtection.AlgorithmName = opts.AlgorithmName\n\t\tws.SheetProtection.SaltValue = saltValue\n\t\tws.SheetProtection.HashValue = hashValue\n\t\tws.SheetProtection.SpinCount = int(sheetProtectionSpinCount)\n\t}\n\treturn err\n}\n\n// UnprotectSheet provides a function to remove protection for a sheet,\n// specified the second optional password parameter to remove sheet\n// protection with password verification.\nfunc (f *File) UnprotectSheet(sheet string, password ...string) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// password verification\n\tif len(password) > 0 {\n\t\tif ws.SheetProtection == nil {\n\t\t\treturn ErrUnprotectSheet\n\t\t}\n\t\tif ws.SheetProtection.AlgorithmName == \"\" && ws.SheetProtection.Password != genSheetPasswd(password[0]) {\n\t\t\treturn ErrUnprotectSheetPassword\n\t\t}\n\t\tif ws.SheetProtection.AlgorithmName != \"\" {\n\t\t\t// check with given salt value\n\t\t\thashValue, _, err := genISOPasswdHash(password[0], ws.SheetProtection.AlgorithmName, ws.SheetProtection.SaltValue, ws.SheetProtection.SpinCount)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif ws.SheetProtection.HashValue != hashValue {\n\t\t\t\treturn ErrUnprotectSheetPassword\n\t\t\t}\n\t\t}\n\t}\n\tws.SheetProtection = nil\n\treturn err\n}\n\n// GetSheetProtection provides a function to get worksheet protection settings\n// by given worksheet name. Note that the password in the returned will always\n// be empty.\nfunc (f *File) GetSheetProtection(sheet string) (SheetProtectionOptions, error) {\n\tvar opts SheetProtectionOptions\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\tif ws.SheetProtection != nil {\n\t\topts.AlgorithmName = ws.SheetProtection.AlgorithmName\n\t\topts.AutoFilter = !ws.SheetProtection.AutoFilter\n\t\topts.DeleteColumns = !ws.SheetProtection.DeleteColumns\n\t\topts.DeleteRows = !ws.SheetProtection.DeleteRows\n\t\topts.EditObjects = !ws.SheetProtection.Objects\n\t\topts.EditScenarios = !ws.SheetProtection.Scenarios\n\t\topts.FormatCells = !ws.SheetProtection.FormatCells\n\t\topts.FormatColumns = !ws.SheetProtection.FormatColumns\n\t\topts.FormatRows = !ws.SheetProtection.FormatRows\n\t\topts.InsertColumns = !ws.SheetProtection.InsertColumns\n\t\topts.InsertHyperlinks = !ws.SheetProtection.InsertHyperlinks\n\t\topts.InsertRows = !ws.SheetProtection.InsertRows\n\t\topts.PivotTables = !ws.SheetProtection.PivotTables\n\t\topts.SelectLockedCells = !ws.SheetProtection.SelectLockedCells\n\t\topts.SelectUnlockedCells = !ws.SheetProtection.SelectUnlockedCells\n\t\topts.Sort = !ws.SheetProtection.Sort\n\t}\n\treturn opts, err\n}\n\n// checkSheetName check whether there are illegal characters in the sheet name.\n// 1. Confirm that the sheet name is not empty\n// 2. Make sure to enter a name with no more than 31 characters\n// 3. Make sure the first or last character of the name cannot be a single quote\n// 4. Verify that the following characters are not included in the name :\\/?*[]\nfunc checkSheetName(name string) error {\n\tif name == \"\" {\n\t\treturn ErrSheetNameBlank\n\t}\n\tif countUTF16String(name) > MaxSheetNameLength {\n\t\treturn ErrSheetNameLength\n\t}\n\tif strings.HasPrefix(name, \"'\") || strings.HasSuffix(name, \"'\") {\n\t\treturn ErrSheetNameSingleQuote\n\t}\n\tif strings.ContainsAny(name, \":\\\\/?*[]\") {\n\t\treturn ErrSheetNameInvalid\n\t}\n\treturn nil\n}\n\n// SetPageLayout provides a function to sets worksheet page layout.\n//\n// The following shows the paper size sorted by excelize index number:\n//\n//\t Index | Paper Size\n//\t-------+-----------------------------------------------\n//\t   1   | Letter paper (8.5 in. by 11 in.)\n//\t   2   | Letter small paper (8.5 in. by 11 in.)\n//\t   3   | Tabloid paper (11 in. by 17 in.)\n//\t   4   | Ledger paper (17 in. by 11 in.)\n//\t   5   | Legal paper (8.5 in. by 14 in.)\n//\t   6   | Statement paper (5.5 in. by 8.5 in.)\n//\t   7   | Executive paper (7.25 in. by 10.5 in.)\n//\t   8   | A3 paper (297 mm by 420 mm)\n//\t   9   | A4 paper (210 mm by 297 mm)\n//\t   10  | A4 small paper (210 mm by 297 mm)\n//\t   11  | A5 paper (148 mm by 210 mm)\n//\t   12  | B4 paper (250 mm by 353 mm)\n//\t   13  | B5 paper (176 mm by 250 mm)\n//\t   14  | Folio paper (8.5 in. by 13 in.)\n//\t   15  | Quarto paper (215 mm by 275 mm)\n//\t   16  | Standard paper (10 in. by 14 in.)\n//\t   17  | Standard paper (11 in. by 17 in.)\n//\t   18  | Note paper (8.5 in. by 11 in.)\n//\t   19  | #9 envelope (3.875 in. by 8.875 in.)\n//\t   20  | #10 envelope (4.125 in. by 9.5 in.)\n//\t   21  | #11 envelope (4.5 in. by 10.375 in.)\n//\t   22  | #12 envelope (4.75 in. by 11 in.)\n//\t   23  | #14 envelope (5 in. by 11.5 in.)\n//\t   24  | C paper (17 in. by 22 in.)\n//\t   25  | D paper (22 in. by 34 in.)\n//\t   26  | E paper (34 in. by 44 in.)\n//\t   27  | DL envelope (110 mm by 220 mm)\n//\t   28  | C5 envelope (162 mm by 229 mm)\n//\t   29  | C3 envelope (324 mm by 458 mm)\n//\t   30  | C4 envelope (229 mm by 324 mm)\n//\t   31  | C6 envelope (114 mm by 162 mm)\n//\t   32  | C65 envelope (114 mm by 229 mm)\n//\t   33  | B4 envelope (250 mm by 353 mm)\n//\t   34  | B5 envelope (176 mm by 250 mm)\n//\t   35  | B6 envelope (176 mm by 125 mm)\n//\t   36  | Italy envelope (110 mm by 230 mm)\n//\t   37  | Monarch envelope (3.875 in. by 7.5 in.).\n//\t   38  | 6 3/4 envelope (3.625 in. by 6.5 in.)\n//\t   39  | US standard fanfold (14.875 in. by 11 in.)\n//\t   40  | German standard fanfold (8.5 in. by 12 in.)\n//\t   41  | German legal fanfold (8.5 in. by 13 in.)\n//\t   42  | ISO B4 (250 mm by 353 mm)\n//\t   43  | Japanese postcard (100 mm by 148 mm)\n//\t   44  | Standard paper (9 in. by 11 in.)\n//\t   45  | Standard paper (10 in. by 11 in.)\n//\t   46  | Standard paper (15 in. by 11 in.)\n//\t   47  | Invite envelope (220 mm by 220 mm)\n//\t   50  | Letter extra paper (9.275 in. by 12 in.)\n//\t   51  | Legal extra paper (9.275 in. by 15 in.)\n//\t   52  | Tabloid extra paper (11.69 in. by 18 in.)\n//\t   53  | A4 extra paper (236 mm by 322 mm)\n//\t   54  | Letter transverse paper (8.275 in. by 11 in.)\n//\t   55  | A4 transverse paper (210 mm by 297 mm)\n//\t   56  | Letter extra transverse paper (9.275 in. by 12 in.)\n//\t   57  | SuperA/SuperA/A4 paper (227 mm by 356 mm)\n//\t   58  | SuperB/SuperB/A3 paper (305 mm by 487 mm)\n//\t   59  | Letter plus paper (8.5 in. by 12.69 in.)\n//\t   60  | A4 plus paper (210 mm by 330 mm)\n//\t   61  | A5 transverse paper (148 mm by 210 mm)\n//\t   62  | JIS B5 transverse paper (182 mm by 257 mm)\n//\t   63  | A3 extra paper (322 mm by 445 mm)\n//\t   64  | A5 extra paper (174 mm by 235 mm)\n//\t   65  | ISO B5 extra paper (201 mm by 276 mm)\n//\t   66  | A2 paper (420 mm by 594 mm)\n//\t   67  | A3 transverse paper (297 mm by 420 mm)\n//\t   68  | A3 extra transverse paper (322 mm by 445 mm)\n//\t   69  | Japanese Double Postcard (200 mm x 148 mm)\n//\t   70  | A6 (105 mm x 148 mm)\n//\t   71  | Japanese Envelope Kaku #2\n//\t   72  | Japanese Envelope Kaku #3\n//\t   73  | Japanese Envelope Chou #3\n//\t   74  | Japanese Envelope Chou #4\n//\t   75  | Letter Rotated (11in x 8 1/2 11 in)\n//\t   76  | A3 Rotated (420 mm x 297 mm)\n//\t   77  | A4 Rotated (297 mm x 210 mm)\n//\t   78  | A5 Rotated (210 mm x 148 mm)\n//\t   79  | B4 (JIS) Rotated (364 mm x 257 mm)\n//\t   80  | B5 (JIS) Rotated (257 mm x 182 mm)\n//\t   81  | Japanese Postcard Rotated (148 mm x 100 mm)\n//\t   82  | Double Japanese Postcard Rotated (148 mm x 200 mm)\n//\t   83  | A6 Rotated (148 mm x 105 mm)\n//\t   84  | Japanese Envelope Kaku #2 Rotated\n//\t   85  | Japanese Envelope Kaku #3 Rotated\n//\t   86  | Japanese Envelope Chou #3 Rotated\n//\t   87  | Japanese Envelope Chou #4 Rotated\n//\t   88  | B6 (JIS) (128 mm x 182 mm)\n//\t   89  | B6 (JIS) Rotated (182 mm x 128 mm)\n//\t   90  | (12 in x 11 in)\n//\t   91  | Japanese Envelope You #4\n//\t   92  | Japanese Envelope You #4 Rotated\n//\t   93  | PRC 16K (146 mm x 215 mm)\n//\t   94  | PRC 32K (97 mm x 151 mm)\n//\t   95  | PRC 32K(Big) (97 mm x 151 mm)\n//\t   96  | PRC Envelope #1 (102 mm x 165 mm)\n//\t   97  | PRC Envelope #2 (102 mm x 176 mm)\n//\t   98  | PRC Envelope #3 (125 mm x 176 mm)\n//\t   99  | PRC Envelope #4 (110 mm x 208 mm)\n//\t   100 | PRC Envelope #5 (110 mm x 220 mm)\n//\t   101 | PRC Envelope #6 (120 mm x 230 mm)\n//\t   102 | PRC Envelope #7 (160 mm x 230 mm)\n//\t   103 | PRC Envelope #8 (120 mm x 309 mm)\n//\t   104 | PRC Envelope #9 (229 mm x 324 mm)\n//\t   105 | PRC Envelope #10 (324 mm x 458 mm)\n//\t   106 | PRC 16K Rotated\n//\t   107 | PRC 32K Rotated\n//\t   108 | PRC 32K(Big) Rotated\n//\t   109 | PRC Envelope #1 Rotated (165 mm x 102 mm)\n//\t   110 | PRC Envelope #2 Rotated (176 mm x 102 mm)\n//\t   111 | PRC Envelope #3 Rotated (176 mm x 125 mm)\n//\t   112 | PRC Envelope #4 Rotated (208 mm x 110 mm)\n//\t   113 | PRC Envelope #5 Rotated (220 mm x 110 mm)\n//\t   114 | PRC Envelope #6 Rotated (230 mm x 120 mm)\n//\t   115 | PRC Envelope #7 Rotated (230 mm x 160 mm)\n//\t   116 | PRC Envelope #8 Rotated (309 mm x 120 mm)\n//\t   117 | PRC Envelope #9 Rotated (324 mm x 229 mm)\n//\t   118 | PRC Envelope #10 Rotated (458 mm x 324 mm)\nfunc (f *File) SetPageLayout(sheet string, opts *PageLayoutOptions) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif opts == nil {\n\t\treturn err\n\t}\n\treturn ws.setPageSetUp(opts)\n}\n\n// newPageSetUp initialize page setup settings for the worksheet if which not\n// exist.\nfunc (ws *xlsxWorksheet) newPageSetUp() {\n\tif ws.PageSetUp == nil {\n\t\tws.PageSetUp = new(xlsxPageSetUp)\n\t}\n}\n\n// setPageSetUp set page setup settings for the worksheet by given options.\nfunc (ws *xlsxWorksheet) setPageSetUp(opts *PageLayoutOptions) error {\n\tif opts.Size != nil {\n\t\tws.newPageSetUp()\n\t\tws.PageSetUp.PaperSize = opts.Size\n\t}\n\tif opts.Orientation != nil {\n\t\tif inStrSlice(supportedPageOrientation, *opts.Orientation, true) == -1 {\n\t\t\treturn newInvalidOptionalValue(\"Orientation\", *opts.Orientation, supportedPageOrientation)\n\t\t}\n\t\tws.newPageSetUp()\n\t\tws.PageSetUp.Orientation = *opts.Orientation\n\t}\n\tif opts.FirstPageNumber != nil && *opts.FirstPageNumber > 0 {\n\t\tws.newPageSetUp()\n\t\tws.PageSetUp.FirstPageNumber = strconv.Itoa(int(*opts.FirstPageNumber))\n\t\tws.PageSetUp.UseFirstPageNumber = true\n\t}\n\tif opts.AdjustTo != nil {\n\t\tif *opts.AdjustTo < 10 || 400 < *opts.AdjustTo {\n\t\t\treturn ErrPageSetupAdjustTo\n\t\t}\n\t\tws.newPageSetUp()\n\t\tws.PageSetUp.Scale = int(*opts.AdjustTo)\n\t}\n\tif opts.FitToHeight != nil {\n\t\tws.newPageSetUp()\n\t\tws.PageSetUp.FitToHeight = opts.FitToHeight\n\t}\n\tif opts.FitToWidth != nil {\n\t\tws.newPageSetUp()\n\t\tws.PageSetUp.FitToWidth = opts.FitToWidth\n\t}\n\tif opts.BlackAndWhite != nil {\n\t\tws.newPageSetUp()\n\t\tws.PageSetUp.BlackAndWhite = *opts.BlackAndWhite\n\t}\n\tif opts.PageOrder != nil {\n\t\tif inStrSlice(supportedPageOrder, *opts.PageOrder, true) == -1 {\n\t\t\treturn newInvalidOptionalValue(\"PageOrder\", *opts.PageOrder, supportedPageOrder)\n\t\t}\n\t\tws.newPageSetUp()\n\t\tws.PageSetUp.PageOrder = *opts.PageOrder\n\t}\n\treturn nil\n}\n\n// GetPageLayout provides a function to gets worksheet page layout.\nfunc (f *File) GetPageLayout(sheet string) (PageLayoutOptions, error) {\n\topts := PageLayoutOptions{\n\t\tSize:            intPtr(0),\n\t\tOrientation:     stringPtr(supportedPageOrientation[0]),\n\t\tFirstPageNumber: uintPtr(1),\n\t\tAdjustTo:        uintPtr(100),\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\tif ws.PageSetUp != nil {\n\t\tif ws.PageSetUp.PaperSize != nil {\n\t\t\topts.Size = ws.PageSetUp.PaperSize\n\t\t}\n\t\tif ws.PageSetUp.Orientation != \"\" {\n\t\t\topts.Orientation = stringPtr(ws.PageSetUp.Orientation)\n\t\t}\n\t\tif num, _ := strconv.Atoi(ws.PageSetUp.FirstPageNumber); num != 0 {\n\t\t\topts.FirstPageNumber = uintPtr(uint(num))\n\t\t}\n\t\tif ws.PageSetUp.Scale >= 10 && ws.PageSetUp.Scale <= 400 {\n\t\t\topts.AdjustTo = uintPtr(uint(ws.PageSetUp.Scale))\n\t\t}\n\t\tif ws.PageSetUp.FitToHeight != nil {\n\t\t\topts.FitToHeight = ws.PageSetUp.FitToHeight\n\t\t}\n\t\tif ws.PageSetUp.FitToWidth != nil {\n\t\t\topts.FitToWidth = ws.PageSetUp.FitToWidth\n\t\t}\n\t\topts.BlackAndWhite = boolPtr(ws.PageSetUp.BlackAndWhite)\n\t\tif ws.PageSetUp.PageOrder != \"\" {\n\t\t\topts.PageOrder = stringPtr(ws.PageSetUp.PageOrder)\n\t\t}\n\t}\n\treturn opts, err\n}\n\n// SetDefinedName provides a function to set the defined names of the workbook\n// or worksheet. If not specified scope, the default scope is workbook.\n// For example:\n//\n//\terr := f.SetDefinedName(&excelize.DefinedName{\n//\t    Name:     \"Amount\",\n//\t    RefersTo: \"Sheet1!$A$2:$D$5\",\n//\t    Comment:  \"defined name comment\",\n//\t})\n//\n// If you fill the RefersTo property with only one columns range without a\n// comma, it will work as \"Columns to repeat at left\" only. For example:\n//\n//\terr := f.SetDefinedName(&excelize.DefinedName{\n//\t    Name:     \"_xlnm.Print_Titles\",\n//\t    RefersTo: \"Sheet1!$A:$A\",\n//\t    Scope:    \"Sheet1\",\n//\t})\n//\n// If you fill the RefersTo property with only one rows range without a comma,\n// it will work as \"Rows to repeat at top\" only. For example:\n//\n//\terr := f.SetDefinedName(&excelize.DefinedName{\n//\t    Name:     \"_xlnm.Print_Titles\",\n//\t    RefersTo: \"Sheet1!$1:$1\",\n//\t    Scope:    \"Sheet1\",\n//\t})\n//\n// You can also use the function in RefersTo. For example:\n//\n//\terr := f.SetDefinedName(&excelize.DefinedName{\n//\t    Name:     \"CustomRange\",\n//\t    RefersTo: \"Sheet1!$A$2+Sheet1!$D$5\",\n//\t    Scope:    \"Sheet1\",\n//\t})\nfunc (f *File) SetDefinedName(definedName *DefinedName) error {\n\tif definedName.Name == \"\" || definedName.RefersTo == \"\" {\n\t\treturn ErrParameterInvalid\n\t}\n\tif err := checkDefinedName(definedName.Name); err != nil && inStrSlice(builtInDefinedNames[:2], definedName.Name, false) == -1 {\n\t\treturn err\n\t}\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.clearCalcCache()\n\td := xlsxDefinedName{\n\t\tName:    definedName.Name,\n\t\tComment: definedName.Comment,\n\t\tData:    definedName.RefersTo,\n\t}\n\tif definedName.Scope != \"\" {\n\t\tif sheetIndex, _ := f.GetSheetIndex(definedName.Scope); sheetIndex >= 0 {\n\t\t\td.LocalSheetID = &sheetIndex\n\t\t}\n\t}\n\tif wb.DefinedNames != nil {\n\t\tfor _, dn := range wb.DefinedNames.DefinedName {\n\t\t\tvar scope string\n\t\t\tif dn.LocalSheetID != nil {\n\t\t\t\tscope = f.GetSheetName(*dn.LocalSheetID)\n\t\t\t}\n\t\t\tif scope == definedName.Scope && dn.Name == definedName.Name {\n\t\t\t\treturn ErrDefinedNameDuplicate\n\t\t\t}\n\t\t}\n\t\twb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName, d)\n\t\treturn nil\n\t}\n\twb.DefinedNames = &xlsxDefinedNames{\n\t\tDefinedName: []xlsxDefinedName{d},\n\t}\n\treturn nil\n}\n\n// DeleteDefinedName provides a function to delete the defined names of the\n// workbook or worksheet. If not specified scope, the default scope is\n// workbook. For example:\n//\n//\terr := f.DeleteDefinedName(&excelize.DefinedName{\n//\t    Name:     \"Amount\",\n//\t    Scope:    \"Sheet2\",\n//\t})\nfunc (f *File) DeleteDefinedName(definedName *DefinedName) error {\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.clearCalcCache()\n\tif wb.DefinedNames != nil {\n\t\tfor idx, dn := range wb.DefinedNames.DefinedName {\n\t\t\tscope := \"Workbook\"\n\t\t\tdeleteScope := definedName.Scope\n\t\t\tif deleteScope == \"\" {\n\t\t\t\tdeleteScope = \"Workbook\"\n\t\t\t}\n\t\t\tif dn.LocalSheetID != nil {\n\t\t\t\tscope = f.GetSheetName(*dn.LocalSheetID)\n\t\t\t}\n\t\t\tif scope == deleteScope && dn.Name == definedName.Name {\n\t\t\t\twb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName[:idx], wb.DefinedNames.DefinedName[idx+1:]...)\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn ErrDefinedNameScope\n}\n\n// GetDefinedName provides a function to get the defined names of the workbook\n// or worksheet.\nfunc (f *File) GetDefinedName() []DefinedName {\n\tvar definedNames []DefinedName\n\twb, _ := f.workbookReader()\n\tif wb.DefinedNames != nil {\n\t\tfor _, dn := range wb.DefinedNames.DefinedName {\n\t\t\tdefinedName := DefinedName{\n\t\t\t\tName:     dn.Name,\n\t\t\t\tComment:  dn.Comment,\n\t\t\t\tRefersTo: dn.Data,\n\t\t\t\tScope:    \"Workbook\",\n\t\t\t}\n\t\t\tif dn.LocalSheetID != nil && *dn.LocalSheetID >= 0 {\n\t\t\t\tdefinedName.Scope = f.GetSheetName(*dn.LocalSheetID)\n\t\t\t}\n\t\t\tdefinedNames = append(definedNames, definedName)\n\t\t}\n\t}\n\treturn definedNames\n}\n\n// GroupSheets provides a function to group worksheets by given worksheets\n// name. Group worksheets must contain an active worksheet.\nfunc (f *File) GroupSheets(sheets []string) error {\n\t// Check an active worksheet in group worksheets\n\tvar inActiveSheet bool\n\tactiveSheet := f.GetActiveSheetIndex()\n\tsheetMap := f.GetSheetList()\n\tfor idx, sheetName := range sheetMap {\n\t\tfor _, s := range sheets {\n\t\t\tif strings.EqualFold(s, sheetName) && idx == activeSheet {\n\t\t\t\tinActiveSheet = true\n\t\t\t}\n\t\t}\n\t}\n\tif !inActiveSheet {\n\t\treturn ErrGroupSheets\n\t}\n\t// check worksheet exists\n\tvar wss []*xlsxWorksheet\n\tfor _, sheet := range sheets {\n\t\tworksheet, err := f.workSheetReader(sheet)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\twss = append(wss, worksheet)\n\t}\n\tfor _, ws := range wss {\n\t\tsheetViews := ws.SheetViews.SheetView\n\t\tfor idx := range sheetViews {\n\t\t\tws.SheetViews.SheetView[idx].TabSelected = true\n\t\t}\n\t}\n\treturn nil\n}\n\n// UngroupSheets provides a function to ungroup worksheets.\nfunc (f *File) UngroupSheets() error {\n\tactiveSheet := f.GetActiveSheetIndex()\n\tfor index, sheet := range f.GetSheetList() {\n\t\tif activeSheet == index {\n\t\t\tcontinue\n\t\t}\n\t\tws, _ := f.workSheetReader(sheet)\n\t\tsheetViews := ws.SheetViews.SheetView\n\t\tfor idx := range sheetViews {\n\t\t\tws.SheetViews.SheetView[idx].TabSelected = false\n\t\t}\n\t}\n\treturn nil\n}\n\n// InsertPageBreak create a page break to determine where the printed page\n// ends and where begins the next one by given worksheet name and cell\n// reference, so the content before the page break will be printed on one page\n// and after the page break on another.\nfunc (f *File) InsertPageBreak(sheet, cell string) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn ws.insertPageBreak(cell)\n}\n\n// insertPageBreak create a page break in the worksheet by specific cell\n// reference.\nfunc (ws *xlsxWorksheet) insertPageBreak(cell string) error {\n\tvar (\n\t\trow, col       int\n\t\terr            error\n\t\trowBrk, colBrk = -1, -1\n\t)\n\tif col, row, err = CellNameToCoordinates(cell); err != nil {\n\t\treturn err\n\t}\n\tcol--\n\trow--\n\tif col == row && col == 0 {\n\t\treturn err\n\t}\n\tif ws.RowBreaks == nil {\n\t\tws.RowBreaks = &xlsxRowBreaks{}\n\t}\n\tif ws.ColBreaks == nil {\n\t\tws.ColBreaks = &xlsxColBreaks{}\n\t}\n\n\tfor idx, brk := range ws.RowBreaks.Brk {\n\t\tif brk.ID == row {\n\t\t\trowBrk = idx\n\t\t}\n\t}\n\tfor idx, brk := range ws.ColBreaks.Brk {\n\t\tif brk.ID == col {\n\t\t\tcolBrk = idx\n\t\t}\n\t}\n\n\tif row != 0 && rowBrk == -1 {\n\t\tws.RowBreaks.Brk = append(ws.RowBreaks.Brk, &xlsxBrk{\n\t\t\tID:  row,\n\t\t\tMax: MaxColumns - 1,\n\t\t\tMan: true,\n\t\t})\n\t\tws.RowBreaks.ManualBreakCount++\n\t}\n\tif col != 0 && colBrk == -1 {\n\t\tws.ColBreaks.Brk = append(ws.ColBreaks.Brk, &xlsxBrk{\n\t\t\tID:  col,\n\t\t\tMax: TotalRows - 1,\n\t\t\tMan: true,\n\t\t})\n\t\tws.ColBreaks.ManualBreakCount++\n\t}\n\tws.RowBreaks.Count = len(ws.RowBreaks.Brk)\n\tws.ColBreaks.Count = len(ws.ColBreaks.Brk)\n\treturn err\n}\n\n// RemovePageBreak remove a page break by given worksheet name and cell\n// reference.\nfunc (f *File) RemovePageBreak(sheet, cell string) error {\n\tvar (\n\t\tws       *xlsxWorksheet\n\t\trow, col int\n\t\terr      error\n\t)\n\tif ws, err = f.workSheetReader(sheet); err != nil {\n\t\treturn err\n\t}\n\tif col, row, err = CellNameToCoordinates(cell); err != nil {\n\t\treturn err\n\t}\n\tcol--\n\trow--\n\tif col == row && col == 0 {\n\t\treturn err\n\t}\n\tremoveBrk := func(ID int, brks []*xlsxBrk) []*xlsxBrk {\n\t\tfor i, brk := range brks {\n\t\t\tif brk.ID == ID {\n\t\t\t\tbrks = append(brks[:i], brks[i+1:]...)\n\t\t\t}\n\t\t}\n\t\treturn brks\n\t}\n\tif ws.RowBreaks == nil || ws.ColBreaks == nil {\n\t\treturn err\n\t}\n\trowBrks := len(ws.RowBreaks.Brk)\n\tcolBrks := len(ws.ColBreaks.Brk)\n\tif rowBrks > 0 && rowBrks == colBrks {\n\t\tws.RowBreaks.Brk = removeBrk(row, ws.RowBreaks.Brk)\n\t\tws.ColBreaks.Brk = removeBrk(col, ws.ColBreaks.Brk)\n\t\tws.RowBreaks.Count = len(ws.RowBreaks.Brk)\n\t\tws.ColBreaks.Count = len(ws.ColBreaks.Brk)\n\t\tws.RowBreaks.ManualBreakCount--\n\t\tws.ColBreaks.ManualBreakCount--\n\t\treturn err\n\t}\n\tif rowBrks > 0 && rowBrks > colBrks {\n\t\tws.RowBreaks.Brk = removeBrk(row, ws.RowBreaks.Brk)\n\t\tws.RowBreaks.Count = len(ws.RowBreaks.Brk)\n\t\tws.RowBreaks.ManualBreakCount--\n\t\treturn err\n\t}\n\tif colBrks > 0 && colBrks > rowBrks {\n\t\tws.ColBreaks.Brk = removeBrk(col, ws.ColBreaks.Brk)\n\t\tws.ColBreaks.Count = len(ws.ColBreaks.Brk)\n\t\tws.ColBreaks.ManualBreakCount--\n\t}\n\treturn err\n}\n\n// relsReader provides a function to get the pointer to the structure\n// after deserialization of relationships parts.\nfunc (f *File) relsReader(path string) (*xlsxRelationships, error) {\n\trels, _ := f.Relationships.Load(path)\n\tif rels == nil {\n\t\tif _, ok := f.Pkg.Load(path); ok {\n\t\t\tc := xlsxRelationships{}\n\t\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))).\n\t\t\t\tDecode(&c); err != nil && err != io.EOF {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tf.Relationships.Store(path, &c)\n\t\t}\n\t}\n\tif rels, _ = f.Relationships.Load(path); rels != nil {\n\t\treturn rels.(*xlsxRelationships), nil\n\t}\n\treturn nil, nil\n}\n\n// fillSheetData ensures there are enough rows, and columns in the chosen\n// row to accept data. Missing rows are backfilled and given their row number\n// Uses the last populated row as a hint for the size of the next row to add\nfunc (ws *xlsxWorksheet) prepareSheetXML(col, row int) {\n\trowCount := len(ws.SheetData.Row)\n\tsizeHint := 0\n\tvar ht *float64\n\tvar customHeight bool\n\tif ws.SheetFormatPr != nil && ws.SheetFormatPr.CustomHeight {\n\t\tht = float64Ptr(ws.SheetFormatPr.DefaultRowHeight)\n\t\tcustomHeight = true\n\t}\n\tif rowCount > 0 {\n\t\tsizeHint = len(ws.SheetData.Row[rowCount-1].C)\n\t}\n\tif rowCount < row {\n\t\t// append missing rows\n\t\tfor rowIdx := rowCount; rowIdx < row; rowIdx++ {\n\t\t\tws.SheetData.Row = append(ws.SheetData.Row, xlsxRow{R: rowIdx + 1, CustomHeight: customHeight, Ht: ht, C: make([]xlsxC, 0, sizeHint)})\n\t\t}\n\t}\n\trowData := &ws.SheetData.Row[row-1]\n\tfillColumns(rowData, col, row)\n}\n\n// fillColumns fill cells in the column of the row as contiguous.\nfunc fillColumns(rowData *xlsxRow, col, row int) {\n\tcellCount := len(rowData.C)\n\tif cellCount < col {\n\t\tfor colIdx := cellCount; colIdx < col; colIdx++ {\n\t\t\tcellName, _ := CoordinatesToCellName(colIdx+1, row)\n\t\t\trowData.C = append(rowData.C, xlsxC{R: cellName})\n\t\t}\n\t}\n}\n\n// makeContiguousColumns make columns in specific rows as contiguous.\nfunc (ws *xlsxWorksheet) makeContiguousColumns(fromRow, toRow, colCount int) {\n\tfor ; fromRow < toRow; fromRow++ {\n\t\trowData := &ws.SheetData.Row[fromRow-1]\n\t\tfillColumns(rowData, colCount, fromRow)\n\t}\n}\n\n// SetSheetDimension provides the method to set or remove the used range of the\n// worksheet by a given range reference. It specifies the row and column bounds\n// of used cells in the worksheet. The range reference is set using the A1\n// reference style(e.g., \"A1:D5\"). Passing an empty range reference will remove\n// the used range of the worksheet.\nfunc (f *File) SetSheetDimension(sheet, rangeRef string) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Remove the dimension element if an empty string is provided\n\tif rangeRef == \"\" {\n\t\tws.Dimension = nil\n\t\treturn nil\n\t}\n\tparts := len(strings.Split(rangeRef, \":\"))\n\tif parts == 1 {\n\t\t_, _, err = CellNameToCoordinates(rangeRef)\n\t\tif err == nil {\n\t\t\tws.Dimension = &xlsxDimension{Ref: strings.ToUpper(rangeRef)}\n\t\t}\n\t\treturn err\n\t}\n\tif parts != 2 {\n\t\treturn ErrParameterInvalid\n\t}\n\tcoordinates, err := rangeRefToCoordinates(rangeRef)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_ = sortCoordinates(coordinates)\n\tref, err := coordinatesToRangeRef(coordinates)\n\tws.Dimension = &xlsxDimension{Ref: ref}\n\treturn err\n}\n\n// GetSheetDimension provides the method to get the used range of the worksheet.\nfunc (f *File) GetSheetDimension(sheet string) (string, error) {\n\tvar ref string\n\tif err := checkSheetName(sheet); err != nil {\n\t\treturn ref, err\n\t}\n\tname, ok := f.getSheetXMLPath(sheet)\n\tif !ok {\n\t\treturn ref, ErrSheetNotExist{sheet}\n\t}\n\tif worksheet, ok := f.Sheet.Load(name); ok && worksheet != nil {\n\t\tws := worksheet.(*xlsxWorksheet)\n\t\tif ws.Dimension != nil {\n\t\t\tref = ws.Dimension.Ref\n\t\t}\n\t\treturn ref, nil\n\t}\n\tneedClose, decoder, tempFile, err := f.xmlDecoder(name)\n\tif needClose && err == nil {\n\t\tdefer func() {\n\t\t\terr = tempFile.Close()\n\t\t}()\n\t}\n\tfor {\n\t\ttoken, _ := decoder.Token()\n\t\tif token == nil {\n\t\t\tbreak\n\t\t}\n\t\tswitch xmlElement := token.(type) {\n\t\tcase xml.StartElement:\n\t\t\tif xmlElement.Name.Local == \"dimension\" {\n\t\t\t\tfor _, attr := range xmlElement.Attr {\n\t\t\t\t\tif attr.Name.Local == \"ref\" {\n\t\t\t\t\t\treturn attr.Value, err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif xmlElement.Name.Local == \"sheetData\" {\n\t\t\t\treturn ref, err\n\t\t\t}\n\t\t}\n\t}\n\treturn ref, err\n}\n\n// AddIgnoredErrors provides the method to ignored error for a range of cells.\nfunc (f *File) AddIgnoredErrors(sheet, rangeRef string, ignoredErrorsType IgnoredErrorsType) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif rangeRef == \"\" {\n\t\treturn ErrParameterInvalid\n\t}\n\tif ws.IgnoredErrors == nil {\n\t\tws.IgnoredErrors = &xlsxIgnoredErrors{}\n\t}\n\tie := map[IgnoredErrorsType]xlsxIgnoredError{\n\t\tIgnoredErrorsEvalError:          {Sqref: rangeRef, EvalError: true},\n\t\tIgnoredErrorsTwoDigitTextYear:   {Sqref: rangeRef, TwoDigitTextYear: true},\n\t\tIgnoredErrorsNumberStoredAsText: {Sqref: rangeRef, NumberStoredAsText: true},\n\t\tIgnoredErrorsFormula:            {Sqref: rangeRef, Formula: true},\n\t\tIgnoredErrorsFormulaRange:       {Sqref: rangeRef, FormulaRange: true},\n\t\tIgnoredErrorsUnlockedFormula:    {Sqref: rangeRef, UnlockedFormula: true},\n\t\tIgnoredErrorsEmptyCellReference: {Sqref: rangeRef, EmptyCellReference: true},\n\t\tIgnoredErrorsListDataValidation: {Sqref: rangeRef, ListDataValidation: true},\n\t\tIgnoredErrorsCalculatedColumn:   {Sqref: rangeRef, CalculatedColumn: true},\n\t}[ignoredErrorsType]\n\tfor _, val := range ws.IgnoredErrors.IgnoredError {\n\t\tif reflect.DeepEqual(val, ie) {\n\t\t\treturn err\n\t\t}\n\t}\n\tws.IgnoredErrors.IgnoredError = append(ws.IgnoredErrors.IgnoredError, ie)\n\treturn err\n}\n"
  },
  {
    "path": "sheet_test.go",
    "content": "package excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewSheet(t *testing.T) {\n\tf := NewFile()\n\t_, err := f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tsheetID, err := f.NewSheet(\"sheet2\")\n\tassert.NoError(t, err)\n\tf.SetActiveSheet(sheetID)\n\t// Test delete original sheet\n\tidx, err := f.GetSheetIndex(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.DeleteSheet(f.GetSheetName(idx)))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestNewSheet.xlsx\")))\n\t// Test create new worksheet with already exists name\n\tsheetID, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tidx, err = f.GetSheetIndex(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, idx, sheetID)\n\t// Test create new worksheet with empty sheet name\n\tsheetID, err = f.NewSheet(\":\\\\/?*[]\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\tassert.Equal(t, -1, sheetID)\n}\n\nfunc TestPanes(t *testing.T) {\n\tf := NewFile()\n\n\tassert.NoError(t, f.SetPanes(\"Sheet1\", &Panes{Freeze: false, Split: false}))\n\t_, err := f.NewSheet(\"Panes 2\")\n\tassert.NoError(t, err)\n\n\texpected := Panes{\n\t\tFreeze:      true,\n\t\tSplit:       false,\n\t\tXSplit:      1,\n\t\tYSplit:      0,\n\t\tTopLeftCell: \"B1\",\n\t\tActivePane:  \"topRight\",\n\t\tSelection: []Selection{\n\t\t\t{SQRef: \"K16\", ActiveCell: \"K16\", Pane: \"topRight\"},\n\t\t},\n\t}\n\tassert.NoError(t, f.SetPanes(\"Panes 2\", &expected))\n\tpanes, err := f.GetPanes(\"Panes 2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected, panes)\n\n\t_, err = f.NewSheet(\"Panes 3\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetPanes(\"Panes 3\",\n\t\t&Panes{\n\t\t\tFreeze:      false,\n\t\t\tSplit:       true,\n\t\t\tXSplit:      3270,\n\t\t\tYSplit:      1800,\n\t\t\tTopLeftCell: \"N57\",\n\t\t\tActivePane:  \"bottomLeft\",\n\t\t\tSelection: []Selection{\n\t\t\t\t{SQRef: \"I36\", ActiveCell: \"I36\"},\n\t\t\t\t{SQRef: \"G33\", ActiveCell: \"G33\", Pane: \"topRight\"},\n\t\t\t\t{SQRef: \"J60\", ActiveCell: \"J60\", Pane: \"bottomLeft\"},\n\t\t\t\t{SQRef: \"O60\", ActiveCell: \"O60\", Pane: \"bottomRight\"},\n\t\t\t},\n\t\t},\n\t))\n\t_, err = f.NewSheet(\"Panes 4\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetPanes(\"Panes 4\",\n\t\t&Panes{\n\t\t\tFreeze:      true,\n\t\t\tSplit:       false,\n\t\t\tXSplit:      0,\n\t\t\tYSplit:      9,\n\t\t\tTopLeftCell: \"A34\",\n\t\t\tActivePane:  \"bottomLeft\",\n\t\t\tSelection: []Selection{\n\t\t\t\t{SQRef: \"A11:XFD11\", ActiveCell: \"A11\", Pane: \"bottomLeft\"},\n\t\t\t},\n\t\t},\n\t))\n\tassert.EqualError(t, f.SetPanes(\"Panes 4\", nil), ErrParameterInvalid.Error())\n\tassert.EqualError(t, f.SetPanes(\"SheetN\", nil), \"sheet SheetN does not exist\")\n\t// Test set panes with invalid sheet name\n\tassert.EqualError(t, f.SetPanes(\"Sheet:1\", &Panes{Freeze: false, Split: false}), ErrSheetNameInvalid.Error())\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetPane.xlsx\")))\n\n\t// Test get panes with empty sheet views\n\tf = NewFile()\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetViews = &xlsxSheetViews{}\n\t_, err = f.GetPanes(\"Sheet1\")\n\tassert.NoError(t, err)\n\t// Test get panes without panes\n\tws.(*xlsxWorksheet).SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{{}}}\n\t_, err = f.GetPanes(\"Sheet1\")\n\tassert.NoError(t, err)\n\t// Test get panes without sheet views\n\tws.(*xlsxWorksheet).SheetViews = nil\n\t_, err = f.GetPanes(\"Sheet1\")\n\tassert.NoError(t, err)\n\t// Test get panes on not exists worksheet\n\t_, err = f.GetPanes(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\n\t// Test add pane on empty sheet views worksheet\n\tf = NewFile()\n\tf.checked = sync.Map{}\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(`<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><sheetData/></worksheet>`))\n\tassert.NoError(t, f.SetPanes(\"Sheet1\",\n\t\t&Panes{\n\t\t\tFreeze:      true,\n\t\t\tSplit:       false,\n\t\t\tXSplit:      1,\n\t\t\tYSplit:      0,\n\t\t\tTopLeftCell: \"B1\",\n\t\t\tActivePane:  \"topRight\",\n\t\t\tSelection: []Selection{\n\t\t\t\t{SQRef: \"K16\", ActiveCell: \"K16\", Pane: \"topRight\"},\n\t\t\t},\n\t\t},\n\t))\n}\n\nfunc TestSearchSheet(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"SharedStrings.xlsx\"))\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\t// Test search in a not exists worksheet\n\t_, err = f.SearchSheet(\"Sheet4\", \"\")\n\tassert.EqualError(t, err, \"sheet Sheet4 does not exist\")\n\t// Test search sheet with invalid sheet name\n\t_, err = f.SearchSheet(\"Sheet:1\", \"\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n\tvar expected []string\n\t// Test search a not exists value\n\tresult, err := f.SearchSheet(\"Sheet1\", \"X\")\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, expected, result)\n\tresult, err = f.SearchSheet(\"Sheet1\", \"A\")\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, []string{\"A1\"}, result)\n\t// Test search the coordinates where the numerical value in the range of\n\t// \"0-9\" of Sheet1 is described by regular expression:\n\tresult, err = f.SearchSheet(\"Sheet1\", \"[0-9]\", true)\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, expected, result)\n\tassert.NoError(t, f.Close())\n\n\t// Test search worksheet data after set cell value\n\tf = NewFile()\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", true))\n\t_, err = f.SearchSheet(\"Sheet1\", \"\")\n\tassert.NoError(t, err)\n\n\tf = NewFile()\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(`<worksheet><sheetData><row r=\"A\"><c r=\"2\" t=\"inlineStr\"><is><t>A</t></is></c></row></sheetData></worksheet>`))\n\tf.checked = sync.Map{}\n\tresult, err = f.SearchSheet(\"Sheet1\", \"A\")\n\tassert.EqualError(t, err, \"strconv.Atoi: parsing \\\"A\\\": invalid syntax\")\n\tassert.Equal(t, []string(nil), result)\n\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(`<worksheet><sheetData><row r=\"2\"><c r=\"A\" t=\"inlineStr\"><is><t>A</t></is></c></row></sheetData></worksheet>`))\n\tresult, err = f.SearchSheet(\"Sheet1\", \"A\")\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), err)\n\tassert.Equal(t, []string(nil), result)\n\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(`<worksheet><sheetData><row r=\"0\"><c r=\"A1\" t=\"inlineStr\"><is><t>A</t></is></c></row></sheetData></worksheet>`))\n\tresult, err = f.SearchSheet(\"Sheet1\", \"A\")\n\tassert.Equal(t, newCoordinatesToCellNameError(1, 0), err)\n\tassert.Equal(t, []string(nil), result)\n\n\t// Test search sheet with unsupported charset shared strings table\n\tf.SharedStrings = nil\n\tf.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)\n\t_, err = f.SearchSheet(\"Sheet1\", \"A\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestSetPageLayout(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetPageLayout(\"Sheet1\", nil))\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).PageSetUp = nil\n\texpected := PageLayoutOptions{\n\t\tSize:            intPtr(1),\n\t\tOrientation:     stringPtr(\"landscape\"),\n\t\tFirstPageNumber: uintPtr(1),\n\t\tAdjustTo:        uintPtr(120),\n\t\tFitToHeight:     intPtr(2),\n\t\tFitToWidth:      intPtr(2),\n\t\tBlackAndWhite:   boolPtr(true),\n\t\tPageOrder:       stringPtr(\"overThenDown\"),\n\t}\n\tassert.NoError(t, f.SetPageLayout(\"Sheet1\", &expected))\n\topts, err := f.GetPageLayout(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected, opts)\n\t// Test set page layout on not exists worksheet\n\tassert.EqualError(t, f.SetPageLayout(\"SheetN\", nil), \"sheet SheetN does not exist\")\n\t// Test set page layout with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.SetPageLayout(\"Sheet:1\", nil))\n\t// Test set page layout with invalid parameters\n\tassert.Equal(t, ErrPageSetupAdjustTo, f.SetPageLayout(\"Sheet1\", &PageLayoutOptions{\n\t\tAdjustTo: uintPtr(5),\n\t}))\n\tassert.EqualError(t, f.SetPageLayout(\"Sheet1\", &PageLayoutOptions{\n\t\tOrientation: stringPtr(\"x\"),\n\t}), \"invalid Orientation value \\\"x\\\", acceptable value should be one of portrait, landscape\")\n\tassert.EqualError(t, f.SetPageLayout(\"Sheet1\", &PageLayoutOptions{\n\t\tPageOrder: stringPtr(\"x\"),\n\t}), \"invalid PageOrder value \\\"x\\\", acceptable value should be one of overThenDown, downThenOver\")\n}\n\nfunc TestGetPageLayout(t *testing.T) {\n\tf := NewFile()\n\t// Test get page layout on not exists worksheet\n\t_, err := f.GetPageLayout(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get page layout with invalid sheet name\n\t_, err = f.GetPageLayout(\"Sheet:1\")\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n}\n\nfunc TestHeaderFooter(t *testing.T) {\n\tf := NewFile()\n\t// Test get header and footer with default header and footer settings\n\topts, err := f.GetHeaderFooter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, (*HeaderFooterOptions)(nil), opts)\n\t// Test get header and footer on not exists worksheet\n\t_, err = f.GetHeaderFooter(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\n\tassert.NoError(t, f.SetCellStr(\"Sheet1\", \"A1\", \"Test SetHeaderFooter\"))\n\t// Test set header and footer on not exists worksheet\n\tassert.EqualError(t, f.SetHeaderFooter(\"SheetN\", nil), \"sheet SheetN does not exist\")\n\t// Test Sheet:1 with invalid sheet name\n\tassert.EqualError(t, f.SetHeaderFooter(\"Sheet:1\", nil), ErrSheetNameInvalid.Error())\n\t// Test set header and footer with illegal setting\n\tassert.EqualError(t, f.SetHeaderFooter(\"Sheet1\", &HeaderFooterOptions{\n\t\tOddHeader: strings.Repeat(\"c\", MaxFieldLength+1),\n\t}), newFieldLengthError(\"OddHeader\").Error())\n\n\tassert.NoError(t, f.SetHeaderFooter(\"Sheet1\", nil))\n\ttext := strings.Repeat(\"一\", MaxFieldLength)\n\tassert.NoError(t, f.SetHeaderFooter(\"Sheet1\", &HeaderFooterOptions{\n\t\tOddHeader:   text,\n\t\tOddFooter:   text,\n\t\tEvenHeader:  text,\n\t\tEvenFooter:  text,\n\t\tFirstHeader: text,\n\t}))\n\texpected := &HeaderFooterOptions{\n\t\tDifferentFirst:   true,\n\t\tDifferentOddEven: true,\n\t\tOddHeader:        \"&R&P\",\n\t\tOddFooter:        \"&C&F\",\n\t\tEvenHeader:       \"&L&P\",\n\t\tEvenFooter:       \"&L&D&R&T\",\n\t\tFirstHeader:      `&CCenter &\"-,Bold\"Bold&\"-,Regular\"HeaderU+000A&D`,\n\t}\n\tassert.NoError(t, f.SetHeaderFooter(\"Sheet1\", expected))\n\topts, err = f.GetHeaderFooter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected, opts)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetHeaderFooter.xlsx\")))\n}\n\nfunc TestDefinedName(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     \"Amount.\",\n\t\tRefersTo: \"Sheet1!$A$2:$D$5\",\n\t\tComment:  \"defined name comment\",\n\t\tScope:    \"Sheet1\",\n\t}))\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     \"Amount\",\n\t\tRefersTo: \"Sheet1!$A$2:$D$5\",\n\t\tComment:  \"defined name comment\",\n\t}))\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     builtInDefinedNames[0],\n\t\tRefersTo: \"Sheet1!$A$1:$Z$100\",\n\t\tScope:    \"Sheet1\",\n\t}))\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     builtInDefinedNames[1],\n\t\tRefersTo: \"Sheet1!$A:$A,Sheet1!$1:$1\",\n\t\tScope:    \"Sheet1\",\n\t}))\n\tassert.EqualError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     \"Amount\",\n\t\tRefersTo: \"Sheet1!$A$2:$D$5\",\n\t\tComment:  \"defined name comment\",\n\t}), ErrDefinedNameDuplicate.Error())\n\tassert.EqualError(t, f.DeleteDefinedName(&DefinedName{\n\t\tName: \"No Exist Defined Name\",\n\t}), ErrDefinedNameScope.Error())\n\t// Test set defined name without name\n\tassert.EqualError(t, f.SetDefinedName(&DefinedName{\n\t\tRefersTo: \"Sheet1!$A$2:$D$5\",\n\t}), ErrParameterInvalid.Error())\n\t// Test set defined name without reference\n\tassert.EqualError(t, f.SetDefinedName(&DefinedName{\n\t\tName: \"Amount\",\n\t}), ErrParameterInvalid.Error())\n\tassert.Exactly(t, \"Sheet1!$A$2:$D$5\", f.GetDefinedName()[1].RefersTo)\n\tassert.NoError(t, f.DeleteDefinedName(&DefinedName{\n\t\tName: \"Amount\",\n\t}))\n\tassert.Exactly(t, \"Sheet1!$A$2:$D$5\", f.GetDefinedName()[0].RefersTo)\n\tassert.Len(t, f.GetDefinedName(), 3)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestDefinedName.xlsx\")))\n\t// Test set defined name with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetDefinedName(&DefinedName{\n\t\tName: \"Amount\", RefersTo: \"Sheet1!$A$2:$D$5\",\n\t}), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test delete defined name with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.DeleteDefinedName(&DefinedName{Name: \"Amount\"}),\n\t\t\"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestGroupSheets(t *testing.T) {\n\tf := NewFile()\n\tsheets := []string{\"Sheet2\", \"Sheet3\"}\n\tfor _, sheet := range sheets {\n\t\t_, err := f.NewSheet(sheet)\n\t\tassert.NoError(t, err)\n\t}\n\tassert.EqualError(t, f.GroupSheets([]string{\"Sheet1\", \"SheetN\"}), \"sheet SheetN does not exist\")\n\tassert.EqualError(t, f.GroupSheets([]string{\"Sheet2\", \"Sheet3\"}), \"group worksheet must contain an active worksheet\")\n\t// Test group sheets with invalid sheet name\n\tassert.EqualError(t, f.GroupSheets([]string{\"Sheet:1\", \"Sheet1\"}), ErrSheetNameInvalid.Error())\n\tassert.NoError(t, f.GroupSheets([]string{\"Sheet1\", \"Sheet2\"}))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestGroupSheets.xlsx\")))\n}\n\nfunc TestUngroupSheets(t *testing.T) {\n\tf := NewFile()\n\tsheets := []string{\"Sheet2\", \"Sheet3\", \"Sheet4\", \"Sheet5\"}\n\tfor _, sheet := range sheets {\n\t\t_, err := f.NewSheet(sheet)\n\t\tassert.NoError(t, err)\n\t}\n\tassert.NoError(t, f.UngroupSheets())\n}\n\nfunc TestInsertPageBreak(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.InsertPageBreak(\"Sheet1\", \"A1\"))\n\tassert.NoError(t, f.InsertPageBreak(\"Sheet1\", \"B2\"))\n\tassert.NoError(t, f.InsertPageBreak(\"Sheet1\", \"C3\"))\n\tassert.NoError(t, f.InsertPageBreak(\"Sheet1\", \"C3\"))\n\tassert.EqualError(t, f.InsertPageBreak(\"Sheet1\", \"A\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\tassert.EqualError(t, f.InsertPageBreak(\"SheetN\", \"C3\"), \"sheet SheetN does not exist\")\n\t// Test insert page break with invalid sheet name\n\tassert.EqualError(t, f.InsertPageBreak(\"Sheet:1\", \"C3\"), ErrSheetNameInvalid.Error())\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestInsertPageBreak.xlsx\")))\n}\n\nfunc TestRemovePageBreak(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.RemovePageBreak(\"Sheet1\", \"A2\"))\n\n\tassert.NoError(t, f.InsertPageBreak(\"Sheet1\", \"A2\"))\n\tassert.NoError(t, f.InsertPageBreak(\"Sheet1\", \"B2\"))\n\tassert.NoError(t, f.RemovePageBreak(\"Sheet1\", \"A1\"))\n\tassert.NoError(t, f.RemovePageBreak(\"Sheet1\", \"B2\"))\n\n\tassert.NoError(t, f.InsertPageBreak(\"Sheet1\", \"C3\"))\n\tassert.NoError(t, f.RemovePageBreak(\"Sheet1\", \"C3\"))\n\n\tassert.NoError(t, f.InsertPageBreak(\"Sheet1\", \"A3\"))\n\tassert.NoError(t, f.RemovePageBreak(\"Sheet1\", \"B3\"))\n\tassert.NoError(t, f.RemovePageBreak(\"Sheet1\", \"A3\"))\n\n\t_, err := f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.InsertPageBreak(\"Sheet2\", \"B2\"))\n\tassert.NoError(t, f.InsertPageBreak(\"Sheet2\", \"C2\"))\n\tassert.NoError(t, f.RemovePageBreak(\"Sheet2\", \"B2\"))\n\n\tassert.EqualError(t, f.RemovePageBreak(\"Sheet1\", \"A\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")).Error())\n\tassert.EqualError(t, f.RemovePageBreak(\"SheetN\", \"C3\"), \"sheet SheetN does not exist\")\n\t// Test remove page break with invalid sheet name\n\tassert.EqualError(t, f.RemovePageBreak(\"Sheet:1\", \"A3\"), ErrSheetNameInvalid.Error())\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestRemovePageBreak.xlsx\")))\n}\n\nfunc TestGetSheetName(t *testing.T) {\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Sheet1\", f.GetSheetName(0))\n\tassert.Equal(t, \"Sheet2\", f.GetSheetName(1))\n\tassert.Empty(t, f.GetSheetName(-1))\n\tassert.Empty(t, f.GetSheetName(2))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestGetSheetMap(t *testing.T) {\n\texpectedMap := map[int]string{\n\t\t1: \"Sheet1\",\n\t\t2: \"Sheet2\",\n\t}\n\tf, err := OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"))\n\tassert.NoError(t, err)\n\tsheetMap := f.GetSheetMap()\n\tfor idx, name := range sheetMap {\n\t\tassert.Equal(t, expectedMap[idx], name)\n\t}\n\tassert.Len(t, sheetMap, 2)\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\t_, err = f.getSheetMap()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestSetActiveSheet(t *testing.T) {\n\tf := NewFile()\n\tf.WorkBook.BookViews = nil\n\tf.SetActiveSheet(1)\n\tf.WorkBook.BookViews = &xlsxBookViews{WorkBookView: []xlsxWorkBookView{}}\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{}}\n\tf.SetActiveSheet(1)\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetViews = nil\n\tf.SetActiveSheet(1)\n\tf = NewFile()\n\tf.SetActiveSheet(-1)\n\tassert.Equal(t, f.GetActiveSheetIndex(), 0)\n\n\tf = NewFile()\n\tf.WorkBook.BookViews = nil\n\tidx, err := f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet2.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{}}\n\tf.SetActiveSheet(idx)\n}\n\nfunc TestSetSheetName(t *testing.T) {\n\tf := NewFile()\n\t// Test set worksheet with the same name\n\tassert.NoError(t, f.SetSheetName(\"Sheet1\", \"Sheet1\"))\n\tassert.Equal(t, \"Sheet1\", f.GetSheetName(0))\n\t// Test set worksheet with the different name\n\tassert.NoError(t, f.SetSheetName(\"Sheet1\", \"sheet1\"))\n\tassert.Equal(t, \"sheet1\", f.GetSheetName(0))\n\t// Test set sheet name with invalid sheet name\n\tassert.Equal(t, f.SetSheetName(\"Sheet:1\", \"Sheet1\"), ErrSheetNameInvalid)\n\t_, err := f.NewSheet(\"Sheet 3\")\n\tassert.NoError(t, err)\n\n\t// Test set worksheet name with existing defined name and auto filter\n\tassert.NoError(t, f.AutoFilter(\"Sheet1\", \"A1:A2\", nil))\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     \"Name1\",\n\t\tRefersTo: \"$B$2\",\n\t}))\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     \"Name2\",\n\t\tRefersTo: \"$A1$2:A2\",\n\t}))\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     \"Name3\",\n\t\tRefersTo: \"Sheet1!$A$1:'Sheet1'!A1:Sheet1!$A$1,Sheet1!A1:Sheet3!A1,Sheet3!A1\",\n\t}))\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{\n\t\tName:     \"Name4\",\n\t\tRefersTo: \"'Sheet 3'!$A1$2:A2\",\n\t}))\n\tassert.NoError(t, f.SetSheetName(\"Sheet1\", \"Sheet 2\"))\n\tfor i, expected := range []string{\"'Sheet 2'!$A$1:$A$2\", \"$B$2\", \"$A1$2:A2\", \"'Sheet 2'!$A$1:'Sheet 2'!A1:'Sheet 2'!$A$1,'Sheet 2'!A1:Sheet3!A1,Sheet3!A1\", \"'Sheet 3'!$A1$2:A2\"} {\n\t\tassert.Equal(t, expected, f.WorkBook.DefinedNames.DefinedName[i].Data)\n\t}\n}\n\nfunc TestWorksheetWriter(t *testing.T) {\n\tf := NewFile()\n\t// Test set cell value with alternate content\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tworksheet := xml.Header + `<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"><sheetData><row r=\"1\"><c r=\"A1\"><v>%d</v></c></row></sheetData><mc:AlternateContent xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"><mc:Choice xmlns:a14=\"http://schemas.microsoft.com/office/drawing/2010/main\" Requires=\"a14\"><xdr:twoCellAnchor editAs=\"oneCell\"></xdr:twoCellAnchor></mc:Choice><mc:Fallback/></mc:AlternateContent></worksheet>`\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", []byte(fmt.Sprintf(worksheet, 1)))\n\tf.checked = sync.Map{}\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 2))\n\tf.workSheetWriter()\n\tvalue, ok := f.Pkg.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tassert.Equal(t, fmt.Sprintf(worksheet, 2), string(value.([]byte)))\n}\n\nfunc TestGetWorkbookPath(t *testing.T) {\n\tf := NewFile()\n\tf.Pkg.Delete(defaultXMLPathRels)\n\tassert.Empty(t, f.getWorkbookPath())\n}\n\nfunc TestGetWorkbookRelsPath(t *testing.T) {\n\tf := NewFile()\n\tf.Pkg.Delete(\"xl/_rels/.rels\")\n\tf.Pkg.Store(defaultXMLPathRels, []byte(xml.Header+`<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument\" Target=\"/workbook.xml\"/></Relationships>`))\n\tassert.Equal(t, \"_rels/workbook.xml.rels\", f.getWorkbookRelsPath())\n}\n\nfunc TestDeleteSheet(t *testing.T) {\n\tf := NewFile()\n\tidx, err := f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tf.SetActiveSheet(idx)\n\t_, err = f.NewSheet(\"Sheet3\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.DeleteSheet(\"Sheet1\"))\n\tassert.Equal(t, \"Sheet2\", f.GetSheetName(f.GetActiveSheetIndex()))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestDeleteSheet.xlsx\")))\n\t// Test with auto filter defined names\n\tf = NewFile()\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\t_, err = f.NewSheet(\"Sheet3\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", \"A\"))\n\tassert.NoError(t, f.SetCellValue(\"Sheet2\", \"A1\", \"A\"))\n\tassert.NoError(t, f.SetCellValue(\"Sheet3\", \"A1\", \"A\"))\n\tassert.NoError(t, f.AutoFilter(\"Sheet1\", \"A1:A1\", nil))\n\tassert.NoError(t, f.AutoFilter(\"Sheet2\", \"A1:A1\", nil))\n\tassert.NoError(t, f.AutoFilter(\"Sheet3\", \"A1:A1\", nil))\n\tassert.NoError(t, f.DeleteSheet(\"Sheet2\"))\n\tassert.NoError(t, f.DeleteSheet(\"Sheet1\"))\n\t// Test delete sheet with invalid sheet name\n\tassert.EqualError(t, f.DeleteSheet(\"Sheet:1\"), ErrSheetNameInvalid.Error())\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestDeleteSheet2.xlsx\")))\n}\n\nfunc TestMoveSheet(t *testing.T) {\n\tf := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, f.Close())\n\t}()\n\tfor i := 2; i < 6; i++ {\n\t\t_, err := f.NewSheet(\"Sheet\" + strconv.Itoa(i))\n\t\tassert.NoError(t, err)\n\t}\n\tassert.Equal(t, []string{\"Sheet1\", \"Sheet2\", \"Sheet3\", \"Sheet4\", \"Sheet5\"}, f.GetSheetList())\n\n\t// Move target to first position\n\tassert.NoError(t, f.MoveSheet(\"Sheet2\", \"Sheet1\"))\n\tassert.Equal(t, []string{\"Sheet2\", \"Sheet1\", \"Sheet3\", \"Sheet4\", \"Sheet5\"}, f.GetSheetList())\n\tassert.Equal(t, \"Sheet1\", f.GetSheetName(f.GetActiveSheetIndex()))\n\n\t// Move target to last position\n\tassert.NoError(t, f.MoveSheet(\"Sheet2\", \"Sheet5\"))\n\tassert.NoError(t, f.MoveSheet(\"Sheet5\", \"Sheet2\"))\n\tassert.Equal(t, []string{\"Sheet1\", \"Sheet3\", \"Sheet4\", \"Sheet5\", \"Sheet2\"}, f.GetSheetList())\n\n\t// Move target to same position\n\tassert.NoError(t, f.MoveSheet(\"Sheet1\", \"Sheet1\"))\n\tassert.Equal(t, []string{\"Sheet1\", \"Sheet3\", \"Sheet4\", \"Sheet5\", \"Sheet2\"}, f.GetSheetList())\n\n\t// Test move sheet with invalid sheet name\n\tassert.Equal(t, ErrSheetNameBlank, f.MoveSheet(\"\", \"Sheet2\"))\n\tassert.Equal(t, ErrSheetNameBlank, f.MoveSheet(\"Sheet1\", \"\"))\n\n\t// Test move sheet on not exists worksheet\n\tassert.Equal(t, ErrSheetNotExist{\"SheetN\"}, f.MoveSheet(\"SheetN\", \"Sheet2\"))\n\tassert.Equal(t, ErrSheetNotExist{\"SheetN\"}, f.MoveSheet(\"Sheet1\", \"SheetN\"))\n\n\t// Test move sheet with unsupported workbook charset\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.MoveSheet(\"Sheet2\", \"Sheet1\"), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestDeleteAndAdjustDefinedNames(t *testing.T) {\n\tdeleteAndAdjustDefinedNames(nil, 0)\n\tdeleteAndAdjustDefinedNames(&xlsxWorkbook{}, 0)\n}\n\nfunc TestGetSheetID(t *testing.T) {\n\tf := NewFile()\n\t_, err := f.NewSheet(\"Sheet1\")\n\tassert.NoError(t, err)\n\tid := f.getSheetID(\"sheet1\")\n\tassert.NotEqual(t, -1, id)\n}\n\nfunc TestSetSheetVisible(t *testing.T) {\n\tf := NewFile()\n\t// Test set sheet visible with invalid sheet name\n\tassert.EqualError(t, f.SetSheetVisible(\"Sheet:1\", false), ErrSheetNameInvalid.Error())\n\tf.WorkBook.Sheets.Sheet[0].Name = \"SheetN\"\n\tassert.EqualError(t, f.SetSheetVisible(\"Sheet1\", false), \"sheet SheetN does not exist\")\n\t// Test set sheet visible with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetSheetVisible(\"Sheet1\", false), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test set sheet visible with empty sheet views\n\tf = NewFile()\n\t_, err := f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet2.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetViews = nil\n\tassert.NoError(t, f.SetSheetVisible(\"Sheet2\", false))\n\tvisible, err := f.GetSheetVisible(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.False(t, visible)\n}\n\nfunc TestGetSheetVisible(t *testing.T) {\n\tf := NewFile()\n\t// Test get sheet visible with invalid sheet name\n\tvisible, err := f.GetSheetVisible(\"Sheet:1\")\n\tassert.Equal(t, false, visible)\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n}\n\nfunc TestGetSheetIndex(t *testing.T) {\n\tf := NewFile()\n\t// Test get sheet index with invalid sheet name\n\tidx, err := f.GetSheetIndex(\"Sheet:1\")\n\tassert.Equal(t, -1, idx)\n\tassert.EqualError(t, err, ErrSheetNameInvalid.Error())\n}\n\nfunc TestSetContentTypes(t *testing.T) {\n\tf := NewFile()\n\t// Test set content type with unsupported charset content types\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.setContentTypes(\"/xl/worksheets/sheet1.xml\", ContentTypeSpreadSheetMLWorksheet), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestRemoveContentTypesPart(t *testing.T) {\n\tf := NewFile()\n\t// Test delete sheet from content types with unsupported charset content types\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.removeContentTypesPart(ContentTypeSpreadSheetMLWorksheet, \"/xl/worksheets/sheet1.xml\"), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc BenchmarkNewSheet(b *testing.B) {\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tnewSheetWithSet()\n\t\t}\n\t})\n}\n\nfunc newSheetWithSet() {\n\tfile := NewFile()\n\tfor i := 0; i < 1000; i++ {\n\t\t_ = file.SetCellInt(\"Sheet1\", \"A\"+strconv.Itoa(i+1), int64(i))\n\t}\n\tfile = nil\n}\n\nfunc BenchmarkFile_SaveAs(b *testing.B) {\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tnewSheetWithSave()\n\t\t}\n\t})\n}\n\nfunc newSheetWithSave() {\n\tfile := NewFile()\n\tfor i := 0; i < 1000; i++ {\n\t\t_ = file.SetCellInt(\"Sheet1\", \"A\"+strconv.Itoa(i+1), int64(i))\n\t}\n\t_ = file.Save()\n}\n\nfunc TestAttrValToBool(t *testing.T) {\n\t_, err := attrValToBool(\"hidden\", []xml.Attr{\n\t\t{Name: xml.Name{Local: \"hidden\"}},\n\t})\n\tassert.EqualError(t, err, `strconv.ParseBool: parsing \"\": invalid syntax`)\n\n\tgot, err := attrValToBool(\"hidden\", []xml.Attr{\n\t\t{Name: xml.Name{Local: \"hidden\"}, Value: \"1\"},\n\t})\n\tassert.NoError(t, err)\n\tassert.Equal(t, true, got)\n}\n\nfunc TestAttrValToFloat(t *testing.T) {\n\t_, err := attrValToFloat(\"ht\", []xml.Attr{\n\t\t{Name: xml.Name{Local: \"ht\"}},\n\t})\n\tassert.EqualError(t, err, `strconv.ParseFloat: parsing \"\": invalid syntax`)\n\n\tgot, err := attrValToFloat(\"ht\", []xml.Attr{\n\t\t{Name: xml.Name{Local: \"ht\"}, Value: \"42.1\"},\n\t})\n\tassert.NoError(t, err)\n\tassert.Equal(t, 42.1, got)\n}\n\nfunc TestSetSheetBackgroundFromBytes(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetSheetName(\"Sheet1\", \".svg\"))\n\tfor i, imageTypes := range []string{\n\t\t\".svg\", \".bmp\", \".emf\", \".emz\", \".gif\",\n\t\t\".jpg\", \".png\", \".tif\", \".wmf\", \".wmz\",\n\t} {\n\t\tfile := fmt.Sprintf(\"excelize%s\", imageTypes)\n\t\tif i > 0 {\n\t\t\tfile = filepath.Join(\"test\", \"images\", fmt.Sprintf(\"excel%s\", imageTypes))\n\t\t\t_, err := f.NewSheet(imageTypes)\n\t\t\tassert.NoError(t, err)\n\t\t}\n\t\timg, err := os.Open(file)\n\t\tassert.NoError(t, err)\n\t\tcontent, err := io.ReadAll(img)\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, img.Close())\n\t\tassert.NoError(t, f.SetSheetBackgroundFromBytes(imageTypes, imageTypes, content))\n\t}\n\t// Test set worksheet background with invalid sheet name\n\timg, err := os.Open(filepath.Join(\"test\", \"images\", \"excel.png\"))\n\tassert.NoError(t, err)\n\tcontent, err := io.ReadAll(img)\n\tassert.NoError(t, err)\n\tassert.EqualError(t, f.SetSheetBackgroundFromBytes(\"Sheet:1\", \".png\", content), ErrSheetNameInvalid.Error())\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetSheetBackgroundFromBytes.xlsx\")))\n\tassert.NoError(t, f.Close())\n\n\tassert.EqualError(t, f.SetSheetBackgroundFromBytes(\"Sheet1\", \".svg\", nil), ErrParameterInvalid.Error())\n}\n\nfunc TestCheckSheetName(t *testing.T) {\n\tfor expected, name := range map[error]string{\n\t\t// Test valid sheet name\n\t\tnil: \"Sheet1\",\n\t\tnil: \"She'et1\",\n\t\t// Test invalid sheet name, empty name\n\t\tErrSheetNameBlank: \"\",\n\t\t// Test invalid sheet name, include :\\/?*[]\n\t\tErrSheetNameInvalid: \"Sheet:\",\n\t\tErrSheetNameInvalid: `Sheet\\`,\n\t\tErrSheetNameInvalid: \"Sheet/\",\n\t\tErrSheetNameInvalid: \"Sheet?\",\n\t\tErrSheetNameInvalid: \"Sheet*\",\n\t\tErrSheetNameInvalid: \"Sheet[\",\n\t\tErrSheetNameInvalid: \"Sheet]\",\n\t\t// Test invalid sheet name, single quotes at the front or at the end\n\t\tErrSheetNameSingleQuote: \"'Sheet\",\n\t\tErrSheetNameSingleQuote: \"Sheet'\",\n\t\t// Test invalid sheet name, exceed max length\n\t\tErrSheetNameLength: \"Sheet\" + strings.Repeat(\"\\U0001F600\", 14),\n\t} {\n\t\tassert.Equal(t, expected, checkSheetName(name))\n\t}\n}\n\nfunc TestSheetDimension(t *testing.T) {\n\tf := NewFile()\n\tconst sheetName = \"Sheet1\"\n\t// Test get a new worksheet dimension\n\tdimension, err := f.GetSheetDimension(sheetName)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"A1\", dimension)\n\t// Test remove the worksheet dimension\n\tassert.NoError(t, f.SetSheetDimension(sheetName, \"\"))\n\tassert.NoError(t, err)\n\tdimension, err = f.GetSheetDimension(sheetName)\n\tassert.NoError(t, err)\n\tassert.Empty(t, dimension)\n\t// Test set the worksheet dimension\n\tfor _, excepted := range []string{\"A1\", \"A1:D5\", \"A1:XFD1048576\", \"a1\", \"A1:d5\"} {\n\t\terr = f.SetSheetDimension(sheetName, excepted)\n\t\tassert.NoError(t, err)\n\t\tdimension, err := f.GetSheetDimension(sheetName)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, strings.ToUpper(excepted), dimension)\n\t}\n\t// Test set the worksheet dimension with invalid range reference or no exists worksheet\n\tfor _, c := range []struct {\n\t\tsheetName string\n\t\trangeRef  string\n\t\terr       string\n\t}{\n\t\t{\"Sheet1\", \"A-1\", \"cannot convert cell \\\"A-1\\\" to coordinates: invalid cell name \\\"A-1\\\"\"},\n\t\t{\"Sheet1\", \"A1:B-1\", \"cannot convert cell \\\"B-1\\\" to coordinates: invalid cell name \\\"B-1\\\"\"},\n\t\t{\"Sheet1\", \"A1:XFD1048577\", \"row number exceeds maximum limit\"},\n\t\t{\"Sheet1\", \"123\", \"cannot convert cell \\\"123\\\" to coordinates: invalid cell name \\\"123\\\"\"},\n\t\t{\"Sheet1\", \"A:B\", \"cannot convert cell \\\"A\\\" to coordinates: invalid cell name \\\"A\\\"\"},\n\t\t{\"Sheet1\", \":B10\", \"cannot convert cell \\\"\\\" to coordinates: invalid cell name \\\"\\\"\"},\n\t\t{\"Sheet1\", \"XFE1\", ErrColumnNumber.Error()},\n\t\t{\"Sheet1\", \"A1048577\", \"row number exceeds maximum limit\"},\n\t\t{\"Sheet1\", \"ZZZ\", \"cannot convert cell \\\"ZZZ\\\" to coordinates: invalid cell name \\\"ZZZ\\\"\"},\n\t\t{\"SheetN\", \"A1\", \"sheet SheetN does not exist\"},\n\t\t{\"Sheet1\", \"A1:B3:D5\", ErrParameterInvalid.Error()},\n\t} {\n\t\terr = f.SetSheetDimension(c.sheetName, c.rangeRef)\n\t\tassert.EqualError(t, err, c.err)\n\t}\n\t// Test get the worksheet dimension no exists worksheet\n\tdimension, err = f.GetSheetDimension(\"SheetN\")\n\tassert.Empty(t, dimension)\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\n\t// Test get the worksheet dimension with blank worksheet name\n\tdimension, err = f.GetSheetDimension(\"\")\n\tassert.Empty(t, dimension)\n\tassert.Equal(t, err, ErrSheetNameBlank)\n\n\t// Test get the worksheet dimension with in mode\n\tf, err = OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"), Options{UnzipXMLSizeLimit: 128})\n\tassert.NoError(t, err)\n\tdimension, err = f.GetSheetDimension(\"Sheet1\")\n\tassert.Equal(t, \"A19:D22\", dimension)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Close())\n\n\t// Test get the worksheet dimension in stream mode without dimension element\n\tf, err = OpenFile(filepath.Join(\"test\", \"Book1.xlsx\"), Options{UnzipXMLSizeLimit: 128})\n\tassert.NoError(t, err)\n\ttempFile, ok := f.tempFiles.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tassert.NoError(t, os.WriteFile(tempFile.(string), fmt.Appendf(nil, `<worksheet xmlns=\"%s\"><sheetData/></worksheet>`, NameSpaceSpreadSheet.Value), 0o644))\n\tdimension, err = f.GetSheetDimension(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, dimension)\n\n\t// Test get the worksheet dimension in stream mode without sheetData element\n\tassert.NoError(t, os.WriteFile(tempFile.(string), fmt.Appendf(nil, `<worksheet xmlns=\"%s\"></worksheet>`, NameSpaceSpreadSheet.Value), 0o644))\n\tdimension, err = f.GetSheetDimension(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, dimension)\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestAddIgnoredErrors(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.AddIgnoredErrors(\"Sheet1\", \"A1\", IgnoredErrorsEvalError))\n\tassert.NoError(t, f.AddIgnoredErrors(\"Sheet1\", \"A1\", IgnoredErrorsEvalError))\n\tassert.NoError(t, f.AddIgnoredErrors(\"Sheet1\", \"A1\", IgnoredErrorsTwoDigitTextYear))\n\tassert.NoError(t, f.AddIgnoredErrors(\"Sheet1\", \"A1\", IgnoredErrorsNumberStoredAsText))\n\tassert.NoError(t, f.AddIgnoredErrors(\"Sheet1\", \"A1\", IgnoredErrorsFormula))\n\tassert.NoError(t, f.AddIgnoredErrors(\"Sheet1\", \"A1\", IgnoredErrorsFormulaRange))\n\tassert.NoError(t, f.AddIgnoredErrors(\"Sheet1\", \"A1\", IgnoredErrorsUnlockedFormula))\n\tassert.NoError(t, f.AddIgnoredErrors(\"Sheet1\", \"A1\", IgnoredErrorsEmptyCellReference))\n\tassert.NoError(t, f.AddIgnoredErrors(\"Sheet1\", \"A1\", IgnoredErrorsListDataValidation))\n\tassert.NoError(t, f.AddIgnoredErrors(\"Sheet1\", \"A1\", IgnoredErrorsCalculatedColumn))\n\n\tassert.Equal(t, ErrSheetNotExist{\"SheetN\"}, f.AddIgnoredErrors(\"SheetN\", \"A1\", IgnoredErrorsEvalError))\n\tassert.Equal(t, ErrParameterInvalid, f.AddIgnoredErrors(\"Sheet1\", \"\", IgnoredErrorsEvalError))\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddIgnoredErrors.xlsx\")))\n\tassert.NoError(t, f.Close())\n}\n"
  },
  {
    "path": "sheetpr.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"reflect\"\n\n// SetPageMargins provides a function to set worksheet page margins.\nfunc (f *File) SetPageMargins(sheet string, opts *PageLayoutMarginsOptions) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif opts == nil {\n\t\treturn err\n\t}\n\tpreparePageMargins := func(ws *xlsxWorksheet) {\n\t\tif ws.PageMargins == nil {\n\t\t\tws.PageMargins = new(xlsxPageMargins)\n\t\t}\n\t}\n\tpreparePrintOptions := func(ws *xlsxWorksheet) {\n\t\tif ws.PrintOptions == nil {\n\t\t\tws.PrintOptions = new(xlsxPrintOptions)\n\t\t}\n\t}\n\ts := reflect.ValueOf(opts).Elem()\n\tfor i := 0; i < 6; i++ {\n\t\tif !s.Field(i).IsNil() {\n\t\t\tpreparePageMargins(ws)\n\t\t\tname := s.Type().Field(i).Name\n\t\t\treflect.ValueOf(ws.PageMargins).Elem().FieldByName(name).Set(s.Field(i).Elem())\n\t\t}\n\t}\n\tif opts.Horizontally != nil {\n\t\tpreparePrintOptions(ws)\n\t\tws.PrintOptions.HorizontalCentered = *opts.Horizontally\n\t}\n\tif opts.Vertically != nil {\n\t\tpreparePrintOptions(ws)\n\t\tws.PrintOptions.VerticalCentered = *opts.Vertically\n\t}\n\treturn err\n}\n\n// GetPageMargins provides a function to get worksheet page margins.\nfunc (f *File) GetPageMargins(sheet string) (PageLayoutMarginsOptions, error) {\n\topts := PageLayoutMarginsOptions{\n\t\tBottom: float64Ptr(0.75),\n\t\tFooter: float64Ptr(0.3),\n\t\tHeader: float64Ptr(0.3),\n\t\tLeft:   float64Ptr(0.7),\n\t\tRight:  float64Ptr(0.7),\n\t\tTop:    float64Ptr(0.75),\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\tif ws.PageMargins != nil {\n\t\topts.Bottom = float64Ptr(ws.PageMargins.Bottom)\n\t\topts.Footer = float64Ptr(ws.PageMargins.Footer)\n\t\topts.Header = float64Ptr(ws.PageMargins.Header)\n\t\topts.Left = float64Ptr(ws.PageMargins.Left)\n\t\topts.Right = float64Ptr(ws.PageMargins.Right)\n\t\topts.Top = float64Ptr(ws.PageMargins.Top)\n\t}\n\tif ws.PrintOptions != nil {\n\t\topts.Horizontally = boolPtr(ws.PrintOptions.HorizontalCentered)\n\t\topts.Vertically = boolPtr(ws.PrintOptions.VerticalCentered)\n\t}\n\treturn opts, err\n}\n\n// prepareSheetPr create sheetPr element which not exist.\nfunc (ws *xlsxWorksheet) prepareSheetPr() {\n\tif ws.SheetPr == nil {\n\t\tws.SheetPr = new(xlsxSheetPr)\n\t}\n}\n\n// setSheetOutlineProps set worksheet outline properties by given options.\nfunc (ws *xlsxWorksheet) setSheetOutlineProps(opts *SheetPropsOptions) {\n\tprepareOutlinePr := func(ws *xlsxWorksheet) {\n\t\tws.prepareSheetPr()\n\t\tif ws.SheetPr.OutlinePr == nil {\n\t\t\tws.SheetPr.OutlinePr = new(xlsxOutlinePr)\n\t\t}\n\t}\n\tif opts.OutlineSummaryBelow != nil {\n\t\tprepareOutlinePr(ws)\n\t\tws.SheetPr.OutlinePr.SummaryBelow = opts.OutlineSummaryBelow\n\t}\n\tif opts.OutlineSummaryRight != nil {\n\t\tprepareOutlinePr(ws)\n\t\tws.SheetPr.OutlinePr.SummaryRight = opts.OutlineSummaryRight\n\t}\n}\n\n// setSheetProps set worksheet format properties by given options.\nfunc (ws *xlsxWorksheet) setSheetProps(opts *SheetPropsOptions) {\n\tpreparePageSetUpPr := func(ws *xlsxWorksheet) {\n\t\tws.prepareSheetPr()\n\t\tif ws.SheetPr.PageSetUpPr == nil {\n\t\t\tws.SheetPr.PageSetUpPr = new(xlsxPageSetUpPr)\n\t\t}\n\t}\n\tprepareTabColor := func(ws *xlsxWorksheet) {\n\t\tws.prepareSheetPr()\n\t\tif ws.SheetPr.TabColor == nil {\n\t\t\tws.SheetPr.TabColor = new(xlsxColor)\n\t\t}\n\t}\n\tif opts.CodeName != nil {\n\t\tws.prepareSheetPr()\n\t\tws.SheetPr.CodeName = *opts.CodeName\n\t}\n\tif opts.EnableFormatConditionsCalculation != nil {\n\t\tws.prepareSheetPr()\n\t\tws.SheetPr.EnableFormatConditionsCalculation = opts.EnableFormatConditionsCalculation\n\t}\n\tif opts.Published != nil {\n\t\tws.prepareSheetPr()\n\t\tws.SheetPr.Published = opts.Published\n\t}\n\tif opts.AutoPageBreaks != nil {\n\t\tpreparePageSetUpPr(ws)\n\t\tws.SheetPr.PageSetUpPr.AutoPageBreaks = *opts.AutoPageBreaks\n\t}\n\tif opts.FitToPage != nil {\n\t\tpreparePageSetUpPr(ws)\n\t\tws.SheetPr.PageSetUpPr.FitToPage = *opts.FitToPage\n\t}\n\tws.setSheetOutlineProps(opts)\n\ts := reflect.ValueOf(opts).Elem()\n\tfor i := 5; i < 9; i++ {\n\t\tif !s.Field(i).IsNil() {\n\t\t\tprepareTabColor(ws)\n\t\t\tname := s.Type().Field(i).Name\n\t\t\tfld := reflect.ValueOf(ws.SheetPr.TabColor).Elem().FieldByName(name[8:])\n\t\t\tif s.Field(i).Kind() == reflect.Ptr && fld.Kind() == reflect.Ptr {\n\t\t\t\tfld.Set(s.Field(i))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfld.Set(s.Field(i).Elem())\n\t\t}\n\t}\n}\n\n// SetSheetProps provides a function to set worksheet properties.\nfunc (f *File) SetSheetProps(sheet string, opts *SheetPropsOptions) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif opts == nil {\n\t\treturn err\n\t}\n\tws.setSheetProps(opts)\n\tif ws.SheetFormatPr == nil {\n\t\tws.SheetFormatPr = &xlsxSheetFormatPr{DefaultRowHeight: defaultRowHeight}\n\t}\n\ts := reflect.ValueOf(opts).Elem()\n\tfor i := 11; i < 18; i++ {\n\t\tif !s.Field(i).IsNil() {\n\t\t\tname := s.Type().Field(i).Name\n\t\t\treflect.ValueOf(ws.SheetFormatPr).Elem().FieldByName(name).Set(s.Field(i).Elem())\n\t\t}\n\t}\n\treturn err\n}\n\n// GetSheetProps provides a function to get worksheet properties.\nfunc (f *File) GetSheetProps(sheet string) (SheetPropsOptions, error) {\n\tbaseColWidth := uint8(8)\n\topts := SheetPropsOptions{\n\t\tEnableFormatConditionsCalculation: boolPtr(true),\n\t\tPublished:                         boolPtr(true),\n\t\tAutoPageBreaks:                    boolPtr(true),\n\t\tOutlineSummaryBelow:               boolPtr(true),\n\t\tBaseColWidth:                      &baseColWidth,\n\t}\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\tif ws.SheetPr != nil {\n\t\topts.CodeName = stringPtr(ws.SheetPr.CodeName)\n\t\tif ws.SheetPr.EnableFormatConditionsCalculation != nil {\n\t\t\topts.EnableFormatConditionsCalculation = ws.SheetPr.EnableFormatConditionsCalculation\n\t\t}\n\t\tif ws.SheetPr.Published != nil {\n\t\t\topts.Published = ws.SheetPr.Published\n\t\t}\n\t\tif ws.SheetPr.PageSetUpPr != nil {\n\t\t\topts.AutoPageBreaks = boolPtr(ws.SheetPr.PageSetUpPr.AutoPageBreaks)\n\t\t\topts.FitToPage = boolPtr(ws.SheetPr.PageSetUpPr.FitToPage)\n\t\t}\n\t\tif ws.SheetPr.OutlinePr != nil {\n\t\t\topts.OutlineSummaryBelow = ws.SheetPr.OutlinePr.SummaryBelow\n\t\t\topts.OutlineSummaryRight = ws.SheetPr.OutlinePr.SummaryRight\n\t\t}\n\t\tif ws.SheetPr.TabColor != nil {\n\t\t\topts.TabColorIndexed = intPtr(ws.SheetPr.TabColor.Indexed)\n\t\t\topts.TabColorRGB = stringPtr(ws.SheetPr.TabColor.RGB)\n\t\t\topts.TabColorTheme = ws.SheetPr.TabColor.Theme\n\t\t\topts.TabColorTint = float64Ptr(ws.SheetPr.TabColor.Tint)\n\t\t}\n\t}\n\tif ws.SheetFormatPr != nil {\n\t\topts.BaseColWidth = &ws.SheetFormatPr.BaseColWidth\n\t\topts.DefaultColWidth = float64Ptr(ws.SheetFormatPr.DefaultColWidth)\n\t\topts.DefaultRowHeight = float64Ptr(ws.SheetFormatPr.DefaultRowHeight)\n\t\topts.CustomHeight = boolPtr(ws.SheetFormatPr.CustomHeight)\n\t\topts.ZeroHeight = boolPtr(ws.SheetFormatPr.ZeroHeight)\n\t\topts.ThickTop = boolPtr(ws.SheetFormatPr.ThickTop)\n\t\topts.ThickBottom = boolPtr(ws.SheetFormatPr.ThickBottom)\n\t}\n\treturn opts, err\n}\n"
  },
  {
    "path": "sheetpr_test.go",
    "content": "package excelize\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSetPageMargins(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetPageMargins(\"Sheet1\", nil))\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).PageMargins = nil\n\tws.(*xlsxWorksheet).PrintOptions = nil\n\texpected := PageLayoutMarginsOptions{\n\t\tBottom:       float64Ptr(1.0),\n\t\tFooter:       float64Ptr(1.0),\n\t\tHeader:       float64Ptr(1.0),\n\t\tLeft:         float64Ptr(1.0),\n\t\tRight:        float64Ptr(1.0),\n\t\tTop:          float64Ptr(1.0),\n\t\tHorizontally: boolPtr(true),\n\t\tVertically:   boolPtr(true),\n\t}\n\tassert.NoError(t, f.SetPageMargins(\"Sheet1\", &expected))\n\topts, err := f.GetPageMargins(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected, opts)\n\t// Test set page margins on not exists worksheet\n\tassert.EqualError(t, f.SetPageMargins(\"SheetN\", nil), \"sheet SheetN does not exist\")\n\t// Test set page margins with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.SetPageMargins(\"Sheet:1\", nil))\n}\n\nfunc TestGetPageMargins(t *testing.T) {\n\tf := NewFile()\n\t// Test get page margins on not exists worksheet\n\t_, err := f.GetPageMargins(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get page margins with invalid sheet name\n\t_, err = f.GetPageMargins(\"Sheet:1\")\n\tassert.Equal(t, ErrSheetNameInvalid, err)\n}\n\nfunc TestSetSheetProps(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetSheetProps(\"Sheet1\", nil))\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetPr = nil\n\tws.(*xlsxWorksheet).SheetFormatPr = nil\n\tbaseColWidth, enable := uint8(8), boolPtr(true)\n\texpected := SheetPropsOptions{\n\t\tCodeName:                          stringPtr(\"code\"),\n\t\tEnableFormatConditionsCalculation: enable,\n\t\tPublished:                         enable,\n\t\tAutoPageBreaks:                    enable,\n\t\tFitToPage:                         enable,\n\t\tTabColorIndexed:                   intPtr(1),\n\t\tTabColorRGB:                       stringPtr(\"FFFF00\"),\n\t\tTabColorTheme:                     intPtr(1),\n\t\tTabColorTint:                      float64Ptr(1),\n\t\tOutlineSummaryBelow:               enable,\n\t\tOutlineSummaryRight:               enable,\n\t\tBaseColWidth:                      &baseColWidth,\n\t\tDefaultColWidth:                   float64Ptr(10),\n\t\tDefaultRowHeight:                  float64Ptr(10),\n\t\tCustomHeight:                      enable,\n\t\tZeroHeight:                        enable,\n\t\tThickTop:                          enable,\n\t\tThickBottom:                       enable,\n\t}\n\tassert.NoError(t, f.SetSheetProps(\"Sheet1\", &expected))\n\topts, err := f.GetSheetProps(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected, opts)\n\n\tws.(*xlsxWorksheet).SheetPr = nil\n\tassert.NoError(t, f.SetSheetProps(\"Sheet1\", &SheetPropsOptions{FitToPage: enable}))\n\tws.(*xlsxWorksheet).SheetPr = nil\n\tassert.NoError(t, f.SetSheetProps(\"Sheet1\", &SheetPropsOptions{TabColorRGB: stringPtr(\"FFFF00\")}))\n\tws.(*xlsxWorksheet).SheetPr = nil\n\tassert.NoError(t, f.SetSheetProps(\"Sheet1\", &SheetPropsOptions{TabColorTheme: intPtr(1)}))\n\tws.(*xlsxWorksheet).SheetPr = nil\n\tassert.NoError(t, f.SetSheetProps(\"Sheet1\", &SheetPropsOptions{TabColorTint: float64Ptr(1)}))\n\n\t// Test get column width after set base column width\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetSheetProps(\"Sheet2\", &SheetPropsOptions{BaseColWidth: &baseColWidth}))\n\twidth, err := f.GetColWidth(\"Sheet2\", \"A\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, 8.0, width)\n\t// Test set worksheet properties on not exists worksheet\n\tassert.EqualError(t, f.SetSheetProps(\"SheetN\", nil), \"sheet SheetN does not exist\")\n\t// Test set worksheet properties with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.SetSheetProps(\"Sheet:1\", nil))\n}\n\nfunc TestGetSheetProps(t *testing.T) {\n\tf := NewFile()\n\t// Test get worksheet properties on not exists worksheet\n\t_, err := f.GetSheetProps(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get worksheet properties with invalid sheet name\n\t_, err = f.GetSheetProps(\"Sheet:1\")\n\tassert.Equal(t, ErrSheetNameInvalid, err)\n}\n"
  },
  {
    "path": "sheetview.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\n// getSheetView returns the SheetView object\nfunc (f *File) getSheetView(sheet string, viewIndex int) (*xlsxSheetView, error) {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif ws.SheetViews == nil {\n\t\tws.SheetViews = &xlsxSheetViews{\n\t\t\tSheetView: []xlsxSheetView{{WorkbookViewID: 0}},\n\t\t}\n\t}\n\tif viewIndex < 0 {\n\t\tif viewIndex < -len(ws.SheetViews.SheetView) {\n\t\t\treturn nil, newViewIdxError(viewIndex)\n\t\t}\n\t\tviewIndex = len(ws.SheetViews.SheetView) + viewIndex\n\t} else if viewIndex >= len(ws.SheetViews.SheetView) {\n\t\treturn nil, newViewIdxError(viewIndex)\n\t}\n\n\treturn &(ws.SheetViews.SheetView[viewIndex]), err\n}\n\n// setSheetView set sheet view by given options.\nfunc (view *xlsxSheetView) setSheetView(opts *ViewOptions) {\n\tif opts.DefaultGridColor != nil {\n\t\tview.DefaultGridColor = opts.DefaultGridColor\n\t}\n\tif opts.RightToLeft != nil {\n\t\tview.RightToLeft = *opts.RightToLeft\n\t}\n\tif opts.ShowFormulas != nil {\n\t\tview.ShowFormulas = *opts.ShowFormulas\n\t}\n\tif opts.ShowGridLines != nil {\n\t\tview.ShowGridLines = opts.ShowGridLines\n\t}\n\tif opts.ShowRowColHeaders != nil {\n\t\tview.ShowRowColHeaders = opts.ShowRowColHeaders\n\t}\n\tif opts.ShowRuler != nil {\n\t\tview.ShowRuler = opts.ShowRuler\n\t}\n\tif opts.ShowZeros != nil {\n\t\tview.ShowZeros = opts.ShowZeros\n\t}\n\tif opts.TopLeftCell != nil {\n\t\tview.TopLeftCell = *opts.TopLeftCell\n\t}\n\tif opts.View != nil {\n\t\tif inStrSlice([]string{\"normal\", \"pageLayout\", \"pageBreakPreview\"}, *opts.View, true) != -1 {\n\t\t\tview.View = *opts.View\n\t\t}\n\t}\n\tif opts.ZoomScale != nil && *opts.ZoomScale >= 10 && *opts.ZoomScale <= 400 {\n\t\tview.ZoomScale = *opts.ZoomScale\n\t}\n}\n\n// SetSheetView sets sheet view options. The viewIndex may be negative and if\n// so is counted backward (-1 is the last view).\nfunc (f *File) SetSheetView(sheet string, viewIndex int, opts *ViewOptions) error {\n\tview, err := f.getSheetView(sheet, viewIndex)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif opts == nil {\n\t\treturn err\n\t}\n\tview.setSheetView(opts)\n\treturn nil\n}\n\n// GetSheetView gets the value of sheet view options. The viewIndex may be\n// negative and if so is counted backward (-1 is the last view).\nfunc (f *File) GetSheetView(sheet string, viewIndex int) (ViewOptions, error) {\n\topts := ViewOptions{\n\t\tDefaultGridColor:  boolPtr(true),\n\t\tShowFormulas:      boolPtr(true),\n\t\tShowGridLines:     boolPtr(true),\n\t\tShowRowColHeaders: boolPtr(true),\n\t\tShowRuler:         boolPtr(true),\n\t\tShowZeros:         boolPtr(true),\n\t\tView:              stringPtr(\"normal\"),\n\t\tZoomScale:         float64Ptr(100),\n\t}\n\tview, err := f.getSheetView(sheet, viewIndex)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\tif view.DefaultGridColor != nil {\n\t\topts.DefaultGridColor = view.DefaultGridColor\n\t}\n\topts.RightToLeft = boolPtr(view.RightToLeft)\n\topts.ShowFormulas = boolPtr(view.ShowFormulas)\n\tif view.ShowGridLines != nil {\n\t\topts.ShowGridLines = view.ShowGridLines\n\t}\n\tif view.ShowRowColHeaders != nil {\n\t\topts.ShowRowColHeaders = view.ShowRowColHeaders\n\t}\n\tif view.ShowRuler != nil {\n\t\topts.ShowRuler = view.ShowRuler\n\t}\n\tif view.ShowZeros != nil {\n\t\topts.ShowZeros = view.ShowZeros\n\t}\n\topts.TopLeftCell = stringPtr(view.TopLeftCell)\n\tif view.View != \"\" {\n\t\topts.View = stringPtr(view.View)\n\t}\n\tif view.ZoomScale >= 10 && view.ZoomScale <= 400 {\n\t\topts.ZoomScale = float64Ptr(view.ZoomScale)\n\t}\n\treturn opts, err\n}\n"
  },
  {
    "path": "sheetview_test.go",
    "content": "package excelize\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSetView(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetSheetView(\"Sheet1\", -1, nil))\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).SheetViews = nil\n\texpected := ViewOptions{\n\t\tDefaultGridColor:  boolPtr(false),\n\t\tRightToLeft:       boolPtr(false),\n\t\tShowFormulas:      boolPtr(false),\n\t\tShowGridLines:     boolPtr(false),\n\t\tShowRowColHeaders: boolPtr(false),\n\t\tShowRuler:         boolPtr(false),\n\t\tShowZeros:         boolPtr(false),\n\t\tTopLeftCell:       stringPtr(\"A1\"),\n\t\tView:              stringPtr(\"normal\"),\n\t\tZoomScale:         float64Ptr(120),\n\t}\n\tassert.NoError(t, f.SetSheetView(\"Sheet1\", 0, &expected))\n\topts, err := f.GetSheetView(\"Sheet1\", 0)\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected, opts)\n\t// Test set sheet view options with invalid view index\n\tassert.EqualError(t, f.SetSheetView(\"Sheet1\", 1, nil), \"view index 1 out of range\")\n\tassert.EqualError(t, f.SetSheetView(\"Sheet1\", -2, nil), \"view index -2 out of range\")\n\t// Test set sheet view options on not exists worksheet\n\tassert.EqualError(t, f.SetSheetView(\"SheetN\", 0, nil), \"sheet SheetN does not exist\")\n}\n\nfunc TestGetView(t *testing.T) {\n\tf := NewFile()\n\t_, err := f.getSheetView(\"SheetN\", 0)\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get sheet view options with invalid view index\n\t_, err = f.GetSheetView(\"Sheet1\", 1)\n\tassert.EqualError(t, err, \"view index 1 out of range\")\n\t_, err = f.GetSheetView(\"Sheet1\", -2)\n\tassert.EqualError(t, err, \"view index -2 out of range\")\n\t// Test get sheet view options on not exists worksheet\n\t_, err = f.GetSheetView(\"SheetN\", 0)\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n}\n"
  },
  {
    "path": "slicer.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n)\n\n// SlicerOptions represents the settings of the slicer.\n//\n// Name specifies the slicer name, should be an existing field name of the given\n// table or pivot table, this setting is required.\n//\n// Cell specifies the left top cell coordinates the position for inserting the\n// slicer, this setting is required.\n//\n// TableSheet specifies the worksheet name of the table or pivot table, this\n// setting is required.\n//\n// TableName specifies the name of the table or pivot table, this setting is\n// required.\n//\n// Caption specifies the caption of the slicer, this setting is optional.\n//\n// Macro used for set macro for the slicer, the workbook extension should be\n// XLSM or XLTM.\n//\n// Width specifies the width of the slicer, this setting is optional.\n//\n// Height specifies the height of the slicer, this setting is optional.\n//\n// DisplayHeader specifies if display header of the slicer, this setting is\n// optional, the default setting is display.\n//\n// ItemDesc specifies descending (Z-A) item sorting, this setting is optional,\n// and the default setting is false (represents ascending).\n//\n// Format specifies the format of the slicer, this setting is optional.\ntype SlicerOptions struct {\n\tslicerXML       string\n\tslicerCacheXML  string\n\tslicerCacheName string\n\tslicerSheetName string\n\tslicerSheetRID  string\n\tdrawingXML      string\n\tName            string\n\tCell            string\n\tTableSheet      string\n\tTableName       string\n\tCaption         string\n\tMacro           string\n\tWidth           uint\n\tHeight          uint\n\tDisplayHeader   *bool\n\tItemDesc        bool\n\tFormat          GraphicOptions\n}\n\n// AddSlicer function inserts a slicer by giving the worksheet name and slicer\n// settings.\n//\n// For example, insert a slicer on the Sheet1!E1 with field Column1 for the\n// table named Table1:\n//\n//\terr := f.AddSlicer(\"Sheet1\", &excelize.SlicerOptions{\n//\t    Name:       \"Column1\",\n//\t    Cell:       \"E1\",\n//\t    TableSheet: \"Sheet1\",\n//\t    TableName:  \"Table1\",\n//\t    Caption:    \"Column1\",\n//\t    Width:      200,\n//\t    Height:     200,\n//\t})\nfunc (f *File) AddSlicer(sheet string, opts *SlicerOptions) error {\n\topts, err := parseSlicerOptions(opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttable, pivotTable, colIdx, err := f.getSlicerSource(opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\textURI, ns := ExtURISlicerListX14, NameSpaceDrawingMLA14\n\tif table != nil {\n\t\textURI = ExtURISlicerListX15\n\t\tns = NameSpaceDrawingMLSlicerX15\n\t}\n\tslicerID, err := f.addSheetSlicer(sheet, extURI)\n\tif err != nil {\n\t\treturn err\n\t}\n\tslicerCacheName, err := f.setSlicerCache(colIdx, opts, table, pivotTable)\n\tif err != nil {\n\t\treturn err\n\t}\n\tslicerName := f.genSlicerName(opts.Name)\n\tif err := f.addDrawingSlicer(sheet, slicerName, ns, opts); err != nil {\n\t\treturn err\n\t}\n\treturn f.addSlicer(slicerID, xlsxSlicer{\n\t\tName:        slicerName,\n\t\tCache:       slicerCacheName,\n\t\tCaption:     opts.Caption,\n\t\tShowCaption: opts.DisplayHeader,\n\t\tRowHeight:   251883,\n\t})\n}\n\n// parseSlicerOptions provides a function to parse the format settings of the\n// slicer with default value.\nfunc parseSlicerOptions(opts *SlicerOptions) (*SlicerOptions, error) {\n\tif opts == nil {\n\t\treturn nil, ErrParameterRequired\n\t}\n\tif opts.Name == \"\" || opts.Cell == \"\" || opts.TableSheet == \"\" || opts.TableName == \"\" {\n\t\treturn nil, ErrParameterInvalid\n\t}\n\tif opts.Width == 0 {\n\t\topts.Width = defaultSlicerWidth\n\t}\n\tif opts.Height == 0 {\n\t\topts.Height = defaultSlicerHeight\n\t}\n\tformat := opts.Format\n\tgraphicOptions, err := format.parseGraphicOptions(nil)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\topts.Format = *graphicOptions\n\treturn opts, err\n}\n\n// countSlicers provides a function to get slicer files count storage in the\n// folder xl/slicers.\nfunc (f *File) countSlicers() int {\n\tcount := 0\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/slicers/slicer\") {\n\t\t\tcount++\n\t\t}\n\t\treturn true\n\t})\n\treturn count\n}\n\n// countSlicerCache provides a function to get slicer cache files count storage\n// in the folder xl/SlicerCaches.\nfunc (f *File) countSlicerCache() int {\n\tcount := 0\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/slicerCaches/slicerCache\") {\n\t\t\tcount++\n\t\t}\n\t\treturn true\n\t})\n\treturn count\n}\n\n// getSlicerSource returns the slicer data source table or pivot table settings\n// and the index of the given slicer fields in the table or pivot table\n// column.\nfunc (f *File) getSlicerSource(opts *SlicerOptions) (*Table, *PivotTableOptions, int, error) {\n\tvar (\n\t\ttable       *Table\n\t\tpivotTable  *PivotTableOptions\n\t\tcolIdx      int\n\t\terr         error\n\t\tdataRange   string\n\t\ttables      []Table\n\t\tpivotTables []PivotTableOptions\n\t)\n\tif tables, err = f.GetTables(opts.TableSheet); err != nil {\n\t\treturn table, pivotTable, colIdx, err\n\t}\n\tfor _, tbl := range tables {\n\t\tif tbl.Name == opts.TableName {\n\t\t\ttable = &tbl\n\t\t\tdataRange = fmt.Sprintf(\"%s!%s\", opts.TableSheet, tbl.Range)\n\t\t\tbreak\n\t\t}\n\t}\n\tif table == nil {\n\t\tif pivotTables, err = f.GetPivotTables(opts.TableSheet); err != nil {\n\t\t\treturn table, pivotTable, colIdx, err\n\t\t}\n\t\tfor _, tbl := range pivotTables {\n\t\t\tif tbl.Name == opts.TableName {\n\t\t\t\tpivotTable = &tbl\n\t\t\t\tdataRange = tbl.DataRange\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif pivotTable == nil {\n\t\t\treturn table, pivotTable, colIdx, newNoExistTableError(opts.TableName)\n\t\t}\n\t}\n\torder, _ := f.getTableFieldsOrder(&PivotTableOptions{DataRange: dataRange})\n\tif colIdx = inStrSlice(order, opts.Name, true); colIdx == -1 {\n\t\treturn table, pivotTable, colIdx, newInvalidSlicerNameError(opts.Name)\n\t}\n\treturn table, pivotTable, colIdx, err\n}\n\n// addSheetSlicer adds a new slicer and updates the namespace and relationships\n// parts of the worksheet by giving the worksheet name.\nfunc (f *File) addSheetSlicer(sheet, extURI string) (int, error) {\n\tvar (\n\t\tslicerID     = f.countSlicers() + 1\n\t\tws, err      = f.workSheetReader(sheet)\n\t\tdecodeExtLst = new(decodeExtLst)\n\t)\n\tif err != nil {\n\t\treturn slicerID, err\n\t}\n\tif ws.ExtLst != nil {\n\t\tif err = f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + ws.ExtLst.Ext + \"</extLst>\")).\n\t\t\tDecode(decodeExtLst); err != nil && err != io.EOF {\n\t\t\treturn slicerID, err\n\t\t}\n\t\tfor _, ext := range decodeExtLst.Ext {\n\t\t\tif ext.URI == extURI {\n\t\t\t\tslicerList := new(decodeSlicerList)\n\t\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(slicerList)\n\t\t\t\tfor _, slicer := range slicerList.Slicer {\n\t\t\t\t\tif slicer.RID != \"\" {\n\t\t\t\t\t\tsheetRelationshipsDrawingXML := f.getSheetRelationshipsTargetByID(sheet, slicer.RID)\n\t\t\t\t\t\tslicerID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingXML, \"../slicers/slicer\"), \".xml\"))\n\t\t\t\t\t\treturn slicerID, err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tsheetRelationshipsSlicerXML := \"../slicers/slicer\" + strconv.Itoa(slicerID) + \".xml\"\n\tsheetXMLPath, _ := f.getSheetXMLPath(sheet)\n\tsheetRels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(sheetXMLPath, \"xl/worksheets/\") + \".rels\"\n\trID := f.addRels(sheetRels, SourceRelationshipSlicer, sheetRelationshipsSlicerXML, \"\")\n\tf.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14)\n\treturn slicerID, f.addSheetTableSlicer(ws, rID, extURI)\n}\n\n// addSheetTableSlicer adds a new table slicer for the worksheet by giving the\n// worksheet relationships ID and extension URI.\nfunc (f *File) addSheetTableSlicer(ws *xlsxWorksheet, rID int, extURI string) error {\n\tvar (\n\t\tdecodeExtLst                 = new(decodeExtLst)\n\t\terr                          error\n\t\tslicerListBytes, extLstBytes []byte\n\t)\n\tif ws.ExtLst != nil {\n\t\tif err = f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + ws.ExtLst.Ext + \"</extLst>\")).\n\t\t\tDecode(decodeExtLst); err != nil && err != io.EOF {\n\t\t\treturn err\n\t\t}\n\t}\n\tslicerListBytes, _ = xml.Marshal(&xlsxX14SlicerList{\n\t\tSlicer: []*xlsxX14Slicer{{RID: \"rId\" + strconv.Itoa(rID)}},\n\t})\n\text := &xlsxExt{\n\t\txmlns: []xml.Attr{{Name: xml.Name{Local: \"xmlns:\" + NameSpaceSpreadSheetX14.Name.Local}, Value: NameSpaceSpreadSheetX14.Value}},\n\t\tURI:   extURI, Content: string(slicerListBytes),\n\t}\n\tif extURI == ExtURISlicerListX15 {\n\t\text.xmlns = []xml.Attr{{Name: xml.Name{Local: \"xmlns:\" + NameSpaceSpreadSheetX15.Name.Local}, Value: NameSpaceSpreadSheetX15.Value}}\n\t}\n\tdecodeExtLst.Ext = append(decodeExtLst.Ext, ext)\n\tsort.Slice(decodeExtLst.Ext, func(i, j int) bool {\n\t\treturn inStrSlice(worksheetExtURIPriority, decodeExtLst.Ext[i].URI, false) <\n\t\t\tinStrSlice(worksheetExtURIPriority, decodeExtLst.Ext[j].URI, false)\n\t})\n\textLstBytes, err = xml.Marshal(decodeExtLst)\n\tws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), \"<extLst>\"), \"</extLst>\")}\n\treturn err\n}\n\n// addSlicer adds a new slicer to the workbook by giving the slicer ID and\n// settings.\nfunc (f *File) addSlicer(slicerID int, slicer xlsxSlicer) error {\n\tslicerXML := \"xl/slicers/slicer\" + strconv.Itoa(slicerID) + \".xml\"\n\tslicers, err := f.slicerReader(slicerXML)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := f.addContentTypePart(slicerID, \"slicer\"); err != nil {\n\t\treturn err\n\t}\n\tslicers.Slicer = append(slicers.Slicer, slicer)\n\toutput, err := xml.Marshal(slicers)\n\tf.saveFileList(slicerXML, output)\n\treturn err\n}\n\n// genSlicerName generates a unique slicer cache name by giving the slicer name.\nfunc (f *File) genSlicerName(name string) string {\n\tvar (\n\t\tcnt        int\n\t\tslicerName string\n\t\tnames      []string\n\t)\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/slicers/slicer\") {\n\t\t\tslicers, err := f.slicerReader(k.(string))\n\t\t\tif err != nil {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tfor _, slicer := range slicers.Slicer {\n\t\t\t\tnames = append(names, slicer.Name)\n\t\t\t}\n\t\t}\n\t\tif strings.Contains(k.(string), \"xl/timelines/timeline\") {\n\t\t\ttimelines, err := f.timelineReader(k.(string))\n\t\t\tif err != nil {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tfor _, timeline := range timelines.Timeline {\n\t\t\t\tnames = append(names, timeline.Name)\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\tslicerName = name\n\tfor {\n\t\ttmp := slicerName\n\t\tif cnt > 0 {\n\t\t\ttmp = fmt.Sprintf(\"%s %d\", slicerName, cnt)\n\t\t}\n\t\tif inStrSlice(names, tmp, true) == -1 {\n\t\t\tslicerName = tmp\n\t\t\tbreak\n\t\t}\n\t\tcnt++\n\t}\n\treturn slicerName\n}\n\n// genSlicerCacheName generates a unique slicer cache name by giving the slicer name.\nfunc (f *File) genSlicerCacheName(name string) string {\n\tvar (\n\t\tcnt             int\n\t\tdefinedNames    []string\n\t\tslicerCacheName string\n\t)\n\tfor _, dn := range f.GetDefinedName() {\n\t\tif dn.Scope == \"Workbook\" {\n\t\t\tdefinedNames = append(definedNames, dn.Name)\n\t\t}\n\t}\n\tfor i, c := range name {\n\t\tif unicode.IsLetter(c) {\n\t\t\tslicerCacheName += string(c)\n\t\t\tcontinue\n\t\t}\n\t\tif i > 0 && (unicode.IsDigit(c) || c == '.') {\n\t\t\tslicerCacheName += string(c)\n\t\t\tcontinue\n\t\t}\n\t\tslicerCacheName += \"_\"\n\t}\n\tslicerCacheName = fmt.Sprintf(\"Slicer_%s\", slicerCacheName)\n\tfor {\n\t\ttmp := slicerCacheName\n\t\tif cnt > 0 {\n\t\t\ttmp = fmt.Sprintf(\"%s%d\", slicerCacheName, cnt)\n\t\t}\n\t\tif inStrSlice(definedNames, tmp, true) == -1 {\n\t\t\tslicerCacheName = tmp\n\t\t\tbreak\n\t\t}\n\t\tcnt++\n\t}\n\treturn slicerCacheName\n}\n\n// setSlicerCache check if a slicer cache already exists or add a new slicer\n// cache by giving the column index, slicer, table options, and returns the\n// slicer cache name.\nfunc (f *File) setSlicerCache(colIdx int, opts *SlicerOptions, table *Table, pivotTable *PivotTableOptions) (string, error) {\n\tvar ok bool\n\tvar slicerCacheName string\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/slicerCaches/slicerCache\") {\n\t\t\tslicerCache, err := f.slicerCacheReader(k.(string))\n\t\t\tif err != nil {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif pivotTable != nil && slicerCache.PivotTables != nil {\n\t\t\t\tfor _, tbl := range slicerCache.PivotTables.PivotTable {\n\t\t\t\t\tif tbl.Name == pivotTable.Name {\n\t\t\t\t\t\tok, slicerCacheName = true, slicerCache.Name\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif table == nil || slicerCache.ExtLst == nil {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\text := new(xlsxExt)\n\t\t\t_ = f.xmlNewDecoder(strings.NewReader(slicerCache.ExtLst.Ext)).Decode(ext)\n\t\t\tif ext.URI == ExtURISlicerCacheDefinition {\n\t\t\t\ttableSlicerCache := new(decodeTableSlicerCache)\n\t\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(tableSlicerCache)\n\t\t\t\tif tableSlicerCache.TableID == table.tID && tableSlicerCache.Column == colIdx+1 {\n\t\t\t\t\tok, slicerCacheName = true, slicerCache.Name\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\tif ok {\n\t\treturn slicerCacheName, nil\n\t}\n\tslicerCacheName = f.genSlicerCacheName(opts.Name)\n\treturn slicerCacheName, f.addSlicerCache(slicerCacheName, colIdx, opts, table, pivotTable)\n}\n\n// slicerReader provides a function to get the pointer to the structure\n// after deserialization of xl/slicers/slicer%d.xml.\nfunc (f *File) slicerReader(slicerXML string) (*xlsxSlicers, error) {\n\tcontent, ok := f.Pkg.Load(slicerXML)\n\tslicer := &xlsxSlicers{\n\t\tXMLNSXMC:  SourceRelationshipCompatibility.Value,\n\t\tXMLNSX:    NameSpaceSpreadSheet.Value,\n\t\tXMLNSXR10: NameSpaceSpreadSheetXR10.Value,\n\t}\n\tif ok && content != nil {\n\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).\n\t\t\tDecode(slicer); err != nil && err != io.EOF {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn slicer, nil\n}\n\n// slicerCacheReader provides a function to get the pointer to the structure\n// after deserialization of xl/slicerCaches/slicerCache%d.xml.\nfunc (f *File) slicerCacheReader(slicerCacheXML string) (*xlsxSlicerCacheDefinition, error) {\n\tcontent, ok := f.Pkg.Load(slicerCacheXML)\n\tslicerCache := &xlsxSlicerCacheDefinition{}\n\tif ok && content != nil {\n\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).\n\t\t\tDecode(slicerCache); err != nil && err != io.EOF {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn slicerCache, nil\n}\n\n// timelineReader provides a function to get the pointer to the structure\n// after deserialization of xl/timelines/timeline%d.xml.\nfunc (f *File) timelineReader(timelineXML string) (*xlsxTimelines, error) {\n\tcontent, ok := f.Pkg.Load(timelineXML)\n\ttimeline := &xlsxTimelines{\n\t\tXMLNSXMC:  SourceRelationshipCompatibility.Value,\n\t\tXMLNSX:    NameSpaceSpreadSheet.Value,\n\t\tXMLNSXR10: NameSpaceSpreadSheetXR10.Value,\n\t}\n\tif ok && content != nil {\n\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).\n\t\t\tDecode(timeline); err != nil && err != io.EOF {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn timeline, nil\n}\n\n// addSlicerCache adds a new slicer cache by giving the slicer cache name,\n// column index, slicer, and table or pivot table options.\nfunc (f *File) addSlicerCache(slicerCacheName string, colIdx int, opts *SlicerOptions, table *Table, pivotTable *PivotTableOptions) error {\n\tvar (\n\t\tsortOrder                                       string\n\t\tslicerCacheBytes, tableSlicerBytes, extLstBytes []byte\n\t\textURI                                          = ExtURISlicerCachesX14\n\t\tslicerCacheID                                   = f.countSlicerCache() + 1\n\t\tdecodeExtLst                                    = new(decodeExtLst)\n\t\tslicerCache                                     = xlsxSlicerCacheDefinition{\n\t\t\tXMLNSXMC:   SourceRelationshipCompatibility.Value,\n\t\t\tXMLNSX:     NameSpaceSpreadSheet.Value,\n\t\t\tXMLNSX15:   NameSpaceSpreadSheetX15.Value,\n\t\t\tXMLNSXR10:  NameSpaceSpreadSheetXR10.Value,\n\t\t\tName:       slicerCacheName,\n\t\t\tSourceName: opts.Name,\n\t\t}\n\t)\n\tif opts.ItemDesc {\n\t\tsortOrder = \"descending\"\n\t}\n\tif pivotTable != nil {\n\t\tpivotCacheID, err := f.addPivotCacheSlicer(pivotTable)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tslicerCache.PivotTables = &xlsxSlicerCachePivotTables{\n\t\t\tPivotTable: []xlsxSlicerCachePivotTable{\n\t\t\t\t{TabID: f.getSheetID(opts.TableSheet), Name: pivotTable.Name},\n\t\t\t},\n\t\t}\n\t\tslicerCache.Data = &xlsxSlicerCacheData{\n\t\t\tTabular: &xlsxTabularSlicerCache{\n\t\t\t\tPivotCacheID: pivotCacheID,\n\t\t\t\tSortOrder:    sortOrder,\n\t\t\t\tShowMissing:  boolPtr(false),\n\t\t\t\tItems: &xlsxTabularSlicerCacheItems{\n\t\t\t\t\tCount: 1, I: []xlsxTabularSlicerCacheItem{{S: true}},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t}\n\tif table != nil {\n\t\ttableSlicerBytes, _ = xml.Marshal(&xlsxTableSlicerCache{\n\t\t\tTableID:   table.tID,\n\t\t\tColumn:    colIdx + 1,\n\t\t\tSortOrder: sortOrder,\n\t\t})\n\t\tdecodeExtLst.Ext = append(decodeExtLst.Ext, &xlsxExt{\n\t\t\txmlns: []xml.Attr{{Name: xml.Name{Local: \"xmlns:\" + NameSpaceSpreadSheetX15.Name.Local}, Value: NameSpaceSpreadSheetX15.Value}},\n\t\t\tURI:   ExtURISlicerCacheDefinition, Content: string(tableSlicerBytes),\n\t\t})\n\t\textLstBytes, _ = xml.Marshal(decodeExtLst)\n\t\tslicerCache.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), \"<extLst>\"), \"</extLst>\")}\n\t\textURI = ExtURISlicerCachesX15\n\t}\n\tslicerCacheXML := \"xl/slicerCaches/slicerCache\" + strconv.Itoa(slicerCacheID) + \".xml\"\n\tslicerCacheBytes, _ = xml.Marshal(slicerCache)\n\tf.saveFileList(slicerCacheXML, slicerCacheBytes)\n\tif err := f.addContentTypePart(slicerCacheID, \"slicerCache\"); err != nil {\n\t\treturn err\n\t}\n\tif err := f.addWorkbookSlicerCache(slicerCacheID, extURI); err != nil {\n\t\treturn err\n\t}\n\treturn f.SetDefinedName(&DefinedName{Name: slicerCacheName, RefersTo: formulaErrorNA})\n}\n\n// addPivotCacheSlicer adds a new slicer cache by giving the pivot table options\n// and returns pivot table cache ID.\nfunc (f *File) addPivotCacheSlicer(opts *PivotTableOptions) (int, error) {\n\tvar (\n\t\tpivotCacheID                  int\n\t\tpivotCacheBytes, extLstBytes  []byte\n\t\tdecodeExtLst                  = new(decodeExtLst)\n\t\tdecodeX14PivotCacheDefinition = new(decodeX14PivotCacheDefinition)\n\t)\n\tpc, err := f.pivotCacheReader(opts.pivotCacheXML)\n\tif err != nil {\n\t\treturn pivotCacheID, err\n\t}\n\tif pc.ExtLst != nil {\n\t\t_ = f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + pc.ExtLst.Ext + \"</extLst>\")).Decode(decodeExtLst)\n\t\tfor _, ext := range decodeExtLst.Ext {\n\t\t\tif ext.URI == ExtURIPivotCacheDefinition {\n\t\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(decodeX14PivotCacheDefinition)\n\t\t\t\treturn decodeX14PivotCacheDefinition.PivotCacheID, err\n\t\t\t}\n\t\t}\n\t}\n\tpivotCacheID = f.genPivotCacheDefinitionID()\n\tpivotCacheBytes, _ = xml.Marshal(&xlsxX14PivotCacheDefinition{PivotCacheID: pivotCacheID})\n\text := &xlsxExt{\n\t\txmlns: []xml.Attr{{Name: xml.Name{Local: \"xmlns:\" + NameSpaceSpreadSheetX14.Name.Local}, Value: NameSpaceSpreadSheetX14.Value}},\n\t\tURI:   ExtURIPivotCacheDefinition, Content: string(pivotCacheBytes),\n\t}\n\tdecodeExtLst.Ext = append(decodeExtLst.Ext, ext)\n\textLstBytes, _ = xml.Marshal(decodeExtLst)\n\tpc.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), \"<extLst>\"), \"</extLst>\")}\n\tpivotCache, err := xml.Marshal(pc)\n\tf.saveFileList(opts.pivotCacheXML, pivotCache)\n\treturn pivotCacheID, err\n}\n\n// addDrawingSlicer adds a slicer shape and fallback shape by giving the\n// worksheet name, slicer name, and slicer options.\nfunc (f *File) addDrawingSlicer(sheet, slicerName string, ns xml.Attr, opts *SlicerOptions) error {\n\tdrawingID := f.countDrawings() + 1\n\tdrawingXML := \"xl/drawings/drawing\" + strconv.Itoa(drawingID) + \".xml\"\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdrawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML)\n\tcontent, cellAnchor, cNvPrID, err := f.cellAnchorShape(sheet, drawingXML, opts.Cell, opts.Width, opts.Height, opts.Format)\n\tif err != nil {\n\t\treturn err\n\t}\n\tgraphicFrame := xlsxGraphicFrame{\n\t\tMacro: opts.Macro,\n\t\tNvGraphicFramePr: xlsxNvGraphicFramePr{\n\t\t\tCNvPr: &xlsxCNvPr{\n\t\t\t\tID:    cNvPrID,\n\t\t\t\tName:  slicerName,\n\t\t\t\tDescr: opts.Format.AltText,\n\t\t\t},\n\t\t},\n\t\tXfrm: xlsxXfrm{Off: xlsxOff{}, Ext: xlsxPositiveSize2D{}},\n\t\tGraphic: &xlsxGraphic{\n\t\t\tGraphicData: &xlsxGraphicData{\n\t\t\t\tURI: NameSpaceDrawingMLSlicer.Value,\n\t\t\t\tSle: &xlsxSle{XMLNS: NameSpaceDrawingMLSlicer.Value, Name: slicerName},\n\t\t\t},\n\t\t},\n\t}\n\tgraphic, _ := xml.Marshal(graphicFrame)\n\tsp := xdrSp{\n\t\tMacro: opts.Macro,\n\t\tNvSpPr: &xdrNvSpPr{\n\t\t\tCNvPr: &xlsxCNvPr{\n\t\t\t\tID:    cNvPrID,\n\t\t\t\tDescr: opts.Format.AltText,\n\t\t\t},\n\t\t\tCNvSpPr: &xdrCNvSpPr{\n\t\t\t\tTxBox: true,\n\t\t\t},\n\t\t},\n\t\tSpPr: &xlsxSpPr{\n\t\t\tXfrm:      xlsxXfrm{Off: xlsxOff{X: 2914650, Y: 152400}, Ext: xlsxPositiveSize2D{Cx: 1828800, Cy: 2238375}},\n\t\t\tSolidFill: &aSolidFill{SrgbClr: &aSrgbClr{Val: stringPtr(\"FFFFFF\")}},\n\t\t\tPrstGeom: xlsxPrstGeom{\n\t\t\t\tPrst: \"rect\",\n\t\t\t},\n\t\t\tLn: xlsxLineProperties{W: 1, SolidFill: &xlsxInnerXML{Content: \"<a:prstClr val=\\\"black\\\"/>\"}},\n\t\t},\n\t\tTxBody: &xdrTxBody{\n\t\t\tBodyPr: &aBodyPr{VertOverflow: \"clip\", HorzOverflow: \"clip\"},\n\t\t\tP: []*aP{\n\t\t\t\t{R: &aR{T: \"This shape represents a table slicer. Table slicers are not supported in this version of Excel.\"}},\n\t\t\t\t{R: &aR{T: \"If the shape was modified in an earlier version of Excel, or if the workbook was saved in Excel 2007 or earlier, the slicer can't be used.\"}},\n\t\t\t},\n\t\t},\n\t}\n\tshape, _ := xml.Marshal(sp)\n\tcellAnchor.ClientData = &xdrClientData{\n\t\tFLocksWithSheet:  *opts.Format.Locked,\n\t\tFPrintsWithSheet: *opts.Format.PrintObject,\n\t}\n\tchoice := xlsxChoice{Requires: ns.Name.Local, Content: string(graphic)}\n\tif ns.Value == NameSpaceDrawingMLA14.Value { // pivot table slicer\n\t\tchoice.XMLNSA14 = ns.Value\n\t}\n\tif ns.Value == NameSpaceDrawingMLSlicerX15.Value { // table slicer\n\t\tchoice.XMLNSSle15 = ns.Value\n\t}\n\tfallback := xlsxFallback{Content: string(shape)}\n\tchoiceBytes, _ := xml.Marshal(choice)\n\tshapeBytes, _ := xml.Marshal(fallback)\n\tcellAnchor.AlternateContent = append(cellAnchor.AlternateContent, &xlsxAlternateContent{\n\t\tXMLNSMC: SourceRelationshipCompatibility.Value,\n\t\tContent: string(choiceBytes) + string(shapeBytes),\n\t})\n\tif opts.Format.Positioning == \"oneCell\" {\n\t\tcontent.OneCellAnchor = append(content.OneCellAnchor, cellAnchor)\n\t} else {\n\t\tcontent.TwoCellAnchor = append(content.TwoCellAnchor, cellAnchor)\n\t}\n\n\tf.Drawings.Store(drawingXML, content)\n\treturn f.addContentTypePart(drawingID, \"drawings\")\n}\n\n// addWorkbookSlicerCache add the association ID of the slicer cache in\n// workbook.xml.\nfunc (f *File) addWorkbookSlicerCache(slicerCacheID int, URI string) error {\n\tvar (\n\t\twb                                               *xlsxWorkbook\n\t\terr                                              error\n\t\tidx                                              int\n\t\tappendMode                                       bool\n\t\tdecodeExtLst                                     = new(decodeExtLst)\n\t\tdecodeSlicerCaches                               = new(decodeSlicerCaches)\n\t\tx14SlicerCaches                                  = new(xlsxX14SlicerCaches)\n\t\tx15SlicerCaches                                  = new(xlsxX15SlicerCaches)\n\t\text                                              *xlsxExt\n\t\tslicerCacheBytes, slicerCachesBytes, extLstBytes []byte\n\t)\n\tif wb, err = f.workbookReader(); err != nil {\n\t\treturn err\n\t}\n\trID := f.addRels(f.getWorkbookRelsPath(), SourceRelationshipSlicerCache, fmt.Sprintf(\"/xl/slicerCaches/slicerCache%d.xml\", slicerCacheID), \"\")\n\tif wb.ExtLst != nil { // append mode ext\n\t\tif err = f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + wb.ExtLst.Ext + \"</extLst>\")).\n\t\t\tDecode(decodeExtLst); err != nil && err != io.EOF {\n\t\t\treturn err\n\t\t}\n\t\tfor idx, ext = range decodeExtLst.Ext {\n\t\t\tif ext.URI == URI {\n\t\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(decodeSlicerCaches)\n\t\t\t\tslicerCache := xlsxX14SlicerCache{RID: fmt.Sprintf(\"rId%d\", rID)}\n\t\t\t\tslicerCacheBytes, _ = xml.Marshal(slicerCache)\n\t\t\t\tif URI == ExtURISlicerCachesX14 { // pivot table slicer\n\t\t\t\t\tx14SlicerCaches.Content = decodeSlicerCaches.Content + string(slicerCacheBytes)\n\t\t\t\t\tx14SlicerCaches.XMLNS = NameSpaceSpreadSheetX14.Value\n\t\t\t\t\tslicerCachesBytes, _ = xml.Marshal(x14SlicerCaches)\n\t\t\t\t}\n\t\t\t\tif URI == ExtURISlicerCachesX15 { // table slicer\n\t\t\t\t\tx15SlicerCaches.Content = decodeSlicerCaches.Content + string(slicerCacheBytes)\n\t\t\t\t\tx15SlicerCaches.XMLNS = NameSpaceSpreadSheetX14.Value\n\t\t\t\t\tslicerCachesBytes, _ = xml.Marshal(x15SlicerCaches)\n\t\t\t\t}\n\t\t\t\tdecodeExtLst.Ext[idx].Content = string(slicerCachesBytes)\n\t\t\t\tappendMode = true\n\t\t\t}\n\t\t}\n\t}\n\tif !appendMode {\n\t\tslicerCache := xlsxX14SlicerCache{RID: fmt.Sprintf(\"rId%d\", rID)}\n\t\tslicerCacheBytes, _ = xml.Marshal(slicerCache)\n\t\tif URI == ExtURISlicerCachesX14 {\n\t\t\tx14SlicerCaches.Content = string(slicerCacheBytes)\n\t\t\tx14SlicerCaches.XMLNS = NameSpaceSpreadSheetX14.Value\n\t\t\tslicerCachesBytes, _ = xml.Marshal(x14SlicerCaches)\n\t\t\tdecodeExtLst.Ext = append(decodeExtLst.Ext, &xlsxExt{\n\t\t\t\txmlns: []xml.Attr{{Name: xml.Name{Local: \"xmlns:\" + NameSpaceSpreadSheetX14.Name.Local}, Value: NameSpaceSpreadSheetX14.Value}},\n\t\t\t\tURI:   ExtURISlicerCachesX14, Content: string(slicerCachesBytes),\n\t\t\t})\n\t\t}\n\t\tif URI == ExtURISlicerCachesX15 {\n\t\t\tx15SlicerCaches.Content = string(slicerCacheBytes)\n\t\t\tx15SlicerCaches.XMLNS = NameSpaceSpreadSheetX14.Value\n\t\t\tslicerCachesBytes, _ = xml.Marshal(x15SlicerCaches)\n\t\t\tdecodeExtLst.Ext = append(decodeExtLst.Ext, &xlsxExt{\n\t\t\t\txmlns: []xml.Attr{{Name: xml.Name{Local: \"xmlns:\" + NameSpaceSpreadSheetX15.Name.Local}, Value: NameSpaceSpreadSheetX15.Value}},\n\t\t\t\tURI:   ExtURISlicerCachesX15, Content: string(slicerCachesBytes),\n\t\t\t})\n\t\t}\n\t}\n\tsort.Slice(decodeExtLst.Ext, func(i, j int) bool {\n\t\treturn inStrSlice(workbookExtURIPriority, decodeExtLst.Ext[i].URI, false) <\n\t\t\tinStrSlice(workbookExtURIPriority, decodeExtLst.Ext[j].URI, false)\n\t})\n\textLstBytes, err = xml.Marshal(decodeExtLst)\n\twb.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), \"<extLst>\"), \"</extLst>\")}\n\treturn err\n}\n\n// GetSlicers provides the method to get all slicers in a worksheet by a given\n// worksheet name. Note that, this function does not support getting the height,\n// width, and graphic options of the slicer shape currently.\nfunc (f *File) GetSlicers(sheet string) ([]SlicerOptions, error) {\n\tvar (\n\t\tslicers      []SlicerOptions\n\t\tws, err      = f.workSheetReader(sheet)\n\t\tdecodeExtLst = new(decodeExtLst)\n\t)\n\tif err != nil {\n\t\treturn slicers, err\n\t}\n\tif ws.ExtLst == nil {\n\t\treturn slicers, err\n\t}\n\ttarget := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)\n\tdrawingXML := strings.TrimPrefix(strings.ReplaceAll(target, \"..\", \"xl\"), \"/\")\n\tif err = f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + ws.ExtLst.Ext + \"</extLst>\")).\n\t\tDecode(decodeExtLst); err != nil && err != io.EOF {\n\t\treturn slicers, err\n\t}\n\tfor _, ext := range decodeExtLst.Ext {\n\t\tif ext.URI == ExtURISlicerListX14 || ext.URI == ExtURISlicerListX15 {\n\t\t\tslicerList := new(decodeSlicerList)\n\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(&slicerList)\n\t\t\tfor _, slicer := range slicerList.Slicer {\n\t\t\t\tif slicer.RID != \"\" {\n\t\t\t\t\topts, err := f.getSlicers(sheet, slicer.RID, drawingXML)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn slicers, err\n\t\t\t\t\t}\n\t\t\t\t\tslicers = append(slicers, opts...)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn slicers, err\n}\n\n// getSlicerCache provides a function to get a slicer cache by given slicer\n// cache name and slicer options.\nfunc (f *File) getSlicerCache(slicerCacheName string, opt *SlicerOptions) *xlsxSlicerCacheDefinition {\n\tvar (\n\t\terr         error\n\t\tslicerCache *xlsxSlicerCacheDefinition\n\t)\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/slicerCaches/slicerCache\") {\n\t\t\tslicerCache, err = f.slicerCacheReader(k.(string))\n\t\t\tif err != nil {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif slicerCache.Name == slicerCacheName {\n\t\t\t\topt.slicerCacheXML = k.(string)\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\treturn slicerCache\n}\n\n// getSlicers provides a function to get slicers options by given worksheet\n// name, slicer part relationship ID and drawing part path.\nfunc (f *File) getSlicers(sheet, rID, drawingXML string) ([]SlicerOptions, error) {\n\tvar (\n\t\topts                        []SlicerOptions\n\t\tsheetRelationshipsSlicerXML = f.getSheetRelationshipsTargetByID(sheet, rID)\n\t\tslicerXML                   = strings.ReplaceAll(sheetRelationshipsSlicerXML, \"..\", \"xl\")\n\t\tslicers, err                = f.slicerReader(slicerXML)\n\t)\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\tfor _, slicer := range slicers.Slicer {\n\t\topt := SlicerOptions{\n\t\t\tslicerXML:       slicerXML,\n\t\t\tslicerCacheName: slicer.Cache,\n\t\t\tslicerSheetName: sheet,\n\t\t\tslicerSheetRID:  rID,\n\t\t\tdrawingXML:      drawingXML,\n\t\t\tName:            slicer.Name,\n\t\t\tCaption:         slicer.Caption,\n\t\t\tDisplayHeader:   slicer.ShowCaption,\n\t\t}\n\t\tslicerCache := f.getSlicerCache(slicer.Cache, &opt)\n\t\tif slicerCache == nil {\n\t\t\treturn opts, err\n\t\t}\n\t\tif err := f.extractTableSlicer(slicerCache, &opt); err != nil {\n\t\t\treturn opts, err\n\t\t}\n\t\tif err := f.extractPivotTableSlicer(slicerCache, &opt); err != nil {\n\t\t\treturn opts, err\n\t\t}\n\t\tif err = f.extractSlicerCellAnchor(drawingXML, &opt); err != nil {\n\t\t\treturn opts, err\n\t\t}\n\t\topts = append(opts, opt)\n\t}\n\treturn opts, err\n}\n\n// extractTableSlicer extract table slicer options from slicer cache.\nfunc (f *File) extractTableSlicer(slicerCache *xlsxSlicerCacheDefinition, opt *SlicerOptions) error {\n\tif slicerCache.ExtLst != nil {\n\t\ttables, err := f.getTables()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\text := new(xlsxExt)\n\t\t_ = f.xmlNewDecoder(strings.NewReader(slicerCache.ExtLst.Ext)).Decode(ext)\n\t\tif ext.URI == ExtURISlicerCacheDefinition {\n\t\t\ttableSlicerCache := new(decodeTableSlicerCache)\n\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(tableSlicerCache)\n\t\t\topt.ItemDesc = tableSlicerCache.SortOrder == \"descending\"\n\t\t\tfor sheetName, sheetTables := range tables {\n\t\t\t\tfor _, table := range sheetTables {\n\t\t\t\t\tif tableSlicerCache.TableID == table.tID {\n\t\t\t\t\t\topt.TableName = table.Name\n\t\t\t\t\t\topt.TableSheet = sheetName\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// extractPivotTableSlicer extract pivot table slicer options from slicer cache.\nfunc (f *File) extractPivotTableSlicer(slicerCache *xlsxSlicerCacheDefinition, opt *SlicerOptions) error {\n\tpivotTables, err := f.getPivotTables()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif slicerCache.PivotTables != nil {\n\t\tfor _, pt := range slicerCache.PivotTables.PivotTable {\n\t\t\topt.TableName = pt.Name\n\t\t\tfor sheetName, sheetPivotTables := range pivotTables {\n\t\t\t\tfor _, pivotTable := range sheetPivotTables {\n\t\t\t\t\tif opt.TableName == pivotTable.Name {\n\t\t\t\t\t\topt.TableSheet = sheetName\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif slicerCache.Data != nil && slicerCache.Data.Tabular != nil {\n\t\t\topt.ItemDesc = slicerCache.Data.Tabular.SortOrder == \"descending\"\n\t\t}\n\t}\n\treturn err\n}\n\n// extractSlicerCellAnchor extract slicer drawing object from two cell anchor by\n// giving drawing part path and slicer options.\nfunc (f *File) extractSlicerCellAnchor(drawingXML string, opt *SlicerOptions) error {\n\tvar (\n\t\twsDr *xlsxWsDr\n\t\terr  error\n\t)\n\tif wsDr, _, err = f.drawingParser(drawingXML); err != nil {\n\t\treturn err\n\t}\n\twsDr.mu.Lock()\n\tdefer wsDr.mu.Unlock()\n\tfor _, anchor := range wsDr.OneCellAnchor {\n\t\tif err = f.extractSlicerFromAnchor(anchor, opt); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = f.extractSlicerFromDecodeAnchor(anchor, opt); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tfor _, anchor := range wsDr.TwoCellAnchor {\n\t\tif err = f.extractSlicerFromAnchor(anchor, opt); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = f.extractSlicerFromDecodeAnchor(anchor, opt); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn err\n}\n\n// extractSlicerFromAnchor extract slicer drawing object from cell anchor by\n// giving drawing part path and slicer options.\nfunc (f *File) extractSlicerFromAnchor(anchor *xdrCellAnchor, opt *SlicerOptions) error {\n\tvar (\n\t\tdeChoice = new(decodeChoice)\n\t\terr      error\n\t)\n\tfor _, ac := range anchor.AlternateContent {\n\t\tif ac != nil {\n\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ac.Content)).Decode(&deChoice)\n\t\t\tif deChoice.XMLNSSle15 == NameSpaceDrawingMLSlicerX15.Value || deChoice.XMLNSA14 == NameSpaceDrawingMLA14.Value {\n\t\t\t\tif deChoice.GraphicFrame.NvGraphicFramePr.CNvPr.Name == opt.Name {\n\t\t\t\t\tif anchor.From != nil {\n\t\t\t\t\t\topt.Macro = deChoice.GraphicFrame.Macro\n\t\t\t\t\t\tif opt.Cell, err = CoordinatesToCellName(anchor.From.Col+1, anchor.From.Row+1); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn err\n}\n\n// extractSlicerFromDecodeAnchor extract slicer drawing object from decode cell\n// anchor by giving drawing part path and slicer options.\nfunc (f *File) extractSlicerFromDecodeAnchor(anchor *xdrCellAnchor, opt *SlicerOptions) error {\n\tvar (\n\t\tdeCellAnchor = new(decodeCellAnchor)\n\t\tdeChoice     = new(decodeChoice)\n\t\terr          error\n\t)\n\t_ = f.xmlNewDecoder(strings.NewReader(\"<decodeCellAnchor>\" + anchor.GraphicFrame + \"</decodeCellAnchor>\")).Decode(&deCellAnchor)\n\tfor _, ac := range deCellAnchor.AlternateContent {\n\t\tif ac != nil {\n\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ac.Content)).Decode(&deChoice)\n\t\t\tif deChoice.XMLNSSle15 == NameSpaceDrawingMLSlicerX15.Value || deChoice.XMLNSA14 == NameSpaceDrawingMLA14.Value {\n\t\t\t\tif deChoice.GraphicFrame.NvGraphicFramePr.CNvPr.Name == opt.Name {\n\t\t\t\t\tif deCellAnchor.From != nil {\n\t\t\t\t\t\topt.Macro = deChoice.GraphicFrame.Macro\n\t\t\t\t\t\tif opt.Cell, err = CoordinatesToCellName(deCellAnchor.From.Col+1, deCellAnchor.From.Row+1); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn err\n}\n\n// getAllSlicers provides a function to get all slicers in a workbook.\nfunc (f *File) getAllSlicers() (map[string][]SlicerOptions, error) {\n\tslicers := map[string][]SlicerOptions{}\n\tfor _, sheetName := range f.GetSheetList() {\n\t\tsles, err := f.GetSlicers(sheetName)\n\t\te := ErrSheetNotExist{sheetName}\n\t\tif err != nil && err.Error() != newNotWorksheetError(sheetName).Error() && err.Error() != e.Error() {\n\t\t\treturn slicers, err\n\t\t}\n\t\tslicers[sheetName] = append(slicers[sheetName], sles...)\n\t}\n\treturn slicers, nil\n}\n\n// DeleteSlicer provides the method to delete a slicer by a given slicer name.\nfunc (f *File) DeleteSlicer(name string) error {\n\tsles, err := f.getAllSlicers()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, slicers := range sles {\n\t\tfor _, slicer := range slicers {\n\t\t\tif slicer.Name != name {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_ = f.deleteSlicer(slicer)\n\t\t\treturn f.deleteSlicerCache(sles, slicer)\n\t\t}\n\t}\n\treturn newNoExistSlicerError(name)\n}\n\n// getSlicers provides a function to delete slicer by given slicer options.\nfunc (f *File) deleteSlicer(opts SlicerOptions) error {\n\tslicers, err := f.slicerReader(opts.slicerXML)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor i := 0; i < len(slicers.Slicer); i++ {\n\t\tif slicers.Slicer[i].Name == opts.Name {\n\t\t\tslicers.Slicer = append(slicers.Slicer[:i], slicers.Slicer[i+1:]...)\n\t\t\ti--\n\t\t}\n\t}\n\tif len(slicers.Slicer) == 0 {\n\t\tvar (\n\t\t\textLstBytes  []byte\n\t\t\tws, err      = f.workSheetReader(opts.slicerSheetName)\n\t\t\tdecodeExtLst = new(decodeExtLst)\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + ws.ExtLst.Ext + \"</extLst>\")).\n\t\t\tDecode(decodeExtLst); err != nil && err != io.EOF {\n\t\t\treturn err\n\t\t}\n\t\tfor i, ext := range decodeExtLst.Ext {\n\t\t\tif ext.URI == ExtURISlicerListX14 || ext.URI == ExtURISlicerListX15 {\n\t\t\t\tslicerList := new(decodeSlicerList)\n\t\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(slicerList)\n\t\t\t\tfor _, slicer := range slicerList.Slicer {\n\t\t\t\t\tif slicer.RID == opts.slicerSheetRID {\n\t\t\t\t\t\tdecodeExtLst.Ext = append(decodeExtLst.Ext[:i], decodeExtLst.Ext[i+1:]...)\n\t\t\t\t\t\textLstBytes, err = xml.Marshal(decodeExtLst)\n\t\t\t\t\t\tws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), \"<extLst>\"), \"</extLst>\")}\n\t\t\t\t\t\tf.Pkg.Delete(opts.slicerXML)\n\t\t\t\t\t\t_ = f.removeContentTypesPart(ContentTypeSlicer, \"/\"+opts.slicerXML)\n\t\t\t\t\t\tf.deleteSheetRelationships(opts.slicerSheetName, opts.slicerSheetRID)\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\toutput, err := xml.Marshal(slicers)\n\tf.saveFileList(opts.slicerXML, output)\n\treturn err\n}\n\n// deleteSlicerCache provides a function to delete the slicer cache by giving\n// slicer options if the slicer cache is no longer used.\nfunc (f *File) deleteSlicerCache(sles map[string][]SlicerOptions, opts SlicerOptions) error {\n\tfor _, slicers := range sles {\n\t\tfor _, slicer := range slicers {\n\t\t\tif slicer.Name != opts.Name && slicer.slicerCacheName == opts.slicerCacheName {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\tif err := f.DeleteDefinedName(&DefinedName{Name: opts.slicerCacheName}); err != nil {\n\t\treturn err\n\t}\n\tf.Pkg.Delete(opts.slicerCacheXML)\n\treturn f.removeContentTypesPart(ContentTypeSlicerCache, \"/\"+opts.slicerCacheXML)\n}\n"
  },
  {
    "path": "slicer_test.go",
    "content": "package excelize\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSlicer(t *testing.T) {\n\tf := NewFile()\n\tdisable, colName := false, \"_!@#$%^&*()-+=|\\\\/<>\"\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B1\", colName))\n\t// Create table in a worksheet\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{\n\t\tName:  \"Table1\",\n\t\tRange: \"A1:D5\",\n\t}))\n\tassert.NoError(t, f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column1\",\n\t\tCell:       \"E1\",\n\t\tTableSheet: \"Sheet1\",\n\t\tTableName:  \"Table1\",\n\t\tCaption:    \"Column1\",\n\t}))\n\tassert.NoError(t, f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column1\",\n\t\tCell:       \"I1\",\n\t\tTableSheet: \"Sheet1\",\n\t\tTableName:  \"Table1\",\n\t\tCaption:    \"Column1\",\n\t}))\n\tassert.NoError(t, f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:          colName,\n\t\tCell:          \"M1\",\n\t\tTableSheet:    \"Sheet1\",\n\t\tTableName:     \"Table1\",\n\t\tCaption:       colName,\n\t\tMacro:         \"Button1_Click\",\n\t\tWidth:         200,\n\t\tHeight:        200,\n\t\tDisplayHeader: &disable,\n\t\tItemDesc:      true,\n\t\tFormat:        GraphicOptions{Positioning: \"oneCell\"},\n\t}))\n\t// Test get table slicers\n\tslicers, err := f.GetSlicers(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Column1\", slicers[0].Name)\n\tassert.Equal(t, \"E1\", slicers[0].Cell)\n\tassert.Equal(t, \"Sheet1\", slicers[0].TableSheet)\n\tassert.Equal(t, \"Table1\", slicers[0].TableName)\n\tassert.Equal(t, \"Column1\", slicers[0].Caption)\n\tassert.Equal(t, \"Column1 1\", slicers[1].Name)\n\tassert.Equal(t, \"I1\", slicers[1].Cell)\n\tassert.Equal(t, \"Sheet1\", slicers[1].TableSheet)\n\tassert.Equal(t, \"Table1\", slicers[1].TableName)\n\tassert.Equal(t, \"Column1\", slicers[1].Caption)\n\tassert.Equal(t, colName, slicers[2].Name)\n\tassert.Equal(t, \"M1\", slicers[2].Cell)\n\tassert.Equal(t, \"Sheet1\", slicers[2].TableSheet)\n\tassert.Equal(t, \"Table1\", slicers[2].TableName)\n\tassert.Equal(t, colName, slicers[2].Caption)\n\tassert.Equal(t, \"Button1_Click\", slicers[2].Macro)\n\tassert.False(t, *slicers[2].DisplayHeader)\n\tassert.True(t, slicers[2].ItemDesc)\n\t// Test create two pivot tables in a new worksheet\n\t_, err = f.NewSheet(\"Sheet2\")\n\tassert.NoError(t, err)\n\t// Create some data in a sheet\n\tmonth := []string{\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"}\n\tyear := []int{2017, 2018, 2019}\n\ttypes := []string{\"Meat\", \"Dairy\", \"Beverages\", \"Produce\"}\n\tregion := []string{\"East\", \"West\", \"North\", \"South\"}\n\tassert.NoError(t, f.SetSheetRow(\"Sheet2\", \"A1\", &[]string{\"Month\", \"Year\", \"Type\", \"Sales\", \"Region\"}))\n\tfor row := 2; row < 32; row++ {\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet2\", fmt.Sprintf(\"A%d\", row), month[rand.Intn(12)]))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet2\", fmt.Sprintf(\"B%d\", row), year[rand.Intn(3)]))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet2\", fmt.Sprintf(\"C%d\", row), types[rand.Intn(4)]))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet2\", fmt.Sprintf(\"D%d\", row), rand.Intn(5000)))\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet2\", fmt.Sprintf(\"E%d\", row), region[rand.Intn(4)]))\n\t}\n\tassert.NoError(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:           \"Sheet2!A1:E31\",\n\t\tPivotTableRange:     \"Sheet2!G2:M34\",\n\t\tName:                \"PivotTable1\",\n\t\tRows:                []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tFilter:              []PivotTableField{{Data: \"Region\"}},\n\t\tColumns:             []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:                []PivotTableField{{Data: \"Sales\", Subtotal: \"Sum\", Name: \"Summarize by Sum\"}},\n\t\tRowGrandTotals:      true,\n\t\tColGrandTotals:      true,\n\t\tShowDrill:           true,\n\t\tShowRowHeaders:      true,\n\t\tShowColHeaders:      true,\n\t\tShowLastColumn:      true,\n\t\tShowError:           true,\n\t\tPivotTableStyleName: \"PivotStyleLight16\",\n\t}))\n\tassert.NoError(t, f.AddPivotTable(&PivotTableOptions{\n\t\tDataRange:       \"Sheet2!A1:E31\",\n\t\tPivotTableRange: \"Sheet2!U34:O2\",\n\t\tRows:            []PivotTableField{{Data: \"Month\", DefaultSubtotal: true}, {Data: \"Year\"}},\n\t\tColumns:         []PivotTableField{{Data: \"Type\", DefaultSubtotal: true}},\n\t\tData:            []PivotTableField{{Data: \"Sales\", Subtotal: \"Average\", Name: \"Summarize by Average\"}},\n\t\tRowGrandTotals:  true,\n\t\tColGrandTotals:  true,\n\t\tShowDrill:       true,\n\t\tShowRowHeaders:  true,\n\t\tShowColHeaders:  true,\n\t\tShowLastColumn:  true,\n\t}))\n\t// Test add a pivot table slicer\n\tassert.NoError(t, f.AddSlicer(\"Sheet2\", &SlicerOptions{\n\t\tName:       \"Month\",\n\t\tCell:       \"G42\",\n\t\tTableSheet: \"Sheet2\",\n\t\tTableName:  \"PivotTable1\",\n\t\tCaption:    \"Month\",\n\t}))\n\t// Test add a pivot table slicer with duplicate field name\n\tassert.NoError(t, f.AddSlicer(\"Sheet2\", &SlicerOptions{\n\t\tName:       \"Month\",\n\t\tCell:       \"K42\",\n\t\tTableSheet: \"Sheet2\",\n\t\tTableName:  \"PivotTable1\",\n\t\tCaption:    \"Month\",\n\t}))\n\t// Test add a pivot table slicer for another pivot table in a worksheet\n\tassert.NoError(t, f.AddSlicer(\"Sheet2\", &SlicerOptions{\n\t\tName:       \"Region\",\n\t\tCell:       \"O42\",\n\t\tTableSheet: \"Sheet2\",\n\t\tTableName:  \"PivotTable2\",\n\t\tCaption:    \"Region\",\n\t\tItemDesc:   true,\n\t}))\n\t// Test get pivot table slicers\n\tslicers, err = f.GetSlicers(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Month\", slicers[0].Name)\n\tassert.Equal(t, \"G42\", slicers[0].Cell)\n\tassert.Equal(t, \"Sheet2\", slicers[0].TableSheet)\n\tassert.Equal(t, \"PivotTable1\", slicers[0].TableName)\n\tassert.Equal(t, \"Month\", slicers[0].Caption)\n\tassert.Equal(t, \"Month 1\", slicers[1].Name)\n\tassert.Equal(t, \"K42\", slicers[1].Cell)\n\tassert.Equal(t, \"Sheet2\", slicers[1].TableSheet)\n\tassert.Equal(t, \"PivotTable1\", slicers[1].TableName)\n\tassert.Equal(t, \"Month\", slicers[1].Caption)\n\tassert.Equal(t, \"Region\", slicers[2].Name)\n\tassert.Equal(t, \"O42\", slicers[2].Cell)\n\tassert.Equal(t, \"Sheet2\", slicers[2].TableSheet)\n\tassert.Equal(t, \"PivotTable2\", slicers[2].TableName)\n\tassert.Equal(t, \"Region\", slicers[2].Caption)\n\tassert.True(t, slicers[2].ItemDesc)\n\t// Test add a table slicer with empty slicer options\n\tassert.Equal(t, ErrParameterRequired, f.AddSlicer(\"Sheet1\", nil))\n\t// Test add a table slicer with invalid slicer options\n\tfor _, opts := range []*SlicerOptions{\n\t\t{Cell: \"Q1\", TableSheet: \"Sheet1\", TableName: \"Table1\"},\n\t\t{Name: \"Column\", Cell: \"Q1\", TableSheet: \"Sheet1\"},\n\t\t{Name: \"Column\", TableSheet: \"Sheet1\", TableName: \"Table1\"},\n\t} {\n\t\tassert.Equal(t, ErrParameterInvalid, f.AddSlicer(\"Sheet1\", opts))\n\t}\n\t// Test add a table slicer with not exist worksheet\n\tassert.EqualError(t, f.AddSlicer(\"SheetN\", &SlicerOptions{\n\t\tName:       \"Column2\",\n\t\tCell:       \"Q1\",\n\t\tTableSheet: \"SheetN\",\n\t\tTableName:  \"Table1\",\n\t}), \"sheet SheetN does not exist\")\n\t// Test add slicer with invalid positioning types\n\tassert.Equal(t, f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column2\",\n\t\tCell:       \"Q1\",\n\t\tTableSheet: \"Sheet1\",\n\t\tTableName:  \"Table1\",\n\t\tFormat:     GraphicOptions{Positioning: \"x\"},\n\t}), newInvalidOptionalValue(\"Positioning\", \"x\", supportedPositioning))\n\t// Test add a table slicer with not exist table name\n\tassert.Equal(t, newNoExistTableError(\"Table2\"), f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column2\",\n\t\tCell:       \"Q1\",\n\t\tTableSheet: \"Sheet1\",\n\t\tTableName:  \"Table2\",\n\t}))\n\t// Test add a table slicer with invalid slicer name\n\tassert.Equal(t, newInvalidSlicerNameError(\"Column6\"), f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column6\",\n\t\tCell:       \"Q1\",\n\t\tTableSheet: \"Sheet1\",\n\t\tTableName:  \"Table1\",\n\t}))\n\tworkbookPath := filepath.Join(\"test\", \"TestAddSlicer.xlsm\")\n\tfile, err := os.ReadFile(filepath.Join(\"test\", \"vbaProject.bin\"))\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.AddVBAProject(file))\n\tassert.NoError(t, f.SaveAs(workbookPath))\n\tassert.NoError(t, f.Close())\n\n\t// Test add a pivot table slicer with unsupported charset pivot table\n\tf, err = OpenFile(workbookPath)\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/pivotTables/pivotTable2.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddSlicer(\"Sheet2\", &SlicerOptions{\n\t\tName:       \"Month\",\n\t\tCell:       \"G42\",\n\t\tTableSheet: \"Sheet2\",\n\t\tTableName:  \"PivotTable1\",\n\t\tCaption:    \"Month\",\n\t}), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\n\t// Test open a workbook and get already exist slicers\n\tf, err = OpenFile(workbookPath)\n\tassert.NoError(t, err)\n\tslicers, err = f.GetSlicers(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Column1\", slicers[0].Name)\n\tassert.Equal(t, \"E1\", slicers[0].Cell)\n\tassert.Equal(t, \"Sheet1\", slicers[0].TableSheet)\n\tassert.Equal(t, \"Table1\", slicers[0].TableName)\n\tassert.Equal(t, \"Column1\", slicers[0].Caption)\n\tassert.Equal(t, \"Column1 1\", slicers[1].Name)\n\tassert.Equal(t, \"I1\", slicers[1].Cell)\n\tassert.Equal(t, \"Sheet1\", slicers[1].TableSheet)\n\tassert.Equal(t, \"Table1\", slicers[1].TableName)\n\tassert.Equal(t, \"Column1\", slicers[1].Caption)\n\tassert.Equal(t, colName, slicers[2].Name)\n\tassert.Equal(t, \"M1\", slicers[2].Cell)\n\tassert.Equal(t, \"Sheet1\", slicers[2].TableSheet)\n\tassert.Equal(t, \"Table1\", slicers[2].TableName)\n\tassert.Equal(t, colName, slicers[2].Caption)\n\tassert.Equal(t, \"Button1_Click\", slicers[2].Macro)\n\tassert.False(t, *slicers[2].DisplayHeader)\n\tassert.True(t, slicers[2].ItemDesc)\n\tslicers, err = f.GetSlicers(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Month\", slicers[0].Name)\n\tassert.Equal(t, \"G42\", slicers[0].Cell)\n\tassert.Equal(t, \"Sheet2\", slicers[0].TableSheet)\n\tassert.Equal(t, \"PivotTable1\", slicers[0].TableName)\n\tassert.Equal(t, \"Month\", slicers[0].Caption)\n\tassert.Equal(t, \"Month 1\", slicers[1].Name)\n\tassert.Equal(t, \"K42\", slicers[1].Cell)\n\tassert.Equal(t, \"Sheet2\", slicers[1].TableSheet)\n\tassert.Equal(t, \"PivotTable1\", slicers[1].TableName)\n\tassert.Equal(t, \"Month\", slicers[1].Caption)\n\tassert.Equal(t, \"Region\", slicers[2].Name)\n\tassert.Equal(t, \"O42\", slicers[2].Cell)\n\tassert.Equal(t, \"Sheet2\", slicers[2].TableSheet)\n\tassert.Equal(t, \"PivotTable2\", slicers[2].TableName)\n\tassert.Equal(t, \"Region\", slicers[2].Caption)\n\tassert.True(t, slicers[2].ItemDesc)\n\n\t// Test add a pivot table slicer with workbook which contains timeline\n\tf, err = OpenFile(workbookPath)\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/timelines/timeline1.xml\", []byte(fmt.Sprintf(`<timelines xmlns=\"%s\"><timeline name=\"a\"/></timelines>`, NameSpaceSpreadSheetX15.Value)))\n\tassert.NoError(t, f.AddSlicer(\"Sheet2\", &SlicerOptions{\n\t\tName:       \"Month\",\n\t\tCell:       \"G42\",\n\t\tTableSheet: \"Sheet2\",\n\t\tTableName:  \"PivotTable1\",\n\t\tCaption:    \"Month\",\n\t}))\n\tassert.NoError(t, f.Close())\n\n\t// Test add a pivot table slicer with unsupported charset timeline\n\tf, err = OpenFile(workbookPath)\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/timelines/timeline1.xml\", MacintoshCyrillicCharset)\n\tassert.NoError(t, f.AddSlicer(\"Sheet2\", &SlicerOptions{\n\t\tName:       \"Month\",\n\t\tCell:       \"G42\",\n\t\tTableSheet: \"Sheet2\",\n\t\tTableName:  \"PivotTable1\",\n\t\tCaption:    \"Month\",\n\t}))\n\tassert.NoError(t, f.Close())\n\n\t// Test add a table slicer with invalid worksheet extension list\n\tf = NewFile()\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{\n\t\tName:  \"Table1\",\n\t\tRange: \"A1:D5\",\n\t}))\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: \"<>\"}\n\tassert.Error(t, f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column1\",\n\t\tCell:       \"E1\",\n\t\tTableSheet: \"Sheet1\",\n\t\tTableName:  \"Table1\",\n\t}))\n\tassert.NoError(t, f.Close())\n\n\t// Test add a table slicer with unsupported charset slicer\n\tf = NewFile()\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{\n\t\tName:  \"Table1\",\n\t\tRange: \"A1:D5\",\n\t}))\n\tf.Pkg.Store(\"xl/slicers/slicer2.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column1\",\n\t\tCell:       \"E1\",\n\t\tTableName:  \"Table1\",\n\t\tTableSheet: \"Sheet1\",\n\t}), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\n\t// Test add a table slicer with read workbook error\n\tf = NewFile()\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{\n\t\tName:  \"Table1\",\n\t\tRange: \"A1:D5\",\n\t}))\n\tf.WorkBook.ExtLst = &xlsxExtLst{Ext: \"<>\"}\n\tassert.Error(t, f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column1\",\n\t\tCell:       \"E1\",\n\t\tTableName:  \"Table1\",\n\t\tTableSheet: \"Sheet1\",\n\t}))\n\tassert.NoError(t, f.Close())\n\n\t// Test add a table slicer with unsupported charset content types\n\tf = NewFile()\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{\n\t\tName:  \"Table1\",\n\t\tRange: \"A1:D5\",\n\t}))\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column1\",\n\t\tCell:       \"E1\",\n\t\tTableName:  \"Table1\",\n\t\tTableSheet: \"Sheet1\",\n\t}), \"XML syntax error on line 1: invalid UTF-8\")\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.addSlicer(0, xlsxSlicer{}), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\t// Create table in a worksheet\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{\n\t\tName:  \"Table1\",\n\t\tRange: \"A1:D5\",\n\t}))\n\tf.Pkg.Store(\"xl/drawings/drawing2.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column1\",\n\t\tCell:       \"E1\",\n\t\tTableSheet: \"Sheet1\",\n\t\tTableName:  \"Table1\",\n\t\tCaption:    \"Column1\",\n\t}), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\t// Test get sheet slicers without slicer\n\tslicers, err = f.GetSlicers(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, slicers)\n\t// Test get sheet slicers with not exist worksheet name\n\t_, err = f.GetSlicers(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\tassert.NoError(t, f.Close())\n\n\tf, err = OpenFile(workbookPath)\n\tassert.NoError(t, err)\n\t// Test get sheet slicers with unsupported charset slicer cache\n\tf.Pkg.Store(\"xl/slicerCaches/slicerCache1.xml\", MacintoshCyrillicCharset)\n\t_, err = f.GetSlicers(\"Sheet1\")\n\tassert.NoError(t, err)\n\t// Test get sheet slicers with unsupported charset slicer\n\tf.Pkg.Store(\"xl/slicers/slicer1.xml\", MacintoshCyrillicCharset)\n\t_, err = f.GetSlicers(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get sheet slicers with invalid worksheet extension list\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).ExtLst.Ext = \"<>\"\n\t_, err = f.GetSlicers(\"Sheet1\")\n\tassert.Error(t, err)\n\tassert.NoError(t, f.Close())\n\n\tf, err = OpenFile(workbookPath)\n\tassert.NoError(t, err)\n\t// Test get sheet slicers without slicer cache\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/slicerCaches/slicerCache\") {\n\t\t\tf.Pkg.Delete(k.(string))\n\t\t}\n\t\treturn true\n\t})\n\tslicers, err = f.GetSlicers(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Empty(t, slicers)\n\tassert.NoError(t, f.Close())\n\t// Test open a workbook and get sheet slicer with invalid cell reference in the drawing one cell anchor\n\tf, err = OpenFile(workbookPath)\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/drawings/drawing1.xml\", []byte(fmt.Sprintf(`<wsDr xmlns=\"%s\"><oneCellAnchor><from><col>-1</col><row>-1</row></from><mc:AlternateContent><mc:Choice xmlns:sle15=\"%s\"><graphicFrame><nvGraphicFramePr><cNvPr id=\"2\" name=\"Column1\"/></nvGraphicFramePr></graphicFrame></mc:Choice></mc:AlternateContent></oneCellAnchor></wsDr>`, NameSpaceDrawingMLSpreadSheet.Value, NameSpaceDrawingMLSlicerX15.Value)))\n\t_, err = f.GetSlicers(\"Sheet1\")\n\tassert.Equal(t, newCoordinatesToCellNameError(0, 0), err)\n\tassert.NoError(t, f.Close())\n\t// Test open a workbook and get sheet slicer with invalid cell reference in the drawing one cell anchor\n\tf, err = OpenFile(workbookPath)\n\tassert.NoError(t, err)\n\t// Test open a workbook and get sheet slicer with invalid cell reference in the drawing two cell anchor\n\tf.Pkg.Store(\"xl/drawings/drawing1.xml\", []byte(fmt.Sprintf(`<wsDr xmlns=\"%s\"><twoCellAnchor><from><col>-1</col><row>-1</row></from><mc:AlternateContent><mc:Choice xmlns:sle15=\"%s\"><graphicFrame><nvGraphicFramePr><cNvPr id=\"2\" name=\"Column1\"/></nvGraphicFramePr></graphicFrame></mc:Choice></mc:AlternateContent></twoCellAnchor></wsDr>`, NameSpaceDrawingMLSpreadSheet.Value, NameSpaceDrawingMLSlicerX15.Value)))\n\t_, err = f.GetSlicers(\"Sheet1\")\n\tassert.Equal(t, newCoordinatesToCellNameError(0, 0), err)\n\t// Test get sheet slicer without slicer shape in the drawing part\n\tf.Drawings.Delete(\"xl/drawings/drawing1.xml\")\n\tf.Pkg.Store(\"xl/drawings/drawing1.xml\", []byte(fmt.Sprintf(`<wsDr xmlns=\"%s\"><twoCellAnchor/></wsDr>`, NameSpaceDrawingMLSpreadSheet.Value)))\n\t_, err = f.GetSlicers(\"Sheet1\")\n\tassert.NoError(t, err)\n\tf.Drawings.Delete(\"xl/drawings/drawing1.xml\")\n\t// Test get sheet slicers with unsupported charset drawing part\n\tf.Pkg.Store(\"xl/drawings/drawing1.xml\", MacintoshCyrillicCharset)\n\t_, err = f.GetSlicers(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get sheet slicers with unsupported charset table\n\tf.Pkg.Store(\"xl/tables/table1.xml\", MacintoshCyrillicCharset)\n\t_, err = f.GetSlicers(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get sheet slicers with unsupported charset pivot table\n\tf.Pkg.Store(\"xl/pivotTables/pivotTable1.xml\", MacintoshCyrillicCharset)\n\t_, err = f.GetSlicers(\"Sheet2\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\t// Test create a workbook and get sheet slicer with invalid cell reference in the drawing one cell anchor\n\tf = NewFile()\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{\n\t\tName:  \"Table1\",\n\t\tRange: \"A1:D5\",\n\t}))\n\tassert.NoError(t, f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column1\",\n\t\tCell:       \"E1\",\n\t\tTableSheet: \"Sheet1\",\n\t\tTableName:  \"Table1\",\n\t\tCaption:    \"Column1\",\n\t\tFormat:     GraphicOptions{Positioning: \"oneCell\"},\n\t}))\n\tdrawing, ok := f.Drawings.Load(\"xl/drawings/drawing1.xml\")\n\tassert.True(t, ok)\n\tdrawing.(*xlsxWsDr).OneCellAnchor[0].From = &xlsxFrom{Col: -1, Row: -1}\n\t_, err = f.GetSlicers(\"Sheet1\")\n\tassert.Equal(t, newCoordinatesToCellNameError(0, 0), err)\n\tassert.NoError(t, f.Close())\n\t// Test create a workbook and get sheet slicer with invalid cell reference in the drawing two cell anchor\n\tf = NewFile()\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{\n\t\tName:  \"Table1\",\n\t\tRange: \"A1:D5\",\n\t}))\n\tassert.NoError(t, f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column1\",\n\t\tCell:       \"E1\",\n\t\tTableSheet: \"Sheet1\",\n\t\tTableName:  \"Table1\",\n\t\tCaption:    \"Column1\",\n\t}))\n\tdrawing, ok = f.Drawings.Load(\"xl/drawings/drawing1.xml\")\n\tassert.True(t, ok)\n\tdrawing.(*xlsxWsDr).TwoCellAnchor[0].From = &xlsxFrom{Col: -1, Row: -1}\n\t_, err = f.GetSlicers(\"Sheet1\")\n\tassert.Equal(t, newCoordinatesToCellNameError(0, 0), err)\n\tassert.NoError(t, f.Close())\n\n\t// Test open a workbook and delete slicers\n\tf, err = OpenFile(workbookPath)\n\tassert.NoError(t, err)\n\tfor _, name := range []string{colName, \"Column1 1\", \"Column1\"} {\n\t\tassert.NoError(t, f.DeleteSlicer(name))\n\t}\n\tfor _, name := range []string{\"Month\", \"Month 1\", \"Region\"} {\n\t\tassert.NoError(t, f.DeleteSlicer(name))\n\t}\n\t// Test delete slicer with no exits slicer name\n\tassert.Equal(t, newNoExistSlicerError(\"x\"), f.DeleteSlicer(\"x\"))\n\tassert.NoError(t, f.Close())\n\n\t// Test open a workbook and delete sheet slicer with unsupported charset slicer cache\n\tf, err = OpenFile(workbookPath)\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/slicers/slicer1.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.DeleteSlicer(\"Column1\"), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestAddSheetSlicer(t *testing.T) {\n\tf := NewFile()\n\t// Test add sheet slicer with not exist worksheet name\n\t_, err := f.addSheetSlicer(\"SheetN\", ExtURISlicerListX15)\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestAddSheetTableSlicer(t *testing.T) {\n\tf := NewFile()\n\t// Test add sheet table slicer with invalid worksheet extension\n\tassert.Error(t, f.addSheetTableSlicer(&xlsxWorksheet{ExtLst: &xlsxExtLst{Ext: \"<>\"}}, 0, ExtURISlicerListX15))\n\t// Test add sheet table slicer with existing worksheet extension\n\tassert.NoError(t, f.addSheetTableSlicer(&xlsxWorksheet{ExtLst: &xlsxExtLst{Ext: fmt.Sprintf(\"<ext uri=\\\"%s\\\"></ext>\", ExtURITimelineRefs)}}, 1, ExtURISlicerListX15))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestSetSlicerCache(t *testing.T) {\n\tf := NewFile()\n\tf.Pkg.Store(\"xl/slicerCaches/slicerCache1.xml\", MacintoshCyrillicCharset)\n\t_, err := f.setSlicerCache(1, &SlicerOptions{}, &Table{}, nil)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\n\tf.Pkg.Store(\"xl/slicerCaches/slicerCache2.xml\", []byte(fmt.Sprintf(`<slicerCacheDefinition xmlns=\"%s\" name=\"Slicer2\" sourceName=\"B1\"><extLst><ext uri=\"%s\"/></extLst></slicerCacheDefinition>`, NameSpaceSpreadSheetX14.Value, ExtURISlicerCacheDefinition)))\n\t_, err = f.setSlicerCache(1, &SlicerOptions{}, &Table{}, nil)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tf.Pkg.Store(\"xl/slicerCaches/slicerCache2.xml\", []byte(fmt.Sprintf(`<slicerCacheDefinition xmlns=\"%s\" name=\"Slicer1\" sourceName=\"B1\"><extLst><ext uri=\"%s\"/></extLst></slicerCacheDefinition>`, NameSpaceSpreadSheetX14.Value, ExtURISlicerCacheDefinition)))\n\t_, err = f.setSlicerCache(1, &SlicerOptions{}, &Table{}, nil)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tf.Pkg.Store(\"xl/slicerCaches/slicerCache2.xml\", []byte(fmt.Sprintf(`<slicerCacheDefinition xmlns=\"%s\" name=\"Slicer1\" sourceName=\"B1\"><extLst><ext uri=\"%s\"><tableSlicerCache tableId=\"1\" column=\"2\"/></ext></extLst></slicerCacheDefinition>`, NameSpaceSpreadSheetX14.Value, ExtURISlicerCacheDefinition)))\n\t_, err = f.setSlicerCache(1, &SlicerOptions{}, &Table{tID: 1}, nil)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Close())\n\n\tf = NewFile()\n\tf.Pkg.Store(\"xl/slicerCaches/slicerCache2.xml\", []byte(fmt.Sprintf(`<slicerCacheDefinition xmlns=\"%s\" name=\"Slicer1\" sourceName=\"B1\"></slicerCacheDefinition>`, NameSpaceSpreadSheetX14.Value)))\n\t_, err = f.setSlicerCache(1, &SlicerOptions{}, &Table{tID: 1}, nil)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestDeleteSlicer(t *testing.T) {\n\tf, slicerXML := NewFile(), \"xl/slicers/slicer1.xml\"\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{\n\t\tName:  \"Table1\",\n\t\tRange: \"A1:D5\",\n\t}))\n\tassert.NoError(t, f.AddSlicer(\"Sheet1\", &SlicerOptions{\n\t\tName:       \"Column1\",\n\t\tCell:       \"E1\",\n\t\tTableSheet: \"Sheet1\",\n\t\tTableName:  \"Table1\",\n\t\tCaption:    \"Column1\",\n\t}))\n\t// Test delete sheet slicers with invalid worksheet extension list\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).ExtLst.Ext = \"<>\"\n\tassert.Error(t, f.deleteSlicer(SlicerOptions{\n\t\tslicerXML:       slicerXML,\n\t\tslicerSheetName: \"Sheet1\",\n\t\tName:            \"Column1\",\n\t}))\n\t// Test delete slicer with unsupported charset worksheet\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.deleteSlicer(SlicerOptions{\n\t\tslicerXML:       slicerXML,\n\t\tslicerSheetName: \"Sheet1\",\n\t\tName:            \"Column1\",\n\t}), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test delete slicer with unsupported charset slicer\n\tf.Pkg.Store(slicerXML, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.deleteSlicer(SlicerOptions{slicerXML: slicerXML}), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestDeleteSlicerCache(t *testing.T) {\n\tf := NewFile()\n\t// Test delete slicer cache with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.deleteSlicerCache(nil, SlicerOptions{}), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestAddSlicerCache(t *testing.T) {\n\tf := NewFile()\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.addSlicerCache(\"Slicer1\", 0, &SlicerOptions{}, &Table{}, nil), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test add a pivot table cache slicer with unsupported charset\n\tpivotCacheXML := \"xl/pivotCache/pivotCacheDefinition1.xml\"\n\tf.Pkg.Store(pivotCacheXML, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.addSlicerCache(\"Slicer1\", 0, &SlicerOptions{}, nil,\n\t\t&PivotTableOptions{pivotCacheXML: pivotCacheXML}), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestAddDrawingSlicer(t *testing.T) {\n\tf := NewFile()\n\t// Test add a drawing slicer with not exist worksheet\n\tassert.EqualError(t, f.addDrawingSlicer(\"SheetN\", \"Column2\", NameSpaceDrawingMLSlicerX15, &SlicerOptions{\n\t\tName:       \"Column2\",\n\t\tCell:       \"Q1\",\n\t\tTableSheet: \"SheetN\",\n\t\tTableName:  \"Table1\",\n\t}), \"sheet SheetN does not exist\")\n\t// Test add a drawing slicer with invalid cell reference\n\tassert.EqualError(t, f.addDrawingSlicer(\"Sheet1\", \"Column2\", NameSpaceDrawingMLSlicerX15, &SlicerOptions{\n\t\tName:       \"Column2\",\n\t\tCell:       \"A\",\n\t\tTableSheet: \"Sheet1\",\n\t\tTableName:  \"Table1\",\n\t}), \"cannot convert cell \\\"A\\\" to coordinates: invalid cell name \\\"A\\\"\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestAddWorkbookSlicerCache(t *testing.T) {\n\t// Test add a workbook slicer cache with unsupported charset workbook\n\tf := NewFile()\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.addWorkbookSlicerCache(1, ExtURISlicerCachesX15), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestGenSlicerCacheName(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetDefinedName(&DefinedName{Name: \"Slicer_Column_1\", RefersTo: formulaErrorNA}))\n\tassert.Equal(t, \"Slicer_Column_11\", f.genSlicerCacheName(\"Column 1\"))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestAddPivotCacheSlicer(t *testing.T) {\n\tf := NewFile()\n\tpivotCacheXML := \"xl/pivotCache/pivotCacheDefinition1.xml\"\n\t// Test add a pivot table cache slicer with existing extension list\n\tf.Pkg.Store(pivotCacheXML, []byte(fmt.Sprintf(`<pivotCacheDefinition xmlns=\"%s\"><extLst><ext uri=\"%s\"><x14:pivotCacheDefinition pivotCacheId=\"1\"/></ext></extLst></pivotCacheDefinition>`, NameSpaceSpreadSheet.Value, ExtURIPivotCacheDefinition)))\n\t_, err := f.addPivotCacheSlicer(&PivotTableOptions{\n\t\tpivotCacheXML: pivotCacheXML,\n\t})\n\tassert.NoError(t, err)\n}\n"
  },
  {
    "path": "sparkline.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"io\"\n\t\"sort\"\n\t\"strings\"\n)\n\n// getSparklineGroupPresets returns the preset list of sparkline group to create\n// x14:sparklineGroups element.\nfunc getSparklineGroupPresets() []*xlsxX14SparklineGroup {\n\treturn []*xlsxX14SparklineGroup{\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(4), Tint: -0.499984740745262},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(5)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(4), Tint: -0.499984740745262},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(4), Tint: 0.39997558519241921},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(4), Tint: 0.39997558519241921},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(4)},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(4)},\n\t\t}, // 0\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(4), Tint: -0.499984740745262},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(5)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(4), Tint: -0.499984740745262},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(4), Tint: 0.39997558519241921},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(4), Tint: 0.39997558519241921},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(4)},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(4)},\n\t\t}, // 1\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(5), Tint: -0.499984740745262},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(6)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(5), Tint: -0.499984740745262},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(5), Tint: 0.39997558519241921},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(5), Tint: 0.39997558519241921},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(5)},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(5)},\n\t\t}, // 2\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(6), Tint: -0.499984740745262},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(7)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(6), Tint: -0.499984740745262},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(6), Tint: 0.39997558519241921},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(6), Tint: 0.39997558519241921},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(6)},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(6)},\n\t\t}, // 3\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(7), Tint: -0.499984740745262},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(8)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(7), Tint: -0.499984740745262},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(7), Tint: 0.39997558519241921},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(7), Tint: 0.39997558519241921},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(7)},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(7)},\n\t\t}, // 4\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(8), Tint: -0.499984740745262},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(9)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(8), Tint: -0.499984740745262},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(8), Tint: 0.39997558519241921},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(8), Tint: 0.39997558519241921},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(8)},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(8)},\n\t\t}, // 5\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(9), Tint: -0.499984740745262},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(4)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(9), Tint: -0.499984740745262},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(9), Tint: 0.39997558519241921},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(9), Tint: 0.39997558519241921},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(9)},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(9)},\n\t\t}, // 6\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(5)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(5), Tint: -0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(5), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(5), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(5)},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(5)},\n\t\t}, // 7\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(5), Tint: -0.249977111117893},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(6)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t}, // 8\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(7)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t}, // 9\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(8)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t}, // 10\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(9)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t}, // 11\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(4)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t}, // 12\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(4)},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(5)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t}, // 13\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(5)},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(6)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(5), Tint: -0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(5), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(5), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(5), Tint: -0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(5), Tint: -0.249977111117893},\n\t\t}, // 14\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(6)},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(7)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t}, // 15\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(7)},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(8)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t}, // 16\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(8)},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(9)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t}, // 17\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(9)},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(4)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t}, // 18\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(4), Tint: 0.39997558519241921},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(0), Tint: -0.499984740745262},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(4), Tint: 0.79998168889431442},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(4), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(4), Tint: -0.499984740745262},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(4), Tint: -0.499984740745262},\n\t\t}, // 19\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(5), Tint: 0.39997558519241921},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(0), Tint: -0.499984740745262},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(5), Tint: 0.79998168889431442},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(5), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(5), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(5), Tint: -0.499984740745262},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(5), Tint: -0.499984740745262},\n\t\t}, // 20\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(6), Tint: 0.39997558519241921},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(0), Tint: -0.499984740745262},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(6), Tint: 0.79998168889431442},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(6), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(6), Tint: -0.499984740745262},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(6), Tint: -0.499984740745262},\n\t\t}, // 21\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(7), Tint: 0.39997558519241921},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(0), Tint: -0.499984740745262},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(7), Tint: 0.79998168889431442},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(7), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(7), Tint: -0.499984740745262},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(7), Tint: -0.499984740745262},\n\t\t}, // 22\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(8), Tint: 0.39997558519241921},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(0), Tint: -0.499984740745262},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(8), Tint: 0.79998168889431442},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(8), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(8), Tint: -0.499984740745262},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(8), Tint: -0.499984740745262},\n\t\t}, // 23\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(9), Tint: 0.39997558519241921},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(0), Tint: -0.499984740745262},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(9), Tint: 0.79998168889431442},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(9), Tint: -0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(9), Tint: -0.499984740745262},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(9), Tint: -0.499984740745262},\n\t\t}, // 24\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(1), Tint: 0.499984740745262},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(1), Tint: 0.249977111117893},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(1), Tint: 0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(1), Tint: 0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(1), Tint: 0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(1), Tint: 0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(1), Tint: 0.249977111117893},\n\t\t}, // 25\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(1), Tint: 0.34998626667073579},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(0), Tint: 0.249977111117893},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(0), Tint: 0.249977111117893},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(0), Tint: 0.249977111117893},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(0), Tint: 0.249977111117893},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(0), Tint: 0.249977111117893},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(0), Tint: 0.249977111117893},\n\t\t}, // 26\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{RGB: \"FF323232\"},\n\t\t\tColorNegative: &xlsxColor{RGB: \"FFD00000\"},\n\t\t\tColorMarkers:  &xlsxColor{RGB: \"FFD00000\"},\n\t\t\tColorFirst:    &xlsxColor{RGB: \"FFD00000\"},\n\t\t\tColorLast:     &xlsxColor{RGB: \"FFD00000\"},\n\t\t\tColorHigh:     &xlsxColor{RGB: \"FFD00000\"},\n\t\t\tColorLow:      &xlsxColor{RGB: \"FFD00000\"},\n\t\t}, // 27\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{RGB: \"FF000000\"},\n\t\t\tColorNegative: &xlsxColor{RGB: \"FF0070C0\"},\n\t\t\tColorMarkers:  &xlsxColor{RGB: \"FF0070C0\"},\n\t\t\tColorFirst:    &xlsxColor{RGB: \"FF0070C0\"},\n\t\t\tColorLast:     &xlsxColor{RGB: \"FF0070C0\"},\n\t\t\tColorHigh:     &xlsxColor{RGB: \"FF0070C0\"},\n\t\t\tColorLow:      &xlsxColor{RGB: \"FF0070C0\"},\n\t\t}, // 28\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{RGB: \"FF376092\"},\n\t\t\tColorNegative: &xlsxColor{RGB: \"FFD00000\"},\n\t\t\tColorMarkers:  &xlsxColor{RGB: \"FFD00000\"},\n\t\t\tColorFirst:    &xlsxColor{RGB: \"FFD00000\"},\n\t\t\tColorLast:     &xlsxColor{RGB: \"FFD00000\"},\n\t\t\tColorHigh:     &xlsxColor{RGB: \"FFD00000\"},\n\t\t\tColorLow:      &xlsxColor{RGB: \"FFD00000\"},\n\t\t}, // 29\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{RGB: \"FF0070C0\"},\n\t\t\tColorNegative: &xlsxColor{RGB: \"FF000000\"},\n\t\t\tColorMarkers:  &xlsxColor{RGB: \"FF000000\"},\n\t\t\tColorFirst:    &xlsxColor{RGB: \"FF000000\"},\n\t\t\tColorLast:     &xlsxColor{RGB: \"FF000000\"},\n\t\t\tColorHigh:     &xlsxColor{RGB: \"FF000000\"},\n\t\t\tColorLow:      &xlsxColor{RGB: \"FF000000\"},\n\t\t}, // 30\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{RGB: \"FF5F5F5F\"},\n\t\t\tColorNegative: &xlsxColor{RGB: \"FFFFB620\"},\n\t\t\tColorMarkers:  &xlsxColor{RGB: \"FFD70077\"},\n\t\t\tColorFirst:    &xlsxColor{RGB: \"FF5687C2\"},\n\t\t\tColorLast:     &xlsxColor{RGB: \"FF359CEB\"},\n\t\t\tColorHigh:     &xlsxColor{RGB: \"FF56BE79\"},\n\t\t\tColorLow:      &xlsxColor{RGB: \"FFFF5055\"},\n\t\t}, // 31\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{RGB: \"FF5687C2\"},\n\t\t\tColorNegative: &xlsxColor{RGB: \"FFFFB620\"},\n\t\t\tColorMarkers:  &xlsxColor{RGB: \"FFD70077\"},\n\t\t\tColorFirst:    &xlsxColor{RGB: \"FF777777\"},\n\t\t\tColorLast:     &xlsxColor{RGB: \"FF359CEB\"},\n\t\t\tColorHigh:     &xlsxColor{RGB: \"FF56BE79\"},\n\t\t\tColorLow:      &xlsxColor{RGB: \"FFFF5055\"},\n\t\t}, // 32\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{RGB: \"FFC6EFCE\"},\n\t\t\tColorNegative: &xlsxColor{RGB: \"FFFFC7CE\"},\n\t\t\tColorMarkers:  &xlsxColor{RGB: \"FF8CADD6\"},\n\t\t\tColorFirst:    &xlsxColor{RGB: \"FFFFDC47\"},\n\t\t\tColorLast:     &xlsxColor{RGB: \"FFFFEB9C\"},\n\t\t\tColorHigh:     &xlsxColor{RGB: \"FF60D276\"},\n\t\t\tColorLow:      &xlsxColor{RGB: \"FFFF5367\"},\n\t\t}, // 33\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{RGB: \"FF00B050\"},\n\t\t\tColorNegative: &xlsxColor{RGB: \"FFFF0000\"},\n\t\t\tColorMarkers:  &xlsxColor{RGB: \"FF0070C0\"},\n\t\t\tColorFirst:    &xlsxColor{RGB: \"FFFFC000\"},\n\t\t\tColorLast:     &xlsxColor{RGB: \"FFFFC000\"},\n\t\t\tColorHigh:     &xlsxColor{RGB: \"FF00B050\"},\n\t\t\tColorLow:      &xlsxColor{RGB: \"FFFF0000\"},\n\t\t}, // 34\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(3)},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(9)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(8)},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(4)},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(5)},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(6)},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(7)},\n\t\t}, // 35\n\t\t{\n\t\t\tColorSeries:   &xlsxColor{Theme: intPtr(1)},\n\t\t\tColorNegative: &xlsxColor{Theme: intPtr(9)},\n\t\t\tColorMarkers:  &xlsxColor{Theme: intPtr(8)},\n\t\t\tColorFirst:    &xlsxColor{Theme: intPtr(4)},\n\t\t\tColorLast:     &xlsxColor{Theme: intPtr(5)},\n\t\t\tColorHigh:     &xlsxColor{Theme: intPtr(6)},\n\t\t\tColorLow:      &xlsxColor{Theme: intPtr(7)},\n\t\t}, // 36\n\t}\n}\n\n// AddSparkline provides a function to add sparklines to the worksheet by\n// given formatting options. Sparklines are small charts that fit in a single\n// cell and are used to show trends in data. Sparklines are a feature of Excel\n// 2010 and later only. You can write them to workbook that can be read by Excel\n// 2007, but they won't be displayed. For example, add a grouped sparkline.\n// Changes are applied to all three:\n//\n//\terr := f.AddSparkline(\"Sheet1\", &excelize.SparklineOptions{\n//\t    Location: []string{\"A1\", \"A2\", \"A3\"},\n//\t    Range:    []string{\"Sheet2!A1:J1\", \"Sheet2!A2:J2\", \"Sheet2!A3:J3\"},\n//\t    Markers:  true,\n//\t})\n//\n// The following shows the formatting options of sparkline supported by excelize:\n//\n//\t Parameter   | Description\n//\t-------------+--------------------------------------------\n//\t Location    | Required, must have the same number with 'Range' parameter\n//\t Range       | Required, must have the same number with 'Location' parameter\n//\t Type        | Enumeration value: line, column, win_loss\n//\t Style       | Value range: 0 - 35\n//\t Hight       | Toggle sparkline high points\n//\t Low         | Toggle sparkline low points\n//\t First       | Toggle sparkline first points\n//\t Last        | Toggle sparkline last points\n//\t Negative    | Toggle sparkline negative points\n//\t Markers     | Toggle sparkline markers\n//\t Axis        | Used to specify if show horizontal axis\n//\t Reverse     | Used to specify if enable plot data right-to-left\n//\t SeriesColor | An RGB Color is specified as RRGGBB\nfunc (f *File) AddSparkline(sheet string, opts *SparklineOptions) error {\n\tvar (\n\t\terr                 error\n\t\tws                  *xlsxWorksheet\n\t\tsparkType           string\n\t\tsparkTypes          map[string]string\n\t\tspecifiedSparkTypes string\n\t\tok                  bool\n\t\tgroup               *xlsxX14SparklineGroup\n\t\tgroups              *xlsxX14SparklineGroups\n\t)\n\n\t// parameter validation\n\tif ws, err = f.parseFormatAddSparklineSet(sheet, opts); err != nil {\n\t\treturn err\n\t}\n\t// Handle the sparkline type\n\tsparkType = \"line\"\n\tsparkTypes = map[string]string{\"line\": \"line\", \"column\": \"column\", \"win_loss\": \"stacked\"}\n\tif opts.Type != \"\" {\n\t\tif specifiedSparkTypes, ok = sparkTypes[opts.Type]; !ok {\n\t\t\terr = ErrSparklineType\n\t\t\treturn err\n\t\t}\n\t\tsparkType = specifiedSparkTypes\n\t}\n\tgroup = getSparklineGroupPresets()[opts.Style]\n\tgroup.Type = sparkType\n\tgroup.ColorAxis = &xlsxColor{RGB: \"FF000000\"}\n\tgroup.DisplayEmptyCellsAs = \"gap\"\n\tgroup.High = opts.High\n\tgroup.Low = opts.Low\n\tgroup.First = opts.First\n\tgroup.Last = opts.Last\n\tgroup.Negative = opts.Negative\n\tgroup.DisplayXAxis = opts.Axis\n\tgroup.Markers = opts.Markers\n\tif opts.SeriesColor != \"\" {\n\t\tgroup.ColorSeries = &xlsxColor{\n\t\t\tRGB: getPaletteColor(opts.SeriesColor),\n\t\t}\n\t}\n\tif opts.Reverse {\n\t\tgroup.RightToLeft = opts.Reverse\n\t}\n\tf.addSparkline(opts, group)\n\tif err = f.appendSparkline(ws, group, groups); err != nil {\n\t\treturn err\n\t}\n\tf.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14)\n\treturn err\n}\n\n// parseFormatAddSparklineSet provides a function to validate sparkline\n// properties.\nfunc (f *File) parseFormatAddSparklineSet(sheet string, opts *SparklineOptions) (*xlsxWorksheet, error) {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn ws, err\n\t}\n\tif opts == nil {\n\t\treturn ws, ErrParameterRequired\n\t}\n\tif len(opts.Location) < 1 {\n\t\treturn ws, ErrSparklineLocation\n\t}\n\tif len(opts.Range) < 1 {\n\t\treturn ws, ErrSparklineRange\n\t}\n\t// The range and locations must match\n\tif len(opts.Location) != len(opts.Range) {\n\t\treturn ws, ErrSparkline\n\t}\n\tif opts.Style < 0 || 35 < opts.Style {\n\t\treturn ws, ErrSparklineStyle\n\t}\n\tif ws.ExtLst == nil {\n\t\tws.ExtLst = &xlsxExtLst{}\n\t}\n\treturn ws, err\n}\n\n// addSparkline provides a function to create a sparkline in a sparkline group\n// by given properties.\nfunc (f *File) addSparkline(opts *SparklineOptions, group *xlsxX14SparklineGroup) {\n\tfor idx, location := range opts.Location {\n\t\tgroup.Sparklines.Sparkline = append(group.Sparklines.Sparkline, &xlsxX14Sparkline{\n\t\t\tF:     opts.Range[idx],\n\t\t\tSqref: location,\n\t\t})\n\t}\n}\n\n// appendSparkline provides a function to append sparkline to sparkline\n// groups.\nfunc (f *File) appendSparkline(ws *xlsxWorksheet, group *xlsxX14SparklineGroup, groups *xlsxX14SparklineGroups) error {\n\tvar (\n\t\terr                                                    error\n\t\tidx                                                    int\n\t\tappendMode                                             bool\n\t\tdecodeExtLst                                           = new(decodeExtLst)\n\t\tdecodeSparklineGroups                                  *decodeX14SparklineGroups\n\t\text                                                    *xlsxExt\n\t\tsparklineGroupsBytes, sparklineGroupBytes, extLstBytes []byte\n\t)\n\tsparklineGroupBytes, _ = xml.Marshal(group)\n\tif ws.ExtLst != nil { // append mode ext\n\t\tif err = f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + ws.ExtLst.Ext + \"</extLst>\")).\n\t\t\tDecode(decodeExtLst); err != nil && err != io.EOF {\n\t\t\treturn err\n\t\t}\n\t\tfor idx, ext = range decodeExtLst.Ext {\n\t\t\tif ext.URI == ExtURISparklineGroups {\n\t\t\t\tdecodeSparklineGroups = new(decodeX14SparklineGroups)\n\t\t\t\tif err = f.xmlNewDecoder(strings.NewReader(ext.Content)).\n\t\t\t\t\tDecode(decodeSparklineGroups); err != nil && err != io.EOF {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif groups == nil {\n\t\t\t\t\tgroups = &xlsxX14SparklineGroups{}\n\t\t\t\t}\n\t\t\t\tgroups.XMLNSXM = NameSpaceSpreadSheetExcel2006Main.Value\n\t\t\t\tgroups.Content = decodeSparklineGroups.Content + string(sparklineGroupBytes)\n\t\t\t\tsparklineGroupsBytes, _ = xml.Marshal(groups)\n\t\t\t\tdecodeExtLst.Ext[idx].Content = string(sparklineGroupsBytes)\n\t\t\t\tappendMode = true\n\t\t\t}\n\t\t}\n\t}\n\tif !appendMode {\n\t\tsparklineGroupsBytes, _ = xml.Marshal(&xlsxX14SparklineGroups{\n\t\t\tXMLNSXM:         NameSpaceSpreadSheetExcel2006Main.Value,\n\t\t\tSparklineGroups: []*xlsxX14SparklineGroup{group},\n\t\t})\n\t\tdecodeExtLst.Ext = append(decodeExtLst.Ext, &xlsxExt{\n\t\t\tURI: ExtURISparklineGroups, Content: string(sparklineGroupsBytes),\n\t\t})\n\t}\n\tsort.Slice(decodeExtLst.Ext, func(i, j int) bool {\n\t\treturn inStrSlice(worksheetExtURIPriority, decodeExtLst.Ext[i].URI, false) <\n\t\t\tinStrSlice(worksheetExtURIPriority, decodeExtLst.Ext[j].URI, false)\n\t})\n\textLstBytes, err = xml.Marshal(decodeExtLst)\n\tws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), \"<extLst>\"), \"</extLst>\")}\n\treturn err\n}\n"
  },
  {
    "path": "sparkline_test.go",
    "content": "package excelize\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestAddSparkline(t *testing.T) {\n\tf, err := prepareSparklineDataset()\n\tassert.NoError(t, err)\n\n\t// Set the columns widths to make the output clearer\n\tstyle, err := f.NewStyle(&Style{Font: &Font{Bold: true}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A1\", \"B1\", style))\n\tviewOpts, err := f.GetSheetView(\"Sheet1\", 0)\n\tassert.NoError(t, err)\n\tviewOpts.ZoomScale = float64Ptr(150)\n\tassert.NoError(t, f.SetSheetView(\"Sheet1\", 0, &viewOpts))\n\n\tassert.NoError(t, f.SetColWidth(\"Sheet1\", \"A\", \"A\", 14))\n\tassert.NoError(t, f.SetColWidth(\"Sheet1\", \"B\", \"B\", 50))\n\t// Headings\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", \"Sparkline\"))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B1\", \"Description\"))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B2\", `A default \"line\" sparkline.`))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A2\"},\n\t\tRange:    []string{\"Sheet3!A1:J1\"},\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B3\", `A default \"column\" sparkline.`))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A3\"},\n\t\tRange:    []string{\"Sheet3!A2:J2\"},\n\t\tType:     \"column\",\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B4\", `A default \"win/loss\" sparkline.`))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A4\"},\n\t\tRange:    []string{\"Sheet3!A3:J3\"},\n\t\tType:     \"win_loss\",\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B6\", \"Line with markers.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A6\"},\n\t\tRange:    []string{\"Sheet3!A1:J1\"},\n\t\tMarkers:  true,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B7\", \"Line with high and low points.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A7\"},\n\t\tRange:    []string{\"Sheet3!A1:J1\"},\n\t\tHigh:     true,\n\t\tLow:      true,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B8\", \"Line with first and last point markers.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A8\"},\n\t\tRange:    []string{\"Sheet3!A1:J1\"},\n\t\tFirst:    true,\n\t\tLast:     true,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B9\", \"Line with negative point markers.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A9\"},\n\t\tRange:    []string{\"Sheet3!A1:J1\"},\n\t\tNegative: true,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B10\", \"Line with axis.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A10\"},\n\t\tRange:    []string{\"Sheet3!A1:J1\"},\n\t\tAxis:     true,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B12\", \"Column with default style (1).\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A12\"},\n\t\tRange:    []string{\"Sheet3!A2:J2\"},\n\t\tType:     \"column\",\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B13\", \"Column with style 2.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A13\"},\n\t\tRange:    []string{\"Sheet3!A2:J2\"},\n\t\tType:     \"column\",\n\t\tStyle:    2,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B14\", \"Column with style 3.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A14\"},\n\t\tRange:    []string{\"Sheet3!A2:J2\"},\n\t\tType:     \"column\",\n\t\tStyle:    3,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B15\", \"Column with style 4.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A15\"},\n\t\tRange:    []string{\"Sheet3!A2:J2\"},\n\t\tType:     \"column\",\n\t\tStyle:    4,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B16\", \"Column with style 5.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A16\"},\n\t\tRange:    []string{\"Sheet3!A2:J2\"},\n\t\tType:     \"column\",\n\t\tStyle:    5,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B17\", \"Column with style 6.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A17\"},\n\t\tRange:    []string{\"Sheet3!A2:J2\"},\n\t\tType:     \"column\",\n\t\tStyle:    6,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B18\", \"Column with a user defined color.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation:    []string{\"A18\"},\n\t\tRange:       []string{\"Sheet3!A2:J2\"},\n\t\tType:        \"column\",\n\t\tSeriesColor: \"E965E0\",\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B20\", \"A win/loss sparkline.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A20\"},\n\t\tRange:    []string{\"Sheet3!A3:J3\"},\n\t\tType:     \"win_loss\",\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B21\", \"A win/loss sparkline with negative points highlighted.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A21\"},\n\t\tRange:    []string{\"Sheet3!A3:J3\"},\n\t\tType:     \"win_loss\",\n\t\tNegative: true,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B23\", \"A left to right column (the default).\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A23\"},\n\t\tRange:    []string{\"Sheet3!A4:J4\"},\n\t\tType:     \"column\",\n\t\tStyle:    20,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B24\", \"A right to left column.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A24\"},\n\t\tRange:    []string{\"Sheet3!A4:J4\"},\n\t\tType:     \"column\",\n\t\tStyle:    20,\n\t\tReverse:  true,\n\t}))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B25\", \"Sparkline and text in one cell.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A25\"},\n\t\tRange:    []string{\"Sheet3!A4:J4\"},\n\t\tType:     \"column\",\n\t\tStyle:    20,\n\t}))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A25\", \"Growth\"))\n\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B27\", \"A grouped sparkline. Changes are applied to all three.\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A27\", \"A28\", \"A29\"},\n\t\tRange:    []string{\"Sheet3!A5:J5\", \"Sheet3!A6:J6\", \"Sheet3!A7:J7\"},\n\t\tMarkers:  true,\n\t}))\n\n\t// Sheet2 sections\n\tassert.NoError(t, f.AddSparkline(\"Sheet2\", &SparklineOptions{\n\t\tLocation: []string{\"F3\"},\n\t\tRange:    []string{\"Sheet2!A3:E3\"},\n\t\tType:     \"win_loss\",\n\t\tNegative: true,\n\t}))\n\n\tassert.NoError(t, f.AddSparkline(\"Sheet2\", &SparklineOptions{\n\t\tLocation: []string{\"F1\"},\n\t\tRange:    []string{\"Sheet2!A1:E1\"},\n\t\tMarkers:  true,\n\t}))\n\n\tassert.NoError(t, f.AddSparkline(\"Sheet2\", &SparklineOptions{\n\t\tLocation: []string{\"F2\"},\n\t\tRange:    []string{\"Sheet2!A2:E2\"},\n\t\tType:     \"column\",\n\t\tStyle:    12,\n\t}))\n\n\tassert.NoError(t, f.AddSparkline(\"Sheet2\", &SparklineOptions{\n\t\tLocation: []string{\"F3\"},\n\t\tRange:    []string{\"Sheet2!A3:E3\"},\n\t\tType:     \"win_loss\",\n\t\tNegative: true,\n\t}))\n\n\t// Save spreadsheet by the given path\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddSparkline.xlsx\")))\n\n\t// Test error exceptions\n\tassert.EqualError(t, f.AddSparkline(\"SheetN\", &SparklineOptions{\n\t\tLocation: []string{\"F3\"},\n\t\tRange:    []string{\"Sheet2!A3:E3\"},\n\t}), \"sheet SheetN does not exist\")\n\n\tassert.Equal(t, ErrParameterRequired, f.AddSparkline(\"Sheet1\", nil))\n\n\t// Test add sparkline with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.AddSparkline(\"Sheet:1\", &SparklineOptions{\n\t\tLocation: []string{\"F3\"},\n\t\tRange:    []string{\"Sheet2!A3:E3\"},\n\t\tType:     \"win_loss\",\n\t\tNegative: true,\n\t}))\n\n\tassert.Equal(t, ErrSparklineLocation, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tRange: []string{\"Sheet2!A3:E3\"},\n\t}))\n\n\tassert.Equal(t, ErrSparklineRange, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"F3\"},\n\t}))\n\n\tassert.Equal(t, ErrSparkline, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"F2\", \"F3\"},\n\t\tRange:    []string{\"Sheet2!A3:E3\"},\n\t}))\n\n\tassert.Equal(t, ErrSparklineType, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"F3\"},\n\t\tRange:    []string{\"Sheet2!A3:E3\"},\n\t\tType:     \"unknown_type\",\n\t}))\n\n\tassert.Equal(t, ErrSparklineStyle, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"F3\"},\n\t\tRange:    []string{\"Sheet2!A3:E3\"},\n\t\tStyle:    -1,\n\t}))\n\n\tassert.Equal(t, ErrSparklineStyle, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"F3\"},\n\t\tRange:    []string{\"Sheet2!A3:E3\"},\n\t\tStyle:    -1,\n\t}))\n\t// Test creating a conditional format with existing extension lists\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: fmt.Sprintf(`<ext uri=\"%s\"><x14:slicerList /></ext><ext uri=\"%s\"><x14:sparklineGroups /></ext>`, ExtURISlicerListX14, ExtURISparklineGroups)}\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A3\"},\n\t\tRange:    []string{\"Sheet3!A2:J2\"},\n\t\tType:     \"column\",\n\t}))\n\t// Test creating a conditional format with invalid extension list characters\n\tws.(*xlsxWorksheet).ExtLst.Ext = fmt.Sprintf(`<ext uri=\"%s\"><x14:sparklineGroups><x14:sparklineGroup></x14:sparklines></x14:sparklineGroup></x14:sparklineGroups></ext>`, ExtURISparklineGroups)\n\tassert.EqualError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"A2\"},\n\t\tRange:    []string{\"Sheet3!A1:J1\"},\n\t}), \"XML syntax error on line 1: element <sparklineGroup> closed by </sparklines>\")\n}\n\nfunc TestAppendSparkline(t *testing.T) {\n\t// Test unsupported charset.\n\tf := NewFile()\n\tws, err := f.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n\tws.ExtLst = &xlsxExtLst{Ext: string(MacintoshCyrillicCharset)}\n\tassert.EqualError(t, f.appendSparkline(ws, &xlsxX14SparklineGroup{}, &xlsxX14SparklineGroups{}), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc prepareSparklineDataset() (*File, error) {\n\tf := NewFile()\n\tsheet2 := [][]int{\n\t\t{-2, 2, 3, -1, 0},\n\t\t{30, 20, 33, 20, 15},\n\t\t{1, -1, -1, 1, -1},\n\t}\n\tsheet3 := [][]int{\n\t\t{-2, 2, 3, -1, 0, -2, 3, 2, 1, 0},\n\t\t{30, 20, 33, 20, 15, 5, 5, 15, 10, 15},\n\t\t{1, 1, -1, -1, 1, -1, 1, 1, 1, -1},\n\t\t{5, 6, 7, 10, 15, 20, 30, 50, 70, 100},\n\t\t{-2, 2, 3, -1, 0, -2, 3, 2, 1, 0},\n\t\t{3, -1, 0, -2, 3, 2, 1, 0, 2, 1},\n\t\t{0, -2, 3, 2, 1, 0, 1, 2, 3, 1},\n\t}\n\tif _, err := f.NewSheet(\"Sheet2\"); err != nil {\n\t\treturn f, err\n\t}\n\tif _, err := f.NewSheet(\"Sheet3\"); err != nil {\n\t\treturn f, err\n\t}\n\tfor row, data := range sheet2 {\n\t\tif err := f.SetSheetRow(\"Sheet2\", fmt.Sprintf(\"A%d\", row+1), &data); err != nil {\n\t\t\tfmt.Println(err)\n\t\t}\n\t}\n\tfor row, data := range sheet3 {\n\t\tif err := f.SetSheetRow(\"Sheet3\", fmt.Sprintf(\"A%d\", row+1), &data); err != nil {\n\t\t\tfmt.Println(err)\n\t\t}\n\t}\n\treturn f, nil\n}\n"
  },
  {
    "path": "stream.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n// StreamWriter defined the type of stream writer.\ntype StreamWriter struct {\n\tfile            *File\n\tSheet           string\n\tSheetID         int\n\tsheetWritten    bool\n\tworksheet       *xlsxWorksheet\n\trawData         bufferedWriter\n\trows            int\n\tmergeCellsCount int\n\tmergeCells      strings.Builder\n\ttableParts      string\n}\n\n// NewStreamWriter returns stream writer struct by given worksheet name used for\n// writing data on a new existing empty worksheet with large amounts of data.\n// Note that after writing data with the stream writer for the worksheet, you\n// must call the 'Flush' method to end the streaming writing process, ensure\n// that the order of row numbers is ascending when set rows, and the normal\n// mode functions and stream mode functions can not be work mixed to writing\n// data on the worksheets. The stream writer will try to use temporary files on\n// disk to reduce the memory usage when in-memory chunks data over 16MB, and\n// you can't get cell value at this time. For example, set data for worksheet\n// of size 102400 rows x 50 columns with numbers and style:\n//\n//\tf := excelize.NewFile()\n//\tdefer func() {\n//\t    if err := f.Close(); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t}()\n//\tsw, err := f.NewStreamWriter(\"Sheet1\")\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\tstyleID, err := f.NewStyle(&excelize.Style{Font: &excelize.Font{Color: \"777777\"}})\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\tif err := sw.SetRow(\"A1\",\n//\t    []interface{}{\n//\t        excelize.Cell{StyleID: styleID, Value: \"Data\"},\n//\t        []excelize.RichTextRun{\n//\t            {Text: \"Rich \", Font: &excelize.Font{Color: \"2354e8\"}},\n//\t            {Text: \"Text\", Font: &excelize.Font{Color: \"e83723\"}},\n//\t        },\n//\t    },\n//\t    excelize.RowOpts{Height: 45, Hidden: false}); err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\tfor rowID := 2; rowID <= 102400; rowID++ {\n//\t    row := make([]interface{}, 50)\n//\t    for colID := 0; colID < 50; colID++ {\n//\t        row[colID] = rand.Intn(640000)\n//\t    }\n//\t    cell, err := excelize.CoordinatesToCellName(1, rowID)\n//\t    if err != nil {\n//\t        fmt.Println(err)\n//\t        break\n//\t    }\n//\t    if err := sw.SetRow(cell, row); err != nil {\n//\t        fmt.Println(err)\n//\t        break\n//\t    }\n//\t}\n//\tif err := sw.Flush(); err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\tif err := f.SaveAs(\"Book1.xlsx\"); err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\n// Set cell value and cell formula for a worksheet with stream writer:\n//\n//\terr := sw.SetRow(\"A1\", []interface{}{\n//\t    excelize.Cell{Value: 1},\n//\t    excelize.Cell{Value: 2},\n//\t    excelize.Cell{Formula: \"SUM(A1,B1)\"}});\n//\n// Set cell value and rows style for a worksheet with stream writer:\n//\n//\terr := sw.SetRow(\"A1\", []interface{}{\n//\t    excelize.Cell{Value: 1}},\n//\t    excelize.RowOpts{StyleID: styleID, Height: 20, Hidden: false});\nfunc (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {\n\tif err := checkSheetName(sheet); err != nil {\n\t\treturn nil, err\n\t}\n\tsheetID := f.getSheetID(sheet)\n\tif sheetID == -1 {\n\t\treturn nil, ErrSheetNotExist{sheet}\n\t}\n\tsw := &StreamWriter{\n\t\tfile:    f,\n\t\tSheet:   sheet,\n\t\tSheetID: sheetID,\n\t\trawData: bufferedWriter{tmpDir: f.options.TmpDir},\n\t}\n\tvar err error\n\tsw.worksheet, err = f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsheetXMLPath, _ := f.getSheetXMLPath(sheet)\n\tif f.streams == nil {\n\t\tf.streams = make(map[string]*StreamWriter)\n\t}\n\tf.streams[sheetXMLPath] = sw\n\n\t_, _ = sw.rawData.WriteString(xml.Header + `<worksheet` + templateNamespaceIDMap)\n\tbulkAppendFields(&sw.rawData, sw.worksheet, 3, 4)\n\treturn sw, err\n}\n\n// AddTable creates an Excel table for the StreamWriter using the given\n// cell range and format set. For example, create a table of A1:D5:\n//\n//\terr := sw.AddTable(&excelize.Table{Range: \"A1:D5\"})\n//\n// Create a table of F2:H6 with format set:\n//\n//\tdisable := false\n//\terr := sw.AddTable(&excelize.Table{\n//\t    Range:             \"F2:H6\",\n//\t    Name:              \"table\",\n//\t    StyleName:         \"TableStyleMedium2\",\n//\t    ShowFirstColumn:   true,\n//\t    ShowLastColumn:    true,\n//\t    ShowRowStripes:    &disable,\n//\t    ShowColumnStripes: true,\n//\t})\n//\n// Note that the table must be at least two lines including the header. The\n// header cells must contain strings and must be unique.\n//\n// Currently, only one table is allowed for a StreamWriter. AddTable must be\n// called after the rows are written but before Flush.\n//\n// See File.AddTable for details on the table format.\nfunc (sw *StreamWriter) AddTable(table *Table) error {\n\toptions, err := parseTableOptions(table)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcoordinates, err := rangeRefToCoordinates(options.Range)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_ = sortCoordinates(coordinates)\n\n\t// Correct the minimum number of rows, the table at least two lines.\n\tif coordinates[1] == coordinates[3] {\n\t\tcoordinates[3]++\n\t}\n\n\t// Correct table reference range, such correct C1:B3 to B1:C3.\n\tref, err := coordinatesToRangeRef(coordinates)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// create table columns using the first row\n\ttableHeaders, err := sw.getRowValues(coordinates[1], coordinates[0], coordinates[2])\n\tif err != nil {\n\t\treturn err\n\t}\n\ttableColumn := make([]*xlsxTableColumn, len(tableHeaders))\n\tfor i, name := range tableHeaders {\n\t\ttableColumn[i] = &xlsxTableColumn{\n\t\t\tID:   i + 1,\n\t\t\tName: name,\n\t\t}\n\t}\n\n\ttableID := sw.file.countTables() + 1\n\n\tname := options.Name\n\tif name == \"\" {\n\t\tname = \"Table\" + strconv.Itoa(tableID)\n\t}\n\n\ttbl := xlsxTable{\n\t\tXMLNS:       NameSpaceSpreadSheet.Value,\n\t\tID:          tableID,\n\t\tName:        name,\n\t\tDisplayName: name,\n\t\tRef:         ref,\n\t\tAutoFilter: &xlsxAutoFilter{\n\t\t\tRef: ref,\n\t\t},\n\t\tTableColumns: &xlsxTableColumns{\n\t\t\tCount:       len(tableColumn),\n\t\t\tTableColumn: tableColumn,\n\t\t},\n\t\tTableStyleInfo: &xlsxTableStyleInfo{\n\t\t\tName:              options.StyleName,\n\t\t\tShowFirstColumn:   options.ShowFirstColumn,\n\t\t\tShowLastColumn:    options.ShowLastColumn,\n\t\t\tShowRowStripes:    *options.ShowRowStripes,\n\t\t\tShowColumnStripes: options.ShowColumnStripes,\n\t\t},\n\t}\n\n\tsheetRelationshipsTableXML := \"../tables/table\" + strconv.Itoa(tableID) + \".xml\"\n\ttableXML := strings.ReplaceAll(sheetRelationshipsTableXML, \"..\", \"xl\")\n\n\t// Add first table for given sheet\n\tsheetPath := sw.file.sheetMap[sw.Sheet]\n\tsheetRels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(sheetPath, \"xl/worksheets/\") + \".rels\"\n\trID := sw.file.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, \"\")\n\n\tsw.tableParts = fmt.Sprintf(`<tableParts count=\"1\"><tablePart r:id=\"rId%d\"></tablePart></tableParts>`, rID)\n\n\tif err = sw.file.addContentTypePart(tableID, \"table\"); err != nil {\n\t\treturn err\n\t}\n\tb, _ := xml.Marshal(tbl)\n\tsw.file.saveFileList(tableXML, b)\n\treturn err\n}\n\n// Extract values from a row in the StreamWriter.\nfunc (sw *StreamWriter) getRowValues(hRow, hCol, vCol int) (res []string, err error) {\n\tres = make([]string, vCol-hCol+1)\n\n\tr, err := sw.rawData.Reader()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdec := sw.file.xmlNewDecoder(r)\n\tfor {\n\t\ttoken, err := dec.Token()\n\t\tif err == io.EOF {\n\t\t\treturn res, nil\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tstartElement, ok := getRowElement(token, hRow)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\t// decode cells\n\t\tvar row xlsxRow\n\t\tif err := dec.DecodeElement(&row, &startElement); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, c := range row.C {\n\t\t\tcol, _, err := CellNameToCoordinates(c.R)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif col < hCol || col > vCol {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tres[col-hCol], _ = c.getValueFrom(sw.file, nil, false)\n\t\t}\n\t\treturn res, nil\n\t}\n}\n\n// Check if the token is an worksheet row with the matching row number.\nfunc getRowElement(token xml.Token, hRow int) (startElement xml.StartElement, ok bool) {\n\tstartElement, ok = token.(xml.StartElement)\n\tif !ok {\n\t\treturn\n\t}\n\tok = startElement.Name.Local == \"row\"\n\tif !ok {\n\t\treturn\n\t}\n\tok = false\n\tfor _, attr := range startElement.Attr {\n\t\tif attr.Name.Local != \"r\" {\n\t\t\tcontinue\n\t\t}\n\t\trow, _ := strconv.Atoi(attr.Value)\n\t\tif row == hRow {\n\t\t\tok = true\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\n// Cell can be used directly in StreamWriter.SetRow to specify a style and\n// a value.\ntype Cell struct {\n\tStyleID int\n\tFormula string\n\tValue   interface{}\n}\n\n// RowOpts define the options for the set row, it can be used directly in\n// StreamWriter.SetRow to specify the style and properties of the row.\ntype RowOpts struct {\n\tHeight       float64\n\tHidden       bool\n\tStyleID      int\n\tOutlineLevel int\n}\n\n// marshalAttrs prepare attributes of the row.\nfunc (r *RowOpts) marshalAttrs() (strings.Builder, error) {\n\tvar (\n\t\terr   error\n\t\tattrs strings.Builder\n\t)\n\tif r == nil {\n\t\treturn attrs, err\n\t}\n\tif r.Height > MaxRowHeight {\n\t\terr = ErrMaxRowHeight\n\t\treturn attrs, err\n\t}\n\tif r.OutlineLevel > 7 {\n\t\terr = ErrOutlineLevel\n\t\treturn attrs, err\n\t}\n\tif r.StyleID > 0 {\n\t\tattrs.WriteString(` s=\"`)\n\t\tattrs.WriteString(strconv.Itoa(r.StyleID))\n\t\tattrs.WriteString(`\" customFormat=\"1\"`)\n\t}\n\tif r.Height > 0 {\n\t\tattrs.WriteString(` ht=\"`)\n\t\tattrs.WriteString(strconv.FormatFloat(r.Height, 'f', -1, 64))\n\t\tattrs.WriteString(`\" customHeight=\"1\"`)\n\t}\n\tif r.OutlineLevel > 0 {\n\t\tattrs.WriteString(` outlineLevel=\"`)\n\t\tattrs.WriteString(strconv.Itoa(r.OutlineLevel))\n\t\tattrs.WriteString(`\"`)\n\t}\n\tif r.Hidden {\n\t\tattrs.WriteString(` hidden=\"1\"`)\n\t}\n\treturn attrs, err\n}\n\n// parseRowOpts provides a function to parse the optional settings for\n// *StreamWriter.SetRow.\nfunc parseRowOpts(opts ...RowOpts) *RowOpts {\n\toptions := &RowOpts{}\n\tfor _, opt := range opts {\n\t\toptions = &opt\n\t}\n\treturn options\n}\n\n// SetRow writes an array to stream rows by giving starting cell reference and a\n// pointer to an array of values. Note that you must call the 'Flush' function\n// to end the streaming writing process.\n//\n// As a special case, if Cell is used as a value, then the Cell.StyleID will be\n// applied to that cell.\nfunc (sw *StreamWriter) SetRow(cell string, values []interface{}, opts ...RowOpts) error {\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif row <= sw.rows {\n\t\treturn newStreamSetRowError(row)\n\t}\n\tsw.rows = row\n\tsw.writeSheetData()\n\toptions := parseRowOpts(opts...)\n\tattrs, err := options.marshalAttrs()\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, _ = sw.rawData.WriteString(`<row r=\"`)\n\t_, _ = sw.rawData.WriteString(strconv.Itoa(row))\n\t_, _ = sw.rawData.WriteString(`\"`)\n\t_, _ = sw.rawData.WriteString(attrs.String())\n\t_, _ = sw.rawData.WriteString(`>`)\n\tfor i, val := range values {\n\t\tif val == nil {\n\t\t\tcontinue\n\t\t}\n\t\tref, err := CoordinatesToCellName(col+i, row)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tc := xlsxC{R: ref, S: sw.worksheet.prepareCellStyle(col+i, row, options.StyleID)}\n\t\tvar s int\n\t\tif v, ok := val.(Cell); ok {\n\t\t\ts, val = v.StyleID, v.Value\n\t\t\tsetCellFormula(&c, v.Formula)\n\t\t} else if v, ok := val.(*Cell); ok && v != nil {\n\t\t\ts, val = v.StyleID, v.Value\n\t\t\tsetCellFormula(&c, v.Formula)\n\t\t}\n\t\tif s > 0 {\n\t\t\tc.S = s\n\t\t}\n\t\tif err = sw.setCellValFunc(&c, val); err != nil {\n\t\t\t_, _ = sw.rawData.WriteString(`</row>`)\n\t\t\treturn err\n\t\t}\n\t\twriteCell(&sw.rawData, c)\n\t}\n\t_, _ = sw.rawData.WriteString(`</row>`)\n\treturn sw.rawData.Sync()\n}\n\n// SetColVisible provides a function set the visibility of a single column or\n// multiple columns for the StreamWriter. Note that you must call the\n// 'SetColVisible' function before the 'SetRow' function.\n//\n// For example hide column D:\n//\n//\terr := sw.SetColVisible(4, 4, false)\n//\n// Hide the columns from D to F (included):\n//\n//\terr := sw.SetColVisible(4, 6, false)\nfunc (sw *StreamWriter) SetColVisible(minVal, maxVal int, visible bool) error {\n\tif sw.sheetWritten {\n\t\treturn newStreamSetRowOrderError(\"SetColVisible\")\n\t}\n\tif minVal < MinColumns || minVal > MaxColumns || maxVal < MinColumns || maxVal > MaxColumns {\n\t\treturn ErrColumnNumber\n\t}\n\tif minVal > maxVal {\n\t\tminVal, maxVal = maxVal, minVal\n\t}\n\tsw.worksheet.setColVisible(minVal, maxVal, visible)\n\treturn nil\n}\n\n// SetColOutlineLevel provides a function to set outline level of a single\n// column for the StreamWriter. The value of parameter 'level' is 1-7. Note that\n// you must call the 'SetColOutlineLevel' function before the 'SetRow' function.\n// For example, set outline level of column D to 2:\n//\n//\terr := sw.SetColOutlineLevel(4, 2)\nfunc (sw *StreamWriter) SetColOutlineLevel(col int, level uint8) error {\n\tif sw.sheetWritten {\n\t\treturn newStreamSetRowOrderError(\"SetColOutlineLevel\")\n\t}\n\tif col < MinColumns || col > MaxColumns {\n\t\treturn ErrColumnNumber\n\t}\n\tif level > 7 || level < 1 {\n\t\treturn ErrOutlineLevel\n\t}\n\tsw.worksheet.setColOutlineLevel(col, level)\n\treturn nil\n}\n\n// SetColStyle provides a function to set the style of a single column or\n// multiple columns for the StreamWriter. Note that you must call\n// the 'SetColStyle' function before the 'SetRow' function. For example set\n// style of column H:\n//\n//\terr := sw.SetColStyle(8, 8, style)\nfunc (sw *StreamWriter) SetColStyle(minVal, maxVal, styleID int) error {\n\tif sw.sheetWritten {\n\t\treturn newStreamSetRowOrderError(\"SetColStyle\")\n\t}\n\tif minVal < MinColumns || minVal > MaxColumns || maxVal < MinColumns || maxVal > MaxColumns {\n\t\treturn ErrColumnNumber\n\t}\n\tif maxVal < minVal {\n\t\tminVal, maxVal = maxVal, minVal\n\t}\n\ts, err := sw.file.stylesReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif styleID < 0 || s.CellXfs == nil || len(s.CellXfs.Xf) <= styleID {\n\t\treturn newInvalidStyleID(styleID)\n\t}\n\tsw.worksheet.setColStyle(minVal, maxVal, styleID)\n\treturn nil\n}\n\n// SetColWidth provides a function to set the width of a single column or\n// multiple columns for the StreamWriter. Note that you must call\n// the 'SetColWidth' function before the 'SetRow' function. For example set\n// the width column B:C as 20:\n//\n//\terr := sw.SetColWidth(2, 3, 20)\nfunc (sw *StreamWriter) SetColWidth(minVal, maxVal int, width float64) error {\n\tif sw.sheetWritten {\n\t\treturn newStreamSetRowOrderError(\"SetColWidth\")\n\t}\n\tif minVal < MinColumns || minVal > MaxColumns || maxVal < MinColumns || maxVal > MaxColumns {\n\t\treturn ErrColumnNumber\n\t}\n\tif width > MaxColumnWidth {\n\t\treturn ErrColumnWidth\n\t}\n\tif minVal > maxVal {\n\t\tminVal, maxVal = maxVal, minVal\n\t}\n\tsw.worksheet.setColWidth(minVal, maxVal, width)\n\treturn nil\n}\n\n// InsertPageBreak creates a page break to determine where the printed page ends\n// and where begins the next one by a given cell reference, the content before\n// the page break will be printed on one page and after the page break on\n// another.\nfunc (sw *StreamWriter) InsertPageBreak(cell string) error {\n\treturn sw.worksheet.insertPageBreak(cell)\n}\n\n// SetPanes provides a function to create and remove freeze panes and split\n// panes by giving panes options for the StreamWriter. Note that you must call\n// the 'SetPanes' function before the 'SetRow' function.\nfunc (sw *StreamWriter) SetPanes(panes *Panes) error {\n\tif sw.sheetWritten {\n\t\treturn newStreamSetRowOrderError(\"SetPanes\")\n\t}\n\treturn sw.worksheet.setPanes(panes)\n}\n\n// MergeCell provides a function to merge cells by a given range reference for\n// the StreamWriter. Don't create a merged cell that overlaps with another\n// existing merged cell.\nfunc (sw *StreamWriter) MergeCell(topLeftCell, bottomRightCell string) error {\n\t_, err := cellRefsToCoordinates(topLeftCell, bottomRightCell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsw.mergeCellsCount++\n\t_, _ = sw.mergeCells.WriteString(`<mergeCell ref=\"`)\n\t_, _ = sw.mergeCells.WriteString(topLeftCell)\n\t_, _ = sw.mergeCells.WriteString(`:`)\n\t_, _ = sw.mergeCells.WriteString(bottomRightCell)\n\t_, _ = sw.mergeCells.WriteString(`\"/>`)\n\treturn nil\n}\n\n// setCellFormula provides a function to set formula of a cell.\nfunc setCellFormula(c *xlsxC, formula string) {\n\tif formula != \"\" {\n\t\tc.T, c.F = \"str\", &xlsxF{Content: formula}\n\t}\n}\n\n// setCellTime provides a function to set number of a cell with a time.\nfunc (sw *StreamWriter) setCellTime(c *xlsxC, val time.Time) error {\n\tvar date1904, isNum bool\n\twb, err := sw.file.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif wb != nil && wb.WorkbookPr != nil {\n\t\tdate1904 = wb.WorkbookPr.Date1904\n\t}\n\tif isNum, err = c.setCellTime(val, date1904); err == nil && isNum && c.S == 0 {\n\t\tstyle, _ := sw.file.NewStyle(&Style{NumFmt: 22})\n\t\tc.S = style\n\t}\n\treturn nil\n}\n\n// setCellValFunc provides a function to set value of a cell.\nfunc (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) error {\n\tvar err error\n\tswitch val := val.(type) {\n\tcase int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:\n\t\tsetCellIntFunc(c, val)\n\tcase float32:\n\t\tc.setCellFloat(float64(val), -1, 32)\n\tcase float64:\n\t\tc.setCellFloat(val, -1, 64)\n\tcase string:\n\t\tc.setCellValue(val)\n\tcase []byte:\n\t\tc.setCellValue(string(val))\n\tcase time.Duration:\n\t\tc.T, c.V = setCellDuration(val)\n\tcase time.Time:\n\t\terr = sw.setCellTime(c, val)\n\tcase bool:\n\t\tc.T, c.V = setCellBool(val)\n\tcase nil:\n\t\treturn err\n\tcase []RichTextRun:\n\t\tc.T, c.IS = \"inlineStr\", &xlsxSI{}\n\t\tc.IS.R, err = setRichText(val)\n\tdefault:\n\t\tc.setCellValue(fmt.Sprint(val))\n\t}\n\treturn err\n}\n\n// setCellIntFunc is a wrapper of SetCellInt.\nfunc setCellIntFunc(c *xlsxC, val interface{}) {\n\tswitch val := val.(type) {\n\tcase int:\n\t\tc.T, c.V = setCellInt(int64(val))\n\tcase int8:\n\t\tc.T, c.V = setCellInt(int64(val))\n\tcase int16:\n\t\tc.T, c.V = setCellInt(int64(val))\n\tcase int32:\n\t\tc.T, c.V = setCellInt(int64(val))\n\tcase int64:\n\t\tc.T, c.V = setCellInt(val)\n\tcase uint:\n\t\tc.T, c.V = setCellUint(uint64(val))\n\tcase uint8:\n\t\tc.T, c.V = setCellUint(uint64(val))\n\tcase uint16:\n\t\tc.T, c.V = setCellUint(uint64(val))\n\tcase uint32:\n\t\tc.T, c.V = setCellUint(uint64(val))\n\tcase uint64:\n\t\tc.T, c.V = setCellUint(val)\n\t}\n}\n\n// writeCell constructs a cell XML and writes it to the buffer.\nfunc writeCell(buf *bufferedWriter, c xlsxC) {\n\t_, _ = buf.WriteString(`<c`)\n\tif c.XMLSpace.Value != \"\" {\n\t\t_, _ = buf.WriteString(` xml:`)\n\t\t_, _ = buf.WriteString(c.XMLSpace.Name.Local)\n\t\t_, _ = buf.WriteString(`=\"`)\n\t\t_, _ = buf.WriteString(c.XMLSpace.Value)\n\t\t_, _ = buf.WriteString(`\"`)\n\t}\n\t_, _ = buf.WriteString(` r=\"`)\n\t_, _ = buf.WriteString(c.R)\n\t_, _ = buf.WriteString(`\"`)\n\tif c.S != 0 {\n\t\t_, _ = buf.WriteString(` s=\"`)\n\t\t_, _ = buf.WriteString(strconv.Itoa(c.S))\n\t\t_, _ = buf.WriteString(`\"`)\n\t}\n\tif c.T != \"\" {\n\t\t_, _ = buf.WriteString(` t=\"`)\n\t\t_, _ = buf.WriteString(c.T)\n\t\t_, _ = buf.WriteString(`\"`)\n\t}\n\t_, _ = buf.WriteString(`>`)\n\tif c.F != nil {\n\t\t_, _ = buf.WriteString(`<f>`)\n\t\t_ = xml.EscapeText(buf, []byte(c.F.Content))\n\t\t_, _ = buf.WriteString(`</f>`)\n\t}\n\tif c.V != \"\" {\n\t\t_, _ = buf.WriteString(`<v>`)\n\t\t_ = xml.EscapeText(buf, []byte(c.V))\n\t\t_, _ = buf.WriteString(`</v>`)\n\t}\n\tif c.IS != nil {\n\t\tif len(c.IS.R) > 0 {\n\t\t\tis, _ := xml.Marshal(c.IS.R)\n\t\t\t_, _ = buf.WriteString(`<is>`)\n\t\t\t_, _ = buf.Write(is)\n\t\t\t_, _ = buf.WriteString(`</is>`)\n\t\t}\n\t\tif c.IS.T != nil {\n\t\t\t_, _ = buf.WriteString(`<is><t`)\n\t\t\tif c.IS.T.Space.Value != \"\" {\n\t\t\t\t_, _ = buf.WriteString(` xml:`)\n\t\t\t\t_, _ = buf.WriteString(c.IS.T.Space.Name.Local)\n\t\t\t\t_, _ = buf.WriteString(`=\"`)\n\t\t\t\t_, _ = buf.WriteString(c.IS.T.Space.Value)\n\t\t\t\t_, _ = buf.WriteString(`\"`)\n\t\t\t}\n\t\t\t_, _ = buf.WriteString(`>`)\n\t\t\t_, _ = buf.Write([]byte(c.IS.T.Val))\n\t\t\t_, _ = buf.WriteString(`</t></is>`)\n\t\t}\n\t}\n\t_, _ = buf.WriteString(`</c>`)\n}\n\n// writeSheetData prepares the element preceding sheetData and writes the\n// sheetData XML start element to the buffer.\nfunc (sw *StreamWriter) writeSheetData() {\n\tif !sw.sheetWritten {\n\t\tbulkAppendFields(&sw.rawData, sw.worksheet, 5, 6)\n\t\tif sw.worksheet.Cols != nil {\n\t\t\t_, _ = sw.rawData.WriteString(\"<cols>\")\n\t\t\tfor _, col := range sw.worksheet.Cols.Col {\n\t\t\t\t_, _ = sw.rawData.WriteString(`<col min=\"`)\n\t\t\t\t_, _ = sw.rawData.WriteString(strconv.Itoa(col.Min))\n\t\t\t\t_, _ = sw.rawData.WriteString(`\" max=\"`)\n\t\t\t\t_, _ = sw.rawData.WriteString(strconv.Itoa(col.Max))\n\t\t\t\t_, _ = sw.rawData.WriteString(`\"`)\n\t\t\t\tif col.Width != nil {\n\t\t\t\t\t_, _ = sw.rawData.WriteString(` width=\"`)\n\t\t\t\t\t_, _ = sw.rawData.WriteString(strconv.FormatFloat(*col.Width, 'f', -1, 64))\n\t\t\t\t\t_, _ = sw.rawData.WriteString(`\" customWidth=\"1\"`)\n\t\t\t\t}\n\t\t\t\tif col.Style != 0 {\n\t\t\t\t\t_, _ = sw.rawData.WriteString(` style=\"`)\n\t\t\t\t\t_, _ = sw.rawData.WriteString(strconv.Itoa(col.Style))\n\t\t\t\t\t_, _ = sw.rawData.WriteString(`\"`)\n\t\t\t\t}\n\t\t\t\tif col.Hidden {\n\t\t\t\t\t_, _ = sw.rawData.WriteString(` hidden=\"1\"`)\n\t\t\t\t}\n\t\t\t\tif col.OutlineLevel > 0 {\n\t\t\t\t\t_, _ = sw.rawData.WriteString(` outlineLevel=\"`)\n\t\t\t\t\t_, _ = sw.rawData.WriteString(strconv.FormatUint(uint64(col.OutlineLevel), 10))\n\t\t\t\t\t_, _ = sw.rawData.WriteString(`\"`)\n\t\t\t\t}\n\t\t\t\t_, _ = sw.rawData.WriteString(`/>`)\n\t\t\t}\n\t\t\t_, _ = sw.rawData.WriteString(\"</cols>\")\n\t\t}\n\t\t_, _ = sw.rawData.WriteString(`<sheetData>`)\n\t\tsw.sheetWritten = true\n\t}\n}\n\n// Flush ending the streaming writing process.\nfunc (sw *StreamWriter) Flush() error {\n\tsw.writeSheetData()\n\t_, _ = sw.rawData.WriteString(`</sheetData>`)\n\tbulkAppendFields(&sw.rawData, sw.worksheet, 9, 16)\n\tmergeCells := strings.Builder{}\n\tif sw.mergeCellsCount > 0 {\n\t\t_, _ = mergeCells.WriteString(`<mergeCells count=\"`)\n\t\t_, _ = mergeCells.WriteString(strconv.Itoa(sw.mergeCellsCount))\n\t\t_, _ = mergeCells.WriteString(`\">`)\n\t\t_, _ = mergeCells.WriteString(sw.mergeCells.String())\n\t\t_, _ = mergeCells.WriteString(`</mergeCells>`)\n\t}\n\t_, _ = sw.rawData.WriteString(mergeCells.String())\n\tbulkAppendFields(&sw.rawData, sw.worksheet, 18, 39)\n\t_, _ = sw.rawData.WriteString(sw.tableParts)\n\tbulkAppendFields(&sw.rawData, sw.worksheet, 41, 41)\n\t_, _ = sw.rawData.WriteString(`</worksheet>`)\n\tif err := sw.rawData.Flush(); err != nil {\n\t\treturn err\n\t}\n\n\tsheetPath := sw.file.sheetMap[sw.Sheet]\n\tsw.file.Sheet.Delete(sheetPath)\n\tsw.file.checked.Delete(sheetPath)\n\tsw.file.Pkg.Delete(sheetPath)\n\n\treturn nil\n}\n\n// bulkAppendFields bulk-appends fields in a worksheet by specified field\n// names order range.\nfunc bulkAppendFields(w io.Writer, ws *xlsxWorksheet, from, to int) {\n\ts := reflect.ValueOf(ws).Elem()\n\tenc := xml.NewEncoder(w)\n\tfor i := 0; i < s.NumField(); i++ {\n\t\tif from <= i && i <= to {\n\t\t\t_ = enc.Encode(s.Field(i).Interface())\n\t\t}\n\t}\n}\n\n// bufferedWriter uses a temp file to store an extended buffer. Writes are\n// always made to an in-memory buffer, which will always succeed. The buffer\n// is written to the temp file with Sync, which may return an error.\n// Therefore, Sync should be periodically called and the error checked.\ntype bufferedWriter struct {\n\ttmpDir string\n\ttmp    *os.File\n\tbuf    bytes.Buffer\n}\n\n// Write to the in-memory buffer. The error is always nil.\nfunc (bw *bufferedWriter) Write(p []byte) (n int, err error) {\n\treturn bw.buf.Write(p)\n}\n\n// WriteString write to the in-memory buffer. The error is always nil.\nfunc (bw *bufferedWriter) WriteString(p string) (n int, err error) {\n\treturn bw.buf.WriteString(p)\n}\n\n// Reader provides read-access to the underlying buffer/file.\nfunc (bw *bufferedWriter) Reader() (io.Reader, error) {\n\tif bw.tmp == nil {\n\t\treturn bytes.NewReader(bw.buf.Bytes()), nil\n\t}\n\tif err := bw.Flush(); err != nil {\n\t\treturn nil, err\n\t}\n\tfi, err := bw.tmp.Stat()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// os.File.ReadAt does not affect the cursor position and is safe to use here\n\treturn io.NewSectionReader(bw.tmp, 0, fi.Size()), nil\n}\n\n// Sync will write the in-memory buffer to a temp file, if the in-memory\n// buffer has grown large enough. Any error will be returned.\nfunc (bw *bufferedWriter) Sync() (err error) {\n\t// Try to use local storage\n\tif bw.buf.Len() < StreamChunkSize {\n\t\treturn nil\n\t}\n\tif bw.tmp == nil {\n\t\tbw.tmp, err = os.CreateTemp(bw.tmpDir, \"excelize-\")\n\t\tif err != nil {\n\t\t\t// can not use local storage\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn bw.Flush()\n}\n\n// Flush the entire in-memory buffer to the temp file, if a temp file is being\n// used.\nfunc (bw *bufferedWriter) Flush() error {\n\tif bw.tmp == nil {\n\t\treturn nil\n\t}\n\t_, err := bw.buf.WriteTo(bw.tmp)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbw.buf.Reset()\n\treturn nil\n}\n\n// Close the underlying temp file and reset the in-memory buffer.\nfunc (bw *bufferedWriter) Close() error {\n\tbw.buf.Reset()\n\tif bw.tmp == nil {\n\t\treturn nil\n\t}\n\tdefer func() {\n\t\t_ = os.Remove(bw.tmp.Name())\n\t}()\n\treturn bw.tmp.Close()\n}\n"
  },
  {
    "path": "stream_test.go",
    "content": "package excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"math/rand\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc BenchmarkStreamWriter(b *testing.B) {\n\tfile := NewFile()\n\tdefer func() {\n\t\tif err := file.Close(); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}()\n\trow := make([]interface{}, 10)\n\tfor colID := 0; colID < 10; colID++ {\n\t\trow[colID] = colID\n\t}\n\n\tfor n := 0; n < b.N; n++ {\n\t\tstreamWriter, _ := file.NewStreamWriter(\"Sheet1\")\n\t\tfor rowID := 10; rowID <= 110; rowID++ {\n\t\t\tcell, _ := CoordinatesToCellName(1, rowID)\n\t\t\t_ = streamWriter.SetRow(cell, row)\n\t\t}\n\t}\n\n\tb.ReportAllocs()\n}\n\nfunc TestStreamWriter(t *testing.T) {\n\tfile := NewFile()\n\tstreamWriter, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\n\t// Test max characters in a cell\n\trow := make([]interface{}, 1)\n\trow[0] = strings.Repeat(\"c\", TotalCellChars+2)\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", row))\n\n\t// Test leading and ending space(s) character characters in a cell\n\trow = make([]interface{}, 1)\n\trow[0] = \" characters\"\n\tassert.NoError(t, streamWriter.SetRow(\"A2\", row))\n\n\trow = make([]interface{}, 1)\n\trow[0] = []byte(\"Word\")\n\tassert.NoError(t, streamWriter.SetRow(\"A3\", row))\n\n\t// Test set cell with style and rich text\n\tstyleID, err := file.NewStyle(&Style{Font: &Font{Color: \"777777\"}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.SetRow(\"A4\", []interface{}{\n\t\tCell{StyleID: styleID},\n\t\tCell{Formula: \"SUM(A10,B10)\", Value: \" preserve space \"},\n\t},\n\t\tRowOpts{Height: 45, StyleID: styleID}))\n\tassert.NoError(t, streamWriter.SetRow(\"A5\", []interface{}{\n\t\t&Cell{StyleID: styleID, Value: \"cell <>&'\\\"\"},\n\t\t&Cell{Formula: \"SUM(A10,B10)\"},\n\t\t[]RichTextRun{\n\t\t\t{Text: \"Rich \", Font: &Font{Color: \"2354E8\"}},\n\t\t\t{Text: \"Text\", Font: &Font{Color: \"E83723\"}},\n\t\t},\n\t}))\n\tassert.NoError(t, streamWriter.SetRow(\"A6\", []interface{}{time.Now()}))\n\tassert.NoError(t, streamWriter.SetRow(\"A7\", nil, RowOpts{Height: 20, Hidden: true, StyleID: styleID}))\n\tassert.Equal(t, ErrMaxRowHeight, streamWriter.SetRow(\"A8\", nil, RowOpts{Height: MaxRowHeight + 1}))\n\n\tassert.NoError(t, streamWriter.SetRow(\"A9\", []interface{}{math.NaN(), math.Inf(0), math.Inf(-1)}))\n\n\tfor rowID := 10; rowID <= 51200; rowID++ {\n\t\trow := make([]interface{}, 50)\n\t\tfor colID := 0; colID < 50; colID++ {\n\t\t\trow[colID] = rand.Intn(640000)\n\t\t}\n\t\tcell, _ := CoordinatesToCellName(1, rowID)\n\t\tassert.NoError(t, streamWriter.SetRow(cell, row))\n\t}\n\n\tassert.NoError(t, streamWriter.Flush())\n\t// Save spreadsheet by the given path\n\tassert.NoError(t, file.SaveAs(filepath.Join(\"test\", \"TestStreamWriter.xlsx\")))\n\n\t// Test set cell column overflow\n\tassert.ErrorIs(t, streamWriter.SetRow(\"XFD51201\", []interface{}{\"A\", \"B\", \"C\"}), ErrColumnNumber)\n\tassert.NoError(t, file.Close())\n\n\t// Test close temporary file error\n\tfile = NewFile(Options{TmpDir: os.TempDir()})\n\tstreamWriter, err = file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tfor rowID := 10; rowID <= 25600; rowID++ {\n\t\trow := make([]interface{}, 50)\n\t\tfor colID := 0; colID < 50; colID++ {\n\t\t\trow[colID] = rand.Intn(640000)\n\t\t}\n\t\tcell, _ := CoordinatesToCellName(1, rowID)\n\t\tassert.NoError(t, streamWriter.SetRow(cell, row))\n\t}\n\tassert.NoError(t, streamWriter.rawData.Close())\n\tassert.Error(t, streamWriter.Flush())\n\n\tstreamWriter.rawData.tmp, err = os.CreateTemp(os.TempDir(), \"excelize-\")\n\tassert.NoError(t, err)\n\t_, err = streamWriter.rawData.Reader()\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.rawData.tmp.Close())\n\tassert.NoError(t, os.Remove(streamWriter.rawData.tmp.Name()))\n\n\t// Test create stream writer with unsupported charset\n\tfile = NewFile()\n\tfile.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tfile.Pkg.Store(\"xl/worksheets/sheet1.xml\", MacintoshCyrillicCharset)\n\t_, err = file.NewStreamWriter(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, file.Close())\n\n\t// Test read cell\n\tfile = NewFile()\n\tstreamWriter, err = file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", []interface{}{Cell{StyleID: styleID, Value: \"Data\"}}))\n\tassert.NoError(t, streamWriter.Flush())\n\tcellValue, err := file.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Data\", cellValue)\n\n\t// Test stream reader for a worksheet with huge amounts of data\n\tfile, err = OpenFile(filepath.Join(\"test\", \"TestStreamWriter.xlsx\"))\n\tassert.NoError(t, err)\n\trows, err := file.Rows(\"Sheet1\")\n\tassert.NoError(t, err)\n\tcells := 0\n\tfor rows.Next() {\n\t\trow, err := rows.Columns()\n\t\tassert.NoError(t, err)\n\t\tcells += len(row)\n\t}\n\tassert.NoError(t, rows.Close())\n\tassert.Equal(t, 2559562, cells)\n\t// Save spreadsheet with password.\n\tassert.NoError(t, file.SaveAs(filepath.Join(\"test\", \"EncryptionTestStreamWriter.xlsx\"), Options{Password: \"password\"}))\n\tassert.NoError(t, file.Close())\n}\n\nfunc TestStreamSetColVisible(t *testing.T) {\n\tfile := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\tstreamWriter, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.SetColVisible(3, 2, false))\n\tassert.Equal(t, ErrColumnNumber, streamWriter.SetColVisible(0, 3, false))\n\tassert.Equal(t, ErrColumnNumber, streamWriter.SetColVisible(MaxColumns+1, 3, false))\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", []interface{}{\"A\", \"B\", \"C\"}))\n\tassert.Equal(t, newStreamSetRowOrderError(\"SetColVisible\"), streamWriter.SetColVisible(2, 3, false))\n\tassert.NoError(t, streamWriter.Flush())\n}\n\nfunc TestStreamSetColOutlineLevel(t *testing.T) {\n\tfile := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\tstreamWriter, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.SetColOutlineLevel(4, 2))\n\tassert.Equal(t, ErrOutlineLevel, streamWriter.SetColOutlineLevel(4, 0))\n\tassert.Equal(t, ErrOutlineLevel, streamWriter.SetColOutlineLevel(4, 8))\n\tassert.Equal(t, ErrColumnNumber, streamWriter.SetColOutlineLevel(0, 2))\n\tassert.Equal(t, ErrColumnNumber, streamWriter.SetColOutlineLevel(MaxColumns+1, 2))\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", []interface{}{\"A\", \"B\", \"C\"}))\n\tassert.Equal(t, newStreamSetRowOrderError(\"SetColOutlineLevel\"), streamWriter.SetColOutlineLevel(4, 2))\n\tassert.NoError(t, streamWriter.Flush())\n}\n\nfunc TestStreamSetColStyle(t *testing.T) {\n\tfile := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\tstreamWriter, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.SetColStyle(3, 2, 0))\n\tassert.Equal(t, ErrColumnNumber, streamWriter.SetColStyle(0, 3, 20))\n\tassert.Equal(t, ErrColumnNumber, streamWriter.SetColStyle(MaxColumns+1, 3, 20))\n\tassert.Equal(t, newInvalidStyleID(2), streamWriter.SetColStyle(1, 3, 2))\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", []interface{}{\"A\", \"B\", \"C\"}))\n\tassert.Equal(t, newStreamSetRowOrderError(\"SetColStyle\"), streamWriter.SetColStyle(2, 3, 0))\n\n\tfile = NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\t// Test set column style with unsupported charset style sheet\n\tfile.Styles = nil\n\tfile.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\tstreamWriter, err = file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.EqualError(t, streamWriter.SetColStyle(3, 2, 0), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestStreamSetColWidth(t *testing.T) {\n\tfile := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\tstyleID, err := file.NewStyle(&Style{\n\t\tFill: Fill{Type: \"pattern\", Color: []string{\"E0EBF5\"}, Pattern: 1},\n\t})\n\tif err != nil {\n\t\tfmt.Println(err)\n\t}\n\tstreamWriter, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.SetColWidth(3, 2, 20))\n\tassert.NoError(t, streamWriter.SetColStyle(3, 2, styleID))\n\tassert.Equal(t, ErrColumnNumber, streamWriter.SetColWidth(0, 3, 20))\n\tassert.Equal(t, ErrColumnNumber, streamWriter.SetColWidth(MaxColumns+1, 3, 20))\n\tassert.Equal(t, ErrColumnWidth, streamWriter.SetColWidth(1, 3, MaxColumnWidth+1))\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", []interface{}{\"A\", \"B\", \"C\"}))\n\tassert.Equal(t, newStreamSetRowOrderError(\"SetColWidth\"), streamWriter.SetColWidth(2, 3, 20))\n\tassert.NoError(t, streamWriter.Flush())\n}\n\nfunc TestStreamSetPanes(t *testing.T) {\n\tfile, paneOpts := NewFile(), &Panes{\n\t\tFreeze:      true,\n\t\tSplit:       false,\n\t\tXSplit:      1,\n\t\tYSplit:      0,\n\t\tTopLeftCell: \"B1\",\n\t\tActivePane:  \"topRight\",\n\t\tSelection: []Selection{\n\t\t\t{SQRef: \"K16\", ActiveCell: \"K16\", Pane: \"topRight\"},\n\t\t},\n\t}\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\tstreamWriter, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.SetPanes(paneOpts))\n\tassert.Equal(t, ErrParameterInvalid, streamWriter.SetPanes(nil))\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", []interface{}{\"A\", \"B\", \"C\"}))\n\tassert.Equal(t, newStreamSetRowOrderError(\"SetPanes\"), streamWriter.SetPanes(paneOpts))\n}\n\nfunc TestStreamTable(t *testing.T) {\n\tfile := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\tstreamWriter, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\t// Test add table without table header\n\tassert.EqualError(t, streamWriter.AddTable(&Table{Range: \"A1:C2\"}), \"XML syntax error on line 2: unexpected EOF\")\n\t// Write some rows. We want enough rows to force a temp file (>16MB)\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", []interface{}{\"A\", \"B\", \"C\"}))\n\trow := []interface{}{1, 2, 3}\n\tfor r := 2; r < 10000; r++ {\n\t\tassert.NoError(t, streamWriter.SetRow(fmt.Sprintf(\"A%d\", r), row))\n\t}\n\n\t// Write a table\n\tassert.NoError(t, streamWriter.AddTable(&Table{Range: \"A1:C2\"}))\n\tassert.NoError(t, streamWriter.Flush())\n\n\t// Verify the table has names\n\tvar table xlsxTable\n\tval, ok := file.Pkg.Load(\"xl/tables/table1.xml\")\n\tassert.True(t, ok)\n\tassert.NoError(t, xml.Unmarshal(val.([]byte), &table))\n\tassert.Equal(t, \"A\", table.TableColumns.TableColumn[0].Name)\n\tassert.Equal(t, \"B\", table.TableColumns.TableColumn[1].Name)\n\tassert.Equal(t, \"C\", table.TableColumns.TableColumn[2].Name)\n\n\tassert.NoError(t, streamWriter.AddTable(&Table{Range: \"A1:C1\"}))\n\n\t// Test add table with illegal cell reference\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), streamWriter.AddTable(&Table{Range: \"A:B1\"}))\n\tassert.Equal(t, newCellNameToCoordinatesError(\"B\", newInvalidCellNameError(\"B\")), streamWriter.AddTable(&Table{Range: \"A1:B\"}))\n\t// Test add table with invalid table name\n\tassert.Equal(t, newInvalidNameError(\"1Table\"), streamWriter.AddTable(&Table{Range: \"A:B1\", Name: \"1Table\"}))\n\t// Test add table with row number exceeds maximum limit\n\tassert.Equal(t, ErrMaxRows, streamWriter.AddTable(&Table{Range: \"A1048576:C1048576\"}))\n\t// Test add table with unsupported charset content types\n\tfile.ContentTypes = nil\n\tfile.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, streamWriter.AddTable(&Table{Range: \"A1:C2\"}), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestStreamMergeCells(t *testing.T) {\n\tfile := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\tstreamWriter, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.MergeCell(\"A1\", \"D1\"))\n\t// Test merge cells with illegal cell reference\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), streamWriter.MergeCell(\"A\", \"D1\"))\n\tassert.NoError(t, streamWriter.Flush())\n\t// Save spreadsheet by the given path\n\tassert.NoError(t, file.SaveAs(filepath.Join(\"test\", \"TestStreamMergeCells.xlsx\")))\n}\n\nfunc TestStreamInsertPageBreak(t *testing.T) {\n\tfile := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\tstreamWriter, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.InsertPageBreak(\"A1\"))\n\tassert.NoError(t, streamWriter.Flush())\n\t// Save spreadsheet by the given path\n\tassert.NoError(t, file.SaveAs(filepath.Join(\"test\", \"TestStreamInsertPageBreak.xlsx\")))\n}\n\nfunc TestNewStreamWriter(t *testing.T) {\n\t// Test error exceptions\n\tfile := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\t_, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\t_, err = file.NewStreamWriter(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test new stream write with invalid sheet name\n\t_, err = file.NewStreamWriter(\"Sheet:1\")\n\tassert.Equal(t, ErrSheetNameInvalid, err)\n}\n\nfunc TestStreamMarshalAttrs(t *testing.T) {\n\tvar r *RowOpts\n\tattrs, err := r.marshalAttrs()\n\tassert.NoError(t, err)\n\tassert.Empty(t, attrs)\n}\n\nfunc TestStreamSetRow(t *testing.T) {\n\t// Test error exceptions\n\tfile := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\tstreamWriter, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), streamWriter.SetRow(\"A\", []interface{}{}))\n\t// Test set row with non-ascending row number\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", []interface{}{}))\n\tassert.Equal(t, newStreamSetRowError(1), streamWriter.SetRow(\"A1\", []interface{}{}))\n\t// Test set row with unsupported charset workbook\n\tfile.WorkBook = nil\n\tfile.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, streamWriter.SetRow(\"A2\", []interface{}{time.Now()}), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestStreamSetRowNilValues(t *testing.T) {\n\tfile := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\tstreamWriter, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", []interface{}{nil, nil, Cell{Value: \"foo\"}}))\n\tassert.NoError(t, streamWriter.Flush())\n\tws, err := file.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.NotEqual(t, ws.SheetData.Row[0].C[0].XMLName.Local, \"c\")\n}\n\nfunc TestStreamSetRowWithStyle(t *testing.T) {\n\tfile := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, file.Close())\n\t}()\n\tgrayStyleID, err := file.NewStyle(&Style{Font: &Font{Color: \"777777\"}})\n\tassert.NoError(t, err)\n\tblueStyleID, err := file.NewStyle(&Style{Font: &Font{Color: \"0000FF\"}})\n\tassert.NoError(t, err)\n\n\tsheetName := \"Sheet1\"\n\tstreamWriter, err := file.NewStreamWriter(sheetName)\n\tassert.NoError(t, err)\n\tassert.NoError(t, streamWriter.SetColStyle(1, 1, grayStyleID))\n\tassert.NoError(t, streamWriter.SetColStyle(3, 3, blueStyleID))\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", []interface{}{\n\t\t\"A1\",\n\t\tCell{Value: \"B1\"},\n\t\t&Cell{Value: \"C1\"},\n\t\tCell{StyleID: blueStyleID, Value: \"D1\"},\n\t\t&Cell{StyleID: blueStyleID, Value: \"E1\"},\n\t}, RowOpts{StyleID: grayStyleID}))\n\tassert.NoError(t, streamWriter.SetRow(\"A2\", []interface{}{\n\t\t\"A2\",\n\t\tCell{Value: \"B2\"},\n\t\t&Cell{Value: \"C2\"},\n\t\tCell{StyleID: grayStyleID, Value: \"D2\"},\n\t\t&Cell{StyleID: blueStyleID, Value: \"E2\"},\n\t}))\n\tassert.NoError(t, streamWriter.Flush())\n\n\tws, err := file.workSheetReader(sheetName)\n\tassert.NoError(t, err)\n\tfor colIdx, expected := range []int{grayStyleID, grayStyleID, grayStyleID, blueStyleID, blueStyleID} {\n\t\tassert.Equal(t, expected, ws.SheetData.Row[0].C[colIdx].S)\n\t}\n\tfor colIdx, expected := range []int{grayStyleID, 0, blueStyleID, grayStyleID, blueStyleID} {\n\t\tassert.Equal(t, expected, ws.SheetData.Row[1].C[colIdx].S)\n\t}\n}\n\nfunc TestStreamSetCellValFunc(t *testing.T) {\n\tf := NewFile()\n\tdefer func() {\n\t\tassert.NoError(t, f.Close())\n\t}()\n\tsw, err := f.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\tc := &xlsxC{}\n\tfor _, val := range []interface{}{\n\t\t128,\n\t\tint8(-128),\n\t\tint16(-32768),\n\t\tint32(-2147483648),\n\t\tint64(-9223372036854775808),\n\t\tuint(128),\n\t\tuint8(255),\n\t\tuint16(65535),\n\t\tuint32(4294967295),\n\t\tuint64(18446744073709551615),\n\t\tfloat32(100.1588),\n\t\t100.1588,\n\t\t\" Hello\",\n\t\t[]byte(\" Hello\"),\n\t\ttime.Now().UTC(),\n\t\ttime.Duration(1e13),\n\t\ttrue,\n\t\tnil,\n\t\tcomplex64(5 + 10i),\n\t} {\n\t\tassert.NoError(t, sw.setCellValFunc(c, val))\n\t}\n}\n\nfunc TestStreamWriterOutlineLevel(t *testing.T) {\n\tfile := NewFile()\n\tstreamWriter, err := file.NewStreamWriter(\"Sheet1\")\n\tassert.NoError(t, err)\n\n\t// Test set outlineLevel in row\n\tassert.NoError(t, streamWriter.SetRow(\"A1\", nil, RowOpts{OutlineLevel: 1}))\n\tassert.NoError(t, streamWriter.SetRow(\"A2\", nil, RowOpts{OutlineLevel: 7}))\n\tassert.ErrorIs(t, ErrOutlineLevel, streamWriter.SetRow(\"A3\", nil, RowOpts{OutlineLevel: 8}))\n\n\tassert.NoError(t, streamWriter.Flush())\n\t// Save spreadsheet by the given path\n\tassert.NoError(t, file.SaveAs(filepath.Join(\"test\", \"TestStreamWriterSetRowOutlineLevel.xlsx\")))\n\n\tfile, err = OpenFile(filepath.Join(\"test\", \"TestStreamWriterSetRowOutlineLevel.xlsx\"))\n\tassert.NoError(t, err)\n\tfor rowIdx, expected := range []uint8{1, 7, 0} {\n\t\tlevel, err := file.GetRowOutlineLevel(\"Sheet1\", rowIdx+1)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, level)\n\t}\n\tassert.NoError(t, file.Close())\n}\n\nfunc TestStreamWriterReader(t *testing.T) {\n\tvar (\n\t\terr error\n\t\tsw  = StreamWriter{\n\t\t\trawData: bufferedWriter{},\n\t\t}\n\t)\n\tsw.rawData.tmp, err = os.CreateTemp(os.TempDir(), \"excelize-\")\n\tassert.NoError(t, err)\n\tassert.NoError(t, sw.rawData.tmp.Close())\n\t// Test reader stat a closed temp file\n\t_, err = sw.rawData.Reader()\n\tassert.Error(t, err)\n\t_, err = sw.getRowValues(1, 1, 1)\n\tassert.Error(t, err)\n\tassert.NoError(t, os.Remove(sw.rawData.tmp.Name()))\n\n\tsw = StreamWriter{\n\t\tfile:    NewFile(),\n\t\trawData: bufferedWriter{},\n\t}\n\t// Test getRowValues without expected row\n\tsw.rawData.buf.WriteString(\"<worksheet><row r=\\\"1\\\"><c r=\\\"B1\\\"></c></row><worksheet/>\")\n\t_, err = sw.getRowValues(1, 1, 1)\n\tassert.NoError(t, err)\n\tsw.rawData.buf.Reset()\n\t// Test getRowValues with illegal cell reference\n\tsw.rawData.buf.WriteString(\"<worksheet><row r=\\\"1\\\"><c r=\\\"A\\\"></c></row><worksheet/>\")\n\t_, err = sw.getRowValues(1, 1, 1)\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), err)\n\tsw.rawData.buf.Reset()\n\t// Test getRowValues with invalid c element characters\n\tsw.rawData.buf.WriteString(\"<worksheet><row r=\\\"1\\\"><c></row><worksheet/>\")\n\t_, err = sw.getRowValues(1, 1, 1)\n\tassert.EqualError(t, err, \"XML syntax error on line 1: element <c> closed by </row>\")\n\tsw.rawData.buf.Reset()\n}\n\nfunc TestStreamWriterGetRowElement(t *testing.T) {\n\t// Test get row element without r attribute\n\tdec := xml.NewDecoder(strings.NewReader(\"<row ht=\\\"0\\\" />\"))\n\tfor {\n\t\ttoken, err := dec.Token()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\t_, ok := getRowElement(token, 0)\n\t\tassert.False(t, ok)\n\t}\n}\n"
  },
  {
    "path": "styles.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// stylesReader provides a function to get the pointer to the structure after\n// deserialization of xl/styles.xml.\nfunc (f *File) stylesReader() (*xlsxStyleSheet, error) {\n\tif f.Styles == nil {\n\t\tf.Styles = new(xlsxStyleSheet)\n\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathStyles)))).\n\t\t\tDecode(f.Styles); err != nil && err != io.EOF {\n\t\t\treturn f.Styles, err\n\t\t}\n\t}\n\treturn f.Styles, nil\n}\n\n// styleSheetWriter provides a function to save xl/styles.xml after serialize\n// structure.\nfunc (f *File) styleSheetWriter() {\n\tif f.Styles != nil {\n\t\toutput, _ := xml.Marshal(f.Styles)\n\t\tf.saveFileList(defaultXMLPathStyles, f.replaceNameSpaceBytes(defaultXMLPathStyles, output))\n\t}\n}\n\n// themeWriter provides a function to save xl/theme/theme1.xml after serialize\n// structure.\nfunc (f *File) themeWriter() {\n\tnewColor := func(c *decodeCTColor) xlsxCTColor {\n\t\treturn xlsxCTColor{\n\t\t\tScrgbClr:  c.ScrgbClr,\n\t\t\tSrgbClr:   c.SrgbClr,\n\t\t\tHslClr:    c.HslClr,\n\t\t\tSysClr:    c.SysClr,\n\t\t\tSchemeClr: c.SchemeClr,\n\t\t\tPrstClr:   c.PrstClr,\n\t\t}\n\t}\n\tnewFontScheme := func(c *decodeFontCollection) xlsxFontCollection {\n\t\treturn xlsxFontCollection{\n\t\t\tLatin:  c.Latin,\n\t\t\tEa:     c.Ea,\n\t\t\tCs:     c.Cs,\n\t\t\tFont:   c.Font,\n\t\t\tExtLst: c.ExtLst,\n\t\t}\n\t}\n\tif f.Theme != nil {\n\t\toutput, _ := xml.Marshal(xlsxTheme{\n\t\t\tXMLNSa: NameSpaceDrawingML.Value,\n\t\t\tXMLNSr: SourceRelationship.Value,\n\t\t\tName:   f.Theme.Name,\n\t\t\tThemeElements: xlsxBaseStyles{\n\t\t\t\tClrScheme: xlsxColorScheme{\n\t\t\t\t\tName:     f.Theme.ThemeElements.ClrScheme.Name,\n\t\t\t\t\tDk1:      newColor(&f.Theme.ThemeElements.ClrScheme.Dk1),\n\t\t\t\t\tLt1:      newColor(&f.Theme.ThemeElements.ClrScheme.Lt1),\n\t\t\t\t\tDk2:      newColor(&f.Theme.ThemeElements.ClrScheme.Dk2),\n\t\t\t\t\tLt2:      newColor(&f.Theme.ThemeElements.ClrScheme.Lt2),\n\t\t\t\t\tAccent1:  newColor(&f.Theme.ThemeElements.ClrScheme.Accent1),\n\t\t\t\t\tAccent2:  newColor(&f.Theme.ThemeElements.ClrScheme.Accent2),\n\t\t\t\t\tAccent3:  newColor(&f.Theme.ThemeElements.ClrScheme.Accent3),\n\t\t\t\t\tAccent4:  newColor(&f.Theme.ThemeElements.ClrScheme.Accent4),\n\t\t\t\t\tAccent5:  newColor(&f.Theme.ThemeElements.ClrScheme.Accent5),\n\t\t\t\t\tAccent6:  newColor(&f.Theme.ThemeElements.ClrScheme.Accent6),\n\t\t\t\t\tHlink:    newColor(&f.Theme.ThemeElements.ClrScheme.Hlink),\n\t\t\t\t\tFolHlink: newColor(&f.Theme.ThemeElements.ClrScheme.FolHlink),\n\t\t\t\t\tExtLst:   f.Theme.ThemeElements.ClrScheme.ExtLst,\n\t\t\t\t},\n\t\t\t\tFontScheme: xlsxFontScheme{\n\t\t\t\t\tName:      f.Theme.ThemeElements.FontScheme.Name,\n\t\t\t\t\tMajorFont: newFontScheme(&f.Theme.ThemeElements.FontScheme.MajorFont),\n\t\t\t\t\tMinorFont: newFontScheme(&f.Theme.ThemeElements.FontScheme.MinorFont),\n\t\t\t\t\tExtLst:    f.Theme.ThemeElements.FontScheme.ExtLst,\n\t\t\t\t},\n\t\t\t\tFmtScheme: xlsxStyleMatrix{\n\t\t\t\t\tName:           f.Theme.ThemeElements.FmtScheme.Name,\n\t\t\t\t\tFillStyleLst:   f.Theme.ThemeElements.FmtScheme.FillStyleLst,\n\t\t\t\t\tLnStyleLst:     f.Theme.ThemeElements.FmtScheme.LnStyleLst,\n\t\t\t\t\tEffectStyleLst: f.Theme.ThemeElements.FmtScheme.EffectStyleLst,\n\t\t\t\t\tBgFillStyleLst: f.Theme.ThemeElements.FmtScheme.BgFillStyleLst,\n\t\t\t\t},\n\t\t\t\tExtLst: f.Theme.ThemeElements.ExtLst,\n\t\t\t},\n\t\t\tObjectDefaults:    f.Theme.ObjectDefaults,\n\t\t\tExtraClrSchemeLst: f.Theme.ExtraClrSchemeLst,\n\t\t\tCustClrLst:        f.Theme.CustClrLst,\n\t\t\tExtLst:            f.Theme.ExtLst,\n\t\t})\n\t\tf.saveFileList(defaultXMLPathTheme, f.replaceNameSpaceBytes(defaultXMLPathTheme, output))\n\t}\n}\n\n// sharedStringsWriter provides a function to save xl/sharedStrings.xml after\n// serialize structure.\nfunc (f *File) sharedStringsWriter() {\n\tif f.SharedStrings != nil {\n\t\toutput, _ := xml.Marshal(f.SharedStrings)\n\t\tf.saveFileList(defaultXMLPathSharedStrings, f.replaceNameSpaceBytes(defaultXMLPathSharedStrings, output))\n\t}\n}\n\n// parseFormatStyleSet provides a function to parse the format settings of the\n// cells and conditional formats.\nfunc parseFormatStyleSet(style *Style) (*Style, error) {\n\tvar err error\n\tif style.Font != nil {\n\t\tif countUTF16String(style.Font.Family) > MaxFontFamilyLength {\n\t\t\treturn style, ErrFontLength\n\t\t}\n\t\tif style.Font.Size > MaxFontSize {\n\t\t\treturn style, ErrFontSize\n\t\t}\n\t}\n\tswitch style.Fill.Type {\n\tcase \"gradient\":\n\t\tif len(style.Fill.Color) != 2 {\n\t\t\treturn style, ErrFillGradientColor\n\t\t}\n\t\tif style.Fill.Shading < 0 || style.Fill.Shading > 16 {\n\t\t\treturn style, ErrFillGradientShading\n\t\t}\n\tcase \"pattern\":\n\t\tif len(style.Fill.Color) > 1 {\n\t\t\treturn style, ErrFillPatternColor\n\t\t}\n\t\tif style.Fill.Pattern < 0 || style.Fill.Pattern > 18 {\n\t\t\treturn style, ErrFillPattern\n\t\t}\n\tcase \"\":\n\tdefault:\n\t\treturn style, ErrFillType\n\t}\n\tif style.CustomNumFmt != nil && len(*style.CustomNumFmt) == 0 {\n\t\terr = ErrCustomNumFmt\n\t}\n\treturn style, err\n}\n\n// NewStyle provides a function to create the style for cells by a given style\n// options, and returns style index. The same style index can not be used\n// across different workbook. This function is concurrency safe. Note that\n// the 'Font.Color' field uses an RGB color represented in 'RRGGBB' hexadecimal\n// notation.\n//\n// The following table shows the border types used in 'Border.Type' supported by\n// excelize:\n//\n//\t Type         | Description\n//\t--------------+------------------\n//\t left         | Left border\n//\t top          | Top border\n//\t right        | Right border\n//\t bottom       | Bottom border\n//\t diagonalDown | Diagonal down border\n//\t diagonalUp   | Diagonal up border\n//\n// The following table shows the border styles used in 'Border.Style' supported\n// by excelize index number:\n//\n//\t Index | Name          | Weight | Style\n//\t-------+---------------+--------+-------------\n//\t 0     | None          | 0      |\n//\t 1     | Continuous    | 1      | -----------\n//\t 2     | Continuous    | 2      | -----------\n//\t 3     | Dash          | 1      | - - - - - -\n//\t 4     | Dot           | 1      | . . . . . .\n//\t 5     | Continuous    | 3      | -----------\n//\t 6     | Double        | 3      | ===========\n//\t 7     | Continuous    | 0      | -----------\n//\t 8     | Dash          | 2      | - - - - - -\n//\t 9     | Dash Dot      | 1      | - . - . - .\n//\t 10    | Dash Dot      | 2      | - . - . - .\n//\t 11    | Dash Dot Dot  | 1      | - . . - . .\n//\t 12    | Dash Dot Dot  | 2      | - . . - . .\n//\t 13    | SlantDash Dot | 2      | / - . / - .\n//\n// The following table shows the border styles used in 'Border.Style' in the\n// order shown in the Excel dialog:\n//\n//\t Index | Style       | Index | Style\n//\t-------+-------------+-------+-------------\n//\t 0     | None        | 12    | - . . - . .\n//\t 7     | ----------- | 13    | / - . / - .\n//\t 4     | . . . . . . | 10    | - . - . - .\n//\t 11    | - . . - . . | 8     | - - - - - -\n//\t 9     | - . - . - . | 2     | -----------\n//\t 3     | - - - - - - | 5     | -----------\n//\t 1     | ----------- | 6     | ===========\n//\n// The 'Fill.Type' use to set fill type, supported types are 'gradient' or\n// 'pattern'. When configuring a fill of type 'gradient', a valid 'Fill.Shading'\n// value must be provided, and 'Fill.Color' must be an array containing exactly\n// two valid color values. When configuring a fill of type 'pattern', a valid\n// 'Fill.Pattern' value must be provided, and 'Fill.Color' must be an array\n// containing exactly one valid color value.\n//\n// The following table shows the shading styles used in 'Fill.Shading' supported\n// by excelize index number:\n//\n//\t Index | Style           | Index | Style\n//\t-------+-----------------+-------+-----------------\n//\t 0-2   | Horizontal      | 9-11  | Diagonal down\n//\t 3-5   | Vertical        | 12-15 | From corner\n//\t 6-8   | Diagonal Up     | 16    | From center\n//\n// The following table shows the pattern styles used in 'Fill.Pattern' supported\n// by excelize index number:\n//\n//\t Index | Style           | Index | Style\n//\t-------+-----------------+-------+-----------------\n//\t 0     | None            | 10    | darkTrellis\n//\t 1     | solid           | 11    | lightHorizontal\n//\t 2     | mediumGray      | 12    | lightVertical\n//\t 3     | darkGray        | 13    | lightDown\n//\t 4     | lightGray       | 14    | lightUp\n//\t 5     | darkHorizontal  | 15    | lightGrid\n//\t 6     | darkVertical    | 16    | lightTrellis\n//\t 7     | darkDown        | 17    | gray125\n//\t 8     | darkUp          | 18    | gray0625\n//\t 9     | darkGrid        |       |\n//\n// The 'Fill.Transparency' only use to set transparency for chart and shape, not\n// used for cell. The value of 'Fill.Transparency' should be a number from 0 to\n// 100, which represents 0% to 100%, The default value is 0, representing full\n// opaque (not transparent).\n//\n// The 'Alignment.Indent' is an integer value, where an increment of 1\n// represents 3 spaces. Indicates the number of spaces (of the normal style\n// font) of indentation for text in a cell. The number of spaces to indent is\n// calculated as following:\n//\n//\tNumber of spaces to indent = indent value * 3\n//\n// For example, an indent value of 1 means that the text begins 3 space widths\n// (of the normal style font) from the edge of the cell. Note: The width of one\n// space character is defined by the font. Only left, right, and distributed\n// horizontal alignments are supported.\n//\n// The following table shows the type of cells' horizontal alignment used\n// in 'Alignment.Horizontal':\n//\n//\t Style\n//\t------------------\n//\t left\n//\t center\n//\t right\n//\t fill\n//\t justify\n//\t centerContinuous\n//\t distributed\n//\n// The following table shows the type of cells' vertical alignment used in\n// 'Alignment.Vertical':\n//\n//\t Style\n//\t------------------\n//\t top\n//\t center\n//\t justify\n//\t distributed\n//\n// The 'Alignment.ReadingOrder' is an uint64 value indicating whether the\n// reading order of the cell is left-to-right, right-to-left, or context\n// dependent. the valid value of this field was:\n//\n//\t Value | Description\n//\t-------+----------------------------------------------------\n//\t 0     | Context Dependent - reading order is determined by scanning the\n//\t       | text for the first non-whitespace character: if it is a strong\n//\t       | right-to-left character, the reading order is right-to-left;\n//\t       | otherwise, the reading order left-to-right.\n//\t 1     | Left-to-Right: reading order is left-to-right in the cell, as in\n//\t       | English.\n//\t 2     | Right-to-Left: reading order is right-to-left in the cell, as in\n//\t       | Hebrew.\n//\n// The 'Alignment.RelativeIndent' is an integer value to indicate the additional\n// number of spaces of indentation to adjust for text in a cell.\n//\n// The following table shows the type of font underline style used in\n// 'Font.Underline':\n//\n//\t Style\n//\t------------------\n//\t none\n//\t single\n//\t double\n//\n// NumFmt is used to set the built-in all languages formats index, built-in\n// language formats index, or built-in currency formats index, it doesn't work\n// when you specify the custom number format by CustomNumFmt. When you get\n// style definition by the GetStyle or GetConditionalStyle function, the NumFmt\n// only works if the number format code is exactly equal with any built-in all\n// languages format code, built-in language formats code, or built-in currency\n// format code.\n//\n// Excel's built-in all languages formats are shown in the following table:\n//\n//\t Index | Format String\n//\t-------+----------------------------------------------------\n//\t 0     | General\n//\t 1     | 0\n//\t 2     | 0.00\n//\t 3     | #,##0\n//\t 4     | #,##0.00\n//\t 5     | ($#,##0_);($#,##0)\n//\t 6     | ($#,##0_);[Red]($#,##0)\n//\t 7     | ($#,##0.00_);($#,##0.00)\n//\t 8     | ($#,##0.00_);[Red]($#,##0.00)\n//\t 9     | 0%\n//\t 10    | 0.00%\n//\t 11    | 0.00E+00\n//\t 12    | # ?/?\n//\t 13    | # ??/??\n//\t 14    | m/d/yy\n//\t 15    | d-mmm-yy\n//\t 16    | d-mmm\n//\t 17    | mmm-yy\n//\t 18    | h:mm AM/PM\n//\t 19    | h:mm:ss AM/PM\n//\t 20    | h:mm\n//\t 21    | h:mm:ss\n//\t 22    | m/d/yy h:mm\n//\t ...   | ...\n//\t 37    | (#,##0_);(#,##0)\n//\t 38    | (#,##0_);[Red](#,##0)\n//\t 39    | (#,##0.00_);(#,##0.00)\n//\t 40    | (#,##0.00_);[Red](#,##0.00)\n//\t 41    | _(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)\n//\t 42    | _($* #,##0_);_($* (#,##0);_($* \"-\"_);_(@_)\n//\t 43    | _(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)\n//\t 44    | _($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)\n//\t 45    | mm:ss\n//\t 46    | [h]:mm:ss\n//\t 47    | mm:ss.0\n//\t 48    | ##0.0E+0\n//\t 49    | @\n//\n// Number format code in zh-tw language:\n//\n//\t Index | Symbol\n//\t-------+-------------------------------------------\n//\t 27    | [$-404]e/m/d\n//\t 28    | [$-404]e\"年\"m\"月\"d\"日\"\n//\t 29    | [$-404]e\"年\"m\"月\"d\"日\"\n//\t 30    | m/d/yy\n//\t 31    | yyyy\"年\"m\"月\"d\"日\"\n//\t 32    | hh\"時\"mm\"分\"\n//\t 33    | hh\"時\"mm\"分\"ss\"秒\"\n//\t 34    | 上午/下午 hh\"時\"mm\"分\"\n//\t 35    | 上午/下午 hh\"時\"mm\"分\"ss\"秒\"\n//\t 36    | [$-404]e/m/d\n//\t 50    | [$-404]e/m/d\n//\t 51    | [$-404]e\"年\"m\"月\"d\"日\"\n//\t 52    | 上午/下午 hh\"時\"mm\"分\"\n//\t 53    | 上午/下午 hh\"時\"mm\"分\"ss\"秒\"\n//\t 54    | [$-404]e\"年\"m\"月\"d\"日\"\n//\t 55    | 上午/下午 hh\"時\"mm\"分\"\n//\t 56    | 上午/下午 hh\"時\"mm\"分\"ss\"秒\"\n//\t 57    | [$-404]e/m/d\n//\t 58    | [$-404]e\"年\"m\"月\"d\"日\"\n//\n// Number format code in zh-cn language:\n//\n//\t Index | Symbol\n//\t-------+-------------------------------------------\n//\t 27    | yyyy\"年\"m\"月\"\n//\t 28    | m\"月\"d\"日\"\n//\t 29    | m\"月\"d\"日\"\n//\t 30    | m-d-yy\n//\t 31    | yyyy\"年\"m\"月\"d\"日\"\n//\t 32    | h\"时\"mm\"分\"\n//\t 33    | h\"时\"mm\"分\"ss\"秒\"\n//\t 34    | 上午/下午 h\"时\"mm\"分\"\n//\t 35    | 上午/下午 h\"时\"mm\"分\"ss\"秒\n//\t 36    | yyyy\"年\"m\"月\n//\t 50    | yyyy\"年\"m\"月\n//\t 51    | m\"月\"d\"日\n//\t 52    | yyyy\"年\"m\"月\n//\t 53    | m\"月\"d\"日\n//\t 54    | m\"月\"d\"日\n//\t 55    | 上午/下午 h\"时\"mm\"分\n//\t 56    | 上午/下午 h\"时\"mm\"分\"ss\"秒\n//\t 57    | yyyy\"年\"m\"月\n//\t 58    | m\"月\"d\"日\"\n//\n// Number format code in ja-jp language:\n//\n//\t Index | Symbol\n//\t-------+-------------------------------------------\n//\t 27    | [$-411]ge.m.d\n//\t 28    | [$-411]ggge\"年\"m\"月\"d\"日\n//\t 29    | [$-411]ggge\"年\"m\"月\"d\"日\n//\t 30    | m/d/y\n//\t 31    | yyyy\"年\"m\"月\"d\"日\n//\t 32    | h\"時\"mm\"分\n//\t 33    | h\"時\"mm\"分\"ss\"秒\n//\t 34    | yyyy\"年\"m\"月\n//\t 35    | m\"月\"d\"日\n//\t 36    | [$-411]ge.m.d\n//\t 50    | [$-411]ge.m.d\n//\t 51    | [$-411]ggge\"年\"m\"月\"d\"日\n//\t 52    | yyyy\"年\"m\"月\n//\t 53    | m\"月\"d\"日\n//\t 54    | [$-411]ggge\"年\"m\"月\"d\"日\n//\t 55    | yyyy\"年\"m\"月\n//\t 56    | m\"月\"d\"日\n//\t 57    | [$-411]ge.m.d\n//\t 58    | [$-411]ggge\"年\"m\"月\"d\"日\"\n//\n// Number format code in ko-kr language:\n//\n//\t Index | Symbol\n//\t-------+-------------------------------------------\n//\t 27    | yyyy\"年\" mm\"月\" dd\"日\n//\t 28    | mm-d\n//\t 29    | mm-d\n//\t 30    | mm-dd-y\n//\t 31    | yyyy\"년\" mm\"월\" dd\"일\n//\t 32    | h\"시\" mm\"분\n//\t 33    | h\"시\" mm\"분\" ss\"초\n//\t 34    | yyyy-mm-d\n//\t 35    | yyyy-mm-d\n//\t 36    | yyyy\"年\" mm\"月\" dd\"日\n//\t 50    | yyyy\"年\" mm\"月\" dd\"日\n//\t 51    | mm-d\n//\t 52    | yyyy-mm-d\n//\t 53    | yyyy-mm-d\n//\t 54    | mm-d\n//\t 55    | yyyy-mm-d\n//\t 56    | yyyy-mm-d\n//\t 57    | yyyy\"年\" mm\"月\" dd\"日\n//\t 58    | mm-dd\n//\n// Number format code in th-th language:\n//\n//\t Index | Symbol\n//\t-------+-------------------------------------------\n//\t 59    | t\n//\t 60    | t0.0\n//\t 61    | t#,##\n//\t 62    | t#,##0.0\n//\t 67    | t0\n//\t 68    | t0.00\n//\t 69    | t# ?/\n//\t 70    | t# ??/?\n//\t 71    | ว/ด/ปปป\n//\t 72    | ว-ดดด-ป\n//\t 73    | ว-ดด\n//\t 74    | ดดด-ป\n//\t 75    | ช:น\n//\t 76    | ช:นน:ท\n//\t 77    | ว/ด/ปปปป ช:น\n//\t 78    | นน:ท\n//\t 79    | [ช]:นน:ท\n//\t 80    | นน:ทท.\n//\t 81    | d/m/bb\n//\n// Excelize built-in currency formats are shown in the following table, only\n// support these types in the following table (Index number is used only for\n// markup and is not used inside an Excel file and you can't get formatted value\n// by the function GetCellValue) currently:\n//\n//\t Index | Symbol\n//\t-------+---------------------------------------------------------------\n//\t 164   | ¥\n//\t 165   | $ English (United States)\n//\t 166   | $ Cherokee (United States)\n//\t 167   | $ Chinese (Singapore)\n//\t 168   | $ Chinese (Taiwan)\n//\t 169   | $ English (Australia)\n//\t 170   | $ English (Belize)\n//\t 171   | $ English (Canada)\n//\t 172   | $ English (Jamaica)\n//\t 173   | $ English (New Zealand)\n//\t 174   | $ English (Singapore)\n//\t 175   | $ English (Trinidad & Tobago)\n//\t 176   | $ English (U.S. Virgin Islands)\n//\t 177   | $ English (United States)\n//\t 178   | $ French (Canada)\n//\t 179   | $ Hawaiian (United States)\n//\t 180   | $ Malay (Brunei)\n//\t 181   | $ Quechua (Ecuador)\n//\t 182   | $ Spanish (Chile)\n//\t 183   | $ Spanish (Colombia)\n//\t 184   | $ Spanish (Ecuador)\n//\t 185   | $ Spanish (El Salvador)\n//\t 186   | $ Spanish (Mexico)\n//\t 187   | $ Spanish (Puerto Rico)\n//\t 188   | $ Spanish (United States)\n//\t 189   | $ Spanish (Uruguay)\n//\t 190   | £ English (United Kingdom)\n//\t 191   | £ Scottish Gaelic (United Kingdom)\n//\t 192   | £ Welsh (United Kindom)\n//\t 193   | ¥ Chinese (China)\n//\t 194   | ¥ Japanese (Japan)\n//\t 195   | ¥ Sichuan Yi (China)\n//\t 196   | ¥ Tibetan (China)\n//\t 197   | ¥ Uyghur (China)\n//\t 198   | ֏ Armenian (Armenia)\n//\t 199   | ؋ Pashto (Afghanistan)\n//\t 200   | ؋ Persian (Afghanistan)\n//\t 201   | ৳ Bengali (Bangladesh)\n//\t 202   | ៛ Khmer (Cambodia)\n//\t 203   | ₡ Spanish (Costa Rica)\n//\t 204   | ₦ Hausa (Nigeria)\n//\t 205   | ₦ Igbo (Nigeria)\n//\t 206   | ₩ Korean (South Korea)\n//\t 207   | ₪ Hebrew (Israel)\n//\t 208   | ₫ Vietnamese (Vietnam)\n//\t 209   | € Basque (Spain)\n//\t 210   | € Breton (France)\n//\t 211   | € Catalan (Spain)\n//\t 212   | € Corsican (France)\n//\t 213   | € Dutch (Belgium)\n//\t 214   | € Dutch (Netherlands)\n//\t 215   | € English (Ireland)\n//\t 216   | € Estonian (Estonia)\n//\t 217   | € Euro (€ 123)\n//\t 218   | € Euro (123 €)\n//\t 219   | € Finnish (Finland)\n//\t 220   | € French (Belgium)\n//\t 221   | € French (France)\n//\t 222   | € French (Luxembourg)\n//\t 223   | € French (Monaco)\n//\t 224   | € French (Réunion)\n//\t 225   | € Galician (Spain)\n//\t 226   | € German (Austria)\n//\t 227   | € German (German)\n//\t 228   | € German (Luxembourg)\n//\t 229   | € Greek (Greece)\n//\t 230   | € Inari Sami (Finland)\n//\t 231   | € Irish (Ireland)\n//\t 232   | € Italian (Italy)\n//\t 233   | € Latin (Italy)\n//\t 234   | € Latin, Serbian (Montenegro)\n//\t 235   | € Larvian (Latvia)\n//\t 236   | € Lithuanian (Lithuania)\n//\t 237   | € Lower Sorbian (Germany)\n//\t 238   | € Luxembourgish (Luxembourg)\n//\t 239   | € Maltese (Malta)\n//\t 240   | € Northern Sami (Finland)\n//\t 241   | € Occitan (France)\n//\t 242   | € Portuguese (Portugal)\n//\t 243   | € Serbian (Montenegro)\n//\t 244   | € Skolt Sami (Finland)\n//\t 245   | € Slovak (Slovakia)\n//\t 246   | € Slovenian (Slovenia)\n//\t 247   | € Spanish (Spain)\n//\t 248   | € Swedish (Finland)\n//\t 249   | € Swiss German (France)\n//\t 250   | € Upper Sorbian (Germany)\n//\t 251   | € Western Frisian (Netherlands)\n//\t 252   | ₭ Lao (Laos)\n//\t 253   | ₮ Mongolian (Mongolia)\n//\t 254   | ₮ Mongolian, Mongolian (Mongolia)\n//\t 255   | ₱ English (Philippines)\n//\t 256   | ₱ Filipino (Philippines)\n//\t 257   | ₴ Ukrainian (Ukraine)\n//\t 258   | ₸ Kazakh (Kazakhstan)\n//\t 259   | ₹ Arabic, Kashmiri (India)\n//\t 260   | ₹ English (India)\n//\t 261   | ₹ Gujarati (India)\n//\t 262   | ₹ Hindi (India)\n//\t 263   | ₹ Kannada (India)\n//\t 264   | ₹ Kashmiri (India)\n//\t 265   | ₹ Konkani (India)\n//\t 266   | ₹ Manipuri (India)\n//\t 267   | ₹ Marathi (India)\n//\t 268   | ₹ Nepali (India)\n//\t 269   | ₹ Oriya (India)\n//\t 270   | ₹ Punjabi (India)\n//\t 271   | ₹ Sanskrit (India)\n//\t 272   | ₹ Sindhi (India)\n//\t 273   | ₹ Tamil (India)\n//\t 274   | ₹ Urdu (India)\n//\t 275   | ₺ Turkish (Turkey)\n//\t 276   | ₼ Azerbaijani (Azerbaijan)\n//\t 277   | ₼ Cyrillic, Azerbaijani (Azerbaijan)\n//\t 278   | ₽ Russian (Russia)\n//\t 279   | ₽ Sakha (Russia)\n//\t 280   | ₾ Georgian (Georgia)\n//\t 281   | B/. Spanish (Panama)\n//\t 282   | Br Oromo (Ethiopia)\n//\t 283   | Br Somali (Ethiopia)\n//\t 284   | Br Tigrinya (Ethiopia)\n//\t 285   | Bs Quechua (Bolivia)\n//\t 286   | Bs Spanish (Bolivia)\n//\t 287   | BS. Spanish (Venezuela)\n//\t 288   | BWP Tswana (Botswana)\n//\t 289   | C$ Spanish (Nicaragua)\n//\t 290   | CA$ Latin, Inuktitut (Canada)\n//\t 291   | CA$ Mohawk (Canada)\n//\t 292   | CA$ Unified Canadian Aboriginal Syllabics, Inuktitut (Canada)\n//\t 293   | CFA French (Mali)\n//\t 294   | CFA French (Senegal)\n//\t 295   | CFA Fulah (Senegal)\n//\t 296   | CFA Wolof (Senegal)\n//\t 297   | CHF French (Switzerland)\n//\t 298   | CHF German (Liechtenstein)\n//\t 299   | CHF German (Switzerland)\n//\t 300   | CHF Italian (Switzerland)\n//\t 301   | CHF Romansh (Switzerland)\n//\t 302   | CLP Mapuche (Chile)\n//\t 303   | CN¥ Mongolian, Mongolian (China)\n//\t 304   | DZD Central Atlas Tamazight (Algeria)\n//\t 305   | FCFA French (Cameroon)\n//\t 306   | Ft Hungarian (Hungary)\n//\t 307   | G French (Haiti)\n//\t 308   | Gs. Spanish (Paraguay)\n//\t 309   | GTQ K'iche' (Guatemala)\n//\t 310   | HK$ Chinese (Hong Kong (China))\n//\t 311   | HK$ English (Hong Kong (China))\n//\t 312   | HRK Croatian (Croatia)\n//\t 313   | IDR English (Indonesia)\n//\t 314   | IQD Arbic, Central Kurdish (Iraq)\n//\t 315   | ISK Icelandic (Iceland)\n//\t 316   | K Burmese (Myanmar (Burma))\n//\t 317   | Kč Czech (Czech Republic)\n//\t 318   | KM Bosnian (Bosnia & Herzegovina)\n//\t 319   | KM Croatian (Bosnia & Herzegovina)\n//\t 320   | KM Latin, Serbian (Bosnia & Herzegovina)\n//\t 321   | kr Faroese (Faroe Islands)\n//\t 322   | kr Northern Sami (Norway)\n//\t 323   | kr Northern Sami (Sweden)\n//\t 324   | kr Norwegian Bokmål (Norway)\n//\t 325   | kr Norwegian Nynorsk (Norway)\n//\t 326   | kr Swedish (Sweden)\n//\t 327   | kr. Danish (Denmark)\n//\t 328   | kr. Kalaallisut (Greenland)\n//\t 329   | Ksh Swahili (kenya)\n//\t 330   | L Romanian (Moldova)\n//\t 331   | L Russian (Moldova)\n//\t 332   | L Spanish (Honduras)\n//\t 333   | Lekë Albanian (Albania)\n//\t 334   | MAD Arabic, Central Atlas Tamazight (Morocco)\n//\t 335   | MAD French (Morocco)\n//\t 336   | MAD Tifinagh, Central Atlas Tamazight (Morocco)\n//\t 337   | MOP$ Chinese (Macau (China))\n//\t 338   | MVR Divehi (Maldives)\n//\t 339   | Nfk Tigrinya (Eritrea)\n//\t 340   | NGN Bini (Nigeria)\n//\t 341   | NGN Fulah (Nigeria)\n//\t 342   | NGN Ibibio (Nigeria)\n//\t 343   | NGN Kanuri (Nigeria)\n//\t 344   | NOK Lule Sami (Norway)\n//\t 345   | NOK Southern Sami (Norway)\n//\t 346   | NZ$ Maori (New Zealand)\n//\t 347   | PKR Sindhi (Pakistan)\n//\t 348   | PYG Guarani (Paraguay)\n//\t 349   | Q Spanish (Guatemala)\n//\t 350   | R Afrikaans (South Africa)\n//\t 351   | R English (South Africa)\n//\t 352   | R Zulu (South Africa)\n//\t 353   | R$ Portuguese (Brazil)\n//\t 354   | RD$ Spanish (Dominican Republic)\n//\t 355   | RF Kinyarwanda (Rwanda)\n//\t 356   | RM English (Malaysia)\n//\t 357   | RM Malay (Malaysia)\n//\t 358   | RON Romanian (Romania)\n//\t 359   | Rp Indonesoan (Indonesia)\n//\t 360   | Rs Urdu (Pakistan)\n//\t 361   | Rs. Tamil (Sri Lanka)\n//\t 362   | RSD Latin, Serbian (Serbia)\n//\t 363   | RSD Serbian (Serbia)\n//\t 364   | RUB Bashkir (Russia)\n//\t 365   | RUB Tatar (Russia)\n//\t 366   | S/. Quechua (Peru)\n//\t 367   | S/. Spanish (Peru)\n//\t 368   | SEK Lule Sami (Sweden)\n//\t 369   | SEK Southern Sami (Sweden)\n//\t 370   | soʻm Latin, Uzbek (Uzbekistan)\n//\t 371   | soʻm Uzbek (Uzbekistan)\n//\t 372   | SYP Syriac (Syria)\n//\t 373   | THB Thai (Thailand)\n//\t 374   | TMT Turkmen (Turkmenistan)\n//\t 375   | US$ English (Zimbabwe)\n//\t 376   | ZAR Northern Sotho (South Africa)\n//\t 377   | ZAR Southern Sotho (South Africa)\n//\t 378   | ZAR Tsonga (South Africa)\n//\t 379   | ZAR Tswana (south Africa)\n//\t 380   | ZAR Venda (South Africa)\n//\t 381   | ZAR Xhosa (South Africa)\n//\t 382   | zł Polish (Poland)\n//\t 383   | ден Macedonian (Macedonia)\n//\t 384   | KM Cyrillic, Bosnian (Bosnia & Herzegovina)\n//\t 385   | KM Serbian (Bosnia & Herzegovina)\n//\t 386   | лв. Bulgarian (Bulgaria)\n//\t 387   | p. Belarusian (Belarus)\n//\t 388   | сом Kyrgyz (Kyrgyzstan)\n//\t 389   | сом Tajik (Tajikistan)\n//\t 390   | ج.م. Arabic (Egypt)\n//\t 391   | د.أ. Arabic (Jordan)\n//\t 392   | د.أ. Arabic (United Arab Emirates)\n//\t 393   | د.ب. Arabic (Bahrain)\n//\t 394   | د.ت. Arabic (Tunisia)\n//\t 395   | د.ج. Arabic (Algeria)\n//\t 396   | د.ع. Arabic (Iraq)\n//\t 397   | د.ك. Arabic (Kuwait)\n//\t 398   | د.ل. Arabic (Libya)\n//\t 399   | د.م. Arabic (Morocco)\n//\t 400   | ر Punjabi (Pakistan)\n//\t 401   | ر.س. Arabic (Saudi Arabia)\n//\t 402   | ر.ع. Arabic (Oman)\n//\t 403   | ر.ق. Arabic (Qatar)\n//\t 404   | ر.ي. Arabic (Yemen)\n//\t 405   | ریال Persian (Iran)\n//\t 406   | ل.س. Arabic (Syria)\n//\t 407   | ل.ل. Arabic (Lebanon)\n//\t 408   | ብር Amharic (Ethiopia)\n//\t 409   | रू Nepaol (Nepal)\n//\t 410   | රු. Sinhala (Sri Lanka)\n//\t 411   | ADP\n//\t 412   | AED\n//\t 413   | AFA\n//\t 414   | AFN\n//\t 415   | ALL\n//\t 416   | AMD\n//\t 417   | ANG\n//\t 418   | AOA\n//\t 419   | ARS\n//\t 420   | ATS\n//\t 421   | AUD\n//\t 422   | AWG\n//\t 423   | AZM\n//\t 424   | AZN\n//\t 425   | BAM\n//\t 426   | BBD\n//\t 427   | BDT\n//\t 428   | BEF\n//\t 429   | BGL\n//\t 430   | BGN\n//\t 431   | BHD\n//\t 432   | BIF\n//\t 433   | BMD\n//\t 434   | BND\n//\t 435   | BOB\n//\t 436   | BOV\n//\t 437   | BRL\n//\t 438   | BSD\n//\t 439   | BTN\n//\t 440   | BWP\n//\t 441   | BYR\n//\t 442   | BZD\n//\t 443   | CAD\n//\t 444   | CDF\n//\t 445   | CHE\n//\t 446   | CHF\n//\t 447   | CHW\n//\t 448   | CLF\n//\t 449   | CLP\n//\t 450   | CNY\n//\t 451   | COP\n//\t 452   | COU\n//\t 453   | CRC\n//\t 454   | CSD\n//\t 455   | CUC\n//\t 456   | CVE\n//\t 457   | CYP\n//\t 458   | CZK\n//\t 459   | DEM\n//\t 460   | DJF\n//\t 461   | DKK\n//\t 462   | DOP\n//\t 463   | DZD\n//\t 464   | ECS\n//\t 465   | ECV\n//\t 466   | EEK\n//\t 467   | EGP\n//\t 468   | ERN\n//\t 469   | ESP\n//\t 470   | ETB\n//\t 471   | EUR\n//\t 472   | FIM\n//\t 473   | FJD\n//\t 474   | FKP\n//\t 475   | FRF\n//\t 476   | GBP\n//\t 477   | GEL\n//\t 478   | GHC\n//\t 479   | GHS\n//\t 480   | GIP\n//\t 481   | GMD\n//\t 482   | GNF\n//\t 483   | GRD\n//\t 484   | GTQ\n//\t 485   | GYD\n//\t 486   | HKD\n//\t 487   | HNL\n//\t 488   | HRK\n//\t 489   | HTG\n//\t 490   | HUF\n//\t 491   | IDR\n//\t 492   | IEP\n//\t 493   | ILS\n//\t 494   | INR\n//\t 495   | IQD\n//\t 496   | IRR\n//\t 497   | ISK\n//\t 498   | ITL\n//\t 499   | JMD\n//\t 500   | JOD\n//\t 501   | JPY\n//\t 502   | KAF\n//\t 503   | KES\n//\t 504   | KGS\n//\t 505   | KHR\n//\t 506   | KMF\n//\t 507   | KPW\n//\t 508   | KRW\n//\t 509   | KWD\n//\t 510   | KYD\n//\t 511   | KZT\n//\t 512   | LAK\n//\t 513   | LBP\n//\t 514   | LKR\n//\t 515   | LRD\n//\t 516   | LSL\n//\t 517   | LTL\n//\t 518   | LUF\n//\t 519   | LVL\n//\t 520   | LYD\n//\t 521   | MAD\n//\t 522   | MDL\n//\t 523   | MGA\n//\t 524   | MGF\n//\t 525   | MKD\n//\t 526   | MMK\n//\t 527   | MNT\n//\t 528   | MOP\n//\t 529   | MRO\n//\t 530   | MTL\n//\t 531   | MUR\n//\t 532   | MVR\n//\t 533   | MWK\n//\t 534   | MXN\n//\t 535   | MXV\n//\t 536   | MYR\n//\t 537   | MZM\n//\t 538   | MZN\n//\t 539   | NAD\n//\t 540   | NGN\n//\t 541   | NIO\n//\t 542   | NLG\n//\t 543   | NOK\n//\t 544   | NPR\n//\t 545   | NTD\n//\t 546   | NZD\n//\t 547   | OMR\n//\t 548   | PAB\n//\t 549   | PEN\n//\t 550   | PGK\n//\t 551   | PHP\n//\t 552   | PKR\n//\t 553   | PLN\n//\t 554   | PTE\n//\t 555   | PYG\n//\t 556   | QAR\n//\t 557   | ROL\n//\t 558   | RON\n//\t 559   | RSD\n//\t 560   | RUB\n//\t 561   | RUR\n//\t 562   | RWF\n//\t 563   | SAR\n//\t 564   | SBD\n//\t 565   | SCR\n//\t 566   | SDD\n//\t 567   | SDG\n//\t 568   | SDP\n//\t 569   | SEK\n//\t 570   | SGD\n//\t 571   | SHP\n//\t 572   | SIT\n//\t 573   | SKK\n//\t 574   | SLL\n//\t 575   | SOS\n//\t 576   | SPL\n//\t 577   | SRD\n//\t 578   | SRG\n//\t 579   | STD\n//\t 580   | SVC\n//\t 581   | SYP\n//\t 582   | SZL\n//\t 583   | THB\n//\t 584   | TJR\n//\t 585   | TJS\n//\t 586   | TMM\n//\t 587   | TMT\n//\t 588   | TND\n//\t 589   | TOP\n//\t 590   | TRL\n//\t 591   | TRY\n//\t 592   | TTD\n//\t 593   | TWD\n//\t 594   | TZS\n//\t 595   | UAH\n//\t 596   | UGX\n//\t 597   | USD\n//\t 598   | USN\n//\t 599   | USS\n//\t 600   | UYI\n//\t 601   | UYU\n//\t 602   | UZS\n//\t 603   | VEB\n//\t 604   | VEF\n//\t 605   | VND\n//\t 606   | VUV\n//\t 607   | WST\n//\t 608   | XAF\n//\t 609   | XAG\n//\t 610   | XAU\n//\t 611   | XB5\n//\t 612   | XBA\n//\t 613   | XBB\n//\t 614   | XBC\n//\t 615   | XBD\n//\t 616   | XCD\n//\t 617   | XDR\n//\t 618   | XFO\n//\t 619   | XFU\n//\t 620   | XOF\n//\t 621   | XPD\n//\t 622   | XPF\n//\t 623   | XPT\n//\t 624   | XTS\n//\t 625   | XXX\n//\t 626   | YER\n//\t 627   | YUM\n//\t 628   | ZAR\n//\t 629   | ZMK\n//\t 630   | ZMW\n//\t 631   | ZWD\n//\t 632   | ZWL\n//\t 633   | ZWN\n//\t 634   | ZWR\n//\n// Excelize support set custom number format for cell by CustomNumFmt field. For\n// example, set number as date type in Uruguay (Spanish) format for Sheet1!A6:\n//\n//\tf := excelize.NewFile()\n//\tdefer func() {\n//\t    if err := f.Close(); err != nil {\n//\t        fmt.Println(err)\n//\t    }\n//\t}()\n//\tif err := f.SetCellValue(\"Sheet1\", \"A6\", 42920.5); err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\texp := \"[$-380A]dddd\\\\,\\\\ dd\\\" de \\\"mmmm\\\" de \\\"yyyy;@\"\n//\tstyle, err := f.NewStyle(&excelize.Style{CustomNumFmt: &exp})\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t    return\n//\t}\n//\terr = f.SetCellStyle(\"Sheet1\", \"A6\", \"A6\", style)\n//\n// Cell Sheet1!A6 in the spreadsheet application: martes, 04 de Julio de 2017\n//\n// DecimalPlaces is used to set the decimal places for built-in currency\n// formats, it doesn't work if you have specified the built-in all languages\n// formats or built-in language formats by NumFmt field, or specify the custom\n// number format by CustomNumFmt. When you get style definition by the GetStyle\n// or GetConditionalStyle function, the DecimalPlaces only doesn't nil if a\n// number format code has the same decimal places in the positive part negative\n// part, or only the positive part.\nfunc (f *File) NewStyle(style *Style) (int, error) {\n\tvar (\n\t\tfs                                  *Style\n\t\terr                                 error\n\t\tcellXfsID, fontID, borderID, fillID int\n\t)\n\tif style == nil {\n\t\treturn cellXfsID, err\n\t}\n\tfs, err = parseFormatStyleSet(style)\n\tif err != nil {\n\t\treturn cellXfsID, err\n\t}\n\tif fs.DecimalPlaces != nil && (*fs.DecimalPlaces < 0 || *fs.DecimalPlaces > 30) {\n\t\tfs.DecimalPlaces = intPtr(2)\n\t}\n\tf.mu.Lock()\n\ts, err := f.stylesReader()\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn cellXfsID, err\n\t}\n\tf.mu.Unlock()\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\t// check given style already exist.\n\tif cellXfsID, err = f.getStyleID(s, fs); err != nil || cellXfsID != -1 {\n\t\treturn cellXfsID, err\n\t}\n\n\tnumFmtID := newNumFmt(s, fs)\n\n\tif fs.Font != nil {\n\t\tfontID = f.getFontID(s, fs)\n\t\tif fontID == -1 {\n\t\t\ts.Fonts.Count++\n\t\t\ts.Fonts.Font = append(s.Fonts.Font, fs.Font.newFont())\n\t\t\tfontID = s.Fonts.Count - 1\n\t\t}\n\t}\n\n\tborderID = getBorderID(s, fs)\n\tif borderID == -1 {\n\t\tif len(fs.Border) == 0 {\n\t\t\tborderID = 0\n\t\t} else {\n\t\t\ts.Borders.Count++\n\t\t\ts.Borders.Border = append(s.Borders.Border, newBorders(fs))\n\t\t\tborderID = s.Borders.Count - 1\n\t\t}\n\t}\n\n\tif fillID = getFillID(s, fs); fillID == -1 {\n\t\tif fill := newFills(fs, true); fill != nil {\n\t\t\ts.Fills.Count++\n\t\t\ts.Fills.Fill = append(s.Fills.Fill, fill)\n\t\t\tfillID = s.Fills.Count - 1\n\t\t} else {\n\t\t\tfillID = 0\n\t\t}\n\t}\n\n\tapplyAlignment, alignment := fs.Alignment != nil, newAlignment(fs)\n\tapplyProtection, protection := fs.Protection != nil, newProtection(fs)\n\treturn setCellXfs(s, fontID, numFmtID, fillID, borderID, applyAlignment, applyProtection, alignment, protection)\n}\n\nvar (\n\t// styleBorders list all types of the cell border style.\n\tstyleBorders = []string{\n\t\t\"none\",\n\t\t\"thin\",\n\t\t\"medium\",\n\t\t\"dashed\",\n\t\t\"dotted\",\n\t\t\"thick\",\n\t\t\"double\",\n\t\t\"hair\",\n\t\t\"mediumDashed\",\n\t\t\"dashDot\",\n\t\t\"mediumDashDot\",\n\t\t\"dashDotDot\",\n\t\t\"mediumDashDotDot\",\n\t\t\"slantDashDot\",\n\t}\n\t// styleBorderTypes list all types of the cell border.\n\tstyleBorderTypes = []string{\n\t\t\"left\", \"right\", \"top\", \"bottom\", \"diagonalUp\", \"diagonalDown\",\n\t}\n\t// styleFillPatterns list all types of the cell fill style.\n\tstyleFillPatterns = []string{\n\t\t\"none\",\n\t\t\"solid\",\n\t\t\"mediumGray\",\n\t\t\"darkGray\",\n\t\t\"lightGray\",\n\t\t\"darkHorizontal\",\n\t\t\"darkVertical\",\n\t\t\"darkDown\",\n\t\t\"darkUp\",\n\t\t\"darkGrid\",\n\t\t\"darkTrellis\",\n\t\t\"lightHorizontal\",\n\t\t\"lightVertical\",\n\t\t\"lightDown\",\n\t\t\"lightUp\",\n\t\t\"lightGrid\",\n\t\t\"lightTrellis\",\n\t\t\"gray125\",\n\t\t\"gray0625\",\n\t}\n\t// styleFillVariants list all preset variants of the fill style.\n\tstyleFillVariants = func() []xlsxGradientFill {\n\t\treturn []xlsxGradientFill{\n\t\t\t{Degree: 90, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},\n\t\t\t{Degree: 270, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},\n\t\t\t{Degree: 90, Stop: []*xlsxGradientFillStop{{}, {Position: 0.5}, {Position: 1}}},\n\t\t\t{Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},\n\t\t\t{Degree: 180, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},\n\t\t\t{Stop: []*xlsxGradientFillStop{{}, {Position: 0.5}, {Position: 1}}},\n\t\t\t{Degree: 45, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},\n\t\t\t{Degree: 255, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},\n\t\t\t{Degree: 45, Stop: []*xlsxGradientFillStop{{}, {Position: 0.5}, {Position: 1}}},\n\t\t\t{Degree: 135, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},\n\t\t\t{Degree: 315, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},\n\t\t\t{Degree: 135, Stop: []*xlsxGradientFillStop{{}, {Position: 0.5}, {Position: 1}}},\n\t\t\t{Stop: []*xlsxGradientFillStop{{}, {Position: 1}}, Type: \"path\"},\n\t\t\t{Stop: []*xlsxGradientFillStop{{}, {Position: 1}}, Type: \"path\", Left: 1, Right: 1},\n\t\t\t{Stop: []*xlsxGradientFillStop{{}, {Position: 1}}, Type: \"path\", Bottom: 1, Top: 1},\n\t\t\t{Stop: []*xlsxGradientFillStop{{}, {Position: 1}}, Type: \"path\", Bottom: 1, Left: 1, Right: 1, Top: 1},\n\t\t\t{Stop: []*xlsxGradientFillStop{{}, {Position: 1}}, Type: \"path\", Bottom: 0.5, Left: 0.5, Right: 0.5, Top: 0.5},\n\t\t}\n\t}\n\n\t// getXfIDFuncs provides a function to get xfID by given style.\n\tgetXfIDFuncs = map[string]func(int, xlsxXf, *Style) bool{\n\t\t\"numFmt\": func(numFmtID int, xf xlsxXf, style *Style) bool {\n\t\t\tif style.CustomNumFmt == nil && numFmtID == -1 {\n\t\t\t\treturn xf.NumFmtID != nil && *xf.NumFmtID == 0\n\t\t\t}\n\t\t\tif style.NegRed || (style.DecimalPlaces != nil && *style.DecimalPlaces != 2) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn xf.NumFmtID != nil && *xf.NumFmtID == numFmtID\n\t\t},\n\t\t\"font\": func(fontID int, xf xlsxXf, style *Style) bool {\n\t\t\tif style.Font == nil || fontID == 0 {\n\t\t\t\treturn (xf.FontID == nil || *xf.FontID == 0) && (xf.ApplyFont == nil || !*xf.ApplyFont)\n\t\t\t}\n\t\t\treturn xf.FontID != nil && *xf.FontID == fontID && xf.ApplyFont != nil && *xf.ApplyFont\n\t\t},\n\t\t\"fill\": func(fillID int, xf xlsxXf, style *Style) bool {\n\t\t\tif style.Fill.Type == \"\" || fillID == 0 {\n\t\t\t\treturn (xf.FillID == nil || *xf.FillID == 0) && (xf.ApplyFill == nil || !*xf.ApplyFill)\n\t\t\t}\n\t\t\treturn xf.FillID != nil && *xf.FillID == fillID && xf.ApplyFill != nil && *xf.ApplyFill\n\t\t},\n\t\t\"border\": func(borderID int, xf xlsxXf, style *Style) bool {\n\t\t\tif len(style.Border) == 0 {\n\t\t\t\treturn (xf.BorderID == nil || *xf.BorderID == 0) && (xf.ApplyBorder == nil || !*xf.ApplyBorder)\n\t\t\t}\n\t\t\treturn xf.BorderID != nil && *xf.BorderID == borderID && xf.ApplyBorder != nil && *xf.ApplyBorder\n\t\t},\n\t\t\"alignment\": func(ID int, xf xlsxXf, style *Style) bool {\n\t\t\tif style.Alignment == nil {\n\t\t\t\treturn xf.ApplyAlignment == nil || !*xf.ApplyAlignment\n\t\t\t}\n\t\t\treturn reflect.DeepEqual(xf.Alignment, newAlignment(style))\n\t\t},\n\t\t\"protection\": func(ID int, xf xlsxXf, style *Style) bool {\n\t\t\tif style.Protection == nil {\n\t\t\t\treturn xf.ApplyProtection == nil || !*xf.ApplyProtection\n\t\t\t}\n\t\t\treturn reflect.DeepEqual(xf.Protection, newProtection(style)) && xf.ApplyProtection != nil && *xf.ApplyProtection\n\t\t},\n\t}\n\n\t// extractStyleCondFuncs provides a function set to returns if should be\n\t// extract style definition by given style.\n\textractStyleCondFuncs = map[string]func(xlsxXf, *xlsxStyleSheet) bool{\n\t\t\"fill\": func(xf xlsxXf, s *xlsxStyleSheet) bool {\n\t\t\treturn (xf.ApplyFill == nil || (xf.ApplyFill != nil && *xf.ApplyFill)) &&\n\t\t\t\txf.FillID != nil && s.Fills != nil &&\n\t\t\t\t*xf.FillID < len(s.Fills.Fill)\n\t\t},\n\t\t\"border\": func(xf xlsxXf, s *xlsxStyleSheet) bool {\n\t\t\treturn (xf.ApplyBorder == nil || (xf.ApplyBorder != nil && *xf.ApplyBorder)) &&\n\t\t\t\txf.BorderID != nil && s.Borders != nil &&\n\t\t\t\t*xf.BorderID < len(s.Borders.Border)\n\t\t},\n\t\t\"font\": func(xf xlsxXf, s *xlsxStyleSheet) bool {\n\t\t\treturn (xf.ApplyFont == nil || (xf.ApplyFont != nil && *xf.ApplyFont)) &&\n\t\t\t\txf.FontID != nil && s.Fonts != nil &&\n\t\t\t\t*xf.FontID < len(s.Fonts.Font)\n\t\t},\n\t\t\"alignment\": func(xf xlsxXf, s *xlsxStyleSheet) bool {\n\t\t\treturn xf.ApplyAlignment == nil || (xf.ApplyAlignment != nil && *xf.ApplyAlignment)\n\t\t},\n\t\t\"protection\": func(xf xlsxXf, s *xlsxStyleSheet) bool {\n\t\t\treturn xf.ApplyProtection == nil || (xf.ApplyProtection != nil && *xf.ApplyProtection)\n\t\t},\n\t}\n\n\t// drawContFmtFunc defines functions to create conditional formats.\n\tdrawContFmtFunc = map[string]func(p int, ct, ref, GUID string, fmtCond *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule){\n\t\t\"cellIs\":            drawCondFmtCellIs,\n\t\t\"timePeriod\":        drawCondFmtTimePeriod,\n\t\t\"text\":              drawCondFmtText,\n\t\t\"top10\":             drawCondFmtTop10,\n\t\t\"aboveAverage\":      drawCondFmtAboveAverage,\n\t\t\"duplicateValues\":   drawCondFmtDuplicateUniqueValues,\n\t\t\"uniqueValues\":      drawCondFmtDuplicateUniqueValues,\n\t\t\"containsBlanks\":    drawCondFmtBlanks,\n\t\t\"notContainsBlanks\": drawCondFmtNoBlanks,\n\t\t\"containsErrors\":    drawCondFmtErrors,\n\t\t\"notContainsErrors\": drawCondFmtNoErrors,\n\t\t\"2_color_scale\":     drawCondFmtColorScale,\n\t\t\"3_color_scale\":     drawCondFmtColorScale,\n\t\t\"dataBar\":           drawCondFmtDataBar,\n\t\t\"expression\":        drawCondFmtExp,\n\t\t\"iconSet\":           drawCondFmtIconSet,\n\t}\n\n\t// extractContFmtFunc defines functions to get conditional formats.\n\textractContFmtFunc = map[string]func(*File, string, *xlsxCfRule, *xlsxExtLst) ConditionalFormatOptions{\n\t\t\"cellIs\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtCellIs(c, extLst)\n\t\t},\n\t\t\"timePeriod\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtTimePeriod(c, ref, extLst)\n\t\t},\n\t\t\"containsText\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtText(c, extLst)\n\t\t},\n\t\t\"notContainsText\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtText(c, extLst)\n\t\t},\n\t\t\"beginsWith\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtText(c, extLst)\n\t\t},\n\t\t\"endsWith\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtText(c, extLst)\n\t\t},\n\t\t\"top10\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtTop10(c, extLst)\n\t\t},\n\t\t\"aboveAverage\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtAboveAverage(c, extLst)\n\t\t},\n\t\t\"duplicateValues\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtDuplicateUniqueValues(c, extLst)\n\t\t},\n\t\t\"uniqueValues\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtDuplicateUniqueValues(c, extLst)\n\t\t},\n\t\t\"containsBlanks\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtBlanks(c, extLst)\n\t\t},\n\t\t\"notContainsBlanks\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtNoBlanks(c, extLst)\n\t\t},\n\t\t\"containsErrors\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtErrors(c, extLst)\n\t\t},\n\t\t\"notContainsErrors\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtNoErrors(c, extLst)\n\t\t},\n\t\t\"colorScale\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtColorScale(c, extLst)\n\t\t},\n\t\t\"dataBar\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtDataBar(c, extLst)\n\t\t},\n\t\t\"expression\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtExp(c, extLst)\n\t\t},\n\t\t\"iconSet\": func(f *File, ref string, c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\t\t\treturn f.extractCondFmtIconSet(c, extLst)\n\t\t},\n\t}\n\n\t// validType defined the list of valid validation types.\n\tvalidType = map[string]string{\n\t\t\"cell\":          \"cellIs\",\n\t\t\"average\":       \"aboveAverage\",\n\t\t\"duplicate\":     \"duplicateValues\",\n\t\t\"unique\":        \"uniqueValues\",\n\t\t\"top\":           \"top10\",\n\t\t\"bottom\":        \"top10\",\n\t\t\"text\":          \"text\",\n\t\t\"time_period\":   \"timePeriod\",\n\t\t\"blanks\":        \"containsBlanks\",\n\t\t\"no_blanks\":     \"notContainsBlanks\",\n\t\t\"errors\":        \"containsErrors\",\n\t\t\"no_errors\":     \"notContainsErrors\",\n\t\t\"2_color_scale\": \"2_color_scale\",\n\t\t\"3_color_scale\": \"3_color_scale\",\n\t\t\"data_bar\":      \"dataBar\",\n\t\t\"formula\":       \"expression\",\n\t\t\"icon_set\":      \"iconSet\",\n\t}\n\t// criteriaType defined the list of valid criteria types.\n\tcriteriaType = map[string]string{\n\t\t\"!=\":                       \"notEqual\",\n\t\t\"<\":                        \"lessThan\",\n\t\t\"<=\":                       \"lessThanOrEqual\",\n\t\t\"<>\":                       \"notEqual\",\n\t\t\"=\":                        \"equal\",\n\t\t\"==\":                       \"equal\",\n\t\t\">\":                        \"greaterThan\",\n\t\t\">=\":                       \"greaterThanOrEqual\",\n\t\t\"begins with\":              \"beginsWith\",\n\t\t\"between\":                  \"between\",\n\t\t\"containing\":               \"containsText\",\n\t\t\"continue month\":           \"nextMonth\",\n\t\t\"continue week\":            \"nextWeek\",\n\t\t\"ends with\":                \"endsWith\",\n\t\t\"equal to\":                 \"equal\",\n\t\t\"greater than or equal to\": \"greaterThanOrEqual\",\n\t\t\"greater than\":             \"greaterThan\",\n\t\t\"last 7 days\":              \"last7Days\",\n\t\t\"last month\":               \"lastMonth\",\n\t\t\"last week\":                \"lastWeek\",\n\t\t\"less than or equal to\":    \"lessThanOrEqual\",\n\t\t\"less than\":                \"lessThan\",\n\t\t\"not between\":              \"notBetween\",\n\t\t\"not containing\":           \"notContains\",\n\t\t\"not equal to\":             \"notEqual\",\n\t\t\"this month\":               \"thisMonth\",\n\t\t\"this week\":                \"thisWeek\",\n\t\t\"today\":                    \"today\",\n\t\t\"tomorrow\":                 \"tomorrow\",\n\t\t\"yesterday\":                \"yesterday\",\n\t}\n\t// operatorType defined the list of valid operator types.\n\toperatorType = map[string]string{\n\t\t\"beginsWith\":         \"begins with\",\n\t\t\"between\":            \"between\",\n\t\t\"containsText\":       \"containing\",\n\t\t\"endsWith\":           \"ends with\",\n\t\t\"equal\":              \"equal to\",\n\t\t\"greaterThan\":        \"greater than\",\n\t\t\"greaterThanOrEqual\": \"greater than or equal to\",\n\t\t\"last7Days\":          \"last 7 days\",\n\t\t\"lastMonth\":          \"last month\",\n\t\t\"lastWeek\":           \"last week\",\n\t\t\"lessThan\":           \"less than\",\n\t\t\"lessThanOrEqual\":    \"less than or equal to\",\n\t\t\"nextMonth\":          \"continue month\",\n\t\t\"nextWeek\":           \"continue week\",\n\t\t\"notBetween\":         \"not between\",\n\t\t\"notContains\":        \"not containing\",\n\t\t\"notEqual\":           \"not equal to\",\n\t\t\"thisMonth\":          \"this month\",\n\t\t\"thisWeek\":           \"this week\",\n\t\t\"today\":              \"today\",\n\t\t\"tomorrow\":           \"tomorrow\",\n\t\t\"yesterday\":          \"yesterday\",\n\t}\n\t// cellIsCriteriaType defined the list of valid criteria types used for\n\t// cellIs conditional formats.\n\tcellIsCriteriaType = []string{\n\t\t\"equal\",\n\t\t\"notEqual\",\n\t\t\"greaterThan\",\n\t\t\"lessThan\",\n\t\t\"greaterThanOrEqual\",\n\t\t\"lessThanOrEqual\",\n\t\t\"containsText\",\n\t\t\"notContains\",\n\t\t\"beginsWith\",\n\t\t\"endsWith\",\n\t}\n\t// cfvo3 defined the icon set conditional formatting rules.\n\tcfvo3 = &xlsxCfRule{IconSet: &xlsxIconSet{Cfvo: []*xlsxCfvo{\n\t\t{Type: \"percent\", Val: \"0\"},\n\t\t{Type: \"percent\", Val: \"33\"},\n\t\t{Type: \"percent\", Val: \"67\"},\n\t}}}\n\t// cfvo4 defined the icon set conditional formatting rules.\n\tcfvo4 = &xlsxCfRule{IconSet: &xlsxIconSet{Cfvo: []*xlsxCfvo{\n\t\t{Type: \"percent\", Val: \"0\"},\n\t\t{Type: \"percent\", Val: \"25\"},\n\t\t{Type: \"percent\", Val: \"50\"},\n\t\t{Type: \"percent\", Val: \"75\"},\n\t}}}\n\t// cfvo5 defined the icon set conditional formatting rules.\n\tcfvo5 = &xlsxCfRule{IconSet: &xlsxIconSet{Cfvo: []*xlsxCfvo{\n\t\t{Type: \"percent\", Val: \"0\"},\n\t\t{Type: \"percent\", Val: \"20\"},\n\t\t{Type: \"percent\", Val: \"40\"},\n\t\t{Type: \"percent\", Val: \"60\"},\n\t\t{Type: \"percent\", Val: \"80\"},\n\t}}}\n\t// condFmtIconSetPresets defined the list of icon set conditional formatting\n\t// rules.\n\tcondFmtIconSetPresets = map[string]*xlsxCfRule{\n\t\t\"3Arrows\":         cfvo3,\n\t\t\"3ArrowsGray\":     cfvo3,\n\t\t\"3Flags\":          cfvo3,\n\t\t\"3Signs\":          cfvo3,\n\t\t\"3Symbols\":        cfvo3,\n\t\t\"3Symbols2\":       cfvo3,\n\t\t\"3TrafficLights1\": cfvo3,\n\t\t\"3TrafficLights2\": cfvo3,\n\t\t\"4Arrows\":         cfvo4,\n\t\t\"4ArrowsGray\":     cfvo4,\n\t\t\"4Rating\":         cfvo4,\n\t\t\"4RedToBlack\":     cfvo4,\n\t\t\"4TrafficLights\":  cfvo4,\n\t\t\"5Arrows\":         cfvo5,\n\t\t\"5ArrowsGray\":     cfvo5,\n\t\t\"5Quarters\":       cfvo5,\n\t\t\"5Rating\":         cfvo5,\n\t}\n\t// cfvo3 defined the icon set conditional formatting rules.\n\tx14cfvo3 = &xlsxX14CfRule{IconSet: &xlsx14IconSet{Cfvo: []*xlsx14Cfvo{\n\t\t{Type: \"percent\", F: \"0\"},\n\t\t{Type: \"percent\", F: \"33\"},\n\t\t{Type: \"percent\", F: \"67\"},\n\t}}}\n\t// cfvo5 defined the icon set conditional formatting rules.\n\tx14cfvo5 = &xlsxX14CfRule{IconSet: &xlsx14IconSet{Cfvo: []*xlsx14Cfvo{\n\t\t{Type: \"percent\", F: \"0\"},\n\t\t{Type: \"percent\", F: \"20\"},\n\t\t{Type: \"percent\", F: \"40\"},\n\t\t{Type: \"percent\", F: \"60\"},\n\t\t{Type: \"percent\", F: \"80\"},\n\t}}}\n\t// condFmtX14IconSetPresets defined the list of icon set conditional\n\t// formatting rules which defined in worksheet extension list.\n\tcondFmtX14IconSetPresets = map[string]*xlsxX14CfRule{\n\t\t\"3Stars\":     x14cfvo3,\n\t\t\"3Triangles\": x14cfvo3,\n\t\t\"5Boxes\":     x14cfvo5,\n\t}\n)\n\n// colorChoice returns a hex color code from the actual color values.\nfunc (clr *decodeCTColor) colorChoice() *string {\n\tif clr.SrgbClr != nil {\n\t\treturn clr.SrgbClr.Val\n\t}\n\tif clr.SysClr != nil {\n\t\treturn &clr.SysClr.LastClr\n\t}\n\treturn nil\n}\n\n// GetBaseColor returns the preferred hex color code by giving hex color code,\n// indexed color, and theme color.\nfunc (f *File) GetBaseColor(hexColor string, indexedColor int, themeColor *int) string {\n\tif f.Theme != nil && themeColor != nil {\n\t\tclrScheme := f.Theme.ThemeElements.ClrScheme\n\t\tif val, ok := map[int]*string{\n\t\t\t0: clrScheme.Lt1.colorChoice(),\n\t\t\t1: clrScheme.Dk1.colorChoice(),\n\t\t\t2: clrScheme.Lt2.colorChoice(),\n\t\t\t3: clrScheme.Dk2.colorChoice(),\n\t\t\t4: clrScheme.Accent1.colorChoice(),\n\t\t\t5: clrScheme.Accent2.colorChoice(),\n\t\t\t6: clrScheme.Accent3.colorChoice(),\n\t\t\t7: clrScheme.Accent4.colorChoice(),\n\t\t\t8: clrScheme.Accent5.colorChoice(),\n\t\t\t9: clrScheme.Accent6.colorChoice(),\n\t\t}[*themeColor]; ok && val != nil {\n\t\t\treturn *val\n\t\t}\n\t}\n\tif len(hexColor) == 6 {\n\t\treturn hexColor\n\t}\n\tif len(hexColor) == 8 {\n\t\treturn strings.TrimPrefix(hexColor, \"FF\")\n\t}\n\tif f.Styles != nil && f.Styles.Colors != nil && f.Styles.Colors.IndexedColors != nil &&\n\t\tindexedColor < len(f.Styles.Colors.IndexedColors.RgbColor) {\n\t\treturn strings.TrimPrefix(f.Styles.Colors.IndexedColors.RgbColor[indexedColor].RGB, \"FF\")\n\t}\n\tif indexedColor < len(IndexedColorMapping) {\n\t\treturn IndexedColorMapping[indexedColor]\n\t}\n\treturn hexColor\n}\n\n// getThemeColor provides a function to convert theme color or index color to\n// RGB color.\nfunc (f *File) getThemeColor(clr *xlsxColor) string {\n\tvar RGB string\n\tif clr == nil || f.Theme == nil {\n\t\treturn RGB\n\t}\n\tif RGB = f.GetBaseColor(clr.RGB, clr.Indexed, clr.Theme); RGB != \"\" {\n\t\tRGB = strings.TrimPrefix(ThemeColor(RGB, clr.Tint), \"FF\")\n\t}\n\treturn RGB\n}\n\n// extractBorders provides a function to extract borders styles settings by\n// given border styles definition.\nfunc (f *File) extractBorders(bdr *xlsxBorder, s *xlsxStyleSheet, style *Style) {\n\tif bdr != nil {\n\t\tvar borders []Border\n\t\textractBorder := func(lineType string, line *xlsxLine) {\n\t\t\tif line != nil && line.Style != \"\" {\n\t\t\t\tborders = append(borders, Border{\n\t\t\t\t\tType:  lineType,\n\t\t\t\t\tColor: f.getThemeColor(line.Color),\n\t\t\t\t\tStyle: inStrSlice(styleBorders, line.Style, false),\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tfor i, line := range []*xlsxLine{\n\t\t\tbdr.Left, bdr.Right, bdr.Top, bdr.Bottom, bdr.Diagonal, bdr.Diagonal,\n\t\t} {\n\t\t\tif i < 4 {\n\t\t\t\textractBorder(styleBorderTypes[i], line)\n\t\t\t}\n\t\t\tif i == 4 && bdr.DiagonalUp {\n\t\t\t\textractBorder(styleBorderTypes[i], line)\n\t\t\t}\n\t\t\tif i == 5 && bdr.DiagonalDown {\n\t\t\t\textractBorder(styleBorderTypes[i], line)\n\t\t\t}\n\t\t}\n\t\tstyle.Border = borders\n\t}\n}\n\n// extractFills provides a function to extract fill styles settings by\n// given fill styles definition.\nfunc (f *File) extractFills(fl *xlsxFill, s *xlsxStyleSheet, style *Style) {\n\tif fl != nil {\n\t\tvar fill Fill\n\t\tif fl.GradientFill != nil {\n\t\t\tfill.Type = \"gradient\"\n\t\t\tfor shading, variants := range styleFillVariants() {\n\t\t\t\tif fl.GradientFill.Bottom == variants.Bottom &&\n\t\t\t\t\tfl.GradientFill.Degree == variants.Degree &&\n\t\t\t\t\tfl.GradientFill.Left == variants.Left &&\n\t\t\t\t\tfl.GradientFill.Right == variants.Right &&\n\t\t\t\t\tfl.GradientFill.Top == variants.Top &&\n\t\t\t\t\tfl.GradientFill.Type == variants.Type {\n\t\t\t\t\tfill.Shading = shading\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor _, stop := range fl.GradientFill.Stop {\n\t\t\t\tfill.Color = append(fill.Color, f.getThemeColor(&stop.Color))\n\t\t\t}\n\t\t}\n\t\tif fl.PatternFill != nil {\n\t\t\tfill.Type = \"pattern\"\n\t\t\tfill.Pattern = inStrSlice(styleFillPatterns, fl.PatternFill.PatternType, false)\n\t\t\tif fl.PatternFill.BgColor != nil {\n\t\t\t\tfill.Color = []string{f.getThemeColor(fl.PatternFill.BgColor)}\n\t\t\t}\n\t\t\tif fl.PatternFill.FgColor != nil {\n\t\t\t\tfill.Color = []string{f.getThemeColor(fl.PatternFill.FgColor)}\n\t\t\t}\n\t\t}\n\t\tstyle.Fill = fill\n\t}\n}\n\n// extractFont provides a function to extract font styles settings by given\n// font styles definition.\nfunc extractFont(fnt *xlsxFont) *Font {\n\tif fnt == nil {\n\t\treturn nil\n\t}\n\tvar font Font\n\tif fnt.Charset != nil {\n\t\tfont.Charset = fnt.Charset.Val\n\t}\n\tfont.Bold = fnt.B.Value()\n\tfont.Italic = fnt.I.Value()\n\tif fnt.U != nil {\n\t\tif font.Underline = fnt.U.Value(); font.Underline == \"\" {\n\t\t\tfont.Underline = \"single\"\n\t\t}\n\t}\n\tfont.Family = fnt.Name.Value()\n\tfont.Size = fnt.Sz.Value()\n\tfont.Strike = fnt.Strike.Value()\n\tif fnt.Color != nil {\n\t\tfont.Color = strings.TrimPrefix(fnt.Color.RGB, \"FF\")\n\t\tfont.ColorIndexed = fnt.Color.Indexed\n\t\tfont.ColorTheme = fnt.Color.Theme\n\t\tfont.ColorTint = fnt.Color.Tint\n\t}\n\tfont.VertAlign = fnt.VertAlign.Value()\n\treturn &font\n}\n\n// extractNumFmt provides a function to extract number format by given styles\n// definition.\nfunc (f *File) extractNumFmt(n *int, s *xlsxStyleSheet, style *Style) {\n\tif n != nil {\n\t\tnumFmtID := *n\n\t\tif builtInFmtCode, ok := builtInNumFmt[numFmtID]; ok || isLangNumFmt(numFmtID) {\n\t\t\tstyle.NumFmt = numFmtID\n\t\t\tif decimalPlaces := f.extractNumFmtDecimal(builtInFmtCode); decimalPlaces != -1 {\n\t\t\t\tstyle.DecimalPlaces = &decimalPlaces\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif s.NumFmts != nil {\n\t\t\tfor _, numFmt := range s.NumFmts.NumFmt {\n\t\t\t\tif numFmt.NumFmtID != numFmtID {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif decimalPlaces := f.extractNumFmtDecimal(numFmt.FormatCode); decimalPlaces != -1 {\n\t\t\t\t\tstyle.DecimalPlaces = &decimalPlaces\n\t\t\t\t}\n\t\t\t\tstyle.CustomNumFmt = &numFmt.FormatCode\n\t\t\t\tif strings.Contains(numFmt.FormatCode, \";[Red]\") {\n\t\t\t\t\tstyle.NegRed = true\n\t\t\t\t}\n\t\t\t\tfor numFmtID, fmtCode := range currencyNumFmt {\n\t\t\t\t\tif style.NegRed {\n\t\t\t\t\t\tfmtCode += \";[Red]\" + fmtCode\n\t\t\t\t\t}\n\t\t\t\t\tif numFmt.FormatCode == fmtCode {\n\t\t\t\t\t\tstyle.NumFmt = numFmtID\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// extractAlignment provides a function to extract alignment format by\n// given style definition.\nfunc (f *File) extractAlignment(a *xlsxAlignment, s *xlsxStyleSheet, style *Style) {\n\tif a != nil {\n\t\tstyle.Alignment = &Alignment{\n\t\t\tHorizontal:      a.Horizontal,\n\t\t\tIndent:          a.Indent,\n\t\t\tJustifyLastLine: a.JustifyLastLine,\n\t\t\tReadingOrder:    a.ReadingOrder,\n\t\t\tRelativeIndent:  a.RelativeIndent,\n\t\t\tShrinkToFit:     a.ShrinkToFit,\n\t\t\tTextRotation:    a.TextRotation,\n\t\t\tVertical:        a.Vertical,\n\t\t\tWrapText:        a.WrapText,\n\t\t}\n\t}\n}\n\n// extractProtection provides a function to extract protection settings by\n// given format definition.\nfunc (f *File) extractProtection(p *xlsxProtection, s *xlsxStyleSheet, style *Style) {\n\tif p != nil {\n\t\tstyle.Protection = &Protection{}\n\t\tif p.Hidden != nil {\n\t\t\tstyle.Protection.Hidden = *p.Hidden\n\t\t}\n\t\tif p.Locked != nil {\n\t\t\tstyle.Protection.Locked = *p.Locked\n\t\t}\n\t}\n}\n\n// GetStyle provides a function to get style definition by given style index.\nfunc (f *File) GetStyle(idx int) (*Style, error) {\n\tvar style *Style\n\tf.mu.Lock()\n\ts, err := f.stylesReader()\n\tif err != nil {\n\t\treturn style, err\n\t}\n\tf.mu.Unlock()\n\tif idx < 0 || s.CellXfs == nil || len(s.CellXfs.Xf) <= idx {\n\t\treturn style, newInvalidStyleID(idx)\n\t}\n\tstyle = &Style{}\n\txf := s.CellXfs.Xf[idx]\n\tif extractStyleCondFuncs[\"fill\"](xf, s) {\n\t\tf.extractFills(s.Fills.Fill[*xf.FillID], s, style)\n\t}\n\tif extractStyleCondFuncs[\"border\"](xf, s) {\n\t\tf.extractBorders(s.Borders.Border[*xf.BorderID], s, style)\n\t}\n\tif extractStyleCondFuncs[\"font\"](xf, s) {\n\t\tstyle.Font = extractFont(s.Fonts.Font[*xf.FontID])\n\t}\n\tif extractStyleCondFuncs[\"alignment\"](xf, s) {\n\t\tf.extractAlignment(xf.Alignment, s, style)\n\t}\n\tif extractStyleCondFuncs[\"protection\"](xf, s) {\n\t\tf.extractProtection(xf.Protection, s, style)\n\t}\n\tf.extractNumFmt(xf.NumFmtID, s, style)\n\treturn style, nil\n}\n\n// getStyleID provides a function to get styleID by given style. If given\n// style does not exist, will return -1.\nfunc (f *File) getStyleID(ss *xlsxStyleSheet, style *Style) (int, error) {\n\tvar (\n\t\terr     error\n\t\tstyleID = -1\n\t)\n\tif ss.CellXfs == nil {\n\t\treturn styleID, err\n\t}\n\tnumFmtID, borderID, fillID := getNumFmtID(ss, style), getBorderID(ss, style), getFillID(ss, style)\n\tfontID := f.getFontID(ss, style)\n\tif style.CustomNumFmt != nil {\n\t\tnumFmtID = getCustomNumFmtID(ss, style)\n\t}\n\tfor xfID, xf := range ss.CellXfs.Xf {\n\t\tif getXfIDFuncs[\"numFmt\"](numFmtID, xf, style) &&\n\t\t\tgetXfIDFuncs[\"font\"](fontID, xf, style) &&\n\t\t\tgetXfIDFuncs[\"fill\"](fillID, xf, style) &&\n\t\t\tgetXfIDFuncs[\"border\"](borderID, xf, style) &&\n\t\t\tgetXfIDFuncs[\"alignment\"](0, xf, style) &&\n\t\t\tgetXfIDFuncs[\"protection\"](0, xf, style) {\n\t\t\tstyleID = xfID\n\t\t\treturn styleID, err\n\t\t}\n\t}\n\treturn styleID, err\n}\n\n// NewConditionalStyle provides a function to create style for conditional\n// format by given style format. The parameters are the same with the NewStyle\n// function.\nfunc (f *File) NewConditionalStyle(style *Style) (int, error) {\n\tf.mu.Lock()\n\ts, err := f.stylesReader()\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn 0, err\n\t}\n\tf.mu.Unlock()\n\tfs, err := parseFormatStyleSet(style)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif fs.DecimalPlaces != nil && (*fs.DecimalPlaces < 0 || *fs.DecimalPlaces > 30) {\n\t\tfs.DecimalPlaces = intPtr(2)\n\t}\n\tdxf := xlsxDxf{\n\t\tFill: newFills(fs, false),\n\t}\n\tif fs.Alignment != nil {\n\t\tdxf.Alignment = newAlignment(fs)\n\t}\n\tif len(fs.Border) > 0 {\n\t\tdxf.Border = newBorders(fs)\n\t}\n\tif fs.Font != nil {\n\t\tdxf.Font = fs.Font.newFont()\n\t}\n\tif fs.Protection != nil {\n\t\tdxf.Protection = newProtection(fs)\n\t}\n\tdxf.NumFmt = newDxfNumFmt(s, style, &dxf)\n\tif s.Dxfs == nil {\n\t\ts.Dxfs = &xlsxDxfs{}\n\t}\n\ts.Dxfs.Count++\n\ts.Dxfs.Dxfs = append(s.Dxfs.Dxfs, &dxf)\n\treturn s.Dxfs.Count - 1, nil\n}\n\n// GetConditionalStyle returns conditional format style definition by specified\n// style index.\nfunc (f *File) GetConditionalStyle(idx int) (*Style, error) {\n\tvar style *Style\n\tf.mu.Lock()\n\ts, err := f.stylesReader()\n\tif err != nil {\n\t\treturn style, err\n\t}\n\tf.mu.Unlock()\n\tif idx < 0 || s.Dxfs == nil || len(s.Dxfs.Dxfs) <= idx {\n\t\treturn style, newInvalidStyleID(idx)\n\t}\n\tstyle = &Style{}\n\txf := s.Dxfs.Dxfs[idx]\n\t// The default pattern fill type of conditional format style is solid\n\tif xf.Fill != nil && xf.Fill.PatternFill != nil && xf.Fill.PatternFill.PatternType == \"\" {\n\t\txf.Fill.PatternFill.PatternType = \"solid\"\n\t}\n\tf.extractFills(xf.Fill, s, style)\n\tf.extractBorders(xf.Border, s, style)\n\tstyle.Font = extractFont(xf.Font)\n\tf.extractAlignment(xf.Alignment, s, style)\n\tf.extractProtection(xf.Protection, s, style)\n\tif xf.NumFmt != nil {\n\t\tf.extractNumFmt(&xf.NumFmt.NumFmtID, s, style)\n\t}\n\treturn style, nil\n}\n\n// newDxfNumFmt provides a function to create number format for conditional\n// format styles.\nfunc newDxfNumFmt(styleSheet *xlsxStyleSheet, style *Style, dxf *xlsxDxf) *xlsxNumFmt {\n\tdp, numFmtID := \"0\", 164 // Default custom number format code from 164.\n\tif style.DecimalPlaces != nil && *style.DecimalPlaces > 0 {\n\t\tdp += \".\"\n\t\tfor i := 0; i < *style.DecimalPlaces; i++ {\n\t\t\tdp += \"0\"\n\t\t}\n\t}\n\tif style.CustomNumFmt != nil {\n\t\tif styleSheet.Dxfs != nil {\n\t\t\tfor _, d := range styleSheet.Dxfs.Dxfs {\n\t\t\t\tif d != nil && d.NumFmt != nil && d.NumFmt.NumFmtID > numFmtID {\n\t\t\t\t\tnumFmtID = d.NumFmt.NumFmtID\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn &xlsxNumFmt{NumFmtID: numFmtID + 1, FormatCode: *style.CustomNumFmt}\n\t}\n\tnumFmtCode, ok := builtInNumFmt[style.NumFmt]\n\tif style.NumFmt > 0 && ok {\n\t\treturn &xlsxNumFmt{NumFmtID: style.NumFmt, FormatCode: numFmtCode}\n\t}\n\tfc, currency := currencyNumFmt[style.NumFmt]\n\tif !currency {\n\t\treturn nil\n\t}\n\tif style.DecimalPlaces != nil {\n\t\tfc = strings.ReplaceAll(fc, \"0.00\", dp)\n\t}\n\tif style.NegRed {\n\t\tfc = fc + \";[Red]\" + fc\n\t}\n\treturn &xlsxNumFmt{NumFmtID: numFmtID, FormatCode: fc}\n}\n\n// GetDefaultFont provides the default font name currently set in the\n// workbook. The spreadsheet generated by excelize default font is Calibri.\nfunc (f *File) GetDefaultFont() (string, error) {\n\tfont, err := f.readDefaultFont()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn *font.Name.Val, err\n}\n\n// SetDefaultFont changes the default font in the workbook.\nfunc (f *File) SetDefaultFont(fontName string) error {\n\tfont, err := f.readDefaultFont()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfont.Name.Val = stringPtr(fontName)\n\tf.mu.Lock()\n\ts, _ := f.stylesReader()\n\tf.mu.Unlock()\n\ts.Fonts.Font[0] = font\n\tcustom := true\n\ts.CellStyles.CellStyle[0].CustomBuiltIn = &custom\n\treturn err\n}\n\n// readDefaultFont provides an un-marshalled font value.\nfunc (f *File) readDefaultFont() (*xlsxFont, error) {\n\tf.mu.Lock()\n\tdefer f.mu.Unlock()\n\ts, err := f.stylesReader()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn s.Fonts.Font[0], err\n}\n\n// getFontID provides a function to get font ID.\n// If given font does not exist, will return -1.\nfunc (f *File) getFontID(styleSheet *xlsxStyleSheet, style *Style) int {\n\tfontID := -1\n\tif styleSheet.Fonts == nil || style.Font == nil {\n\t\treturn fontID\n\t}\n\tfor idx, fnt := range styleSheet.Fonts.Font {\n\t\tif reflect.DeepEqual(*fnt, *style.Font.newFont()) {\n\t\t\tfontID = idx\n\t\t\treturn fontID\n\t\t}\n\t}\n\treturn fontID\n}\n\n// newFontColor set font color by given styles.\nfunc newFontColor(font *Font) *xlsxColor {\n\tvar fontColor *xlsxColor\n\tprepareFontColor := func() {\n\t\tif fontColor != nil {\n\t\t\treturn\n\t\t}\n\t\tfontColor = &xlsxColor{}\n\t}\n\tif font.Color != \"\" {\n\t\tprepareFontColor()\n\t\tfontColor.RGB = getPaletteColor(font.Color)\n\t}\n\tif font.ColorIndexed >= 0 && font.ColorIndexed <= len(IndexedColorMapping)+1 {\n\t\tprepareFontColor()\n\t\tfontColor.Indexed = font.ColorIndexed\n\t}\n\tif font.ColorTheme != nil {\n\t\tprepareFontColor()\n\t\tfontColor.Theme = font.ColorTheme\n\t}\n\tif font.ColorTint != 0 {\n\t\tprepareFontColor()\n\t\tfontColor.Tint = font.ColorTint\n\t}\n\treturn fontColor\n}\n\n// newFont provides a function to add font style by given cell format\n// settings.\nfunc (fnt *Font) newFont() *xlsxFont {\n\tfont := xlsxFont{Family: &attrValInt{Val: intPtr(2)}}\n\tif fnt.Size >= MinFontSize {\n\t\tfont.Sz = &attrValFloat{Val: float64Ptr(fnt.Size)}\n\t}\n\tif fnt.Family != \"\" {\n\t\tfont.Name = &attrValString{Val: stringPtr(fnt.Family)}\n\t}\n\tif fnt.Charset != nil {\n\t\tfont.Charset = &attrValInt{Val: fnt.Charset}\n\t}\n\tfont.Color = newFontColor(fnt)\n\tif fnt.Bold {\n\t\tfont.B = &attrValBool{Val: &fnt.Bold}\n\t}\n\tif fnt.Italic {\n\t\tfont.I = &attrValBool{Val: &fnt.Italic}\n\t}\n\tif fnt.Strike {\n\t\tfont.Strike = &attrValBool{Val: &fnt.Strike}\n\t}\n\tif idx := inStrSlice(supportedUnderlineTypes, fnt.Underline, true); idx != -1 {\n\t\tfont.U = &attrValString{Val: stringPtr(supportedUnderlineTypes[idx])}\n\t}\n\tif inStrSlice(supportedVertAlignTypes, fnt.VertAlign, true) != -1 {\n\t\tfont.VertAlign = &attrValString{Val: &fnt.VertAlign}\n\t}\n\treturn &font\n}\n\n// getNumFmtID provides a function to get number format code ID.\n// If given number format code does not exist, will return -1.\nfunc getNumFmtID(styleSheet *xlsxStyleSheet, style *Style) int {\n\tnumFmtID := -1\n\tif _, ok := builtInNumFmt[style.NumFmt]; ok {\n\t\treturn style.NumFmt\n\t}\n\tif (27 <= style.NumFmt && style.NumFmt <= 36) || (50 <= style.NumFmt && style.NumFmt <= 81) {\n\t\treturn style.NumFmt\n\t}\n\tif fmtCode, ok := currencyNumFmt[style.NumFmt]; ok {\n\t\tnumFmtID = style.NumFmt\n\t\tif styleSheet.NumFmts != nil {\n\t\t\tfor _, numFmt := range styleSheet.NumFmts.NumFmt {\n\t\t\t\tif numFmt.FormatCode == fmtCode {\n\t\t\t\t\treturn numFmt.NumFmtID\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn numFmtID\n}\n\n// newNumFmt provides a function to check if number format code in the range\n// of built-in values.\nfunc newNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {\n\tdp, numFmtID := \"0\", 164 // Default custom number format code from 164.\n\tif style.DecimalPlaces != nil && *style.DecimalPlaces > 0 {\n\t\tdp += \".\"\n\t\tfor i := 0; i < *style.DecimalPlaces; i++ {\n\t\t\tdp += \"0\"\n\t\t}\n\t}\n\tif style.CustomNumFmt != nil {\n\t\tif customNumFmtID := getCustomNumFmtID(styleSheet, style); customNumFmtID != -1 {\n\t\t\treturn customNumFmtID\n\t\t}\n\t\treturn setCustomNumFmt(styleSheet, style)\n\t}\n\tif _, ok := builtInNumFmt[style.NumFmt]; !ok {\n\t\tfc, currency := currencyNumFmt[style.NumFmt]\n\t\tif !currency {\n\t\t\treturn setLangNumFmt(style)\n\t\t}\n\t\tif style.DecimalPlaces != nil {\n\t\t\tfc = strings.ReplaceAll(fc, \"0.00\", dp)\n\t\t}\n\t\tif style.NegRed {\n\t\t\tfc = fc + \";[Red]\" + fc\n\t\t}\n\t\tif styleSheet.NumFmts == nil {\n\t\t\tstyleSheet.NumFmts = &xlsxNumFmts{NumFmt: []*xlsxNumFmt{}}\n\t\t} else {\n\t\t\tnumFmtID = styleSheet.NumFmts.NumFmt[len(styleSheet.NumFmts.NumFmt)-1].NumFmtID + 1\n\t\t}\n\t\tstyleSheet.NumFmts.NumFmt = append(styleSheet.NumFmts.NumFmt, &xlsxNumFmt{\n\t\t\tFormatCode: fc, NumFmtID: numFmtID,\n\t\t})\n\t\tstyleSheet.NumFmts.Count++\n\t\treturn numFmtID\n\t}\n\treturn style.NumFmt\n}\n\n// setCustomNumFmt provides a function to set custom number format code.\nfunc setCustomNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {\n\tnf := xlsxNumFmt{NumFmtID: 163, FormatCode: *style.CustomNumFmt}\n\tif styleSheet.NumFmts == nil {\n\t\tstyleSheet.NumFmts = &xlsxNumFmts{}\n\t}\n\tfor _, numFmt := range styleSheet.NumFmts.NumFmt {\n\t\tif numFmt != nil && nf.NumFmtID < numFmt.NumFmtID {\n\t\t\tnf.NumFmtID = numFmt.NumFmtID\n\t\t}\n\t}\n\tnf.NumFmtID++\n\tstyleSheet.NumFmts.NumFmt = append(styleSheet.NumFmts.NumFmt, &nf)\n\tstyleSheet.NumFmts.Count = len(styleSheet.NumFmts.NumFmt)\n\treturn nf.NumFmtID\n}\n\n// getCustomNumFmtID provides a function to get custom number format code ID.\n// If given custom number format code does not exist, will return -1.\nfunc getCustomNumFmtID(styleSheet *xlsxStyleSheet, style *Style) (customNumFmtID int) {\n\tcustomNumFmtID = -1\n\tif styleSheet.NumFmts == nil {\n\t\treturn\n\t}\n\tfor _, numFmt := range styleSheet.NumFmts.NumFmt {\n\t\tif style.CustomNumFmt != nil && numFmt.FormatCode == *style.CustomNumFmt {\n\t\t\tcustomNumFmtID = numFmt.NumFmtID\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\n// isLangNumFmt provides a function to returns if a given number format ID is a\n// built-in language glyphs number format code.\nfunc isLangNumFmt(ID int) bool {\n\treturn (27 <= ID && ID <= 36) || (50 <= ID && ID <= 62) || (67 <= ID && ID <= 81)\n}\n\n// setLangNumFmt provides a function to set number format code with language.\nfunc setLangNumFmt(style *Style) int {\n\tif isLangNumFmt(style.NumFmt) {\n\t\treturn style.NumFmt\n\t}\n\treturn 0\n}\n\n// getFillID provides a function to get fill ID. If given fill is not\n// exist, will return -1.\nfunc getFillID(styleSheet *xlsxStyleSheet, style *Style) (fillID int) {\n\tfillID = -1\n\tif styleSheet.Fills == nil || style.Fill.Type == \"\" {\n\t\treturn\n\t}\n\tfills := newFills(style, true)\n\tif fills == nil {\n\t\treturn\n\t}\n\tfor idx, fill := range styleSheet.Fills.Fill {\n\t\tif reflect.DeepEqual(fill, fills) {\n\t\t\tfillID = idx\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\n// newFills provides a function to add fill elements in the styles.xml by\n// given cell format settings.\nfunc newFills(style *Style, fg bool) *xlsxFill {\n\tvar fill xlsxFill\n\tswitch style.Fill.Type {\n\tcase \"gradient\":\n\t\tgradient := styleFillVariants()[style.Fill.Shading]\n\t\tgradient.Stop[0].Color.RGB = getPaletteColor(style.Fill.Color[0])\n\t\tgradient.Stop[1].Color.RGB = getPaletteColor(style.Fill.Color[1])\n\t\tif len(gradient.Stop) == 3 {\n\t\t\tgradient.Stop[2].Color.RGB = getPaletteColor(style.Fill.Color[0])\n\t\t}\n\t\tfill.GradientFill = &gradient\n\tcase \"pattern\":\n\t\tvar pattern xlsxPatternFill\n\t\tpattern.PatternType = styleFillPatterns[style.Fill.Pattern]\n\t\tif len(style.Fill.Color) < 1 {\n\t\t\tfill.PatternFill = &pattern\n\t\t\tbreak\n\t\t}\n\t\tif fg {\n\t\t\tif pattern.FgColor == nil {\n\t\t\t\tpattern.FgColor = new(xlsxColor)\n\t\t\t}\n\t\t\tpattern.FgColor.RGB = getPaletteColor(style.Fill.Color[0])\n\t\t} else {\n\t\t\tif pattern.BgColor == nil {\n\t\t\t\tpattern.BgColor = new(xlsxColor)\n\t\t\t}\n\t\t\tpattern.BgColor.RGB = getPaletteColor(style.Fill.Color[0])\n\t\t}\n\t\tfill.PatternFill = &pattern\n\tdefault:\n\t\treturn nil\n\t}\n\treturn &fill\n}\n\n// newAlignment provides a function to formatting information pertaining to\n// text alignment in cells. There are a variety of choices for how text is\n// aligned both horizontally and vertically, as well as indentation settings,\n// and so on.\nfunc newAlignment(style *Style) *xlsxAlignment {\n\tvar alignment xlsxAlignment\n\tif style.Alignment != nil {\n\t\talignment.Horizontal = style.Alignment.Horizontal\n\t\talignment.Indent = style.Alignment.Indent\n\t\talignment.JustifyLastLine = style.Alignment.JustifyLastLine\n\t\talignment.ReadingOrder = style.Alignment.ReadingOrder\n\t\talignment.RelativeIndent = style.Alignment.RelativeIndent\n\t\talignment.ShrinkToFit = style.Alignment.ShrinkToFit\n\t\talignment.TextRotation = style.Alignment.TextRotation\n\t\talignment.Vertical = style.Alignment.Vertical\n\t\talignment.WrapText = style.Alignment.WrapText\n\t}\n\treturn &alignment\n}\n\n// newProtection provides a function to set protection properties associated\n// with the cell.\nfunc newProtection(style *Style) *xlsxProtection {\n\tvar protection xlsxProtection\n\tif style.Protection != nil {\n\t\tprotection.Hidden = &style.Protection.Hidden\n\t\tprotection.Locked = &style.Protection.Locked\n\t}\n\treturn &protection\n}\n\n// getBorderID provides a function to get border ID. If given border is not\n// exist, will return -1.\nfunc getBorderID(styleSheet *xlsxStyleSheet, style *Style) (borderID int) {\n\tborderID = -1\n\tif styleSheet.Borders == nil || len(style.Border) == 0 {\n\t\treturn\n\t}\n\tfor idx, border := range styleSheet.Borders.Border {\n\t\tif reflect.DeepEqual(*border, *newBorders(style)) {\n\t\t\tborderID = idx\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\n// newBorders provides a function to add border elements in the styles.xml by\n// given borders format settings.\nfunc newBorders(style *Style) *xlsxBorder {\n\tvar border xlsxBorder\n\tfor _, v := range style.Border {\n\t\tif 0 <= v.Style && v.Style < 14 {\n\t\t\tline := &xlsxLine{Style: styleBorders[v.Style], Color: &xlsxColor{RGB: getPaletteColor(v.Color)}}\n\t\t\tswitch v.Type {\n\t\t\tcase \"left\":\n\t\t\t\tborder.Left = line\n\t\t\tcase \"right\":\n\t\t\t\tborder.Right = line\n\t\t\tcase \"top\":\n\t\t\t\tborder.Top = line\n\t\t\tcase \"bottom\":\n\t\t\t\tborder.Bottom = line\n\t\t\tcase \"diagonalUp\":\n\t\t\t\tborder.Diagonal, border.DiagonalUp = line, true\n\t\t\tcase \"diagonalDown\":\n\t\t\t\tborder.Diagonal, border.DiagonalDown = line, true\n\t\t\t}\n\t\t}\n\t}\n\treturn &border\n}\n\n// setCellXfs provides a function to set describes all the formatting for a\n// cell.\nfunc setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, applyAlignment, applyProtection bool, alignment *xlsxAlignment, protection *xlsxProtection) (int, error) {\n\tvar xf xlsxXf\n\txf.FontID = intPtr(fontID)\n\tif fontID != 0 {\n\t\txf.ApplyFont = boolPtr(true)\n\t}\n\txf.NumFmtID = intPtr(numFmtID)\n\tif numFmtID != 0 {\n\t\txf.ApplyNumberFormat = boolPtr(true)\n\t}\n\txf.FillID = intPtr(fillID)\n\tif fillID != 0 {\n\t\txf.ApplyFill = boolPtr(true)\n\t}\n\txf.BorderID = intPtr(borderID)\n\tif borderID != 0 {\n\t\txf.ApplyBorder = boolPtr(true)\n\t}\n\tif len(style.CellXfs.Xf) == MaxCellStyles {\n\t\treturn 0, ErrCellStyles\n\t}\n\tstyle.CellXfs.Count = len(style.CellXfs.Xf) + 1\n\txf.Alignment = alignment\n\tif alignment != nil {\n\t\txf.ApplyAlignment = boolPtr(applyAlignment)\n\t}\n\tif applyProtection {\n\t\txf.ApplyProtection = boolPtr(applyProtection)\n\t\txf.Protection = protection\n\t}\n\txfID := 0\n\txf.XfID = &xfID\n\tstyle.CellXfs.Xf = append(style.CellXfs.Xf, xf)\n\treturn style.CellXfs.Count - 1, nil\n}\n\n// GetCellStyle provides a function to get cell style index by given worksheet\n// name and cell reference. This function is concurrency safe.\nfunc (f *File) GetCellStyle(sheet, cell string) (int, error) {\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn 0, err\n\t}\n\tf.mu.Unlock()\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tws.prepareSheetXML(col, row)\n\treturn ws.prepareCellStyle(col, row, ws.SheetData.Row[row-1].C[col-1].S), err\n}\n\n// SetCellStyle provides a function to add style attribute for cells by given\n// worksheet name, range reference and style ID. This function is concurrency\n// safe. Note that diagonalDown and diagonalUp type border should be use same\n// color in the same range. SetCellStyle will overwrite the existing\n// styles for the cell, it won't append or merge style with existing styles.\n//\n// For example create a borders of cell H9 on Sheet1:\n//\n//\tstyle, err := f.NewStyle(&excelize.Style{\n//\t    Border: []excelize.Border{\n//\t        {Type: \"left\", Color: \"0000FF\", Style: 3},\n//\t        {Type: \"top\", Color: \"00FF00\", Style: 4},\n//\t        {Type: \"bottom\", Color: \"FFFF00\", Style: 5},\n//\t        {Type: \"right\", Color: \"FF0000\", Style: 6},\n//\t        {Type: \"diagonalDown\", Color: \"A020F0\", Style: 7},\n//\t        {Type: \"diagonalUp\", Color: \"A020F0\", Style: 8},\n//\t    },\n//\t})\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\terr = f.SetCellStyle(\"Sheet1\", \"H9\", \"H9\", style)\n//\n// Set gradient fill with vertical variants shading styles for cell H9 on\n// Sheet1:\n//\n//\tstyle, err := f.NewStyle(&excelize.Style{\n//\t    Fill: excelize.Fill{Type: \"gradient\", Color: []string{\"FFFFFF\", \"E0EBF5\"}, Shading: 1},\n//\t})\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\terr = f.SetCellStyle(\"Sheet1\", \"H9\", \"H9\", style)\n//\n// Set solid style pattern fill for cell H9 on Sheet1:\n//\n//\tstyle, err := f.NewStyle(&excelize.Style{\n//\t    Fill: excelize.Fill{Type: \"pattern\", Color: []string{\"E0EBF5\"}, Pattern: 1},\n//\t})\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\terr = f.SetCellStyle(\"Sheet1\", \"H9\", \"H9\", style)\n//\n// Set alignment style for cell H9 on Sheet1:\n//\n//\tstyle, err := f.NewStyle(&excelize.Style{\n//\t    Alignment: &excelize.Alignment{\n//\t        Horizontal:      \"center\",\n//\t        Indent:          1,\n//\t        JustifyLastLine: true,\n//\t        ReadingOrder:    0,\n//\t        RelativeIndent:  1,\n//\t        ShrinkToFit:     true,\n//\t        TextRotation:    45,\n//\t        Vertical:        \"\",\n//\t        WrapText:        true,\n//\t    },\n//\t})\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\terr = f.SetCellStyle(\"Sheet1\", \"H9\", \"H9\", style)\n//\n// Dates and times in Excel are represented by real numbers, for example \"Apr 7\n// 2017 12:00 PM\" is represented by the number 42920.5. Set date and time format\n// for cell H9 on Sheet1:\n//\n//\tf.SetCellValue(\"Sheet1\", \"H9\", 42920.5)\n//\tstyle, err := f.NewStyle(&excelize.Style{NumFmt: 22})\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\terr = f.SetCellStyle(\"Sheet1\", \"H9\", \"H9\", style)\n//\n// Set font style for cell H9 on Sheet1:\n//\n//\tstyle, err := f.NewStyle(&excelize.Style{\n//\t    Font: &excelize.Font{\n//\t        Bold:   true,\n//\t        Italic: true,\n//\t        Family: \"Times New Roman\",\n//\t        Size:   36,\n//\t        Color:  \"777777\",\n//\t    },\n//\t})\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\terr = f.SetCellStyle(\"Sheet1\", \"H9\", \"H9\", style)\n//\n// Hide and lock for cell H9 on Sheet1:\n//\n//\tstyle, err := f.NewStyle(&excelize.Style{\n//\t    Protection: &excelize.Protection{\n//\t        Hidden: true,\n//\t        Locked: true,\n//\t    },\n//\t})\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\terr = f.SetCellStyle(\"Sheet1\", \"H9\", \"H9\", style)\nfunc (f *File) SetCellStyle(sheet, topLeftCell, bottomRightCell string, styleID int) error {\n\thCol, hRow, err := CellNameToCoordinates(topLeftCell)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvCol, vRow, err := CellNameToCoordinates(bottomRightCell)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Normalize the range, such correct C1:B3 to B1:C3.\n\tif vCol < hCol {\n\t\tvCol, hCol = hCol, vCol\n\t}\n\n\tif vRow < hRow {\n\t\tvRow, hRow = hRow, vRow\n\t}\n\n\thColIdx := hCol - 1\n\thRowIdx := hRow - 1\n\n\tvColIdx := vCol - 1\n\tvRowIdx := vRow - 1\n\tf.mu.Lock()\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn err\n\t}\n\ts, err := f.stylesReader()\n\tif err != nil {\n\t\tf.mu.Unlock()\n\t\treturn err\n\t}\n\tf.mu.Unlock()\n\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\n\tws.prepareSheetXML(vCol, vRow)\n\tws.makeContiguousColumns(hRow, vRow, vCol)\n\n\tif styleID < 0 || s.CellXfs == nil || len(s.CellXfs.Xf) <= styleID {\n\t\treturn newInvalidStyleID(styleID)\n\t}\n\n\tfor r := hRowIdx; r <= vRowIdx; r++ {\n\t\tfor k := hColIdx; k <= vColIdx; k++ {\n\t\t\tws.SheetData.Row[r].C[k].S = styleID\n\t\t}\n\t}\n\treturn err\n}\n\n// SetConditionalFormat provides a function to create conditional formatting\n// rule for cell value. Conditional formatting is a feature of Excel which\n// allows you to apply a format to a cell or a range of cells based on certain\n// criteria.\n//\n// The type option is a required parameter and it has no default value.\n// Allowable type values and their associated parameters are:\n//\n//\t Type          | Parameters\n//\t---------------+------------------------------------\n//\t cell          | Criteria\n//\t               | Value\n//\t               | MinValue\n//\t               | MaxValue\n//\t time_period   | Criteria\n//\t text          | Criteria\n//\t               | Value\n//\t average       | Criteria\n//\t duplicate     | (none)\n//\t unique        | (none)\n//\t top           | Criteria\n//\t               | Value\n//\t bottom        | Criteria\n//\t               | Value\n//\t blanks        | (none)\n//\t no_blanks     | (none)\n//\t errors        | (none)\n//\t no_errors     | (none)\n//\t 2_color_scale | MinType\n//\t               | MaxType\n//\t               | MinValue\n//\t               | MaxValue\n//\t               | MinColor\n//\t               | MaxColor\n//\t 3_color_scale | MinType\n//\t               | MidType\n//\t               | MaxType\n//\t               | MinValue\n//\t               | MidValue\n//\t               | MaxValue\n//\t               | MinColor\n//\t               | MidColor\n//\t               | MaxColor\n//\t data_bar      | MinType\n//\t               | MaxType\n//\t               | MinValue\n//\t               | MaxValue\n//\t               | BarBorderColor\n//\t               | BarColor\n//\t               | BarDirection\n//\t               | BarOnly\n//\t               | BarSolid\n//\t icon_set      | IconStyle\n//\t               | ReverseIcons\n//\t               | IconsOnly\n//\t formula       | Criteria\n//\n// The 'Criteria' parameter is used to set the criteria by which the cell data\n// will be evaluated. It has no default value. The most common criteria as\n// applied to {Type: \"cell\"} are:\n//\n//\tbetween                  |\n//\tnot between              |\n//\tequal to                 | ==\n//\tnot equal to             | !=\n//\tgreater than             | >\n//\tless than                | <\n//\tgreater than or equal to | >=\n//\tless than or equal to    | <=\n//\n// You can either use Excel's textual description strings, in the first column\n// above, or the more common symbolic alternatives.\n//\n// Additional criteria which are specific to other conditional format types are\n// shown in the relevant sections below.\n//\n// value: The value is generally used along with the criteria parameter to set\n// the rule by which the cell data will be evaluated:\n//\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"D1:D10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {\n//\t            Type:     \"cell\",\n//\t            Criteria: \">\",\n//\t            Format:   &format,\n//\t            Value:    \"6\",\n//\t        },\n//\t    },\n//\t)\n//\n// The value property can also be an cell reference:\n//\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"D1:D10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {\n//\t            Type:     \"cell\",\n//\t            Criteria: \">\",\n//\t            Format:   &format,\n//\t            Value:    \"$C$1\",\n//\t        },\n//\t    },\n//\t)\n//\n// type: format - The format parameter is used to specify the format that will\n// be applied to the cell when the conditional formatting criterion is met. The\n// format is created using the NewConditionalStyle function in the same way as\n// cell formats:\n//\n//\tformat, err := f.NewConditionalStyle(\n//\t    &excelize.Style{\n//\t        Font: &excelize.Font{Color: \"9A0511\"},\n//\t        Fill: excelize.Fill{\n//\t            Type: \"pattern\", Color: []string{\"FEC7CE\"}, Pattern: 1,\n//\t        },\n//\t    },\n//\t)\n//\tif err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\terr = f.SetConditionalFormat(\"Sheet1\", \"D1:D10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {Type: \"cell\", Criteria: \">\", Format: &format, Value: \"6\"},\n//\t    },\n//\t)\n//\n// Note: In Excel, a conditional format is superimposed over the existing cell\n// format and not all cell format properties can be modified. Properties that\n// cannot be modified in a conditional format are font name, font size,\n// superscript and subscript, diagonal borders, all alignment properties and all\n// protection properties.\n//\n// Excel specifies some default formats to be used with conditional formatting.\n// These can be replicated using the following excelize formats:\n//\n//\t// Rose format for bad conditional.\n//\tformat1, err := f.NewConditionalStyle(\n//\t    &excelize.Style{\n//\t        Font: &excelize.Font{Color: \"9A0511\"},\n//\t        Fill: excelize.Fill{\n//\t            Type: \"pattern\", Color: []string{\"#FEC7CE\"}, Pattern: 1,\n//\t        },\n//\t    },\n//\t)\n//\n//\t// Light yellow format for neutral conditional.\n//\tformat2, err := f.NewConditionalStyle(\n//\t    &excelize.Style{\n//\t        Font: &excelize.Font{Color: \"9B5713\"},\n//\t        Fill: excelize.Fill{\n//\t            Type: \"pattern\", Color: []string{\"FEEAA0\"}, Pattern: 1,\n//\t        },\n//\t    },\n//\t)\n//\n//\t// Light green format for good conditional.\n//\tformat3, err := f.NewConditionalStyle(\n//\t    &excelize.Style{\n//\t        Font: &excelize.Font{Color: \"09600B\"},\n//\t        Fill: excelize.Fill{\n//\t            Type: \"pattern\", Color: []string{\"C7EECF\"}, Pattern: 1,\n//\t        },\n//\t    },\n//\t)\n//\n// type: MinValue - The 'MinValue' parameter is used to set the lower limiting\n// value when the criteria is either \"between\" or \"not between\".\n//\n//\t// Highlight cells rules: between...\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"A1:A10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {\n//\t            Type:     \"cell\",\n//\t            Criteria: \"between\",\n//\t            Format:   &format,\n//\t            MinValue: 6\",\n//\t            MaxValue: 8\",\n//\t        },\n//\t    },\n//\t)\n//\n// type: MaxValue - The 'MaxValue' parameter is used to set the upper limiting\n// value when the criteria is either \"between\" or \"not between\". See the\n// previous example.\n//\n// type: average - The average type is used to specify Excel's \"Average\" style\n// conditional format:\n//\n//\t// Top/Bottom rules: Above Average...\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"A1:A10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {\n//\t            Type:         \"average\",\n//\t            Criteria:     \"=\",\n//\t            Format:       &format1,\n//\t            AboveAverage: true,\n//\t        },\n//\t    },\n//\t)\n//\n//\t// Top/Bottom rules: Below Average...\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"B1:B10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {\n//\t            Type:         \"average\",\n//\t            Criteria:     \"=\",\n//\t            Format:       &format2,\n//\t            AboveAverage: false,\n//\t        },\n//\t    },\n//\t)\n//\n// type: duplicate - The duplicate type is used to highlight duplicate cells in\n// a range:\n//\n//\t// Highlight cells rules: Duplicate Values...\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"A1:A10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {Type: \"duplicate\", Criteria: \"=\", Format: &format},\n//\t    },\n//\t)\n//\n// type: unique - The unique type is used to highlight unique cells in a range:\n//\n//\t// Highlight cells rules: Not Equal To...\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"A1:A10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {Type: \"unique\", Criteria: \"=\", Format: &format},\n//\t    },\n//\t)\n//\n// type: top - The top type is used to specify the top n values by number or\n// percentage in a range:\n//\n//\t// Top/Bottom rules: Top 10.\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"H1:H10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {\n//\t            Type:     \"top\",\n//\t            Criteria: \"=\",\n//\t            Format:   &format,\n//\t            Value:    \"6\",\n//\t        },\n//\t    },\n//\t)\n//\n// The criteria can be used to indicate that a percentage condition is required:\n//\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"A1:A10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {\n//\t            Type:     \"top\",\n//\t            Criteria: \"=\",\n//\t            Format:   &format,\n//\t            Value:    \"6\",\n//\t            Percent:  true,\n//\t        },\n//\t    },\n//\t)\n//\n// type: 2_color_scale - The 2_color_scale type is used to specify Excel's \"2\n// Color Scale\" style conditional format:\n//\n//\t// Color scales: 2 color.\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"A1:A10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {\n//\t            Type:     \"2_color_scale\",\n//\t            Criteria: \"=\",\n//\t            MinType:  \"min\",\n//\t            MaxType:  \"max\",\n//\t            MinColor: \"#F8696B\",\n//\t            MaxColor: \"#63BE7B\",\n//\t        },\n//\t    },\n//\t)\n//\n// This conditional type can be modified with MinType, MaxType, MinValue,\n// MaxValue, MinColor and MaxColor, see below.\n//\n// type: 3_color_scale - The 3_color_scale type is used to specify Excel's \"3\n// Color Scale\" style conditional format:\n//\n//\t// Color scales: 3 color.\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"A1:A10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {\n//\t            Type:     \"3_color_scale\",\n//\t            Criteria: \"=\",\n//\t            MinType:  \"min\",\n//\t            MidType:  \"percentile\",\n//\t            MaxType:  \"max\",\n//\t            MinColor: \"#F8696B\",\n//\t            MidColor: \"#FFEB84\",\n//\t            MaxColor: \"#63BE7B\",\n//\t        },\n//\t    },\n//\t)\n//\n// This conditional type can be modified with MinType, MidType, MaxType,\n// MinValue, MidValue, MaxValue, MinColor, MidColor and MaxColor, see\n// below.\n//\n// type: data_bar - The data_bar type is used to specify Excel's \"Data Bar\"\n// style conditional format.\n//\n// MinType - The MinType and MaxType properties are available when the\n// conditional formatting type is 2_color_scale, 3_color_scale or data_bar.\n// The MidType is available for 3_color_scale. The properties are used as\n// follows:\n//\n//\t// Data Bars: Gradient Fill.\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"K1:K10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {\n//\t            Type:     \"data_bar\",\n//\t            Criteria: \"=\",\n//\t            MinType:  \"min\",\n//\t            MaxType:  \"max\",\n//\t            BarColor: \"#638EC6\",\n//\t        },\n//\t    },\n//\t)\n//\n// The available min/mid/max types are:\n//\n//\tmin        (for MinType only)\n//\tnum\n//\tpercent\n//\tpercentile\n//\tformula\n//\tmax        (for MaxType only)\n//\n// MidType - Used for 3_color_scale. Same as MinType, see above.\n//\n// MaxType - Same as MinType, see above.\n//\n// MinValue - The MinValue and MaxValue properties are available when the\n// conditional formatting type is 2_color_scale, 3_color_scale or data_bar.\n//\n// MidValue - The MidValue is available for 3_color_scale. Same as MinValue,\n// see above.\n//\n// MaxValue - Same as MinValue, see above.\n//\n// MinColor - The MinColor and MaxColor properties are available when the\n// conditional formatting type is 2_color_scale, 3_color_scale or data_bar.\n//\n// MidColor - The MidColor is available for 3_color_scale. The properties\n// are used as follows:\n//\n//\t// Color scales: 3 color.\n//\terr := f.SetConditionalFormat(\"Sheet1\", \"B1:B10\",\n//\t    []excelize.ConditionalFormatOptions{\n//\t        {\n//\t            Type:     \"3_color_scale\",\n//\t            Criteria: \"=\",\n//\t            MinType:  \"min\",\n//\t            MidType:  \"percentile\",\n//\t            MaxType:  \"max\",\n//\t            MinColor: \"#F8696B\",\n//\t            MidColor: \"#FFEB84\",\n//\t            MaxColor: \"#63BE7B\",\n//\t        },\n//\t    },\n//\t)\n//\n// MaxColor - Same as MinColor, see above.\n//\n// BarColor - Used for data_bar. Same as MinColor, see above.\n//\n// BarBorderColor - Used for sets the color for the border line of a data bar,\n// this is only visible in Excel 2010 and later.\n//\n// BarDirection - sets the direction for data bars. The available options are:\n//\n//\tcontext - Data bar direction is set by spreadsheet application based on the context of the data displayed.\n//\tleftToRight - Data bar direction is from right to left.\n//\trightToLeft - Data bar direction is from left to right.\n//\n// BarOnly - Used for set displays a bar data but not the data in the cells.\n//\n// BarSolid - Used for turns on a solid (non-gradient) fill for data bars, this\n// is only visible in Excel 2010 and later.\n//\n// IconStyle - The available options are:\n//\n//\t3Arrows\n//\t3ArrowsGray\n//\t3Flags\n//\t3Signs\n//\t3Stars\n//\t3Symbols\n//\t3Symbols2\n//\t3TrafficLights1\n//\t3TrafficLights2\n//\t3Triangles\n//\t4Arrows\n//\t4ArrowsGray\n//\t4Rating\n//\t4RedToBlack\n//\t4TrafficLights\n//\t5Arrows\n//\t5ArrowsGray\n//\t5Boxes\n//\t5Quarters\n//\t5Rating\n//\n// ReverseIcons - Used for set reversed icons sets.\n//\n// IconsOnly - Used for set displayed without the cell value.\n//\n// StopIfTrue - used to set the \"stop if true\" feature of a conditional\n// formatting rule when more than one rule is applied to a cell or a range of\n// cells. When this parameter is set then subsequent rules are not evaluated\n// if the current rule is true.\nfunc (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFormatOptions) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tSQRef, mastCell, err := prepareConditionalFormatRange(rangeRef)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Create a pseudo GUID for each unique rule.\n\tvar rules int\n\tfor _, cf := range ws.ConditionalFormatting {\n\t\trules += len(cf.CfRule)\n\t}\n\tvar (\n\t\tcfRule          []*xlsxCfRule\n\t\tnoCriteriaTypes = []string{\n\t\t\t\"containsBlanks\",\n\t\t\t\"notContainsBlanks\",\n\t\t\t\"containsErrors\",\n\t\t\t\"notContainsErrors\",\n\t\t\t\"expression\",\n\t\t\t\"iconSet\",\n\t\t}\n\t)\n\tfor i, opt := range opts {\n\t\tvar vt, ct string\n\t\tvar ok bool\n\t\t// \"type\" is a required parameter, check for valid validation types.\n\t\tvt, ok = validType[opt.Type]\n\t\tif ok {\n\t\t\t// Check for valid criteria types.\n\t\t\tct, ok = criteriaType[opt.Criteria]\n\t\t\tif ok || inStrSlice(noCriteriaTypes, vt, true) != -1 {\n\t\t\t\tdrawFunc, ok := drawContFmtFunc[vt]\n\t\t\t\tif ok {\n\t\t\t\t\tpriority := rules + i\n\t\t\t\t\trule, x14rule := drawFunc(priority, ct, mastCell,\n\t\t\t\t\t\tfmt.Sprintf(\"{00000000-0000-0000-%04X-%012X}\", f.getSheetID(sheet), priority), &opt)\n\t\t\t\t\tif rule == nil && x14rule == nil {\n\t\t\t\t\t\treturn ErrParameterInvalid\n\t\t\t\t\t}\n\t\t\t\t\tif rule != nil {\n\t\t\t\t\t\tcfRule = append(cfRule, rule)\n\t\t\t\t\t}\n\t\t\t\t\tif x14rule != nil {\n\t\t\t\t\t\tif err = f.appendCfRule(ws, x14rule, SQRef); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t\tf.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14)\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ErrParameterInvalid\n\t\t}\n\t\treturn ErrParameterInvalid\n\t}\n\tif len(cfRule) > 0 {\n\t\tws.ConditionalFormatting = append(ws.ConditionalFormatting, &xlsxConditionalFormatting{\n\t\t\tSQRef:  SQRef,\n\t\t\tCfRule: cfRule,\n\t\t})\n\t}\n\treturn err\n}\n\n// prepareConditionalFormatRange returns checked cell range and master cell\n// reference by giving conditional formatting range reference.\nfunc prepareConditionalFormatRange(rangeRef string) (string, string, error) {\n\tvar SQRef, mastCell string\n\tif rangeRef == \"\" {\n\t\treturn SQRef, mastCell, ErrParameterRequired\n\t}\n\trangeRef = strings.ReplaceAll(rangeRef, \",\", \" \")\n\tfor i, cellRange := range strings.Split(rangeRef, \" \") {\n\t\tvar cellNames []string\n\t\tfor j, ref := range strings.Split(cellRange, \":\") {\n\t\t\tif j > 1 {\n\t\t\t\treturn SQRef, mastCell, ErrParameterInvalid\n\t\t\t}\n\t\t\tcellRef, col, row, err := parseRef(ref)\n\t\t\tif err != nil {\n\t\t\t\treturn SQRef, mastCell, err\n\t\t\t}\n\t\t\tvar c, r int\n\t\t\tif col {\n\t\t\t\tif cellRef.Row = TotalRows; j == 0 {\n\t\t\t\t\tcellRef.Row = 1\n\t\t\t\t}\n\t\t\t}\n\t\t\tif row {\n\t\t\t\tif cellRef.Col = MaxColumns; j == 0 {\n\t\t\t\t\tcellRef.Col = 1\n\t\t\t\t}\n\t\t\t}\n\t\t\tc, r = cellRef.Col, cellRef.Row\n\t\t\tcellName, _ := CoordinatesToCellName(c, r)\n\t\t\tcellNames = append(cellNames, cellName)\n\t\t\tif i == 0 && j == 0 {\n\t\t\t\tmastCell = cellName\n\t\t\t}\n\t\t}\n\t\tSQRef += strings.Join(cellNames, \":\") + \" \"\n\t}\n\treturn strings.TrimSuffix(SQRef, \" \"), mastCell, nil\n}\n\n// appendCfRule provides a function to append rules to conditional formatting.\nfunc (f *File) appendCfRule(ws *xlsxWorksheet, rule *xlsxX14CfRule, sqref string) error {\n\tvar (\n\t\terr                                      error\n\t\tidx                                      int\n\t\tappendMode                               bool\n\t\tdecodeExtLst                             = new(decodeExtLst)\n\t\tcondFmts                                 *xlsxX14ConditionalFormattings\n\t\tdecodeCondFmts                           *decodeX14ConditionalFormattings\n\t\text                                      *xlsxExt\n\t\tcondFmtBytes, condFmtsBytes, extLstBytes []byte\n\t)\n\tcondFmtBytes, _ = xml.Marshal([]*xlsxX14ConditionalFormatting{\n\t\t{XMLNSXM: NameSpaceSpreadSheetExcel2006Main.Value, Sqref: sqref, CfRule: []*xlsxX14CfRule{rule}},\n\t})\n\tif ws.ExtLst != nil { // append mode ext\n\t\tif err = f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + ws.ExtLst.Ext + \"</extLst>\")).\n\t\t\tDecode(decodeExtLst); err != nil && err != io.EOF {\n\t\t\treturn err\n\t\t}\n\t\tfor idx, ext = range decodeExtLst.Ext {\n\t\t\tif ext.URI == ExtURIConditionalFormattings {\n\t\t\t\tdecodeCondFmts = new(decodeX14ConditionalFormattings)\n\t\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(decodeCondFmts)\n\t\t\t\tif condFmts == nil {\n\t\t\t\t\tcondFmts = &xlsxX14ConditionalFormattings{}\n\t\t\t\t}\n\t\t\t\tcondFmts.Content = decodeCondFmts.Content + string(condFmtBytes)\n\t\t\t\tcondFmtsBytes, _ = xml.Marshal(condFmts)\n\t\t\t\tdecodeExtLst.Ext[idx].Content = string(condFmtsBytes)\n\t\t\t\tappendMode = true\n\t\t\t}\n\t\t}\n\t}\n\tif !appendMode {\n\t\tcondFmtsBytes, _ = xml.Marshal(&xlsxX14ConditionalFormattings{Content: string(condFmtBytes)})\n\t\tdecodeExtLst.Ext = append(decodeExtLst.Ext, &xlsxExt{\n\t\t\tURI: ExtURIConditionalFormattings, Content: string(condFmtsBytes),\n\t\t})\n\t}\n\tsort.Slice(decodeExtLst.Ext, func(i, j int) bool {\n\t\treturn inStrSlice(worksheetExtURIPriority, decodeExtLst.Ext[i].URI, false) <\n\t\t\tinStrSlice(worksheetExtURIPriority, decodeExtLst.Ext[j].URI, false)\n\t})\n\textLstBytes, err = xml.Marshal(decodeExtLst)\n\tws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), \"<extLst>\"), \"</extLst>\")}\n\treturn err\n}\n\n// extractCondFmtCellIs provides a function to extract conditional format\n// settings for cell value (include between, not between, equal, not equal,\n// greater than and less than) by given conditional formatting rule.\nfunc (f *File) extractCondFmtCellIs(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\tformat := ConditionalFormatOptions{Format: c.DxfID, StopIfTrue: c.StopIfTrue, Type: \"cell\", Criteria: operatorType[c.Operator]}\n\tif len(c.Formula) == 2 {\n\t\tformat.MinValue, format.MaxValue = c.Formula[0], c.Formula[1]\n\t\treturn format\n\t}\n\tformat.Value = c.Formula[0]\n\treturn format\n}\n\n// extractCondFmtTimePeriod provides a function to extract conditional format\n// settings for time period by given conditional formatting rule.\nfunc (f *File) extractCondFmtTimePeriod(c *xlsxCfRule, ref string, extLst *xlsxExtLst) ConditionalFormatOptions {\n\tvar criteria string\n\tfor _, formula := range c.Formula {\n\t\tif c, ok := map[string]string{\n\t\t\tfmt.Sprintf(\"FLOOR(%s,1)=TODAY()-1\", ref):                                  \"yesterday\",\n\t\t\tfmt.Sprintf(\"FLOOR(%s,1)=TODAY()\", ref):                                    \"today\",\n\t\t\tfmt.Sprintf(\"FLOOR(%s,1)=TODAY()+1\", ref):                                  \"tomorrow\",\n\t\t\tfmt.Sprintf(\"AND(TODAY()-FLOOR(%[1]s,1)<=6,FLOOR(%[1]s,1)<=TODAY())\", ref): \"last 7 days\",\n\t\t\tfmt.Sprintf(\"AND(TODAY()-ROUNDDOWN(%[1]s,0)>=(WEEKDAY(TODAY())),TODAY()-ROUNDDOWN(%[1]s,0)<(WEEKDAY(TODAY())+7))\", ref):               \"last week\",\n\t\t\tfmt.Sprintf(\"AND(TODAY()-ROUNDDOWN(%[1]s,0)<=WEEKDAY(TODAY())-1,ROUNDDOWN(%[1]s,0)-TODAY()>=7-WEEKDAY(TODAY()))\", ref):                \"this week\",\n\t\t\tfmt.Sprintf(\"AND(ROUNDDOWN(%[1]s,0)-TODAY()>(7-WEEKDAY(TODAY())),ROUNDDOWN(%[1]s,0)-TODAY()<(15-WEEKDAY(TODAY())))\", ref):             \"continue week\",\n\t\t\tfmt.Sprintf(\"AND(MONTH(%[1]s)=MONTH(TODAY())-1,OR(YEAR(%[1]s)=YEAR(TODAY()),AND(MONTH(%[1]s)=1,YEAR(%[1]s)=YEAR(TODAY())-1)))\", ref):  \"last month\",\n\t\t\tfmt.Sprintf(\"AND(MONTH(%[1]s)=MONTH(TODAY()),YEAR(%[1]s)=YEAR(TODAY()))\", ref):                                                        \"this month\",\n\t\t\tfmt.Sprintf(\"AND(MONTH(%[1]s)=MONTH(TODAY())+1,OR(YEAR(%[1]s)=YEAR(TODAY()),AND(MONTH(%[1]s)=12,YEAR(%[1]s)=YEAR(TODAY())+1)))\", ref): \"continue month\",\n\t\t}[formula]; ok {\n\t\t\tcriteria = c\n\t\t\tbreak\n\t\t}\n\t}\n\treturn ConditionalFormatOptions{Format: c.DxfID, StopIfTrue: c.StopIfTrue, Type: \"time_period\", Criteria: criteria}\n}\n\n// extractCondFmtText provides a function to extract conditional format\n// settings for text cell values by given conditional formatting rule.\nfunc (f *File) extractCondFmtText(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\treturn ConditionalFormatOptions{Format: c.DxfID, StopIfTrue: c.StopIfTrue, Type: \"text\", Criteria: operatorType[c.Operator], Value: c.Text}\n}\n\n// extractCondFmtTop10 provides a function to extract conditional format\n// settings for top N (default is top 10) by given conditional formatting\n// rule.\nfunc (f *File) extractCondFmtTop10(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\tformat := ConditionalFormatOptions{\n\t\tFormat:     c.DxfID,\n\t\tStopIfTrue: c.StopIfTrue,\n\t\tType:       \"top\",\n\t\tCriteria:   \"=\",\n\t\tPercent:    c.Percent,\n\t\tValue:      strconv.Itoa(c.Rank),\n\t}\n\tif c.Bottom {\n\t\tformat.Type = \"bottom\"\n\t}\n\treturn format\n}\n\n// extractCondFmtAboveAverage provides a function to extract conditional format\n// settings for above average and below average by given conditional formatting\n// rule.\nfunc (f *File) extractCondFmtAboveAverage(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\tformat := ConditionalFormatOptions{\n\t\tFormat:     c.DxfID,\n\t\tStopIfTrue: c.StopIfTrue,\n\t\tType:       \"average\",\n\t\tCriteria:   \"=\",\n\t}\n\tif c.AboveAverage != nil {\n\t\tformat.AboveAverage = *c.AboveAverage\n\t}\n\treturn format\n}\n\n// extractCondFmtDuplicateUniqueValues provides a function to extract\n// conditional format settings for duplicate and unique values by given\n// conditional formatting rule.\nfunc (f *File) extractCondFmtDuplicateUniqueValues(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\treturn ConditionalFormatOptions{\n\t\tFormat:     c.DxfID,\n\t\tStopIfTrue: c.StopIfTrue,\n\t\tType: map[string]string{\n\t\t\t\"duplicateValues\": \"duplicate\",\n\t\t\t\"uniqueValues\":    \"unique\",\n\t\t}[c.Type],\n\t\tCriteria: \"=\",\n\t}\n}\n\n// extractCondFmtBlanks provides a function to extract conditional format\n// settings for blank cells by given conditional formatting rule.\nfunc (f *File) extractCondFmtBlanks(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\treturn ConditionalFormatOptions{\n\t\tFormat:     c.DxfID,\n\t\tStopIfTrue: c.StopIfTrue,\n\t\tType:       \"blanks\",\n\t}\n}\n\n// extractCondFmtNoBlanks provides a function to extract conditional format\n// settings for no blank cells by given conditional formatting rule.\nfunc (f *File) extractCondFmtNoBlanks(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\treturn ConditionalFormatOptions{\n\t\tFormat:     c.DxfID,\n\t\tStopIfTrue: c.StopIfTrue,\n\t\tType:       \"no_blanks\",\n\t}\n}\n\n// extractCondFmtErrors provides a function to extract conditional format\n// settings for cells with errors by given conditional formatting rule.\nfunc (f *File) extractCondFmtErrors(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\treturn ConditionalFormatOptions{\n\t\tFormat:     c.DxfID,\n\t\tStopIfTrue: c.StopIfTrue,\n\t\tType:       \"errors\",\n\t}\n}\n\n// extractCondFmtNoErrors provides a function to extract conditional format\n// settings for cells without errors by given conditional formatting rule.\nfunc (f *File) extractCondFmtNoErrors(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\treturn ConditionalFormatOptions{\n\t\tFormat:     c.DxfID,\n\t\tStopIfTrue: c.StopIfTrue,\n\t\tType:       \"no_errors\",\n\t}\n}\n\n// extractCondFmtColorScale provides a function to extract conditional format\n// settings for color scale (include 2 color scale and 3 color scale) by given\n// conditional formatting rule.\nfunc (f *File) extractCondFmtColorScale(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\tformat := ConditionalFormatOptions{StopIfTrue: c.StopIfTrue}\n\tformat.Type, format.Criteria = \"2_color_scale\", \"=\"\n\tvalues := len(c.ColorScale.Cfvo)\n\tcolors := len(c.ColorScale.Color)\n\tif colors > 1 && values > 1 {\n\t\tformat.MinType = c.ColorScale.Cfvo[0].Type\n\t\tif c.ColorScale.Cfvo[0].Val != \"0\" {\n\t\t\tformat.MinValue = c.ColorScale.Cfvo[0].Val\n\t\t}\n\t\tformat.MinColor = \"#\" + f.getThemeColor(c.ColorScale.Color[0])\n\t\tformat.MaxType = c.ColorScale.Cfvo[1].Type\n\t\tif c.ColorScale.Cfvo[1].Val != \"0\" {\n\t\t\tformat.MaxValue = c.ColorScale.Cfvo[1].Val\n\t\t}\n\t\tformat.MaxColor = \"#\" + f.getThemeColor(c.ColorScale.Color[1])\n\t}\n\tif colors == 3 {\n\t\tformat.Type = \"3_color_scale\"\n\t\tformat.MidType = c.ColorScale.Cfvo[1].Type\n\t\tif c.ColorScale.Cfvo[1].Val != \"0\" {\n\t\t\tformat.MidValue = c.ColorScale.Cfvo[1].Val\n\t\t}\n\t\tformat.MidColor = \"#\" + f.getThemeColor(c.ColorScale.Color[1])\n\t\tformat.MaxType = c.ColorScale.Cfvo[2].Type\n\t\tif c.ColorScale.Cfvo[2].Val != \"0\" {\n\t\t\tformat.MaxValue = c.ColorScale.Cfvo[2].Val\n\t\t}\n\t\tformat.MaxColor = \"#\" + f.getThemeColor(c.ColorScale.Color[2])\n\t}\n\treturn format\n}\n\n// extractCondFmtDataBarRule provides a function to extract conditional format\n// settings for data bar by given conditional formatting rule extension list.\nfunc (f *File) extractCondFmtDataBarRule(ID string, format *ConditionalFormatOptions, condFmts []decodeX14ConditionalFormatting) {\n\tfor _, condFmt := range condFmts {\n\t\tfor _, rule := range condFmt.CfRule {\n\t\t\tif rule.DataBar != nil && rule.ID == ID {\n\t\t\t\tformat.BarDirection = rule.DataBar.Direction\n\t\t\t\tif rule.DataBar.Gradient != nil && !*rule.DataBar.Gradient {\n\t\t\t\t\tformat.BarSolid = true\n\t\t\t\t}\n\t\t\t\tif rule.DataBar.BorderColor != nil {\n\t\t\t\t\tformat.BarBorderColor = \"#\" + f.getThemeColor(rule.DataBar.BorderColor)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// extractCondFmtDataBar provides a function to extract conditional format\n// settings for data bar by given conditional formatting rule.\nfunc (f *File) extractCondFmtDataBar(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\tformat := ConditionalFormatOptions{Type: \"data_bar\", Criteria: \"=\"}\n\tif c.DataBar != nil {\n\t\tformat.StopIfTrue = c.StopIfTrue\n\t\tformat.MinType = c.DataBar.Cfvo[0].Type\n\t\tformat.MinValue = c.DataBar.Cfvo[0].Val\n\t\tformat.MaxType = c.DataBar.Cfvo[1].Type\n\t\tformat.MaxValue = c.DataBar.Cfvo[1].Val\n\t\tformat.BarColor = \"#\" + f.getThemeColor(c.DataBar.Color[0])\n\t\tif c.DataBar.ShowValue != nil {\n\t\t\tformat.BarOnly = !*c.DataBar.ShowValue\n\t\t}\n\t}\n\textractExtLst := func(ID string, extLst *decodeExtLst) {\n\t\tfor _, ext := range extLst.Ext {\n\t\t\tif ext.URI == ExtURIConditionalFormattings {\n\t\t\t\tdecodeCondFmts := new(decodeX14ConditionalFormattingRules)\n\t\t\t\tif err := xml.Unmarshal([]byte(ext.Content), &decodeCondFmts); err == nil {\n\t\t\t\t\tf.extractCondFmtDataBarRule(ID, &format, decodeCondFmts.CondFmt)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif c.ExtLst != nil {\n\t\text := decodeX14ConditionalFormattingExt{}\n\t\tif err := xml.Unmarshal([]byte(c.ExtLst.Ext), &ext); err == nil && extLst != nil {\n\t\t\tdecodeExtLst := new(decodeExtLst)\n\t\t\tif err = xml.Unmarshal([]byte(\"<extLst>\"+extLst.Ext+\"</extLst>\"), decodeExtLst); err == nil {\n\t\t\t\textractExtLst(ext.ID, decodeExtLst)\n\t\t\t}\n\t\t}\n\t}\n\treturn format\n}\n\n// extractCondFmtExp provides a function to extract conditional format settings\n// for expression by given conditional formatting rule.\nfunc (f *File) extractCondFmtExp(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\tformat := ConditionalFormatOptions{Format: c.DxfID, StopIfTrue: c.StopIfTrue, Type: \"formula\"}\n\tif len(c.Formula) > 0 {\n\t\tformat.Criteria = c.Formula[0]\n\t}\n\treturn format\n}\n\n// extractCondFmtIconSet provides a function to extract conditional format\n// settings for icon sets by given conditional formatting rule.\nfunc (f *File) extractCondFmtIconSet(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {\n\tformat := ConditionalFormatOptions{Type: \"icon_set\"}\n\tif c.IconSet != nil {\n\t\tif c.IconSet.ShowValue != nil {\n\t\t\tformat.IconsOnly = !*c.IconSet.ShowValue\n\t\t}\n\t\tformat.IconStyle = c.IconSet.IconSet\n\t\tformat.ReverseIcons = c.IconSet.Reverse\n\t}\n\treturn format\n}\n\n// extractX14CondFmtIconSet provides a function to extract conditional format\n// settings for icon sets by given conditional formatting rule.\nfunc (f *File) extractX14CondFmtIconSetRule(c *decodeX14CfRule) ConditionalFormatOptions {\n\tformat := ConditionalFormatOptions{Type: \"icon_set\"}\n\tif c.IconSet != nil {\n\t\tif c.IconSet.ShowValue != nil {\n\t\t\tformat.IconsOnly = !*c.IconSet.ShowValue\n\t\t}\n\t\tformat.IconStyle = c.IconSet.IconSet\n\t\tformat.ReverseIcons = c.IconSet.Reverse\n\t}\n\treturn format\n}\n\n// GetConditionalFormats returns conditional format settings by given worksheet\n// name.\nfunc (f *File) GetConditionalFormats(sheet string) (map[string][]ConditionalFormatOptions, error) {\n\tconditionalFormats := make(map[string][]ConditionalFormatOptions)\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn conditionalFormats, err\n\t}\n\tfor _, cf := range ws.ConditionalFormatting {\n\t\tvar opts []ConditionalFormatOptions\n\t\t_, mastCell, err := prepareConditionalFormatRange(cf.SQRef)\n\t\tif err != nil {\n\t\t\treturn conditionalFormats, err\n\t\t}\n\t\tfor _, cr := range cf.CfRule {\n\t\t\tif extractFunc, ok := extractContFmtFunc[cr.Type]; ok {\n\t\t\t\topts = append(opts, extractFunc(f, mastCell, cr, ws.ExtLst))\n\t\t\t}\n\t\t}\n\t\tconditionalFormats[cf.SQRef] = opts\n\t}\n\tif ws.ExtLst != nil {\n\t\tdecodeExtLst := new(decodeExtLst)\n\t\tif err = f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + ws.ExtLst.Ext + \"</extLst>\")).\n\t\t\tDecode(decodeExtLst); err != nil && err != io.EOF {\n\t\t\treturn conditionalFormats, err\n\t\t}\n\t\tfor _, ext := range decodeExtLst.Ext {\n\t\t\tif ext.URI == ExtURIConditionalFormattings {\n\t\t\t\tdecodeCondFmts := new(decodeX14ConditionalFormattingRules)\n\t\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(decodeCondFmts)\n\t\t\t\tfor _, condFmt := range decodeCondFmts.CondFmt {\n\t\t\t\t\tvar opts []ConditionalFormatOptions\n\t\t\t\t\tfor _, rule := range condFmt.CfRule {\n\t\t\t\t\t\tif rule.Type == \"iconSet\" {\n\t\t\t\t\t\t\topts = append(opts, f.extractX14CondFmtIconSetRule(rule))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconditionalFormats[condFmt.Sqref] = append(conditionalFormats[condFmt.Sqref], opts...)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn conditionalFormats, err\n}\n\n// UnsetConditionalFormat provides a function to unset the conditional format\n// by given worksheet name and range reference.\nfunc (f *File) UnsetConditionalFormat(sheet, rangeRef string) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdelCells, err := flatSqref(rangeRef)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor i := 0; i < len(ws.ConditionalFormatting); i++ {\n\t\tif ws.ConditionalFormatting[i].SQRef, err = deleteCellsFromSqref(ws.ConditionalFormatting[i].SQRef, delCells); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif len(ws.ConditionalFormatting[i].SQRef) == 0 {\n\t\t\tws.ConditionalFormatting = append(ws.ConditionalFormatting[:i], ws.ConditionalFormatting[i+1:]...)\n\t\t\ti--\n\t\t}\n\t}\n\tif ws.ExtLst != nil {\n\t\tvar condFmtsBytes, extLstBytes []byte\n\t\tdecodeExtLst := new(decodeExtLst)\n\t\tif err = f.xmlNewDecoder(strings.NewReader(\"<extLst>\" + ws.ExtLst.Ext + \"</extLst>\")).\n\t\t\tDecode(decodeExtLst); err != nil && err != io.EOF {\n\t\t\treturn err\n\t\t}\n\t\tfor idx := 0; idx < len(decodeExtLst.Ext); idx++ {\n\t\t\text := decodeExtLst.Ext[idx]\n\t\t\tif ext.URI == ExtURIConditionalFormattings {\n\t\t\t\tdecodeCondFmts := new(decodeX14ConditionalFormattingRules)\n\t\t\t\t_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(decodeCondFmts)\n\t\t\t\tif condFmtsBytes, err = decodeCondFmts.deleteCfRule(delCells); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tdecodeExtLst.Ext[idx].Content = string(condFmtsBytes)\n\t\t\t\tif len(decodeExtLst.Ext[idx].Content) == 57 { // empty x14:conditionalFormattings element\n\t\t\t\t\tdecodeExtLst.Ext = append(decodeExtLst.Ext[:idx], decodeExtLst.Ext[idx+1:]...)\n\t\t\t\t\tidx--\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsort.Slice(decodeExtLst.Ext, func(i, j int) bool {\n\t\t\treturn inStrSlice(worksheetExtURIPriority, decodeExtLst.Ext[i].URI, false) <\n\t\t\t\tinStrSlice(worksheetExtURIPriority, decodeExtLst.Ext[j].URI, false)\n\t\t})\n\t\textLstBytes, err = xml.Marshal(decodeExtLst)\n\t\tws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), \"<extLst>\"), \"</extLst>\")}\n\t\tif len(ws.ExtLst.Ext) == 0 {\n\t\t\tws.ExtLst = nil\n\t\t}\n\t}\n\treturn err\n}\n\n// deleteCfRule provides a function to delete conditional formatting rule by\n// given range reference and return the updated x14:conditionalFormattings\n// element content.\nfunc (r *decodeX14ConditionalFormattingRules) deleteCfRule(delCells map[int][][]int) ([]byte, error) {\n\tvar (\n\t\terr      error\n\t\tcondFmts = &xlsxX14ConditionalFormattings{}\n\t)\n\tfor _, condFmt := range r.CondFmt {\n\t\tif condFmt.Sqref, err = deleteCellsFromSqref(condFmt.Sqref, delCells); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif len(condFmt.Sqref) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tx14ConditionalFormatting := xlsxX14ConditionalFormatting{\n\t\t\tXMLNSXM: NameSpaceSpreadSheetExcel2006Main.Value,\n\t\t\tPivot:   condFmt.Pivot,\n\t\t\tSqref:   condFmt.Sqref,\n\t\t\tExtLst:  condFmt.ExtLst,\n\t\t}\n\t\tfor _, rule := range condFmt.CfRule {\n\t\t\tx14CfRule := &xlsxX14CfRule{\n\t\t\t\tType:          rule.Type,\n\t\t\t\tPriority:      rule.Priority,\n\t\t\t\tStopIfTrue:    rule.StopIfTrue,\n\t\t\t\tAboveAverage:  rule.AboveAverage,\n\t\t\t\tPercent:       rule.Percent,\n\t\t\t\tBottom:        rule.Bottom,\n\t\t\t\tOperator:      rule.Operator,\n\t\t\t\tText:          rule.Text,\n\t\t\t\tTimePeriod:    rule.TimePeriod,\n\t\t\t\tRank:          rule.Rank,\n\t\t\t\tStdDev:        rule.StdDev,\n\t\t\t\tEqualAverage:  rule.EqualAverage,\n\t\t\t\tActivePresent: rule.ActivePresent,\n\t\t\t\tID:            rule.ID,\n\t\t\t\tF:             rule.F,\n\t\t\t\tColorScale:    rule.ColorScale,\n\t\t\t\tDxf:           rule.Dxf,\n\t\t\t\tExtLst:        rule.ExtLst,\n\t\t\t}\n\t\t\tif rule.DataBar != nil {\n\t\t\t\tx14DataBar := &xlsx14DataBar{\n\t\t\t\t\tMaxLength:         rule.DataBar.MaxLength,\n\t\t\t\t\tMinLength:         rule.DataBar.MinLength,\n\t\t\t\t\tBorder:            rule.DataBar.Border,\n\t\t\t\t\tShowValue:         rule.DataBar.ShowValue,\n\t\t\t\t\tDirection:         rule.DataBar.Direction,\n\t\t\t\t\tBorderColor:       rule.DataBar.BorderColor,\n\t\t\t\t\tNegativeFillColor: rule.DataBar.NegativeFillColor,\n\t\t\t\t\tAxisColor:         rule.DataBar.AxisColor,\n\t\t\t\t}\n\t\t\t\tif rule.DataBar.Gradient != nil {\n\t\t\t\t\tx14DataBar.Gradient = *rule.DataBar.Gradient\n\t\t\t\t}\n\t\t\t\tif rule.DataBar.Cfvo != nil {\n\t\t\t\t\tfor _, cfvo := range rule.DataBar.Cfvo {\n\t\t\t\t\t\tx14DataBar.Cfvo = append(x14DataBar.Cfvo, &xlsxCfvo{\n\t\t\t\t\t\t\tGte:    cfvo.Gte,\n\t\t\t\t\t\t\tType:   cfvo.Type,\n\t\t\t\t\t\t\tVal:    cfvo.Val,\n\t\t\t\t\t\t\tExtLst: cfvo.ExtLst,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tx14CfRule.DataBar = x14DataBar\n\t\t\t}\n\t\t\tif rule.IconSet != nil {\n\t\t\t\tx14IconSet := &xlsx14IconSet{\n\t\t\t\t\tIconSet:   rule.IconSet.IconSet,\n\t\t\t\t\tShowValue: rule.IconSet.ShowValue,\n\t\t\t\t\tPercent:   rule.IconSet.Percent,\n\t\t\t\t\tReverse:   rule.IconSet.Reverse,\n\t\t\t\t\tCustom:    rule.IconSet.Custom,\n\t\t\t\t\tCfIcon:    rule.IconSet.CfIcon,\n\t\t\t\t}\n\t\t\t\tif rule.IconSet.Cfvo != nil {\n\t\t\t\t\tfor _, cfvo := range rule.IconSet.Cfvo {\n\t\t\t\t\t\tx14IconSet.Cfvo = append(x14IconSet.Cfvo, &xlsx14Cfvo{\n\t\t\t\t\t\t\tType:   cfvo.Type,\n\t\t\t\t\t\t\tGte:    cfvo.Gte,\n\t\t\t\t\t\t\tF:      cfvo.F,\n\t\t\t\t\t\t\tExtLst: cfvo.ExtLst,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tx14CfRule.IconSet = x14IconSet\n\t\t\t}\n\t\t\tx14ConditionalFormatting.CfRule = append(x14ConditionalFormatting.CfRule, x14CfRule)\n\t\t}\n\t\tcondFmtBytes, _ := xml.Marshal(x14ConditionalFormatting)\n\t\tcondFmts.Content += string(condFmtBytes)\n\t}\n\treturn xml.Marshal(condFmts)\n}\n\n// drawCondFmtCellIs provides a function to create conditional formatting rule\n// for cell value (include between, not between, equal, not equal, greater\n// than and less than) by given priority, criteria type and format settings.\nfunc drawCondFmtCellIs(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\tc := &xlsxCfRule{\n\t\tPriority:   p + 1,\n\t\tStopIfTrue: format.StopIfTrue,\n\t\tType:       validType[format.Type],\n\t\tOperator:   ct,\n\t\tDxfID:      format.Format,\n\t}\n\t// \"between\" and \"not between\" criteria require 2 values.\n\tif ct == \"between\" || ct == \"notBetween\" {\n\t\tc.Formula = append(c.Formula, []string{format.MinValue, format.MaxValue}...)\n\t}\n\tif inStrSlice(cellIsCriteriaType, ct, true) != -1 {\n\t\tc.Formula = append(c.Formula, format.Value)\n\t}\n\treturn c, nil\n}\n\n// drawCondFmtTimePeriod provides a function to create conditional formatting\n// rule for time period by given priority, criteria type and format settings.\nfunc drawCondFmtTimePeriod(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\treturn &xlsxCfRule{\n\t\tPriority:   p + 1,\n\t\tStopIfTrue: format.StopIfTrue,\n\t\tType:       \"timePeriod\",\n\t\tFormula: []string{\n\t\t\tmap[string]string{\n\t\t\t\t\"yesterday\": fmt.Sprintf(\"FLOOR(%s,1)=TODAY()-1\", ref),\n\t\t\t\t\"today\":     fmt.Sprintf(\"FLOOR(%s,1)=TODAY()\", ref),\n\t\t\t\t\"tomorrow\":  fmt.Sprintf(\"FLOOR(%s,1)=TODAY()+1\", ref),\n\t\t\t\t\"last7Days\": fmt.Sprintf(\"AND(TODAY()-FLOOR(%[1]s,1)<=6,FLOOR(%[1]s,1)<=TODAY())\", ref),\n\t\t\t\t\"lastWeek\":  fmt.Sprintf(\"AND(TODAY()-ROUNDDOWN(%[1]s,0)>=(WEEKDAY(TODAY())),TODAY()-ROUNDDOWN(%[1]s,0)<(WEEKDAY(TODAY())+7))\", ref),\n\t\t\t\t\"thisWeek\":  fmt.Sprintf(\"AND(TODAY()-ROUNDDOWN(%[1]s,0)<=WEEKDAY(TODAY())-1,ROUNDDOWN(%[1]s,0)-TODAY()>=7-WEEKDAY(TODAY()))\", ref),\n\t\t\t\t\"nextWeek\":  fmt.Sprintf(\"AND(ROUNDDOWN(%[1]s,0)-TODAY()>(7-WEEKDAY(TODAY())),ROUNDDOWN(%[1]s,0)-TODAY()<(15-WEEKDAY(TODAY())))\", ref),\n\t\t\t\t\"lastMonth\": fmt.Sprintf(\"AND(MONTH(%[1]s)=MONTH(TODAY())-1,OR(YEAR(%[1]s)=YEAR(TODAY()),AND(MONTH(%[1]s)=1,YEAR(%[1]s)=YEAR(TODAY())-1)))\", ref),\n\t\t\t\t\"thisMonth\": fmt.Sprintf(\"AND(MONTH(%[1]s)=MONTH(TODAY()),YEAR(%[1]s)=YEAR(TODAY()))\", ref),\n\t\t\t\t\"nextMonth\": fmt.Sprintf(\"AND(MONTH(%[1]s)=MONTH(TODAY())+1,OR(YEAR(%[1]s)=YEAR(TODAY()),AND(MONTH(%[1]s)=12,YEAR(%[1]s)=YEAR(TODAY())+1)))\", ref),\n\t\t\t}[ct],\n\t\t},\n\t\tDxfID: format.Format,\n\t}, nil\n}\n\n// drawCondFmtText provides a function to create conditional formatting rule for\n// text cell values by given priority, criteria type and format settings.\nfunc drawCondFmtText(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\treturn &xlsxCfRule{\n\t\tPriority:   p + 1,\n\t\tStopIfTrue: format.StopIfTrue,\n\t\tType: map[string]string{\n\t\t\t\"containsText\": \"containsText\",\n\t\t\t\"notContains\":  \"notContainsText\",\n\t\t\t\"beginsWith\":   \"beginsWith\",\n\t\t\t\"endsWith\":     \"endsWith\",\n\t\t}[ct],\n\t\tText:     format.Value,\n\t\tOperator: ct,\n\t\tFormula: []string{\n\t\t\tmap[string]string{\n\t\t\t\t\"containsText\": fmt.Sprintf(\"NOT(ISERROR(SEARCH(\\\"%s\\\",%s)))\",\n\t\t\t\t\tstrings.NewReplacer(`\"`, `\"\"`).Replace(format.Value), ref),\n\t\t\t\t\"notContains\": fmt.Sprintf(\"ISERROR(SEARCH(\\\"%s\\\",%s))\",\n\t\t\t\t\tstrings.NewReplacer(`\"`, `\"\"`).Replace(format.Value), ref),\n\t\t\t\t\"beginsWith\": fmt.Sprintf(\"LEFT(%[2]s,LEN(\\\"%[1]s\\\"))=\\\"%[1]s\\\"\",\n\t\t\t\t\tstrings.NewReplacer(`\"`, `\"\"`).Replace(format.Value), ref),\n\t\t\t\t\"endsWith\": fmt.Sprintf(\"RIGHT(%[2]s,LEN(\\\"%[1]s\\\"))=\\\"%[1]s\\\"\",\n\t\t\t\t\tstrings.NewReplacer(`\"`, `\"\"`).Replace(format.Value), ref),\n\t\t\t}[ct],\n\t\t},\n\t\tDxfID: format.Format,\n\t}, nil\n}\n\n// drawCondFmtTop10 provides a function to create conditional formatting rule\n// for top N (default is top 10) by given priority, criteria type and format\n// settings.\nfunc drawCondFmtTop10(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\tc := &xlsxCfRule{\n\t\tPriority:   p + 1,\n\t\tStopIfTrue: format.StopIfTrue,\n\t\tBottom:     format.Type == \"bottom\",\n\t\tType:       validType[format.Type],\n\t\tRank:       10,\n\t\tDxfID:      format.Format,\n\t\tPercent:    format.Percent,\n\t}\n\tif rank, err := strconv.Atoi(format.Value); err == nil {\n\t\tc.Rank = rank\n\t}\n\treturn c, nil\n}\n\n// drawCondFmtAboveAverage provides a function to create conditional\n// formatting rule for above average and below average by given priority,\n// criteria type and format settings.\nfunc drawCondFmtAboveAverage(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\treturn &xlsxCfRule{\n\t\tPriority:     p + 1,\n\t\tStopIfTrue:   format.StopIfTrue,\n\t\tType:         validType[format.Type],\n\t\tAboveAverage: boolPtr(format.AboveAverage),\n\t\tDxfID:        format.Format,\n\t}, nil\n}\n\n// drawCondFmtDuplicateUniqueValues provides a function to create conditional\n// formatting rule for duplicate and unique values by given priority, criteria\n// type and format settings.\nfunc drawCondFmtDuplicateUniqueValues(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\treturn &xlsxCfRule{\n\t\tPriority:   p + 1,\n\t\tStopIfTrue: format.StopIfTrue,\n\t\tType:       validType[format.Type],\n\t\tDxfID:      format.Format,\n\t}, nil\n}\n\n// drawCondFmtColorScale provides a function to create conditional formatting\n// rule for color scale (include 2 color scale and 3 color scale) by given\n// priority, criteria type and format settings.\nfunc drawCondFmtColorScale(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\tminValue := format.MinValue\n\tif minValue == \"\" {\n\t\tminValue = \"0\"\n\t}\n\tmaxValue := format.MaxValue\n\tif maxValue == \"\" {\n\t\tmaxValue = \"0\"\n\t}\n\tmidValue := format.MidValue\n\tif midValue == \"\" {\n\t\tmidValue = \"50\"\n\t}\n\n\tc := &xlsxCfRule{\n\t\tPriority:   p + 1,\n\t\tStopIfTrue: format.StopIfTrue,\n\t\tType:       \"colorScale\",\n\t\tColorScale: &xlsxColorScale{\n\t\t\tCfvo: []*xlsxCfvo{\n\t\t\t\t{Type: format.MinType, Val: minValue},\n\t\t\t},\n\t\t\tColor: []*xlsxColor{\n\t\t\t\t{RGB: getPaletteColor(format.MinColor)},\n\t\t\t},\n\t\t},\n\t}\n\tif validType[format.Type] == \"3_color_scale\" {\n\t\tc.ColorScale.Cfvo = append(c.ColorScale.Cfvo, &xlsxCfvo{Type: format.MidType, Val: midValue})\n\t\tc.ColorScale.Color = append(c.ColorScale.Color, &xlsxColor{RGB: getPaletteColor(format.MidColor)})\n\t}\n\tc.ColorScale.Cfvo = append(c.ColorScale.Cfvo, &xlsxCfvo{Type: format.MaxType, Val: maxValue})\n\tc.ColorScale.Color = append(c.ColorScale.Color, &xlsxColor{RGB: getPaletteColor(format.MaxColor)})\n\treturn c, nil\n}\n\n// drawCondFmtDataBar provides a function to create conditional formatting\n// rule for data bar by given priority, criteria type and format settings.\nfunc drawCondFmtDataBar(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\tvar x14CfRule *xlsxX14CfRule\n\tvar extLst *xlsxExtLst\n\tif format.BarSolid || format.BarDirection == \"leftToRight\" || format.BarDirection == \"rightToLeft\" || format.BarBorderColor != \"\" {\n\t\textLst = &xlsxExtLst{Ext: fmt.Sprintf(`<ext uri=\"%s\" xmlns:x14=\"%s\"><x14:id>%s</x14:id></ext>`, ExtURIConditionalFormattingRuleID, NameSpaceSpreadSheetX14.Value, GUID)}\n\t\tx14CfRule = &xlsxX14CfRule{\n\t\t\tType: validType[format.Type],\n\t\t\tID:   GUID,\n\t\t\tDataBar: &xlsx14DataBar{\n\t\t\t\tMaxLength:         100,\n\t\t\t\tBorder:            format.BarBorderColor != \"\",\n\t\t\t\tGradient:          !format.BarSolid,\n\t\t\t\tDirection:         format.BarDirection,\n\t\t\t\tCfvo:              []*xlsxCfvo{{Type: \"autoMin\"}, {Type: \"autoMax\"}},\n\t\t\t\tNegativeFillColor: &xlsxColor{RGB: \"FFFF0000\"},\n\t\t\t\tAxisColor:         &xlsxColor{RGB: \"FFFF0000\"},\n\t\t\t},\n\t\t}\n\t\tif x14CfRule.DataBar.Border {\n\t\t\tx14CfRule.DataBar.BorderColor = &xlsxColor{RGB: getPaletteColor(format.BarBorderColor)}\n\t\t}\n\t}\n\treturn &xlsxCfRule{\n\t\tPriority:   p + 1,\n\t\tStopIfTrue: format.StopIfTrue,\n\t\tType:       validType[format.Type],\n\t\tDataBar: &xlsxDataBar{\n\t\t\tShowValue: boolPtr(!format.BarOnly),\n\t\t\tCfvo:      []*xlsxCfvo{{Type: format.MinType, Val: format.MinValue}, {Type: format.MaxType, Val: format.MaxValue}},\n\t\t\tColor:     []*xlsxColor{{RGB: getPaletteColor(format.BarColor)}},\n\t\t},\n\t\tExtLst: extLst,\n\t}, x14CfRule\n}\n\n// drawCondFmtExp provides a function to create conditional formatting rule\n// for expression by given priority, criteria type and format settings.\nfunc drawCondFmtExp(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\treturn &xlsxCfRule{\n\t\tPriority:   p + 1,\n\t\tStopIfTrue: format.StopIfTrue,\n\t\tType:       validType[format.Type],\n\t\tFormula:    []string{format.Criteria},\n\t\tDxfID:      format.Format,\n\t}, nil\n}\n\n// drawCondFmtErrors provides a function to create conditional formatting rule\n// for cells with errors by given priority, criteria type and format settings.\nfunc drawCondFmtErrors(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\treturn &xlsxCfRule{\n\t\tPriority:   p + 1,\n\t\tStopIfTrue: format.StopIfTrue,\n\t\tType:       validType[format.Type],\n\t\tFormula:    []string{fmt.Sprintf(\"ISERROR(%s)\", ref)},\n\t\tDxfID:      format.Format,\n\t}, nil\n}\n\n// drawCondFmtNoErrors provides a function to create conditional formatting rule\n// for cells without errors by given priority, criteria type and format settings.\nfunc drawCondFmtNoErrors(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\treturn &xlsxCfRule{\n\t\tPriority:   p + 1,\n\t\tStopIfTrue: format.StopIfTrue,\n\t\tType:       validType[format.Type],\n\t\tFormula:    []string{fmt.Sprintf(\"NOT(ISERROR(%s))\", ref)},\n\t\tDxfID:      format.Format,\n\t}, nil\n}\n\n// drawCondFmtBlanks provides a function to create conditional formatting rule\n// for blank cells by given priority, criteria type and format settings.\nfunc drawCondFmtBlanks(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\treturn &xlsxCfRule{\n\t\tPriority:   p + 1,\n\t\tStopIfTrue: format.StopIfTrue,\n\t\tType:       validType[format.Type],\n\t\tFormula:    []string{fmt.Sprintf(\"LEN(TRIM(%s))=0\", ref)},\n\t\tDxfID:      format.Format,\n\t}, nil\n}\n\n// drawCondFmtNoBlanks provides a function to create conditional formatting rule\n// for no blanks cells by given priority, criteria type and format settings.\nfunc drawCondFmtNoBlanks(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\treturn &xlsxCfRule{\n\t\tPriority:   p + 1,\n\t\tStopIfTrue: format.StopIfTrue,\n\t\tType:       validType[format.Type],\n\t\tFormula:    []string{fmt.Sprintf(\"LEN(TRIM(%s))>0\", ref)},\n\t\tDxfID:      format.Format,\n\t}, nil\n}\n\n// drawCondFmtIconSet provides a function to create conditional formatting rule\n// for icon set by given priority, criteria type and format settings.\nfunc drawCondFmtIconSet(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {\n\tif cfRule, ok := condFmtIconSetPresets[format.IconStyle]; ok {\n\t\tcfRule.Priority = p + 1\n\t\tcfRule.IconSet.IconSet = format.IconStyle\n\t\tcfRule.IconSet.Reverse = format.ReverseIcons\n\t\tcfRule.IconSet.ShowValue = boolPtr(!format.IconsOnly)\n\t\tcfRule.Type = validType[format.Type]\n\t\treturn cfRule, nil\n\t}\n\tif x14CfRule, ok := condFmtX14IconSetPresets[format.IconStyle]; ok {\n\t\tx14CfRule.Type = validType[format.Type]\n\t\tx14CfRule.Priority = p + 1\n\t\tx14CfRule.StopIfTrue = format.StopIfTrue\n\t\tx14CfRule.AboveAverage = boolPtr(format.AboveAverage)\n\t\tx14CfRule.Percent = format.Percent\n\t\tx14CfRule.ID = GUID\n\t\tx14CfRule.IconSet.IconSet = format.IconStyle\n\t\tx14CfRule.IconSet.Reverse = format.ReverseIcons\n\t\tx14CfRule.IconSet.ShowValue = boolPtr(!format.IconsOnly)\n\t\treturn nil, x14CfRule\n\t}\n\treturn nil, nil\n}\n\n// getPaletteColor provides a function to convert the RBG color by given\n// string.\nfunc getPaletteColor(color string) string {\n\treturn \"FF\" + strings.ReplaceAll(strings.ToUpper(color), \"#\", \"\")\n}\n\n// themeReader provides a function to get the pointer to the xl/theme/theme1.xml\n// structure after deserialization.\nfunc (f *File) themeReader() (*decodeTheme, error) {\n\tif _, ok := f.Pkg.Load(defaultXMLPathTheme); !ok {\n\t\treturn nil, nil\n\t}\n\ttheme := decodeTheme{}\n\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathTheme)))).\n\t\tDecode(&theme); err != nil && err != io.EOF {\n\t\treturn &theme, err\n\t}\n\treturn &theme, nil\n}\n\n// ThemeColor applied the color with tint value.\nfunc ThemeColor(baseColor string, tint float64) string {\n\tif tint == 0 {\n\t\treturn \"FF\" + baseColor\n\t}\n\tr, _ := strconv.ParseUint(baseColor[:2], 16, 64)\n\tg, _ := strconv.ParseUint(baseColor[2:4], 16, 64)\n\tb, _ := strconv.ParseUint(baseColor[4:6], 16, 64)\n\tvar h, s, l float64\n\tif r <= math.MaxUint8 && g <= math.MaxUint8 && b <= math.MaxUint8 {\n\t\th, s, l = RGBToHSL(uint8(r), uint8(g), uint8(b))\n\t}\n\tif tint < 0 {\n\t\tl *= 1 + tint\n\t} else {\n\t\tl = l*(1-tint) + (1 - (1 - tint))\n\t}\n\tbr, bg, bb := HSLToRGB(h, s, l)\n\treturn fmt.Sprintf(\"FF%02X%02X%02X\", br, bg, bb)\n}\n"
  },
  {
    "path": "styles_test.go",
    "content": "package excelize\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestStyleFill(t *testing.T) {\n\tcases := []struct {\n\t\tlabel      string\n\t\tformat     *Style\n\t\texpectFill bool\n\t}{{\n\t\tlabel:      \"no_fill\",\n\t\tformat:     &Style{Alignment: &Alignment{WrapText: true}},\n\t\texpectFill: false,\n\t}, {\n\t\tlabel:      \"fill\",\n\t\tformat:     &Style{Fill: Fill{Type: \"pattern\", Pattern: 1, Color: []string{\"000000\"}}},\n\t\texpectFill: true,\n\t}}\n\n\tfor _, testCase := range cases {\n\t\txl := NewFile()\n\t\tstyleID, err := xl.NewStyle(testCase.format)\n\t\tassert.NoError(t, err)\n\n\t\tstyles, err := xl.stylesReader()\n\t\tassert.NoError(t, err)\n\t\tstyle := styles.CellXfs.Xf[styleID]\n\t\tif testCase.expectFill {\n\t\t\tassert.NotEqual(t, *style.FillID, 0, testCase.label)\n\t\t} else {\n\t\t\tassert.Equal(t, *style.FillID, 0, testCase.label)\n\t\t}\n\t}\n\tf := NewFile()\n\tstyleID1, err := f.NewStyle(&Style{Fill: Fill{Type: \"pattern\", Pattern: 1, Color: []string{\"000000\"}}})\n\tassert.NoError(t, err)\n\tstyleID2, err := f.NewStyle(&Style{Fill: Fill{Type: \"pattern\", Pattern: 1, Color: []string{\"000000\"}}})\n\tassert.NoError(t, err)\n\tassert.Equal(t, styleID1, styleID2)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestStyleFill.xlsx\")))\n}\n\nfunc TestSetConditionalFormat(t *testing.T) {\n\tcases := []struct {\n\t\tlabel  string\n\t\tformat []ConditionalFormatOptions\n\t\trules  []*xlsxCfRule\n\t}{{\n\t\tlabel: \"3_color_scale\",\n\t\tformat: []ConditionalFormatOptions{{\n\t\t\tType:     \"3_color_scale\",\n\t\t\tCriteria: \"=\",\n\t\t\tMinType:  \"num\",\n\t\t\tMidType:  \"num\",\n\t\t\tMaxType:  \"num\",\n\t\t\tMinValue: \"-10\",\n\t\t\tMidValue: \"0\",\n\t\t\tMaxValue: \"10\",\n\t\t\tMinColor: \"ff0000\",\n\t\t\tMidColor: \"00ff00\",\n\t\t\tMaxColor: \"0000ff\",\n\t\t}},\n\t\trules: []*xlsxCfRule{{\n\t\t\tPriority: 1,\n\t\t\tType:     \"colorScale\",\n\t\t\tColorScale: &xlsxColorScale{\n\t\t\t\tCfvo: []*xlsxCfvo{{\n\t\t\t\t\tType: \"num\",\n\t\t\t\t\tVal:  \"-10\",\n\t\t\t\t}, {\n\t\t\t\t\tType: \"num\",\n\t\t\t\t\tVal:  \"0\",\n\t\t\t\t}, {\n\t\t\t\t\tType: \"num\",\n\t\t\t\t\tVal:  \"10\",\n\t\t\t\t}},\n\t\t\t\tColor: []*xlsxColor{{\n\t\t\t\t\tRGB: \"FFFF0000\",\n\t\t\t\t}, {\n\t\t\t\t\tRGB: \"FF00FF00\",\n\t\t\t\t}, {\n\t\t\t\t\tRGB: \"FF0000FF\",\n\t\t\t\t}},\n\t\t\t},\n\t\t}},\n\t}, {\n\t\tlabel: \"3_color_scale default min/mid/max\",\n\t\tformat: []ConditionalFormatOptions{{\n\t\t\tType:     \"3_color_scale\",\n\t\t\tCriteria: \"=\",\n\t\t\tMinType:  \"num\",\n\t\t\tMidType:  \"num\",\n\t\t\tMaxType:  \"num\",\n\t\t\tMinColor: \"ff0000\",\n\t\t\tMidColor: \"00ff00\",\n\t\t\tMaxColor: \"0000ff\",\n\t\t}},\n\t\trules: []*xlsxCfRule{{\n\t\t\tPriority: 1,\n\t\t\tType:     \"colorScale\",\n\t\t\tColorScale: &xlsxColorScale{\n\t\t\t\tCfvo: []*xlsxCfvo{{\n\t\t\t\t\tType: \"num\",\n\t\t\t\t\tVal:  \"0\",\n\t\t\t\t}, {\n\t\t\t\t\tType: \"num\",\n\t\t\t\t\tVal:  \"50\",\n\t\t\t\t}, {\n\t\t\t\t\tType: \"num\",\n\t\t\t\t\tVal:  \"0\",\n\t\t\t\t}},\n\t\t\t\tColor: []*xlsxColor{{\n\t\t\t\t\tRGB: \"FFFF0000\",\n\t\t\t\t}, {\n\t\t\t\t\tRGB: \"FF00FF00\",\n\t\t\t\t}, {\n\t\t\t\t\tRGB: \"FF0000FF\",\n\t\t\t\t}},\n\t\t\t},\n\t\t}},\n\t}, {\n\t\tlabel: \"2_color_scale default min/max\",\n\t\tformat: []ConditionalFormatOptions{{\n\t\t\tType:     \"2_color_scale\",\n\t\t\tCriteria: \"=\",\n\t\t\tMinType:  \"num\",\n\t\t\tMaxType:  \"num\",\n\t\t\tMinColor: \"ff0000\",\n\t\t\tMaxColor: \"0000ff\",\n\t\t}},\n\t\trules: []*xlsxCfRule{{\n\t\t\tPriority: 1,\n\t\t\tType:     \"colorScale\",\n\t\t\tColorScale: &xlsxColorScale{\n\t\t\t\tCfvo: []*xlsxCfvo{{\n\t\t\t\t\tType: \"num\",\n\t\t\t\t\tVal:  \"0\",\n\t\t\t\t}, {\n\t\t\t\t\tType: \"num\",\n\t\t\t\t\tVal:  \"0\",\n\t\t\t\t}},\n\t\t\t\tColor: []*xlsxColor{{\n\t\t\t\t\tRGB: \"FFFF0000\",\n\t\t\t\t}, {\n\t\t\t\t\tRGB: \"FF0000FF\",\n\t\t\t\t}},\n\t\t\t},\n\t\t}},\n\t}}\n\n\tfor _, testCase := range cases {\n\t\tf := NewFile()\n\t\tconst sheet = \"Sheet1\"\n\t\tconst rangeRef = \"A1:A1\"\n\t\tassert.NoError(t, f.SetConditionalFormat(sheet, rangeRef, testCase.format))\n\t\tws, err := f.workSheetReader(sheet)\n\t\tassert.NoError(t, err)\n\t\tcf := ws.ConditionalFormatting\n\t\tassert.Len(t, cf, 1, testCase.label)\n\t\tassert.Len(t, cf[0].CfRule, 1, testCase.label)\n\t\tassert.Equal(t, rangeRef, cf[0].SQRef, testCase.label)\n\t\tassert.EqualValues(t, testCase.rules, cf[0].CfRule, testCase.label)\n\t}\n\t// Test creating a conditional format with a solid color data bar style\n\tf := NewFile()\n\tcondFmts := []ConditionalFormatOptions{\n\t\t{Type: \"data_bar\", BarColor: \"#A9D08E\", BarSolid: true, Format: intPtr(0), Criteria: \"=\", MinType: \"min\", MaxType: \"max\"},\n\t}\n\tfor _, ref := range []string{\"A1:A2\", \"B1:B2\"} {\n\t\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", ref, condFmts))\n\t}\n\tf = NewFile()\n\t// Test creating a conditional format without cell reference\n\tassert.Equal(t, ErrParameterRequired, f.SetConditionalFormat(\"Sheet1\", \"\", nil))\n\t// Test creating a conditional format with invalid cell reference\n\tassert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat(\"Sheet1\", \"A1:A2:A3\", nil))\n\t// Test creating a conditional format with existing extension lists\n\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: fmt.Sprintf(`<ext uri=\"%s\"><x14:slicerList /></ext><ext uri=\"%s\"><x14:sparklineGroups /></ext>`, ExtURISlicerListX14, ExtURISparklineGroups)}\n\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"A1:A2\", []ConditionalFormatOptions{{Type: \"data_bar\", Criteria: \"=\", MinType: \"min\", MaxType: \"max\", BarBorderColor: \"#0000FF\", BarColor: \"#638EC6\", BarSolid: true}}))\n\tf = NewFile()\n\t// Test creating a conditional format with invalid extension list characters\n\tws, ok = f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\tassert.True(t, ok)\n\tws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: \"<ext><x14:conditionalFormattings></x14:conditionalFormatting></x14:conditionalFormattings></ext>\"}\n\tassert.EqualError(t, f.SetConditionalFormat(\"Sheet1\", \"A1:A2\", condFmts), \"XML syntax error on line 1: element <conditionalFormattings> closed by </conditionalFormatting>\")\n\t// Test creating a conditional format with invalid icon set style\n\tassert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat(\"Sheet1\", \"A1:A2\", []ConditionalFormatOptions{{Type: \"icon_set\", IconStyle: \"unknown\"}}))\n\t// Test unsupported conditional formatting rule types\n\tassert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat(\"Sheet1\", \"A1\", []ConditionalFormatOptions{{Type: \"unsupported\"}}))\n\n\tt.Run(\"multi_conditional_formatting_rules_priority\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tvar condFmts []ConditionalFormatOptions\n\t\tfor _, color := range []string{\n\t\t\t\"#264B96\", // Blue\n\t\t\t\"#F9A73E\", // Yellow\n\t\t\t\"#006F3C\", // Green\n\t\t} {\n\t\t\tcondFmts = append(condFmts, ConditionalFormatOptions{\n\t\t\t\tType:     \"data_bar\",\n\t\t\t\tCriteria: \"=\",\n\t\t\t\tMinType:  \"num\",\n\t\t\t\tMaxType:  \"num\",\n\t\t\t\tMinValue: \"0\",\n\t\t\t\tMaxValue: \"5\",\n\t\t\t\tBarColor: color,\n\t\t\t\tBarSolid: true,\n\t\t\t})\n\t\t}\n\t\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"A1:A5\", condFmts))\n\t\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"B1:B5\", condFmts))\n\t\tfor r := 1; r <= 20; r++ {\n\t\t\tcell, err := CoordinatesToCellName(1, r)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", cell, r))\n\t\t\tcell, err = CoordinatesToCellName(2, r)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", cell, r))\n\t\t}\n\t\tws, ok := f.Sheet.Load(\"xl/worksheets/sheet1.xml\")\n\t\tassert.True(t, ok)\n\t\tvar priorities []int\n\t\texpected := []int{1, 2, 3, 4, 5, 6}\n\t\tfor _, condFmt := range ws.(*xlsxWorksheet).ConditionalFormatting {\n\t\t\tfor _, rule := range condFmt.CfRule {\n\t\t\t\tpriorities = append(priorities, rule.Priority)\n\t\t\t}\n\t\t}\n\t\tassert.Equal(t, expected, priorities)\n\t\tassert.NoError(t, f.Close())\n\t})\n}\n\nfunc TestGetConditionalFormats(t *testing.T) {\n\tf := NewFile()\n\tidx, err := f.NewConditionalStyle(&Style{Fill: Fill{Type: \"pattern\", Color: []string{\"FEC7CE\"}, Pattern: 1}})\n\tassert.NoError(t, err)\n\tfor idx, format := range [][]ConditionalFormatOptions{\n\t\t{{Type: \"cell\", Format: &idx, Criteria: \"greater than\", Value: \"6\"}},\n\t\t{{Type: \"cell\", Format: &idx, Criteria: \"between\", MinValue: \"6\", MaxValue: \"8\"}},\n\t\t{{Type: \"time_period\", Format: &idx, Criteria: \"yesterday\"}},\n\t\t{{Type: \"time_period\", Format: &idx, Criteria: \"today\"}},\n\t\t{{Type: \"time_period\", Format: &idx, Criteria: \"tomorrow\"}},\n\t\t{{Type: \"time_period\", Format: &idx, Criteria: \"last 7 days\"}},\n\t\t{{Type: \"time_period\", Format: &idx, Criteria: \"last week\"}},\n\t\t{{Type: \"time_period\", Format: &idx, Criteria: \"this week\"}},\n\t\t{{Type: \"time_period\", Format: &idx, Criteria: \"continue week\"}},\n\t\t{{Type: \"time_period\", Format: &idx, Criteria: \"last month\"}},\n\t\t{{Type: \"time_period\", Format: &idx, Criteria: \"this month\"}},\n\t\t{{Type: \"time_period\", Format: &idx, Criteria: \"continue month\"}},\n\t\t{{Type: \"text\", Format: &idx, Criteria: \"containing\", Value: \"~!@#$%^&*()_+{}|:<>?\\\"';\"}},\n\t\t{{Type: \"text\", Format: &idx, Criteria: \"not containing\", Value: \"text\"}},\n\t\t{{Type: \"text\", Format: &idx, Criteria: \"begins with\", Value: \"prefix\"}},\n\t\t{{Type: \"text\", Format: &idx, Criteria: \"ends with\", Value: \"suffix\"}},\n\t\t{{Type: \"top\", Format: &idx, Criteria: \"=\", Value: \"6\"}},\n\t\t{{Type: \"bottom\", Format: &idx, Criteria: \"=\", Value: \"6\"}},\n\t\t{{Type: \"average\", AboveAverage: true, Format: &idx, Criteria: \"=\"}},\n\t\t{{Type: \"duplicate\", Format: &idx, Criteria: \"=\"}},\n\t\t{{Type: \"unique\", Format: &idx, Criteria: \"=\"}},\n\t\t{{Type: \"3_color_scale\", Criteria: \"=\", MinType: \"num\", MidType: \"num\", MaxType: \"num\", MinValue: \"-10\", MidValue: \"50\", MaxValue: \"10\", MinColor: \"#FF0000\", MidColor: \"#00FF00\", MaxColor: \"#0000FF\"}},\n\t\t{{Type: \"2_color_scale\", Criteria: \"=\", MinType: \"num\", MaxType: \"num\", MinColor: \"#FF0000\", MaxColor: \"#0000FF\"}},\n\t\t{{Type: \"data_bar\", Criteria: \"=\", MinType: \"num\", MaxType: \"num\", MinValue: \"-10\", MaxValue: \"10\", BarBorderColor: \"#0000FF\", BarColor: \"#638EC6\", BarOnly: true, BarSolid: true, StopIfTrue: true}},\n\t\t{{Type: \"data_bar\", Criteria: \"=\", MinType: \"min\", MaxType: \"max\", BarBorderColor: \"#0000FF\", BarColor: \"#638EC6\", BarDirection: \"rightToLeft\", BarOnly: true, BarSolid: true, StopIfTrue: true}},\n\t\t{{Type: \"formula\", Format: &idx, Criteria: \"1\"}},\n\t\t{{Type: \"blanks\", Format: &idx}},\n\t\t{{Type: \"no_blanks\", Format: &idx}},\n\t\t{{Type: \"errors\", Format: &idx}},\n\t\t{{Type: \"no_errors\", Format: &idx}},\n\t\t{{Type: \"icon_set\", IconStyle: \"3Arrows\", ReverseIcons: true, IconsOnly: true}},\n\t\t{{Type: \"icon_set\", IconStyle: \"3Stars\", ReverseIcons: true, IconsOnly: true}},\n\t\t{{Type: \"icon_set\", IconStyle: \"3Triangles\", ReverseIcons: true, IconsOnly: true}},\n\t\t{{Type: \"icon_set\", IconStyle: \"5Boxes\", ReverseIcons: true, IconsOnly: true}},\n\t} {\n\t\tcol, err := ColumnNumberToName(idx + 2)\n\t\tassert.NoError(t, err)\n\t\terr = f.SetConditionalFormat(\"Sheet1\", fmt.Sprintf(\"%s1:%s10\", col, col), format)\n\t\tassert.NoError(t, err)\n\t\topts, err := f.GetConditionalFormats(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, format, opts[fmt.Sprintf(\"%s1:%s10\", col, col)])\n\t}\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestGetConditionalFormats.xlsx\")))\n\t// Test unset all conditional formats\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestGetConditionalFormats.xlsx\"))\n\tassert.NoError(t, f.AddSparkline(\"Sheet1\", &SparklineOptions{\n\t\tLocation: []string{\"C1\"},\n\t\tRange:    []string{\"Sheet1!A1:B1\"},\n\t}))\n\tfor _, rangeRef := range []string{\"Y1:Y10\", \"Z1:Z10\", \"AG1:AG10\", \"AH1:AH10\", \"AI1:AI10\"} {\n\t\tassert.NoError(t, f.UnsetConditionalFormat(\"Sheet1\", rangeRef))\n\t}\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.Close())\n\t// Test unset conditional formats with invalid extension list characters\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestGetConditionalFormats.xlsx\"))\n\tassert.NoError(t, err)\n\tw, err := f.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n\tw.ExtLst = &xlsxExtLst{Ext: fmt.Sprintf(`<ext uri=\"%s\"><x14:conditionalFormattings></ext>`, ExtURIConditionalFormattings)}\n\tassert.EqualError(t, f.UnsetConditionalFormat(\"Sheet1\", \"Y1:Y10\"), \"XML syntax error on line 1: element <conditionalFormattings> closed by </ext>\")\n\tassert.NoError(t, f.Close())\n\t// Test get multiple conditional formats\n\tf = NewFile()\n\texpected := []ConditionalFormatOptions{\n\t\t{Type: \"data_bar\", Criteria: \"=\", MinType: \"num\", MaxType: \"num\", MinValue: \"-10\", MaxValue: \"10\", BarBorderColor: \"#0000FF\", BarColor: \"#638EC6\", BarOnly: true, BarSolid: true, StopIfTrue: true},\n\t\t{Type: \"data_bar\", Criteria: \"=\", MinType: \"min\", MaxType: \"max\", BarBorderColor: \"#0000FF\", BarColor: \"#638EC6\", BarDirection: \"rightToLeft\", BarOnly: true, BarSolid: false, StopIfTrue: true},\n\t}\n\terr = f.SetConditionalFormat(\"Sheet1\", \"A2:A1,B:B,2:2\", expected)\n\tassert.NoError(t, err)\n\topts, err := f.GetConditionalFormats(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected, opts[\"A2:A1 B1:B1048576 A2:XFD2\"])\n\n\tf = NewFile()\n\t// Test get conditional formats on no exists worksheet\n\t_, err = f.GetConditionalFormats(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test get conditional formats with invalid sheet name\n\t_, err = f.GetConditionalFormats(\"Sheet:1\")\n\tassert.Equal(t, ErrSheetNameInvalid, err)\n\t// Test get conditional formats with invalid extension list characters\n\tws, err := f.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n\tws.ExtLst = &xlsxExtLst{Ext: fmt.Sprintf(`<ext uri=\"%s\"><x14:conditionalFormattings></ext>`, ExtURIConditionalFormattings)}\n\t_, err = f.GetConditionalFormats(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: element <conditionalFormattings> closed by </ext>\")\n}\n\nfunc TestUnsetConditionalFormat(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", 7))\n\tassert.NoError(t, f.UnsetConditionalFormat(\"Sheet1\", \"A1:A10\"))\n\tformat, err := f.NewConditionalStyle(&Style{Font: &Font{Color: \"9A0511\"}, Fill: Fill{Type: \"pattern\", Color: []string{\"FEC7CE\"}, Pattern: 1}})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"A1:A10\", []ConditionalFormatOptions{{Type: \"cell\", Criteria: \">\", Format: &format, Value: \"6\"}}))\n\tassert.NoError(t, f.UnsetConditionalFormat(\"Sheet1\", \"A1:A10\"))\n\t// Test unset conditional format with invalid range\n\tassert.Equal(t, f.UnsetConditionalFormat(\"Sheet1\", \"A\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")))\n\t// Test unset conditional format on not exists worksheet\n\tassert.EqualError(t, f.UnsetConditionalFormat(\"SheetN\", \"A1:A10\"), \"sheet SheetN does not exist\")\n\t// Test unset conditional format with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.UnsetConditionalFormat(\"Sheet:1\", \"A1:A10\"))\n\t// Test unset conditional format from extLst\n\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"B1:B10\", []ConditionalFormatOptions{{Type: \"icon_set\", IconStyle: \"3Stars\"}}))\n\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"C1:C10\", []ConditionalFormatOptions{{Type: \"icon_set\", IconStyle: \"5Boxes\"}}))\n\tcondFmts, err := f.GetConditionalFormats(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, condFmts, 2)\n\t// Unset the first extLst conditional format\n\tassert.NoError(t, f.UnsetConditionalFormat(\"Sheet1\", \"B1:B10\"))\n\tcondFmts, err = f.GetConditionalFormats(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, condFmts, 1)\n\tassert.NotNil(t, condFmts[\"C1:C10\"])\n\t// Unset the last extLst conditional format\n\tassert.NoError(t, f.UnsetConditionalFormat(\"Sheet1\", \"C1:C10\"))\n\tcondFmts, err = f.GetConditionalFormats(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, condFmts, 0)\n\t// Save spreadsheet by the given path\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestUnsetConditionalFormat.xlsx\")))\n\t// Test get and unset conditional format with invalid sqref value\n\tf = NewFile()\n\tws, err := f.workSheetReader(\"Sheet1\")\n\tassert.NoError(t, err)\n\tws.ConditionalFormatting = []*xlsxConditionalFormatting{{SQRef: \"\"}}\n\t_, err = f.GetConditionalFormats(\"Sheet1\")\n\tassert.Equal(t, ErrParameterRequired, err)\n\tws.ConditionalFormatting = []*xlsxConditionalFormatting{{SQRef: \"A\"}}\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.UnsetConditionalFormat(\"Sheet1\", \"A1\"))\n\t// Test unset conditional formats with invalid extension list characters\n\tws.ExtLst = &xlsxExtLst{Ext: fmt.Sprintf(`<ext uri=\"%s\"><x14:conditionalFormattings>\n\t<x14:conditionalFormatting><xm:sqref>A</xm:sqref></x14:conditionalFormatting></x14:conditionalFormattings></ext>`, ExtURIConditionalFormattings)}\n\tassert.Equal(t, f.UnsetConditionalFormat(\"Sheet1\", \"A1\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")))\n\n\tt.Run(\"with_unordered_sqref\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tcondFmt := []ConditionalFormatOptions{{Type: \"cell\", Criteria: \"greater than\", Value: \"6\"}}\n\t\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"A5:A10 A15:A20 A3:A4\", condFmt))\n\t\tassert.NoError(t, f.UnsetConditionalFormat(\"Sheet1\", \"A7\"))\n\t\tcondFmts, err := f.GetConditionalFormats(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, condFmts, 1)\n\t\tassert.Equal(t, condFmt, condFmts[\"A3:A6 A8:A10 A15:A20\"])\n\t})\n\n\tt.Run(\"with_unordered_sqref_in_extLst\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tcondFmt := []ConditionalFormatOptions{{Type: \"icon_set\", IconStyle: \"3Stars\"}}\n\t\tassert.NoError(t, f.SetConditionalFormat(\"Sheet1\", \"A5:A10 A15:A20 A3:A4\", condFmt))\n\t\tassert.NoError(t, f.UnsetConditionalFormat(\"Sheet1\", \"A7\"))\n\t\tcondFmts, err := f.GetConditionalFormats(\"Sheet1\")\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, condFmts, 1)\n\t\tassert.Equal(t, condFmt, condFmts[\"A3:A6 A8:A10 A15:A20\"])\n\t})\n}\n\nfunc TestNewStyle(t *testing.T) {\n\tf := NewFile()\n\tfor i := range 16 {\n\t\t_, err := f.NewStyle(&Style{\n\t\t\tFill: Fill{Type: \"gradient\", Color: []string{\"FFFFFF\", \"4E71BE\"}, Shading: i},\n\t\t})\n\t\tassert.NoError(t, err)\n\t}\n\t_, err := f.NewStyle(&Style{\n\t\tFill: Fill{Type: \"solid\"},\n\t})\n\tassert.Equal(t, ErrFillType, err)\n\t_, err = f.NewStyle(&Style{\n\t\tFill: Fill{Type: \"gradient\", Color: []string{\"FFFFFF\"}, Shading: 0},\n\t})\n\tassert.Equal(t, ErrFillGradientColor, err)\n\t_, err = f.NewStyle(&Style{\n\t\tFill: Fill{Type: \"gradient\", Color: []string{\"FFFFFF\", \"4E71BE\"}, Shading: 17},\n\t})\n\tassert.Equal(t, ErrFillGradientShading, err)\n\t_, err = f.NewStyle(&Style{\n\t\tFill: Fill{Type: \"pattern\", Color: []string{\"FFFFFF\", \"4E71BE\"}, Shading: 0},\n\t})\n\tassert.Equal(t, ErrFillPatternColor, err)\n\t_, err = f.NewStyle(&Style{\n\t\tFill: Fill{Type: \"pattern\", Pattern: 19},\n\t})\n\tassert.Equal(t, ErrFillPattern, err)\n\tf = NewFile()\n\tstyleID, err := f.NewStyle(&Style{Font: &Font{Bold: true, Italic: true, Family: \"Times New Roman\", Size: 36, Color: \"777777\"}})\n\tassert.NoError(t, err)\n\tstyles, err := f.stylesReader()\n\tassert.NoError(t, err)\n\tfontID := styles.CellXfs.Xf[styleID].FontID\n\tfont := styles.Fonts.Font[*fontID]\n\tassert.Contains(t, *font.Name.Val, \"Times New Roman\", \"Stored font should contain font name\")\n\tassert.Equal(t, 2, styles.CellXfs.Count, \"Should have 2 styles\")\n\t_, err = f.NewStyle(&Style{})\n\tassert.NoError(t, err)\n\t_, err = f.NewStyle(nil)\n\tassert.NoError(t, err)\n\n\t// Test gradient fills\n\tf = NewFile()\n\tstyleID1, err := f.NewStyle(&Style{Fill: Fill{Type: \"gradient\", Color: []string{\"FFFFFF\", \"4E71BE\"}, Shading: 1, Pattern: 1}})\n\tassert.NoError(t, err)\n\tstyleID2, err := f.NewStyle(&Style{Fill: Fill{Type: \"gradient\", Color: []string{\"FF0000\", \"4E71BE\"}, Shading: 1, Pattern: 1}})\n\tassert.NoError(t, err)\n\tassert.NotEqual(t, styleID1, styleID2)\n\n\tvar exp string\n\tf = NewFile()\n\t_, err = f.NewStyle(&Style{CustomNumFmt: &exp})\n\tassert.Equal(t, ErrCustomNumFmt, err)\n\t_, err = f.NewStyle(&Style{Font: &Font{Family: strings.Repeat(\"s\", MaxFontFamilyLength+1)}})\n\tassert.Equal(t, ErrFontLength, err)\n\t_, err = f.NewStyle(&Style{Font: &Font{Size: MaxFontSize + 1}})\n\tassert.Equal(t, ErrFontSize, err)\n\n\t// Test create numeric custom style\n\tnumFmt := \"####;####\"\n\tf.Styles.NumFmts = nil\n\tstyleID, err = f.NewStyle(&Style{\n\t\tCustomNumFmt: &numFmt,\n\t})\n\tassert.NoError(t, err)\n\tassert.Equal(t, 1, styleID)\n\n\tassert.NotNil(t, f.Styles)\n\tassert.NotNil(t, f.Styles.CellXfs)\n\tassert.NotNil(t, f.Styles.CellXfs.Xf)\n\n\tnf := f.Styles.CellXfs.Xf[styleID]\n\tassert.Equal(t, 164, *nf.NumFmtID)\n\n\t// Test create currency custom style\n\tf.Styles.NumFmts = nil\n\tstyleID, err = f.NewStyle(&Style{\n\t\tNumFmt: 32, // must not be in currencyNumFmt\n\t})\n\tassert.NoError(t, err)\n\tassert.Equal(t, 2, styleID)\n\n\tassert.NotNil(t, f.Styles)\n\tassert.NotNil(t, f.Styles.CellXfs)\n\tassert.NotNil(t, f.Styles.CellXfs.Xf)\n\n\tnf = f.Styles.CellXfs.Xf[styleID]\n\tassert.Equal(t, 32, *nf.NumFmtID)\n\n\t// Test set build-in scientific number format\n\tstyleID, err = f.NewStyle(&Style{NumFmt: 11})\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A1\", \"B1\", styleID))\n\tassert.NoError(t, f.SetSheetRow(\"Sheet1\", \"A1\", &[]float64{1.23, 1.234}))\n\trows, err := f.GetRows(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, [][]string{{\"1.23E+00\", \"1.23E+00\"}}, rows)\n\n\tf = NewFile()\n\t// Test currency number format\n\tcustomNumFmt := \"[$$-409]#,##0.00\"\n\tstyle1, err := f.NewStyle(&Style{CustomNumFmt: &customNumFmt})\n\tassert.NoError(t, err)\n\tstyle2, err := f.NewStyle(&Style{NumFmt: 165})\n\tassert.NoError(t, err)\n\tassert.Equal(t, style1, style2)\n\n\tstyle3, err := f.NewStyle(&Style{NumFmt: 166})\n\tassert.NoError(t, err)\n\tassert.Equal(t, 2, style3)\n\n\tf = NewFile()\n\tf.Styles.NumFmts = nil\n\tf.Styles.CellXfs.Xf = nil\n\tstyle4, err := f.NewStyle(&Style{NumFmt: 160})\n\tassert.NoError(t, err)\n\tassert.Equal(t, 0, style4)\n\n\tf = NewFile()\n\tf.Styles.NumFmts = nil\n\tf.Styles.CellXfs.Xf = nil\n\tstyle5, err := f.NewStyle(&Style{NumFmt: 160})\n\tassert.NoError(t, err)\n\tassert.Equal(t, 0, style5)\n\n\t// Test create style with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\t_, err = f.NewStyle(&Style{NumFmt: 165})\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test create cell styles reach maximum\n\tf = NewFile()\n\tf.Styles.CellXfs.Xf = make([]xlsxXf, MaxCellStyles)\n\tf.Styles.CellXfs.Count = MaxCellStyles\n\t_, err = f.NewStyle(&Style{NumFmt: 0})\n\tassert.Equal(t, ErrCellStyles, err)\n\n\tt.Run(\"for_create_new_style_with_font_charset\", func(t *testing.T) {\n\t\tf, charset := NewFile(), 178\n\t\tstyle := &Style{Font: &Font{\n\t\t\tFamily:  \"B Titr\",\n\t\t\tSize:    12,\n\t\t\tColor:   \"000000\",\n\t\t\tCharset: &charset,\n\t\t}}\n\t\tstyleID, err := f.NewStyle(style)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, 1, styleID)\n\t\ts, err := f.GetStyle(styleID)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, style.Font, s.Font)\n\t\ttext := \"\\u0627\\u06cc\\u0646\\u0020\\u06cc\\u06a9\\u0020\\u0645\\u062a\\u0646\\u0020\\u0622\\u0632\\u0645\\u0627\\u06cc\\u0634\\u06cc\\u0020\\u0627\\u0633\\u062a\\u002e\"\n\t\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", text))\n\t\tassert.NoError(t, f.SetCellStyle(\"Sheet1\", \"A1\", \"A1\", styleID))\n\t\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestSetFontCharset.xlsx\")))\n\t})\n\n\tt.Run(\"for_recreate_default_style\", func(t *testing.T) {\n\t\tf := NewFile()\n\t\tstyle, err := f.GetStyle(0)\n\t\tassert.NoError(t, err)\n\t\tstyleID, err := f.NewStyle(style)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, 0, styleID)\n\t})\n}\n\nfunc TestConditionalStyle(t *testing.T) {\n\tf := NewFile()\n\texpected := &Style{Protection: &Protection{Hidden: true, Locked: true}}\n\tidx, err := f.NewConditionalStyle(expected)\n\tassert.NoError(t, err)\n\tstyle, err := f.GetConditionalStyle(idx)\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected, style)\n\t_, err = f.NewConditionalStyle(&Style{DecimalPlaces: intPtr(4), NumFmt: 165, NegRed: true})\n\tassert.NoError(t, err)\n\t_, err = f.NewConditionalStyle(&Style{DecimalPlaces: intPtr(-1)})\n\tassert.NoError(t, err)\n\texpected = &Style{NumFmt: 1}\n\tidx, err = f.NewConditionalStyle(expected)\n\tassert.NoError(t, err)\n\tstyle, err = f.GetConditionalStyle(idx)\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected.NumFmt, style.NumFmt)\n\tassert.Zero(t, *style.DecimalPlaces)\n\t_, err = f.NewConditionalStyle(&Style{NumFmt: 27})\n\tassert.NoError(t, err)\n\tnumFmt := \"general\"\n\t_, err = f.NewConditionalStyle(&Style{CustomNumFmt: &numFmt})\n\tassert.NoError(t, err)\n\tnumFmt1 := \"0.00\"\n\t_, err = f.NewConditionalStyle(&Style{CustomNumFmt: &numFmt1})\n\tassert.NoError(t, err)\n\t// Test create conditional style with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\t_, err = f.NewConditionalStyle(&Style{Font: &Font{Color: \"9A0511\"}, Fill: Fill{Type: \"pattern\", Color: []string{\"FEC7CE\"}, Pattern: 1}})\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get conditional style with invalid style index\n\t_, err = f.GetConditionalStyle(1)\n\tassert.Equal(t, newInvalidStyleID(1), err)\n\t// Test get conditional style with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\t_, err = f.GetConditionalStyle(1)\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\n\tf = NewFile()\n\t// Test get conditional style with background color and empty pattern type\n\tidx, err = f.NewConditionalStyle(&Style{Fill: Fill{Type: \"pattern\", Color: []string{\"FEC7CE\"}, Pattern: 1}})\n\tassert.NoError(t, err)\n\tf.Styles.Dxfs.Dxfs[0].Fill.PatternFill.PatternType = \"\"\n\tf.Styles.Dxfs.Dxfs[0].Fill.PatternFill.FgColor = nil\n\tf.Styles.Dxfs.Dxfs[0].Fill.PatternFill.BgColor = &xlsxColor{Theme: intPtr(6)}\n\tstyle, err = f.GetConditionalStyle(idx)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"pattern\", style.Fill.Type)\n\tassert.Equal(t, []string{\"A5A5A5\"}, style.Fill.Color)\n}\n\nfunc TestGetDefaultFont(t *testing.T) {\n\tf := NewFile()\n\ts, err := f.GetDefaultFont()\n\tassert.NoError(t, err)\n\tassert.Equal(t, s, \"Calibri\", \"Default font should be Calibri\")\n\t// Test get default font with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\t_, err = f.GetDefaultFont()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestSetDefaultFont(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetDefaultFont(\"Arial\"))\n\tstyles, err := f.stylesReader()\n\tassert.NoError(t, err)\n\ts, err := f.GetDefaultFont()\n\tassert.NoError(t, err)\n\tassert.Equal(t, s, \"Arial\", \"Default font should change to Arial\")\n\tassert.Equal(t, *styles.CellStyles.CellStyle[0].CustomBuiltIn, true)\n\t// Test set default font with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetDefaultFont(\"Arial\"), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestStylesReader(t *testing.T) {\n\tf := NewFile()\n\t// Test read styles with unsupported charset\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\tstyles, err := f.stylesReader()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.EqualValues(t, new(xlsxStyleSheet), styles)\n}\n\nfunc TestThemeReader(t *testing.T) {\n\tf := NewFile()\n\t// Test read theme with unsupported charset\n\tf.Pkg.Store(defaultXMLPathTheme, MacintoshCyrillicCharset)\n\ttheme, err := f.themeReader()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.EqualValues(t, &decodeTheme{}, theme)\n}\n\nfunc TestSetCellStyle(t *testing.T) {\n\tf := NewFile()\n\t// Test set cell style on not exists worksheet\n\tassert.EqualError(t, f.SetCellStyle(\"SheetN\", \"A1\", \"A2\", 1), \"sheet SheetN does not exist\")\n\t// Test set cell style with invalid style ID\n\tassert.Equal(t, newInvalidStyleID(-1), f.SetCellStyle(\"Sheet1\", \"A1\", \"A2\", -1))\n\t// Test set cell style with not exists style ID\n\tassert.Equal(t, newInvalidStyleID(10), f.SetCellStyle(\"Sheet1\", \"A1\", \"A2\", 10))\n\t// Test set cell style with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetCellStyle(\"Sheet1\", \"A1\", \"A2\", 1), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestGetStyleID(t *testing.T) {\n\tf := NewFile()\n\tstyleID, err := f.getStyleID(&xlsxStyleSheet{}, nil)\n\tassert.NoError(t, err)\n\tassert.Equal(t, -1, styleID)\n}\n\nfunc TestGetFillID(t *testing.T) {\n\tstyles, err := NewFile().stylesReader()\n\tassert.NoError(t, err)\n\tassert.Equal(t, -1, getFillID(styles, &Style{Fill: Fill{Type: \"unknown\"}}))\n}\n\nfunc TestThemeColor(t *testing.T) {\n\tfor _, clr := range [][]string{\n\t\t{\"FF000000\", ThemeColor(\"000000\", -0.1)},\n\t\t{\"FF000000\", ThemeColor(\"000000\", 0)},\n\t\t{\"FF33FF33\", ThemeColor(\"00FF00\", 0.2)},\n\t\t{\"FFFFFFFF\", ThemeColor(\"000000\", 1)},\n\t\t{\"FFFFFFFF\", ThemeColor(strings.Repeat(string(rune(math.MaxUint8+1)), 6), 1)},\n\t\t{\"FFFFFFFF\", ThemeColor(strings.Repeat(string(rune(-1)), 6), 1)},\n\t} {\n\t\tassert.Equal(t, clr[0], clr[1])\n\t}\n}\n\nfunc TestGetNumFmtID(t *testing.T) {\n\tf := NewFile()\n\n\tfs1, err := parseFormatStyleSet(&Style{Protection: &Protection{Hidden: false, Locked: false}, NumFmt: 10})\n\tassert.NoError(t, err)\n\tid1 := getNumFmtID(&xlsxStyleSheet{}, fs1)\n\n\tfs2, err := parseFormatStyleSet(&Style{Protection: &Protection{Hidden: false, Locked: false}, NumFmt: 0})\n\tassert.NoError(t, err)\n\tid2 := getNumFmtID(&xlsxStyleSheet{}, fs2)\n\n\tassert.NotEqual(t, id1, id2)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestStyleNumFmt.xlsx\")))\n}\n\nfunc TestGetThemeColor(t *testing.T) {\n\tassert.Empty(t, (&File{}).getThemeColor(&xlsxColor{}))\n\tf := NewFile()\n\tassert.Empty(t, f.getThemeColor(nil))\n\tvar theme int\n\tassert.Equal(t, \"FFFFFF\", f.getThemeColor(&xlsxColor{Theme: &theme}))\n\tassert.Equal(t, \"FFFFFF\", f.getThemeColor(&xlsxColor{RGB: \"FFFFFF\"}))\n\tassert.Equal(t, \"FF8080\", f.getThemeColor(&xlsxColor{Indexed: 2, Tint: 0.5}))\n\tassert.Empty(t, f.getThemeColor(&xlsxColor{Indexed: len(IndexedColorMapping), Tint: 0.5}))\n\tclr := &decodeCTColor{}\n\tassert.Nil(t, clr.colorChoice())\n}\n\nfunc TestGetStyle(t *testing.T) {\n\tf := NewFile()\n\texpected := &Style{\n\t\tBorder: []Border{\n\t\t\t{Type: \"left\", Color: \"0000FF\", Style: 3},\n\t\t\t{Type: \"right\", Color: \"FF0000\", Style: 6},\n\t\t\t{Type: \"top\", Color: \"00FF00\", Style: 4},\n\t\t\t{Type: \"bottom\", Color: \"FFFF00\", Style: 5},\n\t\t\t{Type: \"diagonalUp\", Color: \"A020F0\", Style: 7},\n\t\t\t{Type: \"diagonalDown\", Color: \"A020F0\", Style: 7},\n\t\t},\n\t\tFill: Fill{Type: \"gradient\", Shading: 16, Color: []string{\"0000FF\", \"00FF00\"}},\n\t\tFont: &Font{\n\t\t\tBold: true, Italic: true, Underline: \"single\", Family: \"Arial\",\n\t\t\tSize: 8.5, Strike: true, Color: \"777777\", ColorIndexed: 1,\n\t\t\tColorTint: 0.1, VertAlign: \"superscript\",\n\t\t},\n\t\tAlignment: &Alignment{\n\t\t\tHorizontal:      \"center\",\n\t\t\tIndent:          1,\n\t\t\tJustifyLastLine: true,\n\t\t\tReadingOrder:    1,\n\t\t\tRelativeIndent:  1,\n\t\t\tShrinkToFit:     true,\n\t\t\tTextRotation:    180,\n\t\t\tVertical:        \"center\",\n\t\t\tWrapText:        true,\n\t\t},\n\t\tProtection: &Protection{Hidden: true, Locked: true},\n\t\tNumFmt:     49,\n\t}\n\tstyleID, err := f.NewStyle(expected)\n\tassert.NoError(t, err)\n\tstyle, err := f.GetStyle(styleID)\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected.Border, style.Border)\n\tassert.Equal(t, expected.Fill, style.Fill)\n\tassert.Equal(t, expected.Font, style.Font)\n\tassert.Equal(t, expected.Alignment, style.Alignment)\n\tassert.Equal(t, expected.Protection, style.Protection)\n\tassert.Equal(t, expected.NumFmt, style.NumFmt)\n\tassert.Nil(t, style.DecimalPlaces)\n\n\texpected = &Style{\n\t\tFill: Fill{Type: \"pattern\", Pattern: 1, Color: []string{\"0000FF\"}},\n\t}\n\tstyleID, err = f.NewStyle(expected)\n\tassert.NoError(t, err)\n\tstyle, err = f.GetStyle(styleID)\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected.Fill, style.Fill)\n\tassert.Nil(t, style.DecimalPlaces)\n\n\texpected = &Style{NumFmt: 2}\n\tstyleID, err = f.NewStyle(expected)\n\tassert.NoError(t, err)\n\tstyle, err = f.GetStyle(styleID)\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected.NumFmt, style.NumFmt)\n\tassert.Equal(t, 2, *style.DecimalPlaces)\n\n\texpected = &Style{NumFmt: 27}\n\tstyleID, err = f.NewStyle(expected)\n\tassert.NoError(t, err)\n\tstyle, err = f.GetStyle(styleID)\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected.NumFmt, style.NumFmt)\n\tassert.Nil(t, style.DecimalPlaces)\n\n\texpected = &Style{NumFmt: 165}\n\tstyleID, err = f.NewStyle(expected)\n\tassert.NoError(t, err)\n\tstyle, err = f.GetStyle(styleID)\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected.NumFmt, style.NumFmt)\n\tassert.Equal(t, 2, *style.DecimalPlaces)\n\n\tdecimal := 4\n\texpected = &Style{NumFmt: 165, DecimalPlaces: &decimal, NegRed: true}\n\tstyleID, err = f.NewStyle(expected)\n\tassert.NoError(t, err)\n\tstyle, err = f.GetStyle(styleID)\n\tassert.NoError(t, err)\n\tassert.Equal(t, 0, style.NumFmt)\n\tassert.Equal(t, *expected.DecimalPlaces, *style.DecimalPlaces)\n\tassert.Equal(t, \"[$$-409]#,##0.0000;[Red][$$-409]#,##0.0000\", *style.CustomNumFmt)\n\n\tfor _, val := range [][]interface{}{\n\t\t{\"$#,##0\", 0},\n\t\t{\"$#,##0.0\", 1},\n\t\t{\"_($* #,##0_);_($* (#,##0);_($* \\\"-\\\"_);_(@_)\", 0},\n\t\t{\"_($* #,##000_);_($* (#,##000);_($* \\\"-\\\"_);_(@_)\", 0},\n\t\t{\"_($* #,##0.0000_);_($* (#,##0.0000);_($* \\\"-\\\"????_);_(@_)\", 4},\n\t} {\n\t\tnumFmtCode := val[0].(string)\n\t\texpected = &Style{CustomNumFmt: &numFmtCode}\n\t\tstyleID, err = f.NewStyle(expected)\n\t\tassert.NoError(t, err)\n\t\tstyle, err = f.GetStyle(styleID)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, val[1].(int), *style.DecimalPlaces, numFmtCode)\n\t}\n\n\tfor _, val := range []string{\n\t\t\";$#,##0\",\n\t\t\";$#,##0;\",\n\t\t\";$#,##0.0\",\n\t\t\";$#,##0.0;\",\n\t\t\"$#,##0;0.0\",\n\t\t\"_($* #,##0_);;_($* \\\"-\\\"_);_(@_)\",\n\t\t\"_($* #,##0.0_);_($* (#,##0.00);_($* \\\"-\\\"_);_(@_)\",\n\t} {\n\t\texpected = &Style{CustomNumFmt: &val}\n\t\tstyleID, err = f.NewStyle(expected)\n\t\tassert.NoError(t, err)\n\t\tstyle, err = f.GetStyle(styleID)\n\t\tassert.NoError(t, err)\n\t\tassert.Nil(t, style.DecimalPlaces)\n\t}\n\n\t// Test get style with custom color index\n\tf.Styles.Colors = &xlsxStyleColors{\n\t\tIndexedColors: &xlsxIndexedColors{\n\t\t\tRgbColor: []xlsxColor{{RGB: \"FF012345\"}},\n\t\t},\n\t}\n\tassert.Equal(t, \"012345\", f.getThemeColor(&xlsxColor{Indexed: 0}))\n\n\tf.Styles.Fonts.Font[0].U = &attrValString{}\n\tf.Styles.CellXfs.Xf[0].FontID = intPtr(0)\n\tstyle, err = f.GetStyle(styleID)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"single\", style.Font.Underline)\n\n\t// Test get style with invalid style index\n\tstyle, err = f.GetStyle(-1)\n\tassert.Nil(t, style)\n\tassert.Equal(t, err, newInvalidStyleID(-1))\n\t// Test get style with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\tstyle, err = f.GetStyle(1)\n\tassert.Nil(t, style)\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n"
  },
  {
    "path": "table.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nvar (\n\texpressionFormat = regexp.MustCompile(`\"(?:[^\"]|\"\")*\"|\\S+`)\n\tconditionFormat  = regexp.MustCompile(`(or|\\|\\|)`)\n\tblankFormat      = regexp.MustCompile(\"blanks|nonblanks\")\n\tmatchFormat      = regexp.MustCompile(\"[*?]\")\n)\n\n// parseTableOptions provides a function to parse the format settings of the\n// table with default value.\nfunc parseTableOptions(opts *Table) (*Table, error) {\n\tvar err error\n\tif opts == nil {\n\t\treturn &Table{ShowRowStripes: boolPtr(true)}, err\n\t}\n\tif opts.ShowRowStripes == nil {\n\t\topts.ShowRowStripes = boolPtr(true)\n\t}\n\tif err = checkDefinedName(opts.Name); err != nil {\n\t\treturn opts, err\n\t}\n\treturn opts, err\n}\n\n// AddTable provides the method to add table in a worksheet by given worksheet\n// name, range reference and format set. For example, create a table of A1:D5\n// on Sheet1:\n//\n//\terr := f.AddTable(\"Sheet1\", &excelize.Table{Range: \"A1:D5\"})\n//\n// Create a table of F2:H6 on Sheet2 with format set:\n//\n//\tdisable := false\n//\terr := f.AddTable(\"Sheet2\", &excelize.Table{\n//\t    Range:             \"F2:H6\",\n//\t    Name:              \"table\",\n//\t    StyleName:         \"TableStyleMedium2\",\n//\t    ShowFirstColumn:   true,\n//\t    ShowLastColumn:    true,\n//\t    ShowRowStripes:    &disable,\n//\t    ShowColumnStripes: true,\n//\t})\n//\n// Note that the table must be at least two lines including the header. The\n// header cells must contain strings and must be unique, and must set the\n// header row data of the table before calling the AddTable function. Multiple\n// tables range reference that can't have an intersection.\n//\n// Name: The name of the table, in the same worksheet name of the table should\n// be unique, starts with a letter or underscore (_), doesn't include a\n// space or character, and should be no more than 255 characters\n//\n// StyleName: The built-in table style names\n//\n//\tTableStyleLight1 - TableStyleLight21\n//\tTableStyleMedium1 - TableStyleMedium28\n//\tTableStyleDark1 - TableStyleDark11\nfunc (f *File) AddTable(sheet string, table *Table) error {\n\toptions, err := parseTableOptions(table)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar exist bool\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/tables/table\") {\n\t\t\tvar t xlsxTable\n\t\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(v.([]byte)))).\n\t\t\t\tDecode(&t); err != nil && err != io.EOF {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif exist = t.Name == options.Name; exist {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\tif exist {\n\t\treturn ErrExistsTableName\n\t}\n\t// Coordinate conversion, convert C1:B3 to 2,0,1,2.\n\tcoordinates, err := rangeRefToCoordinates(options.Range)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Correct table reference range, such correct C1:B3 to B1:C3.\n\t_ = sortCoordinates(coordinates)\n\ttableID := f.countTables() + 1\n\tsheetRelationshipsTableXML := \"../tables/table\" + strconv.Itoa(tableID) + \".xml\"\n\ttableXML := strings.ReplaceAll(sheetRelationshipsTableXML, \"..\", \"xl\")\n\t// Add first table for given sheet.\n\tsheetXMLPath, _ := f.getSheetXMLPath(sheet)\n\tsheetRels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(sheetXMLPath, \"xl/worksheets/\") + \".rels\"\n\trID := f.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, \"\")\n\tif err = f.addSheetTable(sheet, rID); err != nil {\n\t\treturn err\n\t}\n\tf.addSheetNameSpace(sheet, SourceRelationship)\n\tf.clearCalcCache()\n\tif err = f.addTable(sheet, tableXML, coordinates[0], coordinates[1], coordinates[2], coordinates[3], tableID, options); err != nil {\n\t\treturn err\n\t}\n\treturn f.addContentTypePart(tableID, \"table\")\n}\n\n// GetTables provides the method to get all tables in a worksheet by given\n// worksheet name.\nfunc (f *File) GetTables(sheet string) ([]Table, error) {\n\tvar tables []Table\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn tables, err\n\t}\n\tif ws.TableParts == nil {\n\t\treturn tables, err\n\t}\n\tfor _, tbl := range ws.TableParts.TableParts {\n\t\tif tbl != nil {\n\t\t\ttarget := f.getSheetRelationshipsTargetByID(sheet, tbl.RID)\n\t\t\ttableXML := strings.ReplaceAll(target, \"..\", \"xl\")\n\t\t\tcontent, ok := f.Pkg.Load(tableXML)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar t xlsxTable\n\t\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).\n\t\t\t\tDecode(&t); err != nil && err != io.EOF {\n\t\t\t\treturn tables, err\n\t\t\t}\n\t\t\ttable := Table{\n\t\t\t\trID:      tbl.RID,\n\t\t\t\ttID:      t.ID,\n\t\t\t\ttableXML: tableXML,\n\t\t\t\tRange:    t.Ref,\n\t\t\t\tName:     t.Name,\n\t\t\t}\n\t\t\tif t.TableStyleInfo != nil {\n\t\t\t\ttable.StyleName = t.TableStyleInfo.Name\n\t\t\t\ttable.ShowColumnStripes = t.TableStyleInfo.ShowColumnStripes\n\t\t\t\ttable.ShowFirstColumn = t.TableStyleInfo.ShowFirstColumn\n\t\t\t\ttable.ShowLastColumn = t.TableStyleInfo.ShowLastColumn\n\t\t\t\ttable.ShowRowStripes = &t.TableStyleInfo.ShowRowStripes\n\t\t\t}\n\t\t\ttables = append(tables, table)\n\t\t}\n\t}\n\treturn tables, err\n}\n\n// DeleteTable provides the method to delete table by given table name.\nfunc (f *File) DeleteTable(name string) error {\n\tif err := checkDefinedName(name); err != nil {\n\t\treturn err\n\t}\n\ttbls, err := f.getTables()\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.clearCalcCache()\n\tfor sheet, tables := range tbls {\n\t\tfor _, table := range tables {\n\t\t\tif table.Name != name {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tws, _ := f.workSheetReader(sheet)\n\t\t\tfor i, tbl := range ws.TableParts.TableParts {\n\t\t\t\tif tbl.RID == table.rID {\n\t\t\t\t\tws.TableParts.TableParts = append(ws.TableParts.TableParts[:i], ws.TableParts.TableParts[i+1:]...)\n\t\t\t\t\tf.Pkg.Delete(table.tableXML)\n\t\t\t\t\t_ = f.removeContentTypesPart(ContentTypeSpreadSheetMLTable, \"/\"+table.tableXML)\n\t\t\t\t\tf.deleteSheetRelationships(sheet, tbl.RID)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ws.TableParts.Count = len(ws.TableParts.TableParts); ws.TableParts.Count == 0 {\n\t\t\t\tws.TableParts = nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\treturn newNoExistTableError(name)\n}\n\n// getTables provides a function to get all tables in a workbook.\nfunc (f *File) getTables() (map[string][]Table, error) {\n\ttables := map[string][]Table{}\n\tfor _, sheetName := range f.GetSheetList() {\n\t\ttbls, err := f.GetTables(sheetName)\n\t\te := ErrSheetNotExist{sheetName}\n\t\tif err != nil && err.Error() != newNotWorksheetError(sheetName).Error() && err.Error() != e.Error() {\n\t\t\treturn tables, err\n\t\t}\n\t\ttables[sheetName] = append(tables[sheetName], tbls...)\n\t}\n\treturn tables, nil\n}\n\n// countTables provides a function to get table files count storage in the\n// folder xl/tables.\nfunc (f *File) countTables() int {\n\tcount := 0\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/tables/tableSingleCells\") {\n\t\t\tvar cells xlsxSingleXMLCells\n\t\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(v.([]byte)))).\n\t\t\t\tDecode(&cells); err != nil && err != io.EOF {\n\t\t\t\tcount++\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tfor _, cell := range cells.SingleXmlCell {\n\t\t\t\tif count < cell.ID {\n\t\t\t\t\tcount = cell.ID\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif strings.Contains(k.(string), \"xl/tables/table\") {\n\t\t\tvar t xlsxTable\n\t\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(v.([]byte)))).\n\t\t\t\tDecode(&t); err != nil && err != io.EOF {\n\t\t\t\tcount++\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif count < t.ID {\n\t\t\t\tcount = t.ID\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\treturn count\n}\n\n// addSheetTable provides a function to add tablePart element to\n// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.\nfunc (f *File) addSheetTable(sheet string, rID int) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttable := &xlsxTablePart{\n\t\tRID: \"rId\" + strconv.Itoa(rID),\n\t}\n\tif ws.TableParts == nil {\n\t\tws.TableParts = &xlsxTableParts{}\n\t}\n\tws.TableParts.Count++\n\tws.TableParts.TableParts = append(ws.TableParts.TableParts, table)\n\treturn err\n}\n\n// setTableColumns provides a function to set cells value in header row for the\n// table.\nfunc (f *File) setTableColumns(sheet string, showHeaderRow bool, x1, y1, x2 int, tbl *xlsxTable) error {\n\tvar (\n\t\tidx            int\n\t\theader         []string\n\t\ttableColumns   []*xlsxTableColumn\n\t\tgetTableColumn = func(name string) *xlsxTableColumn {\n\t\t\tif tbl != nil && tbl.TableColumns != nil {\n\t\t\t\tfor _, column := range tbl.TableColumns.TableColumn {\n\t\t\t\t\tif column.Name == name {\n\t\t\t\t\t\treturn column\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t)\n\tfor i := x1; i <= x2; i++ {\n\t\tidx++\n\t\tcell, err := CoordinatesToCellName(i, y1)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tname, _ := f.GetCellValue(sheet, cell, Options{RawCellValue: true})\n\t\tif _, err := strconv.Atoi(name); err == nil {\n\t\t\tif showHeaderRow {\n\t\t\t\t_ = f.SetCellStr(sheet, cell, name)\n\t\t\t}\n\t\t}\n\t\tif name == \"\" || inStrSlice(header, name, true) != -1 {\n\t\t\tname = \"Column\" + strconv.Itoa(idx)\n\t\t\tif showHeaderRow {\n\t\t\t\t_ = f.SetCellStr(sheet, cell, name)\n\t\t\t}\n\t\t}\n\t\theader = append(header, name)\n\t\tif column := getTableColumn(name); column != nil {\n\t\t\tcolumn.ID, column.DataDxfID, column.QueryTableFieldID = idx, 0, 0\n\t\t\ttableColumns = append(tableColumns, column)\n\t\t\tcontinue\n\t\t}\n\t\ttableColumns = append(tableColumns, &xlsxTableColumn{\n\t\t\tID:   idx,\n\t\t\tName: name,\n\t\t})\n\t}\n\ttbl.TableColumns = &xlsxTableColumns{\n\t\tCount:       len(tableColumns),\n\t\tTableColumn: tableColumns,\n\t}\n\treturn nil\n}\n\n// checkDefinedName check whether there are illegal characters in the defined\n// name or table name. Verify that the name:\n// 1. Starts with a letter or underscore (_)\n// 2. Doesn't include a space or character that isn't allowed\nfunc checkDefinedName(name string) error {\n\tif countUTF16String(name) > MaxFieldLength {\n\t\treturn ErrNameLength\n\t}\n\tinCodeRange := func(code int, tbl []int) bool {\n\t\tfor i := 0; i < len(tbl); i += 2 {\n\t\t\tif tbl[i] <= code && code <= tbl[i+1] {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\tfor i, c := range name {\n\t\tif i == 0 {\n\t\t\tif inCodeRange(int(c), supportedDefinedNameAtStartCharCodeRange) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn newInvalidNameError(name)\n\t\t}\n\t\tif inCodeRange(int(c), supportedDefinedNameAfterStartCharCodeRange) {\n\t\t\tcontinue\n\t\t}\n\t\treturn newInvalidNameError(name)\n\t}\n\treturn nil\n}\n\n// addTable provides a function to add table by given worksheet name,\n// range reference and format set.\nfunc (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *Table) error {\n\t// Correct the minimum number of rows, the table at least two lines.\n\tif y1 == y2 {\n\t\ty2++\n\t}\n\thideHeaderRow := opts != nil && opts.ShowHeaderRow != nil && !*opts.ShowHeaderRow\n\tif hideHeaderRow {\n\t\ty1++\n\t}\n\t// Correct table range reference, such correct C1:B3 to B1:C3.\n\tref, err := coordinatesToRangeRef([]int{x1, y1, x2, y2})\n\tif err != nil {\n\t\treturn err\n\t}\n\tname := opts.Name\n\tif name == \"\" {\n\t\tname = \"Table\" + strconv.Itoa(i)\n\t}\n\tt := xlsxTable{\n\t\tXMLNS:       NameSpaceSpreadSheet.Value,\n\t\tID:          i,\n\t\tName:        name,\n\t\tDisplayName: name,\n\t\tRef:         ref,\n\t\tAutoFilter: &xlsxAutoFilter{\n\t\t\tRef: ref,\n\t\t},\n\t\tTableStyleInfo: &xlsxTableStyleInfo{\n\t\t\tName:              opts.StyleName,\n\t\t\tShowFirstColumn:   opts.ShowFirstColumn,\n\t\t\tShowLastColumn:    opts.ShowLastColumn,\n\t\t\tShowRowStripes:    *opts.ShowRowStripes,\n\t\t\tShowColumnStripes: opts.ShowColumnStripes,\n\t\t},\n\t}\n\t_ = f.setTableColumns(sheet, !hideHeaderRow, x1, y1, x2, &t)\n\tif hideHeaderRow {\n\t\tt.AutoFilter = nil\n\t\tt.HeaderRowCount = intPtr(0)\n\t}\n\ttable, err := xml.Marshal(t)\n\tf.saveFileList(tableXML, table)\n\treturn err\n}\n\n// AutoFilter provides the method to add auto filter in a worksheet by given\n// worksheet name, range reference and settings. An auto filter in Excel is a\n// way of filtering a 2D range of data based on some simple criteria. For\n// example applying an auto filter to a cell range A1:D4 in the Sheet1:\n//\n//\terr := f.AutoFilter(\"Sheet1\", \"A1:D4\", []excelize.AutoFilterOptions{})\n//\n// Filter data in an auto filter:\n//\n//\terr := f.AutoFilter(\"Sheet1\", \"A1:D4\", []excelize.AutoFilterOptions{\n//\t    {Column: \"B\", Expression: \"x != blanks\"},\n//\t})\n//\n// Column defines the filter columns in an auto filter range based on simple\n// criteria\n//\n// It isn't sufficient to just specify the filter condition. You must also\n// hide any rows that don't match the filter condition. Rows are hidden using\n// the SetRowVisible function. Excelize can't filter rows automatically since\n// this isn't part of the file format.\n//\n// Setting a filter criteria for a column:\n//\n// Expression defines the conditions, the following operators are available\n// for setting the filter criteria:\n//\n//\t==\n//\t!=\n//\t>\n//\t<\n//\t>=\n//\t<=\n//\tand\n//\tor\n//\n// An expression can comprise a single statement or two statements separated\n// by the 'and' and 'or' operators. For example:\n//\n//\tx <  2000\n//\tx >  2000\n//\tx == 2000\n//\tx >  2000 and x <  5000\n//\tx == 2000 or  x == 5000\n//\n// Filtering of blank or non-blank data can be achieved by using a value of\n// Blanks or NonBlanks in the expression:\n//\n//\tx == Blanks\n//\tx == NonBlanks\n//\n// Excel also allows some simple string matching operations:\n//\n//\tx == b*      // begins with b\n//\tx != b*      // doesn't begin with b\n//\tx == *b      // ends with b\n//\tx != *b      // doesn't end with b\n//\tx == *b*     // contains b\n//\tx != *b*     // doesn't contain b\n//\n// You can also use '*' to match any character or number and '?' to match any\n// single character or number. No other regular expression quantifier is\n// supported by Excel's filters. Excel's regular expression characters can be\n// escaped using '~'.\n//\n// The placeholder variable x in the above examples can be replaced by any\n// simple string. The actual placeholder name is ignored internally so the\n// following are all equivalent:\n//\n//\tx     < 2000\n//\tcol   < 2000\n//\tPrice < 2000\nfunc (f *File) AutoFilter(sheet, rangeRef string, opts []AutoFilterOptions) error {\n\tcoordinates, err := rangeRefToCoordinates(rangeRef)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_ = sortCoordinates(coordinates)\n\t// Correct reference range, such correct C1:B3 to B1:C3.\n\tref, _ := coordinatesToRangeRef(coordinates, true)\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tsheetID, err := f.GetSheetIndex(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfilterRange := fmt.Sprintf(\"'%s'!%s\", sheet, ref)\n\td := xlsxDefinedName{\n\t\tName:         builtInDefinedNames[3],\n\t\tHidden:       true,\n\t\tLocalSheetID: intPtr(sheetID),\n\t\tData:         filterRange,\n\t}\n\tif wb.DefinedNames == nil {\n\t\twb.DefinedNames = &xlsxDefinedNames{\n\t\t\tDefinedName: []xlsxDefinedName{d},\n\t\t}\n\t} else {\n\t\tvar definedNameExists bool\n\t\tfor idx := range wb.DefinedNames.DefinedName {\n\t\t\tdefinedName, localSheetID := wb.DefinedNames.DefinedName[idx], 0\n\t\t\tif definedName.LocalSheetID != nil {\n\t\t\t\tlocalSheetID = *definedName.LocalSheetID\n\t\t\t}\n\t\t\tif definedName.Name == builtInDefinedNames[3] && localSheetID == sheetID && definedName.Hidden {\n\t\t\t\twb.DefinedNames.DefinedName[idx].Data = filterRange\n\t\t\t\tdefinedNameExists = true\n\t\t\t}\n\t\t}\n\t\tif !definedNameExists {\n\t\t\twb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName, d)\n\t\t}\n\t}\n\tcolumns := coordinates[2] - coordinates[0]\n\treturn f.autoFilter(sheet, ref, columns, coordinates[0], opts)\n}\n\n// autoFilter provides a function to extract the tokens from the filter\n// expression. The tokens are mainly non-whitespace groups.\nfunc (f *File) autoFilter(sheet, ref string, columns, col int, opts []AutoFilterOptions) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif ws.SheetPr != nil {\n\t\tws.SheetPr.FilterMode = true\n\t}\n\tws.SheetPr = &xlsxSheetPr{FilterMode: true}\n\tfilter := &xlsxAutoFilter{\n\t\tRef: ref,\n\t}\n\tws.AutoFilter = filter\n\tfor _, opt := range opts {\n\t\tif opt.Column == \"\" || opt.Expression == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tfsCol, err := ColumnNameToNumber(opt.Column)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\toffset := fsCol - col\n\t\tif offset < 0 || offset > columns {\n\t\t\treturn newInvalidAutoFilterColumnError(opt.Column)\n\t\t}\n\t\tfc := &xlsxFilterColumn{ColID: offset}\n\t\ttoken := expressionFormat.FindAllString(opt.Expression, -1)\n\t\tif len(token) != 3 && len(token) != 7 {\n\t\t\treturn newInvalidAutoFilterExpError(opt.Expression)\n\t\t}\n\t\texpressions, tokens, err := f.parseFilterExpression(opt.Expression, token)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tf.writeAutoFilter(fc, expressions, tokens)\n\t\tfilter.FilterColumn = append(filter.FilterColumn, fc)\n\t}\n\tws.AutoFilter = filter\n\treturn nil\n}\n\n// writeAutoFilter provides a function to check for single or double custom\n// filters as default filters and handle them accordingly.\nfunc (f *File) writeAutoFilter(fc *xlsxFilterColumn, exp []int, tokens []string) {\n\tif len(exp) == 1 && exp[0] == 2 {\n\t\t// Single equality.\n\t\tvar filters []*xlsxFilter\n\t\tfilters = append(filters, &xlsxFilter{Val: tokens[0]})\n\t\tfc.Filters = &xlsxFilters{Filter: filters}\n\t\treturn\n\t}\n\tif len(exp) == 3 && exp[0] == 2 && exp[1] == 1 && exp[2] == 2 {\n\t\t// Double equality with \"or\" operator.\n\t\tvar filters []*xlsxFilter\n\t\tfor _, v := range tokens {\n\t\t\tfilters = append(filters, &xlsxFilter{Val: v})\n\t\t}\n\t\tfc.Filters = &xlsxFilters{Filter: filters}\n\t\treturn\n\t}\n\t// Non default custom filter.\n\texpRel, andRel := map[int]int{0: 0, 1: 2}, map[int]bool{0: true, 1: false}\n\tfor k, v := range tokens {\n\t\tf.writeCustomFilter(fc, exp[expRel[k]], v)\n\t\tif k == 1 {\n\t\t\tfc.CustomFilters.And = andRel[exp[k]]\n\t\t}\n\t}\n}\n\n// writeCustomFilter provides a function to write the <customFilter> element.\nfunc (f *File) writeCustomFilter(fc *xlsxFilterColumn, operator int, val string) {\n\toperators := map[int]string{\n\t\t1:  \"lessThan\",\n\t\t2:  \"equal\",\n\t\t3:  \"lessThanOrEqual\",\n\t\t4:  \"greaterThan\",\n\t\t5:  \"notEqual\",\n\t\t6:  \"greaterThanOrEqual\",\n\t\t22: \"equal\",\n\t}\n\tcustomFilter := xlsxCustomFilter{\n\t\tOperator: operators[operator],\n\t\tVal:      val,\n\t}\n\tif fc.CustomFilters != nil {\n\t\tfc.CustomFilters.CustomFilter = append(fc.CustomFilters.CustomFilter, &customFilter)\n\t\treturn\n\t}\n\tvar customFilters []*xlsxCustomFilter\n\tcustomFilters = append(customFilters, &customFilter)\n\tfc.CustomFilters = &xlsxCustomFilters{CustomFilter: customFilters}\n}\n\n// parseFilterExpression provides a function to converts the tokens of a\n// possibly conditional expression into 1 or 2 sub expressions for further\n// parsing.\n//\n// Examples:\n//\n//\t('x', '==', 2000) -> exp1\n//\t('x', '>',  2000, 'and', 'x', '<', 5000) -> exp1 and exp2\nfunc (f *File) parseFilterExpression(expression string, tokens []string) ([]int, []string, error) {\n\tvar expressions []int\n\tvar t []string\n\tif len(tokens) == 7 {\n\t\t// The number of tokens will be either 3 (for 1 expression) or 7 (for 2\n\t\t// expressions).\n\t\tconditional, c := 0, tokens[3]\n\t\tif conditionFormat.MatchString(c) {\n\t\t\tconditional = 1\n\t\t}\n\t\texpression1, token1, err := f.parseFilterTokens(expression, tokens[:3])\n\t\tif err != nil {\n\t\t\treturn expressions, t, err\n\t\t}\n\t\texpression2, token2, err := f.parseFilterTokens(expression, tokens[4:7])\n\t\tif err != nil {\n\t\t\treturn expressions, t, err\n\t\t}\n\t\treturn []int{expression1[0], conditional, expression2[0]}, []string{token1, token2}, nil\n\t}\n\texp, token, err := f.parseFilterTokens(expression, tokens)\n\tif err != nil {\n\t\treturn expressions, t, err\n\t}\n\treturn exp, []string{token}, nil\n}\n\n// parseFilterTokens provides a function to parse the 3 tokens of a filter\n// expression and return the operator and token.\nfunc (f *File) parseFilterTokens(expression string, tokens []string) ([]int, string, error) {\n\toperators := map[string]int{\n\t\t\"==\": 2,\n\t\t\"=\":  2,\n\t\t\"=~\": 2,\n\t\t\"eq\": 2,\n\t\t\"!=\": 5,\n\t\t\"!~\": 5,\n\t\t\"ne\": 5,\n\t\t\"<>\": 5,\n\t\t\"<\":  1,\n\t\t\"<=\": 3,\n\t\t\">\":  4,\n\t\t\">=\": 6,\n\t}\n\toperator, ok := operators[strings.ToLower(tokens[1])]\n\tif !ok {\n\t\t// Convert the operator from a number to a descriptive string.\n\t\treturn []int{}, \"\", newUnknownFilterTokenError(tokens[1])\n\t}\n\ttoken := tokens[2]\n\t// Special handling for Blanks/NonBlanks.\n\tre := blankFormat.MatchString(strings.ToLower(token))\n\tif re {\n\t\t// Only allow Equals or NotEqual in this context.\n\t\tif operator != 2 && operator != 5 {\n\t\t\treturn []int{operator}, token, newInvalidAutoFilterOperatorError(tokens[1], expression)\n\t\t}\n\t\ttoken = strings.ToLower(token)\n\t\t// The operator should always be 2 (=) to flag a \"simple\" equality in\n\t\t// the binary record. Therefore we convert <> to =.\n\t\tif token == \"blanks\" {\n\t\t\tif operator == 5 {\n\t\t\t\ttoken = \" \"\n\t\t\t}\n\t\t} else {\n\t\t\tif operator == 5 {\n\t\t\t\toperator = 2\n\t\t\t\ttoken = \"blanks\"\n\t\t\t} else {\n\t\t\t\toperator = 5\n\t\t\t\ttoken = \" \"\n\t\t\t}\n\t\t}\n\t}\n\t// If the string token contains an Excel match character then change the\n\t// operator type to indicate a non \"simple\" equality.\n\tif re = matchFormat.MatchString(token); operator == 2 && re {\n\t\toperator = 22\n\t}\n\treturn []int{operator}, token, nil\n}\n"
  },
  {
    "path": "table_test.go",
    "content": "package excelize\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestAddTable(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{Range: \"B26:A21\"}))\n\tassert.NoError(t, f.AddTable(\"Sheet2\", &Table{\n\t\tRange:             \"A2:B5\",\n\t\tName:              \"table\",\n\t\tStyleName:         \"TableStyleMedium2\",\n\t\tShowColumnStripes: true,\n\t\tShowFirstColumn:   true,\n\t\tShowLastColumn:    true,\n\t\tShowRowStripes:    boolPtr(true),\n\t}))\n\tassert.NoError(t, f.AddTable(\"Sheet2\", &Table{\n\t\tRange:         \"D1:D11\",\n\t\tShowHeaderRow: boolPtr(false),\n\t}))\n\tassert.NoError(t, f.AddTable(\"Sheet2\", &Table{Range: \"F1:F1\", StyleName: \"TableStyleMedium8\"}))\n\t// Test get tables in worksheet\n\ttables, err := f.GetTables(\"Sheet2\")\n\tassert.Len(t, tables, 3)\n\tassert.NoError(t, err)\n\n\t// Test add table with already exist table name\n\tassert.Equal(t, f.AddTable(\"Sheet2\", &Table{Name: \"Table1\"}), ErrExistsTableName)\n\t// Test add table with invalid table options\n\tassert.Equal(t, f.AddTable(\"Sheet1\", nil), ErrParameterInvalid)\n\t// Test add table in not exist worksheet\n\tassert.EqualError(t, f.AddTable(\"SheetN\", &Table{Range: \"B26:A21\"}), \"sheet SheetN does not exist\")\n\t// Test add table with illegal cell reference\n\tassert.Equal(t, f.AddTable(\"Sheet1\", &Table{Range: \"A:B1\"}), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")))\n\tassert.Equal(t, f.AddTable(\"Sheet1\", &Table{Range: \"A1:B\"}), newCellNameToCoordinatesError(\"B\", newInvalidCellNameError(\"B\")))\n\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddTable.xlsx\")))\n\n\t// Test add table with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.AddTable(\"Sheet:1\", &Table{Range: \"B26:A21\"}))\n\t// Test addTable with illegal cell reference\n\tf = NewFile()\n\tassert.Equal(t, newCoordinatesToCellNameError(0, 0), f.addTable(\"sheet1\", \"\", 0, 0, 0, 0, 0, nil))\n\tassert.Equal(t, newCoordinatesToCellNameError(0, 0), f.addTable(\"sheet1\", \"\", 1, 1, 0, 0, 0, nil))\n\t// Test set defined name and add table with invalid name\n\tfor _, cases := range []struct {\n\t\tname string\n\t\terr  error\n\t}{\n\t\t{name: \"1Table\", err: newInvalidNameError(\"1Table\")},\n\t\t{name: \"-Table\", err: newInvalidNameError(\"-Table\")},\n\t\t{name: \"'Table\", err: newInvalidNameError(\"'Table\")},\n\t\t{name: \"Table 1\", err: newInvalidNameError(\"Table 1\")},\n\t\t{name: \"A&B\", err: newInvalidNameError(\"A&B\")},\n\t\t{name: \"_1Table'\", err: newInvalidNameError(\"_1Table'\")},\n\t\t{name: \"\\u0f5f\\u0fb3\\u0f0b\\u0f21\", err: newInvalidNameError(\"\\u0f5f\\u0fb3\\u0f0b\\u0f21\")},\n\t\t{name: strings.Repeat(\"c\", MaxFieldLength+1), err: ErrNameLength},\n\t} {\n\t\tassert.Equal(t, cases.err, f.AddTable(\"Sheet1\", &Table{\n\t\t\tRange: \"A1:B2\",\n\t\t\tName:  cases.name,\n\t\t}))\n\t\tassert.Equal(t, cases.err, f.SetDefinedName(&DefinedName{\n\t\t\tName: cases.name, RefersTo: \"Sheet1!$A$2:$D$5\",\n\t\t}))\n\t}\n\t// Test check duplicate table name with unsupported charset table parts\n\tf = NewFile()\n\tf.Pkg.Store(\"xl/tables/table1.xml\", MacintoshCyrillicCharset)\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{Range: \"A1:B2\"}))\n\tassert.NoError(t, f.Close())\n\tf = NewFile()\n\t// Test add table with workbook with single cells parts\n\tf.Pkg.Store(\"xl/tables/tableSingleCells1.xml\", []byte(\"<singleXmlCells><singleXmlCell id=\\\"2\\\" r=\\\"A1\\\" connectionId=\\\"2\\\" /></singleXmlCells>\"))\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{Range: \"A1:B2\"}))\n\t// Test add table with workbook with unsupported charset single cells parts\n\tf.Pkg.Store(\"xl/tables/tableSingleCells1.xml\", MacintoshCyrillicCharset)\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{Range: \"A1:B2\"}))\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestGetTables(t *testing.T) {\n\tf := NewFile()\n\t// Test get tables in none table worksheet\n\ttables, err := f.GetTables(\"Sheet1\")\n\tassert.Len(t, tables, 0)\n\tassert.NoError(t, err)\n\t// Test get tables in not exist worksheet\n\t_, err = f.GetTables(\"SheetN\")\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n\t// Test adjust table with unsupported charset\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{Range: \"B26:A21\"}))\n\tf.Pkg.Store(\"xl/tables/table1.xml\", MacintoshCyrillicCharset)\n\t_, err = f.GetTables(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test adjust table with no exist table parts\n\tf.Pkg.Delete(\"xl/tables/table1.xml\")\n\ttables, err = f.GetTables(\"Sheet1\")\n\tassert.Len(t, tables, 0)\n\tassert.NoError(t, err)\n}\n\nfunc TestDeleteTable(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{Range: \"A1:B4\", Name: \"Table1\"}))\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{Range: \"B26:A21\", Name: \"Table2\"}))\n\tassert.NoError(t, f.DeleteTable(\"Table2\"))\n\tassert.NoError(t, f.DeleteTable(\"Table1\"))\n\t// Test delete table with invalid table name\n\tassert.Equal(t, newInvalidNameError(\"Table 1\"), f.DeleteTable(\"Table 1\"))\n\t// Test delete table with no exist table name\n\tassert.Equal(t, newNoExistTableError(\"Table\"), f.DeleteTable(\"Table\"))\n\t// Test delete table with unsupported charset\n\tf.Sheet.Delete(\"xl/worksheets/sheet1.xml\")\n\tf.Pkg.Store(\"xl/worksheets/sheet1.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.DeleteTable(\"Table1\"), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test delete table without deleting table header\n\tf = NewFile()\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"A1\", \"Date\"))\n\tassert.NoError(t, f.SetCellValue(\"Sheet1\", \"B1\", \"Values\"))\n\tassert.NoError(t, f.UpdateLinkedValue())\n\tassert.NoError(t, f.AddTable(\"Sheet1\", &Table{Range: \"A1:B2\", Name: \"Table1\"}))\n\tassert.NoError(t, f.DeleteTable(\"Table1\"))\n\tval, err := f.GetCellValue(\"Sheet1\", \"A1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Date\", val)\n\tval, err = f.GetCellValue(\"Sheet1\", \"B1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Values\", val)\n}\n\nfunc TestSetTableColumns(t *testing.T) {\n\tf := NewFile()\n\tassert.Equal(t, newCoordinatesToCellNameError(1, 0), f.setTableColumns(\"Sheet1\", true, 1, 0, 1, nil))\n}\n\nfunc TestAutoFilter(t *testing.T) {\n\toutFile := filepath.Join(\"test\", \"TestAutoFilter%d.xlsx\")\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\tfor i, opts := range [][]AutoFilterOptions{\n\t\t{},\n\t\t{{Column: \"B\", Expression: \"\"}},\n\t\t{{Column: \"B\", Expression: \"x != blanks\"}},\n\t\t{{Column: \"B\", Expression: \"x == blanks\"}},\n\t\t{{Column: \"B\", Expression: \"x != nonblanks\"}},\n\t\t{{Column: \"B\", Expression: \"x == nonblanks\"}},\n\t\t{{Column: \"B\", Expression: \"x <= 1 and x >= 2\"}},\n\t\t{{Column: \"B\", Expression: \"x == 1 or x == 2\"}},\n\t\t{{Column: \"B\", Expression: \"x == 1 or x == 2*\"}},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"Expression%d\", i+1), func(t *testing.T) {\n\t\t\tassert.NoError(t, f.AutoFilter(\"Sheet1\", \"D4:B1\", opts))\n\t\t\tassert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, i+1)))\n\t\t})\n\t}\n\n\t// Test add auto filter with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.AutoFilter(\"Sheet:1\", \"A1:B1\", nil))\n\t// Test add auto filter with illegal cell reference\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.AutoFilter(\"Sheet1\", \"A:B1\", nil))\n\tassert.Equal(t, newCellNameToCoordinatesError(\"B\", newInvalidCellNameError(\"B\")), f.AutoFilter(\"Sheet1\", \"A1:B\", nil))\n\t// Test add auto filter with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AutoFilter(\"Sheet1\", \"D4:B1\", nil), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test add auto filter with empty local sheet ID\n\tf = NewFile()\n\tf.WorkBook = &xlsxWorkbook{DefinedNames: &xlsxDefinedNames{DefinedName: []xlsxDefinedName{{Name: builtInDefinedNames[3], Hidden: true}}}}\n\tassert.NoError(t, f.AutoFilter(\"Sheet1\", \"A1:B1\", nil))\n}\n\nfunc TestAutoFilterError(t *testing.T) {\n\toutFile := filepath.Join(\"test\", \"TestAutoFilterError%d.xlsx\")\n\tf, err := prepareTestBook1()\n\tassert.NoError(t, err)\n\tfor i, opts := range [][]AutoFilterOptions{\n\t\t{{Column: \"B\", Expression: \"x <= 1 and x >= blanks\"}},\n\t\t{{Column: \"B\", Expression: \"x -- y or x == *2*\"}},\n\t\t{{Column: \"B\", Expression: \"x != y or x ? *2\"}},\n\t\t{{Column: \"B\", Expression: \"x -- y o r x == *2\"}},\n\t\t{{Column: \"B\", Expression: \"x -- y\"}},\n\t\t{{Column: \"A\", Expression: \"x -- y\"}},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"Expression%d\", i+1), func(t *testing.T) {\n\t\t\tif assert.Error(t, f.AutoFilter(\"Sheet2\", \"D4:B1\", opts)) {\n\t\t\t\tassert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, i+1)))\n\t\t\t}\n\t\t})\n\t}\n\n\tassert.Equal(t, ErrSheetNotExist{\"SheetN\"}, f.autoFilter(\"SheetN\", \"A1\", 1, 1, []AutoFilterOptions{{\n\t\tColumn:     \"A\",\n\t\tExpression: \"\",\n\t}}))\n\tassert.Equal(t, newInvalidColumnNameError(\"-\"), f.autoFilter(\"Sheet1\", \"A1\", 1, 1, []AutoFilterOptions{{\n\t\tColumn:     \"-\",\n\t\tExpression: \"-\",\n\t}}))\n\tassert.Equal(t, newInvalidAutoFilterColumnError(\"A\"), f.autoFilter(\"Sheet1\", \"A1\", 1, 100, []AutoFilterOptions{{\n\t\tColumn:     \"A\",\n\t\tExpression: \"-\",\n\t}}))\n\tassert.Equal(t, newInvalidAutoFilterExpError(\"-\"), f.autoFilter(\"Sheet1\", \"A1\", 1, 1, []AutoFilterOptions{{\n\t\tColumn:     \"A\",\n\t\tExpression: \"-\",\n\t}}))\n}\n\nfunc TestParseFilterTokens(t *testing.T) {\n\tf := NewFile()\n\t// Test with unknown operator\n\t_, _, err := f.parseFilterTokens(\"\", []string{\"\", \"!\"})\n\tassert.EqualError(t, err, \"unknown operator: !\")\n\t// Test invalid operator in context\n\t_, _, err = f.parseFilterTokens(\"\", []string{\"\", \"<\", \"x != blanks\"})\n\tassert.Equal(t, newInvalidAutoFilterOperatorError(\"<\", \"\"), err)\n}\n"
  },
  {
    "path": "templates.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n//\n// This file contains default templates for XML files we don't yet populated\n// based on content.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// Source relationship and namespace list, associated prefixes and schema in which it was\n// introduced.\nvar (\n\tNameSpaceDocumentPropertiesVariantTypes = xml.Attr{Name: xml.Name{Local: \"vt\", Space: \"xmlns\"}, Value: \"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes\"}\n\tNameSpaceDrawing2016SVG                 = xml.Attr{Name: xml.Name{Local: \"asvg\", Space: \"xmlns\"}, Value: \"http://schemas.microsoft.com/office/drawing/2016/SVG/main\"}\n\tNameSpaceDrawingML                      = xml.Attr{Name: xml.Name{Local: \"a\", Space: \"xmlns\"}, Value: \"http://schemas.openxmlformats.org/drawingml/2006/main\"}\n\tNameSpaceDrawingMLA14                   = xml.Attr{Name: xml.Name{Local: \"a14\", Space: \"xmlns\"}, Value: \"http://schemas.microsoft.com/office/drawing/2010/main\"}\n\tNameSpaceDrawingMLChart                 = xml.Attr{Name: xml.Name{Local: \"c\", Space: \"xmlns\"}, Value: \"http://schemas.openxmlformats.org/drawingml/2006/chart\"}\n\tNameSpaceDrawingMLSlicer                = xml.Attr{Name: xml.Name{Local: \"sle\", Space: \"xmlns\"}, Value: \"http://schemas.microsoft.com/office/drawing/2010/slicer\"}\n\tNameSpaceDrawingMLSlicerX15             = xml.Attr{Name: xml.Name{Local: \"sle15\", Space: \"xmlns\"}, Value: \"http://schemas.microsoft.com/office/drawing/2012/slicer\"}\n\tNameSpaceDrawingMLSpreadSheet           = xml.Attr{Name: xml.Name{Local: \"xdr\", Space: \"xmlns\"}, Value: \"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing\"}\n\tNameSpaceMacExcel2008Main               = xml.Attr{Name: xml.Name{Local: \"mx\", Space: \"xmlns\"}, Value: \"http://schemas.microsoft.com/office/mac/excel/2008/main\"}\n\tNameSpaceSpreadSheet                    = xml.Attr{Name: xml.Name{Local: \"xmlns\"}, Value: \"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"}\n\tNameSpaceSpreadSheetExcel2006Main       = xml.Attr{Name: xml.Name{Local: \"xne\", Space: \"xmlns\"}, Value: \"http://schemas.microsoft.com/office/excel/2006/main\"}\n\tNameSpaceSpreadSheetX14                 = xml.Attr{Name: xml.Name{Local: \"x14\", Space: \"xmlns\"}, Value: \"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main\"}\n\tNameSpaceSpreadSheetX15                 = xml.Attr{Name: xml.Name{Local: \"x15\", Space: \"xmlns\"}, Value: \"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main\"}\n\tNameSpaceSpreadSheetXR10                = xml.Attr{Name: xml.Name{Local: \"xr10\", Space: \"xmlns\"}, Value: \"http://schemas.microsoft.com/office/spreadsheetml/2016/revision10\"}\n\tSourceRelationship                      = xml.Attr{Name: xml.Name{Local: \"r\", Space: \"xmlns\"}, Value: \"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"}\n\tSourceRelationshipChart20070802         = xml.Attr{Name: xml.Name{Local: \"c14\", Space: \"xmlns\"}, Value: \"http://schemas.microsoft.com/office/drawing/2007/8/2/chart\"}\n\tSourceRelationshipChart2014             = xml.Attr{Name: xml.Name{Local: \"c16\", Space: \"xmlns\"}, Value: \"http://schemas.microsoft.com/office/drawing/2014/chart\"}\n\tSourceRelationshipChart201506           = xml.Attr{Name: xml.Name{Local: \"c16r2\", Space: \"xmlns\"}, Value: \"http://schemas.microsoft.com/office/drawing/2015/06/chart\"}\n\tSourceRelationshipCompatibility         = xml.Attr{Name: xml.Name{Local: \"mc\", Space: \"xmlns\"}, Value: \"http://schemas.openxmlformats.org/markup-compatibility/2006\"}\n)\n\n// Source relationship and namespace.\nconst (\n\tContentTypeAddinMacro                         = \"application/vnd.ms-excel.addin.macroEnabled.main+xml\"\n\tContentTypeCustomProperties                   = \"application/vnd.openxmlformats-officedocument.custom-properties+xml\"\n\tContentTypeDrawing                            = \"application/vnd.openxmlformats-officedocument.drawing+xml\"\n\tContentTypeDrawingML                          = \"application/vnd.openxmlformats-officedocument.drawingml.chart+xml\"\n\tContentTypeMacro                              = \"application/vnd.ms-excel.sheet.macroEnabled.main+xml\"\n\tContentTypeRelationships                      = \"application/vnd.openxmlformats-package.relationships+xml\"\n\tContentTypeSheetML                            = \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"\n\tContentTypeSlicer                             = \"application/vnd.ms-excel.slicer+xml\"\n\tContentTypeSlicerCache                        = \"application/vnd.ms-excel.slicerCache+xml\"\n\tContentTypeSpreadSheetMLChartsheet            = \"application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml\"\n\tContentTypeSpreadSheetMLComments              = \"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml\"\n\tContentTypeSpreadSheetMLPivotCacheDefinition  = \"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml\"\n\tContentTypeSpreadSheetMLPivotTable            = \"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml\"\n\tContentTypeSpreadSheetMLSharedStrings         = \"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml\"\n\tContentTypeSpreadSheetMLTable                 = \"application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml\"\n\tContentTypeSpreadSheetMLWorksheet             = \"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"\n\tContentTypeTemplate                           = \"application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml\"\n\tContentTypeTemplateMacro                      = \"application/vnd.ms-excel.template.macroEnabled.main+xml\"\n\tContentTypeVBA                                = \"application/vnd.ms-office.vbaProject\"\n\tContentTypeVML                                = \"application/vnd.openxmlformats-officedocument.vmlDrawing\"\n\tNameSpaceDrawingMLMain                        = \"http://schemas.openxmlformats.org/drawingml/2006/main\"\n\tNameSpaceDublinCore                           = \"http://purl.org/dc/elements/1.1/\"\n\tNameSpaceDublinCoreMetadataInitiative         = \"http://purl.org/dc/dcmitype/\"\n\tNameSpaceDublinCoreTerms                      = \"http://purl.org/dc/terms/\"\n\tNameSpaceExtendedProperties                   = \"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties\"\n\tNameSpaceXML                                  = \"http://www.w3.org/XML/1998/namespace\"\n\tNameSpaceXMLSchemaInstance                    = \"http://www.w3.org/2001/XMLSchema-instance\"\n\tSourceRelationshipChart                       = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart\"\n\tSourceRelationshipChartsheet                  = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet\"\n\tSourceRelationshipComments                    = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments\"\n\tSourceRelationshipCustomProperties            = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties\"\n\tSourceRelationshipDialogsheet                 = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet\"\n\tSourceRelationshipDrawingML                   = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing\"\n\tSourceRelationshipDrawingVML                  = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing\"\n\tSourceRelationshipExtendProperties            = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties\"\n\tSourceRelationshipHyperLink                   = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink\"\n\tSourceRelationshipImage                       = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image\"\n\tSourceRelationshipOfficeDocument              = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\"\n\tSourceRelationshipPivotCache                  = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition\"\n\tSourceRelationshipPivotTable                  = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable\"\n\tSourceRelationshipSharedStrings               = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings\"\n\tSourceRelationshipSlicer                      = \"http://schemas.microsoft.com/office/2007/relationships/slicer\"\n\tSourceRelationshipSlicerCache                 = \"http://schemas.microsoft.com/office/2007/relationships/slicerCache\"\n\tSourceRelationshipTable                       = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/table\"\n\tSourceRelationshipVBAProject                  = \"http://schemas.microsoft.com/office/2006/relationships/vbaProject\"\n\tSourceRelationshipWorkSheet                   = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\"\n\tStrictNameSpaceDocumentPropertiesVariantTypes = \"http://purl.oclc.org/ooxml/officeDocument/docPropsVTypes\"\n\tStrictNameSpaceDrawingMLMain                  = \"http://purl.oclc.org/ooxml/drawingml/main\"\n\tStrictNameSpaceExtendedProperties             = \"http://purl.oclc.org/ooxml/officeDocument/extendedProperties\"\n\tStrictNameSpaceSpreadSheet                    = \"http://purl.oclc.org/ooxml/spreadsheetml/main\"\n\tStrictSourceRelationship                      = \"http://purl.oclc.org/ooxml/officeDocument/relationships\"\n\tStrictSourceRelationshipChart                 = \"http://purl.oclc.org/ooxml/officeDocument/relationships/chart\"\n\tStrictSourceRelationshipComments              = \"http://purl.oclc.org/ooxml/officeDocument/relationships/comments\"\n\tStrictSourceRelationshipExtendProperties      = \"http://purl.oclc.org/ooxml/officeDocument/relationships/extendedProperties\"\n\tStrictSourceRelationshipImage                 = \"http://purl.oclc.org/ooxml/officeDocument/relationships/image\"\n\tStrictSourceRelationshipOfficeDocument        = \"http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument\"\n\t// The following constants defined the extLst child element\n\t// ([ISO/IEC29500-1:2016] section 18.2.10) of the workbook and worksheet\n\t// elements extended by the addition of new child ext elements.\n\tExtURICalcFeatures                   = \"{B58B0392-4F1F-4190-BB64-5DF3571DCE5F}\"\n\tExtURIConditionalFormattingRuleID    = \"{B025F937-C7B1-47D3-B67F-A62EFF666E3E}\"\n\tExtURIConditionalFormattings         = \"{78C0D931-6437-407d-A8EE-F0AAD7539E65}\"\n\tEXtURICustomPropertyFmtID            = \"{D5CDD505-2E9C-101B-9397-08002B2CF9AE}\"\n\tExtURIDataField                      = \"{E15A36E0-9728-4E99-A89B-3F7291B0FE68}\"\n\tExtURIDataModel                      = \"{FCE2AD5D-F65C-4FA6-A056-5C36A1767C68}\"\n\tExtURIDataValidations                = \"{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}\"\n\tExtURIDrawingBlip                    = \"{28A0092B-C50C-407E-A947-70E740481C1C}\"\n\tExtURIExternalLinkPr                 = \"{FCE6A71B-6B00-49CD-AB44-F6B1AE7CDE65}\"\n\tExtURIIgnoredErrors                  = \"{01252117-D84E-4E92-8308-4BE1C098FCBB}\"\n\tExtURIMacExcelMX                     = \"{64002731-A6B0-56B0-2670-7721B7C09600}\"\n\tExtURIModelTimeGroupings             = \"{9835A34E-60A6-4A7C-AAB8-D5F71C897F49}\"\n\tExtURIPivotCacheDefinition           = \"{725AE2AE-9491-48be-B2B4-4EB974FC3084}\"\n\tExtURIPivotCachesX14                 = \"{876F7934-8845-4945-9796-88D515C7AA90}\"\n\tExtURIPivotCachesX15                 = \"{841E416B-1EF1-43b6-AB56-02D37102CBD5}\"\n\tExtURIPivotField                     = \"{2946ED86-A175-432a-8AC1-64E0C546D7DE}\"\n\tExtURIPivotFilter                    = \"{0605FD5F-26C8-4aeb-8148-2DB25E43C511}\"\n\tExtURIPivotHierarchy                 = \"{F1805F06-0CD304483-9156-8803C3D141DF}\"\n\tExtURIPivotTableReferences           = \"{983426D0-5260-488c-9760-48F4B6AC55F4}\"\n\tExtURIProtectedRanges                = \"{FC87AEE6-9EDD-4A0A-B7FB-166176984837}\"\n\tExtURISlicerCacheDefinition          = \"{2F2917AC-EB37-4324-AD4E-5DD8C200BD13}\"\n\tExtURISlicerCacheHideItemsWithNoData = \"{470722E0-AACD-4C17-9CDC-17EF765DBC7E}\"\n\tExtURISlicerCachesX14                = \"{BBE1A952-AA13-448e-AADC-164F8A28A991}\"\n\tExtURISlicerCachesX15                = \"{46BE6895-7355-4a93-B00E-2C351335B9C9}\"\n\tExtURISlicerListX14                  = \"{A8765BA9-456A-4dab-B4F3-ACF838C121DE}\"\n\tExtURISlicerListX15                  = \"{3A4CF648-6AED-40f4-86FF-DC5316D8AED3}\"\n\tExtURISparklineGroups                = \"{05C60535-1F16-4fd2-B633-F4F36F0B64E0}\"\n\tExtURISVG                            = \"{96DAC541-7B7A-43D3-8B79-37D633B846F1}\"\n\tExtURITimelineCachePivotCaches       = \"{A2CB5862-8E78-49c6-8D9D-AF26E26ADB89}\"\n\tExtURITimelineCacheRefs              = \"{D0CA8CA8-9F24-4464-BF8E-62219DCF47F9}\"\n\tExtURITimelineRefs                   = \"{7E03D99C-DC04-49d9-9315-930204A7B6E9}\"\n\tExtURIWebExtensions                  = \"{F7C9EE02-42E1-4005-9D12-6889AFFD525C}\"\n\tExtURIWorkbookPrX14                  = \"{79F54976-1DA5-4618-B147-ACDE4B953A38}\"\n\tExtURIWorkbookPrX15                  = \"{140A7094-0E35-4892-8432-C4D2E57EDEB5}\"\n)\n\n// workbookExtURIPriority is the priority of URI in the workbook extension lists.\nvar workbookExtURIPriority = []string{\n\tExtURIPivotCachesX14,\n\tExtURISlicerCachesX14,\n\tExtURISlicerCachesX15,\n\tExtURIWorkbookPrX14,\n\tExtURIPivotCachesX15,\n\tExtURIPivotTableReferences,\n\tExtURITimelineCachePivotCaches,\n\tExtURITimelineCacheRefs,\n\tExtURIWorkbookPrX15,\n\tExtURIDataModel,\n\tExtURICalcFeatures,\n\tExtURIExternalLinkPr,\n\tExtURIModelTimeGroupings,\n}\n\n// worksheetExtURIPriority is the priority of URI in the worksheet extension lists.\nvar worksheetExtURIPriority = []string{\n\tExtURIConditionalFormattings,\n\tExtURIDataValidations,\n\tExtURISparklineGroups,\n\tExtURISlicerListX14,\n\tExtURIProtectedRanges,\n\tExtURIIgnoredErrors,\n\tExtURIWebExtensions,\n\tExtURISlicerListX15,\n\tExtURITimelineRefs,\n\tExtURIExternalLinkPr,\n}\n\n// Excel specifications and limits\nconst (\n\tEMU                     = 9525\n\tMaxCellStyles           = 65430\n\tMaxColumns              = 16384\n\tMaxColumnWidth          = 255\n\tMaxFieldLength          = 255\n\tMaxFilePathLength       = 207\n\tMaxFormControlValue     = 30000\n\tMaxFontFamilyLength     = 31\n\tMaxGraphicAltTextLength = 65535\n\tMaxGraphicNameLength    = 254\n\tMaxFontSize             = 409\n\tMaxRowHeight            = 409\n\tMaxSheetNameLength      = 31\n\tMinColumns              = 1\n\tMinFontSize             = 1\n\tStreamChunkSize         = 1 << 24\n\tTotalCellChars          = 32767\n\tTotalRows               = 1048576\n\tTotalSheetHyperlinks    = 65529\n\tUnzipSizeLimit          = 1000 << 24\n\t// pivotTableVersion should be greater than 3. One or more of the\n\t// PivotTables chosen are created in a version of Excel earlier than\n\t// Excel 2007 or in compatibility mode. Slicer can only be used with\n\t// PivotTables created in Excel 2007 or a newer version of Excel.\n\tpivotTableVersion           = 3\n\tpivotTableRefreshedVersion  = 8\n\tdefaultDrawingScale         = 1.0\n\tdefaultChartDimensionWidth  = 480\n\tdefaultChartDimensionHeight = 260\n\tdefaultSlicerWidth          = 200\n\tdefaultSlicerHeight         = 200\n\tdefaultChartLegendPosition  = \"bottom\"\n\tdefaultChartShowBlanksAs    = \"gap\"\n\tdefaultShapeSize            = 160\n\tdefaultShapeLineWidth       = 1\n\tdefaultColWidth             = 9.140625\n\tdefaultColWidthPixels       = 64.0\n\tdefaultRowHeight            = 15.0\n\tdefaultRowHeightPixels      = 20.0\n\tdefaultFontSize             = 11.0\n)\n\n// ColorMappingType is the type of color transformation.\ntype ColorMappingType byte\n\n// Color transformation types enumeration.\nconst (\n\tColorMappingTypeLight1 ColorMappingType = iota\n\tColorMappingTypeDark1\n\tColorMappingTypeLight2\n\tColorMappingTypeDark2\n\tColorMappingTypeAccent1\n\tColorMappingTypeAccent2\n\tColorMappingTypeAccent3\n\tColorMappingTypeAccent4\n\tColorMappingTypeAccent5\n\tColorMappingTypeAccent6\n\tColorMappingTypeHyperlink\n\tColorMappingTypeFollowedHyperlink\n\tColorMappingTypeUnset int = -1\n)\n\n// ChartDataLabelPositionType is the type of chart data labels position.\ntype ChartDataLabelPositionType byte\n\n// Chart data labels positions types enumeration.\nconst (\n\tChartDataLabelsPositionUnset ChartDataLabelPositionType = iota\n\tChartDataLabelsPositionBestFit\n\tChartDataLabelsPositionBelow\n\tChartDataLabelsPositionCenter\n\tChartDataLabelsPositionInsideBase\n\tChartDataLabelsPositionInsideEnd\n\tChartDataLabelsPositionLeft\n\tChartDataLabelsPositionOutsideEnd\n\tChartDataLabelsPositionRight\n\tChartDataLabelsPositionAbove\n)\n\n// chartDataLabelsPositionTypes defined supported chart data labels position\n// types.\nvar chartDataLabelsPositionTypes = map[ChartDataLabelPositionType]string{\n\tChartDataLabelsPositionBestFit:    \"bestFit\",\n\tChartDataLabelsPositionBelow:      \"b\",\n\tChartDataLabelsPositionCenter:     \"ctr\",\n\tChartDataLabelsPositionInsideBase: \"inBase\",\n\tChartDataLabelsPositionInsideEnd:  \"inEnd\",\n\tChartDataLabelsPositionLeft:       \"l\",\n\tChartDataLabelsPositionOutsideEnd: \"outEnd\",\n\tChartDataLabelsPositionRight:      \"r\",\n\tChartDataLabelsPositionAbove:      \"t\",\n}\n\n// chartDashTypes defined supported preset chart dash types.\nvar chartDashTypes = map[ChartDashType]string{\n\tChartDashSolid:         \"solid\",\n\tChartDashDot:           \"dot\",\n\tChartDashDash:          \"dash\",\n\tChartDashLgDash:        \"lgDash\",\n\tChartDashSashDot:       \"dashDot\",\n\tChartDashLgDashDot:     \"lgDashDot\",\n\tChartDashLgDashDotDot:  \"lgDashDotDot\",\n\tChartDashSysDash:       \"sysDash\",\n\tChartDashSysDot:        \"sysDot\",\n\tChartDashSysDashDot:    \"sysDashDot\",\n\tChartDashSysDashDotDot: \"sysDashDotDot\",\n}\n\n// supportedChartDataLabelsPosition defined supported chart data labels position\n// types for each type of chart.\nvar supportedChartDataLabelsPosition = map[ChartType][]ChartDataLabelPositionType{\n\tBar:               {ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideBase, ChartDataLabelsPositionInsideEnd, ChartDataLabelsPositionOutsideEnd},\n\tBarStacked:        {ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideBase, ChartDataLabelsPositionInsideEnd},\n\tBarPercentStacked: {ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideBase, ChartDataLabelsPositionInsideEnd},\n\tCol:               {ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideBase, ChartDataLabelsPositionInsideEnd, ChartDataLabelsPositionOutsideEnd},\n\tColStacked:        {ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideBase, ChartDataLabelsPositionInsideEnd},\n\tColPercentStacked: {ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideBase, ChartDataLabelsPositionInsideEnd},\n\tLine:              {ChartDataLabelsPositionBelow, ChartDataLabelsPositionCenter, ChartDataLabelsPositionLeft, ChartDataLabelsPositionRight, ChartDataLabelsPositionAbove},\n\tPie:               {ChartDataLabelsPositionBestFit, ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideEnd, ChartDataLabelsPositionOutsideEnd},\n\tPie3D:             {ChartDataLabelsPositionBestFit, ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideEnd, ChartDataLabelsPositionOutsideEnd},\n\tScatter:           {ChartDataLabelsPositionBelow, ChartDataLabelsPositionCenter, ChartDataLabelsPositionLeft, ChartDataLabelsPositionRight, ChartDataLabelsPositionAbove},\n\tBubble:            {ChartDataLabelsPositionBelow, ChartDataLabelsPositionCenter, ChartDataLabelsPositionLeft, ChartDataLabelsPositionRight, ChartDataLabelsPositionAbove},\n\tBubble3D:          {ChartDataLabelsPositionBelow, ChartDataLabelsPositionCenter, ChartDataLabelsPositionLeft, ChartDataLabelsPositionRight, ChartDataLabelsPositionAbove},\n}\n\nconst (\n\tdefaultTempFileSST                = \"sharedStrings\"\n\tdefaultXMLMetadata                = \"xl/metadata.xml\"\n\tdefaultXMLPathCalcChain           = \"xl/calcChain.xml\"\n\tdefaultXMLPathCellImages          = \"xl/cellimages.xml\"\n\tdefaultXMLPathCellImagesRels      = \"xl/_rels/cellimages.xml.rels\"\n\tdefaultXMLPathContentTypes        = \"[Content_Types].xml\"\n\tdefaultXMLPathDocPropsApp         = \"docProps/app.xml\"\n\tdefaultXMLPathDocPropsCore        = \"docProps/core.xml\"\n\tdefaultXMLPathDocPropsCustom      = \"docProps/custom.xml\"\n\tdefaultXMLPathRels                = \"_rels/.rels\"\n\tdefaultXMLPathSharedStrings       = \"xl/sharedStrings.xml\"\n\tdefaultXMLPathSheet               = \"xl/worksheets/sheet1.xml\"\n\tdefaultXMLPathStyles              = \"xl/styles.xml\"\n\tdefaultXMLPathTheme               = \"xl/theme/theme1.xml\"\n\tdefaultXMLPathVolatileDeps        = \"xl/volatileDependencies.xml\"\n\tdefaultXMLPathWorkbook            = \"xl/workbook.xml\"\n\tdefaultXMLPathWorkbookRels        = \"xl/_rels/workbook.xml.rels\"\n\tdefaultXMLRdRichValue             = \"xl/richData/rdrichvalue.xml\"\n\tdefaultXMLRdRichValueRel          = \"xl/richData/richValueRel.xml\"\n\tdefaultXMLRdRichValueRelRels      = \"xl/richData/_rels/richValueRel.xml.rels\"\n\tdefaultXMLRdRichValueStructure    = \"xl/richData/rdrichvaluestructure.xml\"\n\tdefaultXMLRdRichValueTypes        = \"xl/richData/rdRichValueTypes.xml\"\n\tdefaultXMLRdRichValueWebImage     = \"xl/richData/rdRichValueWebImage.xml\"\n\tdefaultXMLRdRichValueWebImageRels = \"xl/richData/_rels/rdRichValueWebImage.xml.rels\"\n)\n\n// IndexedColorMapping is the table of default mappings from indexed color value\n// to RGB value. Note that 0-7 are redundant of 8-15 to preserve backwards\n// compatibility. A legacy indexing scheme for colors that is still required\n// for some records, and for backwards compatibility with legacy formats. This\n// element contains a sequence of RGB color values that correspond to color\n// indexes (zero-based). When using the default indexed color palette, the\n// values are not written out, but instead are implied. When the color palette\n// has been modified from default, then the entire color palette is written\n// out.\nvar IndexedColorMapping = []string{\n\t\"000000\", \"FFFFFF\", \"FF0000\", \"00FF00\", \"0000FF\", \"FFFF00\", \"FF00FF\", \"00FFFF\",\n\t\"000000\", \"FFFFFF\", \"FF0000\", \"00FF00\", \"0000FF\", \"FFFF00\", \"FF00FF\", \"00FFFF\",\n\t\"800000\", \"008000\", \"000080\", \"808000\", \"800080\", \"008080\", \"C0C0C0\", \"808080\",\n\t\"9999FF\", \"993366\", \"FFFFCC\", \"CCFFFF\", \"660066\", \"FF8080\", \"0066CC\", \"CCCCFF\",\n\t\"000080\", \"FF00FF\", \"FFFF00\", \"00FFFF\", \"800080\", \"800000\", \"008080\", \"0000FF\",\n\t\"00CCFF\", \"CCFFFF\", \"CCFFCC\", \"FFFF99\", \"99CCFF\", \"FF99CC\", \"CC99FF\", \"FFCC99\",\n\t\"3366FF\", \"33CCCC\", \"99CC00\", \"FFCC00\", \"FF9900\", \"FF6600\", \"666699\", \"969696\",\n\t\"003366\", \"339966\", \"003300\", \"333300\", \"993300\", \"993366\", \"333399\", \"333333\",\n\t\"000000\", \"FFFFFF\",\n}\n\n// supportedDefinedNameAtStartCharCodeRange list the valid first character of a\n// defined name ASCII letters.\nvar supportedDefinedNameAtStartCharCodeRange = []int{\n\t65, 90, 92, 92, 95, 95, 97, 122, 161, 161, 164, 164,\n\t167, 168, 170, 170, 173, 173, 175, 186, 188, 696, 699, 705,\n\t711, 711, 713, 715, 717, 717, 720, 721, 728, 731, 733, 733,\n\t736, 740, 750, 750, 880, 883, 886, 887, 890, 893, 902, 902,\n\t904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1162, 1315,\n\t1329, 1366, 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1569, 1610,\n\t1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, 1786, 1788,\n\t1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, 1969, 1969, 1994, 2026,\n\t2036, 2037, 2042, 2042, 2308, 2361, 2365, 2365, 2384, 2384, 2392, 2401,\n\t2417, 2418, 2427, 2431, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480,\n\t2482, 2482, 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529,\n\t2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611,\n\t2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2701,\n\t2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749,\n\t2768, 2768, 2784, 2785, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864,\n\t2866, 2867, 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929,\n\t2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972,\n\t2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3024, 3024, 3077, 3084,\n\t3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3133, 3133, 3160, 3161,\n\t3168, 3169, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257,\n\t3261, 3261, 3294, 3294, 3296, 3297, 3333, 3340, 3342, 3344, 3346, 3368,\n\t3370, 3385, 3389, 3389, 3424, 3425, 3450, 3455, 3461, 3478, 3482, 3505,\n\t3507, 3515, 3517, 3517, 3520, 3526, 3585, 3642, 3648, 3662, 3713, 3714,\n\t3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743,\n\t3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760, 3762, 3763,\n\t3773, 3773, 3776, 3780, 3782, 3782, 3804, 3805, 3840, 3840, 3904, 3911,\n\t3913, 3948, 3976, 3979, 4096, 4138, 4159, 4159, 4176, 4181, 4186, 4189,\n\t4193, 4193, 4197, 4198, 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293,\n\t4304, 4346, 4348, 4348, 4352, 4441, 4447, 4514, 4520, 4601, 4608, 4680,\n\t4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749,\n\t4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822,\n\t4824, 4880, 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5108, 5121, 5740,\n\t5743, 5750, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900, 5902, 5905,\n\t5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000, 6016, 6067, 6103, 6103,\n\t6108, 6108, 6176, 6263, 6272, 6312, 6314, 6314, 6400, 6428, 6480, 6509,\n\t6512, 6516, 6528, 6569, 6593, 6599, 6656, 6678, 6917, 6963, 6981, 6987,\n\t7043, 7072, 7086, 7087, 7168, 7203, 7245, 7247, 7258, 7293, 7424, 7615,\n\t7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025,\n\t8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126,\n\t8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180,\n\t8182, 8188, 8208, 8208, 8211, 8214, 8216, 8216, 8220, 8221, 8224, 8225,\n\t8229, 8231, 8240, 8240, 8242, 8243, 8245, 8245, 8251, 8251, 8305, 8305,\n\t8308, 8308, 8319, 8319, 8321, 8324, 8336, 8340, 8450, 8451, 8453, 8453,\n\t8455, 8455, 8457, 8467, 8469, 8470, 8473, 8477, 8481, 8482, 8484, 8484,\n\t8486, 8486, 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521,\n\t8526, 8526, 8531, 8532, 8539, 8542, 8544, 8584, 8592, 8601, 8658, 8658,\n\t8660, 8660, 8704, 8704, 8706, 8707, 8711, 8712, 8715, 8715, 8719, 8719,\n\t8721, 8721, 8725, 8725, 8730, 8730, 8733, 8736, 8739, 8739, 8741, 8741,\n\t8743, 8748, 8750, 8750, 8756, 8759, 8764, 8765, 8776, 8776, 8780, 8780,\n\t8786, 8786, 8800, 8801, 8804, 8807, 8810, 8811, 8814, 8815, 8834, 8835,\n\t8838, 8839, 8853, 8853, 8857, 8857, 8869, 8869, 8895, 8895, 8978, 8978,\n\t9312, 9397, 9424, 9449, 9472, 9547, 9552, 9588, 9601, 9615, 9618, 9621,\n\t9632, 9633, 9635, 9641, 9650, 9651, 9654, 9655, 9660, 9661, 9664, 9665,\n\t9670, 9672, 9675, 9675, 9678, 9681, 9698, 9701, 9711, 9711, 9733, 9734,\n\t9737, 9737, 9742, 9743, 9756, 9756, 9758, 9758, 9792, 9792, 9794, 9794,\n\t9824, 9825, 9827, 9829, 9831, 9834, 9836, 9837, 9839, 9839, 11264, 11310,\n\t11312, 11358, 11360, 11375, 11377, 11389, 11392, 11492, 11520, 11557, 11568, 11621,\n\t11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710,\n\t11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 12288, 12291, 12293, 12311,\n\t12317, 12319, 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, 12443, 12447,\n\t12449, 12543, 12549, 12589, 12593, 12686, 12704, 12727, 12784, 12828, 12832, 12841,\n\t12849, 12850, 12857, 12857, 12896, 12923, 12927, 12927, 12963, 12968, 13059, 13059,\n\t13069, 13069, 13076, 13076, 13080, 13080, 13090, 13091, 13094, 13095, 13099, 13099,\n\t13110, 13110, 13115, 13115, 13129, 13130, 13133, 13133, 13137, 13137, 13143, 13143,\n\t13179, 13182, 13184, 13188, 13192, 13258, 13261, 13267, 13269, 13270, 13272, 13272,\n\t13275, 13277, 13312, 19893, 19968, 40899, 40960, 42124, 42240, 42508, 42512, 42527,\n\t42538, 42539, 42560, 42591, 42594, 42606, 42624, 42647, 42786, 42887, 42891, 42892,\n\t43003, 43009, 43011, 43013, 43015, 43018, 43020, 43042, 43072, 43123, 43138, 43187,\n\t43274, 43301, 43312, 43334, 43520, 43560, 43584, 43586, 43588, 43595, 44032, 55203,\n\t57344, 63560, 63744, 64045, 64048, 64106, 64112, 64217, 64256, 64262, 64275, 64279,\n\t64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321,\n\t64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019,\n\t65072, 65073, 65075, 65092, 65097, 65106, 65108, 65111, 65113, 65126, 65128, 65131,\n\t65136, 65140, 65142, 65276, 65281, 65374, 65377, 65470, 65474, 65479, 65482, 65487,\n\t65490, 65495, 65498, 65500, 65504, 65510,\n}\n\n// supportedDefinedNameAfterStartCharCodeRange list the valid after first\n// character of a defined name ASCII letters.\nvar supportedDefinedNameAfterStartCharCodeRange = []int{\n\t46, 46, 48, 57, 63, 63, 65, 90, 92, 92, 95, 95,\n\t97, 122, 161, 161, 164, 164, 167, 168, 170, 170, 173, 173,\n\t175, 186, 188, 887, 890, 893, 900, 902, 904, 906, 908, 908,\n\t910, 929, 931, 1315, 1329, 1366, 1369, 1369, 1377, 1415, 1425, 1469,\n\t1471, 1471, 1473, 1474, 1476, 1477, 1479, 1479, 1488, 1514, 1520, 1522,\n\t1536, 1539, 1542, 1544, 1547, 1547, 1550, 1562, 1567, 1567, 1569, 1630,\n\t1632, 1641, 1646, 1747, 1749, 1791, 1807, 1866, 1869, 1969, 1984, 2038,\n\t2042, 2042, 2305, 2361, 2364, 2381, 2384, 2388, 2392, 2403, 2406, 2415,\n\t2417, 2418, 2427, 2431, 2433, 2435, 2437, 2444, 2447, 2448, 2451, 2472,\n\t2474, 2480, 2482, 2482, 2486, 2489, 2492, 2500, 2503, 2504, 2507, 2510,\n\t2519, 2519, 2524, 2525, 2527, 2531, 2534, 2554, 2561, 2563, 2565, 2570,\n\t2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617,\n\t2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2641, 2641, 2649, 2652,\n\t2654, 2654, 2662, 2677, 2689, 2691, 2693, 2701, 2703, 2705, 2707, 2728,\n\t2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765,\n\t2768, 2768, 2784, 2787, 2790, 2799, 2801, 2801, 2817, 2819, 2821, 2828,\n\t2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2876, 2884,\n\t2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2915, 2918, 2929,\n\t2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972,\n\t2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3006, 3010, 3014, 3016,\n\t3018, 3021, 3024, 3024, 3031, 3031, 3046, 3066, 3073, 3075, 3077, 3084,\n\t3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3133, 3140, 3142, 3144,\n\t3146, 3149, 3157, 3158, 3160, 3161, 3168, 3171, 3174, 3183, 3192, 3199,\n\t3202, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257,\n\t3260, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3299,\n\t3302, 3311, 3313, 3314, 3330, 3331, 3333, 3340, 3342, 3344, 3346, 3368,\n\t3370, 3385, 3389, 3396, 3398, 3400, 3402, 3405, 3415, 3415, 3424, 3427,\n\t3430, 3445, 3449, 3455, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515,\n\t3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551,\n\t3570, 3571, 3585, 3642, 3647, 3662, 3664, 3673, 3713, 3714, 3716, 3716,\n\t3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747,\n\t3749, 3749, 3751, 3751, 3754, 3755, 3757, 3769, 3771, 3773, 3776, 3780,\n\t3782, 3782, 3784, 3789, 3792, 3801, 3804, 3805, 3840, 3843, 3859, 3897,\n\t3902, 3911, 3913, 3948, 3953, 3972, 3974, 3979, 3984, 3991, 3993, 4028,\n\t4030, 4044, 4046, 4047, 4096, 4169, 4176, 4249, 4254, 4293, 4304, 4346,\n\t4348, 4348, 4352, 4441, 4447, 4514, 4520, 4601, 4608, 4680, 4682, 4685,\n\t4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784,\n\t4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880,\n\t4882, 4885, 4888, 4954, 4959, 4960, 4969, 4988, 4992, 5017, 5024, 5108,\n\t5121, 5740, 5743, 5750, 5760, 5786, 5792, 5866, 5870, 5872, 5888, 5900,\n\t5902, 5908, 5920, 5940, 5952, 5971, 5984, 5996, 5998, 6000, 6002, 6003,\n\t6016, 6099, 6103, 6103, 6107, 6109, 6112, 6121, 6128, 6137, 6155, 6158,\n\t6160, 6169, 6176, 6263, 6272, 6314, 6400, 6428, 6432, 6443, 6448, 6459,\n\t6464, 6464, 6470, 6509, 6512, 6516, 6528, 6569, 6576, 6601, 6608, 6617,\n\t6624, 6683, 6912, 6987, 6992, 7001, 7009, 7036, 7040, 7082, 7086, 7097,\n\t7168, 7223, 7232, 7241, 7245, 7293, 7424, 7654, 7678, 7957, 7960, 7965,\n\t7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029,\n\t8031, 8061, 8064, 8116, 8118, 8132, 8134, 8147, 8150, 8155, 8157, 8175,\n\t8178, 8180, 8182, 8190, 8192, 8208, 8211, 8214, 8216, 8216, 8220, 8221,\n\t8224, 8225, 8229, 8240, 8242, 8243, 8245, 8245, 8251, 8251, 8260, 8260,\n\t8274, 8274, 8287, 8292, 8298, 8305, 8308, 8316, 8319, 8332, 8336, 8340,\n\t8352, 8373, 8400, 8432, 8448, 8527, 8531, 8584, 8592, 9000, 9003, 9191,\n\t9216, 9254, 9280, 9290, 9312, 9885, 9888, 9916, 9920, 9923, 9985, 9988,\n\t9990, 9993, 9996, 10023, 10025, 10059, 10061, 10061, 10063, 10066, 10070, 10070,\n\t10072, 10078, 10081, 10087, 10102, 10132, 10136, 10159, 10161, 10174, 10176, 10180,\n\t10183, 10186, 10188, 10188, 10192, 10213, 10224, 10626, 10649, 10711, 10716, 10747,\n\t10750, 11084, 11088, 11092, 11264, 11310, 11312, 11358, 11360, 11375, 11377, 11389,\n\t11392, 11498, 11517, 11517, 11520, 11557, 11568, 11621, 11631, 11631, 11648, 11670,\n\t11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726,\n\t11728, 11734, 11736, 11742, 11744, 11775, 11823, 11823, 11904, 11929, 11931, 12019,\n\t12032, 12245, 12272, 12283, 12288, 12311, 12317, 12335, 12337, 12348, 12350, 12351,\n\t12353, 12438, 12441, 12447, 12449, 12543, 12549, 12589, 12593, 12686, 12688, 12727,\n\t12736, 12771, 12784, 12830, 12832, 12867, 12880, 13054, 13056, 19893, 19904, 40899,\n\t40960, 42124, 42128, 42182, 42240, 42508, 42512, 42539, 42560, 42591, 42594, 42610,\n\t42620, 42621, 42623, 42647, 42752, 42892, 43003, 43051, 43072, 43123, 43136, 43204,\n\t43216, 43225, 43264, 43310, 43312, 43347, 43520, 43574, 43584, 43597, 43600, 43609,\n\t44032, 55203, 55296, 64045, 64048, 64106, 64112, 64217, 64256, 64262, 64275, 64279,\n\t64285, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433,\n\t64467, 64829, 64848, 64911, 64914, 64967, 65008, 65021, 65024, 65039, 65056, 65062,\n\t65072, 65073, 65075, 65092, 65097, 65106, 65108, 65111, 65113, 65126, 65128, 65131,\n\t65136, 65140, 65142, 65276, 65279, 65279, 65281, 65374, 65377, 65470, 65474, 65479,\n\t65482, 65487, 65490, 65495, 65498, 65500, 65504, 65510, 65512, 65518, 65529, 65533,\n}\n\n// supportedFontWidthFactors defines the average character width ratio of common\n// font families. This is used for more accurate column width calculation when\n// different font families are used.\nvar supportedFontWidthFactors = map[string][]float64{\n\t\"calibri\":                        {0.97, 1.30, 1.00},\n\t\"agency fb\":                      {0.69, 0.86, 1.00},\n\t\"aptos\":                          {1.03, 1.37, 1.00},\n\t\"arial black\":                    {1.35, 1.59, 1.00},\n\t\"arial narrow\":                   {0.88, 1.20, 1.00},\n\t\"arial rounded mt bold\":          {1.07, 1.57, 1.00},\n\t\"arial unicode ms\":               {1.07, 1.42, 1.07},\n\t\"arial\":                          {1.07, 1.42, 1.00},\n\t\"bahnschrift\":                    {1.10, 1.30, 1.00},\n\t\"baskerville old face\":           {0.98, 1.56, 1.00},\n\t\"batang\":                         {1.05, 1.31, 0.99},\n\t\"batangche\":                      {1.10, 1.10, 1.10},\n\t\"bauhaus 93\":                     {1.10, 1.43, 1.00},\n\t\"bell mt\":                        {0.95, 1.33, 1.00},\n\t\"berlin sans fb\":                 {1.03, 1.36, 1.00},\n\t\"bernard mt condensed\":           {0.94, 1.24, 1.00},\n\t\"blackadder itc\":                 {0.76, 2.12, 1.00},\n\t\"bodoni mt condensed\":            {0.82, 1.17, 1.00},\n\t\"bodoni mt poster compressed\":    {0.72, 1.06, 1.00},\n\t\"bodoni mt\":                      {1.06, 1.47, 1.00},\n\t\"book antiqua\":                   {1.12, 1.44, 1.00},\n\t\"bookman old style\":              {1.14, 1.66, 1.00},\n\t\"broadway\":                       {1.50, 1.75, 1.00},\n\t\"brush script mt\":                {0.82, 1.34, 1.00},\n\t\"calibri light\":                  {0.98, 1.19, 1.00},\n\t\"calisto mt\":                     {1.03, 1.42, 1.00},\n\t\"cambria math\":                   {1.07, 1.28, 1.00},\n\t\"cambria\":                        {1.07, 1.28, 1.00},\n\t\"candara light\":                  {1.02, 1.35, 1.00},\n\t\"cascadia mono extralight\":       {1.42, 1.49, 1.00},\n\t\"castellar\":                      {1.48, 1.58, 1.00},\n\t\"centaur\":                        {0.93, 1.30, 1.00},\n\t\"century gothic\":                 {1.19, 1.36, 1.00},\n\t\"century schoolbook\":             {1.10, 1.63, 1.00},\n\t\"century\":                        {1.09, 1.52, 1.00},\n\t\"chiller\":                        {0.90, 1.22, 1.00},\n\t\"colonna mt\":                     {1.10, 1.40, 1.00},\n\t\"comic sans ms\":                  {1.14, 1.46, 1.00},\n\t\"consolas\":                       {1.21, 1.21, 1.00},\n\t\"cooper black\":                   {1.28, 1.60, 1.00},\n\t\"copperplate gothic bold\":        {1.32, 1.62, 1.00},\n\t\"copperplate gothic light\":       {1.26, 1.58, 1.00},\n\t\"corbel light\":                   {0.98, 1.26, 1.00},\n\t\"corbel\":                         {1.03, 1.30, 1.00},\n\t\"courier new\":                    {1.45, 1.45, 1.00},\n\t\"curlz mt\":                       {1.16, 1.47, 1.00},\n\t\"david\":                          {1.25, 1.28, 1.00},\n\t\"dengxian light\":                 {1.02, 1.26, 1.02},\n\t\"dengxian\":                       {1.05, 1.28, 1.08},\n\t\"dfkai-sb\":                       {1.10, 1.10, 1.10},\n\t\"dotum\":                          {1.25, 1.31, 0.99},\n\t\"dotumche\":                       {1.10, 1.10, 1.10},\n\t\"dubai\":                          {1.00, 1.32, 1.00},\n\t\"ebrima\":                         {1.08, 1.31, 1.00},\n\t\"edwardian script itc\":           {0.82, 1.78, 1.00},\n\t\"elephant\":                       {1.24, 1.86, 1.00},\n\t\"engravers mt\":                   {1.99, 1.99, 1.00},\n\t\"eras bold itc\":                  {1.14, 1.45, 1.00},\n\t\"eras demi itc\":                  {1.10, 1.42, 1.00},\n\t\"eras light itc\":                 {1.07, 1.40, 1.00},\n\t\"eras medium itc\":                {1.10, 1.42, 1.00},\n\t\"fangsong_gb2312\":                {1.20, 1.36, 1.10},\n\t\"felix titling\":                  {1.20, 1.55, 1.00},\n\t\"footlight mt light\":             {1.03, 1.37, 1.00},\n\t\"forte\":                          {0.93, 1.25, 1.00},\n\t\"franklin gothic demi cond\":      {0.88, 1.20, 1.00},\n\t\"franklin gothic demi\":           {1.08, 1.42, 1.00},\n\t\"franklin gothic heavy\":          {1.16, 1.45, 1.00},\n\t\"franklin gothic medium\":         {1.08, 1.42, 1.00},\n\t\"freestyle script\":               {0.87, 1.18, 1.00},\n\t\"french script mt\":               {0.82, 1.54, 1.00},\n\t\"fzshuti\":                        {1.10, 1.36, 1.10},\n\t\"fzyaoti\":                        {1.10, 1.36, 1.10},\n\t\"gabriola\":                       {0.95, 1.28, 1.00},\n\t\"garamond\":                       {1.16, 1.39, 1.00},\n\t\"georgia\":                        {1.19, 1.43, 1.00},\n\t\"gigi\":                           {0.95, 1.28, 1.00},\n\t\"gill sans mt condensed\":         {0.85, 1.18, 1.00},\n\t\"gill sans mt\":                   {1.03, 1.36, 1.00},\n\t\"gill sans ultra bold\":           {1.30, 1.60, 1.00},\n\t\"gloucester mt extra condensed\":  {0.72, 1.02, 1.00},\n\t\"goudy old style\":                {0.98, 1.37, 1.00},\n\t\"goudy stout\":                    {2.56, 2.80, 1.00},\n\t\"gulim\":                          {1.19, 1.31, 0.99},\n\t\"gulimche\":                       {1.20, 1.36, 1.10},\n\t\"gungsuh\":                        {1.39, 1.39, 0.99},\n\t\"gungsuhche\":                     {1.28, 1.36, 1.10},\n\t\"haettenschweiler\":               {0.82, 1.02, 1.00},\n\t\"harlow solid italic\":            {0.90, 1.82, 1.00},\n\t\"harrington\":                     {1.07, 1.40, 1.00},\n\t\"helvetica neue\":                 {1.10, 1.42, 1.00},\n\t\"helvetica\":                      {1.07, 1.42, 1.00},\n\t\"high tower text\":                {0.97, 1.53, 1.00},\n\t\"impact\":                         {1.01, 1.12, 1.00},\n\t\"informal roman\":                 {1.07, 1.40, 1.00},\n\t\"ink free\":                       {1.02, 1.33, 1.00},\n\t\"javanese text\":                  {1.10, 1.42, 1.00},\n\t\"jokerman\":                       {1.26, 1.67, 1.00},\n\t\"juice itc\":                      {0.82, 1.14, 1.00},\n\t\"kaiti_gb2312\":                   {1.20, 1.20, 1.10},\n\t\"kaiti\":                          {1.20, 1.20, 1.10},\n\t\"kartika\":                        {1.10, 1.32, 1.00},\n\t\"kokila\":                         {1.17, 1.30, 1.00},\n\t\"kristen itc\":                    {1.10, 1.42, 1.00},\n\t\"kunstler script\":                {0.72, 1.72, 1.00},\n\t\"latha\":                          {1.17, 1.28, 1.00},\n\t\"leelawadee ui\":                  {1.08, 1.31, 1.00},\n\t\"leelawadee\":                     {1.03, 1.36, 1.00},\n\t\"lisu\":                           {1.10, 1.42, 1.00},\n\t\"lucida bright\":                  {1.18, 1.47, 1.00},\n\t\"lucida calligraphy\":             {1.18, 1.67, 1.00},\n\t\"lucida console\":                 {1.32, 1.32, 1.00},\n\t\"lucida fax\":                     {1.14, 1.45, 1.00},\n\t\"lucida handwriting\":             {1.39, 1.67, 1.00},\n\t\"lucida sans typewriter\":         {1.32, 1.32, 1.00},\n\t\"lucida sans unicode\":            {1.18, 1.47, 1.00},\n\t\"lucida sans\":                    {1.18, 1.47, 1.00},\n\t\"lxgw wenkai\":                    {1.10, 1.60, 1.10},\n\t\"magneto\":                        {1.48, 1.98, 1.00},\n\t\"maiandra gd\":                    {1.10, 1.42, 1.00},\n\t\"malgun gothic semilight\":        {1.07, 1.51, 1.07},\n\t\"malgun gothic\":                  {1.10, 1.33, 1.10},\n\t\"mangal\":                         {1.10, 1.42, 1.00},\n\t\"meiryo\":                         {1.19, 1.42, 1.19},\n\t\"microsoft himalaya\":             {0.85, 0.96, 1.00},\n\t\"microsoft jhenghei light\":       {1.10, 1.30, 1.10},\n\t\"microsoft jhenghei ui\":          {1.16, 1.39, 1.16},\n\t\"microsoft jhenghei\":             {1.16, 1.39, 1.16},\n\t\"microsoft new tai lue\":          {1.03, 1.38, 1.00},\n\t\"microsoft phagspa\":              {1.00, 1.30, 1.00},\n\t\"microsoft sans serif\":           {1.07, 1.40, 1.00},\n\t\"microsoft tai le\":               {1.00, 1.30, 1.00},\n\t\"microsoft uighur\":               {0.90, 1.10, 1.00},\n\t\"microsoft yahei light\":          {1.10, 1.30, 1.10},\n\t\"microsoft yahei ui\":             {1.18, 1.30, 1.18},\n\t\"microsoft yahei\":                {1.18, 1.30, 1.18},\n\t\"microsoft yi baiti\":             {1.00, 1.12, 1.00},\n\t\"mingliu_hkscs\":                  {1.10, 1.20, 1.10},\n\t\"mingliu-extb\":                   {1.10, 1.20, 1.10},\n\t\"mingliu\":                        {1.10, 1.20, 1.10},\n\t\"mistral\":                        {0.87, 0.90, 1.00},\n\t\"modern no. 20\":                  {0.90, 1.42, 1.00},\n\t\"mongolian baiti\":                {0.95, 1.38, 1.00},\n\t\"monotype corsiva\":               {0.85, 1.26, 1.00},\n\t\"ms gothic\":                      {1.10, 1.10, 1.10},\n\t\"ms pgothic\":                     {0.99, 1.31, 0.99},\n\t\"ms reference sans serif\":        {1.18, 1.47, 1.00},\n\t\"ms sans serif\":                  {1.10, 1.42, 1.00},\n\t\"narkisim\":                       {1.10, 1.25, 1.00},\n\t\"niagara engraved\":               {0.55, 0.58, 1.00},\n\t\"niagara solid\":                  {0.48, 0.58, 1.00},\n\t\"nirmala ui semilight\":           {1.01, 1.32, 1.00},\n\t\"nirmala ui\":                     {1.03, 1.36, 1.00},\n\t\"noto sans cjk\":                  {1.10, 1.33, 1.10},\n\t\"noto serif cjk\":                 {1.10, 1.33, 1.10},\n\t\"nsimsun\":                        {1.10, 1.10, 1.10},\n\t\"nyala\":                          {1.15, 1.32, 1.00},\n\t\"onyx\":                           {0.68, 0.98, 1.00},\n\t\"palace script mt\":               {0.72, 1.22, 1.00},\n\t\"palatino linotype\":              {1.12, 1.44, 1.00},\n\t\"papyrus\":                        {1.10, 1.82, 1.00},\n\t\"parchment\":                      {0.74, 1.84, 1.00},\n\t\"perpetua titling mt\":            {1.23, 1.52, 1.00},\n\t\"perpetua\":                       {0.95, 1.33, 1.00},\n\t\"playbill\":                       {0.72, 1.02, 1.00},\n\t\"poor richard\":                   {0.97, 1.36, 1.00},\n\t\"pristina\":                       {0.87, 1.18, 1.00},\n\t\"rage italic\":                    {0.82, 1.14, 1.00},\n\t\"ravie\":                          {1.55, 1.86, 1.00},\n\t\"rockwell condensed\":             {0.90, 1.22, 1.00},\n\t\"rockwell extra bold\":            {1.42, 1.72, 1.00},\n\t\"rockwell\":                       {1.16, 1.48, 1.00},\n\t\"rod\":                            {1.25, 1.25, 1.00},\n\t\"sakkal majalla\":                 {1.10, 1.22, 1.00},\n\t\"sarasa gothic\":                  {1.10, 1.33, 1.10},\n\t\"segoe print\":                    {1.16, 1.47, 1.00},\n\t\"segoe script\":                   {1.10, 1.42, 1.00},\n\t\"segoe ui black\":                 {1.12, 1.34, 1.00},\n\t\"segoe ui emoji\":                 {1.08, 1.31, 1.00},\n\t\"segoe ui historic\":              {1.08, 1.31, 1.00},\n\t\"segoe ui semibold\":              {1.12, 1.34, 1.00},\n\t\"segoe ui semilight\":             {1.08, 1.31, 1.00},\n\t\"segoe ui symbol\":                {1.08, 1.31, 1.00},\n\t\"segoe ui\":                       {1.08, 1.31, 1.00},\n\t\"shonar bangla\":                  {1.00, 1.32, 1.00},\n\t\"showcard gothic\":                {1.50, 1.92, 1.00},\n\t\"shruti\":                         {0.97, 1.30, 1.00},\n\t\"simfang\":                        {1.10, 1.10, 1.10},\n\t\"simhei\":                         {1.10, 1.10, 1.10},\n\t\"simkai\":                         {1.10, 1.10, 1.10},\n\t\"simsun-extb\":                    {1.10, 1.10, 1.10},\n\t\"simsun\":                         {1.10, 1.10, 1.10},\n\t\"sitka banner\":                   {0.95, 1.28, 1.00},\n\t\"sitka display\":                  {1.00, 1.32, 1.00},\n\t\"sitka heading\":                  {1.03, 1.36, 1.00},\n\t\"sitka small\":                    {1.07, 1.40, 1.00},\n\t\"sitka subheading\":               {1.03, 1.36, 1.00},\n\t\"snap itc\":                       {1.32, 1.62, 1.00},\n\t\"source han sans\":                {1.10, 1.33, 1.10},\n\t\"source han serif\":               {1.10, 1.33, 1.10},\n\t\"stcaiyun\":                       {1.10, 1.10, 1.10},\n\t\"stencil\":                        {1.16, 1.47, 1.00},\n\t\"stfangsong\":                     {1.10, 1.90, 1.10},\n\t\"stheiti\":                        {1.10, 1.10, 1.10},\n\t\"sthupo\":                         {1.10, 1.90, 1.10},\n\t\"stkaiti\":                        {1.10, 1.10, 1.10},\n\t\"stliti\":                         {1.10, 1.10, 1.10},\n\t\"stsong\":                         {1.10, 1.36, 1.10},\n\t\"stxingkai\":                      {1.10, 1.36, 1.10},\n\t\"stxinwei\":                       {1.10, 1.36, 1.10},\n\t\"stzhongsong\":                    {1.10, 1.66, 1.10},\n\t\"sylfaen\":                        {1.03, 1.36, 1.00},\n\t\"tahoma\":                         {1.17, 1.30, 1.00},\n\t\"tempus sans itc\":                {1.14, 1.46, 1.00},\n\t\"times new roman\":                {1.11, 1.40, 1.00},\n\t\"traditional arabic\":             {0.95, 1.56, 1.00},\n\t\"trebuchet ms\":                   {1.10, 1.26, 1.00},\n\t\"tunga\":                          {1.15, 1.28, 1.00},\n\t\"tw cen mt condensed extra bold\": {0.88, 1.22, 1.00},\n\t\"tw cen mt condensed\":            {0.86, 1.18, 1.00},\n\t\"tw cen mt\":                      {0.98, 1.32, 1.00},\n\t\"ud digi kyokasho n-b\":           {1.30, 1.36, 1.10},\n\t\"ud digi kyokasho n-r\":           {1.30, 1.36, 1.10},\n\t\"ud digi kyokasho nk-b\":          {1.30, 1.36, 1.10},\n\t\"ud digi kyokasho nk-r\":          {1.30, 1.36, 1.10},\n\t\"ud デジタル 教科書体 n\":                 {1.30, 1.36, 1.10},\n\t\"ud デジタル 教科書体 nk\":                {1.30, 1.36, 1.10},\n\t\"urdu typesetting\":               {1.52, 1.52, 1.00},\n\t\"utsaah\":                         {1.43, 1.43, 1.00},\n\t\"vani\":                           {1.27, 1.30, 1.00},\n\t\"verdana\":                        {1.33, 1.44, 1.00},\n\t\"vijaya\":                         {1.10, 1.22, 1.00},\n\t\"vivaldi\":                        {0.76, 2.12, 1.00},\n\t\"vladimir script\":                {0.82, 1.54, 1.00},\n\t\"vrinda\":                         {1.15, 1.28, 1.00},\n\t\"wide latin\":                     {1.88, 2.65, 1.00},\n\t\"youyuan\":                        {1.20, 1.36, 1.10},\n\t\"yu gothic light\":                {1.03, 1.34, 1.03},\n\t\"yu gothic medium\":               {1.12, 1.41, 1.12},\n\t\"yu gothic ui light\":             {1.03, 1.24, 1.03},\n\t\"yu gothic ui semibold\":          {1.12, 1.31, 1.12},\n\t\"yu gothic ui semilight\":         {1.03, 1.24, 1.03},\n\t\"yu gothic ui\":                   {1.12, 1.41, 1.12},\n\t\"yu gothic\":                      {1.12, 1.41, 1.12},\n\t\"yu mincho\":                      {1.08, 1.48, 1.08},\n\t\"굴림\":                             {0.99, 1.31, 1.01},\n\t\"굴림체\":                            {1.10, 1.36, 1.20},\n\t\"궁서\":                             {0.99, 1.31, 1.01},\n\t\"궁서체\":                            {1.10, 1.36, 1.20},\n\t\"돋움\":                             {0.99, 1.31, 0.99},\n\t\"돋움체\":                            {1.10, 1.10, 1.15},\n\t\"맑은 고딕 semilight\":                {1.07, 1.51, 1.07},\n\t\"맑은 고딕\":                          {1.10, 1.33, 1.10},\n\t\"바탕\":                             {0.99, 1.31, 1.01},\n\t\"바탕체\":                            {1.10, 1.10, 1.20},\n\t\"メイリオ\":                           {1.19, 1.42, 1.19},\n\t\"仿宋\":                             {1.20, 1.36, 1.40},\n\t\"华文中宋\":                           {1.10, 1.66, 1.10},\n\t\"华文仿宋\":                           {1.10, 1.90, 1.10},\n\t\"华文宋体\":                           {1.10, 1.36, 1.10},\n\t\"华文彩云\":                           {1.10, 1.10, 1.10},\n\t\"华文新魏\":                           {1.10, 1.36, 1.10},\n\t\"华文楷体\":                           {1.10, 1.10, 1.10},\n\t\"华文琥珀\":                           {1.10, 1.90, 1.10},\n\t\"华文行楷\":                           {1.10, 1.36, 1.10},\n\t\"华文隶书\":                           {1.10, 1.10, 1.10},\n\t\"华文黑体\":                           {1.10, 1.10, 1.10},\n\t\"宋体\":                             {1.10, 1.10, 1.10},\n\t\"幼圆\":                             {1.10, 1.36, 1.10},\n\t\"微软正黑体 light\":                    {1.10, 1.30, 1.10},\n\t\"微软正黑体\":                          {1.16, 1.39, 1.16},\n\t\"微软雅黑 light\":                     {1.10, 1.30, 1.10},\n\t\"微软雅黑\":                           {1.18, 1.30, 1.18},\n\t\"思源宋体\":                           {1.10, 1.33, 1.10},\n\t\"思源黑体\":                           {1.10, 1.33, 1.10},\n\t\"新宋体\":                            {1.10, 1.10, 1.10},\n\t\"新细明体\":                           {1.10, 1.20, 1.10},\n\t\"方正姚体\":                           {1.10, 1.36, 1.10},\n\t\"方正舒体\":                           {1.10, 1.36, 1.10},\n\t\"更纱黑体\":                           {1.10, 1.33, 1.10},\n\t\"楷体\":                             {1.20, 1.20, 1.10},\n\t\"標楷體\":                            {1.20, 1.20, 1.10},\n\t\"游ゴシック light\":                    {1.03, 1.34, 1.03},\n\t\"游ゴシック medium\":                   {1.12, 1.41, 1.12},\n\t\"游ゴシック ui\":                       {1.12, 1.41, 1.12},\n\t\"游ゴシック\":                          {1.12, 1.41, 1.12},\n\t\"游明朝\":                            {1.08, 1.48, 1.08},\n\t\"等线\":                             {1.05, 1.28, 1.05},\n\t\"细明体\":                            {1.10, 1.20, 1.10},\n\t\"隶书\":                             {1.10, 1.10, 1.10},\n\t\"霞鹜文楷\":                           {1.10, 1.60, 1.10},\n\t\"黑体\":                             {1.10, 1.10, 1.10},\n\t\"\\uff2d\\uff33 \\uff30ゴシック\":        {0.99, 1.31, 0.99},\n\t\"\\uff2d\\uff33 ゴシック\":              {1.10, 1.10, 1.10},\n\t\"\\uff2d\\uff33 明朝\":                {1.10, 1.10, 1.10},\n}\n\n// supportedImageTypes defined supported image types.\nvar supportedImageTypes = map[string]string{\n\t\".bmp\": \".bmp\", \".emf\": \".emf\", \".emz\": \".emz\", \".gif\": \".gif\",\n\t\".ico\": \".ico\", \".jpeg\": \".jpeg\", \".jpg\": \".jpeg\", \".png\": \".png\",\n\t\".svg\": \".svg\", \".tif\": \".tiff\", \".tiff\": \".tiff\", \".wmf\": \".wmf\",\n\t\".wmz\": \".wmz\",\n}\n\n// supportedCalcMode defined supported formula calculate mode.\nvar supportedCalcMode = []string{\"manual\", \"auto\", \"autoNoTable\"}\n\n// supportedRefMode defined supported formula calculate mode.\nvar supportedRefMode = []string{\"A1\", \"R1C1\"}\n\n// supportedContentTypes defined supported file format types.\nvar supportedContentTypes = map[string]string{\n\t\".xlam\": ContentTypeAddinMacro,\n\t\".xlsm\": ContentTypeMacro,\n\t\".xlsx\": ContentTypeSheetML,\n\t\".xltm\": ContentTypeTemplateMacro,\n\t\".xltx\": ContentTypeTemplate,\n}\n\n// supportedUnderlineTypes defined supported underline types.\nvar supportedUnderlineTypes = []string{\"none\", \"single\", \"double\"}\n\n// supportedVertAlignTypes defined supported vertical align types.\nvar supportedVertAlignTypes = []string{\"baseline\", \"superscript\", \"subscript\"}\n\n// supportedDrawingUnderlineTypes defined supported underline types in drawing\n// markup language.\nvar supportedDrawingUnderlineTypes = []string{\n\t\"none\", \"words\", \"sng\", \"dbl\", \"heavy\", \"dotted\", \"dottedHeavy\", \"dash\", \"dashHeavy\", \"dashLong\", \"dashLongHeavy\", \"dotDash\", \"dotDashHeavy\", \"dotDotDash\", \"dotDotDashHeavy\", \"wavy\", \"wavyHeavy\",\n\t\"wavyDbl\",\n}\n\n// supportedDrawingTextVerticalType defined supported text vertical types in\n// drawing markup language.\nvar supportedDrawingTextVerticalType = []string{\"horz\", \"vert\", \"vert270\", \"wordArtVert\", \"eaVert\", \"mongolianVert\", \"wordArtVertRtl\"}\n\n// supportedPositioning defined supported positioning types.\nvar supportedPositioning = []string{\"absolute\", \"oneCell\", \"twoCell\"}\n\n// supportedPageOrientation defined supported page setup page orientation.\nvar supportedPageOrientation = []string{\"portrait\", \"landscape\"}\n\n// supportedPageOrder defined supported page setup page order.\nvar supportedPageOrder = []string{\"overThenDown\", \"downThenOver\"}\n\n// builtInDefinedNames defined built-in defined names are built with a _xlnm prefix.\nvar builtInDefinedNames = []string{\"_xlnm.Print_Area\", \"_xlnm.Print_Titles\", \"_xlnm.Criteria\", \"_xlnm._FilterDatabase\", \"_xlnm.Extract\", \"_xlnm.Consolidate_Area\", \"_xlnm.Database\", \"_xlnm.Sheet_Title\"}\n\nconst templateDocpropsApp = `<Properties xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties\" xmlns:vt=\"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes\"><TotalTime>0</TotalTime><Application>Go Excelize</Application></Properties>`\n\nconst templateContentTypes = `<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Override PartName=\"/xl/theme/theme1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.theme+xml\"/><Override PartName=\"/xl/styles.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml\"/><Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/><Default Extension=\"xml\" ContentType=\"application/xml\"/><Override PartName=\"/xl/workbook.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/><Override PartName=\"/docProps/app.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.extended-properties+xml\"/><Override PartName=\"/xl/worksheets/sheet1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/><Override PartName=\"/docProps/core.xml\" ContentType=\"application/vnd.openxmlformats-package.core-properties+xml\"/></Types>`\n\nconst templateWorkbook = `<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" mc:Ignorable=\"x15\" xmlns:x15=\"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main\"><fileVersion appName=\"xl\" lastEdited=\"6\" lowestEdited=\"6\" rupBuild=\"14420\" /><workbookPr filterPrivacy=\"1\" defaultThemeVersion=\"164011\" /><bookViews><workbookView xWindow=\"0\" yWindow=\"0\" windowWidth=\"14805\" windowHeight=\"8010\" /></bookViews><sheets><sheet name=\"Sheet1\" sheetId=\"1\" r:id=\"rId1\" /></sheets><calcPr calcId=\"122211\" /></workbook>`\n\nconst templateStyles = `<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" mc:Ignorable=\"x14ac x16r2\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\" xmlns:x16r2=\"http://schemas.microsoft.com/office/spreadsheetml/2015/02/main\"><fonts count=\"1\" x14ac:knownFonts=\"1\"><font><sz val=\"11\"/><color theme=\"1\"/><name val=\"Calibri\"/><family val=\"2\"/></font></fonts><fills count=\"2\"><fill><patternFill patternType=\"none\"/></fill><fill><patternFill patternType=\"gray125\"/></fill></fills><borders count=\"1\"><border><left/><right/><top/><bottom/><diagonal/></border></borders><cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/></cellStyleXfs><cellXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/></cellXfs><cellStyles count=\"1\"><cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/></cellStyles><dxfs count=\"0\"/><tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium2\" defaultPivotStyle=\"PivotStyleLight16\"/></styleSheet>`\n\nconst templateSheet = `<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"><dimension ref=\"A1\"/><sheetViews><sheetView tabSelected=\"1\" workbookViewId=\"0\"/></sheetViews><sheetFormatPr defaultRowHeight=\"15\"/><sheetData/></worksheet>`\n\nconst templateWorkbookRels = `<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\" Target=\"worksheets/sheet1.xml\"/><Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/><Relationship Id=\"rId3\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme\" Target=\"theme/theme1.xml\"/></Relationships>`\n\nconst templateDocpropsCore = `<cp:coreProperties xmlns:cp=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:dcterms=\"http://purl.org/dc/terms/\" xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><dc:creator>xuri</dc:creator><dcterms:created xsi:type=\"dcterms:W3CDTF\">2006-09-16T00:00:00Z</dcterms:created><dcterms:modified xsi:type=\"dcterms:W3CDTF\">2006-09-16T00:00:00Z</dcterms:modified></cp:coreProperties>`\n\nconst templateRels = `<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId3\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties\" Target=\"docProps/app.xml\"/><Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties\" Target=\"docProps/core.xml\"/><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"xl/workbook.xml\"/></Relationships>`\n\nconst templateTheme = `<a:theme xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" name=\"Office Theme\"><a:themeElements><a:clrScheme name=\"Office\"><a:dk1><a:sysClr val=\"windowText\" lastClr=\"000000\"/></a:dk1><a:lt1><a:sysClr val=\"window\" lastClr=\"FFFFFF\"/></a:lt1><a:dk2><a:srgbClr val=\"44546A\"/></a:dk2><a:lt2><a:srgbClr val=\"E7E6E6\"/></a:lt2><a:accent1><a:srgbClr val=\"5B9BD5\"/></a:accent1><a:accent2><a:srgbClr val=\"ED7D31\"/></a:accent2><a:accent3><a:srgbClr val=\"A5A5A5\"/></a:accent3><a:accent4><a:srgbClr val=\"FFC000\"/></a:accent4><a:accent5><a:srgbClr val=\"4472C4\"/></a:accent5><a:accent6><a:srgbClr val=\"70AD47\"/></a:accent6><a:hlink><a:srgbClr val=\"0563C1\"/></a:hlink><a:folHlink><a:srgbClr val=\"954F72\"/></a:folHlink></a:clrScheme><a:fontScheme name=\"Office\"><a:majorFont><a:latin typeface=\"Calibri Light\" panose=\"020F0302020204030204\"/><a:ea typeface=\"\"/><a:cs typeface=\"\"/><a:font script=\"Jpan\" typeface=\"游ゴシック Light\"/><a:font script=\"Hang\" typeface=\"맑은 고딕\"/><a:font script=\"Hans\" typeface=\"等线 Light\"/><a:font script=\"Hant\" typeface=\"新細明體\"/><a:font script=\"Arab\" typeface=\"Times New Roman\"/><a:font script=\"Hebr\" typeface=\"Times New Roman\"/><a:font script=\"Thai\" typeface=\"Tahoma\"/><a:font script=\"Ethi\" typeface=\"Nyala\"/><a:font script=\"Beng\" typeface=\"Vrinda\"/><a:font script=\"Gujr\" typeface=\"Shruti\"/><a:font script=\"Khmr\" typeface=\"MoolBoran\"/><a:font script=\"Knda\" typeface=\"Tunga\"/><a:font script=\"Guru\" typeface=\"Raavi\"/><a:font script=\"Cans\" typeface=\"Euphemia\"/><a:font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/><a:font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/><a:font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/><a:font script=\"Thaa\" typeface=\"MV Boli\"/><a:font script=\"Deva\" typeface=\"Mangal\"/><a:font script=\"Telu\" typeface=\"Gautami\"/><a:font script=\"Taml\" typeface=\"Latha\"/><a:font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/><a:font script=\"Orya\" typeface=\"Kalinga\"/><a:font script=\"Mlym\" typeface=\"Kartika\"/><a:font script=\"Laoo\" typeface=\"DokChampa\"/><a:font script=\"Sinh\" typeface=\"Iskoola Pota\"/><a:font script=\"Mong\" typeface=\"Mongolian Baiti\"/><a:font script=\"Viet\" typeface=\"Times New Roman\"/><a:font script=\"Uigh\" typeface=\"Microsoft Uighur\"/><a:font script=\"Geor\" typeface=\"Sylfaen\"/></a:majorFont><a:minorFont><a:latin typeface=\"Calibri\" panose=\"020F0502020204030204\"/><a:ea typeface=\"\"/><a:cs typeface=\"\"/><a:font script=\"Jpan\" typeface=\"游ゴシック\"/><a:font script=\"Hang\" typeface=\"맑은 고딕\"/><a:font script=\"Hans\" typeface=\"等线\"/><a:font script=\"Hant\" typeface=\"新細明體\"/><a:font script=\"Arab\" typeface=\"Arial\"/><a:font script=\"Hebr\" typeface=\"Arial\"/><a:font script=\"Thai\" typeface=\"Tahoma\"/><a:font script=\"Ethi\" typeface=\"Nyala\"/><a:font script=\"Beng\" typeface=\"Vrinda\"/><a:font script=\"Gujr\" typeface=\"Shruti\"/><a:font script=\"Khmr\" typeface=\"DaunPenh\"/><a:font script=\"Knda\" typeface=\"Tunga\"/><a:font script=\"Guru\" typeface=\"Raavi\"/><a:font script=\"Cans\" typeface=\"Euphemia\"/><a:font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/><a:font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/><a:font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/><a:font script=\"Thaa\" typeface=\"MV Boli\"/><a:font script=\"Deva\" typeface=\"Mangal\"/><a:font script=\"Telu\" typeface=\"Gautami\"/><a:font script=\"Taml\" typeface=\"Latha\"/><a:font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/><a:font script=\"Orya\" typeface=\"Kalinga\"/><a:font script=\"Mlym\" typeface=\"Kartika\"/><a:font script=\"Laoo\" typeface=\"DokChampa\"/><a:font script=\"Sinh\" typeface=\"Iskoola Pota\"/><a:font script=\"Mong\" typeface=\"Mongolian Baiti\"/><a:font script=\"Viet\" typeface=\"Arial\"/><a:font script=\"Uigh\" typeface=\"Microsoft Uighur\"/><a:font script=\"Geor\" typeface=\"Sylfaen\"/></a:minorFont></a:fontScheme><a:fmtScheme name=\"Office\"><a:fillStyleLst><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"110000\"/><a:satMod val=\"105000\"/><a:tint val=\"67000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"105000\"/><a:satMod val=\"103000\"/><a:tint val=\"73000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"105000\"/><a:satMod val=\"109000\"/><a:tint val=\"81000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:satMod val=\"103000\"/><a:lumMod val=\"102000\"/><a:tint val=\"94000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:satMod val=\"110000\"/><a:lumMod val=\"100000\"/><a:shade val=\"100000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"99000\"/><a:satMod val=\"120000\"/><a:shade val=\"78000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill></a:fillStyleLst><a:lnStyleLst><a:ln w=\"6350\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln><a:ln w=\"12700\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln><a:ln w=\"19050\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad=\"57150\" dist=\"19050\" dir=\"5400000\" algn=\"ctr\" rotWithShape=\"0\"><a:srgbClr val=\"000000\"><a:alpha val=\"63000\"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:solidFill><a:schemeClr val=\"phClr\"><a:tint val=\"95000\"/><a:satMod val=\"170000\"/></a:schemeClr></a:solidFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:tint val=\"93000\"/><a:satMod val=\"150000\"/><a:shade val=\"98000\"/><a:lumMod val=\"102000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:tint val=\"98000\"/><a:satMod val=\"130000\"/><a:shade val=\"90000\"/><a:lumMod val=\"103000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:shade val=\"63000\"/><a:satMod val=\"120000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements><a:objectDefaults/><a:extraClrSchemeLst/></a:theme>`\n\nconst templateNamespaceIDMap = ` xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:ap=\"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties\" xmlns:op=\"http://schemas.openxmlformats.org/officeDocument/2006/custom-properties\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:c=\"http://schemas.openxmlformats.org/drawingml/2006/chart\" xmlns:cdr=\"http://schemas.openxmlformats.org/drawingml/2006/chartDrawing\" xmlns:comp=\"http://schemas.openxmlformats.org/drawingml/2006/compatibility\" xmlns:dgm=\"http://schemas.openxmlformats.org/drawingml/2006/diagram\" xmlns:lc=\"http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas\" xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\" xmlns:xdr=\"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:ds=\"http://schemas.openxmlformats.org/officeDocument/2006/customXml\" xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\" xmlns:x=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:sl=\"http://schemas.openxmlformats.org/schemaLibrary/2006/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:xne=\"http://schemas.microsoft.com/office/excel/2006/main\" xmlns:mso=\"http://schemas.microsoft.com/office/2006/01/customui\" xmlns:ax=\"http://schemas.microsoft.com/office/2006/activeX\" xmlns:cppr=\"http://schemas.microsoft.com/office/2006/coverPageProps\" xmlns:cdip=\"http://schemas.microsoft.com/office/2006/customDocumentInformationPanel\" xmlns:ct=\"http://schemas.microsoft.com/office/2006/metadata/contentType\" xmlns:ntns=\"http://schemas.microsoft.com/office/2006/metadata/customXsn\" xmlns:lp=\"http://schemas.microsoft.com/office/2006/metadata/longProperties\" xmlns:ma=\"http://schemas.microsoft.com/office/2006/metadata/properties/metaAttributes\" xmlns:msink=\"http://schemas.microsoft.com/ink/2010/main\" xmlns:c14=\"http://schemas.microsoft.com/office/drawing/2007/8/2/chart\" xmlns:cdr14=\"http://schemas.microsoft.com/office/drawing/2010/chartDrawing\" xmlns:a14=\"http://schemas.microsoft.com/office/drawing/2010/main\" xmlns:pic14=\"http://schemas.microsoft.com/office/drawing/2010/picture\" xmlns:x14=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main\" xmlns:xdr14=\"http://schemas.microsoft.com/office/excel/2010/spreadsheetDrawing\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\" xmlns:dsp=\"http://schemas.microsoft.com/office/drawing/2008/diagram\" xmlns:mso14=\"http://schemas.microsoft.com/office/2009/07/customui\" xmlns:dgm14=\"http://schemas.microsoft.com/office/drawing/2010/diagram\" xmlns:x15=\"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main\" xmlns:x12ac=\"http://schemas.microsoft.com/office/spreadsheetml/2011/1/ac\" xmlns:x15ac=\"http://schemas.microsoft.com/office/spreadsheetml/2010/11/ac\" xmlns:xr=\"http://schemas.microsoft.com/office/spreadsheetml/2014/revision\" xmlns:xr2=\"http://schemas.microsoft.com/office/spreadsheetml/2015/revision2\" xmlns:xr3=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision3\" xmlns:xr4=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision4\" xmlns:xr5=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision5\" xmlns:xr6=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision6\" xmlns:xr7=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision7\" xmlns:xr8=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision8\" xmlns:xr9=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision9\" xmlns:xr10=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision10\" xmlns:xr11=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision11\" xmlns:xr12=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision12\" xmlns:xr13=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision13\" xmlns:xr14=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision14\" xmlns:xr15=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision15\" xmlns:x16=\"http://schemas.microsoft.com/office/spreadsheetml/2014/11/main\" xmlns:x16r2=\"http://schemas.microsoft.com/office/spreadsheetml/2015/02/main\" mc:Ignorable=\"c14 cdr14 a14 pic14 x14 xdr14 x14ac dsp mso14 dgm14 x15 x12ac x15ac xr xr2 xr3 xr4 xr5 xr6 xr7 xr8 xr9 xr10 xr11 xr12 xr13 xr14 xr15 x15 x16 x16r2 mo mx mv o v\" xmlns:mo=\"http://schemas.microsoft.com/office/mac/office/2008/main\" xmlns:mx=\"http://schemas.microsoft.com/office/mac/excel/2008/main\" xmlns:mv=\"urn:schemas-microsoft-com:mac:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xr:uid=\"{00000000-0001-0000-0000-000000000000}\">`\n"
  },
  {
    "path": "vml.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// FormControlType is the type of supported form controls.\ntype FormControlType byte\n\n// This section defines the currently supported form control types enumeration.\nconst (\n\tFormControlNote FormControlType = iota\n\tFormControlButton\n\tFormControlOptionButton\n\tFormControlSpinButton\n\tFormControlCheckBox\n\tFormControlGroupBox\n\tFormControlLabel\n\tFormControlScrollBar\n)\n\n// HeaderFooterImagePositionType is the type of header and footer image position.\ntype HeaderFooterImagePositionType byte\n\n// Worksheet header and footer image position types enumeration.\nconst (\n\tHeaderFooterImagePositionLeft HeaderFooterImagePositionType = iota\n\tHeaderFooterImagePositionCenter\n\tHeaderFooterImagePositionRight\n)\n\n// GetComments retrieves all comments in a worksheet by given worksheet name.\nfunc (f *File) GetComments(sheet string) ([]Comment, error) {\n\tvar comments []Comment\n\tsheetXMLPath, ok := f.getSheetXMLPath(sheet)\n\tif !ok {\n\t\treturn comments, ErrSheetNotExist{sheet}\n\t}\n\tcommentsXML := f.getSheetComments(filepath.Base(sheetXMLPath))\n\tif !strings.HasPrefix(commentsXML, \"/\") {\n\t\tcommentsXML = \"xl\" + strings.TrimPrefix(commentsXML, \"..\")\n\t}\n\tcommentsXML = strings.TrimPrefix(commentsXML, \"/\")\n\tcmts, err := f.commentsReader(commentsXML)\n\tif err != nil {\n\t\treturn comments, err\n\t}\n\tif cmts != nil {\n\t\tfor _, cmt := range cmts.CommentList.Comment {\n\t\t\tcomment := Comment{}\n\t\t\tif cmt.AuthorID < len(cmts.Authors.Author) {\n\t\t\t\tcomment.Author = cmts.Authors.Author[cmt.AuthorID]\n\t\t\t}\n\t\t\tcomment.Cell = cmt.Ref\n\t\t\tcomment.AuthorID = cmt.AuthorID\n\t\t\tif cmt.Text.T != nil {\n\t\t\t\tcomment.Text += *cmt.Text.T\n\t\t\t}\n\t\t\tfor _, text := range cmt.Text.R {\n\t\t\t\tif text.T != nil {\n\t\t\t\t\trun := RichTextRun{Text: text.T.Val}\n\t\t\t\t\tif text.RPr != nil {\n\t\t\t\t\t\trun.Font = text.RPr.getFont()\n\t\t\t\t\t}\n\t\t\t\t\tcomment.Paragraph = append(comment.Paragraph, run)\n\t\t\t\t}\n\t\t\t}\n\t\t\tcomments = append(comments, comment)\n\t\t}\n\t}\n\treturn comments, nil\n}\n\n// getSheetComments provides the method to get the target comment reference by\n// given worksheet file path.\nfunc (f *File) getSheetComments(sheetFile string) string {\n\trels, _ := f.relsReader(\"xl/worksheets/_rels/\" + sheetFile + \".rels\")\n\tif sheetRels := rels; sheetRels != nil {\n\t\tsheetRels.mu.Lock()\n\t\tdefer sheetRels.mu.Unlock()\n\t\tfor _, v := range sheetRels.Relationships {\n\t\t\tif v.Type == SourceRelationshipComments {\n\t\t\t\treturn v.Target\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// AddComment provides the method to add comments in a sheet by giving the\n// worksheet name, cell reference, and format set (such as author and text).\n// Note that the maximum author name length is 255 and the max text length is\n// 32512, and each cell can only have one comment, an error will return if\n// adding comment on a cell which already exist comment. For example, add a\n// rich-text comment with a specified comments box size in Sheet1!A5:\n//\n//\terr := f.AddComment(\"Sheet1\", excelize.Comment{\n//\t    Cell:   \"A5\",\n//\t    Author: \"Excelize\",\n//\t    Paragraph: []excelize.RichTextRun{\n//\t        {Text: \"Excelize: \", Font: &excelize.Font{Bold: true}},\n//\t        {Text: \"This is a comment.\"},\n//\t    },\n//\t    Height: 40,\n//\t    Width:  180,\n//\t})\nfunc (f *File) AddComment(sheet string, opts Comment) error {\n\treturn f.addVMLObject(vmlOptions{\n\t\tsheet: sheet, Comment: opts,\n\t\tFormControl: FormControl{\n\t\t\tCell:      opts.Cell,\n\t\t\tType:      FormControlNote,\n\t\t\tText:      opts.Text,\n\t\t\tParagraph: opts.Paragraph,\n\t\t\tWidth:     opts.Width,\n\t\t\tHeight:    opts.Height,\n\t\t},\n\t})\n}\n\n// DeleteComment provides the method to delete comment in a worksheet by given\n// worksheet name and cell reference. For example, delete the comment in\n// Sheet1!$A$30:\n//\n//\terr := f.DeleteComment(\"Sheet1\", \"A30\")\nfunc (f *File) DeleteComment(sheet, cell string) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif ws.LegacyDrawing == nil {\n\t\treturn err\n\t}\n\tsheetXMLPath, _ := f.getSheetXMLPath(sheet)\n\tcommentsXML := f.getSheetComments(filepath.Base(sheetXMLPath))\n\tif !strings.HasPrefix(commentsXML, \"/\") {\n\t\tcommentsXML = \"xl\" + strings.TrimPrefix(commentsXML, \"..\")\n\t}\n\tcommentsXML = strings.TrimPrefix(commentsXML, \"/\")\n\tcmts, err := f.commentsReader(commentsXML)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif cmts != nil {\n\t\tfor i := 0; i < len(cmts.CommentList.Comment); i++ {\n\t\t\tcmt := cmts.CommentList.Comment[i]\n\t\t\tif cmt.Ref != cell {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif len(cmts.CommentList.Comment) > 1 {\n\t\t\t\tcmts.CommentList.Comment = append(\n\t\t\t\t\tcmts.CommentList.Comment[:i],\n\t\t\t\t\tcmts.CommentList.Comment[i+1:]...,\n\t\t\t\t)\n\t\t\t\tif idx := inStrSlice(cmts.cells, cell, true); idx != -1 {\n\t\t\t\t\tcmts.cells = append(\n\t\t\t\t\t\tcmts.cells[:idx],\n\t\t\t\t\t\tcmts.cells[idx+1:]...,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\ti--\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcmts.CommentList.Comment = nil\n\t\t\tcmts.cells = nil\n\t\t}\n\t\tf.Comments[commentsXML] = cmts\n\t}\n\tsheetRelationshipsDrawingVML := f.getSheetRelationshipsTargetByID(sheet, ws.LegacyDrawing.RID)\n\treturn f.deleteFormControl(sheetRelationshipsDrawingVML, cell, true)\n}\n\n// deleteFormControl provides the method to delete shape from\n// xl/drawings/vmlDrawing%d.xml by giving path, cell and shape type.\nfunc (f *File) deleteFormControl(sheetRelationshipsDrawingVML, cell string, isComment bool) error {\n\tcol, row, err := CellNameToCoordinates(cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvmlID, _ := strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingVML, \"../drawings/vmlDrawing\"), \".vml\"))\n\tdrawingVML := strings.ReplaceAll(sheetRelationshipsDrawingVML, \"..\", \"xl\")\n\tvml := f.VMLDrawing[drawingVML]\n\tif vml == nil {\n\t\tvml = &vmlDrawing{\n\t\t\tXMLNSv:  \"urn:schemas-microsoft-com:vml\",\n\t\t\tXMLNSo:  \"urn:schemas-microsoft-com:office:office\",\n\t\t\tXMLNSx:  \"urn:schemas-microsoft-com:office:excel\",\n\t\t\tXMLNSmv: \"http://macVmlSchemaUri\",\n\t\t\tShapeLayout: &xlsxShapeLayout{\n\t\t\t\tExt: \"edit\", IDmap: &xlsxIDmap{Ext: \"edit\", Data: vmlID},\n\t\t\t},\n\t\t\tShapeType: &xlsxShapeType{\n\t\t\t\tStroke: &xlsxStroke{JoinStyle: \"miter\"},\n\t\t\t\tVPath:  &vPath{GradientShapeOK: \"t\", ConnectType: \"rect\"},\n\t\t\t},\n\t\t}\n\t\t// Load exist VML shapes from xl/drawings/vmlDrawing%d.vml\n\t\td, err := f.decodeVMLDrawingReader(drawingVML)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif d != nil {\n\t\t\tvml.ShapeType.ID = d.ShapeType.ID\n\t\t\tvml.ShapeType.CoordSize = d.ShapeType.CoordSize\n\t\t\tvml.ShapeType.Spt = d.ShapeType.Spt\n\t\t\tvml.ShapeType.Path = d.ShapeType.Path\n\t\t\tfor _, v := range d.Shape {\n\t\t\t\ts := xlsxShape{\n\t\t\t\t\tID:          v.ID,\n\t\t\t\t\tType:        v.Type,\n\t\t\t\t\tStyle:       v.Style,\n\t\t\t\t\tButton:      v.Button,\n\t\t\t\t\tFilled:      v.Filled,\n\t\t\t\t\tFillColor:   v.FillColor,\n\t\t\t\t\tInsetMode:   v.InsetMode,\n\t\t\t\t\tStroked:     v.Stroked,\n\t\t\t\t\tStrokeColor: v.StrokeColor,\n\t\t\t\t\tVal:         v.Val,\n\t\t\t\t}\n\t\t\t\tvml.Shape = append(vml.Shape, s)\n\t\t\t}\n\t\t}\n\t}\n\tcond := func(objectType string) bool {\n\t\tif isComment {\n\t\t\treturn objectType == \"Note\"\n\t\t}\n\t\treturn objectType != \"Note\"\n\t}\n\tfor i, sp := range vml.Shape {\n\t\tvar shapeVal decodeShapeVal\n\t\tif err = xml.Unmarshal([]byte(fmt.Sprintf(\"<shape>%s</shape>\", sp.Val)), &shapeVal); err == nil &&\n\t\t\tcond(shapeVal.ClientData.ObjectType) && shapeVal.ClientData.Anchor != \"\" {\n\t\t\tleftCol, topRow, err := extractAnchorCell(shapeVal.ClientData.Anchor)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif leftCol == col-1 && topRow == row-1 {\n\t\t\t\tvml.Shape = append(vml.Shape[:i], vml.Shape[i+1:]...)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tf.VMLDrawing[drawingVML] = vml\n\treturn err\n}\n\n// addComment provides a function to create chart as xl/comments%d.xml by\n// given cell and format sets.\nfunc (f *File) addComment(commentsXML string, opts vmlOptions) error {\n\tif opts.Author == \"\" {\n\t\topts.Author = \"Author\"\n\t}\n\tif countUTF16String(opts.Author) > MaxFieldLength {\n\t\topts.Author = truncateUTF16Units(opts.Author, MaxFieldLength)\n\t}\n\tcmts, err := f.commentsReader(commentsXML)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar authorID int\n\tif cmts == nil {\n\t\tcmts = &xlsxComments{Authors: xlsxAuthor{Author: []string{opts.Author}}}\n\t}\n\tif inStrSlice(cmts.cells, opts.Comment.Cell, true) != -1 {\n\t\treturn newAddCommentError(opts.Comment.Cell)\n\t}\n\tif inStrSlice(cmts.Authors.Author, opts.Author, true) == -1 {\n\t\tcmts.Authors.Author = append(cmts.Authors.Author, opts.Author)\n\t\tauthorID = len(cmts.Authors.Author) - 1\n\t}\n\tdefaultFont, err := f.GetDefaultFont()\n\tif err != nil {\n\t\treturn err\n\t}\n\tchars, cmt := 0, xlsxComment{\n\t\tRef:      opts.Comment.Cell,\n\t\tAuthorID: authorID,\n\t\tText:     xlsxText{R: []xlsxR{}},\n\t}\n\tif opts.Comment.Text != \"\" {\n\t\tif countUTF16String(opts.Comment.Text) > TotalCellChars {\n\t\t\topts.Comment.Text = truncateUTF16Units(opts.Comment.Text, TotalCellChars)\n\t\t}\n\t\tcmt.Text.T = stringPtr(opts.Comment.Text)\n\t\tchars += countUTF16String(opts.Comment.Text)\n\t}\n\tfor _, run := range opts.Comment.Paragraph {\n\t\tif chars == TotalCellChars {\n\t\t\tbreak\n\t\t}\n\t\tif chars+countUTF16String(run.Text) > TotalCellChars {\n\t\t\trun.Text = truncateUTF16Units(run.Text, TotalCellChars-chars)\n\t\t}\n\t\tchars += countUTF16String(run.Text)\n\t\tr := xlsxR{\n\t\t\tRPr: &xlsxRPr{\n\t\t\t\tSz: &attrValFloat{Val: float64Ptr(9)},\n\t\t\t\tColor: &xlsxColor{\n\t\t\t\t\tIndexed: 81,\n\t\t\t\t},\n\t\t\t\tRFont:  &attrValString{Val: stringPtr(defaultFont)},\n\t\t\t\tFamily: &attrValInt{Val: intPtr(2)},\n\t\t\t},\n\t\t\tT: &xlsxT{Val: run.Text, Space: xml.Attr{\n\t\t\t\tName:  xml.Name{Space: NameSpaceXML, Local: \"space\"},\n\t\t\t\tValue: \"preserve\",\n\t\t\t}},\n\t\t}\n\t\tif run.Font != nil {\n\t\t\tr.RPr = run.Font.newRpr()\n\t\t}\n\t\tcmt.Text.R = append(cmt.Text.R, r)\n\t}\n\tcmts.CommentList.Comment = append(cmts.CommentList.Comment, cmt)\n\tcmts.cells = append(cmts.cells, cmt.Ref)\n\tf.Comments[commentsXML] = cmts\n\treturn err\n}\n\n// countComments provides a function to get comments files count storage in\n// the folder xl.\nfunc (f *File) countComments() int {\n\tcomments := map[string]struct{}{}\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/comments\") {\n\t\t\tcomments[k.(string)] = struct{}{}\n\t\t}\n\t\treturn true\n\t})\n\tfor rel := range f.Comments {\n\t\tif strings.Contains(rel, \"xl/comments\") {\n\t\t\tcomments[rel] = struct{}{}\n\t\t}\n\t}\n\treturn len(comments)\n}\n\n// commentsReader provides a function to get the pointer to the structure\n// after deserialization of xl/comments%d.xml.\nfunc (f *File) commentsReader(path string) (*xlsxComments, error) {\n\tif f.Comments[path] == nil {\n\t\tcontent, ok := f.Pkg.Load(path)\n\t\tif ok && content != nil {\n\t\t\tf.Comments[path] = new(xlsxComments)\n\t\t\tif err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).\n\t\t\t\tDecode(f.Comments[path]); err != nil && err != io.EOF {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\tif cmts := f.Comments[path]; cmts != nil && len(cmts.cells) == 0 {\n\t\tfor idx := 0; idx < len(cmts.CommentList.Comment); idx++ {\n\t\t\tcmts.cells = append(cmts.cells, cmts.CommentList.Comment[idx].Ref)\n\t\t}\n\t\tf.Comments[path] = cmts\n\t}\n\treturn f.Comments[path], nil\n}\n\n// commentsWriter provides a function to save xl/comments%d.xml after\n// serialize structure.\nfunc (f *File) commentsWriter() {\n\tfor path, c := range f.Comments {\n\t\tif c != nil {\n\t\t\tv, _ := xml.Marshal(c)\n\t\t\tf.saveFileList(path, v)\n\t\t}\n\t}\n}\n\n// AddFormControl provides the method to add form control object in a worksheet\n// by given worksheet name and form control options. Supported form control\n// type: button, check box, group box, label, option button, scroll bar and\n// spinner. If set macro for the form control, the workbook extension should be\n// XLSM or XLTM. Scroll value must be between 0 and 30000. Please note that if a\n// cell link is set for a checkbox form control, Excelize will not assign a\n// value to the linked cell when the checkbox is checked. To reflect the\n// checkbox state, please use the 'SetCellValue' function to manually set the\n// linked cell's value to true.\n//\n// Example 1, add button form control with macro, rich-text, custom button size,\n// print property on Sheet1!A2, and let the button do not move or size with\n// cells:\n//\n//\tenable := true\n//\terr := f.AddFormControl(\"Sheet1\", excelize.FormControl{\n//\t    Cell:   \"A2\",\n//\t    Type:   excelize.FormControlButton,\n//\t    Macro:  \"Button1_Click\",\n//\t    Width:  140,\n//\t    Height: 60,\n//\t    Text:   \"Button 1\\r\\n\",\n//\t    Paragraph: []excelize.RichTextRun{\n//\t        {\n//\t            Font: &excelize.Font{\n//\t                Bold:      true,\n//\t                Italic:    true,\n//\t                Underline: \"single\",\n//\t                Family:    \"Times New Roman\",\n//\t                Size:      14,\n//\t                Color:     \"777777\",\n//\t            },\n//\t            Text: \"C1=A1+B1\",\n//\t        },\n//\t    },\n//\t    Format: excelize.GraphicOptions{\n//\t        PrintObject: &enable,\n//\t        Positioning: \"absolute\",\n//\t    },\n//\t})\n//\n// Example 2, add option button form control with checked status and text on\n// Sheet1!A1:\n//\n//\terr := f.AddFormControl(\"Sheet1\", excelize.FormControl{\n//\t    Cell:    \"A1\",\n//\t    Type:    excelize.FormControlOptionButton,\n//\t    Text:    \"Option Button 1\",\n//\t    Checked: true,\n//\t})\n//\n// Example 3, add spin button form control on Sheet1!B1 to increase or decrease\n// the value of Sheet1!A1:\n//\n//\terr := f.AddFormControl(\"Sheet1\", excelize.FormControl{\n//\t    Cell:       \"B1\",\n//\t    Type:       excelize.FormControlSpinButton,\n//\t    Width:      15,\n//\t    Height:     40,\n//\t    CurrentVal: 7,\n//\t    MinVal:     5,\n//\t    MaxVal:     10,\n//\t    IncChange:  1,\n//\t    CellLink:   \"A1\",\n//\t})\n//\n// Example 4, add horizontally scroll bar form control on Sheet1!A2 to change\n// the value of Sheet1!A1 by click the scroll arrows or drag the scroll box:\n//\n//\terr := f.AddFormControl(\"Sheet1\", excelize.FormControl{\n//\t    Cell:         \"A2\",\n//\t    Type:         excelize.FormControlScrollBar,\n//\t    Width:        140,\n//\t    Height:       20,\n//\t    CurrentVal:   50,\n//\t    MinVal:       10,\n//\t    MaxVal:       100,\n//\t    IncChange:    1,\n//\t    PageChange:   1,\n//\t    CellLink:     \"A1\",\n//\t    Horizontally: true,\n//\t})\nfunc (f *File) AddFormControl(sheet string, opts FormControl) error {\n\treturn f.addVMLObject(vmlOptions{\n\t\tformCtrl: true, sheet: sheet, FormControl: opts,\n\t})\n}\n\n// DeleteFormControl provides the method to delete form control in a worksheet\n// by given worksheet name and cell reference. For example, delete the form\n// control in Sheet1!$A$1:\n//\n//\terr := f.DeleteFormControl(\"Sheet1\", \"A1\")\nfunc (f *File) DeleteFormControl(sheet, cell string) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif ws.LegacyDrawing == nil {\n\t\treturn err\n\t}\n\tsheetRelationshipsDrawingVML := f.getSheetRelationshipsTargetByID(sheet, ws.LegacyDrawing.RID)\n\treturn f.deleteFormControl(sheetRelationshipsDrawingVML, cell, false)\n}\n\n// countVMLDrawing provides a function to get VML drawing files count storage\n// in the folder xl/drawings.\nfunc (f *File) countVMLDrawing() int {\n\tdrawings := map[string]struct{}{}\n\tf.Pkg.Range(func(k, v interface{}) bool {\n\t\tif strings.Contains(k.(string), \"xl/drawings/vmlDrawing\") {\n\t\t\tdrawings[k.(string)] = struct{}{}\n\t\t}\n\t\treturn true\n\t})\n\tfor rel := range f.VMLDrawing {\n\t\tif strings.Contains(rel, \"xl/drawings/vmlDrawing\") {\n\t\t\tdrawings[rel] = struct{}{}\n\t\t}\n\t}\n\treturn len(drawings)\n}\n\n// decodeVMLDrawingReader provides a function to get the pointer to the\n// structure after deserialization of xl/drawings/vmlDrawing%d.xml.\nfunc (f *File) decodeVMLDrawingReader(path string) (*decodeVmlDrawing, error) {\n\tif f.DecodeVMLDrawing[path] == nil {\n\t\tc, ok := f.Pkg.Load(path)\n\t\tif ok && c != nil {\n\t\t\tf.DecodeVMLDrawing[path] = new(decodeVmlDrawing)\n\t\t\tif err := f.xmlNewDecoder(bytes.NewReader(bytesReplace(namespaceStrictToTransitional(c.([]byte)), []byte(\"<br>\\r\\n\"), []byte(\"<br></br>\\r\\n\"), -1))).\n\t\t\t\tDecode(f.DecodeVMLDrawing[path]); err != nil && err != io.EOF {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\treturn f.DecodeVMLDrawing[path], nil\n}\n\n// vmlDrawingWriter provides a function to save xl/drawings/vmlDrawing%d.xml\n// after serialize structure.\nfunc (f *File) vmlDrawingWriter() {\n\tfor path, vml := range f.VMLDrawing {\n\t\tif vml != nil {\n\t\t\tv, _ := xml.Marshal(vml)\n\t\t\tf.Pkg.Store(path, v)\n\t\t}\n\t}\n}\n\n// addVMLObject provides a function to create VML drawing parts and\n// relationships for comments and form controls.\nfunc (f *File) addVMLObject(opts vmlOptions) error {\n\t// Read sheet data\n\tws, err := f.workSheetReader(opts.sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvmlID := f.countComments() + 1\n\tif opts.formCtrl {\n\t\tif opts.Type > FormControlScrollBar {\n\t\t\treturn ErrParameterInvalid\n\t\t}\n\t\tvmlID = f.countVMLDrawing() + 1\n\t}\n\tsheetID := f.getSheetID(opts.sheet)\n\tdrawingVML := \"xl/drawings/vmlDrawing\" + strconv.Itoa(vmlID) + \".vml\"\n\tsheetRelationshipsDrawingVML := \"../drawings/vmlDrawing\" + strconv.Itoa(vmlID) + \".vml\"\n\tsheetXMLPath, _ := f.getSheetXMLPath(opts.sheet)\n\tsheetRels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(sheetXMLPath, \"xl/worksheets/\") + \".rels\"\n\tif ws.LegacyDrawing != nil {\n\t\t// The worksheet already has a VML relationships, use the relationships drawing ../drawings/vmlDrawing%d.vml.\n\t\tsheetRelationshipsDrawingVML = f.getSheetRelationshipsTargetByID(opts.sheet, ws.LegacyDrawing.RID)\n\t\tvmlID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingVML, \"../drawings/vmlDrawing\"), \".vml\"))\n\t\tdrawingVML = strings.ReplaceAll(sheetRelationshipsDrawingVML, \"..\", \"xl\")\n\t} else {\n\t\t// Add first VML drawing for given sheet.\n\t\trID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, \"\")\n\t\tf.addSheetNameSpace(opts.sheet, SourceRelationship)\n\t\tf.addSheetLegacyDrawing(opts.sheet, rID)\n\t}\n\tif err = f.addDrawingVML(sheetID, drawingVML, prepareFormCtrlOptions(&opts)); err != nil {\n\t\treturn err\n\t}\n\tif !opts.formCtrl {\n\t\tcommentsXML := \"xl/comments\" + strconv.Itoa(vmlID) + \".xml\"\n\t\tif err = f.addComment(commentsXML, opts); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif sheetXMLPath, ok := f.getSheetXMLPath(opts.sheet); ok && f.getSheetComments(filepath.Base(sheetXMLPath)) == \"\" {\n\t\t\tsheetRelationshipsComments := \"../comments\" + strconv.Itoa(vmlID) + \".xml\"\n\t\t\tf.addRels(sheetRels, SourceRelationshipComments, sheetRelationshipsComments, \"\")\n\t\t}\n\t}\n\treturn f.addContentTypePart(vmlID, \"comments\")\n}\n\n// prepareFormCtrlOptions provides a function to parse the format settings of\n// the form control with default value.\nfunc prepareFormCtrlOptions(opts *vmlOptions) *vmlOptions {\n\tif opts.Format.ScaleX == 0 {\n\t\topts.Format.ScaleX = 1\n\t}\n\tif opts.Format.ScaleY == 0 {\n\t\topts.Format.ScaleY = 1\n\t}\n\tif opts.FormControl.Width == 0 {\n\t\topts.FormControl.Width = 140\n\t}\n\tif opts.FormControl.Height == 0 {\n\t\topts.FormControl.Height = 60\n\t}\n\treturn opts\n}\n\n// formCtrlText returns font element in the VML for control form text.\nfunc formCtrlText(opts *vmlOptions) []vmlFont {\n\tvar font []vmlFont\n\tif opts.FormControl.Text != \"\" {\n\t\tfont = append(font, vmlFont{Content: opts.FormControl.Text})\n\t}\n\tfor _, run := range opts.FormControl.Paragraph {\n\t\tfnt := vmlFont{\n\t\t\tContent: run.Text + \"<br></br>\\r\\n\",\n\t\t}\n\t\tif run.Font != nil {\n\t\t\tfnt.Face = run.Font.Family\n\t\t\tfnt.Color = run.Font.Color\n\t\t\tif !strings.HasPrefix(run.Font.Color, \"#\") {\n\t\t\t\tfnt.Color = \"#\" + fnt.Color\n\t\t\t}\n\t\t\tif run.Font.Size != 0 {\n\t\t\t\tfnt.Size = uint(run.Font.Size * 20)\n\t\t\t}\n\t\t\tif run.Font.Underline == \"single\" {\n\t\t\t\tfnt.Content = \"<u>\" + fnt.Content + \"</u>\"\n\t\t\t}\n\t\t\tif run.Font.Underline == \"double\" {\n\t\t\t\tfnt.Content = \"<u class=\\\"font1\\\">\" + fnt.Content + \"</u>\"\n\t\t\t}\n\t\t\tif run.Font.Italic {\n\t\t\t\tfnt.Content = \"<i>\" + fnt.Content + \"</i>\"\n\t\t\t}\n\t\t\tif run.Font.Bold {\n\t\t\t\tfnt.Content = \"<b>\" + fnt.Content + \"</b>\"\n\t\t\t}\n\t\t}\n\t\tfont = append(font, fnt)\n\t}\n\treturn font\n}\n\nvar formCtrlPresets = map[FormControlType]formCtrlPreset{\n\tFormControlNote: {\n\t\tobjectType:   \"Note\",\n\t\tautoFill:     \"True\",\n\t\tfilled:       \"\",\n\t\tfillColor:    \"#FBF6D6\",\n\t\tstroked:      \"\",\n\t\tstrokeColor:  \"#EDEAA1\",\n\t\tstrokeButton: \"\",\n\t\tfill: &vFill{\n\t\t\tColor2: \"#FBFE82\",\n\t\t\tAngle:  -180,\n\t\t\tType:   \"gradient\",\n\t\t\tFill:   &oFill{Ext: \"view\", Type: \"gradientUnscaled\"},\n\t\t},\n\t\ttextHAlign:  \"\",\n\t\ttextVAlign:  \"\",\n\t\tnoThreeD:    nil,\n\t\tfirstButton: nil,\n\t\tshadow:      &vShadow{On: \"t\", Color: \"black\", Obscured: \"t\"},\n\t},\n\tFormControlButton: {\n\t\tobjectType:   \"Button\",\n\t\tautoFill:     \"True\",\n\t\tfilled:       \"\",\n\t\tfillColor:    \"buttonFace [67]\",\n\t\tstroked:      \"\",\n\t\tstrokeColor:  \"windowText [64]\",\n\t\tstrokeButton: \"t\",\n\t\tfill: &vFill{\n\t\t\tColor2: \"buttonFace [67]\",\n\t\t\tAngle:  -180,\n\t\t\tType:   \"gradient\",\n\t\t\tFill:   &oFill{Ext: \"view\", Type: \"gradientUnscaled\"},\n\t\t},\n\t\ttextHAlign:  \"Center\",\n\t\ttextVAlign:  \"Center\",\n\t\tnoThreeD:    nil,\n\t\tfirstButton: nil,\n\t\tshadow:      nil,\n\t},\n\tFormControlCheckBox: {\n\t\tobjectType:   \"Checkbox\",\n\t\tautoFill:     \"True\",\n\t\tfilled:       \"f\",\n\t\tfillColor:    \"window [65]\",\n\t\tstroked:      \"f\",\n\t\tstrokeColor:  \"windowText [64]\",\n\t\tstrokeButton: \"\",\n\t\tfill:         nil,\n\t\ttextHAlign:   \"\",\n\t\ttextVAlign:   \"Center\",\n\t\tnoThreeD:     stringPtr(\"\"),\n\t\tfirstButton:  nil,\n\t\tshadow:       nil,\n\t},\n\tFormControlGroupBox: {\n\t\tobjectType:   \"GBox\",\n\t\tautoFill:     \"False\",\n\t\tfilled:       \"f\",\n\t\tfillColor:    \"\",\n\t\tstroked:      \"f\",\n\t\tstrokeColor:  \"windowText [64]\",\n\t\tstrokeButton: \"\",\n\t\tfill:         nil,\n\t\ttextHAlign:   \"\",\n\t\ttextVAlign:   \"\",\n\t\tnoThreeD:     stringPtr(\"\"),\n\t\tfirstButton:  nil,\n\t\tshadow:       nil,\n\t},\n\tFormControlLabel: {\n\t\tobjectType:   \"Label\",\n\t\tautoFill:     \"False\",\n\t\tfilled:       \"f\",\n\t\tfillColor:    \"window [65]\",\n\t\tstroked:      \"f\",\n\t\tstrokeColor:  \"windowText [64]\",\n\t\tstrokeButton: \"\",\n\t\tfill:         nil,\n\t\ttextHAlign:   \"\",\n\t\ttextVAlign:   \"\",\n\t\tnoThreeD:     nil,\n\t\tfirstButton:  nil,\n\t\tshadow:       nil,\n\t},\n\tFormControlOptionButton: {\n\t\tobjectType:   \"Radio\",\n\t\tautoFill:     \"False\",\n\t\tfilled:       \"f\",\n\t\tfillColor:    \"window [65]\",\n\t\tstroked:      \"f\",\n\t\tstrokeColor:  \"windowText [64]\",\n\t\tstrokeButton: \"\",\n\t\tfill:         nil,\n\t\ttextHAlign:   \"\",\n\t\ttextVAlign:   \"Center\",\n\t\tnoThreeD:     stringPtr(\"\"),\n\t\tfirstButton:  stringPtr(\"\"),\n\t\tshadow:       nil,\n\t},\n\tFormControlScrollBar: {\n\t\tobjectType:   \"Scroll\",\n\t\tautoFill:     \"\",\n\t\tfilled:       \"\",\n\t\tfillColor:    \"\",\n\t\tstroked:      \"f\",\n\t\tstrokeColor:  \"windowText [64]\",\n\t\tstrokeButton: \"\",\n\t\tfill:         nil,\n\t\ttextHAlign:   \"\",\n\t\ttextVAlign:   \"\",\n\t\tnoThreeD:     nil,\n\t\tfirstButton:  nil,\n\t\tshadow:       nil,\n\t},\n\tFormControlSpinButton: {\n\t\tobjectType:   \"Spin\",\n\t\tautoFill:     \"False\",\n\t\tfilled:       \"\",\n\t\tfillColor:    \"\",\n\t\tstroked:      \"f\",\n\t\tstrokeColor:  \"windowText [64]\",\n\t\tstrokeButton: \"\",\n\t\tfill:         nil,\n\t\ttextHAlign:   \"\",\n\t\ttextVAlign:   \"\",\n\t\tnoThreeD:     nil,\n\t\tfirstButton:  nil,\n\t\tshadow:       nil,\n\t},\n}\n\n// addFormCtrl check and add scroll bar or spinner form control by given options.\nfunc (sp *encodeShape) addFormCtrl(opts *vmlOptions) error {\n\tif opts.Type != FormControlScrollBar && opts.Type != FormControlSpinButton {\n\t\treturn nil\n\t}\n\tif opts.CurrentVal > MaxFormControlValue ||\n\t\topts.MinVal > MaxFormControlValue ||\n\t\topts.MaxVal > MaxFormControlValue ||\n\t\topts.IncChange > MaxFormControlValue ||\n\t\topts.PageChange > MaxFormControlValue {\n\t\treturn ErrFormControlValue\n\t}\n\tif opts.CellLink != \"\" {\n\t\tif _, _, err := CellNameToCoordinates(opts.CellLink); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tsp.ClientData.FmlaLink = opts.CellLink\n\tsp.ClientData.Val = opts.CurrentVal\n\tsp.ClientData.Min = opts.MinVal\n\tsp.ClientData.Max = opts.MaxVal\n\tsp.ClientData.Inc = opts.IncChange\n\tsp.ClientData.Page = opts.PageChange\n\tif opts.Type == FormControlScrollBar {\n\t\tif opts.Horizontally {\n\t\t\tsp.ClientData.Horiz = stringPtr(\"\")\n\t\t}\n\t\tsp.ClientData.Dx = 15\n\t}\n\treturn nil\n}\n\n// addFormCtrlShape returns a VML shape by given preset and options.\nfunc (f *File) addFormCtrlShape(preset formCtrlPreset, col, row int, anchor string, opts *vmlOptions) (*encodeShape, error) {\n\tsp := encodeShape{\n\t\tFill:   preset.fill,\n\t\tShadow: preset.shadow,\n\t\tPath:   &vPath{ConnectType: \"none\"},\n\t\tTextBox: &vTextBox{\n\t\t\tStyle: \"mso-direction-alt:auto\",\n\t\t\tDiv:   &xlsxDiv{Style: \"text-align:left\"},\n\t\t},\n\t\tClientData: &xClientData{\n\t\t\tObjectType:  preset.objectType,\n\t\t\tAnchor:      anchor,\n\t\t\tAutoFill:    preset.autoFill,\n\t\t\tRow:         intPtr(row - 1),\n\t\t\tColumn:      intPtr(col - 1),\n\t\t\tTextHAlign:  preset.textHAlign,\n\t\t\tTextVAlign:  preset.textVAlign,\n\t\t\tNoThreeD:    preset.noThreeD,\n\t\t\tFirstButton: preset.firstButton,\n\t\t},\n\t}\n\tif opts.Format.PrintObject != nil && !*opts.Format.PrintObject {\n\t\tsp.ClientData.PrintObject = \"False\"\n\t}\n\tif opts.Format.Positioning != \"\" {\n\t\tidx := inStrSlice(supportedPositioning, opts.Format.Positioning, true)\n\t\tif idx == -1 {\n\t\t\treturn &sp, newInvalidOptionalValue(\"Positioning\", opts.Format.Positioning, supportedPositioning)\n\t\t}\n\t\tsp.ClientData.MoveWithCells = []*string{stringPtr(\"\"), nil, nil}[idx]\n\t\tsp.ClientData.SizeWithCells = []*string{stringPtr(\"\"), stringPtr(\"\"), nil}[idx]\n\t}\n\tif opts.Type == FormControlNote {\n\t\tsp.ClientData.MoveWithCells = stringPtr(\"\")\n\t\tsp.ClientData.SizeWithCells = stringPtr(\"\")\n\t}\n\tif !opts.formCtrl {\n\t\treturn &sp, nil\n\t}\n\tsp.TextBox.Div.Font = formCtrlText(opts)\n\tsp.ClientData.FmlaMacro = opts.Macro\n\tif (opts.Type == FormControlCheckBox || opts.Type == FormControlOptionButton) && opts.Checked {\n\t\tsp.ClientData.Checked = 1\n\t}\n\tif opts.Type == FormControlCheckBox {\n\t\tsp.ClientData.FmlaLink = opts.CellLink\n\t}\n\treturn &sp, sp.addFormCtrl(opts)\n}\n\n// addDrawingVML provides a function to create VML drawing XML as\n// xl/drawings/vmlDrawing%d.vml by given data ID, XML path and VML options. The\n// anchor value is a comma-separated list of data written out as: LeftColumn,\n// LeftOffset, TopRow, TopOffset, RightColumn, RightOffset, BottomRow,\n// BottomOffset.\nfunc (f *File) addDrawingVML(sheetID int, drawingVML string, opts *vmlOptions) error {\n\tcol, row, err := CellNameToCoordinates(opts.FormControl.Cell)\n\tif err != nil {\n\t\treturn err\n\t}\n\tleftOffset, vmlID, vml, preset := 23, 202, f.VMLDrawing[drawingVML], formCtrlPresets[opts.Type]\n\tstyle := \"position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden\"\n\tif opts.formCtrl {\n\t\tleftOffset, vmlID = 0, 201\n\t\tstyle = \"position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;mso-wrap-style:tight\"\n\t}\n\tcolStart, rowStart, colEnd, rowEnd, _, _, x2, y2 := f.positionObjectPixels(opts.sheet, col, row, int(opts.FormControl.Width), int(opts.FormControl.Height), &opts.Format)\n\tanchor := fmt.Sprintf(\"%d, %d, %d, 0, %d, %d, %d, %d\", colStart, leftOffset, rowStart, colEnd, x2, rowEnd, y2)\n\tif vml == nil {\n\t\tvml = &vmlDrawing{\n\t\t\tXMLNSv:  \"urn:schemas-microsoft-com:vml\",\n\t\t\tXMLNSo:  \"urn:schemas-microsoft-com:office:office\",\n\t\t\tXMLNSx:  \"urn:schemas-microsoft-com:office:excel\",\n\t\t\tXMLNSmv: \"http://macVmlSchemaUri\",\n\t\t\tShapeLayout: &xlsxShapeLayout{\n\t\t\t\tExt: \"edit\", IDmap: &xlsxIDmap{Ext: \"edit\", Data: sheetID},\n\t\t\t},\n\t\t\tShapeType: &xlsxShapeType{\n\t\t\t\tID:        fmt.Sprintf(\"_x0000_t%d\", vmlID),\n\t\t\t\tCoordSize: \"21600,21600\",\n\t\t\t\tSpt:       202,\n\t\t\t\tPath:      \"m0,0l0,21600,21600,21600,21600,0xe\",\n\t\t\t\tStroke:    &xlsxStroke{JoinStyle: \"miter\"},\n\t\t\t\tVPath:     &vPath{GradientShapeOK: \"t\", ConnectType: \"rect\"},\n\t\t\t},\n\t\t}\n\t\t// Load exist VML shapes from xl/drawings/vmlDrawing%d.vml\n\t\td, err := f.decodeVMLDrawingReader(drawingVML)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif d != nil {\n\t\t\tvml.ShapeType.ID = d.ShapeType.ID\n\t\t\tvml.ShapeType.CoordSize = d.ShapeType.CoordSize\n\t\t\tvml.ShapeType.Spt = d.ShapeType.Spt\n\t\t\tvml.ShapeType.Path = d.ShapeType.Path\n\t\t\tfor _, v := range d.Shape {\n\t\t\t\ts := xlsxShape{\n\t\t\t\t\tID:          v.ID,\n\t\t\t\t\tType:        v.Type,\n\t\t\t\t\tStyle:       v.Style,\n\t\t\t\t\tButton:      v.Button,\n\t\t\t\t\tFilled:      v.Filled,\n\t\t\t\t\tFillColor:   v.FillColor,\n\t\t\t\t\tInsetMode:   v.InsetMode,\n\t\t\t\t\tStroked:     v.Stroked,\n\t\t\t\t\tStrokeColor: v.StrokeColor,\n\t\t\t\t\tVal:         v.Val,\n\t\t\t\t}\n\t\t\t\tvml.Shape = append(vml.Shape, s)\n\t\t\t}\n\t\t}\n\t}\n\tsp, err := f.addFormCtrlShape(preset, col, row, anchor, opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\ts, _ := xml.Marshal(sp)\n\tshape := xlsxShape{\n\t\tID:          \"_x0000_s1025\",\n\t\tType:        fmt.Sprintf(\"#_x0000_t%d\", vmlID),\n\t\tStyle:       style,\n\t\tButton:      preset.strokeButton,\n\t\tFilled:      preset.filled,\n\t\tFillColor:   preset.fillColor,\n\t\tStroked:     preset.stroked,\n\t\tStrokeColor: preset.strokeColor,\n\t\tVal:         string(s[13 : len(s)-14]),\n\t}\n\tvml.Shape = append(vml.Shape, shape)\n\tf.VMLDrawing[drawingVML] = vml\n\treturn err\n}\n\n// GetFormControls retrieves all form controls in a worksheet by a given\n// worksheet name. Note that, this function does not support getting the width\n// and height of the form controls currently.\nfunc (f *File) GetFormControls(sheet string) ([]FormControl, error) {\n\tvar formControls []FormControl\n\t// Read sheet data\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn formControls, err\n\t}\n\tif ws.LegacyDrawing == nil {\n\t\treturn formControls, err\n\t}\n\ttarget := f.getSheetRelationshipsTargetByID(sheet, ws.LegacyDrawing.RID)\n\tdrawingVML := strings.ReplaceAll(target, \"..\", \"xl\")\n\tvml := f.VMLDrawing[drawingVML]\n\tif vml == nil {\n\t\t// Load exist VML shapes from xl/drawings/vmlDrawing%d.vml\n\t\td, err := f.decodeVMLDrawingReader(drawingVML)\n\t\tif err != nil {\n\t\t\treturn formControls, err\n\t\t}\n\t\tfor _, sp := range d.Shape {\n\t\t\tif sp.Type != \"#_x0000_t201\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tformControl, err := extractFormControl(sp.Val)\n\t\t\tif err != nil {\n\t\t\t\treturn formControls, err\n\t\t\t}\n\t\t\tif formControl.Type == FormControlNote || formControl.Cell == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tformControls = append(formControls, formControl)\n\t\t}\n\t\treturn formControls, err\n\t}\n\tfor _, sp := range vml.Shape {\n\t\tif sp.Type != \"#_x0000_t201\" {\n\t\t\tcontinue\n\t\t}\n\t\tformControl, err := extractFormControl(sp.Val)\n\t\tif err != nil {\n\t\t\treturn formControls, err\n\t\t}\n\t\tif formControl.Type == FormControlNote || formControl.Cell == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tformControls = append(formControls, formControl)\n\t}\n\treturn formControls, err\n}\n\n// extractFormControl provides a function to extract form controls for a\n// worksheets by given client data.\nfunc extractFormControl(clientData string) (FormControl, error) {\n\tvar (\n\t\terr         error\n\t\tformControl FormControl\n\t\tshapeVal    decodeShapeVal\n\t)\n\tif err = xml.Unmarshal([]byte(fmt.Sprintf(\"<shape>%s</shape>\", clientData)), &shapeVal); err != nil {\n\t\treturn formControl, err\n\t}\n\tfor formCtrlType, preset := range formCtrlPresets {\n\t\tif shapeVal.ClientData.ObjectType == preset.objectType && shapeVal.ClientData.Anchor != \"\" {\n\t\t\tformControl.Paragraph = extractVMLFont(shapeVal.TextBox.Div.Font)\n\t\t\tif len(formControl.Paragraph) > 0 && formControl.Paragraph[0].Font == nil {\n\t\t\t\tformControl.Text = formControl.Paragraph[0].Text\n\t\t\t\tformControl.Paragraph = formControl.Paragraph[1:]\n\t\t\t}\n\t\t\tformControl.Type = formCtrlType\n\t\t\tcol, row, err := extractAnchorCell(shapeVal.ClientData.Anchor)\n\t\t\tif err != nil {\n\t\t\t\treturn formControl, err\n\t\t\t}\n\t\t\tif formControl.Cell, err = CoordinatesToCellName(col+1, row+1); err != nil {\n\t\t\t\treturn formControl, err\n\t\t\t}\n\t\t\tformControl.Macro = shapeVal.ClientData.FmlaMacro\n\t\t\tformControl.Checked = shapeVal.ClientData.Checked != 0\n\t\t\tformControl.CellLink = shapeVal.ClientData.FmlaLink\n\t\t\tformControl.CurrentVal = shapeVal.ClientData.Val\n\t\t\tformControl.MinVal = shapeVal.ClientData.Min\n\t\t\tformControl.MaxVal = shapeVal.ClientData.Max\n\t\t\tformControl.IncChange = shapeVal.ClientData.Inc\n\t\t\tformControl.PageChange = shapeVal.ClientData.Page\n\t\t\tformControl.Horizontally = shapeVal.ClientData.Horiz != nil\n\t\t}\n\t}\n\treturn formControl, err\n}\n\n// extractAnchorCell extract left-top cell coordinates from given VML anchor\n// comma-separated list values.\nfunc extractAnchorCell(anchor string) (int, int, error) {\n\tvar (\n\t\tleftCol, topRow int\n\t\terr             error\n\t\tpos             = strings.Split(anchor, \",\")\n\t)\n\tif len(pos) != 8 {\n\t\treturn leftCol, topRow, ErrParameterInvalid\n\t}\n\tleftCol, err = strconv.Atoi(strings.TrimSpace(pos[0]))\n\tif err != nil {\n\t\treturn leftCol, topRow, ErrColumnNumber\n\t}\n\ttopRow, err = strconv.Atoi(strings.TrimSpace(pos[2]))\n\treturn leftCol, topRow, err\n}\n\n// extractVMLFont extract rich-text and font format from given VML font element.\nfunc extractVMLFont(font []decodeVMLFont) []RichTextRun {\n\tvar runs []RichTextRun\n\textractU := func(u *decodeVMLFontU, run *RichTextRun) {\n\t\tif u == nil {\n\t\t\treturn\n\t\t}\n\t\trun.Text += u.Val\n\t\tif run.Font == nil {\n\t\t\trun.Font = &Font{}\n\t\t}\n\t\trun.Font.Underline = \"single\"\n\t\tif u.Class == \"font1\" {\n\t\t\trun.Font.Underline = \"double\"\n\t\t}\n\t}\n\textractI := func(i *decodeVMLFontI, run *RichTextRun) {\n\t\tif i == nil {\n\t\t\treturn\n\t\t}\n\t\textractU(i.U, run)\n\t\trun.Text += i.Val\n\t\tif run.Font == nil {\n\t\t\trun.Font = &Font{}\n\t\t}\n\t\trun.Font.Italic = true\n\t}\n\textractB := func(b *decodeVMLFontB, run *RichTextRun) {\n\t\tif b == nil {\n\t\t\treturn\n\t\t}\n\t\textractI(b.I, run)\n\t\trun.Text += b.Val\n\t\tif run.Font == nil {\n\t\t\trun.Font = &Font{}\n\t\t}\n\t\trun.Font.Bold = true\n\t}\n\tfor _, fnt := range font {\n\t\tvar run RichTextRun\n\t\textractB(fnt.B, &run)\n\t\textractI(fnt.I, &run)\n\t\textractU(fnt.U, &run)\n\t\trun.Text += fnt.Val\n\t\tif fnt.Face != \"\" || fnt.Size > 0 || fnt.Color != \"\" {\n\t\t\tif run.Font == nil {\n\t\t\t\trun.Font = &Font{}\n\t\t\t}\n\t\t\trun.Font.Family = fnt.Face\n\t\t\trun.Font.Size = float64(fnt.Size / 20)\n\t\t\trun.Font.Color = fnt.Color\n\t\t}\n\t\truns = append(runs, run)\n\t}\n\treturn runs\n}\n\n// AddHeaderFooterImage provides a mechanism to set the graphics that can be\n// referenced in the header and footer definitions via &G, supported image\n// types: EMF, EMZ, GIF, ICO, JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ.\n//\n// The extension should be provided with a \".\" in front, e.g. \".png\".\n// The width and height should have units in them, e.g. \"100pt\".\nfunc (f *File) AddHeaderFooterImage(sheet string, opts *HeaderFooterImageOptions) error {\n\tws, err := f.workSheetReader(sheet)\n\tif err != nil {\n\t\treturn err\n\t}\n\text, ok := supportedImageTypes[strings.ToLower(opts.Extension)]\n\tif !ok {\n\t\treturn ErrImgExt\n\t}\n\tsheetID := f.getSheetID(sheet)\n\tvmlID := f.countVMLDrawing() + 1\n\tdrawingVML := \"xl/drawings/vmlDrawing\" + strconv.Itoa(vmlID) + \".vml\"\n\tsheetRelationshipsDrawingVML := \"../drawings/vmlDrawing\" + strconv.Itoa(vmlID) + \".vml\"\n\tsheetXMLPath, _ := f.getSheetXMLPath(sheet)\n\tsheetRels := \"xl/worksheets/_rels/\" + strings.TrimPrefix(sheetXMLPath, \"xl/worksheets/\") + \".rels\"\n\tif ws.LegacyDrawingHF != nil {\n\t\t// The worksheet already has a VML relationships, use the relationships drawing ../drawings/vmlDrawing%d.vml.\n\t\tsheetRelationshipsDrawingVML = f.getSheetRelationshipsTargetByID(sheet, ws.LegacyDrawingHF.RID)\n\t\tvmlID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingVML, \"../drawings/vmlDrawing\"), \".vml\"))\n\t\tdrawingVML = strings.ReplaceAll(sheetRelationshipsDrawingVML, \"..\", \"xl\")\n\t} else {\n\t\t// Add first VML drawing for given sheet.\n\t\trID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, \"\")\n\t\tf.addSheetNameSpace(sheet, SourceRelationship)\n\t\tf.addSheetLegacyDrawingHF(sheet, rID)\n\t}\n\n\tshapeID := map[HeaderFooterImagePositionType]string{\n\t\tHeaderFooterImagePositionLeft:   \"L\",\n\t\tHeaderFooterImagePositionCenter: \"C\",\n\t\tHeaderFooterImagePositionRight:  \"R\",\n\t}[opts.Position] +\n\t\tmap[bool]string{false: \"H\", true: \"F\"}[opts.IsFooter] +\n\t\tmap[bool]string{false: \"\", true: \"FIRST\"}[opts.FirstPage]\n\tvml := f.VMLDrawing[drawingVML]\n\tif vml == nil {\n\t\tvml = &vmlDrawing{\n\t\t\tXMLNSv: \"urn:schemas-microsoft-com:vml\",\n\t\t\tXMLNSo: \"urn:schemas-microsoft-com:office:office\",\n\t\t\tXMLNSx: \"urn:schemas-microsoft-com:office:excel\",\n\t\t\tShapeLayout: &xlsxShapeLayout{\n\t\t\t\tExt: \"edit\", IDmap: &xlsxIDmap{Ext: \"edit\", Data: sheetID},\n\t\t\t},\n\t\t\tShapeType: &xlsxShapeType{\n\t\t\t\tID:             \"_x0000_t75\",\n\t\t\t\tCoordSize:      \"21600,21600\",\n\t\t\t\tSpt:            75,\n\t\t\t\tPreferRelative: \"t\",\n\t\t\t\tPath:           \"m@4@5l@4@11@9@11@9@5xe\",\n\t\t\t\tFilled:         \"f\",\n\t\t\t\tStroked:        \"f\",\n\t\t\t\tStroke:         &xlsxStroke{JoinStyle: \"miter\"},\n\t\t\t\tVFormulas: &vFormulas{\n\t\t\t\t\tFormulas: []vFormula{\n\t\t\t\t\t\t{Equation: \"if lineDrawn pixelLineWidth 0\"},\n\t\t\t\t\t\t{Equation: \"sum @0 1 0\"},\n\t\t\t\t\t\t{Equation: \"sum 0 0 @1\"},\n\t\t\t\t\t\t{Equation: \"prod @2 1 2\"},\n\t\t\t\t\t\t{Equation: \"prod @3 21600 pixelWidth\"},\n\t\t\t\t\t\t{Equation: \"prod @3 21600 pixelHeight\"},\n\t\t\t\t\t\t{Equation: \"sum @0 0 1\"},\n\t\t\t\t\t\t{Equation: \"prod @6 1 2\"},\n\t\t\t\t\t\t{Equation: \"prod @7 21600 pixelWidth\"},\n\t\t\t\t\t\t{Equation: \"sum @8 21600 0\"},\n\t\t\t\t\t\t{Equation: \"prod @7 21600 pixelHeight\"},\n\t\t\t\t\t\t{Equation: \"sum @10 21600 0\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tVPath: &vPath{ExtrusionOK: \"f\", GradientShapeOK: \"t\", ConnectType: \"rect\"},\n\t\t\t\tLock:  &oLock{Ext: \"edit\", AspectRatio: \"t\"},\n\t\t\t},\n\t\t}\n\t\t// Load exist VML shapes from xl/drawings/vmlDrawing%d.vml\n\t\td, err := f.decodeVMLDrawingReader(drawingVML)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif d != nil {\n\t\t\tvml.ShapeType.ID = d.ShapeType.ID\n\t\t\tvml.ShapeType.CoordSize = d.ShapeType.CoordSize\n\t\t\tvml.ShapeType.Spt = d.ShapeType.Spt\n\t\t\tvml.ShapeType.PreferRelative = d.ShapeType.PreferRelative\n\t\t\tvml.ShapeType.Path = d.ShapeType.Path\n\t\t\tvml.ShapeType.Filled = d.ShapeType.Filled\n\t\t\tvml.ShapeType.Stroked = d.ShapeType.Stroked\n\t\t\tfor _, v := range d.Shape {\n\t\t\t\ts := xlsxShape{\n\t\t\t\t\tID:    v.ID,\n\t\t\t\t\tSpID:  v.SpID,\n\t\t\t\t\tType:  v.Type,\n\t\t\t\t\tStyle: v.Style,\n\t\t\t\t\tVal:   v.Val,\n\t\t\t\t}\n\t\t\t\tvml.Shape = append(vml.Shape, s)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor idx, shape := range vml.Shape {\n\t\tif shape.ID == shapeID {\n\t\t\tvml.Shape = append(vml.Shape[:idx], vml.Shape[idx+1:]...)\n\t\t}\n\t}\n\n\tstyle := fmt.Sprintf(\"position:absolute;margin-left:0;margin-top:0;width:%s;height:%s;z-index:1\", opts.Width, opts.Height)\n\tdrawingVMLRels := \"xl/drawings/_rels/vmlDrawing\" + strconv.Itoa(vmlID) + \".vml.rels\"\n\n\tmediaStr := \"..\" + strings.TrimPrefix(f.addMedia(opts.File, ext), \"xl\")\n\timageID := f.addRels(drawingVMLRels, SourceRelationshipImage, mediaStr, \"\")\n\n\tshape := xlsxShape{\n\t\tID:    shapeID,\n\t\tSpID:  \"_x0000_s1025\",\n\t\tType:  \"#_x0000_t75\",\n\t\tStyle: style,\n\t}\n\tsp, _ := xml.Marshal(encodeShape{\n\t\tImageData: &vImageData{RelID: \"rId\" + strconv.Itoa(imageID)},\n\t\tLock:      &oLock{Ext: \"edit\", Rotation: \"t\"},\n\t})\n\n\tshape.Val = string(sp[13 : len(sp)-14])\n\tvml.Shape = append(vml.Shape, shape)\n\tf.VMLDrawing[drawingVML] = vml\n\n\tif err := f.setContentTypePartImageExtensions(); err != nil {\n\t\treturn err\n\t}\n\treturn f.setContentTypePartVMLExtensions()\n}\n"
  },
  {
    "path": "vmlDrawing.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// vmlDrawing directly maps the root element in the file\n// xl/drawings/vmlDrawing%d.vml.\ntype vmlDrawing struct {\n\tXMLName     xml.Name         `xml:\"xml\"`\n\tXMLNSv      string           `xml:\"xmlns:v,attr\"`\n\tXMLNSo      string           `xml:\"xmlns:o,attr\"`\n\tXMLNSx      string           `xml:\"xmlns:x,attr\"`\n\tXMLNSmv     string           `xml:\"xmlns:mv,attr,omitempty\"`\n\tShapeLayout *xlsxShapeLayout `xml:\"o:shapelayout\"`\n\tShapeType   *xlsxShapeType   `xml:\"v:shapetype\"`\n\tShape       []xlsxShape      `xml:\"v:shape\"`\n}\n\n// xlsxShapeLayout directly maps the shapelayout element. This element contains\n// child elements that store information used in the editing and layout of\n// shapes.\ntype xlsxShapeLayout struct {\n\tExt   string     `xml:\"v:ext,attr\"`\n\tIDmap *xlsxIDmap `xml:\"o:idmap\"`\n}\n\n// xlsxIDmap directly maps the idmap element.\ntype xlsxIDmap struct {\n\tExt  string `xml:\"v:ext,attr\"`\n\tData int    `xml:\"data,attr\"`\n}\n\n// xlsxShape directly maps the shape element.\ntype xlsxShape struct {\n\tXMLName     xml.Name `xml:\"v:shape\"`\n\tID          string   `xml:\"id,attr\"`\n\tSpID        string   `xml:\"o:spid,attr,omitempty\"`\n\tType        string   `xml:\"type,attr\"`\n\tStyle       string   `xml:\"style,attr\"`\n\tButton      string   `xml:\"o:button,attr,omitempty\"`\n\tFilled      string   `xml:\"filled,attr,omitempty\"`\n\tFillColor   string   `xml:\"fillcolor,attr,omitempty\"`\n\tInsetMode   string   `xml:\"urn:schemas-microsoft-com:office:office insetmode,attr,omitempty\"`\n\tStroked     string   `xml:\"stroked,attr,omitempty\"`\n\tStrokeColor string   `xml:\"strokecolor,attr,omitempty\"`\n\tVal         string   `xml:\",innerxml\"`\n}\n\n// xlsxShapeType directly maps the shapetype element.\ntype xlsxShapeType struct {\n\tID             string      `xml:\"id,attr\"`\n\tCoordSize      string      `xml:\"coordsize,attr\"`\n\tSpt            int         `xml:\"o:spt,attr\"`\n\tPreferRelative string      `xml:\"o:preferrelative,attr,omitempty\"`\n\tPath           string      `xml:\"path,attr\"`\n\tFilled         string      `xml:\"filled,attr,omitempty\"`\n\tStroked        string      `xml:\"stroked,attr,omitempty\"`\n\tStroke         *xlsxStroke `xml:\"v:stroke\"`\n\tVFormulas      *vFormulas  `xml:\"v:formulas\"`\n\tVPath          *vPath      `xml:\"v:path\"`\n\tLock           *oLock      `xml:\"o:lock\"`\n}\n\n// xlsxStroke directly maps the stroke element.\ntype xlsxStroke struct {\n\tJoinStyle string `xml:\"joinstyle,attr\"`\n}\n\n// vPath directly maps the v:path element.\ntype vPath struct {\n\tExtrusionOK     string `xml:\"o:extrusionok,attr,omitempty\"`\n\tGradientShapeOK string `xml:\"gradientshapeok,attr,omitempty\"`\n\tConnectType     string `xml:\"o:connecttype,attr\"`\n}\n\n// oLock directly maps the o:lock element.\ntype oLock struct {\n\tExt         string `xml:\"v:ext,attr\"`\n\tRotation    string `xml:\"rotation,attr,omitempty\"`\n\tAspectRatio string `xml:\"aspectratio,attr,omitempty\"`\n}\n\n// vFormulas directly maps to the v:formulas element\ntype vFormulas struct {\n\tFormulas []vFormula `xml:\"v:f\"`\n}\n\n// vFormula directly maps to the v:f element\ntype vFormula struct {\n\tEquation string `xml:\"eqn,attr\"`\n}\n\n// vFill directly maps the v:fill element. This element must be defined within a\n// Shape element.\ntype vFill struct {\n\tAngle  int    `xml:\"angle,attr,omitempty\"`\n\tColor2 string `xml:\"color2,attr\"`\n\tType   string `xml:\"type,attr,omitempty\"`\n\tFill   *oFill `xml:\"o:fill\"`\n}\n\n// oFill directly maps the o:fill element.\ntype oFill struct {\n\tExt  string `xml:\"v:ext,attr\"`\n\tType string `xml:\"type,attr,omitempty\"`\n}\n\n// vShadow directly maps the v:shadow element. This element must be defined\n// within a Shape element. In addition, the On attribute must be set to True.\ntype vShadow struct {\n\tOn       string `xml:\"on,attr\"`\n\tColor    string `xml:\"color,attr,omitempty\"`\n\tObscured string `xml:\"obscured,attr\"`\n}\n\n// vTextBox directly maps the v:textbox element. This element must be defined\n// within a Shape element.\ntype vTextBox struct {\n\tStyle string   `xml:\"style,attr\"`\n\tDiv   *xlsxDiv `xml:\"div\"`\n}\n\n// vImageData directly maps the v:imagedata element. This element must be\n// defined within a Shape element.\ntype vImageData struct {\n\tRelID string `xml:\"o:relid,attr\"`\n\tTitle string `xml:\"o:title,attr,omitempty\"`\n}\n\n// xlsxDiv directly maps the div element.\ntype xlsxDiv struct {\n\tStyle string    `xml:\"style,attr\"`\n\tFont  []vmlFont `xml:\"font\"`\n}\n\ntype vmlFont struct {\n\tFace    string `xml:\"face,attr,omitempty\"`\n\tSize    uint   `xml:\"size,attr,omitempty\"`\n\tColor   string `xml:\"color,attr,omitempty\"`\n\tContent string `xml:\",innerxml\"`\n}\n\n// xClientData (Attached Object Data) directly maps the x:ClientData element.\n// This element specifies data associated with objects attached to a\n// spreadsheet. While this element might contain any of the child elements\n// below, only certain combinations are meaningful. The ObjectType attribute\n// determines the kind of object the element represents and which subset of\n// child elements is appropriate. Relevant groups are identified for each child\n// element.\ntype xClientData struct {\n\tObjectType    string  `xml:\"ObjectType,attr\"`\n\tMoveWithCells *string `xml:\"x:MoveWithCells\"`\n\tSizeWithCells *string `xml:\"x:SizeWithCells\"`\n\tAnchor        string  `xml:\"x:Anchor\"`\n\tLocked        string  `xml:\"x:Locked,omitempty\"`\n\tPrintObject   string  `xml:\"x:PrintObject,omitempty\"`\n\tAutoFill      string  `xml:\"x:AutoFill,omitempty\"`\n\tFmlaMacro     string  `xml:\"x:FmlaMacro,omitempty\"`\n\tTextHAlign    string  `xml:\"x:TextHAlign,omitempty\"`\n\tTextVAlign    string  `xml:\"x:TextVAlign,omitempty\"`\n\tRow           *int    `xml:\"x:Row\"`\n\tColumn        *int    `xml:\"x:Column\"`\n\tChecked       int     `xml:\"x:Checked,omitempty\"`\n\tFmlaLink      string  `xml:\"x:FmlaLink,omitempty\"`\n\tNoThreeD      *string `xml:\"x:NoThreeD\"`\n\tFirstButton   *string `xml:\"x:FirstButton\"`\n\tVal           uint    `xml:\"x:Val,omitempty\"`\n\tMin           uint    `xml:\"x:Min,omitempty\"`\n\tMax           uint    `xml:\"x:Max,omitempty\"`\n\tInc           uint    `xml:\"x:Inc,omitempty\"`\n\tPage          uint    `xml:\"x:Page,omitempty\"`\n\tHoriz         *string `xml:\"x:Horiz\"`\n\tDx            uint    `xml:\"x:Dx,omitempty\"`\n}\n\n// decodeVmlDrawing defines the structure used to parse the file\n// xl/drawings/vmlDrawing%d.vml.\ntype decodeVmlDrawing struct {\n\tShapeType decodeShapeType `xml:\"urn:schemas-microsoft-com:vml shapetype\"`\n\tShape     []decodeShape   `xml:\"urn:schemas-microsoft-com:vml shape\"`\n}\n\n// decodeShapeType defines the structure used to parse the shapetype element in\n// the file xl/drawings/vmlDrawing%d.vml.\ntype decodeShapeType struct {\n\tID             string `xml:\"id,attr\"`\n\tCoordSize      string `xml:\"coordsize,attr\"`\n\tSpt            int    `xml:\"spt,attr\"`\n\tPreferRelative string `xml:\"preferrelative,attr,omitempty\"`\n\tPath           string `xml:\"path,attr\"`\n\tFilled         string `xml:\"filled,attr,omitempty\"`\n\tStroked        string `xml:\"stroked,attr,omitempty\"`\n}\n\n// decodeShape defines the structure used to parse the particular shape element.\ntype decodeShape struct {\n\tID          string `xml:\"id,attr\"`\n\tSpID        string `xml:\"spid,attr,omitempty\"`\n\tType        string `xml:\"type,attr\"`\n\tStyle       string `xml:\"style,attr\"`\n\tButton      string `xml:\"button,attr,omitempty\"`\n\tFilled      string `xml:\"filled,attr,omitempty\"`\n\tFillColor   string `xml:\"fillcolor,attr,omitempty\"`\n\tInsetMode   string `xml:\"urn:schemas-microsoft-com:office:office insetmode,attr,omitempty\"`\n\tStroked     string `xml:\"stroked,attr,omitempty\"`\n\tStrokeColor string `xml:\"strokecolor,attr,omitempty\"`\n\tVal         string `xml:\",innerxml\"`\n}\n\n// decodeShapeVal defines the structure used to parse the sub-element of the\n// shape in the file xl/drawings/vmlDrawing%d.vml.\ntype decodeShapeVal struct {\n\tTextBox    decodeVMLTextBox    `xml:\"textbox\"`\n\tClientData decodeVMLClientData `xml:\"ClientData\"`\n}\n\n// decodeVMLFontU defines the structure used to parse the u element in the VML.\ntype decodeVMLFontU struct {\n\tClass string `xml:\"class,attr\"`\n\tVal   string `xml:\",chardata\"`\n}\n\n// decodeVMLFontI defines the structure used to parse the i element in the VML.\ntype decodeVMLFontI struct {\n\tU   *decodeVMLFontU `xml:\"u\"`\n\tVal string          `xml:\",chardata\"`\n}\n\n// decodeVMLFontB defines the structure used to parse the b element in the VML.\ntype decodeVMLFontB struct {\n\tI   *decodeVMLFontI `xml:\"i\"`\n\tU   *decodeVMLFontU `xml:\"u\"`\n\tVal string          `xml:\",chardata\"`\n}\n\n// decodeVMLFont defines the structure used to parse the font element in the VML.\ntype decodeVMLFont struct {\n\tFace  string          `xml:\"face,attr,omitempty\"`\n\tSize  uint            `xml:\"size,attr,omitempty\"`\n\tColor string          `xml:\"color,attr,omitempty\"`\n\tB     *decodeVMLFontB `xml:\"b\"`\n\tI     *decodeVMLFontI `xml:\"i\"`\n\tU     *decodeVMLFontU `xml:\"u\"`\n\tVal   string          `xml:\",chardata\"`\n}\n\n// decodeVMLDiv defines the structure used to parse the div element in the VML.\ntype decodeVMLDiv struct {\n\tFont []decodeVMLFont `xml:\"font\"`\n}\n\n// decodeVMLTextBox defines the structure used to parse the v:textbox element in\n// the file xl/drawings/vmlDrawing%d.vml.\ntype decodeVMLTextBox struct {\n\tDiv decodeVMLDiv `xml:\"div\"`\n}\n\n// decodeVMLClientData defines the structure used to parse the x:ClientData\n// element in the file xl/drawings/vmlDrawing%d.vml.\ntype decodeVMLClientData struct {\n\tObjectType string `xml:\"ObjectType,attr\"`\n\tAnchor     string\n\tFmlaMacro  string\n\tColumn     *int\n\tRow        *int\n\tChecked    int\n\tFmlaLink   string\n\tVal        uint\n\tMin        uint\n\tMax        uint\n\tInc        uint\n\tPage       uint\n\tHoriz      *string\n}\n\n// encodeShape defines the structure used to re-serialization shape element.\ntype encodeShape struct {\n\tFill       *vFill       `xml:\"v:fill\"`\n\tShadow     *vShadow     `xml:\"v:shadow\"`\n\tPath       *vPath       `xml:\"v:path\"`\n\tTextBox    *vTextBox    `xml:\"v:textbox\"`\n\tImageData  *vImageData  `xml:\"v:imagedata\"`\n\tClientData *xClientData `xml:\"x:ClientData\"`\n\tLock       *oLock       `xml:\"o:lock\"`\n}\n\n// formCtrlPreset defines the structure used to form control presets.\ntype formCtrlPreset struct {\n\tautoFill     string\n\tfill         *vFill\n\tfillColor    string\n\tfilled       string\n\tfirstButton  *string\n\tnoThreeD     *string\n\tobjectType   string\n\tshadow       *vShadow\n\tstrokeButton string\n\tstrokeColor  string\n\tstroked      string\n\ttextHAlign   string\n\ttextVAlign   string\n}\n\n// vmlOptions defines the structure used to internal comments and form controls.\ntype vmlOptions struct {\n\tformCtrl bool\n\tsheet    string\n\tComment\n\tFormControl\n}\n\n// FormControl directly maps the form controls information.\ntype FormControl struct {\n\tCell         string\n\tMacro        string\n\tWidth        uint\n\tHeight       uint\n\tChecked      bool\n\tCurrentVal   uint\n\tMinVal       uint\n\tMaxVal       uint\n\tIncChange    uint\n\tPageChange   uint\n\tHorizontally bool\n\tCellLink     string\n\tText         string\n\tParagraph    []RichTextRun\n\tType         FormControlType\n\tFormat       GraphicOptions\n}\n\n// HeaderFooterImageOptions defines the settings for an image to be accessible\n// from the worksheet header and footer options.\ntype HeaderFooterImageOptions struct {\n\tPosition  HeaderFooterImagePositionType\n\tFile      []byte\n\tIsFooter  bool\n\tFirstPage bool\n\tExtension string\n\tWidth     string\n\tHeight    string\n}\n"
  },
  {
    "path": "vml_test.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestAddComment(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\n\ts := strings.Repeat(\"c\", TotalCellChars+1)\n\tassert.NoError(t, f.AddComment(\"Sheet1\", Comment{Cell: \"A30\", Author: s, Text: s, Paragraph: []RichTextRun{{Text: s}, {Text: s}}}))\n\tassert.NoError(t, f.AddComment(\"Sheet2\", Comment{Cell: \"B7\", Author: \"Excelize\", Text: s[:TotalCellChars-1], Paragraph: []RichTextRun{{Text: \"Excelize: \", Font: &Font{Bold: true}}, {Text: \"This is a comment.\"}}}))\n\n\t// Test add comment on a cell which already exists a comment\n\tassert.Equal(t, f.AddComment(\"Sheet2\", Comment{Cell: \"B7\", Author: s, Text: s}), newAddCommentError(\"B7\"))\n\t// Test add comment on not exists worksheet\n\tassert.EqualError(t, f.AddComment(\"SheetN\", Comment{Cell: \"B7\", Author: \"Excelize\", Paragraph: []RichTextRun{{Text: \"Excelize: \", Font: &Font{Bold: true}}, {Text: \"This is a comment.\"}}}), \"sheet SheetN does not exist\")\n\t// Test add comment on with illegal cell reference\n\tassert.Equal(t, newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")), f.AddComment(\"Sheet1\", Comment{Cell: \"A\", Author: \"Excelize\", Paragraph: []RichTextRun{{Text: \"Excelize: \", Font: &Font{Bold: true}}, {Text: \"This is a comment.\"}}}))\n\tcomments, err := f.GetComments(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, comments, 2)\n\tcomments, err = f.GetComments(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.Len(t, comments, 1)\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddComments.xlsx\")))\n\n\tf.Comments[\"xl/comments2.xml\"] = nil\n\tf.Pkg.Store(\"xl/comments2.xml\", []byte(xml.Header+`<comments xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><authors><author>Excelize: </author></authors><commentList><comment ref=\"B7\" authorId=\"0\"><text><t>Excelize: </t></text></comment></commentList></comments>`))\n\tcomments, err = f.GetComments(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, comments, 2)\n\tcomments, err = f.GetComments(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.Len(t, comments, 1)\n\tcomments, err = NewFile().GetComments(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, comments, 0)\n\n\t// Test add comments with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.AddComment(\"Sheet:1\", Comment{Cell: \"A1\", Author: \"Excelize\", Text: \"This is a comment.\"}))\n\n\t// Test add comments with unsupported charset\n\tf.Comments[\"xl/comments2.xml\"] = nil\n\tf.Pkg.Store(\"xl/comments2.xml\", MacintoshCyrillicCharset)\n\t_, err = f.GetComments(\"Sheet2\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test add comments with unsupported charset\n\tf.Comments[\"xl/comments2.xml\"] = nil\n\tf.Pkg.Store(\"xl/comments2.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddComment(\"Sheet2\", Comment{Cell: \"A30\", Text: \"Comment\"}), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test add comments with unsupported charset style sheet\n\tf.Styles = nil\n\tf.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddComment(\"Sheet2\", Comment{Cell: \"A30\", Text: \"Comment\"}), \"XML syntax error on line 1: invalid UTF-8\")\n\n\t// Test get comments on not exists worksheet\n\tcomments, err = f.GetComments(\"SheetN\")\n\tassert.Len(t, comments, 0)\n\tassert.EqualError(t, err, \"sheet SheetN does not exist\")\n}\n\nfunc TestDeleteComment(t *testing.T) {\n\tf, err := prepareTestBook1()\n\tif !assert.NoError(t, err) {\n\t\tt.FailNow()\n\t}\n\n\tassert.NoError(t, f.AddComment(\"Sheet2\", Comment{Cell: \"A40\", Text: \"Excelize: This is a comment1.\"}))\n\tassert.NoError(t, f.AddComment(\"Sheet2\", Comment{Cell: \"A41\", Paragraph: []RichTextRun{{Text: \"Excelize: \", Font: &Font{Bold: true}}, {Text: \"This is a comment2.\"}}}))\n\tassert.NoError(t, f.AddComment(\"Sheet2\", Comment{Cell: \"C41\", Paragraph: []RichTextRun{{Text: \"Excelize: \", Font: &Font{Bold: true}}, {Text: \"This is a comment3.\"}}}))\n\tassert.NoError(t, f.AddComment(\"Sheet2\", Comment{Cell: \"C42\", Paragraph: []RichTextRun{{Text: \"Excelize: \", Font: &Font{Bold: true}}, {Text: \"This is a comment4.\"}}}))\n\n\tassert.NoError(t, f.DeleteComment(\"Sheet2\", \"A40\"))\n\n\tcomments, err := f.GetComments(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.Len(t, comments, 3)\n\n\tcomments, err = NewFile().GetComments(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, comments, 0)\n\n\t// Test delete comment with invalid sheet name\n\tassert.Equal(t, ErrSheetNameInvalid, f.DeleteComment(\"Sheet:1\", \"A1\"))\n\t// Test delete all comments in a worksheet\n\tassert.NoError(t, f.DeleteComment(\"Sheet2\", \"A41\"))\n\tassert.NoError(t, f.DeleteComment(\"Sheet2\", \"C41\"))\n\tassert.NoError(t, f.DeleteComment(\"Sheet2\", \"C42\"))\n\tcomments, err = f.GetComments(\"Sheet2\")\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, 0, len(comments))\n\t// Test delete comment on not exists worksheet\n\tassert.EqualError(t, f.DeleteComment(\"SheetN\", \"A1\"), \"sheet SheetN does not exist\")\n\t// Test delete comment with worksheet part\n\tf.Pkg.Delete(\"xl/worksheets/sheet1.xml\")\n\tassert.NoError(t, f.DeleteComment(\"Sheet1\", \"A22\"))\n\n\tf.Comments[\"xl/comments2.xml\"] = nil\n\tf.Pkg.Store(\"xl/comments2.xml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.DeleteComment(\"Sheet2\", \"A41\"), \"XML syntax error on line 1: invalid UTF-8\")\n\n\tf = NewFile()\n\t// Test delete comment on a no comments worksheet\n\tassert.NoError(t, f.DeleteComment(\"Sheet1\", \"A1\"))\n}\n\nfunc TestDecodeVMLDrawingReader(t *testing.T) {\n\tf := NewFile()\n\tpath := \"xl/drawings/vmlDrawing1.xml\"\n\tf.Pkg.Store(path, MacintoshCyrillicCharset)\n\t_, err := f.decodeVMLDrawingReader(path)\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestCommentsReader(t *testing.T) {\n\tf := NewFile()\n\t// Test read comments with unsupported charset\n\tpath := \"xl/comments1.xml\"\n\tf.Pkg.Store(path, MacintoshCyrillicCharset)\n\t_, err := f.commentsReader(path)\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestCountComments(t *testing.T) {\n\tf := NewFile()\n\tf.Comments[\"xl/comments1.xml\"] = nil\n\tassert.Equal(t, f.countComments(), 1)\n}\n\nfunc TestAddDrawingVML(t *testing.T) {\n\t// Test addDrawingVML with illegal cell reference\n\tf := NewFile()\n\tassert.Equal(t, f.addDrawingVML(0, \"\", &vmlOptions{FormControl: FormControl{Cell: \"*\"}}), newCellNameToCoordinatesError(\"*\", newInvalidCellNameError(\"*\")))\n\n\tf.Pkg.Store(\"xl/drawings/vmlDrawing1.vml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.addDrawingVML(0, \"xl/drawings/vmlDrawing1.vml\", &vmlOptions{sheet: \"Sheet1\", FormControl: FormControl{Cell: \"A1\"}}), \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestFormControl(t *testing.T) {\n\tf := NewFile()\n\tformControls := []FormControl{\n\t\t{\n\t\t\tCell: \"D1\", Type: FormControlButton, Macro: \"Button1_Click\",\n\t\t},\n\t\t{\n\t\t\tCell: \"A1\", Type: FormControlButton, Macro: \"Button1_Click\",\n\t\t\tWidth: 140, Height: 60, Text: \"Button 1\\n\",\n\t\t\tParagraph: []RichTextRun{\n\t\t\t\t{\n\t\t\t\t\tFont: &Font{\n\t\t\t\t\t\tBold:      true,\n\t\t\t\t\t\tItalic:    true,\n\t\t\t\t\t\tUnderline: \"single\",\n\t\t\t\t\t\tFamily:    \"Times New Roman\",\n\t\t\t\t\t\tSize:      14,\n\t\t\t\t\t\tColor:     \"777777\",\n\t\t\t\t\t},\n\t\t\t\t\tText: \"C1=A1+B1\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tFormat: GraphicOptions{PrintObject: boolPtr(true), Positioning: \"absolute\"},\n\t\t},\n\t\t{\n\t\t\tCell: \"A5\", Type: FormControlCheckBox, Text: \"Check Box 1\",\n\t\t\tChecked: true, Format: GraphicOptions{\n\t\t\t\tPrintObject: boolPtr(false), Positioning: \"oneCell\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tCell: \"A6\", Type: FormControlCheckBox, Text: \"Check Box 2\",\n\t\t\tCellLink: \"C5\", Format: GraphicOptions{Positioning: \"twoCell\"},\n\t\t},\n\t\t{\n\t\t\tCell: \"A7\", Type: FormControlOptionButton, Text: \"Option Button 1\", Checked: true,\n\t\t},\n\t\t{\n\t\t\tCell: \"A8\", Type: FormControlOptionButton, Text: \"Option Button 2\",\n\t\t},\n\t\t{\n\t\t\tCell: \"D3\", Type: FormControlGroupBox, Text: \"Group Box 1\",\n\t\t\tWidth: 140, Height: 60,\n\t\t},\n\t\t{\n\t\t\tCell: \"A9\", Type: FormControlLabel, Text: \"Label 1\", Width: 140,\n\t\t},\n\t\t{\n\t\t\tCell: \"C5\", Type: FormControlSpinButton, Width: 40, Height: 60,\n\t\t\tCurrentVal: 7, MinVal: 5, MaxVal: 10, IncChange: 1, CellLink: \"C2\",\n\t\t},\n\t\t{\n\t\t\tCell: \"D7\", Type: FormControlScrollBar, Width: 140, Height: 20,\n\t\t\tCurrentVal: 50, MinVal: 10, MaxVal: 100, IncChange: 1, PageChange: 1, Horizontally: true, CellLink: \"C3\",\n\t\t},\n\t\t{\n\t\t\tCell: \"G1\", Type: FormControlScrollBar, Width: 20, Height: 140,\n\t\t\tCurrentVal: 50, MinVal: 1000, MaxVal: 100, IncChange: 1, PageChange: 1, CellLink: \"C4\",\n\t\t},\n\t}\n\tfor _, formCtrl := range formControls {\n\t\tassert.NoError(t, f.AddFormControl(\"Sheet1\", formCtrl))\n\t}\n\t// Test get from controls\n\tresult, err := f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, result, 11)\n\tfor i, formCtrl := range formControls {\n\t\tassert.Equal(t, formCtrl.Type, result[i].Type)\n\t\tassert.Equal(t, formCtrl.Cell, result[i].Cell)\n\t\tassert.Equal(t, formCtrl.Macro, result[i].Macro)\n\t\tassert.Equal(t, formCtrl.Checked, result[i].Checked)\n\t\tassert.Equal(t, formCtrl.CurrentVal, result[i].CurrentVal)\n\t\tassert.Equal(t, formCtrl.MinVal, result[i].MinVal)\n\t\tassert.Equal(t, formCtrl.MaxVal, result[i].MaxVal)\n\t\tassert.Equal(t, formCtrl.IncChange, result[i].IncChange)\n\t\tassert.Equal(t, formCtrl.Horizontally, result[i].Horizontally)\n\t\tassert.Equal(t, formCtrl.CellLink, result[i].CellLink)\n\t\tassert.Equal(t, formCtrl.Text, result[i].Text)\n\t\tassert.Equal(t, len(formCtrl.Paragraph), len(result[i].Paragraph))\n\t}\n\tassert.NoError(t, f.SetSheetProps(\"Sheet1\", &SheetPropsOptions{CodeName: stringPtr(\"Sheet1\")}))\n\tfile, err := os.ReadFile(filepath.Join(\"test\", \"vbaProject.bin\"))\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.AddVBAProject(file))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestAddFormControl.xlsm\")))\n\tassert.NoError(t, f.Close())\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestAddFormControl.xlsm\"))\n\tassert.NoError(t, err)\n\t// Test get from controls before add form controls\n\tresult, err = f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, result, 11)\n\t// Test add from control to a worksheet which already contains form controls\n\tassert.NoError(t, f.AddFormControl(\"Sheet1\", FormControl{\n\t\tCell: \"D4\", Type: FormControlButton, Macro: \"Button1_Click\",\n\t\tParagraph: []RichTextRun{{Font: &Font{Underline: \"double\"}, Text: \"Button 2\"}},\n\t}))\n\t// Test get from controls after add form controls\n\tresult, err = f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, result, 12)\n\t// Test add unsupported form control\n\tassert.Equal(t, f.AddFormControl(\"Sheet1\", FormControl{\n\t\tCell: \"A1\", Type: 0x37, Macro: \"Button1_Click\",\n\t}), ErrParameterInvalid)\n\t// Test add form control on not exists worksheet\n\tassert.Equal(t, ErrSheetNotExist{\"SheetN\"}, f.AddFormControl(\"SheetN\", FormControl{\n\t\tCell: \"A1\", Type: FormControlButton, Macro: \"Button1_Click\",\n\t}))\n\t// Test add form control with invalid positioning types\n\tassert.Equal(t, newInvalidOptionalValue(\"Positioning\", \"x\", supportedPositioning),\n\t\tf.AddFormControl(\"Sheet1\", FormControl{\n\t\t\tCell: \"A1\", Type: FormControlButton,\n\t\t\tFormat: GraphicOptions{Positioning: \"x\"},\n\t\t}))\n\t// Test add spin form control with illegal cell link reference\n\tassert.Equal(t, f.AddFormControl(\"Sheet1\", FormControl{\n\t\tCell: \"C5\", Type: FormControlSpinButton, CellLink: \"*\",\n\t}), newCellNameToCoordinatesError(\"*\", newInvalidCellNameError(\"*\")))\n\t// Test add spin form control with invalid scroll value\n\tassert.Equal(t, f.AddFormControl(\"Sheet1\", FormControl{\n\t\tCell: \"C5\", Type: FormControlSpinButton, CurrentVal: MaxFormControlValue + 1,\n\t}), ErrFormControlValue)\n\tassert.NoError(t, f.Close())\n\t// Test delete form control\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestAddFormControl.xlsm\"))\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.DeleteFormControl(\"Sheet1\", \"D1\"))\n\tassert.NoError(t, f.DeleteFormControl(\"Sheet1\", \"A1\"))\n\t// Test get from controls after delete form controls\n\tresult, err = f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, result, 9)\n\t// Test delete form control on not exists worksheet\n\tassert.Equal(t, ErrSheetNotExist{\"SheetN\"}, f.DeleteFormControl(\"SheetN\", \"A1\"))\n\t// Test delete form control with illegal cell link reference\n\tassert.Equal(t, f.DeleteFormControl(\"Sheet1\", \"A\"), newCellNameToCoordinatesError(\"A\", newInvalidCellNameError(\"A\")))\n\tassert.NoError(t, f.SaveAs(filepath.Join(\"test\", \"TestDeleteFormControl.xlsm\")))\n\tassert.NoError(t, f.Close())\n\t// Test delete form control with expected element\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestAddFormControl.xlsm\"))\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/drawings/vmlDrawing1.vml\", MacintoshCyrillicCharset)\n\tassert.Error(t, f.DeleteFormControl(\"Sheet1\", \"A1\"), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test delete form controls with invalid shape anchor\n\tf.DecodeVMLDrawing[\"xl/drawings/vmlDrawing1.vml\"] = &decodeVmlDrawing{\n\t\tShape: []decodeShape{{Type: \"#_x0000_t201\", Val: \"<x:ClientData ObjectType=\\\"Scroll\\\"><x:Anchor>0</x:Anchor></x:ClientData>\"}},\n\t}\n\tassert.Equal(t, ErrParameterInvalid, f.DeleteFormControl(\"Sheet1\", \"A1\"))\n\tassert.NoError(t, f.Close())\n\t// Test delete form control on a worksheet without form control\n\tf = NewFile()\n\tassert.NoError(t, f.DeleteFormControl(\"Sheet1\", \"A1\"))\n\t// Test get form controls on a worksheet without form control\n\t_, err = f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\t// Test get form controls on not exists worksheet\n\t_, err = f.GetFormControls(\"SheetN\")\n\tassert.Equal(t, ErrSheetNotExist{\"SheetN\"}, err)\n\t// Test get form controls with unsupported charset VML drawing\n\tf, err = OpenFile(filepath.Join(\"test\", \"TestAddFormControl.xlsm\"))\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/drawings/vmlDrawing1.vml\", MacintoshCyrillicCharset)\n\t_, err = f.GetFormControls(\"Sheet1\")\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get form controls with unsupported shape type\n\tf.DecodeVMLDrawing[\"xl/drawings/vmlDrawing1.vml\"] = &decodeVmlDrawing{\n\t\tShape: []decodeShape{{Type: \"_x0000_t202\"}},\n\t}\n\tformControls, err = f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, formControls, 0)\n\t// Test get form controls with bold font format\n\tf.DecodeVMLDrawing[\"xl/drawings/vmlDrawing1.vml\"] = &decodeVmlDrawing{\n\t\tShape: []decodeShape{{Type: \"#_x0000_t201\", Val: \"<v:textbox><div><font><b>Text</b></font></div></v:textbox><x:ClientData ObjectType=\\\"Scroll\\\"><x:Anchor>0,0,0,0,0,0,0,0</x:Anchor></x:ClientData>\"}},\n\t}\n\tformControls, err = f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.True(t, formControls[0].Paragraph[0].Font.Bold)\n\t// Test get form controls with italic font format\n\tf.DecodeVMLDrawing[\"xl/drawings/vmlDrawing1.vml\"] = &decodeVmlDrawing{\n\t\tShape: []decodeShape{{Type: \"#_x0000_t201\", Val: \"<v:textbox><div><font><i>Text</i></font></div></v:textbox><x:ClientData ObjectType=\\\"Scroll\\\"><x:Anchor>0,0,0,0,0,0,0,0</x:Anchor></x:ClientData>\"}},\n\t}\n\tformControls, err = f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.True(t, formControls[0].Paragraph[0].Font.Italic)\n\t// Test get form controls with font format\n\tf.DecodeVMLDrawing[\"xl/drawings/vmlDrawing1.vml\"] = &decodeVmlDrawing{\n\t\tShape: []decodeShape{{Type: \"#_x0000_t201\", Val: \"<v:textbox><div><font face=\\\"Calibri\\\" size=\\\"280\\\" color=\\\"#777777\\\">Text</font></div></v:textbox><x:ClientData ObjectType=\\\"Scroll\\\"><x:Anchor>0,0,0,0,0,0,0,0</x:Anchor></x:ClientData>\"}},\n\t}\n\tformControls, err = f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"Calibri\", formControls[0].Paragraph[0].Font.Family)\n\tassert.Equal(t, 14.0, formControls[0].Paragraph[0].Font.Size)\n\tassert.Equal(t, \"#777777\", formControls[0].Paragraph[0].Font.Color)\n\t// Test get form controls with italic font format\n\tf.DecodeVMLDrawing[\"xl/drawings/vmlDrawing1.vml\"] = &decodeVmlDrawing{\n\t\tShape: []decodeShape{{Type: \"#_x0000_t201\", Val: \"<v:textbox><div><font><i>Text</i></font></div></v:textbox><x:ClientData ObjectType=\\\"Scroll\\\"><x:Anchor>0,0,0,0,0,0,0,0</x:Anchor></x:ClientData>\"}},\n\t}\n\tformControls, err = f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.True(t, formControls[0].Paragraph[0].Font.Italic)\n\t// Test get form controls with invalid column number\n\tf.DecodeVMLDrawing[\"xl/drawings/vmlDrawing1.vml\"] = &decodeVmlDrawing{\n\t\tShape: []decodeShape{{Type: \"#_x0000_t201\", Val: fmt.Sprintf(\"<x:ClientData ObjectType=\\\"Scroll\\\"><x:Anchor>%d,0,0,0,0,0,0,0</x:Anchor></x:ClientData>\", MaxColumns)}},\n\t}\n\tformControls, err = f.GetFormControls(\"Sheet1\")\n\tassert.Equal(t, err, ErrColumnNumber)\n\tassert.Len(t, formControls, 0)\n\t// Test get form controls with comment (Note) shape type\n\tf.DecodeVMLDrawing[\"xl/drawings/vmlDrawing1.vml\"] = &decodeVmlDrawing{\n\t\tShape: []decodeShape{{Type: \"#_x0000_t201\", Val: \"<x:ClientData ObjectType=\\\"Note\\\"></x:ClientData>\"}},\n\t}\n\tformControls, err = f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, formControls, 0)\n\t// Test get form controls with unsupported shape type\n\tf.VMLDrawing[\"xl/drawings/vmlDrawing1.vml\"] = &vmlDrawing{\n\t\tShape: []xlsxShape{{Type: \"_x0000_t202\"}},\n\t}\n\tformControls, err = f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, formControls, 0)\n\t// Test get form controls with invalid column number\n\tf.VMLDrawing[\"xl/drawings/vmlDrawing1.vml\"] = &vmlDrawing{\n\t\tShape: []xlsxShape{{Type: \"#_x0000_t201\", Val: fmt.Sprintf(\"<x:ClientData ObjectType=\\\"Scroll\\\"><x:Anchor>%d,0,0,0,0,0,0,0</x:Anchor></x:ClientData>\", MaxColumns)}},\n\t}\n\tformControls, err = f.GetFormControls(\"Sheet1\")\n\tassert.Equal(t, err, ErrColumnNumber)\n\tassert.Len(t, formControls, 0)\n\t// Test get form controls with invalid shape anchor\n\tf.VMLDrawing[\"xl/drawings/vmlDrawing1.vml\"] = &vmlDrawing{\n\t\tShape: []xlsxShape{{Type: \"#_x0000_t201\", Val: \"<x:ClientData ObjectType=\\\"Scroll\\\"><x:Anchor>x,0,0,0,0,0,0,0</x:Anchor></x:ClientData>\"}},\n\t}\n\tformControls, err = f.GetFormControls(\"Sheet1\")\n\tassert.Equal(t, ErrColumnNumber, err)\n\tassert.Len(t, formControls, 0)\n\t// Test get form controls with comment (Note) shape type\n\tf.VMLDrawing[\"xl/drawings/vmlDrawing1.vml\"] = &vmlDrawing{\n\t\tShape: []xlsxShape{{Type: \"#_x0000_t201\", Val: \"<x:ClientData ObjectType=\\\"Note\\\"></x:ClientData>\"}},\n\t}\n\tformControls, err = f.GetFormControls(\"Sheet1\")\n\tassert.NoError(t, err)\n\tassert.Len(t, formControls, 0)\n\tassert.NoError(t, f.Close())\n}\n\nfunc TestExtractFormControl(t *testing.T) {\n\t// Test extract form control with unsupported charset\n\t_, err := extractFormControl(string(MacintoshCyrillicCharset))\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestAddHeaderFooterImage(t *testing.T) {\n\tf, sheet, wb := NewFile(), \"Sheet1\", filepath.Join(\"test\", \"TestAddHeaderFooterImage.xlsx\")\n\theaderFooterOptions := HeaderFooterOptions{\n\t\tDifferentFirst: true,\n\t\tOddHeader:      \"&L&GExcelize&C&G&R&G\",\n\t\tOddFooter:      \"&L&GExcelize&C&G&R&G\",\n\t\tFirstHeader:    \"&L&GExcelize&C&G&R&G\",\n\t\tFirstFooter:    \"&L&GExcelize&C&G&R&G\",\n\t}\n\tassert.NoError(t, f.SetHeaderFooter(sheet, &headerFooterOptions))\n\tassert.NoError(t, f.SetSheetView(sheet, -1, &ViewOptions{View: stringPtr(\"pageLayout\")}))\n\timages := map[string][]byte{\n\t\t\".wmf\": nil, \".tif\": nil, \".png\": nil,\n\t\t\".jpg\": nil, \".gif\": nil, \".emz\": nil, \".emf\": nil,\n\t}\n\tfor ext := range images {\n\t\timg, err := os.ReadFile(filepath.Join(\"test\", \"images\", \"excel\"+ext))\n\t\tassert.NoError(t, err)\n\t\timages[ext] = img\n\t}\n\tfor _, opt := range []struct {\n\t\tposition  HeaderFooterImagePositionType\n\t\tfile      []byte\n\t\tisFooter  bool\n\t\tfirstPage bool\n\t\text       string\n\t}{\n\t\t{position: HeaderFooterImagePositionLeft, file: images[\".tif\"], firstPage: true, ext: \".tif\"},\n\t\t{position: HeaderFooterImagePositionCenter, file: images[\".gif\"], firstPage: true, ext: \".gif\"},\n\t\t{position: HeaderFooterImagePositionRight, file: images[\".png\"], firstPage: true, ext: \".png\"},\n\t\t{position: HeaderFooterImagePositionLeft, file: images[\".emf\"], isFooter: true, firstPage: true, ext: \".emf\"},\n\t\t{position: HeaderFooterImagePositionCenter, file: images[\".wmf\"], isFooter: true, firstPage: true, ext: \".wmf\"},\n\t\t{position: HeaderFooterImagePositionRight, file: images[\".emz\"], isFooter: true, firstPage: true, ext: \".emz\"},\n\t\t{position: HeaderFooterImagePositionLeft, file: images[\".png\"], ext: \".png\"},\n\t\t{position: HeaderFooterImagePositionCenter, file: images[\".png\"], ext: \".png\"},\n\t\t{position: HeaderFooterImagePositionRight, file: images[\".png\"], ext: \".png\"},\n\t\t{position: HeaderFooterImagePositionLeft, file: images[\".tif\"], isFooter: true, ext: \".tif\"},\n\t\t{position: HeaderFooterImagePositionCenter, file: images[\".tif\"], isFooter: true, ext: \".tif\"},\n\t\t{position: HeaderFooterImagePositionRight, file: images[\".tif\"], isFooter: true, ext: \".tif\"},\n\t} {\n\t\tassert.NoError(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{\n\t\t\tPosition:  opt.position,\n\t\t\tFile:      opt.file,\n\t\t\tIsFooter:  opt.isFooter,\n\t\t\tFirstPage: opt.firstPage,\n\t\t\tExtension: opt.ext,\n\t\t\tWidth:     \"50pt\",\n\t\t\tHeight:    \"32pt\",\n\t\t}))\n\t}\n\tassert.NoError(t, f.SetCellValue(sheet, \"A1\", \"Example\"))\n\n\t// Test add header footer image with not exist sheet\n\tassert.EqualError(t, f.AddHeaderFooterImage(\"SheetN\", nil), \"sheet SheetN does not exist\")\n\t// Test add header footer image with unsupported file type\n\tassert.Equal(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{\n\t\tExtension: \"jpg\",\n\t}), ErrImgExt)\n\tassert.NoError(t, f.SaveAs(wb))\n\tassert.NoError(t, f.Close())\n\t// Test change already exist header image with the different image\n\tf, err := OpenFile(wb)\n\tassert.NoError(t, err)\n\tassert.NoError(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{\n\t\tFile:      images[\".jpg\"],\n\t\tFirstPage: true,\n\t\tExtension: \".jpg\",\n\t\tWidth:     \"50pt\",\n\t\tHeight:    \"32pt\",\n\t}))\n\tassert.NoError(t, f.Save())\n\tassert.NoError(t, f.Close())\n\n\t// Test add header image with unsupported charset VML drawing\n\tf, err = OpenFile(wb)\n\tassert.NoError(t, err)\n\tf.Pkg.Store(\"xl/drawings/vmlDrawing1.vml\", MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{\n\t\tFile:      images[\".jpg\"],\n\t\tExtension: \".jpg\",\n\t\tWidth:     \"50pt\",\n\t\tHeight:    \"32pt\",\n\t}), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n\t// Test set legacy drawing header/footer with unsupported charset content types\n\tf = NewFile()\n\tf.ContentTypes = nil\n\tf.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{\n\t\tExtension: \".png\",\n\t\tFile:      images[\".png\"],\n\t\tWidth:     \"50pt\",\n\t\tHeight:    \"32pt\",\n\t}), \"XML syntax error on line 1: invalid UTF-8\")\n\tassert.NoError(t, f.Close())\n}\n"
  },
  {
    "path": "workbook.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"io\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// SetWorkbookProps provides a function to sets workbook properties.\nfunc (f *File) SetWorkbookProps(opts *WorkbookPropsOptions) error {\n\tif opts == nil {\n\t\treturn nil\n\t}\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif wb.WorkbookPr == nil {\n\t\twb.WorkbookPr = new(xlsxWorkbookPr)\n\t}\n\tsetNoPtrFieldsVal([]string{\"Date1904\", \"FilterPrivacy\", \"CodeName\"},\n\t\treflect.ValueOf(*opts), reflect.ValueOf(wb.WorkbookPr).Elem())\n\treturn err\n}\n\n// GetWorkbookProps provides a function to gets workbook properties.\nfunc (f *File) GetWorkbookProps() (WorkbookPropsOptions, error) {\n\tvar opts WorkbookPropsOptions\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\tif wb.WorkbookPr == nil {\n\t\treturn opts, err\n\t}\n\tsetPtrFieldsVal([]string{\"Date1904\", \"FilterPrivacy\", \"CodeName\"},\n\t\treflect.ValueOf(*wb.WorkbookPr), reflect.ValueOf(&opts).Elem())\n\treturn opts, err\n}\n\n// SetCalcProps provides a function to sets calculation properties. Optional\n// value of \"CalcMode\" property is: \"manual\", \"auto\" or \"autoNoTable\". Optional\n// value of \"RefMode\" property is: \"A1\" or \"R1C1\".\nfunc (f *File) SetCalcProps(opts *CalcPropsOptions) error {\n\tif opts == nil {\n\t\treturn nil\n\t}\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif wb.CalcPr == nil {\n\t\twb.CalcPr = new(xlsxCalcPr)\n\t}\n\tif opts.CalcMode != nil && inStrSlice(supportedCalcMode, *opts.CalcMode, true) == -1 {\n\t\treturn newInvalidOptionalValue(\"CalcMode\", *opts.CalcMode, supportedCalcMode)\n\t}\n\tif opts.RefMode != nil && inStrSlice(supportedRefMode, *opts.RefMode, true) == -1 {\n\t\treturn newInvalidOptionalValue(\"RefMode\", *opts.RefMode, supportedRefMode)\n\t}\n\tsetNoPtrFieldsVal([]string{\n\t\t\"CalcCompleted\", \"CalcOnSave\", \"ForceFullCalc\", \"FullCalcOnLoad\", \"FullPrecision\", \"Iterate\",\n\t\t\"IterateDelta\",\n\t\t\"CalcMode\", \"RefMode\",\n\t}, reflect.ValueOf(*opts), reflect.ValueOf(wb.CalcPr).Elem())\n\tif opts.CalcID != nil {\n\t\twb.CalcPr.CalcID = int(*opts.CalcID)\n\t}\n\tif opts.ConcurrentManualCount != nil {\n\t\twb.CalcPr.ConcurrentManualCount = int(*opts.ConcurrentManualCount)\n\t}\n\tif opts.IterateCount != nil {\n\t\twb.CalcPr.IterateCount = int(*opts.IterateCount)\n\t}\n\twb.CalcPr.ConcurrentCalc = opts.ConcurrentCalc\n\treturn err\n}\n\n// GetCalcProps provides a function to gets calculation properties.\nfunc (f *File) GetCalcProps() (CalcPropsOptions, error) {\n\tvar opts CalcPropsOptions\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn opts, err\n\t}\n\tif wb.CalcPr == nil {\n\t\treturn opts, err\n\t}\n\tsetPtrFieldsVal([]string{\n\t\t\"CalcCompleted\", \"CalcOnSave\", \"ForceFullCalc\", \"FullCalcOnLoad\", \"FullPrecision\", \"Iterate\",\n\t\t\"IterateDelta\",\n\t\t\"CalcMode\", \"RefMode\",\n\t}, reflect.ValueOf(*wb.CalcPr), reflect.ValueOf(&opts).Elem())\n\topts.CalcID = uintPtr(uint(wb.CalcPr.CalcID))\n\topts.ConcurrentManualCount = uintPtr(uint(wb.CalcPr.ConcurrentManualCount))\n\topts.IterateCount = uintPtr(uint(wb.CalcPr.IterateCount))\n\topts.ConcurrentCalc = wb.CalcPr.ConcurrentCalc\n\treturn opts, err\n}\n\n// ProtectWorkbook provides a function to prevent other users from viewing\n// hidden worksheets, adding, moving, deleting, or hiding worksheets, and\n// renaming worksheets in a workbook. The optional field AlgorithmName\n// specified hash algorithm, support XOR, MD4, MD5, SHA-1, SHA2-56, SHA-384,\n// and SHA-512 currently, if no hash algorithm specified, will be using the XOR\n// algorithm as default. The generated workbook only works on Microsoft Office\n// 2007 and later. For example, protect workbook with protection settings:\n//\n//\terr := f.ProtectWorkbook(&excelize.WorkbookProtectionOptions{\n//\t    Password:      \"password\",\n//\t    LockStructure: true,\n//\t})\nfunc (f *File) ProtectWorkbook(opts *WorkbookProtectionOptions) error {\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif wb.WorkbookProtection == nil {\n\t\twb.WorkbookProtection = new(xlsxWorkbookProtection)\n\t}\n\tif opts == nil {\n\t\topts = &WorkbookProtectionOptions{}\n\t}\n\twb.WorkbookProtection = &xlsxWorkbookProtection{\n\t\tLockStructure: opts.LockStructure,\n\t\tLockWindows:   opts.LockWindows,\n\t}\n\tif opts.Password != \"\" {\n\t\tif opts.AlgorithmName == \"\" {\n\t\t\topts.AlgorithmName = \"SHA-512\"\n\t\t}\n\t\thashValue, saltValue, err := genISOPasswdHash(opts.Password, opts.AlgorithmName, \"\", int(workbookProtectionSpinCount))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\twb.WorkbookProtection.WorkbookAlgorithmName = opts.AlgorithmName\n\t\twb.WorkbookProtection.WorkbookSaltValue = saltValue\n\t\twb.WorkbookProtection.WorkbookHashValue = hashValue\n\t\twb.WorkbookProtection.WorkbookSpinCount = int(workbookProtectionSpinCount)\n\t}\n\treturn err\n}\n\n// UnprotectWorkbook provides a function to remove protection for workbook,\n// specified the optional password parameter to remove workbook protection with\n// password verification.\nfunc (f *File) UnprotectWorkbook(password ...string) error {\n\twb, err := f.workbookReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\t// password verification\n\tif len(password) > 0 {\n\t\tif wb.WorkbookProtection == nil {\n\t\t\treturn ErrUnprotectWorkbook\n\t\t}\n\t\tif wb.WorkbookProtection.WorkbookAlgorithmName != \"\" {\n\t\t\t// check with given salt value\n\t\t\thashValue, _, err := genISOPasswdHash(password[0], wb.WorkbookProtection.WorkbookAlgorithmName, wb.WorkbookProtection.WorkbookSaltValue, wb.WorkbookProtection.WorkbookSpinCount)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif wb.WorkbookProtection.WorkbookHashValue != hashValue {\n\t\t\t\treturn ErrUnprotectWorkbookPassword\n\t\t\t}\n\t\t}\n\t}\n\twb.WorkbookProtection = nil\n\treturn err\n}\n\n// setWorkbook update workbook property of the spreadsheet. Maximum 31\n// characters are allowed in sheet title.\nfunc (f *File) setWorkbook(name string, sheetID, rid int) {\n\twb, _ := f.workbookReader()\n\twb.Sheets.Sheet = append(wb.Sheets.Sheet, xlsxSheet{\n\t\tName:    name,\n\t\tSheetID: sheetID,\n\t\tID:      \"rId\" + strconv.Itoa(rid),\n\t})\n}\n\n// getWorkbookPath provides a function to get the path of the workbook.xml in\n// the spreadsheet.\nfunc (f *File) getWorkbookPath() (path string) {\n\tif rels, _ := f.relsReader(defaultXMLPathRels); rels != nil {\n\t\trels.mu.Lock()\n\t\tdefer rels.mu.Unlock()\n\t\tfor _, rel := range rels.Relationships {\n\t\t\tif rel.Type == SourceRelationshipOfficeDocument {\n\t\t\t\tpath = strings.TrimPrefix(rel.Target, \"/\")\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// getWorkbookRelsPath provides a function to get the path of the workbook.xml.rels\n// in the spreadsheet.\nfunc (f *File) getWorkbookRelsPath() (path string) {\n\twbPath := f.getWorkbookPath()\n\twbDir := filepath.Dir(wbPath)\n\tif wbDir == \".\" {\n\t\tpath = \"_rels/\" + filepath.Base(wbPath) + \".rels\"\n\t\treturn\n\t}\n\tpath = strings.TrimPrefix(filepath.Dir(wbPath)+\"/_rels/\"+filepath.Base(wbPath)+\".rels\", \"/\")\n\treturn\n}\n\n// deleteWorkbookRels provides a function to delete relationships in\n// xl/_rels/workbook.xml.rels by given type and target.\nfunc (f *File) deleteWorkbookRels(relType, relTarget string) (string, error) {\n\tvar rID string\n\trels, err := f.relsReader(f.getWorkbookRelsPath())\n\tif err != nil {\n\t\treturn rID, err\n\t}\n\tif rels == nil {\n\t\trels = &xlsxRelationships{}\n\t}\n\tfor k, v := range rels.Relationships {\n\t\tif v.Type == relType && v.Target == relTarget {\n\t\t\trID = v.ID\n\t\t\trels.Relationships = append(rels.Relationships[:k], rels.Relationships[k+1:]...)\n\t\t}\n\t}\n\treturn rID, err\n}\n\n// workbookReader provides a function to get the pointer to the workbook.xml\n// structure after deserialization.\nfunc (f *File) workbookReader() (*xlsxWorkbook, error) {\n\tvar err error\n\tif f.WorkBook == nil {\n\t\twbPath := f.getWorkbookPath()\n\t\tf.WorkBook = new(xlsxWorkbook)\n\t\tif attrs, ok := f.xmlAttr.Load(wbPath); !ok {\n\t\t\td := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath))))\n\t\t\tif attrs == nil {\n\t\t\t\tattrs = []xml.Attr{}\n\t\t\t}\n\t\t\tattrs = append(attrs.([]xml.Attr), getRootElement(d)...)\n\t\t\tf.xmlAttr.Store(wbPath, attrs)\n\t\t\tf.addNameSpaces(wbPath, SourceRelationship)\n\t\t}\n\t\tif err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath)))).\n\t\t\tDecode(f.WorkBook); err != nil && err != io.EOF {\n\t\t\treturn f.WorkBook, err\n\t\t}\n\t}\n\treturn f.WorkBook, err\n}\n\n// workBookWriter provides a function to save workbook.xml after serialize\n// structure.\nfunc (f *File) workBookWriter() {\n\tif f.WorkBook != nil {\n\t\tif f.WorkBook.DecodeAlternateContent != nil {\n\t\t\tf.WorkBook.AlternateContent = &xlsxAlternateContent{\n\t\t\t\tContent: f.WorkBook.DecodeAlternateContent.Content,\n\t\t\t\tXMLNSMC: SourceRelationshipCompatibility.Value,\n\t\t\t}\n\t\t}\n\t\tf.WorkBook.DecodeAlternateContent = nil\n\t\toutput, _ := xml.Marshal(f.WorkBook)\n\t\tf.saveFileList(f.getWorkbookPath(), replaceRelationshipsBytes(f.replaceNameSpaceBytes(f.getWorkbookPath(), output)))\n\t}\n}\n\n// setContentTypePartRelsExtensions provides a function to set the content type\n// for relationship parts and the Main Document part.\nfunc (f *File) setContentTypePartRelsExtensions() error {\n\tvar rels bool\n\tcontent, err := f.contentTypesReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, v := range content.Defaults {\n\t\tif v.Extension == \"rels\" {\n\t\t\trels = true\n\t\t}\n\t}\n\tif !rels {\n\t\tcontent.Defaults = append(content.Defaults, xlsxDefault{\n\t\t\tExtension:   \"rels\",\n\t\t\tContentType: ContentTypeRelationships,\n\t\t})\n\t}\n\treturn err\n}\n\n// setContentTypePartImageExtensions provides a function to set the content type\n// for relationship parts and the Main Document part.\nfunc (f *File) setContentTypePartImageExtensions() error {\n\timageTypes := map[string]string{\n\t\t\"bmp\": \"image/\", \"ico\": \"image/x-\", \"jpeg\": \"image/\", \"png\": \"image/\", \"gif\": \"image/\",\n\t\t\"svg\": \"image/\", \"tiff\": \"image/\", \"emf\": \"image/x-\", \"wmf\": \"image/x-\",\n\t\t\"emz\": \"image/x-\", \"wmz\": \"image/x-\",\n\t}\n\tcontent, err := f.contentTypesReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tcontent.mu.Lock()\n\tdefer content.mu.Unlock()\n\tfor _, file := range content.Defaults {\n\t\tdelete(imageTypes, file.Extension)\n\t}\n\tfor extension, prefix := range imageTypes {\n\t\tcontent.Defaults = append(content.Defaults, xlsxDefault{\n\t\t\tExtension:   extension,\n\t\t\tContentType: prefix + extension,\n\t\t})\n\t}\n\treturn err\n}\n\n// setContentTypePartVMLExtensions provides a function to set the content type\n// for relationship parts and the Main Document part.\nfunc (f *File) setContentTypePartVMLExtensions() error {\n\tvar vml bool\n\tcontent, err := f.contentTypesReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tcontent.mu.Lock()\n\tdefer content.mu.Unlock()\n\tfor _, v := range content.Defaults {\n\t\tif v.Extension == \"vml\" {\n\t\t\tvml = true\n\t\t}\n\t}\n\tif !vml {\n\t\tcontent.Defaults = append(content.Defaults, xlsxDefault{\n\t\t\tExtension:   \"vml\",\n\t\t\tContentType: ContentTypeVML,\n\t\t})\n\t}\n\treturn err\n}\n\n// addContentTypePart provides a function to add content type part relationships\n// in the file [Content_Types].xml by given index and content type.\nfunc (f *File) addContentTypePart(index int, contentType string) error {\n\tsetContentType := map[string]func() error{\n\t\t\"comments\": f.setContentTypePartVMLExtensions,\n\t\t\"drawings\": f.setContentTypePartImageExtensions,\n\t}\n\tpartNames := map[string]string{\n\t\t\"chart\":            \"/xl/charts/chart\" + strconv.Itoa(index) + \".xml\",\n\t\t\"chartsheet\":       \"/xl/chartsheets/sheet\" + strconv.Itoa(index) + \".xml\",\n\t\t\"comments\":         \"/xl/comments\" + strconv.Itoa(index) + \".xml\",\n\t\t\"customProperties\": \"/docProps/custom.xml\",\n\t\t\"drawings\":         \"/xl/drawings/drawing\" + strconv.Itoa(index) + \".xml\",\n\t\t\"table\":            \"/xl/tables/table\" + strconv.Itoa(index) + \".xml\",\n\t\t\"pivotTable\":       \"/xl/pivotTables/pivotTable\" + strconv.Itoa(index) + \".xml\",\n\t\t\"pivotCache\":       \"/xl/pivotCache/pivotCacheDefinition\" + strconv.Itoa(index) + \".xml\",\n\t\t\"sharedStrings\":    \"/xl/sharedStrings.xml\",\n\t\t\"slicer\":           \"/xl/slicers/slicer\" + strconv.Itoa(index) + \".xml\",\n\t\t\"slicerCache\":      \"/xl/slicerCaches/slicerCache\" + strconv.Itoa(index) + \".xml\",\n\t}\n\tcontentTypes := map[string]string{\n\t\t\"chart\":            ContentTypeDrawingML,\n\t\t\"chartsheet\":       ContentTypeSpreadSheetMLChartsheet,\n\t\t\"comments\":         ContentTypeSpreadSheetMLComments,\n\t\t\"customProperties\": ContentTypeCustomProperties,\n\t\t\"drawings\":         ContentTypeDrawing,\n\t\t\"table\":            ContentTypeSpreadSheetMLTable,\n\t\t\"pivotTable\":       ContentTypeSpreadSheetMLPivotTable,\n\t\t\"pivotCache\":       ContentTypeSpreadSheetMLPivotCacheDefinition,\n\t\t\"sharedStrings\":    ContentTypeSpreadSheetMLSharedStrings,\n\t\t\"slicer\":           ContentTypeSlicer,\n\t\t\"slicerCache\":      ContentTypeSlicerCache,\n\t}\n\ts, ok := setContentType[contentType]\n\tif ok {\n\t\tif err := s(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tcontent, err := f.contentTypesReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tcontent.mu.Lock()\n\tdefer content.mu.Unlock()\n\tfor _, v := range content.Overrides {\n\t\tif v.PartName == partNames[contentType] {\n\t\t\treturn err\n\t\t}\n\t}\n\tcontent.Overrides = append(content.Overrides, xlsxOverride{\n\t\tPartName:    partNames[contentType],\n\t\tContentType: contentTypes[contentType],\n\t})\n\treturn f.setContentTypePartRelsExtensions()\n}\n\n// removeContentTypesPart provides a function to remove relationships by given\n// content type and part name in the file [Content_Types].xml.\nfunc (f *File) removeContentTypesPart(contentType, partName string) error {\n\tif !strings.HasPrefix(partName, \"/\") {\n\t\tpartName = \"/xl/\" + partName\n\t}\n\tcontent, err := f.contentTypesReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tcontent.mu.Lock()\n\tdefer content.mu.Unlock()\n\tfor k, v := range content.Overrides {\n\t\tif v.PartName == partName && v.ContentType == contentType {\n\t\t\tcontent.Overrides = append(content.Overrides[:k], content.Overrides[k+1:]...)\n\t\t}\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "workbook_test.go",
    "content": "package excelize\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestWorkbookProps(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetWorkbookProps(nil))\n\twb, err := f.workbookReader()\n\tassert.NoError(t, err)\n\twb.WorkbookPr = nil\n\texpected := WorkbookPropsOptions{\n\t\tDate1904:      boolPtr(true),\n\t\tFilterPrivacy: boolPtr(true),\n\t\tCodeName:      stringPtr(\"code\"),\n\t}\n\tassert.NoError(t, f.SetWorkbookProps(&expected))\n\topts, err := f.GetWorkbookProps()\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected, opts)\n\twb.WorkbookPr = nil\n\topts, err = f.GetWorkbookProps()\n\tassert.NoError(t, err)\n\tassert.Equal(t, WorkbookPropsOptions{}, opts)\n\t// Test set workbook properties with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetWorkbookProps(&expected), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get workbook properties with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\t_, err = f.GetWorkbookProps()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestCalcProps(t *testing.T) {\n\tf := NewFile()\n\tassert.NoError(t, f.SetCalcProps(nil))\n\twb, err := f.workbookReader()\n\tassert.NoError(t, err)\n\twb.CalcPr = nil\n\texpected := CalcPropsOptions{\n\t\tFullCalcOnLoad:        boolPtr(true),\n\t\tCalcID:                uintPtr(122211),\n\t\tConcurrentManualCount: uintPtr(5),\n\t\tIterateCount:          uintPtr(10),\n\t\tConcurrentCalc:        boolPtr(true),\n\t}\n\tassert.NoError(t, f.SetCalcProps(&expected))\n\topts, err := f.GetCalcProps()\n\tassert.NoError(t, err)\n\tassert.Equal(t, expected, opts)\n\twb.CalcPr = nil\n\topts, err = f.GetCalcProps()\n\tassert.NoError(t, err)\n\tassert.Equal(t, CalcPropsOptions{}, opts)\n\t// Test set calculation properties with unsupported optional value\n\tassert.Equal(t, newInvalidOptionalValue(\"CalcMode\", \"AUTO\", supportedCalcMode), f.SetCalcProps(&CalcPropsOptions{CalcMode: stringPtr(\"AUTO\")}))\n\tassert.Equal(t, newInvalidOptionalValue(\"RefMode\", \"a1\", supportedRefMode), f.SetCalcProps(&CalcPropsOptions{RefMode: stringPtr(\"a1\")}))\n\t// Test set calculation properties with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\tassert.EqualError(t, f.SetCalcProps(&expected), \"XML syntax error on line 1: invalid UTF-8\")\n\t// Test get calculation properties with unsupported charset workbook\n\tf.WorkBook = nil\n\tf.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)\n\t_, err = f.GetCalcProps()\n\tassert.EqualError(t, err, \"XML syntax error on line 1: invalid UTF-8\")\n}\n\nfunc TestDeleteWorkbookRels(t *testing.T) {\n\tf := NewFile()\n\t// Test delete pivot table without worksheet relationships\n\tf.Relationships.Delete(\"xl/_rels/workbook.xml.rels\")\n\tf.Pkg.Delete(\"xl/_rels/workbook.xml.rels\")\n\trID, err := f.deleteWorkbookRels(\"\", \"\")\n\tassert.Empty(t, rID)\n\tassert.NoError(t, err)\n}\n"
  },
  {
    "path": "xmlApp.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// AppProperties directly maps the document application properties.\ntype AppProperties struct {\n\tApplication       string\n\tScaleCrop         bool\n\tDocSecurity       int\n\tCompany           string\n\tLinksUpToDate     bool\n\tHyperlinksChanged bool\n\tAppVersion        string\n}\n\n// xlsxProperties specifies to an OOXML document properties such as the\n// template used, the number of pages and words, and the application name and\n// version.\ntype xlsxProperties struct {\n\tXMLName              xml.Name `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties Properties\"`\n\tVt                   string   `xml:\"xmlns:vt,attr\"`\n\tTemplate             string   `xml:\",omitempty\"`\n\tManager              string   `xml:\",omitempty\"`\n\tCompany              string   `xml:\",omitempty\"`\n\tPages                int      `xml:\",omitempty\"`\n\tWords                int      `xml:\",omitempty\"`\n\tCharacters           int      `xml:\",omitempty\"`\n\tPresentationFormat   string   `xml:\",omitempty\"`\n\tLines                int      `xml:\",omitempty\"`\n\tParagraphs           int      `xml:\",omitempty\"`\n\tSlides               int      `xml:\",omitempty\"`\n\tNotes                int      `xml:\",omitempty\"`\n\tTotalTime            int      `xml:\",omitempty\"`\n\tHiddenSlides         int      `xml:\",omitempty\"`\n\tMMClips              int      `xml:\",omitempty\"`\n\tScaleCrop            bool     `xml:\",omitempty\"`\n\tHeadingPairs         *xlsxVectorVariant\n\tTitlesOfParts        *xlsxVectorLpstr\n\tLinksUpToDate        bool   `xml:\",omitempty\"`\n\tCharactersWithSpaces int    `xml:\",omitempty\"`\n\tSharedDoc            bool   `xml:\",omitempty\"`\n\tHyperlinkBase        string `xml:\",omitempty\"`\n\tHLinks               *xlsxVectorVariant\n\tHyperlinksChanged    bool `xml:\",omitempty\"`\n\tDigSig               *xlsxDigSig\n\tApplication          string `xml:\",omitempty\"`\n\tAppVersion           string `xml:\",omitempty\"`\n\tDocSecurity          int    `xml:\",omitempty\"`\n}\n\n// xlsxVectorVariant specifies the set of hyperlinks that were in this\n// document when last saved.\ntype xlsxVectorVariant struct {\n\tContent string `xml:\",innerxml\"`\n}\n\ntype xlsxVectorLpstr struct {\n\tContent string `xml:\",innerxml\"`\n}\n\n// xlsxDigSig contains the signature of a digitally signed document.\ntype xlsxDigSig struct {\n\tContent string `xml:\",innerxml\"`\n}\n"
  },
  {
    "path": "xmlCalcChain.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// xlsxCalcChain directly maps the calcChain element. This element represents\n// the root of the calculation chain.\ntype xlsxCalcChain struct {\n\tXMLName xml.Name         `xml:\"http://schemas.openxmlformats.org/spreadsheetml/2006/main calcChain\"`\n\tC       []xlsxCalcChainC `xml:\"c\"`\n}\n\n// xlsxCalcChainC directly maps the c element.\n//\n//\t Attributes               | Attributes\n//\t--------------------------+----------------------------------------------------------\n//\t a (Array)                | A Boolean flag indicating whether the cell's formula\n//\t                          | is an array formula. True if this cell's formula is\n//\t                          | an array formula, false otherwise. If there is a\n//\t                          | conflict between this attribute and the t attribute\n//\t                          | of the f element (§18.3.1.40), the t attribute takes\n//\t                          | precedence. The possible values for this attribute\n//\t                          | are defined by the W3C XML Schema boolean datatype.\n//\t                          |\n//\t i (Sheet Id)             | A sheet Id of a sheet the cell belongs to. If this is\n//\t                          | omitted, it is assumed to be the same as the i value\n//\t                          | of the previous cell.The possible values for this\n//\t                          | attribute are defined by the W3C XML Schema int datatype.\n//\t                          |\n//\t l (New Dependency Level) | A Boolean flag indicating that the cell's formula\n//\t                          | starts a new dependency level. True if the formula\n//\t                          | starts a new dependency level, false otherwise.\n//\t                          | Starting a new dependency level means that all\n//\t                          | concurrent calculations, and child calculations, shall\n//\t                          | be completed - and the cells have new values - before\n//\t                          | the calc chain can continue. In other words, this\n//\t                          | dependency level might depend on levels that came before\n//\t                          | it, and any later dependency levels might depend on\n//\t                          | this level; but not later dependency levels can have\n//\t                          | any calculations started until this dependency level\n//\t                          | completes.The possible values for this attribute are\n//\t                          | defined by the W3C XML Schema boolean datatype.\n//\t                          |\n//\t r (Cell Reference)       | An A-1 style reference to a cell.The possible values\n//\t                          | for this attribute are defined by the ST_CellRef\n//\t                          | simple type (§18.18.7).\n//\t                          |\n//\t s (Child Chain)          | A Boolean flag indicating whether the cell's formula\n//\t                          | is on a child chain. True if this cell is part of a\n//\t                          | child chain, false otherwise. If this is omitted, it\n//\t                          | is assumed to be the same as the s value of the\n//\t                          | previous cell .A child chain is a list of calculations\n//\t                          | that occur which depend on the parent to the chain.\n//\t                          | There shall not be cross dependencies between child\n//\t                          | chains. Child chains are not the same as dependency\n//\t                          | levels - a child chain and its parent are all on the\n//\t                          | same dependency level. Child chains are series of\n//\t                          | calculations that can be independently farmed out to\n//\t                          | other threads or processors.The possible values for\n//\t                          | this attribute is defined by the W3C XML Schema\n//\t                          | boolean datatype.\n//\t                          |\n//\t t (New Thread)           | A Boolean flag indicating whether the cell's formula\n//\t                          | starts a new thread. True if the cell's formula starts\n//\t                          | a new thread, false otherwise.The possible values for\n//\t                          | this attribute is defined by the W3C XML Schema\n//\t                          | boolean datatype.\ntype xlsxCalcChainC struct {\n\tR string `xml:\"r,attr\"`\n\tI int    `xml:\"i,attr,omitempty\"`\n\tL bool   `xml:\"l,attr,omitempty\"`\n\tS bool   `xml:\"s,attr,omitempty\"`\n\tT bool   `xml:\"t,attr,omitempty\"`\n\tA bool   `xml:\"a,attr,omitempty\"`\n}\n\n// xlsxVolTypes maps the volatileDependencies part provides a cache of data that\n// supports Real Time Data (RTD) and CUBE functions in the workbook.\ntype xlsxVolTypes struct {\n\tXMLName xml.Name      `xml:\"http://schemas.openxmlformats.org/spreadsheetml/2006/main volTypes\"`\n\tVolType []xlsxVolType `xml:\"volType\"`\n\tExtLst  *xlsxExtLst   `xml:\"extLst\"`\n}\n\n// xlsxVolType represents dependency information for a specific type of external\n// data server.\ntype xlsxVolType struct {\n\tType string        `xml:\"type,attr\"`\n\tMain []xlsxVolMain `xml:\"main\"`\n}\n\n// xlsxVolMain represents dependency information for all topics within a\n// volatile dependency type that share the same first string or function\n// argument.\ntype xlsxVolMain struct {\n\tFirst string         `xml:\"first,attr\"`\n\tTp    []xlsxVolTopic `xml:\"tp\"`\n}\n\n// xlsxVolTopic represents dependency information for all topics within a\n// volatile dependency type that share the same first string or argument.\ntype xlsxVolTopic struct {\n\tT   string            `xml:\"t,attr,omitempty\"`\n\tV   string            `xml:\"v\"`\n\tStp []string          `xml:\"stp\"`\n\tTr  []xlsxVolTopicRef `xml:\"tr\"`\n}\n\n// xlsxVolTopicRef represents the reference to a cell that depends on this\n// topic. Each topic can have one or more cells dependencies.\ntype xlsxVolTopicRef struct {\n\tR string `xml:\"r,attr\"`\n\tS int    `xml:\"s,attr\"`\n}\n"
  },
  {
    "path": "xmlChart.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// xlsxChartSpace directly maps the chartSpace element. The chart namespace in\n// DrawingML is for representing visualizations of numeric data with column\n// charts, pie charts, scatter charts, or other types of charts.\ntype xlsxChartSpace struct {\n\tXMLName        xml.Name        `xml:\"http://schemas.openxmlformats.org/drawingml/2006/chart chartSpace\"`\n\tXMLNSa         string          `xml:\"xmlns:a,attr\"`\n\tDate1904       *attrValBool    `xml:\"date1904\"`\n\tLang           *attrValString  `xml:\"lang\"`\n\tRoundedCorners *attrValBool    `xml:\"roundedCorners\"`\n\tChart          cChart          `xml:\"chart\"`\n\tSpPr           *cSpPr          `xml:\"spPr\"`\n\tTxPr           *cTxPr          `xml:\"txPr\"`\n\tPrintSettings  *cPrintSettings `xml:\"printSettings\"`\n}\n\n// cThicknessSpPr directly maps the element that specifies the thickness of\n// the walls or floor as a percentage of the largest dimension of the plot\n// volume and SpPr element.\ntype cThicknessSpPr struct {\n\tThickness *attrValInt `xml:\"thickness\"`\n\tSpPr      *cSpPr      `xml:\"spPr\"`\n}\n\n// cChart (Chart) directly maps the chart element. This element specifies a\n// title.\ntype cChart struct {\n\tTitle            *cTitle            `xml:\"title\"`\n\tAutoTitleDeleted *cAutoTitleDeleted `xml:\"autoTitleDeleted\"`\n\tView3D           *cView3D           `xml:\"view3D\"`\n\tFloor            *cThicknessSpPr    `xml:\"floor\"`\n\tSideWall         *cThicknessSpPr    `xml:\"sideWall\"`\n\tBackWall         *cThicknessSpPr    `xml:\"backWall\"`\n\tPlotArea         *cPlotArea         `xml:\"plotArea\"`\n\tLegend           *cLegend           `xml:\"legend\"`\n\tPlotVisOnly      *attrValBool       `xml:\"plotVisOnly\"`\n\tDispBlanksAs     *attrValString     `xml:\"dispBlanksAs\"`\n\tShowDLblsOverMax *attrValBool       `xml:\"showDLblsOverMax\"`\n}\n\n// cTitle (Title) directly maps the title element. This element specifies a\n// title.\ntype cTitle struct {\n\tTx      cTx          `xml:\"tx,omitempty\"`\n\tLayout  string       `xml:\"layout,omitempty\"`\n\tOverlay *attrValBool `xml:\"overlay\"`\n\tSpPr    cSpPr        `xml:\"spPr,omitempty\"`\n\tTxPr    cTxPr        `xml:\"txPr,omitempty\"`\n}\n\n// cTx (Chart Text) directly maps the tx element. This element specifies text\n// to use on a chart, including rich text formatting.\ntype cTx struct {\n\tStrRef *cStrRef `xml:\"strRef\"`\n\tRich   *cRich   `xml:\"rich,omitempty\"`\n}\n\n// cRich (Rich Text) directly maps the rich element. This element contains a\n// string with rich text formatting.\ntype cRich struct {\n\tBodyPr   aBodyPr `xml:\"a:bodyPr,omitempty\"`\n\tLstStyle string  `xml:\"a:lstStyle,omitempty\"`\n\tP        []aP    `xml:\"a:p\"`\n}\n\n// aBodyPr (Body Properties) directly maps the a:bodyPr element. This element\n// defines the body properties for the text body within a shape.\ntype aBodyPr struct {\n\tAnchor           string  `xml:\"anchor,attr,omitempty\"`\n\tAnchorCtr        bool    `xml:\"anchorCtr,attr\"`\n\tRot              int     `xml:\"rot,attr\"`\n\tBIns             float64 `xml:\"bIns,attr,omitempty\"`\n\tCompatLnSpc      bool    `xml:\"compatLnSpc,attr,omitempty\"`\n\tForceAA          bool    `xml:\"forceAA,attr,omitempty\"`\n\tFromWordArt      bool    `xml:\"fromWordArt,attr,omitempty\"`\n\tHorzOverflow     string  `xml:\"horzOverflow,attr,omitempty\"`\n\tLIns             float64 `xml:\"lIns,attr,omitempty\"`\n\tNumCol           int     `xml:\"numCol,attr,omitempty\"`\n\tRIns             float64 `xml:\"rIns,attr,omitempty\"`\n\tRtlCol           bool    `xml:\"rtlCol,attr,omitempty\"`\n\tSpcCol           int     `xml:\"spcCol,attr,omitempty\"`\n\tSpcFirstLastPara bool    `xml:\"spcFirstLastPara,attr\"`\n\tTIns             float64 `xml:\"tIns,attr,omitempty\"`\n\tUpright          bool    `xml:\"upright,attr,omitempty\"`\n\tVert             string  `xml:\"vert,attr,omitempty\"`\n\tVertOverflow     string  `xml:\"vertOverflow,attr,omitempty\"`\n\tWrap             string  `xml:\"wrap,attr,omitempty\"`\n}\n\n// aP (Paragraph) directly maps the a:p element. This element specifies a\n// paragraph of content in the document.\ntype aP struct {\n\tPPr        *aPPr        `xml:\"a:pPr\"`\n\tR          *aR          `xml:\"a:r\"`\n\tEndParaRPr *aEndParaRPr `xml:\"a:endParaRPr\"`\n}\n\n// aPPr (Paragraph Properties) directly maps the a:pPr element. This element\n// specifies a set of paragraph properties which shall be applied to the\n// contents of the parent paragraph after all style/numbering/table properties\n// have been applied to the text. These properties are defined as direct\n// formatting, since they are directly applied to the paragraph and supersede\n// any formatting from styles.\ntype aPPr struct {\n\tDefRPr aRPr `xml:\"a:defRPr\"`\n}\n\n// aSrgbClr (RGB Color Model - Hex Variant) specifies a color using the red,\n// green, blue RGB color model. Red, green, and blue is expressed as sequence of\n// hex digits, RRGGBB. A perceptual gamma of 2.2 is used.\ntype aSrgbClr struct {\n\tVal      *string     `xml:\"val,attr\"`\n\tTint     *attrValInt `xml:\"a:tint\"`\n\tShade    *attrValInt `xml:\"a:shade\"`\n\tComp     *attrValInt `xml:\"a:comp\"`\n\tInv      *attrValInt `xml:\"a:inv\"`\n\tGray     *attrValInt `xml:\"a:gray\"`\n\tAlpha    *attrValInt `xml:\"a:alpha\"`\n\tAlphaOff *attrValInt `xml:\"a:alphaOff\"`\n\tAlphaMod *attrValInt `xml:\"a:alphaMod\"`\n\tHue      *attrValInt `xml:\"a:hue\"`\n\tHueOff   *attrValInt `xml:\"a:hueOff\"`\n\tHueMod   *attrValInt `xml:\"a:hueMod\"`\n\tSat      *attrValInt `xml:\"a:sat\"`\n\tSatOff   *attrValInt `xml:\"a:satOff\"`\n\tSatMod   *attrValInt `xml:\"a:satMod\"`\n\tLum      *attrValInt `xml:\"a:lum\"`\n\tLumOff   *attrValInt `xml:\"a:lumOff\"`\n\tLumMod   *attrValInt `xml:\"a:lumMod\"`\n\tRed      *attrValInt `xml:\"a:red\"`\n\tRedOff   *attrValInt `xml:\"a:redOff\"`\n\tRedMod   *attrValInt `xml:\"a:redMod\"`\n\tGreen    *attrValInt `xml:\"a:green\"`\n\tGreenOff *attrValInt `xml:\"a:greenOff\"`\n\tGreenMod *attrValInt `xml:\"a:greenMod\"`\n\tBlue     *attrValInt `xml:\"a:blue\"`\n\tBlueOff  *attrValInt `xml:\"a:blueOff\"`\n\tBlueMod  *attrValInt `xml:\"a:blueMod\"`\n\tGamma    *attrValInt `xml:\"a:gamma\"`\n\tInvGamma *attrValInt `xml:\"a:invGamma\"`\n}\n\n// aSolidFill (Solid Fill) directly maps the solidFill element. This element\n// specifies a solid color fill. The shape is filled entirely with the specified\n// color.\ntype aSolidFill struct {\n\tSchemeClr *aSchemeClr `xml:\"a:schemeClr\"`\n\tSrgbClr   *aSrgbClr   `xml:\"a:srgbClr\"`\n}\n\n// aSchemeClr (Scheme Color) directly maps the a:schemeClr element. This\n// element specifies a color bound to a user's theme. As with all elements which\n// define a color, it is possible to apply a list of color transforms to the\n// base color defined.\ntype aSchemeClr struct {\n\tVal    string      `xml:\"val,attr,omitempty\"`\n\tLumMod *attrValInt `xml:\"a:lumMod\"`\n\tLumOff *attrValInt `xml:\"a:lumOff\"`\n}\n\n// attrValInt directly maps the val element with integer data type as an\n// attribute.\ntype attrValInt struct {\n\tVal *int `xml:\"val,attr\"`\n}\n\n// attrValFloat directly maps the val element with float64 data type as an\n// attribute.\ntype attrValFloat struct {\n\tVal *float64 `xml:\"val,attr\"`\n}\n\n// attrValBool directly maps the val element with boolean data type as an\n// attribute.\ntype attrValBool struct {\n\tVal *bool `xml:\"val,attr\"`\n}\n\n// attrValString directly maps the val element with string data type as an\n// attribute.\ntype attrValString struct {\n\tVal *string `xml:\"val,attr\"`\n}\n\ntype xlsxCTTextFont struct {\n\tTypeface    string `xml:\"typeface,attr\"`\n\tPanose      string `xml:\"panose,attr,omitempty\"`\n\tPitchFamily string `xml:\"pitchFamily,attr,omitempty\"`\n\tCharset     string `xml:\"Charset,attr,omitempty\"`\n}\n\n// aR directly maps the a:r element.\ntype aR struct {\n\tRPr aRPr   `xml:\"a:rPr,omitempty\"`\n\tT   string `xml:\"a:t,omitempty\"`\n}\n\n// aRPr (Run Properties) directly maps the rPr element. This element\n// specifies a set of run properties which shall be applied to the contents of\n// the parent run after all style formatting has been applied to the text. These\n// properties are defined as direct formatting, since they are directly applied\n// to the run and supersede any formatting from styles.\ntype aRPr struct {\n\tAltLang    string          `xml:\"altLang,attr,omitempty\"`\n\tB          bool            `xml:\"b,attr\"`\n\tBaseline   int             `xml:\"baseline,attr\"`\n\tBmk        string          `xml:\"bmk,attr,omitempty\"`\n\tCap        string          `xml:\"cap,attr,omitempty\"`\n\tDirty      bool            `xml:\"dirty,attr,omitempty\"`\n\tErr        bool            `xml:\"err,attr,omitempty\"`\n\tI          bool            `xml:\"i,attr\"`\n\tKern       int             `xml:\"kern,attr\"`\n\tKumimoji   bool            `xml:\"kumimoji,attr,omitempty\"`\n\tLang       string          `xml:\"lang,attr,omitempty\"`\n\tNoProof    bool            `xml:\"noProof,attr,omitempty\"`\n\tNormalizeH bool            `xml:\"normalizeH,attr,omitempty\"`\n\tSmtClean   bool            `xml:\"smtClean,attr,omitempty\"`\n\tSmtID      uint64          `xml:\"smtId,attr,omitempty\"`\n\tSpc        int             `xml:\"spc,attr\"`\n\tStrike     string          `xml:\"strike,attr,omitempty\"`\n\tSz         float64         `xml:\"sz,attr,omitempty\"`\n\tU          string          `xml:\"u,attr,omitempty\"`\n\tSolidFill  *aSolidFill     `xml:\"a:solidFill\"`\n\tLatin      *xlsxCTTextFont `xml:\"a:latin\"`\n\tEa         *xlsxCTTextFont `xml:\"a:ea\"`\n\tCs         *xlsxCTTextFont `xml:\"a:cs\"`\n}\n\n// cDTable (Data Table) directly maps the dTable element.\ntype cDTable struct {\n\tShowHorzBorder *attrValBool `xml:\"showHorzBorder\"`\n\tShowVertBorder *attrValBool `xml:\"showVertBorder\"`\n\tShowOutline    *attrValBool `xml:\"showOutline\"`\n\tShowKeys       *attrValBool `xml:\"showKeys\"`\n\tSpPr           *cSpPr       `xml:\"spPr\"`\n\tTxPr           *cTxPr       `xml:\"txPr\"`\n\tExtLst         *xlsxExtLst  `xml:\"extLst\"`\n}\n\n// cSpPr (Shape Properties) directly maps the spPr element. This element\n// specifies the visual shape properties that can be applied to a shape. These\n// properties include the shape fill, outline, geometry, effects, and 3D\n// orientation.\ntype cSpPr struct {\n\tNoFill    *string     `xml:\"a:noFill\"`\n\tSolidFill *aSolidFill `xml:\"a:solidFill\"`\n\tLn        *aLn        `xml:\"a:ln\"`\n\tSp3D      *aSp3D      `xml:\"a:sp3d\"`\n\tEffectLst *string     `xml:\"a:effectLst\"`\n}\n\n// aSp3D (3-D Shape Properties) directly maps the a:sp3d element. This element\n// defines the 3D properties associated with a particular shape in DrawingML.\n// The 3D properties which can be applied to a shape are top and bottom bevels,\n// a contour and an extrusion.\ntype aSp3D struct {\n\tContourW   int          `xml:\"contourW,attr\"`\n\tContourClr *aContourClr `xml:\"a:contourClr\"`\n}\n\n// aContourClr (Contour Color) directly maps the a:contourClr element. This\n// element defines the color for the contour on a shape. The contour of a shape\n// is a solid filled line which surrounds the outer edges of the shape.\ntype aContourClr struct {\n\tSchemeClr *aSchemeClr `xml:\"a:schemeClr\"`\n}\n\n// aLn (Outline) directly maps the a:ln element. This element specifies an\n// outline style that can be applied to a number of different objects such as\n// shapes and text. The line allows for the specifying of many different types\n// of outlines including even line dashes and bevels.\ntype aLn struct {\n\tAlgn      string         `xml:\"algn,attr,omitempty\"`\n\tCap       string         `xml:\"cap,attr,omitempty\"`\n\tCmpd      string         `xml:\"cmpd,attr,omitempty\"`\n\tW         int            `xml:\"w,attr,omitempty\"`\n\tNoFill    *attrValString `xml:\"a:noFill\"`\n\tRound     string         `xml:\"a:round,omitempty\"`\n\tSolidFill *aSolidFill    `xml:\"a:solidFill\"`\n\tPrstDash  *attrValString `xml:\"a:prstDash\"`\n}\n\n// cTxPr (Text Properties) directly maps the txPr element. This element\n// specifies text formatting. The lstStyle element is not supported.\ntype cTxPr struct {\n\tBodyPr   aBodyPr `xml:\"a:bodyPr,omitempty\"`\n\tLstStyle string  `xml:\"a:lstStyle,omitempty\"`\n\tP        aP      `xml:\"a:p,omitempty\"`\n}\n\n// aEndParaRPr (End Paragraph Run Properties) directly maps the a:endParaRPr\n// element. This element specifies the text run properties that are to be used\n// if another run is inserted after the last run specified. This effectively\n// saves the run property state so that it can be applied when the user enters\n// additional text. If this element is omitted, then the application can\n// determine which default properties to apply. It is recommended that this\n// element be specified at the end of the list of text runs within the paragraph\n// so that an orderly list is maintained.\ntype aEndParaRPr struct {\n\tLang    string `xml:\"lang,attr\"`\n\tAltLang string `xml:\"altLang,attr,omitempty\"`\n\tSz      int    `xml:\"sz,attr,omitempty\"`\n}\n\n// cAutoTitleDeleted (Auto Title Is Deleted) directly maps the\n// autoTitleDeleted element. This element specifies the title shall not be\n// shown for this chart.\ntype cAutoTitleDeleted struct {\n\tVal bool `xml:\"val,attr\"`\n}\n\n// cView3D (View In 3D) directly maps the view3D element. This element\n// specifies the 3-D view of the chart.\ntype cView3D struct {\n\tRotX         *attrValInt `xml:\"rotX\"`\n\tRotY         *attrValInt `xml:\"rotY\"`\n\tRAngAx       *attrValInt `xml:\"rAngAx\"`\n\tDepthPercent *attrValInt `xml:\"depthPercent\"`\n\tPerspective  *attrValInt `xml:\"perspective\"`\n\tExtLst       *xlsxExtLst `xml:\"extLst\"`\n}\n\n// cPlotArea directly maps the plotArea element. This element specifies the\n// plot area of the chart.\ntype cPlotArea struct {\n\tLayout         *string    `xml:\"layout\"`\n\tAreaChart      []*cCharts `xml:\"areaChart\"`\n\tArea3DChart    []*cCharts `xml:\"area3DChart\"`\n\tBarChart       []*cCharts `xml:\"barChart\"`\n\tBar3DChart     []*cCharts `xml:\"bar3DChart\"`\n\tBubbleChart    []*cCharts `xml:\"bubbleChart\"`\n\tDoughnutChart  []*cCharts `xml:\"doughnutChart\"`\n\tLineChart      []*cCharts `xml:\"lineChart\"`\n\tLine3DChart    []*cCharts `xml:\"line3DChart\"`\n\tStockChart     []*cCharts `xml:\"stockChart\"`\n\tPieChart       []*cCharts `xml:\"pieChart\"`\n\tPie3DChart     []*cCharts `xml:\"pie3DChart\"`\n\tOfPieChart     []*cCharts `xml:\"ofPieChart\"`\n\tRadarChart     []*cCharts `xml:\"radarChart\"`\n\tScatterChart   []*cCharts `xml:\"scatterChart\"`\n\tSurface3DChart []*cCharts `xml:\"surface3DChart\"`\n\tSurfaceChart   []*cCharts `xml:\"surfaceChart\"`\n\tCatAx          []*cAxs    `xml:\"catAx\"`\n\tValAx          []*cAxs    `xml:\"valAx\"`\n\tDateAx         []*cAxs    `xml:\"dateAx\"`\n\tSerAx          []*cAxs    `xml:\"serAx\"`\n\tDTable         *cDTable   `xml:\"dTable\"`\n\tSpPr           *cSpPr     `xml:\"spPr\"`\n}\n\n// cCharts specifies the common element of the chart.\ntype cCharts struct {\n\tBarDir       *attrValString `xml:\"barDir\"`\n\tBubbleScale  *attrValFloat  `xml:\"bubbleScale\"`\n\tGrouping     *attrValString `xml:\"grouping\"`\n\tRadarStyle   *attrValString `xml:\"radarStyle\"`\n\tScatterStyle *attrValString `xml:\"scatterStyle\"`\n\tOfPieType    *attrValString `xml:\"ofPieType\"`\n\tVaryColors   *attrValBool   `xml:\"varyColors\"`\n\tWireframe    *attrValBool   `xml:\"wireframe\"`\n\tSer          *[]cSer        `xml:\"ser\"`\n\tSplitPos     *attrValInt    `xml:\"splitPos\"`\n\tSerLines     *attrValString `xml:\"serLines\"`\n\tDLbls        *cDLbls        `xml:\"dLbls\"`\n\tDropLines    *cChartLines   `xml:\"dropLines\"`\n\tHiLowLines   *cChartLines   `xml:\"hiLowLines\"`\n\tUpDownBars   *cUpDownBars   `xml:\"upDownBars\"`\n\tGapWidth     *attrValInt    `xml:\"gapWidth\"`\n\tShape        *attrValString `xml:\"shape\"`\n\tHoleSize     *attrValInt    `xml:\"holeSize\"`\n\tSmooth       *attrValBool   `xml:\"smooth\"`\n\tOverlap      *attrValInt    `xml:\"overlap\"`\n\tAxID         []*attrValInt  `xml:\"axId\"`\n}\n\n// cAxs directly maps the catAx and valAx element.\ntype cAxs struct {\n\tAxID           *attrValInt    `xml:\"axId\"`\n\tScaling        *cScaling      `xml:\"scaling\"`\n\tDelete         *attrValBool   `xml:\"delete\"`\n\tAxPos          *attrValString `xml:\"axPos\"`\n\tMajorGridlines *cChartLines   `xml:\"majorGridlines\"`\n\tMinorGridlines *cChartLines   `xml:\"minorGridlines\"`\n\tTitle          *cTitle        `xml:\"title\"`\n\tNumFmt         *cNumFmt       `xml:\"numFmt\"`\n\tMajorTickMark  *attrValString `xml:\"majorTickMark\"`\n\tMinorTickMark  *attrValString `xml:\"minorTickMark\"`\n\tTickLblPos     *attrValString `xml:\"tickLblPos\"`\n\tSpPr           *cSpPr         `xml:\"spPr\"`\n\tTxPr           *cTxPr         `xml:\"txPr\"`\n\tCrossAx        *attrValInt    `xml:\"crossAx\"`\n\tCrosses        *attrValString `xml:\"crosses\"`\n\tCrossBetween   *attrValString `xml:\"crossBetween\"`\n\tMajorUnit      *attrValFloat  `xml:\"majorUnit\"`\n\tMinorUnit      *attrValFloat  `xml:\"minorUnit\"`\n\tAuto           *attrValBool   `xml:\"auto\"`\n\tLblAlgn        *attrValString `xml:\"lblAlgn\"`\n\tLblOffset      *attrValInt    `xml:\"lblOffset\"`\n\tTickLblSkip    *attrValInt    `xml:\"tickLblSkip\"`\n\tTickMarkSkip   *attrValInt    `xml:\"tickMarkSkip\"`\n\tNoMultiLvlLbl  *attrValBool   `xml:\"noMultiLvlLbl\"`\n}\n\n// cUpDownBars directly maps the upDownBars lement. This element specifies\n// the up and down bars.\ntype cUpDownBars struct {\n\tGapWidth *attrValString `xml:\"gapWidth\"`\n\tUpBars   *cChartLines   `xml:\"upBars\"`\n\tDownBars *cChartLines   `xml:\"downBars\"`\n\tExtLst   *xlsxExtLst    `xml:\"extLst\"`\n}\n\n// cChartLines directly maps the chart lines content model.\ntype cChartLines struct {\n\tSpPr *cSpPr `xml:\"spPr\"`\n}\n\n// cScaling directly maps the scaling element. This element contains\n// additional axis settings.\ntype cScaling struct {\n\tLogBase     *attrValFloat  `xml:\"logBase\"`\n\tOrientation *attrValString `xml:\"orientation\"`\n\tMax         *attrValFloat  `xml:\"max\"`\n\tMin         *attrValFloat  `xml:\"min\"`\n}\n\n// cNumFmt (Numbering Format) directly maps the numFmt element. This element\n// specifies number formatting for the parent element.\ntype cNumFmt struct {\n\tFormatCode   string `xml:\"formatCode,attr\"`\n\tSourceLinked bool   `xml:\"sourceLinked,attr\"`\n}\n\n// cSer directly maps the ser element. This element specifies a series on a\n// chart.\ntype cSer struct {\n\tIDx              *attrValInt  `xml:\"idx\"`\n\tOrder            *attrValInt  `xml:\"order\"`\n\tTx               *cTx         `xml:\"tx\"`\n\tSpPr             *cSpPr       `xml:\"spPr\"`\n\tDPt              []*cDPt      `xml:\"dPt\"`\n\tDLbls            *cDLbls      `xml:\"dLbls\"`\n\tMarker           *cMarker     `xml:\"marker\"`\n\tInvertIfNegative *attrValBool `xml:\"invertIfNegative\"`\n\tCat              *cCat        `xml:\"cat\"`\n\tVal              *cVal        `xml:\"val\"`\n\tXVal             *cCat        `xml:\"xVal\"`\n\tYVal             *cVal        `xml:\"yVal\"`\n\tSmooth           *attrValBool `xml:\"smooth\"`\n\tBubbleSize       *cVal        `xml:\"bubbleSize\"`\n\tBubble3D         *attrValBool `xml:\"bubble3D\"`\n}\n\n// cMarker (Marker) directly maps the marker element. This element specifies a\n// data marker.\ntype cMarker struct {\n\tSymbol *attrValString `xml:\"symbol\"`\n\tSize   *attrValInt    `xml:\"size\"`\n\tSpPr   *cSpPr         `xml:\"spPr\"`\n}\n\n// cDPt (Data Point) directly maps the dPt element. This element specifies a\n// single data point.\ntype cDPt struct {\n\tIDx      *attrValInt  `xml:\"idx\"`\n\tBubble3D *attrValBool `xml:\"bubble3D\"`\n\tSpPr     *cSpPr       `xml:\"spPr\"`\n}\n\n// cCat (Category Axis Data) directly maps the cat element. This element\n// specifies the data used for the category axis.\ntype cCat struct {\n\tStrRef *cStrRef `xml:\"strRef\"`\n}\n\n// cStrRef (String Reference) directly maps the strRef element. This element\n// specifies a reference to data for a single data label or title with a cache\n// of the last values used.\ntype cStrRef struct {\n\tF        string     `xml:\"f\"`\n\tStrCache *cStrCache `xml:\"strCache\"`\n}\n\n// cStrCache (String Cache) directly maps the strCache element. This element\n// specifies the last string data used for a chart.\ntype cStrCache struct {\n\tPt      []*cPt      `xml:\"pt\"`\n\tPtCount *attrValInt `xml:\"ptCount\"`\n}\n\n// cPt directly maps the pt element. This element specifies data for a\n// particular data point.\ntype cPt struct {\n\tIDx int     `xml:\"idx,attr\"`\n\tV   *string `xml:\"v\"`\n}\n\n// cVal directly maps the val element. This element specifies the data values\n// which shall be used to define the location of data markers on a chart.\ntype cVal struct {\n\tNumRef *cNumRef `xml:\"numRef\"`\n}\n\n// cNumRef directly maps the numRef element. This element specifies a\n// reference to numeric data with a cache of the last values used.\ntype cNumRef struct {\n\tF        string     `xml:\"f\"`\n\tNumCache *cNumCache `xml:\"numCache\"`\n}\n\n// cNumCache directly maps the numCache element. This element specifies the\n// last data shown on the chart for a series.\ntype cNumCache struct {\n\tFormatCode string      `xml:\"formatCode\"`\n\tPt         []*cPt      `xml:\"pt\"`\n\tPtCount    *attrValInt `xml:\"ptCount\"`\n}\n\n// cDLbls (Data Labels) directly maps the dLbls element. This element serves\n// as a root element that specifies the settings for the data labels for an\n// entire series or the entire chart. It contains child elements that specify\n// the specific formatting and positioning settings.\ntype cDLbls struct {\n\tNumFmt          *cNumFmt       `xml:\"numFmt\"`\n\tSpPr            *cSpPr         `xml:\"spPr\"`\n\tTxPr            *cTxPr         `xml:\"txPr\"`\n\tDLblPos         *attrValString `xml:\"dLblPos\"`\n\tShowLegendKey   *attrValBool   `xml:\"showLegendKey\"`\n\tShowVal         *attrValBool   `xml:\"showVal\"`\n\tShowCatName     *attrValBool   `xml:\"showCatName\"`\n\tShowSerName     *attrValBool   `xml:\"showSerName\"`\n\tShowPercent     *attrValBool   `xml:\"showPercent\"`\n\tShowBubbleSize  *attrValBool   `xml:\"showBubbleSize\"`\n\tShowLeaderLines *attrValBool   `xml:\"showLeaderLines\"`\n}\n\n// cLegendEntry (Legend Entry) directly maps the legendEntry element. This\n// element specifies the legend entry.\ntype cLegendEntry struct {\n\tIDx  *attrValInt `xml:\"idx\"`\n\tTxPr *cTxPr      `xml:\"txPr\"`\n}\n\n// cLegend (Legend) directly maps the legend element. This element specifies\n// the legend.\ntype cLegend struct {\n\tLayout      *string        `xml:\"layout\"`\n\tLegendPos   *attrValString `xml:\"legendPos\"`\n\tLegendEntry []cLegendEntry `xml:\"legendEntry\"`\n\tOverlay     *attrValBool   `xml:\"overlay\"`\n\tSpPr        *cSpPr         `xml:\"spPr\"`\n\tTxPr        *cTxPr         `xml:\"txPr\"`\n}\n\n// cPrintSettings directly maps the printSettings element. This element\n// specifies the print settings for the chart.\ntype cPrintSettings struct {\n\tHeaderFooter *string       `xml:\"headerFooter\"`\n\tPageMargins  *cPageMargins `xml:\"pageMargins\"`\n\tPageSetup    *string       `xml:\"pageSetup\"`\n}\n\n// cPageMargins directly maps the pageMargins element. This element specifies\n// the page margins for a chart.\ntype cPageMargins struct {\n\tB      float64 `xml:\"b,attr\"`\n\tFooter float64 `xml:\"footer,attr\"`\n\tHeader float64 `xml:\"header,attr\"`\n\tL      float64 `xml:\"l,attr\"`\n\tR      float64 `xml:\"r,attr\"`\n\tT      float64 `xml:\"t,attr\"`\n}\n\n// ChartNumFmt directly maps the number format settings of the chart.\ntype ChartNumFmt struct {\n\tCustomNumFmt string\n\tSourceLinked bool\n}\n\n// ChartAxis directly maps the format settings of the chart axis.\ntype ChartAxis struct {\n\tNone              bool\n\tDropLines         bool\n\tHighLowLines      bool\n\tMajorGridLines    bool\n\tMinorGridLines    bool\n\tMajorUnit         float64\n\tTickLabelPosition ChartTickLabelPositionType\n\tTickLabelSkip     int\n\tReverseOrder      bool\n\tSecondary         bool\n\tMaximum           *float64\n\tMinimum           *float64\n\tAlignment         Alignment\n\tFont              Font\n\tLogBase           float64\n\tNumFmt            ChartNumFmt\n\tTitle             []RichTextRun\n\taxID              int\n}\n\n// ChartDimension directly maps the dimension of the chart.\ntype ChartDimension struct {\n\tWidth  uint\n\tHeight uint\n}\n\n// ChartUpDownBar directly maps the format settings of the stock chart up bars\n// and down bars.\ntype ChartUpDownBar struct {\n\tFill   Fill\n\tBorder ChartLine\n}\n\n// ChartPlotArea directly maps the format settings of the plot area.\ntype ChartPlotArea struct {\n\tSecondPlotValues  int\n\tShowBubbleSize    bool\n\tShowCatName       bool\n\tShowDataTable     bool\n\tShowDataTableKeys bool\n\tShowLeaderLines   bool\n\tShowPercent       bool\n\tShowSerName       bool\n\tShowVal           bool\n\tFill              Fill\n\tUpBars            ChartUpDownBar\n\tDownBars          ChartUpDownBar\n\tNumFmt            ChartNumFmt\n}\n\n// Chart directly maps the format settings of the chart.\ntype Chart struct {\n\tType         ChartType\n\tSeries       []ChartSeries\n\tFormat       GraphicOptions\n\tDimension    ChartDimension\n\tLegend       ChartLegend\n\tTitle        []RichTextRun\n\tVaryColors   *bool\n\tXAxis        ChartAxis\n\tYAxis        ChartAxis\n\tPlotArea     ChartPlotArea\n\tFill         Fill\n\tBorder       ChartLine\n\tShowBlanksAs string\n\tBubbleSize   int\n\tHoleSize     int\n\tGapWidth     *uint\n\tOverlap      *int\n\torder        int\n}\n\n// ChartLegend directly maps the format settings of the chart legend.\ntype ChartLegend struct {\n\tPosition      string\n\tShowLegendKey bool\n\tFont          *Font\n}\n\n// ChartMarker directly maps the format settings of the chart marker.\ntype ChartMarker struct {\n\tBorder ChartLine\n\tFill   Fill\n\tSymbol string\n\tSize   int\n}\n\n// ChartLine directly maps the format settings of the chart line.\ntype ChartLine struct {\n\tType   ChartLineType\n\tDash   ChartDashType\n\tFill   Fill\n\tSmooth bool\n\tWidth  float64\n}\n\n// ChartDataLabel directly maps the format settings of the chart labels.\ntype ChartDataLabel struct {\n\tAlignment Alignment\n\tFont      Font\n\tFill      Fill\n}\n\n// ChartDataPoint directly maps the format settings of the chart data point for\n// doughnut, pie and 3D pie charts.\ntype ChartDataPoint struct {\n\tIndex int\n\tFill  Fill\n}\n\n// ChartSeries directly maps the format settings of the chart series.\ntype ChartSeries struct {\n\tName              string\n\tCategories        string\n\tValues            string\n\tSizes             string\n\tFill              Fill\n\tLegend            ChartLegend\n\tLine              ChartLine\n\tMarker            ChartMarker\n\tDataLabel         ChartDataLabel\n\tDataLabelPosition ChartDataLabelPositionType\n\tDataPoint         []ChartDataPoint\n}\n"
  },
  {
    "path": "xmlChartSheet.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// struct code generated by github.com/xuri/xgen\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// xlsxChartsheet directly maps the chartsheet element of Chartsheet Parts in\n// a SpreadsheetML document.\ntype xlsxChartsheet struct {\n\tXMLName          xml.Name                   `xml:\"http://schemas.openxmlformats.org/spreadsheetml/2006/main chartsheet\"`\n\tSheetPr          *xlsxChartsheetPr          `xml:\"sheetPr\"`\n\tSheetViews       *xlsxChartsheetViews       `xml:\"sheetViews\"`\n\tSheetProtection  *xlsxChartsheetProtection  `xml:\"sheetProtection\"`\n\tCustomSheetViews *xlsxCustomChartsheetViews `xml:\"customSheetViews\"`\n\tPageMargins      *xlsxPageMargins           `xml:\"pageMargins\"`\n\tPageSetup        *xlsxPageSetUp             `xml:\"pageSetup\"`\n\tHeaderFooter     *xlsxHeaderFooter          `xml:\"headerFooter\"`\n\tDrawing          *xlsxDrawing               `xml:\"drawing\"`\n\tDrawingHF        *xlsxDrawingHF             `xml:\"drawingHF\"`\n\tPicture          *xlsxPicture               `xml:\"picture\"`\n\tWebPublishItems  *xlsxInnerXML              `xml:\"webPublishItems\"`\n\tExtLst           *xlsxExtLst                `xml:\"extLst\"`\n}\n\n// xlsxChartsheetPr specifies chart sheet properties.\ntype xlsxChartsheetPr struct {\n\tXMLName       xml.Name   `xml:\"sheetPr\"`\n\tPublishedAttr bool       `xml:\"published,attr,omitempty\"`\n\tCodeNameAttr  string     `xml:\"codeName,attr,omitempty\"`\n\tTabColor      *xlsxColor `xml:\"tabColor\"`\n}\n\n// xlsxChartsheetViews specifies chart sheet views.\ntype xlsxChartsheetViews struct {\n\tXMLName   xml.Name              `xml:\"sheetViews\"`\n\tSheetView []*xlsxChartsheetView `xml:\"sheetView\"`\n\tExtLst    []*xlsxExtLst         `xml:\"extLst\"`\n}\n\n// xlsxChartsheetView defines custom view properties for chart sheets.\ntype xlsxChartsheetView struct {\n\tXMLName            xml.Name      `xml:\"sheetView\"`\n\tTabSelectedAttr    bool          `xml:\"tabSelected,attr,omitempty\"`\n\tZoomScaleAttr      uint32        `xml:\"zoomScale,attr,omitempty\"`\n\tWorkbookViewIDAttr uint32        `xml:\"workbookViewId,attr\"`\n\tZoomToFitAttr      bool          `xml:\"zoomToFit,attr,omitempty\"`\n\tExtLst             []*xlsxExtLst `xml:\"extLst\"`\n}\n\n// xlsxChartsheetProtection collection expresses the chart sheet protection\n// options to enforce when the chart sheet is protected.\ntype xlsxChartsheetProtection struct {\n\tXMLName           xml.Name `xml:\"sheetProtection\"`\n\tAlgorithmNameAttr string   `xml:\"algorithmName,attr,omitempty\"`\n\tHashValueAttr     []byte   `xml:\"hashValue,attr,omitempty\"`\n\tSaltValueAttr     []byte   `xml:\"saltValue,attr,omitempty\"`\n\tSpinCountAttr     uint32   `xml:\"spinCount,attr,omitempty\"`\n\tContentAttr       bool     `xml:\"content,attr,omitempty\"`\n\tObjectsAttr       bool     `xml:\"objects,attr,omitempty\"`\n}\n\n// xlsxCustomChartsheetViews collection of custom Chart Sheet View\n// information.\ntype xlsxCustomChartsheetViews struct {\n\tXMLName         xml.Name                    `xml:\"customSheetViews\"`\n\tCustomSheetView []*xlsxCustomChartsheetView `xml:\"customSheetView\"`\n}\n\n// xlsxCustomChartsheetView defines custom view properties for chart sheets.\ntype xlsxCustomChartsheetView struct {\n\tXMLName       xml.Name            `xml:\"customSheetView\"`\n\tGUIDAttr      string              `xml:\"guid,attr\"`\n\tScaleAttr     uint32              `xml:\"scale,attr,omitempty\"`\n\tStateAttr     string              `xml:\"state,attr,omitempty\"`\n\tZoomToFitAttr bool                `xml:\"zoomToFit,attr,omitempty\"`\n\tPageMargins   []*xlsxPageMargins  `xml:\"pageMargins\"`\n\tPageSetup     []*xlsxPageSetUp    `xml:\"pageSetup\"`\n\tHeaderFooter  []*xlsxHeaderFooter `xml:\"headerFooter\"`\n}\n"
  },
  {
    "path": "xmlComments.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// xlsxComments directly maps the comments element from the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main. A comment is a\n// rich text note that is attached to and associated with a cell, separate from\n// other cell content. Comment content is stored separate from the cell, and is\n// displayed in a drawing object (like a text box) that is separate from, but\n// associated with, a cell. Comments are used as reminders, such as noting how a\n// complex formula works, or to provide feedback to other users. Comments can\n// also be used to explain assumptions made in a formula or to call out\n// something special about the cell.\ntype xlsxComments struct {\n\tXMLName     xml.Name        `xml:\"http://schemas.openxmlformats.org/spreadsheetml/2006/main comments\"`\n\tAuthors     xlsxAuthor      `xml:\"authors\"`\n\tCommentList xlsxCommentList `xml:\"commentList\"`\n\tcells       []string\n}\n\n// xlsxAuthor directly maps the author element. This element holds a string\n// representing the name of a single author of comments. Every comment shall\n// have an author. The maximum length of the author string is an implementation\n// detail, but a good guideline is 255 chars.\ntype xlsxAuthor struct {\n\tAuthor []string `xml:\"author\"`\n}\n\n// xlsxCommentList (List of Comments) directly maps the xlsxCommentList element.\n// This element is a container that holds a list of comments for the sheet.\ntype xlsxCommentList struct {\n\tComment []xlsxComment `xml:\"comment\"`\n}\n\n// xlsxComment directly maps the comment element. This element represents a\n// single user entered comment. Each comment shall have an author and can\n// optionally contain richly formatted text.\ntype xlsxComment struct {\n\tRef      string   `xml:\"ref,attr\"`\n\tAuthorID int      `xml:\"authorId,attr\"`\n\tText     xlsxText `xml:\"text\"`\n}\n\n// xlsxText directly maps the text element. This element contains rich text\n// which represents the text of a comment. The maximum length for this text is a\n// spreadsheet application implementation detail. A recommended guideline is\n// 32767 chars.\ntype xlsxText struct {\n\tT          *string          `xml:\"t\"`\n\tR          []xlsxR          `xml:\"r\"`\n\tRPh        *xlsxPhoneticRun `xml:\"rPh\"`\n\tPhoneticPr *xlsxPhoneticPr  `xml:\"phoneticPr\"`\n}\n\n// xlsxPhoneticRun element represents a run of text which displays a phonetic\n// hint for this String Item (si). Phonetic hints are used to give information\n// about the pronunciation of an East Asian language. The hints are displayed\n// as text within the spreadsheet cells across the top portion of the cell.\ntype xlsxPhoneticRun struct {\n\tSb uint32 `xml:\"sb,attr\"`\n\tEb uint32 `xml:\"eb,attr\"`\n\tT  string `xml:\"t\"`\n}\n\n// Comment directly maps the comment information.\ntype Comment struct {\n\tAuthor    string\n\tAuthorID  int\n\tCell      string\n\tText      string\n\tWidth     uint\n\tHeight    uint\n\tParagraph []RichTextRun\n}\n"
  },
  {
    "path": "xmlContentTypes.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"sync\"\n)\n\n// xlsxTypes directly maps the types' element of content types for relationship\n// parts, it takes a Multipurpose Internet Mail Extension (MIME) media type as a\n// value.\ntype xlsxTypes struct {\n\tmu        sync.Mutex\n\tXMLName   xml.Name       `xml:\"http://schemas.openxmlformats.org/package/2006/content-types Types\"`\n\tDefaults  []xlsxDefault  `xml:\"Default\"`\n\tOverrides []xlsxOverride `xml:\"Override\"`\n}\n\n// xlsxOverride directly maps the override element in the namespace\n// http://schemas.openxmlformats.org/package/2006/content-types\ntype xlsxOverride struct {\n\tPartName    string `xml:\",attr\"`\n\tContentType string `xml:\",attr\"`\n}\n\n// xlsxDefault directly maps the default element in the namespace\n// http://schemas.openxmlformats.org/package/2006/content-types\ntype xlsxDefault struct {\n\tExtension   string `xml:\",attr\"`\n\tContentType string `xml:\",attr\"`\n}\n"
  },
  {
    "path": "xmlCore.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// DocProperties directly maps the document core properties.\ntype DocProperties struct {\n\tCategory       string\n\tContentStatus  string\n\tCreated        string\n\tCreator        string\n\tDescription    string\n\tIdentifier     string\n\tKeywords       string\n\tLastModifiedBy string\n\tModified       string\n\tRevision       string\n\tSubject        string\n\tTitle          string\n\tLanguage       string\n\tVersion        string\n}\n\n// decodeDcTerms directly maps the DCMI metadata terms for the coreProperties.\ntype decodeDcTerms struct {\n\tText string `xml:\",chardata\"`\n\tType string `xml:\"http://www.w3.org/2001/XMLSchema-instance type,attr\"`\n}\n\n// decodeCoreProperties directly maps the root element for a part of this\n// content type shall coreProperties. In order to solve the problem that the\n// label structure is changed after serialization and deserialization, two\n// different structures are defined. decodeCoreProperties just for\n// deserialization.\ntype decodeCoreProperties struct {\n\tXMLName        xml.Name       `xml:\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties coreProperties\"`\n\tTitle          string         `xml:\"http://purl.org/dc/elements/1.1/ title,omitempty\"`\n\tSubject        string         `xml:\"http://purl.org/dc/elements/1.1/ subject,omitempty\"`\n\tCreator        string         `xml:\"http://purl.org/dc/elements/1.1/ creator\"`\n\tKeywords       string         `xml:\"keywords,omitempty\"`\n\tDescription    string         `xml:\"http://purl.org/dc/elements/1.1/ description,omitempty\"`\n\tLastModifiedBy string         `xml:\"lastModifiedBy\"`\n\tLanguage       string         `xml:\"http://purl.org/dc/elements/1.1/ language,omitempty\"`\n\tIdentifier     string         `xml:\"http://purl.org/dc/elements/1.1/ identifier,omitempty\"`\n\tRevision       string         `xml:\"revision,omitempty\"`\n\tCreated        *decodeDcTerms `xml:\"http://purl.org/dc/terms/ created\"`\n\tModified       *decodeDcTerms `xml:\"http://purl.org/dc/terms/ modified\"`\n\tContentStatus  string         `xml:\"contentStatus,omitempty\"`\n\tCategory       string         `xml:\"category,omitempty\"`\n\tVersion        string         `xml:\"version,omitempty\"`\n}\n\n// xlsxDcTerms directly maps the DCMI metadata terms for the coreProperties.\ntype xlsxDcTerms struct {\n\tText string `xml:\",chardata\"`\n\tType string `xml:\"xsi:type,attr\"`\n}\n\n// xlsxCoreProperties directly maps the root element for a part of this\n// content type shall coreProperties.\ntype xlsxCoreProperties struct {\n\tXMLName        xml.Name     `xml:\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties coreProperties\"`\n\tDc             string       `xml:\"xmlns:dc,attr\"`\n\tDcterms        string       `xml:\"xmlns:dcterms,attr\"`\n\tDcmitype       string       `xml:\"xmlns:dcmitype,attr\"`\n\tXSI            string       `xml:\"xmlns:xsi,attr\"`\n\tTitle          string       `xml:\"dc:title,omitempty\"`\n\tSubject        string       `xml:\"dc:subject,omitempty\"`\n\tCreator        string       `xml:\"dc:creator\"`\n\tKeywords       string       `xml:\"keywords,omitempty\"`\n\tDescription    string       `xml:\"dc:description,omitempty\"`\n\tLastModifiedBy string       `xml:\"lastModifiedBy\"`\n\tLanguage       string       `xml:\"dc:language,omitempty\"`\n\tIdentifier     string       `xml:\"dc:identifier,omitempty\"`\n\tRevision       string       `xml:\"revision,omitempty\"`\n\tCreated        *xlsxDcTerms `xml:\"dcterms:created\"`\n\tModified       *xlsxDcTerms `xml:\"dcterms:modified\"`\n\tContentStatus  string       `xml:\"contentStatus,omitempty\"`\n\tCategory       string       `xml:\"category,omitempty\"`\n\tVersion        string       `xml:\"version,omitempty\"`\n}\n"
  },
  {
    "path": "xmlCustom.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// xlsxCustomProperties directly maps the element for the custom file properties\n// part, that represents additional information. The information can be used as\n// metadata for XML.\ntype xlsxCustomProperties struct {\n\tXMLName  xml.Name       `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/custom-properties Properties\"`\n\tVt       string         `xml:\"xmlns:vt,attr\"`\n\tProperty []xlsxProperty `xml:\"property\"`\n}\n\n// xlsxProperty directly maps the element specifies a single custom file\n// property. Custom file property type is defined through child elements in the\n// File Properties Variant Type namespace. Custom file property value can be set\n// by setting the appropriate Variant Type child element value.\ntype xlsxProperty struct {\n\tXMLName    xml.Name `xml:\"property\"`\n\tFmtID      string   `xml:\"fmtid,attr\"`\n\tPID        int      `xml:\"pid,attr\"`\n\tName       string   `xml:\"name,attr,omitempty\"`\n\tLinkTarget string   `xml:\"linkTarget,attr,omitempty\"`\n\tVector     *string  `xml:\"vt:vector\"`\n\tArray      *string  `xml:\"vt:array\"`\n\tBlob       *string  `xml:\"vt:blob\"`\n\tOblob      *string  `xml:\"vt:oblob\"`\n\tEmpty      *string  `xml:\"vt:empty\"`\n\tNull       *string  `xml:\"vt:null\"`\n\tI1         *int8    `xml:\"vt:i1\"`\n\tI2         *int16   `xml:\"vt:i2\"`\n\tI4         *int32   `xml:\"vt:i4\"`\n\tI8         *int64   `xml:\"vt:i8\"`\n\tInt        *int     `xml:\"vt:int\"`\n\tUi1        *uint8   `xml:\"vt:ui1\"`\n\tUi2        *uint16  `xml:\"vt:ui2\"`\n\tUi4        *uint32  `xml:\"vt:ui4\"`\n\tUi8        *uint64  `xml:\"vt:ui8\"`\n\tUint       *uint    `xml:\"vt:uint\"`\n\tR4         *float32 `xml:\"vt:r4\"`\n\tR8         *float64 `xml:\"vt:r8\"`\n\tDecimal    *string  `xml:\"vt:decimal\"`\n\tLpstr      *string  `xml:\"vt:lpstr\"`\n\tLpwstr     *string  `xml:\"vt:lpwstr\"`\n\tBstr       *string  `xml:\"vt:bstr\"`\n\tDate       *string  `xml:\"vt:date\"`\n\tFileTime   *string  `xml:\"vt:filetime\"`\n\tBool       *bool    `xml:\"vt:bool\"`\n\tCy         *string  `xml:\"vt:cy\"`\n\tError      *string  `xml:\"vt:error\"`\n\tStream     *string  `xml:\"vt:stream\"`\n\tOstream    *string  `xml:\"vt:ostream\"`\n\tStorage    *string  `xml:\"vt:storage\"`\n\tOstorage   *string  `xml:\"vt:ostorage\"`\n\tVstream    *string  `xml:\"vt:vstream\"`\n\tClsID      *string  `xml:\"vt:clsid\"`\n}\n\n// decodeCustomProperties specifies to an OOXML document custom properties.\n// decodeCustomProperties just for deserialization.\ntype decodeCustomProperties struct {\n\tXMLName  xml.Name         `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/custom-properties Properties\"`\n\tVt       string           `xml:\"xmlns:vt,attr\"`\n\tProperty []decodeProperty `xml:\"property\"`\n}\n\n// decodeProperty specifies to an OOXML document custom property. decodeProperty\n// just for deserialization.\ntype decodeProperty struct {\n\tXMLName    xml.Name `xml:\"property\"`\n\tFmtID      string   `xml:\"fmtid,attr\"`\n\tPID        int      `xml:\"pid,attr\"`\n\tName       string   `xml:\"name,attr,omitempty\"`\n\tLinkTarget string   `xml:\"linkTarget,attr,omitempty\"`\n\tVector     *string  `xml:\"vector\"`\n\tArray      *string  `xml:\"array\"`\n\tBlob       *string  `xml:\"blob\"`\n\tOblob      *string  `xml:\"oblob\"`\n\tEmpty      *string  `xml:\"empty\"`\n\tNull       *string  `xml:\"null\"`\n\tI1         *int8    `xml:\"i1\"`\n\tI2         *int16   `xml:\"i2\"`\n\tI4         *int32   `xml:\"i4\"`\n\tI8         *int64   `xml:\"i8\"`\n\tInt        *int     `xml:\"int\"`\n\tUi1        *uint8   `xml:\"ui1\"`\n\tUi2        *uint16  `xml:\"ui2\"`\n\tUi4        *uint32  `xml:\"ui4\"`\n\tUi8        *uint64  `xml:\"ui8\"`\n\tUint       *uint    `xml:\"uint\"`\n\tR4         *float32 `xml:\"r4\"`\n\tR8         *float64 `xml:\"r8\"`\n\tDecimal    *string  `xml:\"decimal\"`\n\tLpstr      *string  `xml:\"lpstr\"`\n\tLpwstr     *string  `xml:\"lpwstr\"`\n\tBstr       *string  `xml:\"bstr\"`\n\tDate       *string  `xml:\"date\"`\n\tFileTime   *string  `xml:\"filetime\"`\n\tBool       *bool    `xml:\"bool\"`\n\tCy         *string  `xml:\"cy\"`\n\tError      *string  `xml:\"error\"`\n\tStream     *string  `xml:\"stream\"`\n\tOstream    *string  `xml:\"ostream\"`\n\tStorage    *string  `xml:\"storage\"`\n\tOstorage   *string  `xml:\"ostorage\"`\n\tVstream    *string  `xml:\"vstream\"`\n\tClsID      *string  `xml:\"clsid\"`\n}\n\n// CustomProperty directly maps the custom property of the workbook. The value\n// date type may be one of the following: int32, float64, string, bool,\n// time.Time, or nil.\ntype CustomProperty struct {\n\tName  string\n\tValue interface{}\n}\n"
  },
  {
    "path": "xmlDecodeDrawing.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// decodeCellAnchor directly maps the oneCellAnchor (One Cell Anchor Shape\n// Size) and twoCellAnchor (Two Cell Anchor Shape Size). This element\n// specifies a two cell anchor placeholder for a group, a shape, or a drawing\n// element. It moves with cells and its extents are in EMU units.\ntype decodeCellAnchor struct {\n\tEditAs           string                  `xml:\"editAs,attr,omitempty\"`\n\tFrom             *decodeFrom             `xml:\"from\"`\n\tTo               *decodeTo               `xml:\"to\"`\n\tExt              *decodePositiveSize2D   `xml:\"ext\"`\n\tSp               *decodeSp               `xml:\"sp\"`\n\tPic              *decodePic              `xml:\"pic\"`\n\tClientData       *decodeClientData       `xml:\"clientData\"`\n\tAlternateContent []*xlsxAlternateContent `xml:\"AlternateContent\"`\n\tContent          string                  `xml:\",innerxml\"`\n}\n\n// decodeCellAnchorPos defines the structure used to deserialize the cell anchor\n// for adjust drawing object on inserting/deleting column/rows.\ntype decodeCellAnchorPos struct {\n\tEditAs           string                  `xml:\"editAs,attr,omitempty\"`\n\tFrom             *xlsxFrom               `xml:\"from\"`\n\tTo               *xlsxTo                 `xml:\"to\"`\n\tPos              *xlsxInnerXML           `xml:\"pos\"`\n\tExt              *xlsxPositiveSize2D     `xml:\"ext\"`\n\tSp               *xlsxSp                 `xml:\"sp\"`\n\tGrpSp            *xlsxInnerXML           `xml:\"grpSp\"`\n\tGraphicFrame     *xlsxInnerXML           `xml:\"graphicFrame\"`\n\tCxnSp            *xlsxInnerXML           `xml:\"cxnSp\"`\n\tPic              *xlsxInnerXML           `xml:\"pic\"`\n\tContentPart      *xlsxInnerXML           `xml:\"contentPart\"`\n\tAlternateContent []*xlsxAlternateContent `xml:\"AlternateContent\"`\n\tClientData       *xlsxInnerXML           `xml:\"clientData\"`\n}\n\n// decodeChoice defines the structure used to deserialize the mc:Choice element.\ntype decodeChoice struct {\n\tXMLName      xml.Name           `xml:\"Choice\"`\n\tXMLNSA14     string             `xml:\"a14,attr\"`\n\tXMLNSSle15   string             `xml:\"sle15,attr\"`\n\tRequires     string             `xml:\"Requires,attr\"`\n\tGraphicFrame decodeGraphicFrame `xml:\"graphicFrame\"`\n}\n\n// decodeGraphicFrame defines the structure used to deserialize the\n// xdr:graphicFrame element.\ntype decodeGraphicFrame struct {\n\tMacro            string                 `xml:\"macro,attr\"`\n\tNvGraphicFramePr decodeNvGraphicFramePr `xml:\"nvGraphicFramePr\"`\n}\n\n// decodeNvGraphicFramePr defines the structure used to deserialize the\n// xdr:nvGraphicFramePr element.\ntype decodeNvGraphicFramePr struct {\n\tCNvPr decodeCNvPr `xml:\"cNvPr\"`\n}\n\n// decodeSp defines the structure used to deserialize the sp element.\ntype decodeSp struct {\n\tMacro      string        `xml:\"macro,attr,omitempty\"`\n\tTextLink   string        `xml:\"textlink,attr,omitempty\"`\n\tFLocksText bool          `xml:\"fLocksText,attr,omitempty\"`\n\tFPublished *bool         `xml:\"fPublished,attr\"`\n\tNvSpPr     *decodeNvSpPr `xml:\"nvSpPr\"`\n\tSpPr       *decodeSpPr   `xml:\"spPr\"`\n}\n\n// decodeNvSpPr (Non-Visual Properties for a Shape) directly maps the nvSpPr\n// element. This element specifies all non-visual properties for a shape. This\n// element is a container for the non-visual identification properties, shape\n// properties and application properties that are to be associated with a\n// shape. This allows for additional information that does not affect the\n// appearance of the shape to be stored.\ntype decodeNvSpPr struct {\n\tCNvPr   *decodeCNvPr          `xml:\"cNvPr\"`\n\tExtLst  *decodePositiveSize2D `xml:\"extLst\"`\n\tCNvSpPr *decodeCNvSpPr        `xml:\"cNvSpPr\"`\n}\n\n// decodeCNvSpPr (Connection Non-Visual Shape Properties) directly maps the\n// cNvSpPr element. This element specifies the set of non-visual properties\n// for a connection shape. These properties specify all data about the\n// connection shape which do not affect its display within a spreadsheet.\ntype decodeCNvSpPr struct {\n\tTxBox bool `xml:\"txBox,attr\"`\n}\n\n// decodeWsDr directly maps the root element for a part of this content type\n// shall wsDr. In order to solve the problem that the label structure is\n// changed after serialization and deserialization, two different structures\n// are defined. decodeWsDr just for deserialization.\ntype decodeWsDr struct {\n\tXMLName          xml.Name            `xml:\"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing wsDr\"`\n\tA                string              `xml:\"xmlns a,attr\"`\n\tXdr              string              `xml:\"xmlns xdr,attr\"`\n\tR                string              `xml:\"xmlns r,attr\"`\n\tAlternateContent []*xlsxInnerXML     `xml:\"http://schemas.openxmlformats.org/markup-compatibility/2006 AlternateContent\"`\n\tOneCellAnchor    []*decodeCellAnchor `xml:\"oneCellAnchor,omitempty\"`\n\tTwoCellAnchor    []*decodeCellAnchor `xml:\"twoCellAnchor,omitempty\"`\n}\n\n// decodeCNvPr directly maps the cNvPr (Non-Visual Drawing Properties). This\n// element specifies non-visual canvas properties. This allows for additional\n// information that does not affect the appearance of the picture to be\n// stored.\ntype decodeCNvPr struct {\n\tXMLName    xml.Name          `xml:\"cNvPr\"`\n\tID         int               `xml:\"id,attr\"`\n\tName       string            `xml:\"name,attr\"`\n\tDescr      string            `xml:\"descr,attr\"`\n\tTitle      string            `xml:\"title,attr,omitempty\"`\n\tHlinkClick *decodeHlinkClick `xml:\"hlinkClick,omitempty\"`\n}\n\n// decodeHlinkClick directly maps the hlinkClick (Hyperlink Click).This element\n// specifies the on-click hyperlink information to be applied to a run of text.\n// When the hyperlink text is clicked the link is fetched.\n// clicked the link is fetched.\ntype decodeHlinkClick struct {\n\tRID            string `xml:\"id,attr,omitempty\"`\n\tInvalidURL     string `xml:\"invalidUrl,attr,omitempty\"`\n\tAction         string `xml:\"action,attr,omitempty\"`\n\tTgtFrame       string `xml:\"tgtFrame,attr,omitempty\"`\n\tTooltip        string `xml:\"tooltip,attr,omitempty\"`\n\tHistory        bool   `xml:\"history,attr,omitempty\"`\n\tHighlightClick bool   `xml:\"highlightClick,attr,omitempty\"`\n\tEndSnd         bool   `xml:\"endSnd,attr,omitempty\"`\n}\n\n// decodePicLocks directly maps the picLocks (Picture Locks). This element\n// specifies all locking properties for a graphic frame. These properties\n// inform the generating application about specific properties that have been\n// previously locked and thus should not be changed.\ntype decodePicLocks struct {\n\tNoAdjustHandles    bool `xml:\"noAdjustHandles,attr,omitempty\"`\n\tNoChangeArrowheads bool `xml:\"noChangeArrowheads,attr,omitempty\"`\n\tNoChangeAspect     bool `xml:\"noChangeAspect,attr\"`\n\tNoChangeShapeType  bool `xml:\"noChangeShapeType,attr,omitempty\"`\n\tNoCrop             bool `xml:\"noCrop,attr,omitempty\"`\n\tNoEditPoints       bool `xml:\"noEditPoints,attr,omitempty\"`\n\tNoGrp              bool `xml:\"noGrp,attr,omitempty\"`\n\tNoMove             bool `xml:\"noMove,attr,omitempty\"`\n\tNoResize           bool `xml:\"noResize,attr,omitempty\"`\n\tNoRot              bool `xml:\"noRot,attr,omitempty\"`\n\tNoSelect           bool `xml:\"noSelect,attr,omitempty\"`\n}\n\n// decodeBlip element specifies the existence of an image (binary large image\n// or picture) and contains a reference to the image data.\ntype decodeBlip struct {\n\tEmbed  string `xml:\"embed,attr\"`\n\tCstate string `xml:\"cstate,attr,omitempty\"`\n\tR      string `xml:\"r,attr\"`\n}\n\n// decodeStretch directly maps the stretch element. This element specifies\n// that a BLIP should be stretched to fill the target rectangle. The other\n// option is a tile where a BLIP is tiled to fill the available area.\ntype decodeStretch struct {\n\tFillRect string `xml:\"fillRect\"`\n}\n\n// decodeOff directly maps the colOff and rowOff element. This element is used\n// to specify the column offset within a cell.\ntype decodeOff struct {\n\tX int `xml:\"x,attr\"`\n\tY int `xml:\"y,attr\"`\n}\n\n// decodePositiveSize2D directly maps the a:ext element.\ntype decodePositiveSize2D struct {\n\tCx int `xml:\"cx,attr\"`\n\tCy int `xml:\"cy,attr\"`\n}\n\n// decodePrstGeom directly maps the prstGeom (Preset geometry). This element\n// specifies when a preset geometric shape should be used instead of a custom\n// geometric shape. The generating application should be able to render all\n// preset geometries enumerated in the ST_ShapeType list.\ntype decodePrstGeom struct {\n\tPrst string `xml:\"prst,attr\"`\n}\n\n// decodeXfrm directly maps the xfrm (2D Transform for Graphic Frame). This\n// element specifies the transform to be applied to the corresponding graphic\n// frame. This transformation is applied to the graphic frame just as it would\n// be for a shape or group shape.\ntype decodeXfrm struct {\n\tOff decodeOff            `xml:\"off\"`\n\tExt decodePositiveSize2D `xml:\"ext\"`\n}\n\n// decodeCNvPicPr directly maps the cNvPicPr (Non-Visual Picture Drawing\n// Properties). This element specifies the non-visual properties for the picture\n// canvas. These properties are to be used by the generating application to\n// determine how certain properties are to be changed for the picture object in\n// question.\ntype decodeCNvPicPr struct {\n\tPicLocks decodePicLocks `xml:\"picLocks\"`\n}\n\n// directly maps the nvPicPr (Non-Visual Properties for a Picture). This\n// element specifies all non-visual properties for a picture. This element is\n// a container for the non-visual identification properties, shape properties\n// and application properties that are to be associated with a picture. This\n// allows for additional information that does not affect the appearance of\n// the picture to be stored.\ntype decodeNvPicPr struct {\n\tCNvPr    decodeCNvPr    `xml:\"cNvPr\"`\n\tCNvPicPr decodeCNvPicPr `xml:\"cNvPicPr\"`\n}\n\n// decodeBlipFill directly maps the blipFill (Picture Fill). This element\n// specifies the kind of picture fill that the picture object has. Because a\n// picture has a picture fill already by default, it is possible to have two\n// fills specified for a picture object.\ntype decodeBlipFill struct {\n\tBlip    decodeBlip    `xml:\"blip\"`\n\tStretch decodeStretch `xml:\"stretch\"`\n}\n\n// decodeSpPr directly maps the spPr (Shape Properties). This element\n// specifies the visual shape properties that can be applied to a picture.\n// These are the same properties that are allowed to describe the visual\n// properties of a shape but are used here to describe the visual appearance\n// of a picture within a document.\ntype decodeSpPr struct {\n\tXfrm     decodeXfrm     `xml:\"xfrm\"`\n\tPrstGeom decodePrstGeom `xml:\"prstGeom\"`\n}\n\n// decodePic elements encompass the definition of pictures within the\n// DrawingML framework. While pictures are in many ways very similar to shapes\n// they have specific properties that are unique in order to optimize for\n// picture-specific scenarios.\ntype decodePic struct {\n\tNvPicPr  decodeNvPicPr  `xml:\"nvPicPr\"`\n\tBlipFill decodeBlipFill `xml:\"blipFill\"`\n\tSpPr     decodeSpPr     `xml:\"spPr\"`\n}\n\n// decodeFrom specifies the starting anchor.\ntype decodeFrom struct {\n\tCol    int `xml:\"col\"`\n\tColOff int `xml:\"colOff\"`\n\tRow    int `xml:\"row\"`\n\tRowOff int `xml:\"rowOff\"`\n}\n\n// decodeTo directly specifies the ending anchor.\ntype decodeTo struct {\n\tCol    int `xml:\"col\"`\n\tColOff int `xml:\"colOff\"`\n\tRow    int `xml:\"row\"`\n\tRowOff int `xml:\"rowOff\"`\n}\n\n// decodeClientData directly maps the clientData element. An empty element\n// which specifies (via attributes) certain properties related to printing and\n// selection of the drawing object. The fLocksWithSheet attribute (either true\n// or false) determines whether to disable selection when the sheet is\n// protected, and fPrintsWithSheet attribute (either true or false) determines\n// whether the object is printed when the sheet is printed.\ntype decodeClientData struct {\n\tFLocksWithSheet  bool `xml:\"fLocksWithSheet,attr\"`\n\tFPrintsWithSheet bool `xml:\"fPrintsWithSheet,attr\"`\n}\n\n// decodeCellImages directly maps the Kingsoft WPS Office embedded cell images.\ntype decodeCellImages struct {\n\tXMLName   xml.Name          `xml:\"http://www.wps.cn/officeDocument/2017/etCustomData cellImages\"`\n\tCellImage []decodeCellImage `xml:\"cellImage\"`\n}\n\n// decodeCellImage defines the structure used to deserialize the Kingsoft WPS\n// Office embedded cell images.\ntype decodeCellImage struct {\n\tPic decodePic `xml:\"pic\"`\n}\n"
  },
  {
    "path": "xmlDrawing.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"sync\"\n)\n\n// xlsxCNvPr directly maps the cNvPr (Non-Visual Drawing Properties). This\n// element specifies non-visual canvas properties. This allows for additional\n// information that does not affect the appearance of the picture to be stored.\ntype xlsxCNvPr struct {\n\tID         int             `xml:\"id,attr\"`\n\tName       string          `xml:\"name,attr\"`\n\tDescr      string          `xml:\"descr,attr\"`\n\tTitle      string          `xml:\"title,attr,omitempty\"`\n\tHlinkClick *xlsxHlinkClick `xml:\"a:hlinkClick\"`\n}\n\n// xlsxHlinkClick (Click Hyperlink) Specifies the on-click hyperlink\n// information to be applied to a run of text. When the hyperlink text is\n// clicked the link is fetched.\ntype xlsxHlinkClick struct {\n\tR              string `xml:\"xmlns:r,attr,omitempty\"`\n\tRID            string `xml:\"r:id,attr,omitempty\"`\n\tInvalidURL     string `xml:\"invalidUrl,attr,omitempty\"`\n\tAction         string `xml:\"action,attr,omitempty\"`\n\tTgtFrame       string `xml:\"tgtFrame,attr,omitempty\"`\n\tTooltip        string `xml:\"tooltip,attr,omitempty\"`\n\tHistory        bool   `xml:\"history,attr,omitempty\"`\n\tHighlightClick bool   `xml:\"highlightClick,attr,omitempty\"`\n\tEndSnd         bool   `xml:\"endSnd,attr,omitempty\"`\n}\n\n// xlsxPicLocks directly maps the picLocks (Picture Locks). This element\n// specifies all locking properties for a graphic frame. These properties inform\n// the generating application about specific properties that have been\n// previously locked and thus should not be changed.\ntype xlsxPicLocks struct {\n\tNoAdjustHandles    bool `xml:\"noAdjustHandles,attr,omitempty\"`\n\tNoChangeArrowheads bool `xml:\"noChangeArrowheads,attr,omitempty\"`\n\tNoChangeAspect     bool `xml:\"noChangeAspect,attr\"`\n\tNoChangeShapeType  bool `xml:\"noChangeShapeType,attr,omitempty\"`\n\tNoCrop             bool `xml:\"noCrop,attr,omitempty\"`\n\tNoEditPoints       bool `xml:\"noEditPoints,attr,omitempty\"`\n\tNoGrp              bool `xml:\"noGrp,attr,omitempty\"`\n\tNoMove             bool `xml:\"noMove,attr,omitempty\"`\n\tNoResize           bool `xml:\"noResize,attr,omitempty\"`\n\tNoRot              bool `xml:\"noRot,attr,omitempty\"`\n\tNoSelect           bool `xml:\"noSelect,attr,omitempty\"`\n}\n\n// xlsxBlip element specifies the existence of an image (binary large image or\n// picture) and contains a reference to the image data.\ntype xlsxBlip struct {\n\tEmbed   string                        `xml:\"r:embed,attr\"`\n\tCstate  string                        `xml:\"cstate,attr,omitempty\"`\n\tR       string                        `xml:\"xmlns:r,attr\"`\n\tExtList *xlsxEGOfficeArtExtensionList `xml:\"a:extLst\"`\n}\n\n// xlsxStretch directly maps the stretch element. This element specifies that a\n// BLIP should be stretched to fill the target rectangle. The other option is a\n// tile where a BLIP is tiled to fill the available area.\ntype xlsxStretch struct {\n\tFillRect string `xml:\"a:fillRect\"`\n}\n\n// xlsxOff directly maps the colOff and rowOff element. This element is used to\n// specify the column offset within a cell.\ntype xlsxOff struct {\n\tX int `xml:\"x,attr\"`\n\tY int `xml:\"y,attr\"`\n}\n\n// xlsxPositiveSize2D directly maps the a:ext element.\ntype xlsxPositiveSize2D struct {\n\tCx int `xml:\"cx,attr\"`\n\tCy int `xml:\"cy,attr\"`\n}\n\n// xlsxPrstGeom directly maps the prstGeom (Preset geometry). This element\n// specifies when a preset geometric shape should be used instead of a custom\n// geometric shape. The generating application should be able to render all\n// preset geometries enumerated in the ST_ShapeType list.\ntype xlsxPrstGeom struct {\n\tPrst string `xml:\"prst,attr\"`\n}\n\n// xlsxXfrm directly maps the xfrm (2D Transform for Graphic Frame). This\n// element specifies the transform to be applied to the corresponding graphic\n// frame. This transformation is applied to the graphic frame just as it would\n// be for a shape or group shape.\ntype xlsxXfrm struct {\n\tOff xlsxOff            `xml:\"a:off\"`\n\tExt xlsxPositiveSize2D `xml:\"a:ext\"`\n}\n\n// xlsxCNvPicPr directly maps the cNvPicPr (Non-Visual Picture Drawing\n// Properties). This element specifies the non-visual properties for the picture\n// canvas. These properties are to be used by the generating application to\n// determine how certain properties are to be changed for the picture object in\n// question.\ntype xlsxCNvPicPr struct {\n\tPicLocks xlsxPicLocks `xml:\"a:picLocks\"`\n}\n\n// directly maps the nvPicPr (Non-Visual Properties for a Picture). This element\n// specifies all non-visual properties for a picture. This element is a\n// container for the non-visual identification properties, shape properties and\n// application properties that are to be associated with a picture. This allows\n// for additional information that does not affect the appearance of the picture\n// to be stored.\ntype xlsxNvPicPr struct {\n\tCNvPr    xlsxCNvPr    `xml:\"xdr:cNvPr\"`\n\tCNvPicPr xlsxCNvPicPr `xml:\"xdr:cNvPicPr\"`\n}\n\n// xlsxCTSVGBlip specifies a graphic element in Scalable Vector Graphics (SVG)\n// format.\ntype xlsxCTSVGBlip struct {\n\tXMLNSaAVG string `xml:\"xmlns:asvg,attr\"`\n\tEmbed     string `xml:\"r:embed,attr\"`\n\tLink      string `xml:\"r:link,attr,omitempty\"`\n}\n\n// xlsxCTOfficeArtExtension used for future extensibility and is seen elsewhere\n// throughout the drawing area.\ntype xlsxCTOfficeArtExtension struct {\n\tXMLName xml.Name      `xml:\"a:ext\"`\n\tURI     string        `xml:\"uri,attr\"`\n\tSVGBlip xlsxCTSVGBlip `xml:\"asvg:svgBlip\"`\n}\n\n// xlsxEGOfficeArtExtensionList used for future extensibility and is seen\n// elsewhere throughout the drawing area.\ntype xlsxEGOfficeArtExtensionList struct {\n\tExt []xlsxCTOfficeArtExtension `xml:\"ext\"`\n}\n\n// xlsxBlipFill directly maps the blipFill (Picture Fill). This element\n// specifies the kind of picture fill that the picture object has. Because a\n// picture has a picture fill already by default, it is possible to have two\n// fills specified for a picture object.\ntype xlsxBlipFill struct {\n\tBlip    xlsxBlip    `xml:\"a:blip\"`\n\tStretch xlsxStretch `xml:\"a:stretch\"`\n}\n\n// xlsxLineProperties specifies the width of a line in EMUs. This simple type\n// has a minimum value of greater than or equal to 0. This simple type has a\n// maximum value of less than or equal to 20116800.\ntype xlsxLineProperties struct {\n\tW         int           `xml:\"w,attr,omitempty\"`\n\tSolidFill *xlsxInnerXML `xml:\"a:solidFill\"`\n}\n\n// xlsxSpPr directly maps the spPr (Shape Properties). This element specifies\n// the visual shape properties that can be applied to a picture. These are the\n// same properties that are allowed to describe the visual properties of a shape\n// but are used here to describe the visual appearance of a picture within a\n// document.\ntype xlsxSpPr struct {\n\tXfrm      xlsxXfrm           `xml:\"a:xfrm\"`\n\tPrstGeom  xlsxPrstGeom       `xml:\"a:prstGeom\"`\n\tSolidFill *aSolidFill        `xml:\"a:solidFill\"`\n\tLn        xlsxLineProperties `xml:\"a:ln\"`\n}\n\n// xlsxPic elements encompass the definition of pictures within the DrawingML\n// framework. While pictures are in many ways very similar to shapes they have\n// specific properties that are unique in order to optimize for picture-\n// specific scenarios.\ntype xlsxPic struct {\n\tNvPicPr  xlsxNvPicPr  `xml:\"xdr:nvPicPr\"`\n\tBlipFill xlsxBlipFill `xml:\"xdr:blipFill\"`\n\tSpPr     xlsxSpPr     `xml:\"xdr:spPr\"`\n}\n\n// xlsxFrom specifies the starting anchor.\ntype xlsxFrom struct {\n\tCol    int `xml:\"xdr:col\"`\n\tColOff int `xml:\"xdr:colOff\"`\n\tRow    int `xml:\"xdr:row\"`\n\tRowOff int `xml:\"xdr:rowOff\"`\n}\n\n// xlsxTo directly specifies the ending anchor.\ntype xlsxTo struct {\n\tCol    int `xml:\"xdr:col\"`\n\tColOff int `xml:\"xdr:colOff\"`\n\tRow    int `xml:\"xdr:row\"`\n\tRowOff int `xml:\"xdr:rowOff\"`\n}\n\n// xdrClientData directly maps the clientData element. An empty element which\n// specifies (via attributes) certain properties related to printing and\n// selection of the drawing object. The fLocksWithSheet attribute (either true\n// or false) determines whether to disable selection when the sheet is\n// protected, and fPrintsWithSheet attribute (either true or false) determines\n// whether the object is printed when the sheet is printed.\ntype xdrClientData struct {\n\tFLocksWithSheet  bool `xml:\"fLocksWithSheet,attr\"`\n\tFPrintsWithSheet bool `xml:\"fPrintsWithSheet,attr\"`\n}\n\n// xdrCellAnchor specifies a oneCellAnchor (One Cell Anchor Shape Size) and\n// twoCellAnchor (Two Cell Anchor Shape Size) placeholder for a group, a shape,\n// or a drawing element. It moves with cells and its extents are in EMU units.\ntype xdrCellAnchor struct {\n\tEditAs           string                  `xml:\"editAs,attr,omitempty\"`\n\tPos              *xlsxPoint2D            `xml:\"xdr:pos\"`\n\tFrom             *xlsxFrom               `xml:\"xdr:from\"`\n\tTo               *xlsxTo                 `xml:\"xdr:to\"`\n\tExt              *xlsxPositiveSize2D     `xml:\"xdr:ext\"`\n\tSp               *xdrSp                  `xml:\"xdr:sp\"`\n\tPic              *xlsxPic                `xml:\"xdr:pic,omitempty\"`\n\tGraphicFrame     string                  `xml:\",innerxml\"`\n\tAlternateContent []*xlsxAlternateContent `xml:\"mc:AlternateContent\"`\n\tClientData       *xdrClientData          `xml:\"xdr:clientData\"`\n}\n\n// xlsxCellAnchorPos defines the structure used to serialize the cell anchor for\n// adjust drawing object on inserting/deleting column/rows.\ntype xlsxCellAnchorPos struct {\n\tEditAs           string                  `xml:\"editAs,attr,omitempty\"`\n\tFrom             *xlsxFrom               `xml:\"xdr:from\"`\n\tTo               *xlsxTo                 `xml:\"xdr:to\"`\n\tPos              *xlsxInnerXML           `xml:\"xdr:pos\"`\n\tExt              *xlsxPositiveSize2D     `xml:\"xdr:ext\"`\n\tSp               *xlsxSp                 `xml:\"xdr:sp\"`\n\tGrpSp            *xlsxInnerXML           `xml:\"xdr:grpSp\"`\n\tGraphicFrame     *xlsxInnerXML           `xml:\"xdr:graphicFrame\"`\n\tCxnSp            *xlsxInnerXML           `xml:\"xdr:cxnSp\"`\n\tPic              *xlsxInnerXML           `xml:\"xdr:pic\"`\n\tContentPart      *xlsxInnerXML           `xml:\"xdr:contentPart\"`\n\tAlternateContent []*xlsxAlternateContent `xml:\"mc:AlternateContent\"`\n\tClientData       *xlsxInnerXML           `xml:\"xdr:clientData\"`\n}\n\n// xdrSp (Shape) directly maps the sp element. This element specifies the\n// existence of a single shape. A shape can either be a preset or a custom\n// geometry, defined using the SpreadsheetDrawingML framework. In addition to\n// a geometry each shape can have both visual and non-visual properties\n// attached. Text and corresponding styling information can also be attached\n// to a shape. This shape is specified along with all other shapes within\n// either the shape tree or group shape elements.\ntype xlsxSp struct {\n\tMacro      string `xml:\"macro,attr,omitempty\"`\n\tTextLink   string `xml:\"textlink,attr,omitempty\"`\n\tFLocksText bool   `xml:\"fLocksText,attr,omitempty\"`\n\tFPublished *bool  `xml:\"fPublished,attr\"`\n\tContent    string `xml:\",innerxml\"`\n}\n\n// xlsxPoint2D describes the position of a drawing element within a spreadsheet.\ntype xlsxPoint2D struct {\n\tXMLName xml.Name `xml:\"xdr:pos\"`\n\tX       int      `xml:\"x,attr\"`\n\tY       int      `xml:\"y,attr\"`\n}\n\n// xlsxWsDr directly maps the root element for a part of this content type shall\n// wsDr.\ntype xlsxWsDr struct {\n\tmu               sync.Mutex\n\tXMLName          xml.Name                `xml:\"xdr:wsDr\"`\n\tNS               string                  `xml:\"xmlns,attr,omitempty\"`\n\tA                string                  `xml:\"xmlns:a,attr,omitempty\"`\n\tXdr              string                  `xml:\"xmlns:xdr,attr,omitempty\"`\n\tR                string                  `xml:\"xmlns:r,attr,omitempty\"`\n\tAlternateContent []*xlsxAlternateContent `xml:\"mc:AlternateContent\"`\n\tAbsoluteAnchor   []*xdrCellAnchor        `xml:\"xdr:absoluteAnchor\"`\n\tOneCellAnchor    []*xdrCellAnchor        `xml:\"xdr:oneCellAnchor\"`\n\tTwoCellAnchor    []*xdrCellAnchor        `xml:\"xdr:twoCellAnchor\"`\n}\n\n// xlsxGraphicFrame (Graphic Frame) directly maps the xdr:graphicFrame element.\n// This element specifies the existence of a graphics frame. This frame contains\n// a graphic that was generated by an external source and needs a container in\n// which to be displayed on the slide surface.\ntype xlsxGraphicFrame struct {\n\tXMLName          xml.Name             `xml:\"xdr:graphicFrame\"`\n\tMacro            string               `xml:\"macro,attr\"`\n\tNvGraphicFramePr xlsxNvGraphicFramePr `xml:\"xdr:nvGraphicFramePr\"`\n\tXfrm             xlsxXfrm             `xml:\"xdr:xfrm\"`\n\tGraphic          *xlsxGraphic         `xml:\"a:graphic\"`\n}\n\n// xlsxNvGraphicFramePr (Non-Visual Properties for a Graphic Frame) directly\n// maps the xdr:nvGraphicFramePr element. This element specifies all non-visual\n// properties for a graphic frame. This element is a container for the non-\n// visual identification properties, shape properties and application properties\n// that are to be associated with a graphic frame. This allows for additional\n// information that does not affect the appearance of the graphic frame to be\n// stored.\ntype xlsxNvGraphicFramePr struct {\n\tCNvPr                *xlsxCNvPr `xml:\"xdr:cNvPr\"`\n\tChicNvGraphicFramePr string     `xml:\"xdr:cNvGraphicFramePr\"`\n}\n\n// xlsxGraphic (Graphic Object) directly maps the a:graphic element. This\n// element specifies the existence of a single graphic object. Document authors\n// should refer to this element when they wish to persist a graphical object of\n// some kind. The specification for this graphical object is provided entirely\n// by the document author and referenced within the graphicData child element.\ntype xlsxGraphic struct {\n\tGraphicData *xlsxGraphicData `xml:\"a:graphicData\"`\n}\n\n// xlsxGraphicData (Graphic Object Data) directly maps the a:graphicData\n// element. This element specifies the reference to a graphic object within the\n// document. This graphic object is provided entirely by the document authors\n// who choose to persist this data within the document.\ntype xlsxGraphicData struct {\n\tURI   string     `xml:\"uri,attr\"`\n\tChart *xlsxChart `xml:\"c:chart,omitempty\"`\n\tSle   *xlsxSle   `xml:\"sle:slicer\"`\n}\n\ntype xlsxSle struct {\n\tXMLNS string `xml:\"xmlns:sle,attr\"`\n\tName  string `xml:\"name,attr\"`\n}\n\n// xlsxChart (Chart) directly maps the c:chart element.\ntype xlsxChart struct {\n\tC   string `xml:\"xmlns:c,attr\"`\n\tRID string `xml:\"r:id,attr\"`\n\tR   string `xml:\"xmlns:r,attr\"`\n}\n\n// xdrSp (Shape) directly maps the xdr:sp element. This element specifies the\n// existence of a single shape. A shape can either be a preset or a custom\n// geometry, defined using the SpreadsheetDrawingML framework. In addition to a\n// geometry each shape can have both visual and non-visual properties attached.\n// Text and corresponding styling information can also be attached to a shape.\n// This shape is specified along with all other shapes within either the shape\n// tree or group shape elements.\ntype xdrSp struct {\n\tXMLName  xml.Name   `xml:\"xdr:sp\"`\n\tMacro    string     `xml:\"macro,attr\"`\n\tTextlink string     `xml:\"textlink,attr\"`\n\tNvSpPr   *xdrNvSpPr `xml:\"xdr:nvSpPr\"`\n\tSpPr     *xlsxSpPr  `xml:\"xdr:spPr\"`\n\tStyle    *xdrStyle  `xml:\"xdr:style\"`\n\tTxBody   *xdrTxBody `xml:\"xdr:txBody\"`\n}\n\n// xdrNvSpPr (Non-Visual Properties for a Shape) directly maps the xdr:nvSpPr\n// element. This element specifies all non-visual properties for a shape. This\n// element is a container for the non-visual identification properties, shape\n// properties and application properties that are to be associated with a shape.\n// This allows for additional information that does not affect the appearance of\n// the shape to be stored.\ntype xdrNvSpPr struct {\n\tCNvPr   *xlsxCNvPr  `xml:\"xdr:cNvPr\"`\n\tCNvSpPr *xdrCNvSpPr `xml:\"xdr:cNvSpPr\"`\n}\n\n// xdrCNvSpPr (Connection Non-Visual Shape Properties) directly maps the\n// xdr:cNvSpPr element. This element specifies the set of non-visual properties\n// for a connection shape. These properties specify all data about the\n// connection shape which do not affect its display within a spreadsheet.\ntype xdrCNvSpPr struct {\n\tTxBox bool `xml:\"txBox,attr\"`\n}\n\n// xdrStyle (Shape Style) directly maps the xdr:style element. The element\n// specifies the style that is applied to a shape and the corresponding\n// references for each of the style components such as lines and fills.\ntype xdrStyle struct {\n\tLnRef     *aRef     `xml:\"a:lnRef\"`\n\tFillRef   *aRef     `xml:\"a:fillRef\"`\n\tEffectRef *aRef     `xml:\"a:effectRef\"`\n\tFontRef   *aFontRef `xml:\"a:fontRef\"`\n}\n\n// aRef directly maps the a:lnRef, a:fillRef and a:effectRef element.\ntype aRef struct {\n\tIdx       int            `xml:\"idx,attr\"`\n\tScrgbClr  *aScrgbClr     `xml:\"a:scrgbClr\"`\n\tSchemeClr *attrValString `xml:\"a:schemeClr\"`\n\tSrgbClr   *attrValString `xml:\"a:srgbClr\"`\n}\n\n// aScrgbClr (RGB Color Model - Percentage Variant) directly maps the a:scrgbClr\n// element. This element specifies a color using the red, green, blue RGB color\n// model. Each component, red, green, and blue is expressed as a percentage from\n// 0% to 100%. A linear gamma of 1.0 is assumed.\ntype aScrgbClr struct {\n\tR float64 `xml:\"r,attr\"`\n\tG float64 `xml:\"g,attr\"`\n\tB float64 `xml:\"b,attr\"`\n}\n\n// aFontRef (Font Reference) directly maps the a:fontRef element. This element\n// represents a reference to a themed font. When used it specifies which themed\n// font to use along with a choice of color.\ntype aFontRef struct {\n\tIdx       string         `xml:\"idx,attr\"`\n\tSchemeClr *attrValString `xml:\"a:schemeClr\"`\n}\n\n// xdrTxBody (Shape Text Body) directly maps the xdr:txBody element. This\n// element specifies the existence of text to be contained within the\n// corresponding shape. All visible text and visible text related properties are\n// contained within this element. There can be multiple paragraphs and within\n// paragraphs multiple runs of text.\ntype xdrTxBody struct {\n\tBodyPr *aBodyPr `xml:\"a:bodyPr\"`\n\tP      []*aP    `xml:\"a:p\"`\n}\n\n// Picture maps the format settings of the picture.\ntype Picture struct {\n\tExtension  string\n\tFile       []byte\n\tFormat     *GraphicOptions\n\tInsertType PictureInsertType\n}\n\n// GraphicOptions directly maps the format settings of the picture.\ntype GraphicOptions struct {\n\tAltText             string\n\tName                string\n\tPrintObject         *bool\n\tLocked              *bool\n\tLockAspectRatio     bool\n\tAutoFit             bool\n\tAutoFitIgnoreAspect bool\n\tOffsetX             int\n\tOffsetY             int\n\tScaleX              float64\n\tScaleY              float64\n\tHyperlink           string\n\tHyperlinkType       string\n\tPositioning         string\n}\n\n// Shape directly maps the format settings of the shape.\ntype Shape struct {\n\tCell      string\n\tType      string\n\tMacro     string\n\tWidth     uint\n\tHeight    uint\n\tFormat    GraphicOptions\n\tFill      Fill\n\tLine      ShapeLine\n\tParagraph []RichTextRun\n}\n\n// ShapeLine directly maps the line settings of the shape.\ntype ShapeLine struct {\n\tColor string\n\tWidth *float64\n}\n"
  },
  {
    "path": "xmlMetaData.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// xlsxMetadata directly maps the metadata element. A cell in a spreadsheet\n// application can have metadata associated with it. Metadata is just a set of\n// additional properties about the particular cell, and this metadata is stored\n// in the metadata xml part. There are two types of metadata: cell metadata and\n// value metadata. Cell metadata contains information about the cell itself,\n// and this metadata can be carried along with the cell as it moves\n// (insert, shift, copy/paste, merge, unmerge, etc). Value metadata is\n// information about the value of a particular cell. Value metadata properties\n// can be propagated along with the value as it is referenced in formulas.\ntype xlsxMetadata struct {\n\tXMLName         xml.Name             `xml:\"metadata\"`\n\tMetadataTypes   *xlsxInnerXML        `xml:\"metadataTypes\"`\n\tMetadataStrings *xlsxInnerXML        `xml:\"metadataStrings\"`\n\tMdxMetadata     *xlsxInnerXML        `xml:\"mdxMetadata\"`\n\tFutureMetadata  []xlsxFutureMetadata `xml:\"futureMetadata\"`\n\tCellMetadata    *xlsxMetadataBlocks  `xml:\"cellMetadata\"`\n\tValueMetadata   *xlsxMetadataBlocks  `xml:\"valueMetadata\"`\n\tExtLst          *xlsxInnerXML        `xml:\"extLst\"`\n}\n\n// xlsxFutureMetadata directly maps the futureMetadata element. This element\n// represents future metadata information.\ntype xlsxFutureMetadata struct {\n\tBk     []xlsxFutureMetadataBlock `xml:\"bk\"`\n\tExtLst *xlsxInnerXML             `xml:\"extLst\"`\n}\n\n// xlsxFutureMetadataBlock directly maps the kb element. This element represents\n// a block of future metadata information. This is a location for storing\n// feature extension information.\ntype xlsxFutureMetadataBlock struct {\n\tExtLst *xlsxInnerXML `xml:\"extLst\"`\n}\n\n// xlsxMetadataBlocks directly maps the metadata element. This element\n// represents cell metadata information. Cell metadata is information metadata\n// about a specific cell, and it stays tied to that cell position.\ntype xlsxMetadataBlocks struct {\n\tCount int                 `xml:\"count,attr,omitempty\"`\n\tBk    []xlsxMetadataBlock `xml:\"bk\"`\n}\n\n// xlsxMetadataBlock directly maps the bk element. This element represents a\n// block of metadata records.\ntype xlsxMetadataBlock struct {\n\tRc []xlsxMetadataRecord `xml:\"rc\"`\n}\n\n// xlsxMetadataRecord directly maps the rc element. This element represents a\n// reference to a specific metadata record.\ntype xlsxMetadataRecord struct {\n\tT int `xml:\"t,attr\"`\n\tV int `xml:\"v,attr\"`\n}\n\n// xlsxRichValueData directly maps the rvData element that specifies rich value\n// data.\ntype xlsxRichValueData struct {\n\tXMLName xml.Name        `xml:\"rvData\"`\n\tCount   int             `xml:\"count,attr,omitempty\"`\n\tRv      []xlsxRichValue `xml:\"rv\"`\n\tExtLst  *xlsxInnerXML   `xml:\"extLst\"`\n}\n\n// xlsxRichValue directly maps the rv element that specifies rich value data\n// information for a single rich value\ntype xlsxRichValue struct {\n\tS  int           `xml:\"s,attr\"`\n\tV  []string      `xml:\"v\"`\n\tFb *xlsxInnerXML `xml:\"fb\"`\n}\n\n// xlsxRichValueRels directly maps the richValueRels element. This element that\n// specifies a list of rich value relationships.\ntype xlsxRichValueRels struct {\n\tXMLName xml.Name                       `xml:\"richValueRels\"`\n\tRels    []xlsxRichValueRelRelationship `xml:\"rel\"`\n\tExtLst  *xlsxInnerXML                  `xml:\"extLst\"`\n}\n\n// xlsxRichValueRelRelationship directly maps the rel element. This element\n// specifies a relationship for a rich value property.\ntype xlsxRichValueRelRelationship struct {\n\tID string `xml:\"id,attr\"`\n}\n\n// xlsxRichValueStructures directly maps the rvStructures element. This element\n// specifies rich value structures, which contain lists of rich value keys and\n// the data types for the corresponding rich value data.\ntype xlsxRichValueStructures struct {\n\tXMLName xml.Name                 `xml:\"rvStructures\"`\n\tCount   int                      `xml:\"count,attr,omitempty\"`\n\tS       []xlsxRichValueStructure `xml:\"s\"`\n\tExtLst  *xlsxInnerXML            `xml:\"extLst\"`\n}\n\n// xlsxRichValueStructure directly maps the s element. This element specifies\n// the list of rich value structures.\ntype xlsxRichValueStructure struct {\n\tT string             `xml:\"t,attr\"`\n\tK []xlsxRichValueKey `xml:\"k\"`\n}\n\n// xlsxRichValueKey directly maps the k element. This element specifies the rich\n// value key.\ntype xlsxRichValueKey struct {\n\tN string `xml:\"n,attr\"`\n\tT string `xml:\"t,attr,omitempty\"`\n}\n\n// xlsxWebImagesSupportingRichData directly maps the webImagesSrd element. This\n// element specifies a list of sets of properties associated with web image rich\n// values.\ntype xlsxWebImagesSupportingRichData struct {\n\tXMLName     xml.Name                         `xml:\"webImagesSrd\"`\n\tWebImageSrd []xlsxWebImageSupportingRichData `xml:\"webImageSrd\"`\n\tExtLst      *xlsxInnerXML                    `xml:\"extLst\"`\n}\n\n// xlsxWebImageSupportingRichData directly maps the webImageSrd element. This\n// element specifies a set of properties for a web image rich value.\ntype xlsxWebImageSupportingRichData struct {\n\tAddress           xlsxExternalReference `xml:\"address\"`\n\tMoreImagesAddress xlsxExternalReference `xml:\"moreImagesAddress\"`\n\tBlip              xlsxExternalReference `xml:\"blip\"`\n}\n"
  },
  {
    "path": "xmlPivotCache.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// xlsxPivotCacheDefinition represents the pivotCacheDefinition part. This part\n// defines each field in the source data, including the name, the string\n// resources of the instance data (for shared items), and information about\n// the type of data that appears in the field.\ntype xlsxPivotCacheDefinition struct {\n\tXMLName               xml.Name               `xml:\"http://schemas.openxmlformats.org/spreadsheetml/2006/main pivotCacheDefinition\"`\n\tRID                   string                 `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty\"`\n\tInvalid               bool                   `xml:\"invalid,attr,omitempty\"`\n\tSaveData              bool                   `xml:\"saveData,attr\"`\n\tRefreshOnLoad         bool                   `xml:\"refreshOnLoad,attr,omitempty\"`\n\tOptimizeMemory        bool                   `xml:\"optimizeMemory,attr,omitempty\"`\n\tEnableRefresh         bool                   `xml:\"enableRefresh,attr,omitempty\"`\n\tRefreshedBy           string                 `xml:\"refreshedBy,attr,omitempty\"`\n\tRefreshedDate         float64                `xml:\"refreshedDate,attr,omitempty\"`\n\tRefreshedDateIso      float64                `xml:\"refreshedDateIso,attr,omitempty\"`\n\tBackgroundQuery       bool                   `xml:\"backgroundQuery,attr\"`\n\tMissingItemsLimit     int                    `xml:\"missingItemsLimit,attr,omitempty\"`\n\tCreatedVersion        int                    `xml:\"createdVersion,attr,omitempty\"`\n\tRefreshedVersion      int                    `xml:\"refreshedVersion,attr,omitempty\"`\n\tMinRefreshableVersion int                    `xml:\"minRefreshableVersion,attr,omitempty\"`\n\tRecordCount           int                    `xml:\"recordCount,attr,omitempty\"`\n\tUpgradeOnRefresh      bool                   `xml:\"upgradeOnRefresh,attr,omitempty\"`\n\tTupleCacheAttr        bool                   `xml:\"tupleCache,attr,omitempty\"`\n\tSupportSubquery       bool                   `xml:\"supportSubquery,attr,omitempty\"`\n\tSupportAdvancedDrill  bool                   `xml:\"supportAdvancedDrill,attr,omitempty\"`\n\tCacheSource           *xlsxCacheSource       `xml:\"cacheSource\"`\n\tCacheFields           *xlsxCacheFields       `xml:\"cacheFields\"`\n\tCacheHierarchies      *xlsxCacheHierarchies  `xml:\"cacheHierarchies\"`\n\tKpis                  *xlsxKpis              `xml:\"kpis\"`\n\tTupleCache            *xlsxTupleCache        `xml:\"tupleCache\"`\n\tCalculatedItems       *xlsxCalculatedItems   `xml:\"calculatedItems\"`\n\tCalculatedMembers     *xlsxCalculatedMembers `xml:\"calculatedMembers\"`\n\tDimensions            *xlsxDimensions        `xml:\"dimensions\"`\n\tMeasureGroups         *xlsxMeasureGroups     `xml:\"measureGroups\"`\n\tMaps                  *xlsxMaps              `xml:\"maps\"`\n\tExtLst                *xlsxExtLst            `xml:\"extLst\"`\n}\n\n// xlsxCacheSource represents the description of data source whose data is\n// stored in the pivot cache. The data source refers to the underlying rows or\n// database records that provide the data for a PivotTable. You can create a\n// PivotTable report from a SpreadsheetML table, an external database\n// (including OLAP cubes), multiple SpreadsheetML worksheets, or another\n// PivotTable.\ntype xlsxCacheSource struct {\n\tType            string               `xml:\"type,attr\"`\n\tConnectionID    int                  `xml:\"connectionId,attr,omitempty\"`\n\tWorksheetSource *xlsxWorksheetSource `xml:\"worksheetSource\"`\n\tConsolidation   *xlsxConsolidation   `xml:\"consolidation\"`\n\tExtLst          *xlsxExtLst          `xml:\"extLst\"`\n}\n\n// xlsxWorksheetSource represents the location of the source of the data that\n// is stored in the cache.\ntype xlsxWorksheetSource struct {\n\tRID   string `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty\"`\n\tRef   string `xml:\"ref,attr,omitempty\"`\n\tName  string `xml:\"name,attr,omitempty\"`\n\tSheet string `xml:\"sheet,attr,omitempty\"`\n}\n\n// xlsxConsolidation represents the description of the PivotCache source using\n// multiple consolidation ranges. This element is used when the source of the\n// PivotTable is a collection of ranges in the workbook. The ranges are\n// specified in the rangeSets collection. The logic for how the application\n// consolidates the data in the ranges is application- defined.\ntype xlsxConsolidation struct{}\n\n// xlsxCacheFields represents the collection of field definitions in the\n// source data.\ntype xlsxCacheFields struct {\n\tCount      int               `xml:\"count,attr\"`\n\tCacheField []*xlsxCacheField `xml:\"cacheField\"`\n}\n\n// xlsxCacheField represent a single field in the PivotCache. This definition\n// contains information about the field, such as its source, data type, and\n// location within a level or hierarchy. The sharedItems element stores\n// additional information about the data in this field. If there are no shared\n// items, then values are stored directly in the pivotCacheRecords part.\ntype xlsxCacheField struct {\n\tName                string           `xml:\"name,attr\"`\n\tCaption             string           `xml:\"caption,attr,omitempty\"`\n\tPropertyName        string           `xml:\"propertyName,attr,omitempty\"`\n\tServerField         bool             `xml:\"serverField,attr,omitempty\"`\n\tUniqueList          bool             `xml:\"uniqueList,attr,omitempty\"`\n\tNumFmtID            int              `xml:\"numFmtId,attr\"`\n\tFormula             string           `xml:\"formula,attr,omitempty\"`\n\tSQLType             int              `xml:\"sqlType,attr,omitempty\"`\n\tHierarchy           int              `xml:\"hierarchy,attr,omitempty\"`\n\tLevel               int              `xml:\"level,attr,omitempty\"`\n\tDatabaseField       bool             `xml:\"databaseField,attr,omitempty\"`\n\tMappingCount        int              `xml:\"mappingCount,attr,omitempty\"`\n\tMemberPropertyField bool             `xml:\"memberPropertyField,attr,omitempty\"`\n\tSharedItems         *xlsxSharedItems `xml:\"sharedItems\"`\n\tFieldGroup          *xlsxFieldGroup  `xml:\"fieldGroup\"`\n\tMpMap               *xlsxX           `xml:\"mpMap\"`\n\tExtLst              *xlsxExtLst      `xml:\"extLst\"`\n}\n\n// xlsxSharedItems represents the collection of unique items for a field in\n// the PivotCacheDefinition. The sharedItems complex type stores data type and\n// formatting information about the data in a field. Items in the\n// PivotCacheDefinition can be shared in order to reduce the redundancy of\n// those values that are referenced in multiple places across all the\n// PivotTable parts.\ntype xlsxSharedItems struct {\n\tContainsSemiMixedTypes bool           `xml:\"containsSemiMixedTypes,attr,omitempty\"`\n\tContainsNonDate        bool           `xml:\"containsNonDate,attr,omitempty\"`\n\tContainsDate           bool           `xml:\"containsDate,attr,omitempty\"`\n\tContainsString         bool           `xml:\"containsString,attr,omitempty\"`\n\tContainsBlank          bool           `xml:\"containsBlank,attr,omitempty\"`\n\tContainsMixedTypes     bool           `xml:\"containsMixedTypes,attr,omitempty\"`\n\tContainsNumber         bool           `xml:\"containsNumber,attr,omitempty\"`\n\tContainsInteger        bool           `xml:\"containsInteger,attr,omitempty\"`\n\tMinValue               float64        `xml:\"minValue,attr,omitempty\"`\n\tMaxValue               float64        `xml:\"maxValue,attr,omitempty\"`\n\tMinDate                string         `xml:\"minDate,attr,omitempty\"`\n\tMaxDate                string         `xml:\"maxDate,attr,omitempty\"`\n\tCount                  int            `xml:\"count,attr\"`\n\tLongText               bool           `xml:\"longText,attr,omitempty\"`\n\tM                      []xlsxMissing  `xml:\"m\"`\n\tN                      []xlsxNumber   `xml:\"n\"`\n\tB                      []xlsxBoolean  `xml:\"b\"`\n\tE                      []xlsxError    `xml:\"e\"`\n\tS                      []xlsxString   `xml:\"s\"`\n\tD                      []xlsxDateTime `xml:\"d\"`\n}\n\n// xlsxMissing represents a value that was not specified.\ntype xlsxMissing struct{}\n\n// xlsxNumber represents a numeric value in the PivotTable.\ntype xlsxNumber struct {\n\tV    float64     `xml:\"v,attr\"`\n\tU    bool        `xml:\"u,attr,omitempty\"`\n\tF    bool        `xml:\"f,attr,omitempty\"`\n\tC    string      `xml:\"c,attr,omitempty\"`\n\tCp   int         `xml:\"cp,attr,omitempty\"`\n\tIn   int         `xml:\"in,attr,omitempty\"`\n\tBc   string      `xml:\"bc,attr,omitempty\"`\n\tFc   string      `xml:\"fc,attr,omitempty\"`\n\tI    bool        `xml:\"i,attr,omitempty\"`\n\tUn   bool        `xml:\"un,attr,omitempty\"`\n\tSt   bool        `xml:\"st,attr,omitempty\"`\n\tB    bool        `xml:\"b,attr,omitempty\"`\n\tTpls *xlsxTuples `xml:\"tpls\"`\n\tX    *attrValInt `xml:\"x\"`\n}\n\n// xlsxTuples represents members for the OLAP sheet data entry, also known as\n// a tuple.\ntype xlsxTuples struct{}\n\n// xlsxBoolean represents a boolean value for an item in the PivotTable.\ntype xlsxBoolean struct{}\n\n// xlsxError represents an error value. The use of this item indicates that an\n// error value is present in the PivotTable source. The error is recorded in\n// the value attribute.\ntype xlsxError struct{}\n\n// xlsxString represents a character value in a PivotTable.\ntype xlsxString struct {\n\tV    string      `xml:\"v,attr\"`\n\tU    bool        `xml:\"u,attr,omitempty\"`\n\tF    bool        `xml:\"f,attr,omitempty\"`\n\tC    string      `xml:\"c,attr,omitempty\"`\n\tCp   int         `xml:\"cp,attr,omitempty\"`\n\tIn   int         `xml:\"in,attr,omitempty\"`\n\tBc   string      `xml:\"bc,attr,omitempty\"`\n\tFc   string      `xml:\"fc,attr,omitempty\"`\n\tI    bool        `xml:\"i,attr,omitempty\"`\n\tUn   bool        `xml:\"un,attr,omitempty\"`\n\tSt   bool        `xml:\"st,attr,omitempty\"`\n\tB    bool        `xml:\"b,attr,omitempty\"`\n\tTpls *xlsxTuples `xml:\"tpls\"`\n\tX    *attrValInt `xml:\"x\"`\n}\n\n// xlsxDateTime represents a date-time value in the PivotTable.\ntype xlsxDateTime struct{}\n\n// xlsxFieldGroup represents the collection of properties for a field group.\ntype xlsxFieldGroup struct{}\n\n// xlsxCacheHierarchies represents the collection of OLAP hierarchies in the\n// PivotCache.\ntype xlsxCacheHierarchies struct{}\n\n// xlsxKpis represents the collection of Key Performance Indicators (KPIs)\n// defined on the OLAP server and stored in the PivotCache.\ntype xlsxKpis struct{}\n\n// xlsxTupleCache represents the cache of OLAP sheet data members, or tuples.\ntype xlsxTupleCache struct{}\n\n// xlsxCalculatedItems represents the collection of calculated items.\ntype xlsxCalculatedItems struct{}\n\n// xlsxCalculatedMembers represents the collection of calculated members in an\n// OLAP PivotTable.\ntype xlsxCalculatedMembers struct{}\n\n// xlsxDimensions represents the collection of PivotTable OLAP dimensions.\ntype xlsxDimensions struct{}\n\n// xlsxMeasureGroups represents the collection of PivotTable OLAP measure\n// groups.\ntype xlsxMeasureGroups struct{}\n\n// xlsxMaps represents the PivotTable OLAP measure group - Dimension maps.\ntype xlsxMaps struct{}\n\n// xlsxX14PivotCacheDefinition specifies the extended properties of a pivot\n// table cache definition.\ntype xlsxX14PivotCacheDefinition struct {\n\tXMLName      xml.Name `xml:\"x14:pivotCacheDefinition\"`\n\tPivotCacheID int      `xml:\"pivotCacheId,attr\"`\n}\n\n// decodeX14PivotCacheDefinition defines the structure used to parse the\n// x14:pivotCacheDefinition element of a pivot table cache.\ntype decodeX14PivotCacheDefinition struct {\n\tXMLName      xml.Name `xml:\"pivotCacheDefinition\"`\n\tPivotCacheID int      `xml:\"pivotCacheId,attr\"`\n}\n"
  },
  {
    "path": "xmlPivotTable.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// xlsxPivotTableDefinition represents the PivotTable root element for\n// non-null PivotTables. There exists one pivotTableDefinition for each\n// PivotTableDefinition part\ntype xlsxPivotTableDefinition struct {\n\tXMLName                 xml.Name                 `xml:\"http://schemas.openxmlformats.org/spreadsheetml/2006/main pivotTableDefinition\"`\n\tName                    string                   `xml:\"name,attr\"`\n\tCacheID                 int                      `xml:\"cacheId,attr\"`\n\tApplyNumberFormats      bool                     `xml:\"applyNumberFormats,attr,omitempty\"`\n\tApplyBorderFormats      bool                     `xml:\"applyBorderFormats,attr,omitempty\"`\n\tApplyFontFormats        bool                     `xml:\"applyFontFormats,attr,omitempty\"`\n\tApplyPatternFormats     bool                     `xml:\"applyPatternFormats,attr,omitempty\"`\n\tApplyAlignmentFormats   bool                     `xml:\"applyAlignmentFormats,attr,omitempty\"`\n\tApplyWidthHeightFormats bool                     `xml:\"applyWidthHeightFormats,attr,omitempty\"`\n\tDataOnRows              bool                     `xml:\"dataOnRows,attr,omitempty\"`\n\tDataPosition            int                      `xml:\"dataPosition,attr,omitempty\"`\n\tDataCaption             string                   `xml:\"dataCaption,attr\"`\n\tGrandTotalCaption       string                   `xml:\"grandTotalCaption,attr,omitempty\"`\n\tErrorCaption            string                   `xml:\"errorCaption,attr,omitempty\"`\n\tShowError               *bool                    `xml:\"showError,attr\"`\n\tMissingCaption          string                   `xml:\"missingCaption,attr,omitempty\"`\n\tShowMissing             bool                     `xml:\"showMissing,attr,omitempty\"`\n\tPageStyle               string                   `xml:\"pageStyle,attr,omitempty\"`\n\tPivotTableStyle         string                   `xml:\"pivotTableStyle,attr,omitempty\"`\n\tVacatedStyle            string                   `xml:\"vacatedStyle,attr,omitempty\"`\n\tTag                     string                   `xml:\"tag,attr,omitempty\"`\n\tUpdatedVersion          int                      `xml:\"updatedVersion,attr,omitempty\"`\n\tMinRefreshableVersion   int                      `xml:\"minRefreshableVersion,attr,omitempty\"`\n\tAsteriskTotals          bool                     `xml:\"asteriskTotals,attr,omitempty\"`\n\tShowItems               bool                     `xml:\"showItems,attr,omitempty\"`\n\tEditData                bool                     `xml:\"editData,attr,omitempty\"`\n\tDisableFieldList        bool                     `xml:\"disableFieldList,attr,omitempty\"`\n\tShowCalcMbrs            bool                     `xml:\"showCalcMbrs,attr,omitempty\"`\n\tVisualTotals            bool                     `xml:\"visualTotals,attr,omitempty\"`\n\tShowMultipleLabel       bool                     `xml:\"showMultipleLabel,attr,omitempty\"`\n\tShowDataDropDown        bool                     `xml:\"showDataDropDown,attr,omitempty\"`\n\tShowDrill               *bool                    `xml:\"showDrill,attr\"`\n\tPrintDrill              bool                     `xml:\"printDrill,attr,omitempty\"`\n\tShowMemberPropertyTips  bool                     `xml:\"showMemberPropertyTips,attr,omitempty\"`\n\tShowDataTips            bool                     `xml:\"showDataTips,attr,omitempty\"`\n\tEnableWizard            bool                     `xml:\"enableWizard,attr,omitempty\"`\n\tEnableDrill             bool                     `xml:\"enableDrill,attr,omitempty\"`\n\tEnableFieldProperties   bool                     `xml:\"enableFieldProperties,attr,omitempty\"`\n\tPreserveFormatting      bool                     `xml:\"preserveFormatting,attr,omitempty\"`\n\tUseAutoFormatting       *bool                    `xml:\"useAutoFormatting,attr\"`\n\tPageWrap                int                      `xml:\"pageWrap,attr,omitempty\"`\n\tPageOverThenDown        *bool                    `xml:\"pageOverThenDown,attr\"`\n\tSubtotalHiddenItems     bool                     `xml:\"subtotalHiddenItems,attr,omitempty\"`\n\tRowGrandTotals          *bool                    `xml:\"rowGrandTotals,attr\"`\n\tColGrandTotals          *bool                    `xml:\"colGrandTotals,attr\"`\n\tFieldPrintTitles        bool                     `xml:\"fieldPrintTitles,attr,omitempty\"`\n\tItemPrintTitles         bool                     `xml:\"itemPrintTitles,attr,omitempty\"`\n\tMergeItem               *bool                    `xml:\"mergeItem,attr\"`\n\tShowDropZones           bool                     `xml:\"showDropZones,attr,omitempty\"`\n\tCreatedVersion          int                      `xml:\"createdVersion,attr,omitempty\"`\n\tIndent                  int                      `xml:\"indent,attr,omitempty\"`\n\tShowEmptyRow            bool                     `xml:\"showEmptyRow,attr,omitempty\"`\n\tShowEmptyCol            bool                     `xml:\"showEmptyCol,attr,omitempty\"`\n\tShowHeaders             bool                     `xml:\"showHeaders,attr,omitempty\"`\n\tCompact                 *bool                    `xml:\"compact,attr\"`\n\tOutline                 *bool                    `xml:\"outline,attr\"`\n\tOutlineData             bool                     `xml:\"outlineData,attr,omitempty\"`\n\tCompactData             *bool                    `xml:\"compactData,attr\"`\n\tPublished               bool                     `xml:\"published,attr,omitempty\"`\n\tGridDropZones           bool                     `xml:\"gridDropZones,attr,omitempty\"`\n\tImmersive               bool                     `xml:\"immersive,attr,omitempty\"`\n\tMultipleFieldFilters    bool                     `xml:\"multipleFieldFilters,attr,omitempty\"`\n\tChartFormat             int                      `xml:\"chartFormat,attr,omitempty\"`\n\tRowHeaderCaption        string                   `xml:\"rowHeaderCaption,attr,omitempty\"`\n\tColHeaderCaption        string                   `xml:\"colHeaderCaption,attr,omitempty\"`\n\tFieldListSortAscending  bool                     `xml:\"fieldListSortAscending,attr,omitempty\"`\n\tMdxSubqueries           bool                     `xml:\"mdxSubqueries,attr,omitempty\"`\n\tCustomListSort          bool                     `xml:\"customListSort,attr,omitempty\"`\n\tLocation                *xlsxLocation            `xml:\"location\"`\n\tPivotFields             *xlsxPivotFields         `xml:\"pivotFields\"`\n\tRowFields               *xlsxRowFields           `xml:\"rowFields\"`\n\tRowItems                *xlsxRowItems            `xml:\"rowItems\"`\n\tColFields               *xlsxColFields           `xml:\"colFields\"`\n\tColItems                *xlsxColItems            `xml:\"colItems\"`\n\tPageFields              *xlsxPageFields          `xml:\"pageFields\"`\n\tDataFields              *xlsxDataFields          `xml:\"dataFields\"`\n\tConditionalFormats      *xlsxConditionalFormats  `xml:\"conditionalFormats\"`\n\tPivotTableStyleInfo     *xlsxPivotTableStyleInfo `xml:\"pivotTableStyleInfo\"`\n}\n\n// xlsxLocation represents location information for the PivotTable.\ntype xlsxLocation struct {\n\tRef            string `xml:\"ref,attr\"`\n\tFirstHeaderRow int    `xml:\"firstHeaderRow,attr\"`\n\tFirstDataRow   int    `xml:\"firstDataRow,attr\"`\n\tFirstDataCol   int    `xml:\"firstDataCol,attr\"`\n\tRowPageCount   int    `xml:\"rowPageCount,attr,omitempty\"`\n\tColPageCount   int    `xml:\"colPageCount,attr,omitempty\"`\n}\n\n// xlsxPivotFields represents the collection of fields that appear on the\n// PivotTable.\ntype xlsxPivotFields struct {\n\tCount      int               `xml:\"count,attr\"`\n\tPivotField []*xlsxPivotField `xml:\"pivotField\"`\n}\n\n// xlsxPivotField represents a single field in the PivotTable. This element\n// contains information about the field, including the collection of items in\n// the field.\ntype xlsxPivotField struct {\n\tName                         string             `xml:\"name,attr,omitempty\"`\n\tAxis                         string             `xml:\"axis,attr,omitempty\"`\n\tDataField                    bool               `xml:\"dataField,attr,omitempty\"`\n\tSubtotalCaption              string             `xml:\"subtotalCaption,attr,omitempty\"`\n\tShowDropDowns                bool               `xml:\"showDropDowns,attr,omitempty\"`\n\tHiddenLevel                  bool               `xml:\"hiddenLevel,attr,omitempty\"`\n\tUniqueMemberProperty         string             `xml:\"uniqueMemberProperty,attr,omitempty\"`\n\tCompact                      *bool              `xml:\"compact,attr\"`\n\tAllDrilled                   bool               `xml:\"allDrilled,attr,omitempty\"`\n\tNumFmtID                     string             `xml:\"numFmtId,attr,omitempty\"`\n\tOutline                      *bool              `xml:\"outline,attr\"`\n\tSubtotalTop                  bool               `xml:\"subtotalTop,attr,omitempty\"`\n\tDragToRow                    bool               `xml:\"dragToRow,attr,omitempty\"`\n\tDragToCol                    bool               `xml:\"dragToCol,attr,omitempty\"`\n\tMultipleItemSelectionAllowed bool               `xml:\"multipleItemSelectionAllowed,attr,omitempty\"`\n\tDragToPage                   bool               `xml:\"dragToPage,attr,omitempty\"`\n\tDragToData                   bool               `xml:\"dragToData,attr,omitempty\"`\n\tDragOff                      bool               `xml:\"dragOff,attr,omitempty\"`\n\tShowAll                      bool               `xml:\"showAll,attr\"`\n\tInsertBlankRow               bool               `xml:\"insertBlankRow,attr,omitempty\"`\n\tServerField                  bool               `xml:\"serverField,attr,omitempty\"`\n\tInsertPageBreak              bool               `xml:\"insertPageBreak,attr,omitempty\"`\n\tAutoShow                     bool               `xml:\"autoShow,attr,omitempty\"`\n\tTopAutoShow                  bool               `xml:\"topAutoShow,attr,omitempty\"`\n\tHideNewItems                 bool               `xml:\"hideNewItems,attr,omitempty\"`\n\tMeasureFilter                bool               `xml:\"measureFilter,attr,omitempty\"`\n\tIncludeNewItemsInFilter      bool               `xml:\"includeNewItemsInFilter,attr,omitempty\"`\n\tItemPageCount                int                `xml:\"itemPageCount,attr,omitempty\"`\n\tSortType                     string             `xml:\"sortType,attr,omitempty\"`\n\tDataSourceSort               bool               `xml:\"dataSourceSort,attr,omitempty\"`\n\tNonAutoSortDefault           bool               `xml:\"nonAutoSortDefault,attr,omitempty\"`\n\tRankBy                       int                `xml:\"rankBy,attr,omitempty\"`\n\tDefaultSubtotal              *bool              `xml:\"defaultSubtotal,attr\"`\n\tSumSubtotal                  bool               `xml:\"sumSubtotal,attr,omitempty\"`\n\tCountASubtotal               bool               `xml:\"countASubtotal,attr,omitempty\"`\n\tAvgSubtotal                  bool               `xml:\"avgSubtotal,attr,omitempty\"`\n\tMaxSubtotal                  bool               `xml:\"maxSubtotal,attr,omitempty\"`\n\tMinSubtotal                  bool               `xml:\"minSubtotal,attr,omitempty\"`\n\tProductSubtotal              bool               `xml:\"productSubtotal,attr,omitempty\"`\n\tCountSubtotal                bool               `xml:\"countSubtotal,attr,omitempty\"`\n\tStdDevSubtotal               bool               `xml:\"stdDevSubtotal,attr,omitempty\"`\n\tStdDevPSubtotal              bool               `xml:\"stdDevPSubtotal,attr,omitempty\"`\n\tVarSubtotal                  bool               `xml:\"varSubtotal,attr,omitempty\"`\n\tVarPSubtotal                 bool               `xml:\"varPSubtotal,attr,omitempty\"`\n\tShowPropCell                 bool               `xml:\"showPropCell,attr,omitempty\"`\n\tShowPropTip                  bool               `xml:\"showPropTip,attr,omitempty\"`\n\tShowPropAsCaption            bool               `xml:\"showPropAsCaption,attr,omitempty\"`\n\tDefaultAttributeDrillState   bool               `xml:\"defaultAttributeDrillState,attr,omitempty\"`\n\tItems                        *xlsxItems         `xml:\"items\"`\n\tAutoSortScope                *xlsxAutoSortScope `xml:\"autoSortScope\"`\n\tExtLst                       *xlsxExtLst        `xml:\"extLst\"`\n}\n\n// xlsxItems represents the collection of items in a PivotTable field. The\n// items in the collection are ordered by index. Items represent the unique\n// entries from the field in the source data.\ntype xlsxItems struct {\n\tCount int         `xml:\"count,attr\"`\n\tItem  []*xlsxItem `xml:\"item\"`\n}\n\n// xlsxItem represents a single item in PivotTable field.\ntype xlsxItem struct {\n\tN  string `xml:\"n,attr,omitempty\"`\n\tT  string `xml:\"t,attr,omitempty\"`\n\tH  bool   `xml:\"h,attr,omitempty\"`\n\tS  bool   `xml:\"s,attr,omitempty\"`\n\tSD bool   `xml:\"sd,attr,omitempty\"`\n\tF  bool   `xml:\"f,attr,omitempty\"`\n\tM  bool   `xml:\"m,attr,omitempty\"`\n\tC  bool   `xml:\"c,attr,omitempty\"`\n\tX  *int   `xml:\"x,attr,omitempty\"`\n\tD  bool   `xml:\"d,attr,omitempty\"`\n\tE  bool   `xml:\"e,attr,omitempty\"`\n}\n\n// xlsxAutoSortScope represents the sorting scope for the PivotTable.\ntype xlsxAutoSortScope struct{}\n\n// xlsxRowFields represents the collection of row fields for the PivotTable.\ntype xlsxRowFields struct {\n\tCount int          `xml:\"count,attr\"`\n\tField []*xlsxField `xml:\"field\"`\n}\n\n// xlsxField represents a generic field that can appear either on the column\n// or the row region of the PivotTable. There areas many <x> elements as there\n// are item values in any particular column or row.\ntype xlsxField struct {\n\tX int `xml:\"x,attr\"`\n}\n\n// xlsxRowItems represents the collection of items in row axis of the\n// PivotTable.\ntype xlsxRowItems struct {\n\tCount int      `xml:\"count,attr\"`\n\tI     []*xlsxI `xml:\"i\"`\n}\n\n// xlsxI represents the collection of items in the row region of the\n// PivotTable.\ntype xlsxI struct {\n\tX []*xlsxX `xml:\"x\"`\n}\n\n// xlsxX represents an array of indexes to cached shared item values.\ntype xlsxX struct{}\n\n// xlsxColFields represents the collection of fields that are on the column\n// axis of the PivotTable.\ntype xlsxColFields struct {\n\tCount int          `xml:\"count,attr\"`\n\tField []*xlsxField `xml:\"field\"`\n}\n\n// xlsxColItems represents the collection of column items of the PivotTable.\ntype xlsxColItems struct {\n\tCount int      `xml:\"count,attr\"`\n\tI     []*xlsxI `xml:\"i\"`\n}\n\n// xlsxPageFields represents the collection of items in the page or report\n// filter region of the PivotTable.\ntype xlsxPageFields struct {\n\tCount     int              `xml:\"count,attr\"`\n\tPageField []*xlsxPageField `xml:\"pageField\"`\n}\n\n// xlsxPageField represents a field on the page or report filter of the\n// PivotTable.\ntype xlsxPageField struct {\n\tFld    int         `xml:\"fld,attr\"`\n\tItem   int         `xml:\"item,attr,omitempty\"`\n\tHier   int         `xml:\"hier,attr,omitempty\"`\n\tName   string      `xml:\"name,attr,omitempty\"`\n\tCap    string      `xml:\"cap,attr,omitempty\"`\n\tExtLst *xlsxExtLst `xml:\"extLst\"`\n}\n\n// xlsxDataFields represents the collection of items in the data region of the\n// PivotTable.\ntype xlsxDataFields struct {\n\tCount     int              `xml:\"count,attr\"`\n\tDataField []*xlsxDataField `xml:\"dataField\"`\n}\n\n// xlsxDataField represents a field from a source list, table, or database\n// that contains data that is summarized in a PivotTable.\ntype xlsxDataField struct {\n\tName       string      `xml:\"name,attr,omitempty\"`\n\tFld        int         `xml:\"fld,attr\"`\n\tSubtotal   string      `xml:\"subtotal,attr,omitempty\"`\n\tShowDataAs string      `xml:\"showDataAs,attr,omitempty\"`\n\tBaseField  int         `xml:\"baseField,attr,omitempty\"`\n\tBaseItem   int64       `xml:\"baseItem,attr,omitempty\"`\n\tNumFmtID   int         `xml:\"numFmtId,attr,omitempty\"`\n\tExtLst     *xlsxExtLst `xml:\"extLst\"`\n}\n\n// xlsxConditionalFormats represents the collection of conditional formats\n// applied to a PivotTable.\ntype xlsxConditionalFormats struct{}\n\n// xlsxPivotTableStyleInfo represent information on style applied to the\n// PivotTable.\ntype xlsxPivotTableStyleInfo struct {\n\tName           string `xml:\"name,attr\"`\n\tShowRowHeaders bool   `xml:\"showRowHeaders,attr\"`\n\tShowColHeaders bool   `xml:\"showColHeaders,attr\"`\n\tShowRowStripes bool   `xml:\"showRowStripes,attr,omitempty\"`\n\tShowColStripes bool   `xml:\"showColStripes,attr,omitempty\"`\n\tShowLastColumn bool   `xml:\"showLastColumn,attr,omitempty\"`\n}\n"
  },
  {
    "path": "xmlSharedStrings.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"sync\"\n)\n\n// xlsxSST directly maps the sst element from the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main. String values may\n// be stored directly inside spreadsheet cell elements; however, storing the\n// same value inside multiple cell elements can result in very large worksheet\n// Parts, possibly resulting in performance degradation. The Shared String Table\n// is an indexed list of string values, shared across the workbook, which allows\n// implementations to store values only once.\ntype xlsxSST struct {\n\tmu          sync.Mutex\n\tXMLName     xml.Name `xml:\"http://schemas.openxmlformats.org/spreadsheetml/2006/main sst\"`\n\tCount       int      `xml:\"count,attr\"`\n\tUniqueCount int      `xml:\"uniqueCount,attr\"`\n\tSI          []xlsxSI `xml:\"si\"`\n}\n\n// xlsxSI (String Item) is the representation of an individual string in the\n// Shared String table. If the string is just a simple string with formatting\n// applied at the cell level, then the String Item (si) should contain a\n// single text element used to express the string. However, if the string in\n// the cell is more complex - i.e., has formatting applied at the character\n// level - then the string item shall consist of multiple rich text runs which\n// collectively are used to express the string.\ntype xlsxSI struct {\n\tT          *xlsxT             `xml:\"t,omitempty\"`\n\tR          []xlsxR            `xml:\"r\"`\n\tRPh        []*xlsxPhoneticRun `xml:\"rPh\"`\n\tPhoneticPr *xlsxPhoneticPr    `xml:\"phoneticPr\"`\n}\n\n// xlsxR represents a run of rich text. A rich text run is a region of text\n// that share a common set of properties, such as formatting properties. The\n// properties are defined in the rPr element, and the text displayed to the\n// user is defined in the Text (t) element.\ntype xlsxR struct {\n\tXMLName xml.Name `xml:\"r\"`\n\tRPr     *xlsxRPr `xml:\"rPr\"`\n\tT       *xlsxT   `xml:\"t\"`\n}\n\n// xlsxT directly maps the t element in the run properties.\ntype xlsxT struct {\n\tXMLName xml.Name `xml:\"t\"`\n\tSpace   xml.Attr `xml:\"space,attr,omitempty\"`\n\tVal     string   `xml:\",chardata\"`\n}\n\n// xlsxRPr (Run Properties) specifies a set of run properties which shall be\n// applied to the contents of the parent run after all style formatting has been\n// applied to the text. These properties are defined as direct formatting, since\n// they are directly applied to the run and supersede any formatting from\n// styles.\ntype xlsxRPr struct {\n\tRFont     *attrValString `xml:\"rFont\"`\n\tCharset   *attrValInt    `xml:\"charset\"`\n\tFamily    *attrValInt    `xml:\"family\"`\n\tB         *attrValBool   `xml:\"b\"`\n\tI         *attrValBool   `xml:\"i\"`\n\tStrike    *attrValBool   `xml:\"strike\"`\n\tOutline   *attrValBool   `xml:\"outline\"`\n\tShadow    *attrValBool   `xml:\"shadow\"`\n\tCondense  *attrValBool   `xml:\"condense\"`\n\tExtend    *attrValBool   `xml:\"extend\"`\n\tColor     *xlsxColor     `xml:\"color\"`\n\tSz        *attrValFloat  `xml:\"sz\"`\n\tU         *attrValString `xml:\"u\"`\n\tVertAlign *attrValString `xml:\"vertAlign\"`\n\tScheme    *attrValString `xml:\"scheme\"`\n}\n\n// RichTextRun directly maps the settings of the rich text run.\ntype RichTextRun struct {\n\tFont *Font\n\tText string\n}\n"
  },
  {
    "path": "xmlSlicers.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// xlsxSlicers directly maps the slicers element that specifies a slicer view on\n// the worksheet.\ntype xlsxSlicers struct {\n\tXMLName   xml.Name     `xml:\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main slicers\"`\n\tXMLNSXMC  string       `xml:\"xmlns:mc,attr\"`\n\tXMLNSX    string       `xml:\"xmlns:x,attr\"`\n\tXMLNSXR10 string       `xml:\"xmlns:xr10,attr\"`\n\tSlicer    []xlsxSlicer `xml:\"slicer\"`\n}\n\n// xlsxSlicer is a complex type that specifies a slicer view.\ntype xlsxSlicer struct {\n\tName           string `xml:\"name,attr\"`\n\tXR10UID        string `xml:\"xr10:uid,attr,omitempty\"`\n\tCache          string `xml:\"cache,attr\"`\n\tCaption        string `xml:\"caption,attr,omitempty\"`\n\tStartItem      *int   `xml:\"startItem,attr\"`\n\tColumnCount    *int   `xml:\"columnCount,attr\"`\n\tShowCaption    *bool  `xml:\"showCaption,attr\"`\n\tLevel          int    `xml:\"level,attr,omitempty\"`\n\tStyle          string `xml:\"style,attr,omitempty\"`\n\tLockedPosition bool   `xml:\"lockedPosition,attr,omitempty\"`\n\tRowHeight      int    `xml:\"rowHeight,attr\"`\n}\n\n// slicerCacheDefinition directly maps the slicerCacheDefinition element that\n// specifies a slicer cache.\ntype xlsxSlicerCacheDefinition struct {\n\tXMLName     xml.Name                    `xml:\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main slicerCacheDefinition\"`\n\tXMLNSXMC    string                      `xml:\"xmlns:mc,attr\"`\n\tXMLNSX      string                      `xml:\"xmlns:x,attr\"`\n\tXMLNSX15    string                      `xml:\"xmlns:x15,attr,omitempty\"`\n\tXMLNSXR10   string                      `xml:\"xmlns:xr10,attr\"`\n\tName        string                      `xml:\"name,attr\"`\n\tXR10UID     string                      `xml:\"xr10:uid,attr,omitempty\"`\n\tSourceName  string                      `xml:\"sourceName,attr\"`\n\tPivotTables *xlsxSlicerCachePivotTables `xml:\"pivotTables\"`\n\tData        *xlsxSlicerCacheData        `xml:\"data\"`\n\tExtLst      *xlsxExtLst                 `xml:\"extLst\"`\n}\n\n// xlsxSlicerCachePivotTables is a complex type that specifies a group of\n// pivotTable elements that specify the PivotTable views that are filtered by\n// the slicer cache.\ntype xlsxSlicerCachePivotTables struct {\n\tPivotTable []xlsxSlicerCachePivotTable `xml:\"pivotTable\"`\n}\n\n// xlsxSlicerCachePivotTable is a complex type that specifies a PivotTable view\n// filtered by a slicer cache.\ntype xlsxSlicerCachePivotTable struct {\n\tTabID int    `xml:\"tabId,attr\"`\n\tName  string `xml:\"name,attr\"`\n}\n\n// xlsxSlicerCacheData is a complex type that specifies a data source for the\n// slicer cache.\ntype xlsxSlicerCacheData struct {\n\tOLAP    *xlsxInnerXML           `xml:\"olap\"`\n\tTabular *xlsxTabularSlicerCache `xml:\"tabular\"`\n}\n\n// xlsxTabularSlicerCache is a complex type that specifies non-OLAP slicer items\n// that are cached within this slicer cache and properties of the slicer cache\n// specific to non-OLAP slicer items.\ntype xlsxTabularSlicerCache struct {\n\tPivotCacheID   int                          `xml:\"pivotCacheId,attr\"`\n\tSortOrder      string                       `xml:\"sortOrder,attr,omitempty\"`\n\tCustomListSort *bool                        `xml:\"customListSort,attr\"`\n\tShowMissing    *bool                        `xml:\"showMissing,attr\"`\n\tCrossFilter    string                       `xml:\"crossFilter,attr,omitempty\"`\n\tItems          *xlsxTabularSlicerCacheItems `xml:\"items\"`\n\tExtLst         *xlsxExtLst                  `xml:\"extLst\"`\n}\n\n// xlsxTabularSlicerCacheItems is a complex type that specifies non-OLAP slicer\n// items that are cached within this slicer cache.\ntype xlsxTabularSlicerCacheItems struct {\n\tCount int                          `xml:\"count,attr,omitempty\"`\n\tI     []xlsxTabularSlicerCacheItem `xml:\"i\"`\n}\n\n// xlsxTabularSlicerCacheItem is a complex type that specifies a non-OLAP slicer\n// item that is cached within this slicer cache.\ntype xlsxTabularSlicerCacheItem struct {\n\tX  int  `xml:\"x,attr\"`\n\tS  bool `xml:\"s,attr,omitempty\"`\n\tND bool `xml:\"nd,attr,omitempty\"`\n}\n\n// xlsxTableSlicerCache specifies a table data source for the slicer cache.\ntype xlsxTableSlicerCache struct {\n\tXMLName        xml.Name    `xml:\"x15:tableSlicerCache\"`\n\tTableID        int         `xml:\"tableId,attr\"`\n\tColumn         int         `xml:\"column,attr\"`\n\tSortOrder      string      `xml:\"sortOrder,attr,omitempty\"`\n\tCustomListSort *bool       `xml:\"customListSort,attr\"`\n\tCrossFilter    string      `xml:\"crossFilter,attr,omitempty\"`\n\tExtLst         *xlsxExtLst `xml:\"extLst\"`\n}\n\n// xlsxX14SlicerList specifies a list of slicer.\ntype xlsxX14SlicerList struct {\n\tXMLName xml.Name         `xml:\"x14:slicerList\"`\n\tSlicer  []*xlsxX14Slicer `xml:\"x14:slicer\"`\n}\n\n// xlsxX14Slicer specifies a slicer view,\ntype xlsxX14Slicer struct {\n\tXMLName xml.Name `xml:\"x14:slicer\"`\n\tRID     string   `xml:\"r:id,attr\"`\n}\n\n// xlsxX14SlicerCaches directly maps the x14:slicerCache element.\ntype xlsxX14SlicerCaches struct {\n\tXMLName xml.Name `xml:\"x14:slicerCaches\"`\n\tXMLNS   string   `xml:\"xmlns:x14,attr\"`\n\tContent string   `xml:\",innerxml\"`\n}\n\n// xlsxX15SlicerCaches directly maps the x14:slicerCache element.\ntype xlsxX14SlicerCache struct {\n\tXMLName xml.Name `xml:\"x14:slicerCache\"`\n\tRID     string   `xml:\"r:id,attr\"`\n}\n\n// xlsxX15SlicerCaches directly maps the x15:slicerCaches element.\ntype xlsxX15SlicerCaches struct {\n\tXMLName xml.Name `xml:\"x15:slicerCaches\"`\n\tXMLNS   string   `xml:\"xmlns:x14,attr\"`\n\tContent string   `xml:\",innerxml\"`\n}\n\n// decodeTableSlicerCache defines the structure used to parse the\n// x15:tableSlicerCache element of the table slicer cache.\ntype decodeTableSlicerCache struct {\n\tXMLName   xml.Name `xml:\"tableSlicerCache\"`\n\tTableID   int      `xml:\"tableId,attr\"`\n\tColumn    int      `xml:\"column,attr\"`\n\tSortOrder string   `xml:\"sortOrder,attr\"`\n}\n\n// decodeSlicerList defines the structure used to parse the x14:slicerList\n// element of a list of slicer.\ntype decodeSlicerList struct {\n\tXMLName xml.Name        `xml:\"slicerList\"`\n\tSlicer  []*decodeSlicer `xml:\"slicer\"`\n}\n\n// decodeSlicer defines the structure used to parse the x14:slicer element of a\n// slicer.\ntype decodeSlicer struct {\n\tRID string `xml:\"id,attr\"`\n}\n\n// decodeSlicerCaches defines the structure used to parse the\n// x14:slicerCaches and x15:slicerCaches element of a slicer cache.\ntype decodeSlicerCaches struct {\n\tXMLName xml.Name `xml:\"slicerCaches\"`\n\tContent string   `xml:\",innerxml\"`\n}\n\n// xlsxTimelines is a mechanism for filtering data in pivot table views, cube\n// functions and charts based on non-worksheet pivot tables. In the case of\n// using OLAP Timeline source data, a Timeline is based on a key attribute of\n// an OLAP hierarchy. In the case of using native Timeline source data, a\n// Timeline is based on a data table column.\ntype xlsxTimelines struct {\n\tXMLName   xml.Name       `xml:\"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main timelines\"`\n\tXMLNSXMC  string         `xml:\"xmlns:mc,attr\"`\n\tXMLNSX    string         `xml:\"xmlns:x,attr\"`\n\tXMLNSXR10 string         `xml:\"xmlns:xr10,attr\"`\n\tTimeline  []xlsxTimeline `xml:\"timeline\"`\n}\n\n// xlsxTimeline is timeline view specifies the display of a timeline on a\n// worksheet.\ntype xlsxTimeline struct {\n\tName                    string `xml:\"name,attr\"`\n\tXR10UID                 string `xml:\"xr10:uid,attr,omitempty\"`\n\tCache                   string `xml:\"cache,attr\"`\n\tCaption                 string `xml:\"caption,attr,omitempty\"`\n\tShowHeader              *bool  `xml:\"showHeader,attr\"`\n\tShowSelectionLabel      *bool  `xml:\"showSelectionLabel,attr\"`\n\tShowTimeLevel           *bool  `xml:\"showTimeLevel,attr\"`\n\tShowHorizontalScrollbar *bool  `xml:\"showHorizontalScrollbar,attr\"`\n\tLevel                   int    `xml:\"level,attr\"`\n\tSelectionLevel          int    `xml:\"selectionLevel,attr\"`\n\tScrollPosition          string `xml:\"scrollPosition,attr,omitempty\"`\n\tStyle                   string `xml:\"style,attr,omitempty\"`\n}\n"
  },
  {
    "path": "xmlStyles.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"sync\"\n)\n\n// xlsxStyleSheet is the root element of the Styles part.\ntype xlsxStyleSheet struct {\n\tmu           sync.Mutex\n\tXMLName      xml.Name          `xml:\"http://schemas.openxmlformats.org/spreadsheetml/2006/main styleSheet\"`\n\tNumFmts      *xlsxNumFmts      `xml:\"numFmts\"`\n\tFonts        *xlsxFonts        `xml:\"fonts\"`\n\tFills        *xlsxFills        `xml:\"fills\"`\n\tBorders      *xlsxBorders      `xml:\"borders\"`\n\tCellStyleXfs *xlsxCellStyleXfs `xml:\"cellStyleXfs\"`\n\tCellXfs      *xlsxCellXfs      `xml:\"cellXfs\"`\n\tCellStyles   *xlsxCellStyles   `xml:\"cellStyles\"`\n\tDxfs         *xlsxDxfs         `xml:\"dxfs\"`\n\tTableStyles  *xlsxTableStyles  `xml:\"tableStyles\"`\n\tColors       *xlsxStyleColors  `xml:\"colors\"`\n\tExtLst       *xlsxExtLst       `xml:\"extLst\"`\n}\n\n// xlsxAlignment formatting information pertaining to text alignment in cells.\n// There are a variety of choices for how text is aligned both horizontally and\n// vertically, as well as indentation settings, and so on.\ntype xlsxAlignment struct {\n\tHorizontal      string `xml:\"horizontal,attr,omitempty\"`\n\tIndent          int    `xml:\"indent,attr,omitempty\"`\n\tJustifyLastLine bool   `xml:\"justifyLastLine,attr,omitempty\"`\n\tReadingOrder    uint64 `xml:\"readingOrder,attr,omitempty\"`\n\tRelativeIndent  int    `xml:\"relativeIndent,attr,omitempty\"`\n\tShrinkToFit     bool   `xml:\"shrinkToFit,attr,omitempty\"`\n\tTextRotation    int    `xml:\"textRotation,attr,omitempty\"`\n\tVertical        string `xml:\"vertical,attr,omitempty\"`\n\tWrapText        bool   `xml:\"wrapText,attr,omitempty\"`\n}\n\n// xlsxProtection (Protection Properties) contains protection properties\n// associated with the cell. Each cell has protection properties that can be\n// set. The cell protection properties do not take effect unless the sheet has\n// been protected.\ntype xlsxProtection struct {\n\tHidden *bool `xml:\"hidden,attr\"`\n\tLocked *bool `xml:\"locked,attr\"`\n}\n\n// xlsxLine expresses a single set of cell border.\ntype xlsxLine struct {\n\tStyle string     `xml:\"style,attr,omitempty\"`\n\tColor *xlsxColor `xml:\"color\"`\n}\n\n// xlsxColor is a common mapping used for both the fgColor and bgColor elements.\n// Foreground color of the cell fill pattern. Cell fill patterns operate with\n// two colors: a background color and a foreground color. These combine\n// to make a patterned cell fill. Background color of the cell fill pattern.\n// Cell fill patterns operate with two colors: a background color and a\n// foreground color. These combine to make a patterned cell fill.\ntype xlsxColor struct {\n\tAuto    bool    `xml:\"auto,attr,omitempty\"`\n\tRGB     string  `xml:\"rgb,attr,omitempty\"`\n\tIndexed int     `xml:\"indexed,attr,omitempty\"`\n\tTheme   *int    `xml:\"theme,attr\"`\n\tTint    float64 `xml:\"tint,attr,omitempty\"`\n}\n\n// xlsxFonts directly maps the font element. This element contains all font\n// definitions for this workbook.\ntype xlsxFonts struct {\n\tCount int         `xml:\"count,attr\"`\n\tFont  []*xlsxFont `xml:\"font\"`\n}\n\n// xlsxFont directly maps the font element. This element defines the\n// properties for one of the fonts used in this workbook.\ntype xlsxFont struct {\n\tName      *attrValString `xml:\"name\"`\n\tCharset   *attrValInt    `xml:\"charset\"`\n\tFamily    *attrValInt    `xml:\"family\"`\n\tB         *attrValBool   `xml:\"b\"`\n\tI         *attrValBool   `xml:\"i\"`\n\tStrike    *attrValBool   `xml:\"strike\"`\n\tOutline   *attrValBool   `xml:\"outline\"`\n\tShadow    *attrValBool   `xml:\"shadow\"`\n\tCondense  *attrValBool   `xml:\"condense\"`\n\tExtend    *attrValBool   `xml:\"extend\"`\n\tColor     *xlsxColor     `xml:\"color\"`\n\tSz        *attrValFloat  `xml:\"sz\"`\n\tU         *attrValString `xml:\"u\"`\n\tVertAlign *attrValString `xml:\"vertAlign\"`\n\tScheme    *attrValString `xml:\"scheme\"`\n}\n\n// xlsxFills directly maps the fills' element. This element defines the cell\n// fills portion of the Styles part, consisting of a sequence of fill records. A\n// cell fill consists of a background color, foreground color, and pattern to be\n// applied across the cell.\ntype xlsxFills struct {\n\tCount int         `xml:\"count,attr\"`\n\tFill  []*xlsxFill `xml:\"fill\"`\n}\n\n// xlsxFill directly maps the fill element. This element specifies fill\n// formatting.\ntype xlsxFill struct {\n\tPatternFill  *xlsxPatternFill  `xml:\"patternFill\"`\n\tGradientFill *xlsxGradientFill `xml:\"gradientFill\"`\n}\n\n// xlsxPatternFill is used to specify cell fill information for pattern and\n// solid color cell fills. For solid cell fills (no pattern), fgColor is used.\n// For cell fills with patterns specified, then the cell fill color is\n// specified by the bgColor element.\ntype xlsxPatternFill struct {\n\tPatternType string     `xml:\"patternType,attr,omitempty\"`\n\tFgColor     *xlsxColor `xml:\"fgColor\"`\n\tBgColor     *xlsxColor `xml:\"bgColor\"`\n}\n\n// xlsxGradientFill defines a gradient-style cell fill. Gradient cell fills can\n// use one or two colors as the end points of color interpolation.\ntype xlsxGradientFill struct {\n\tBottom float64                 `xml:\"bottom,attr,omitempty\"`\n\tDegree float64                 `xml:\"degree,attr,omitempty\"`\n\tLeft   float64                 `xml:\"left,attr,omitempty\"`\n\tRight  float64                 `xml:\"right,attr,omitempty\"`\n\tTop    float64                 `xml:\"top,attr,omitempty\"`\n\tType   string                  `xml:\"type,attr,omitempty\"`\n\tStop   []*xlsxGradientFillStop `xml:\"stop\"`\n}\n\n// xlsxGradientFillStop directly maps the stop element.\ntype xlsxGradientFillStop struct {\n\tPosition float64   `xml:\"position,attr\"`\n\tColor    xlsxColor `xml:\"color,omitempty\"`\n}\n\n// xlsxBorders directly maps the borders' element. This element contains borders\n// formatting information, specifying all border definitions for all cells in\n// the workbook.\ntype xlsxBorders struct {\n\tCount  int           `xml:\"count,attr\"`\n\tBorder []*xlsxBorder `xml:\"border\"`\n}\n\n// xlsxBorder directly maps the border element. Expresses a single set of cell\n// border formats (left, right, top, bottom, diagonal). Color is optional. When\n// missing, 'automatic' is implied.\ntype xlsxBorder struct {\n\tDiagonalDown bool      `xml:\"diagonalDown,attr,omitempty\"`\n\tDiagonalUp   bool      `xml:\"diagonalUp,attr,omitempty\"`\n\tOutline      bool      `xml:\"outline,attr,omitempty\"`\n\tLeft         *xlsxLine `xml:\"left\"`\n\tRight        *xlsxLine `xml:\"right\"`\n\tTop          *xlsxLine `xml:\"top\"`\n\tBottom       *xlsxLine `xml:\"bottom\"`\n\tDiagonal     *xlsxLine `xml:\"diagonal\"`\n\tVertical     *xlsxLine `xml:\"vertical\"`\n\tHorizontal   *xlsxLine `xml:\"horizontal\"`\n}\n\n// xlsxCellStyles directly maps the cellStyles element. This element contains\n// the named cell styles, consisting of a sequence of named style records. A\n// named cell style is a collection of direct or themed formatting (e.g., cell\n// border, cell fill, and font type/size/style) grouped together into a single\n// named style, and can be applied to a cell.\ntype xlsxCellStyles struct {\n\tXMLName   xml.Name         `xml:\"cellStyles\"`\n\tCount     int              `xml:\"count,attr\"`\n\tCellStyle []*xlsxCellStyle `xml:\"cellStyle\"`\n}\n\n// xlsxCellStyle directly maps the cellStyle element. This element represents\n// the name and related formatting records for a named cell style in this\n// workbook.\ntype xlsxCellStyle struct {\n\tXMLName       xml.Name `xml:\"cellStyle\"`\n\tName          string   `xml:\"name,attr\"`\n\tXfID          int      `xml:\"xfId,attr\"`\n\tBuiltInID     *int     `xml:\"builtinId,attr\"`\n\tILevel        *int     `xml:\"iLevel,attr\"`\n\tHidden        *bool    `xml:\"hidden,attr\"`\n\tCustomBuiltIn *bool    `xml:\"customBuiltin,attr\"`\n}\n\n// xlsxCellStyleXfs directly maps the cellStyleXfs element. This element\n// contains the master formatting records (xf's) which define the formatting for\n// all named cell styles in this workbook. Master formatting records reference\n// individual elements of formatting (e.g., number format, font definitions,\n// cell fills, etc.) by specifying a zero-based index into those collections.\n// Master formatting records also specify whether to apply or ignore particular\n// aspects of formatting.\ntype xlsxCellStyleXfs struct {\n\tCount int      `xml:\"count,attr\"`\n\tXf    []xlsxXf `xml:\"xf,omitempty\"`\n}\n\n// xlsxXf directly maps the xf element. A single xf element describes all the\n// formatting for a cell.\ntype xlsxXf struct {\n\tNumFmtID          *int            `xml:\"numFmtId,attr\"`\n\tFontID            *int            `xml:\"fontId,attr\"`\n\tFillID            *int            `xml:\"fillId,attr\"`\n\tBorderID          *int            `xml:\"borderId,attr\"`\n\tXfID              *int            `xml:\"xfId,attr\"`\n\tQuotePrefix       *bool           `xml:\"quotePrefix,attr\"`\n\tPivotButton       *bool           `xml:\"pivotButton,attr\"`\n\tApplyNumberFormat *bool           `xml:\"applyNumberFormat,attr\"`\n\tApplyFont         *bool           `xml:\"applyFont,attr\"`\n\tApplyFill         *bool           `xml:\"applyFill,attr\"`\n\tApplyBorder       *bool           `xml:\"applyBorder,attr\"`\n\tApplyAlignment    *bool           `xml:\"applyAlignment,attr\"`\n\tApplyProtection   *bool           `xml:\"applyProtection,attr\"`\n\tAlignment         *xlsxAlignment  `xml:\"alignment\"`\n\tProtection        *xlsxProtection `xml:\"protection\"`\n}\n\n// xlsxCellXfs directly maps the cellXfs element. This element contains the\n// master formatting records (xf) which define the formatting applied to cells\n// in this workbook. These records are the starting point for determining the\n// formatting for a cell. Cells in the Sheet Part reference the xf records by\n// zero-based index.\ntype xlsxCellXfs struct {\n\tCount int      `xml:\"count,attr\"`\n\tXf    []xlsxXf `xml:\"xf,omitempty\"`\n}\n\n// xlsxDxfs directly maps the dxfs element. This element contains the master\n// differential formatting records (dxf's) which define formatting for all\n// non-cell formatting in this workbook. Whereas xf records fully specify a\n// particular aspect of formatting (e.g., cell borders) by referencing those\n// formatting definitions elsewhere in the Styles part, dxf records specify\n// incremental (or differential) aspects of formatting directly inline within\n// the dxf element. The dxf formatting is to be applied on top of or in addition\n// to any formatting already present on the object using the dxf record.\ntype xlsxDxfs struct {\n\tCount int        `xml:\"count,attr\"`\n\tDxfs  []*xlsxDxf `xml:\"dxf\"`\n}\n\n// xlsxDxf directly maps the dxf element. A single dxf record, expressing\n// incremental formatting to be applied.\ntype xlsxDxf struct {\n\tFont       *xlsxFont           `xml:\"font\"`\n\tNumFmt     *xlsxNumFmt         `xml:\"numFmt\"`\n\tFill       *xlsxFill           `xml:\"fill\"`\n\tAlignment  *xlsxAlignment      `xml:\"alignment\"`\n\tBorder     *xlsxBorder         `xml:\"border\"`\n\tProtection *xlsxProtection     `xml:\"protection\"`\n\tExtLst     *xlsxPositiveSize2D `xml:\"extLst\"`\n}\n\n// xlsxTableStyles directly maps the tableStyles element. This element\n// represents a collection of Table style definitions for Table styles and\n// PivotTable styles used in this workbook. It consists of a sequence of\n// tableStyle records, each defining a single Table style.\ntype xlsxTableStyles struct {\n\tCount             int               `xml:\"count,attr\"`\n\tDefaultPivotStyle string            `xml:\"defaultPivotStyle,attr\"`\n\tDefaultTableStyle string            `xml:\"defaultTableStyle,attr\"`\n\tTableStyles       []*xlsxTableStyle `xml:\"tableStyle\"`\n}\n\n// xlsxTableStyle directly maps the tableStyle element. This element represents\n// a single table style definition that indicates how a spreadsheet application\n// should format and display a table.\ntype xlsxTableStyle struct {\n\tName              string `xml:\"name,attr,omitempty\"`\n\tPivot             int    `xml:\"pivot,attr\"`\n\tCount             int    `xml:\"count,attr,omitempty\"`\n\tTable             bool   `xml:\"table,attr,omitempty\"`\n\tTableStyleElement string `xml:\",innerxml\"`\n}\n\n// xlsxNumFmts directly maps the numFmts element. This element defines the\n// number formats in this workbook, consisting of a sequence of numFmt records,\n// where each numFmt record defines a particular number format, indicating how\n// to format and render the numeric value of a cell.\ntype xlsxNumFmts struct {\n\tCount  int           `xml:\"count,attr\"`\n\tNumFmt []*xlsxNumFmt `xml:\"numFmt\"`\n}\n\n// xlsxNumFmt directly maps the numFmt element. This element specifies number\n// format properties which indicate how to format and render the numeric value\n// of a cell.\ntype xlsxNumFmt struct {\n\tNumFmtID     int    `xml:\"numFmtId,attr\"`\n\tFormatCode   string `xml:\"formatCode,attr\"`\n\tFormatCode16 string `xml:\"http://schemas.microsoft.com/office/spreadsheetml/2015/02/main formatCode16,attr,omitempty\"`\n}\n\n// xlsxIndexedColors directly maps the single ARGB entry for the corresponding\n// color index.\ntype xlsxIndexedColors struct {\n\tRgbColor []xlsxColor `xml:\"rgbColor\"`\n}\n\n// xlsxStyleColors directly maps the colors' element. Color information\n// associated with this style sheet. This collection is written whenever the\n// legacy color palette has been modified (backwards compatibility settings) or\n// a custom color has been selected while using this workbook.\ntype xlsxStyleColors struct {\n\tIndexedColors *xlsxIndexedColors `xml:\"indexedColors\"`\n\tMruColors     *xlsxInnerXML      `xml:\"mruColors\"`\n}\n\n// Alignment directly maps the alignment settings of the cells.\ntype Alignment struct {\n\tHorizontal      string\n\tIndent          int\n\tJustifyLastLine bool\n\tReadingOrder    uint64\n\tRelativeIndent  int\n\tShrinkToFit     bool\n\tTextRotation    int\n\tVertical        string\n\tWrapText        bool\n}\n\n// Border directly maps the border settings of the cells.\ntype Border struct {\n\tType  string\n\tColor string\n\tStyle int\n}\n\n// Font directly maps the font settings of the fonts.\ntype Font struct {\n\tBold         bool\n\tItalic       bool\n\tUnderline    string\n\tFamily       string\n\tSize         float64\n\tStrike       bool\n\tColor        string\n\tColorIndexed int\n\tColorTheme   *int\n\tColorTint    float64\n\tVertAlign    string\n\tCharset      *int\n}\n\n// Fill directly maps the fill settings of the cells.\ntype Fill struct {\n\tType         string\n\tPattern      int\n\tColor        []string\n\tShading      int\n\tTransparency int\n}\n\n// Protection directly maps the protection settings of the cells.\ntype Protection struct {\n\tHidden bool\n\tLocked bool\n}\n\n// Style directly maps the style settings of the cells.\ntype Style struct {\n\tBorder        []Border\n\tFill          Fill\n\tFont          *Font\n\tAlignment     *Alignment\n\tProtection    *Protection\n\tNumFmt        int\n\tDecimalPlaces *int\n\tCustomNumFmt  *string\n\tNegRed        bool\n}\n"
  },
  {
    "path": "xmlTable.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// xlsxTable directly maps the table element. A table helps organize and provide\n// structure to list of information in a worksheet. Tables have clearly labeled\n// columns, rows, and data regions. Tables make it easier for users to sort,\n// analyze, format, manage, add, and delete information. This element is the\n// root element for a table that is not a single cell XML table.\ntype xlsxTable struct {\n\tXMLName              xml.Name            `xml:\"table\"`\n\tXMLNS                string              `xml:\"xmlns,attr\"`\n\tID                   int                 `xml:\"id,attr\"`\n\tName                 string              `xml:\"name,attr\"`\n\tDisplayName          string              `xml:\"displayName,attr,omitempty\"`\n\tComment              string              `xml:\"comment,attr,omitempty\"`\n\tRef                  string              `xml:\"ref,attr\"`\n\tTableType            string              `xml:\"tableType,attr,omitempty\"`\n\tHeaderRowCount       *int                `xml:\"headerRowCount,attr\"`\n\tInsertRow            bool                `xml:\"insertRow,attr,omitempty\"`\n\tInsertRowShift       bool                `xml:\"insertRowShift,attr,omitempty\"`\n\tTotalsRowCount       int                 `xml:\"totalsRowCount,attr,omitempty\"`\n\tTotalsRowShown       *bool               `xml:\"totalsRowShown,attr\"`\n\tPublished            bool                `xml:\"published,attr,omitempty\"`\n\tHeaderRowDxfID       int                 `xml:\"headerRowDxfId,attr,omitempty\"`\n\tDataDxfID            int                 `xml:\"dataDxfId,attr,omitempty\"`\n\tTotalsRowDxfID       int                 `xml:\"totalsRowDxfId,attr,omitempty\"`\n\tHeaderRowBorderDxfID int                 `xml:\"headerRowBorderDxfId,attr,omitempty\"`\n\tTableBorderDxfID     int                 `xml:\"tableBorderDxfId,attr,omitempty\"`\n\tTotalsRowBorderDxfID int                 `xml:\"totalsRowBorderDxfId,attr,omitempty\"`\n\tHeaderRowCellStyle   string              `xml:\"headerRowCellStyle,attr,omitempty\"`\n\tDataCellStyle        string              `xml:\"dataCellStyle,attr,omitempty\"`\n\tTotalsRowCellStyle   string              `xml:\"totalsRowCellStyle,attr,omitempty\"`\n\tConnectionID         int                 `xml:\"connectionId,attr,omitempty\"`\n\tAutoFilter           *xlsxAutoFilter     `xml:\"autoFilter\"`\n\tTableColumns         *xlsxTableColumns   `xml:\"tableColumns\"`\n\tTableStyleInfo       *xlsxTableStyleInfo `xml:\"tableStyleInfo\"`\n}\n\n// xlsxAutoFilter temporarily hides rows based on a filter criteria, which is\n// applied column by column to a table of data in the worksheet. This collection\n// expresses AutoFilter settings.\ntype xlsxAutoFilter struct {\n\tXMLName      xml.Name            `xml:\"autoFilter\"`\n\tRef          string              `xml:\"ref,attr\"`\n\tFilterColumn []*xlsxFilterColumn `xml:\"filterColumn\"`\n}\n\n// xlsxFilterColumn directly maps the filterColumn element. The filterColumn\n// collection identifies a particular column in the AutoFilter range and\n// specifies filter information that has been applied to this column. If a\n// column in the AutoFilter range has no criteria specified, then there is no\n// corresponding filterColumn collection expressed for that column.\ntype xlsxFilterColumn struct {\n\tColID         int                `xml:\"colId,attr\"`\n\tHiddenButton  bool               `xml:\"hiddenButton,attr,omitempty\"`\n\tShowButton    bool               `xml:\"showButton,attr,omitempty\"`\n\tCustomFilters *xlsxCustomFilters `xml:\"customFilters\"`\n\tFilters       *xlsxFilters       `xml:\"filters\"`\n\tColorFilter   *xlsxColorFilter   `xml:\"colorFilter\"`\n\tDynamicFilter *xlsxDynamicFilter `xml:\"dynamicFilter\"`\n\tIconFilter    *xlsxIconFilter    `xml:\"iconFilter\"`\n\tTop10         *xlsxTop10         `xml:\"top10\"`\n}\n\n// xlsxCustomFilters directly maps the customFilters element. When there is more\n// than one custom filter criteria to apply (an 'and' or 'or' joining two\n// criteria), then this element groups the customFilter elements together.\ntype xlsxCustomFilters struct {\n\tAnd          bool                `xml:\"and,attr,omitempty\"`\n\tCustomFilter []*xlsxCustomFilter `xml:\"customFilter\"`\n}\n\n// xlsxCustomFilter directly maps the customFilter element. A custom AutoFilter\n// specifies an operator and a value. There can be at most two customFilters\n// specified, and in that case the parent element specifies whether the two\n// conditions are joined by 'and' or 'or'. For any cells whose values do not\n// meet the specified criteria, the corresponding rows shall be hidden from view\n// when the filter is applied.\ntype xlsxCustomFilter struct {\n\tOperator string `xml:\"operator,attr,omitempty\"`\n\tVal      string `xml:\"val,attr,omitempty\"`\n}\n\n// xlsxFilters directly maps the filters (Filter Criteria) element. When\n// multiple values are chosen to filter by, or when a group of date values are\n// chosen to filter by, this element groups those criteria together.\ntype xlsxFilters struct {\n\tBlank         bool                 `xml:\"blank,attr,omitempty\"`\n\tCalendarType  string               `xml:\"calendarType,attr,omitempty\"`\n\tFilter        []*xlsxFilter        `xml:\"filter\"`\n\tDateGroupItem []*xlsxDateGroupItem `xml:\"dateGroupItem\"`\n}\n\n// xlsxFilter directly maps the filter element. This element expresses a filter\n// criteria value.\ntype xlsxFilter struct {\n\tVal string `xml:\"val,attr,omitempty\"`\n}\n\n// xlsxColorFilter directly maps the colorFilter element. This element specifies\n// the color to filter by and whether to use the cell's fill or font color in\n// the filter criteria. If the cell's font or fill color does not match the\n// color specified in the criteria, the rows corresponding to those cells are\n// hidden from view.\ntype xlsxColorFilter struct {\n\tCellColor bool `xml:\"cellColor,attr\"`\n\tDxfID     int  `xml:\"dxfId,attr\"`\n}\n\n// xlsxDynamicFilter directly maps the dynamicFilter element. This collection\n// specifies dynamic filter criteria. These criteria are considered dynamic\n// because they can change, either with the data itself (e.g., \"above average\")\n// or with the current system date (e.g., show values for \"today\"). For any\n// cells whose values do not meet the specified criteria, the corresponding rows\n// shall be hidden from view when the filter is applied.\ntype xlsxDynamicFilter struct {\n\tMaxValISO string  `xml:\"maxValIso,attr,omitempty\"`\n\tType      string  `xml:\"type,attr,omitempty\"`\n\tVal       float64 `xml:\"val,attr,omitempty\"`\n\tValISO    string  `xml:\"valIso,attr,omitempty\"`\n}\n\n// xlsxIconFilter directly maps the iconFilter element. This element specifies\n// the icon set and particular icon within that set to filter by. For any cells\n// whose icon does not match the specified criteria, the corresponding rows\n// shall be hidden from view when the filter is applied.\ntype xlsxIconFilter struct {\n\tIconID  int    `xml:\"iconId,attr\"`\n\tIconSet string `xml:\"iconSet,attr,omitempty\"`\n}\n\n// xlsxTop10 directly maps the top10 element. This element specifies the top N\n// (percent or number of items) to filter by.\ntype xlsxTop10 struct {\n\tFilterVal float64 `xml:\"filterVal,attr,omitempty\"`\n\tPercent   bool    `xml:\"percent,attr,omitempty\"`\n\tTop       bool    `xml:\"top,attr\"`\n\tVal       float64 `xml:\"val,attr,omitempty\"`\n}\n\n// xlsxDateGroupItem directly maps the dateGroupItem element. This collection is\n// used to express a group of dates or times which are used in an AutoFilter\n// criteria. [Note: See parent element for an example. end note] Values are\n// always written in the calendar type of the first date encountered in the\n// filter range, so that all subsequent dates, even when formatted or\n// represented by other calendar types, can be correctly compared for the\n// purposes of filtering.\ntype xlsxDateGroupItem struct {\n\tDateTimeGrouping string `xml:\"dateTimeGrouping,attr,omitempty\"`\n\tDay              int    `xml:\"day,attr,omitempty\"`\n\tHour             int    `xml:\"hour,attr,omitempty\"`\n\tMinute           int    `xml:\"minute,attr,omitempty\"`\n\tMonth            int    `xml:\"month,attr,omitempty\"`\n\tSecond           int    `xml:\"second,attr,omitempty\"`\n\tYear             int    `xml:\"year,attr,omitempty\"`\n}\n\n// xlsxTableColumns directly maps the element representing the collection of all\n// table columns for this table.\ntype xlsxTableColumns struct {\n\tCount       int                `xml:\"count,attr\"`\n\tTableColumn []*xlsxTableColumn `xml:\"tableColumn\"`\n}\n\n// xlsxTableColumn directly maps the element representing a single column for\n// this table.\ntype xlsxTableColumn struct {\n\tID                 int    `xml:\"id,attr\"`\n\tUniqueName         string `xml:\"uniqueName,attr,omitempty\"`\n\tName               string `xml:\"name,attr\"`\n\tTotalsRowFunction  string `xml:\"totalsRowFunction,attr,omitempty\"`\n\tTotalsRowLabel     string `xml:\"totalsRowLabel,attr,omitempty\"`\n\tQueryTableFieldID  int    `xml:\"queryTableFieldId,attr,omitempty\"`\n\tHeaderRowDxfID     int    `xml:\"headerRowDxfId,attr,omitempty\"`\n\tDataDxfID          int    `xml:\"dataDxfId,attr,omitempty\"`\n\tTotalsRowDxfID     int    `xml:\"totalsRowDxfId,attr,omitempty\"`\n\tHeaderRowCellStyle string `xml:\"headerRowCellStyle,attr,omitempty\"`\n\tDataCellStyle      string `xml:\"dataCellStyle,attr,omitempty\"`\n\tTotalsRowCellStyle string `xml:\"totalsRowCellStyle,attr,omitempty\"`\n}\n\n// xlsxTableStyleInfo directly maps the tableStyleInfo element. This element\n// describes which style is used to display this table, and specifies which\n// portions of the table have the style applied.\ntype xlsxTableStyleInfo struct {\n\tName              string `xml:\"name,attr,omitempty\"`\n\tShowFirstColumn   bool   `xml:\"showFirstColumn,attr\"`\n\tShowLastColumn    bool   `xml:\"showLastColumn,attr\"`\n\tShowRowStripes    bool   `xml:\"showRowStripes,attr\"`\n\tShowColumnStripes bool   `xml:\"showColumnStripes,attr\"`\n}\n\n// xlsxSingleXMLCells is a single cell table is generated from an XML mapping.\n// These really just look like regular cells to the spreadsheet user, but shall\n// be implemented as Tables \"under the covers.\"\ntype xlsxSingleXMLCells struct {\n\tXMLName       xml.Name            `xml:\"singleXmlCells\"`\n\tSingleXmlCell []xlsxSingleXMLCell `xml:\"singleXmlCell\"`\n}\n\n// xlsxSingleXMLCell is a element represents the table properties for a single\n// cell XML table.\ntype xlsxSingleXMLCell struct {\n\tXMLName      xml.Name      `xml:\"singleXmlCell\"`\n\tID           int           `xml:\"id,attr\"`\n\tR            string        `xml:\"r,attr\"`\n\tConnectionID int           `xml:\"connectionId,attr\"`\n\tXMLCellPr    xlsxXMLCellPr `xml:\"xmlCellPr\"`\n\tExtLst       *xlsxInnerXML `xml:\"extLst\"`\n}\n\n// xlsxXMLCellPr is a element stores the XML properties for the cell of a single\n// cell xml table.\ntype xlsxXMLCellPr struct {\n\tXMLName    xml.Name      `xml:\"xmlCellPr\"`\n\tID         int           `xml:\"id,attr\"`\n\tUniqueName string        `xml:\"uniqueName,attr,omitempty\"`\n\tXMLPr      *xlsxInnerXML `xml:\"xmlPr\"`\n\tExtLst     *xlsxInnerXML `xml:\"extLst\"`\n}\n\n// Table directly maps the format settings of the table.\ntype Table struct {\n\ttID               int\n\trID               string\n\ttableXML          string\n\tRange             string\n\tName              string\n\tStyleName         string\n\tShowColumnStripes bool\n\tShowFirstColumn   bool\n\tShowHeaderRow     *bool\n\tShowLastColumn    bool\n\tShowRowStripes    *bool\n}\n\n// AutoFilterOptions directly maps the auto filter settings.\ntype AutoFilterOptions struct {\n\tColumn     string\n\tExpression string\n}\n"
  },
  {
    "path": "xmlTheme.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport \"encoding/xml\"\n\n// xlsxTheme directly maps the theme element in the namespace\n// http://schemas.openxmlformats.org/drawingml/2006/main\ntype xlsxTheme struct {\n\tXMLName           xml.Name              `xml:\"a:theme\"`\n\tXMLNSa            string                `xml:\"xmlns:a,attr\"`\n\tXMLNSr            string                `xml:\"xmlns:r,attr\"`\n\tName              string                `xml:\"name,attr\"`\n\tThemeElements     xlsxBaseStyles        `xml:\"a:themeElements\"`\n\tObjectDefaults    xlsxObjectDefaults    `xml:\"a:objectDefaults\"`\n\tExtraClrSchemeLst xlsxExtraClrSchemeLst `xml:\"a:extraClrSchemeLst\"`\n\tCustClrLst        *xlsxInnerXML         `xml:\"a:custClrLst\"`\n\tExtLst            *xlsxExtLst           `xml:\"a:extLst\"`\n}\n\n// xlsxBaseStyles defines the theme elements for a theme, and is the workhorse\n// of the theme. The bulk of the shared theme information that is used by a\n// given document is defined here. Within this complex type is defined a color\n// scheme, a font scheme, and a style matrix (format scheme) that defines\n// different formatting options for different pieces of a document.\ntype xlsxBaseStyles struct {\n\tClrScheme  xlsxColorScheme `xml:\"a:clrScheme\"`\n\tFontScheme xlsxFontScheme  `xml:\"a:fontScheme\"`\n\tFmtScheme  xlsxStyleMatrix `xml:\"a:fmtScheme\"`\n\tExtLst     *xlsxExtLst     `xml:\"a:extLst\"`\n}\n\n// xlsxCTColor holds the actual color values that are to be applied to a given\n// diagram and how those colors are to be applied.\ntype xlsxCTColor struct {\n\tScrgbClr  *xlsxInnerXML  `xml:\"a:scrgbClr\"`\n\tSrgbClr   *attrValString `xml:\"a:srgbClr\"`\n\tHslClr    *xlsxInnerXML  `xml:\"a:hslClr\"`\n\tSysClr    *xlsxSysClr    `xml:\"a:sysClr\"`\n\tSchemeClr *xlsxInnerXML  `xml:\"a:schemeClr\"`\n\tPrstClr   *xlsxInnerXML  `xml:\"a:prstClr\"`\n}\n\n// xlsxColorScheme defines a set of colors for the theme. The set of colors\n// consists of twelve color slots that can each hold a color of choice.\ntype xlsxColorScheme struct {\n\tName     string      `xml:\"name,attr\"`\n\tDk1      xlsxCTColor `xml:\"a:dk1\"`\n\tLt1      xlsxCTColor `xml:\"a:lt1\"`\n\tDk2      xlsxCTColor `xml:\"a:dk2\"`\n\tLt2      xlsxCTColor `xml:\"a:lt2\"`\n\tAccent1  xlsxCTColor `xml:\"a:accent1\"`\n\tAccent2  xlsxCTColor `xml:\"a:accent2\"`\n\tAccent3  xlsxCTColor `xml:\"a:accent3\"`\n\tAccent4  xlsxCTColor `xml:\"a:accent4\"`\n\tAccent5  xlsxCTColor `xml:\"a:accent5\"`\n\tAccent6  xlsxCTColor `xml:\"a:accent6\"`\n\tHlink    xlsxCTColor `xml:\"a:hlink\"`\n\tFolHlink xlsxCTColor `xml:\"a:folHlink\"`\n\tExtLst   *xlsxExtLst `xml:\"a:extLst\"`\n}\n\n// objectDefaults element allows for the definition of default shape, line,\n// and textbox formatting properties. An application can use this information\n// to format a shape (or text) initially on insertion into a document.\ntype xlsxObjectDefaults struct {\n\tObjectDefaults string `xml:\",innerxml\"`\n}\n\n// xlsxExtraClrSchemeLst element is a container for the list of extra color\n// schemes present in a document.\ntype xlsxExtraClrSchemeLst struct {\n\tExtraClrSchemeLst string `xml:\",innerxml\"`\n}\n\n// xlsxCTSupplementalFont defines an additional font that is used for language\n// specific fonts in themes. For example, one can specify a font that gets used\n// only within the Japanese language context.\ntype xlsxCTSupplementalFont struct {\n\tScript   string `xml:\"script,attr\"`\n\tTypeface string `xml:\"typeface,attr\"`\n}\n\n// xlsxFontCollection defines a major and minor font which is used in the font\n// scheme. A font collection consists of a font definition for Latin, East\n// Asian, and complex script. On top of these three definitions, one can also\n// define a font for use in a specific language or languages.\ntype xlsxFontCollection struct {\n\tLatin  *xlsxCTTextFont          `xml:\"a:latin\"`\n\tEa     *xlsxCTTextFont          `xml:\"a:ea\"`\n\tCs     *xlsxCTTextFont          `xml:\"a:cs\"`\n\tFont   []xlsxCTSupplementalFont `xml:\"a:font\"`\n\tExtLst *xlsxExtLst              `xml:\"a:extLst\"`\n}\n\n// xlsxFontScheme element defines the font scheme within the theme. The font\n// scheme consists of a pair of major and minor fonts for which to use in a\n// document. The major font corresponds well with the heading areas of a\n// document, and the minor font corresponds well with the normal text or\n// paragraph areas.\ntype xlsxFontScheme struct {\n\tName      string             `xml:\"name,attr\"`\n\tMajorFont xlsxFontCollection `xml:\"a:majorFont\"`\n\tMinorFont xlsxFontCollection `xml:\"a:minorFont\"`\n\tExtLst    *xlsxExtLst        `xml:\"a:extLst\"`\n}\n\n// xlsxStyleMatrix defines a set of formatting options, which can be referenced\n// by documents that apply a certain style to a given part of an object. For\n// example, in a given shape, say a rectangle, one can reference a themed line\n// style, themed effect, and themed fill that would be theme specific and\n// change when the theme is changed.\ntype xlsxStyleMatrix struct {\n\tName           string             `xml:\"name,attr,omitempty\"`\n\tFillStyleLst   xlsxFillStyleLst   `xml:\"a:fillStyleLst\"`\n\tLnStyleLst     xlsxLnStyleLst     `xml:\"a:lnStyleLst\"`\n\tEffectStyleLst xlsxEffectStyleLst `xml:\"a:effectStyleLst\"`\n\tBgFillStyleLst xlsxBgFillStyleLst `xml:\"a:bgFillStyleLst\"`\n}\n\n// xlsxFillStyleLst element defines a set of three fill styles that are used\n// within a theme. The three fill styles are arranged in order from subtle to\n// moderate to intense.\ntype xlsxFillStyleLst struct {\n\tFillStyleLst string `xml:\",innerxml\"`\n}\n\n// xlsxLnStyleLst element defines a list of three line styles for use within a\n// theme. The three line styles are arranged in order from subtle to moderate\n// to intense versions of lines. This list makes up part of the style matrix.\ntype xlsxLnStyleLst struct {\n\tLnStyleLst string `xml:\",innerxml\"`\n}\n\n// xlsxEffectStyleLst element defines a set of three effect styles that create\n// the effect style list for a theme. The effect styles are arranged in order\n// of subtle to moderate to intense.\ntype xlsxEffectStyleLst struct {\n\tEffectStyleLst string `xml:\",innerxml\"`\n}\n\n// xlsxBgFillStyleLst element defines a list of background fills that are\n// used within a theme. The background fills consist of three fills, arranged\n// in order from subtle to moderate to intense.\ntype xlsxBgFillStyleLst struct {\n\tBgFillStyleLst string `xml:\",innerxml\"`\n}\n\n// xlsxSysClr element specifies a color bound to predefined operating system\n// elements.\ntype xlsxSysClr struct {\n\tVal     string `xml:\"val,attr\"`\n\tLastClr string `xml:\"lastClr,attr\"`\n}\n\n// decodeTheme defines the structure used to parse the a:theme element for the\n// theme.\ntype decodeTheme struct {\n\tXMLName           xml.Name              `xml:\"http://schemas.openxmlformats.org/drawingml/2006/main theme\"`\n\tName              string                `xml:\"name,attr\"`\n\tThemeElements     decodeBaseStyles      `xml:\"themeElements\"`\n\tObjectDefaults    xlsxObjectDefaults    `xml:\"objectDefaults\"`\n\tExtraClrSchemeLst xlsxExtraClrSchemeLst `xml:\"extraClrSchemeLst\"`\n\tCustClrLst        *xlsxInnerXML         `xml:\"custClrLst\"`\n\tExtLst            *xlsxExtLst           `xml:\"extLst\"`\n}\n\n// decodeBaseStyles defines the structure used to parse the theme elements for a\n// theme, and is the workhorse of the theme.\ntype decodeBaseStyles struct {\n\tClrScheme  decodeColorScheme `xml:\"clrScheme\"`\n\tFontScheme decodeFontScheme  `xml:\"fontScheme\"`\n\tFmtScheme  decodeStyleMatrix `xml:\"fmtScheme\"`\n\tExtLst     *xlsxExtLst       `xml:\"extLst\"`\n}\n\n// decodeColorScheme defines the structure used to parse a set of colors for the\n// theme.\ntype decodeColorScheme struct {\n\tName     string        `xml:\"name,attr\"`\n\tDk1      decodeCTColor `xml:\"dk1\"`\n\tLt1      decodeCTColor `xml:\"lt1\"`\n\tDk2      decodeCTColor `xml:\"dk2\"`\n\tLt2      decodeCTColor `xml:\"lt2\"`\n\tAccent1  decodeCTColor `xml:\"accent1\"`\n\tAccent2  decodeCTColor `xml:\"accent2\"`\n\tAccent3  decodeCTColor `xml:\"accent3\"`\n\tAccent4  decodeCTColor `xml:\"accent4\"`\n\tAccent5  decodeCTColor `xml:\"accent5\"`\n\tAccent6  decodeCTColor `xml:\"accent6\"`\n\tHlink    decodeCTColor `xml:\"hlink\"`\n\tFolHlink decodeCTColor `xml:\"folHlink\"`\n\tExtLst   *xlsxExtLst   `xml:\"extLst\"`\n}\n\n// decodeFontScheme defines the structure used to parse font scheme within the\n// theme.\ntype decodeFontScheme struct {\n\tName      string               `xml:\"name,attr\"`\n\tMajorFont decodeFontCollection `xml:\"majorFont\"`\n\tMinorFont decodeFontCollection `xml:\"minorFont\"`\n\tExtLst    *xlsxExtLst          `xml:\"extLst\"`\n}\n\n// decodeFontCollection defines the structure used to parse a major and minor\n// font which is used in the font scheme.\ntype decodeFontCollection struct {\n\tLatin  *xlsxCTTextFont          `xml:\"latin\"`\n\tEa     *xlsxCTTextFont          `xml:\"ea\"`\n\tCs     *xlsxCTTextFont          `xml:\"cs\"`\n\tFont   []xlsxCTSupplementalFont `xml:\"font\"`\n\tExtLst *xlsxExtLst              `xml:\"extLst\"`\n}\n\n// decodeCTColor defines the structure used to parse the actual color values\n// that are to be applied to a given diagram and how those colors are to be\n// applied.\ntype decodeCTColor struct {\n\tScrgbClr  *xlsxInnerXML  `xml:\"scrgbClr\"`\n\tSrgbClr   *attrValString `xml:\"srgbClr\"`\n\tHslClr    *xlsxInnerXML  `xml:\"hslClr\"`\n\tSysClr    *xlsxSysClr    `xml:\"sysClr\"`\n\tSchemeClr *xlsxInnerXML  `xml:\"schemeClr\"`\n\tPrstClr   *xlsxInnerXML  `xml:\"prstClr\"`\n}\n\n// decodeStyleMatrix defines the structure used to parse a set of formatting\n// options, which can be referenced by documents that apply a certain style to\n// a given part of an object.\ntype decodeStyleMatrix struct {\n\tName           string             `xml:\"name,attr,omitempty\"`\n\tFillStyleLst   xlsxFillStyleLst   `xml:\"fillStyleLst\"`\n\tLnStyleLst     xlsxLnStyleLst     `xml:\"lnStyleLst\"`\n\tEffectStyleLst xlsxEffectStyleLst `xml:\"effectStyleLst\"`\n\tBgFillStyleLst xlsxBgFillStyleLst `xml:\"bgFillStyleLst\"`\n}\n"
  },
  {
    "path": "xmlWorkbook.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"sync\"\n)\n\n// xlsxRelationships describe references from parts to other internal resources\n// in the package or to external resources.\ntype xlsxRelationships struct {\n\tmu            sync.Mutex\n\tXMLName       xml.Name           `xml:\"http://schemas.openxmlformats.org/package/2006/relationships Relationships\"`\n\tRelationships []xlsxRelationship `xml:\"Relationship\"`\n}\n\n// xlsxRelationship contains relations which maps id and XML.\ntype xlsxRelationship struct {\n\tID         string `xml:\"Id,attr\"`\n\tTarget     string `xml:\",attr\"`\n\tType       string `xml:\",attr\"`\n\tTargetMode string `xml:\",attr,omitempty\"`\n}\n\n// xlsxWorkbook contains elements and attributes that encompass the data\n// content of the workbook. The workbook's child elements each have their own\n// subclause references.\ntype xlsxWorkbook struct {\n\tXMLName                xml.Name                 `xml:\"http://schemas.openxmlformats.org/spreadsheetml/2006/main workbook\"`\n\tConformance            string                   `xml:\"conformance,attr,omitempty\"`\n\tFileVersion            *xlsxFileVersion         `xml:\"fileVersion\"`\n\tFileSharing            *xlsxExtLst              `xml:\"fileSharing\"`\n\tWorkbookPr             *xlsxWorkbookPr          `xml:\"workbookPr\"`\n\tAlternateContent       *xlsxAlternateContent    `xml:\"mc:AlternateContent\"`\n\tDecodeAlternateContent *xlsxInnerXML            `xml:\"http://schemas.openxmlformats.org/markup-compatibility/2006 AlternateContent\"`\n\tWorkbookProtection     *xlsxWorkbookProtection  `xml:\"workbookProtection\"`\n\tBookViews              *xlsxBookViews           `xml:\"bookViews\"`\n\tSheets                 xlsxSheets               `xml:\"sheets\"`\n\tFunctionGroups         *xlsxFunctionGroups      `xml:\"functionGroups\"`\n\tExternalReferences     *xlsxExternalReferences  `xml:\"externalReferences\"`\n\tDefinedNames           *xlsxDefinedNames        `xml:\"definedNames\"`\n\tCalcPr                 *xlsxCalcPr              `xml:\"calcPr\"`\n\tOleSize                *xlsxExtLst              `xml:\"oleSize\"`\n\tCustomWorkbookViews    *xlsxCustomWorkbookViews `xml:\"customWorkbookViews\"`\n\tPivotCaches            *xlsxPivotCaches         `xml:\"pivotCaches\"`\n\tSmartTagPr             *xlsxExtLst              `xml:\"smartTagPr\"`\n\tSmartTagTypes          *xlsxExtLst              `xml:\"smartTagTypes\"`\n\tWebPublishing          *xlsxExtLst              `xml:\"webPublishing\"`\n\tFileRecoveryPr         *xlsxFileRecoveryPr      `xml:\"fileRecoveryPr\"`\n\tWebPublishObjects      *xlsxExtLst              `xml:\"webPublishObjects\"`\n\tExtLst                 *xlsxExtLst              `xml:\"extLst\"`\n}\n\n// xlsxFileRecoveryPr maps sheet recovery information. This element defines\n// properties that track the state of the workbook file, such as whether the\n// file was saved during a crash, or whether it should be opened in auto-recover\n// mode.\ntype xlsxFileRecoveryPr struct {\n\tAutoRecover     bool `xml:\"autoRecover,attr,omitempty\"`\n\tCrashSave       bool `xml:\"crashSave,attr,omitempty\"`\n\tDataExtractLoad bool `xml:\"dataExtractLoad,attr,omitempty\"`\n\tRepairLoad      bool `xml:\"repairLoad,attr,omitempty\"`\n}\n\n// xlsxWorkbookProtection directly maps the workbookProtection element. This\n// element specifies options for protecting data in the workbook. Applications\n// might use workbook protection to prevent anyone from accidentally changing,\n// moving, or deleting important data. This protection can be ignored by\n// applications which choose not to support this optional protection mechanism.\n// When a password is to be hashed and stored in this element, it shall be\n// hashed as defined below, starting from a UTF-16LE encoded string value. If\n// there is a leading BOM character (U+FEFF) in the encoded password it is\n// removed before hash calculation.\ntype xlsxWorkbookProtection struct {\n\tLockRevision           bool   `xml:\"lockRevision,attr,omitempty\"`\n\tLockStructure          bool   `xml:\"lockStructure,attr,omitempty\"`\n\tLockWindows            bool   `xml:\"lockWindows,attr,omitempty\"`\n\tRevisionsAlgorithmName string `xml:\"revisionsAlgorithmName,attr,omitempty\"`\n\tRevisionsHashValue     string `xml:\"revisionsHashValue,attr,omitempty\"`\n\tRevisionsSaltValue     string `xml:\"revisionsSaltValue,attr,omitempty\"`\n\tRevisionsSpinCount     int    `xml:\"revisionsSpinCount,attr,omitempty\"`\n\tWorkbookAlgorithmName  string `xml:\"workbookAlgorithmName,attr,omitempty\"`\n\tWorkbookHashValue      string `xml:\"workbookHashValue,attr,omitempty\"`\n\tWorkbookSaltValue      string `xml:\"workbookSaltValue,attr,omitempty\"`\n\tWorkbookSpinCount      int    `xml:\"workbookSpinCount,attr,omitempty\"`\n}\n\n// xlsxFileVersion directly maps the fileVersion element. This element defines\n// properties that track which version of the application accessed the data and\n// source code contained in the file.\ntype xlsxFileVersion struct {\n\tAppName      string `xml:\"appName,attr,omitempty\"`\n\tCodeName     string `xml:\"codeName,attr,omitempty\"`\n\tLastEdited   string `xml:\"lastEdited,attr,omitempty\"`\n\tLowestEdited string `xml:\"lowestEdited,attr,omitempty\"`\n\tRupBuild     string `xml:\"rupBuild,attr,omitempty\"`\n}\n\n// xlsxWorkbookPr directly maps the workbookPr element from the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main This element\n// defines a collection of workbook properties.\ntype xlsxWorkbookPr struct {\n\tDate1904                   bool   `xml:\"date1904,attr,omitempty\"`\n\tShowObjects                string `xml:\"showObjects,attr,omitempty\"`\n\tShowBorderUnselectedTables *bool  `xml:\"showBorderUnselectedTables,attr\"`\n\tFilterPrivacy              bool   `xml:\"filterPrivacy,attr,omitempty\"`\n\tPromptedSolutions          bool   `xml:\"promptedSolutions,attr,omitempty\"`\n\tShowInkAnnotation          *bool  `xml:\"showInkAnnotation,attr\"`\n\tBackupFile                 bool   `xml:\"backupFile,attr,omitempty\"`\n\tSaveExternalLinkValues     *bool  `xml:\"saveExternalLinkValues,attr\"`\n\tUpdateLinks                string `xml:\"updateLinks,attr,omitempty\"`\n\tCodeName                   string `xml:\"codeName,attr,omitempty\"`\n\tHidePivotFieldList         bool   `xml:\"hidePivotFieldList,attr,omitempty\"`\n\tShowPivotChartFilter       bool   `xml:\"showPivotChartFilter,attr,omitempty\"`\n\tAllowRefreshQuery          bool   `xml:\"allowRefreshQuery,attr,omitempty\"`\n\tPublishItems               bool   `xml:\"publishItems,attr,omitempty\"`\n\tCheckCompatibility         bool   `xml:\"checkCompatibility,attr,omitempty\"`\n\tAutoCompressPictures       *bool  `xml:\"autoCompressPictures,attr\"`\n\tRefreshAllConnections      bool   `xml:\"refreshAllConnections,attr,omitempty\"`\n\tDefaultThemeVersion        string `xml:\"defaultThemeVersion,attr,omitempty\"`\n}\n\n// xlsxBookViews directly maps the bookViews element. This element specifies the\n// collection of workbook views of the enclosing workbook. Each view can specify\n// a window position, filter options, and other configurations. There is no\n// limit on the number of workbook views that can be defined for a workbook.\ntype xlsxBookViews struct {\n\tWorkBookView []xlsxWorkBookView `xml:\"workbookView\"`\n}\n\n// xlsxWorkBookView directly maps the workbookView element from the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main This element\n// specifies a single Workbook view.\ntype xlsxWorkBookView struct {\n\tVisibility             string  `xml:\"visibility,attr,omitempty\"`\n\tMinimized              bool    `xml:\"minimized,attr,omitempty\"`\n\tShowHorizontalScroll   *bool   `xml:\"showHorizontalScroll,attr\"`\n\tShowVerticalScroll     *bool   `xml:\"showVerticalScroll,attr\"`\n\tShowSheetTabs          *bool   `xml:\"showSheetTabs,attr\"`\n\tXWindow                string  `xml:\"xWindow,attr,omitempty\"`\n\tYWindow                string  `xml:\"yWindow,attr,omitempty\"`\n\tWindowWidth            int     `xml:\"windowWidth,attr,omitempty\"`\n\tWindowHeight           int     `xml:\"windowHeight,attr,omitempty\"`\n\tTabRatio               float64 `xml:\"tabRatio,attr,omitempty\"`\n\tFirstSheet             int     `xml:\"firstSheet,attr,omitempty\"`\n\tActiveTab              int     `xml:\"activeTab,attr,omitempty\"`\n\tAutoFilterDateGrouping *bool   `xml:\"autoFilterDateGrouping,attr\"`\n}\n\n// xlsxSheets directly maps the sheets element from the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main.\ntype xlsxSheets struct {\n\tSheet []xlsxSheet `xml:\"sheet\"`\n}\n\n// xlsxSheet defines a sheet in this workbook. Sheet data is stored in a\n// separate part.\ntype xlsxSheet struct {\n\tName    string `xml:\"name,attr,omitempty\"`\n\tSheetID int    `xml:\"sheetId,attr,omitempty\"`\n\tID      string `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr\"`\n\tState   string `xml:\"state,attr,omitempty\"`\n}\n\n// xlsxFunctionGroup represents a single function group.\ntype xlsxFunctionGroup struct {\n\tName string `xml:\"name,attr\"`\n}\n\n// xlsxFunctionGroups defines the collection of function groups for the workbook.\ntype xlsxFunctionGroups struct {\n\tBuiltInGroupCount *int                `xml:\"builtInGroupCount,attr\"`\n\tFunctionGroup     []xlsxFunctionGroup `xml:\"functionGroup\"`\n}\n\n// xlsxExternalReferences directly maps the externalReferences element of the\n// external workbook references part.\ntype xlsxExternalReferences struct {\n\tExternalReference []xlsxExternalReference `xml:\"externalReference\"`\n}\n\n// xlsxExternalReference directly maps the externalReference element of the\n// external workbook references part.\ntype xlsxExternalReference struct {\n\tRID string `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty\"`\n}\n\n// xlsxPivotCaches element enumerates pivot cache definition parts used by pivot\n// tables and formulas in this workbook.\ntype xlsxPivotCaches struct {\n\tPivotCache []xlsxPivotCache `xml:\"pivotCache\"`\n}\n\n// xlsxPivotCache directly maps the pivotCache element.\ntype xlsxPivotCache struct {\n\tCacheID int    `xml:\"cacheId,attr\"`\n\tRID     string `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty\"`\n}\n\n// extLst element provides a convention for extending spreadsheetML in\n// predefined locations. The locations shall be denoted with the extLst element,\n// and are called extension lists. Extension list locations within the markup\n// document are specified in the markup specification and can be used to store\n// extensions to the markup specification, whether those are future version\n// extensions of the markup specification or are private extensions implemented\n// independently of the markup specification. Markup within an extension might\n// not be understood by a consumer.\ntype xlsxExtLst struct {\n\tExt string `xml:\",innerxml\"`\n}\n\n// xlsxExt represents a the future feature data storage area. Each extension\n// within an extension list shall be contained within an ext element.\n// Extensions shall be versioned by namespace, using the uri attribute, and\n// shall be allowed to appear in any order within the extension list. Any\n// number of extensions shall be allowed within an extension list.\ntype xlsxExt struct {\n\tXMLName xml.Name `xml:\"ext\"`\n\tURI     string   `xml:\"uri,attr\"`\n\tContent string   `xml:\",innerxml\"`\n\txmlns   []xml.Attr\n}\n\n// xlsxAlternateContent is a container for a sequence of multiple\n// representations of a given piece of content. The program reading the file\n// should only process one of these, and the one chosen should be based on\n// which conditions match.\ntype xlsxAlternateContent struct {\n\tXMLNSMC string `xml:\"xmlns:mc,attr,omitempty\"`\n\tContent string `xml:\",innerxml\"`\n}\n\n// xlsxChoice element shall be an element in the Markup Compatibility namespace\n// with local name \"Choice\". Parent elements of Choice elements shall be\n// AlternateContent elements.\ntype xlsxChoice struct {\n\tXMLName    xml.Name `xml:\"mc:Choice\"`\n\tXMLNSA14   string   `xml:\"xmlns:a14,attr,omitempty\"`\n\tXMLNSSle15 string   `xml:\"xmlns:sle15,attr,omitempty\"`\n\tRequires   string   `xml:\"Requires,attr,omitempty\"`\n\tContent    string   `xml:\",innerxml\"`\n}\n\n// xlsxFallback element shall be an element in the Markup Compatibility\n// namespace with local name \"Fallback\". Parent elements of Fallback elements\n// shall be AlternateContent elements.\ntype xlsxFallback struct {\n\tXMLName xml.Name `xml:\"mc:Fallback\"`\n\tContent string   `xml:\",innerxml\"`\n}\n\n// xlsxInnerXML holds parts of XML content currently not unmarshal.\ntype xlsxInnerXML struct {\n\tContent string `xml:\",innerxml\"`\n}\n\n// decodeExtLst defines the structure used to parse the extLst element\n// of the future feature data storage area.\ntype decodeExtLst struct {\n\tXMLName xml.Name   `xml:\"extLst\"`\n\tExt     []*xlsxExt `xml:\"ext\"`\n}\n\n// decodeExt defines the structure used to parse the ext element.\ntype decodeExt struct {\n\tURI     string `xml:\"uri,attr,omitempty\"`\n\tContent string `xml:\",innerxml\"`\n}\n\n// xlsxDefinedNames directly maps the definedNames element. This element defines\n// the collection of defined names for this workbook. Defined names are\n// descriptive names to represent cells, ranges of cells, formulas, or constant\n// values. Defined names can be used to represent a range on any worksheet.\ntype xlsxDefinedNames struct {\n\tDefinedName []xlsxDefinedName `xml:\"definedName\"`\n}\n\n// xlsxDefinedName directly maps the definedName element from the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main This element\n// defines a defined name within this workbook. A defined name is descriptive\n// text that is used to represent a cell, range of cells, formula, or constant\n// value. For a descriptions of the attributes see https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.definedname\ntype xlsxDefinedName struct {\n\tComment           string `xml:\"comment,attr,omitempty\"`\n\tCustomMenu        string `xml:\"customMenu,attr,omitempty\"`\n\tDescription       string `xml:\"description,attr,omitempty\"`\n\tFunction          bool   `xml:\"function,attr,omitempty\"`\n\tFunctionGroupID   int    `xml:\"functionGroupId,attr,omitempty\"`\n\tHelp              string `xml:\"help,attr,omitempty\"`\n\tHidden            bool   `xml:\"hidden,attr,omitempty\"`\n\tLocalSheetID      *int   `xml:\"localSheetId,attr\"`\n\tName              string `xml:\"name,attr,omitempty\"`\n\tPublishToServer   bool   `xml:\"publishToServer,attr,omitempty\"`\n\tShortcutKey       string `xml:\"shortcutKey,attr,omitempty\"`\n\tStatusBar         string `xml:\"statusBar,attr,omitempty\"`\n\tVbProcedure       bool   `xml:\"vbProcedure,attr,omitempty\"`\n\tWorkbookParameter bool   `xml:\"workbookParameter,attr,omitempty\"`\n\tXlm               bool   `xml:\"xml,attr,omitempty\"`\n\tData              string `xml:\",chardata\"`\n}\n\n// xlsxCalcPr directly maps the calcPr element. This element defines the\n// collection of properties the application uses to record calculation status\n// and details. Calculation is the process of computing formulas and then\n// displaying the results as values in the cells that contain the formulas.\ntype xlsxCalcPr struct {\n\tCalcCompleted         bool    `xml:\"calcCompleted,attr,omitempty\"`\n\tCalcID                int     `xml:\"calcId,attr,omitempty\"`\n\tCalcMode              string  `xml:\"calcMode,attr,omitempty\"`\n\tCalcOnSave            bool    `xml:\"calcOnSave,attr,omitempty\"`\n\tConcurrentCalc        *bool   `xml:\"concurrentCalc,attr\"`\n\tConcurrentManualCount int     `xml:\"concurrentManualCount,attr,omitempty\"`\n\tForceFullCalc         bool    `xml:\"forceFullCalc,attr,omitempty\"`\n\tFullCalcOnLoad        bool    `xml:\"fullCalcOnLoad,attr,omitempty\"`\n\tFullPrecision         bool    `xml:\"fullPrecision,attr,omitempty\"`\n\tIterate               bool    `xml:\"iterate,attr,omitempty\"`\n\tIterateCount          int     `xml:\"iterateCount,attr,omitempty\"`\n\tIterateDelta          float64 `xml:\"iterateDelta,attr,omitempty\"`\n\tRefMode               string  `xml:\"refMode,attr,omitempty\"`\n}\n\n// xlsxCustomWorkbookViews defines the collection of custom workbook views that\n// are defined for this workbook. A customWorkbookView is similar in concept to\n// a workbookView in that its attributes contain settings related to the way\n// that the workbook should be displayed on a screen by a spreadsheet\n// application.\ntype xlsxCustomWorkbookViews struct {\n\tCustomWorkbookView []xlsxCustomWorkbookView `xml:\"customWorkbookView\"`\n}\n\n// xlsxCustomWorkbookView directly maps the customWorkbookView element. This\n// element specifies a single custom workbook view. A custom workbook view\n// consists of a set of display and print settings that you can name and apply\n// to a workbook. You can create more than one custom workbook view of the same\n// workbook. Custom Workbook Views are not required in order to construct a\n// valid SpreadsheetML document, and are not necessary if the document is never\n// displayed by a spreadsheet application, or if the spreadsheet application has\n// a fixed display for workbooks. However, if a spreadsheet application chooses\n// to implement configurable display modes, the customWorkbookView element\n// should be used to persist the settings for those display modes.\ntype xlsxCustomWorkbookView struct {\n\tActiveSheetID        *int     `xml:\"activeSheetId,attr\"`\n\tAutoUpdate           *bool    `xml:\"autoUpdate,attr\"`\n\tChangesSavedWin      *bool    `xml:\"changesSavedWin,attr\"`\n\tGUID                 *string  `xml:\"guid,attr\"`\n\tIncludeHiddenRowCol  *bool    `xml:\"includeHiddenRowCol,attr\"`\n\tIncludePrintSettings *bool    `xml:\"includePrintSettings,attr\"`\n\tMaximized            *bool    `xml:\"maximized,attr\"`\n\tMergeInterval        int      `xml:\"mergeInterval,attr\"`\n\tMinimized            *bool    `xml:\"minimized,attr\"`\n\tName                 *string  `xml:\"name,attr\"`\n\tOnlySync             *bool    `xml:\"onlySync,attr\"`\n\tPersonalView         *bool    `xml:\"personalView,attr\"`\n\tShowComments         *string  `xml:\"showComments,attr\"`\n\tShowFormulaBar       *bool    `xml:\"showFormulaBar,attr\"`\n\tShowHorizontalScroll *bool    `xml:\"showHorizontalScroll,attr\"`\n\tShowObjects          *string  `xml:\"showObjects,attr\"`\n\tShowSheetTabs        *bool    `xml:\"showSheetTabs,attr\"`\n\tShowStatusbar        *bool    `xml:\"showStatusbar,attr\"`\n\tShowVerticalScroll   *bool    `xml:\"showVerticalScroll,attr\"`\n\tTabRatio             *float64 `xml:\"tabRatio,attr\"`\n\tWindowHeight         *int     `xml:\"windowHeight,attr\"`\n\tWindowWidth          *int     `xml:\"windowWidth,attr\"`\n\tXWindow              *int     `xml:\"xWindow,attr\"`\n\tYWindow              *int     `xml:\"yWindow,attr\"`\n}\n\n// DefinedName directly maps the name for a cell or cell range on a\n// worksheet.\ntype DefinedName struct {\n\tName     string\n\tComment  string\n\tRefersTo string\n\tScope    string\n}\n\n// CalcPropsOptions defines the collection of properties the application uses to\n// record calculation status and details.\ntype CalcPropsOptions struct {\n\tCalcID                *uint\n\tCalcMode              *string\n\tFullCalcOnLoad        *bool\n\tRefMode               *string\n\tIterate               *bool\n\tIterateCount          *uint\n\tIterateDelta          *float64\n\tFullPrecision         *bool\n\tCalcCompleted         *bool\n\tCalcOnSave            *bool\n\tConcurrentCalc        *bool\n\tConcurrentManualCount *uint\n\tForceFullCalc         *bool\n}\n\n// WorkbookPropsOptions directly maps the settings of workbook proprieties.\ntype WorkbookPropsOptions struct {\n\tDate1904      *bool\n\tFilterPrivacy *bool\n\tCodeName      *string\n}\n\n// WorkbookProtectionOptions directly maps the settings of workbook protection.\ntype WorkbookProtectionOptions struct {\n\tAlgorithmName string\n\tPassword      string\n\tLockStructure bool\n\tLockWindows   bool\n}\n"
  },
  {
    "path": "xmlWorksheet.go",
    "content": "// Copyright 2016 - 2026 The excelize Authors. All rights reserved. Use of\n// this source code is governed by a BSD-style license that can be found in\n// the LICENSE file.\n//\n// Package excelize providing a set of functions that allow you to write to and\n// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and\n// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.\n// Supports complex components by high compatibility, and provided streaming\n// API for generating or reading data from a worksheet with huge amounts of\n// data. This library needs Go version 1.25.0 or later.\n\npackage excelize\n\nimport (\n\t\"encoding/xml\"\n\t\"sync\"\n)\n\n// xlsxWorksheet directly maps the worksheet element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main.\ntype xlsxWorksheet struct {\n\tmu                     sync.Mutex\n\tformulaSI              sync.Map\n\tXMLName                xml.Name                     `xml:\"http://schemas.openxmlformats.org/spreadsheetml/2006/main worksheet\"`\n\tSheetPr                *xlsxSheetPr                 `xml:\"sheetPr\"`\n\tDimension              *xlsxDimension               `xml:\"dimension\"`\n\tSheetViews             *xlsxSheetViews              `xml:\"sheetViews\"`\n\tSheetFormatPr          *xlsxSheetFormatPr           `xml:\"sheetFormatPr\"`\n\tCols                   *xlsxCols                    `xml:\"cols\"`\n\tSheetData              xlsxSheetData                `xml:\"sheetData\"`\n\tSheetCalcPr            *xlsxInnerXML                `xml:\"sheetCalcPr\"`\n\tSheetProtection        *xlsxSheetProtection         `xml:\"sheetProtection\"`\n\tProtectedRanges        *xlsxInnerXML                `xml:\"protectedRanges\"`\n\tScenarios              *xlsxInnerXML                `xml:\"scenarios\"`\n\tAutoFilter             *xlsxAutoFilter              `xml:\"autoFilter\"`\n\tSortState              *xlsxSortState               `xml:\"sortState\"`\n\tDataConsolidate        *xlsxInnerXML                `xml:\"dataConsolidate\"`\n\tCustomSheetViews       *xlsxCustomSheetViews        `xml:\"customSheetViews\"`\n\tMergeCells             *xlsxMergeCells              `xml:\"mergeCells\"`\n\tPhoneticPr             *xlsxPhoneticPr              `xml:\"phoneticPr\"`\n\tConditionalFormatting  []*xlsxConditionalFormatting `xml:\"conditionalFormatting\"`\n\tDataValidations        *xlsxDataValidations         `xml:\"dataValidations\"`\n\tHyperlinks             *xlsxHyperlinks              `xml:\"hyperlinks\"`\n\tPrintOptions           *xlsxPrintOptions            `xml:\"printOptions\"`\n\tPageMargins            *xlsxPageMargins             `xml:\"pageMargins\"`\n\tPageSetUp              *xlsxPageSetUp               `xml:\"pageSetup\"`\n\tHeaderFooter           *xlsxHeaderFooter            `xml:\"headerFooter\"`\n\tRowBreaks              *xlsxRowBreaks               `xml:\"rowBreaks\"`\n\tColBreaks              *xlsxColBreaks               `xml:\"colBreaks\"`\n\tCustomProperties       *xlsxInnerXML                `xml:\"customProperties\"`\n\tCellWatches            *xlsxInnerXML                `xml:\"cellWatches\"`\n\tIgnoredErrors          *xlsxIgnoredErrors           `xml:\"ignoredErrors\"`\n\tSmartTags              *xlsxInnerXML                `xml:\"smartTags\"`\n\tDrawing                *xlsxDrawing                 `xml:\"drawing\"`\n\tLegacyDrawing          *xlsxLegacyDrawing           `xml:\"legacyDrawing\"`\n\tLegacyDrawingHF        *xlsxLegacyDrawingHF         `xml:\"legacyDrawingHF\"`\n\tDrawingHF              *xlsxDrawingHF               `xml:\"drawingHF\"`\n\tPicture                *xlsxPicture                 `xml:\"picture\"`\n\tOleObjects             *xlsxInnerXML                `xml:\"oleObjects\"`\n\tControls               *xlsxInnerXML                `xml:\"controls\"`\n\tWebPublishItems        *xlsxInnerXML                `xml:\"webPublishItems\"`\n\tAlternateContent       *xlsxAlternateContent        `xml:\"mc:AlternateContent\"`\n\tTableParts             *xlsxTableParts              `xml:\"tableParts\"`\n\tExtLst                 *xlsxExtLst                  `xml:\"extLst\"`\n\tDecodeAlternateContent *xlsxInnerXML                `xml:\"http://schemas.openxmlformats.org/markup-compatibility/2006 AlternateContent\"`\n}\n\n// xlsxDrawing change r:id to rid in the namespace.\ntype xlsxDrawing struct {\n\tXMLName xml.Name `xml:\"drawing\"`\n\tRID     string   `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty\"`\n}\n\n// xlsxHeaderFooter directly maps the headerFooter element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main - When printed or\n// viewed in page layout view (§18.18.69), each page of a worksheet can have a\n// page header, a page footer, or both. The headers and footers on odd-numbered\n// pages can differ from those on even-numbered pages, and the headers and\n// footers on the first page can differ from those on odd- and even-numbered\n// pages. In the latter case, the first page is not considered an odd page.\ntype xlsxHeaderFooter struct {\n\tXMLName          xml.Name `xml:\"headerFooter\"`\n\tDifferentOddEven bool     `xml:\"differentOddEven,attr,omitempty\"`\n\tDifferentFirst   bool     `xml:\"differentFirst,attr,omitempty\"`\n\tScaleWithDoc     *bool    `xml:\"scaleWithDoc,attr\"`\n\tAlignWithMargins *bool    `xml:\"alignWithMargins,attr\"`\n\tOddHeader        string   `xml:\"oddHeader,omitempty\"`\n\tOddFooter        string   `xml:\"oddFooter,omitempty\"`\n\tEvenHeader       string   `xml:\"evenHeader,omitempty\"`\n\tEvenFooter       string   `xml:\"evenFooter,omitempty\"`\n\tFirstHeader      string   `xml:\"firstHeader,omitempty\"`\n\tFirstFooter      string   `xml:\"firstFooter,omitempty\"`\n}\n\n// xlsxDrawingHF (Drawing Reference in Header Footer) specifies the usage of\n// drawing objects to be rendered in the headers and footers of the sheet. It\n// specifies an explicit relationship to the part containing the DrawingML\n// shapes used in the headers and footers. It also indicates where in the\n// headers and footers each shape belongs. One drawing object can appear in\n// each of the left section, center section and right section of a header and\n// a footer.\ntype xlsxDrawingHF struct {\n\tContent string `xml:\",innerxml\"`\n}\n\n// xlsxPageSetUp directly maps the pageSetup element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Page setup\n// settings for the worksheet.\ntype xlsxPageSetUp struct {\n\tXMLName            xml.Name `xml:\"pageSetup\"`\n\tBlackAndWhite      bool     `xml:\"blackAndWhite,attr,omitempty\"`\n\tCellComments       string   `xml:\"cellComments,attr,omitempty\"`\n\tCopies             int      `xml:\"copies,attr,omitempty\"`\n\tDraft              bool     `xml:\"draft,attr,omitempty\"`\n\tErrors             string   `xml:\"errors,attr,omitempty\"`\n\tFirstPageNumber    string   `xml:\"firstPageNumber,attr,omitempty\"`\n\tFitToHeight        *int     `xml:\"fitToHeight,attr\"`\n\tFitToWidth         *int     `xml:\"fitToWidth,attr\"`\n\tHorizontalDPI      string   `xml:\"horizontalDpi,attr,omitempty\"`\n\tRID                string   `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty\"`\n\tOrientation        string   `xml:\"orientation,attr,omitempty\"`\n\tPageOrder          string   `xml:\"pageOrder,attr,omitempty\"`\n\tPaperHeight        string   `xml:\"paperHeight,attr,omitempty\"`\n\tPaperSize          *int     `xml:\"paperSize,attr\"`\n\tPaperWidth         string   `xml:\"paperWidth,attr,omitempty\"`\n\tScale              int      `xml:\"scale,attr,omitempty\"`\n\tUseFirstPageNumber bool     `xml:\"useFirstPageNumber,attr,omitempty\"`\n\tUsePrinterDefaults bool     `xml:\"usePrinterDefaults,attr,omitempty\"`\n\tVerticalDPI        string   `xml:\"verticalDpi,attr,omitempty\"`\n}\n\n// xlsxPrintOptions directly maps the printOptions element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Print options for\n// the sheet. Printer-specific settings are stored separately in the Printer\n// Settings part.\ntype xlsxPrintOptions struct {\n\tXMLName            xml.Name `xml:\"printOptions\"`\n\tGridLines          bool     `xml:\"gridLines,attr,omitempty\"`\n\tGridLinesSet       bool     `xml:\"gridLinesSet,attr,omitempty\"`\n\tHeadings           bool     `xml:\"headings,attr,omitempty\"`\n\tHorizontalCentered bool     `xml:\"horizontalCentered,attr,omitempty\"`\n\tVerticalCentered   bool     `xml:\"verticalCentered,attr,omitempty\"`\n}\n\n// xlsxPageMargins directly maps the pageMargins element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Page margins for\n// a sheet or a custom sheet view.\ntype xlsxPageMargins struct {\n\tXMLName xml.Name `xml:\"pageMargins\"`\n\tLeft    float64  `xml:\"left,attr\"`\n\tRight   float64  `xml:\"right,attr\"`\n\tTop     float64  `xml:\"top,attr\"`\n\tBottom  float64  `xml:\"bottom,attr\"`\n\tHeader  float64  `xml:\"header,attr\"`\n\tFooter  float64  `xml:\"footer,attr\"`\n}\n\n// xlsxSheetFormatPr directly maps the sheetFormatPr element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main. This element\n// specifies the sheet formatting properties.\ntype xlsxSheetFormatPr struct {\n\tXMLName          xml.Name `xml:\"sheetFormatPr\"`\n\tBaseColWidth     uint8    `xml:\"baseColWidth,attr,omitempty\"`\n\tDefaultColWidth  float64  `xml:\"defaultColWidth,attr,omitempty\"`\n\tDefaultRowHeight float64  `xml:\"defaultRowHeight,attr\"`\n\tCustomHeight     bool     `xml:\"customHeight,attr,omitempty\"`\n\tZeroHeight       bool     `xml:\"zeroHeight,attr,omitempty\"`\n\tThickTop         bool     `xml:\"thickTop,attr,omitempty\"`\n\tThickBottom      bool     `xml:\"thickBottom,attr,omitempty\"`\n\tOutlineLevelRow  uint8    `xml:\"outlineLevelRow,attr,omitempty\"`\n\tOutlineLevelCol  uint8    `xml:\"outlineLevelCol,attr,omitempty\"`\n}\n\n// xlsxSheetViews represents worksheet views collection.\ntype xlsxSheetViews struct {\n\tXMLName   xml.Name        `xml:\"sheetViews\"`\n\tSheetView []xlsxSheetView `xml:\"sheetView\"`\n}\n\n// xlsxSheetView represents a single sheet view definition. When more than one\n// sheet view is defined in the file, it means that when opening the workbook,\n// each sheet view corresponds to a separate window within the spreadsheet\n// application, where each window is showing the particular sheet containing\n// the same workbookViewId value, the last sheetView definition is loaded, and\n// the others are discarded. When multiple windows are viewing the same sheet,\n// multiple sheetView elements (with corresponding workbookView entries) are\n// saved.\ntype xlsxSheetView struct {\n\tWindowProtection         bool             `xml:\"windowProtection,attr,omitempty\"`\n\tShowFormulas             bool             `xml:\"showFormulas,attr,omitempty\"`\n\tShowGridLines            *bool            `xml:\"showGridLines,attr\"`\n\tShowRowColHeaders        *bool            `xml:\"showRowColHeaders,attr\"`\n\tShowZeros                *bool            `xml:\"showZeros,attr,omitempty\"`\n\tRightToLeft              bool             `xml:\"rightToLeft,attr,omitempty\"`\n\tTabSelected              bool             `xml:\"tabSelected,attr,omitempty\"`\n\tShowRuler                *bool            `xml:\"showRuler,attr,omitempty\"`\n\tShowWhiteSpace           *bool            `xml:\"showWhiteSpace,attr\"`\n\tShowOutlineSymbols       bool             `xml:\"showOutlineSymbols,attr,omitempty\"`\n\tDefaultGridColor         *bool            `xml:\"defaultGridColor,attr\"`\n\tView                     string           `xml:\"view,attr,omitempty\"`\n\tTopLeftCell              string           `xml:\"topLeftCell,attr,omitempty\"`\n\tColorID                  int              `xml:\"colorId,attr,omitempty\"`\n\tZoomScale                float64          `xml:\"zoomScale,attr,omitempty\"`\n\tZoomScaleNormal          float64          `xml:\"zoomScaleNormal,attr,omitempty\"`\n\tZoomScalePageLayoutView  float64          `xml:\"zoomScalePageLayoutView,attr,omitempty\"`\n\tZoomScaleSheetLayoutView float64          `xml:\"zoomScaleSheetLayoutView,attr,omitempty\"`\n\tWorkbookViewID           int              `xml:\"workbookViewId,attr\"`\n\tPane                     *xlsxPane        `xml:\"pane,omitempty\"`\n\tSelection                []*xlsxSelection `xml:\"selection\"`\n}\n\n// xlsxSelection directly maps the selection element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Worksheet view\n// selection.\ntype xlsxSelection struct {\n\tActiveCell   string `xml:\"activeCell,attr,omitempty\"`\n\tActiveCellID *int   `xml:\"activeCellId,attr\"`\n\tPane         string `xml:\"pane,attr,omitempty\"`\n\tSQRef        string `xml:\"sqref,attr,omitempty\"`\n}\n\n// xlsxSelection directly maps the selection element. Worksheet view pane.\ntype xlsxPane struct {\n\tActivePane  string  `xml:\"activePane,attr,omitempty\"`\n\tState       string  `xml:\"state,attr,omitempty\"` // Either \"split\" or \"frozen\"\n\tTopLeftCell string  `xml:\"topLeftCell,attr,omitempty\"`\n\tXSplit      float64 `xml:\"xSplit,attr,omitempty\"`\n\tYSplit      float64 `xml:\"ySplit,attr,omitempty\"`\n}\n\n// xlsxSheetPr directly maps the sheetPr element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Sheet-level\n// properties.\ntype xlsxSheetPr struct {\n\tXMLName                           xml.Name         `xml:\"sheetPr\"`\n\tSyncHorizontal                    bool             `xml:\"syncHorizontal,attr,omitempty\"`\n\tSyncVertical                      bool             `xml:\"syncVertical,attr,omitempty\"`\n\tSyncRef                           string           `xml:\"syncRef,attr,omitempty\"`\n\tTransitionEvaluation              bool             `xml:\"transitionEvaluation,attr,omitempty\"`\n\tTransitionEntry                   bool             `xml:\"transitionEntry,attr,omitempty\"`\n\tPublished                         *bool            `xml:\"published,attr\"`\n\tCodeName                          string           `xml:\"codeName,attr,omitempty\"`\n\tFilterMode                        bool             `xml:\"filterMode,attr,omitempty\"`\n\tEnableFormatConditionsCalculation *bool            `xml:\"enableFormatConditionsCalculation,attr\"`\n\tTabColor                          *xlsxColor       `xml:\"tabColor\"`\n\tOutlinePr                         *xlsxOutlinePr   `xml:\"outlinePr\"`\n\tPageSetUpPr                       *xlsxPageSetUpPr `xml:\"pageSetUpPr\"`\n}\n\n// xlsxOutlinePr maps to the outlinePr element. SummaryBelow allows you to\n// adjust the direction of grouper controls.\ntype xlsxOutlinePr struct {\n\tApplyStyles        *bool `xml:\"applyStyles,attr\"`\n\tSummaryBelow       *bool `xml:\"summaryBelow,attr\"`\n\tSummaryRight       *bool `xml:\"summaryRight,attr\"`\n\tShowOutlineSymbols *bool `xml:\"showOutlineSymbols,attr\"`\n}\n\n// xlsxPageSetUpPr expresses page setup properties of the worksheet.\ntype xlsxPageSetUpPr struct {\n\tAutoPageBreaks bool `xml:\"autoPageBreaks,attr,omitempty\"`\n\tFitToPage      bool `xml:\"fitToPage,attr,omitempty\"`\n}\n\n// xlsxCols defines column width and column formatting for one or more columns\n// of the worksheet.\ntype xlsxCols struct {\n\tXMLName xml.Name  `xml:\"cols\"`\n\tCol     []xlsxCol `xml:\"col\"`\n}\n\n// xlsxCol directly maps the col (Column Width & Formatting). Defines column\n// width and column formatting for one or more columns of the worksheet.\ntype xlsxCol struct {\n\tBestFit      bool     `xml:\"bestFit,attr,omitempty\"`\n\tCollapsed    bool     `xml:\"collapsed,attr,omitempty\"`\n\tCustomWidth  bool     `xml:\"customWidth,attr,omitempty\"`\n\tHidden       bool     `xml:\"hidden,attr,omitempty\"`\n\tMax          int      `xml:\"max,attr\"`\n\tMin          int      `xml:\"min,attr\"`\n\tOutlineLevel uint8    `xml:\"outlineLevel,attr,omitempty\"`\n\tPhonetic     bool     `xml:\"phonetic,attr,omitempty\"`\n\tStyle        int      `xml:\"style,attr,omitempty\"`\n\tWidth        *float64 `xml:\"width,attr\"`\n}\n\n// xlsxDimension directly maps the dimension element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main - This element\n// specifies the used range of the worksheet. It specifies the row and column\n// bounds of used cells in the worksheet. This is optional and is not\n// required. Used cells include cells with formulas, text content, and cell\n// formatting. When an entire column is formatted, only the first cell in that\n// column is considered used.\ntype xlsxDimension struct {\n\tXMLName xml.Name `xml:\"dimension\"`\n\tRef     string   `xml:\"ref,attr\"`\n}\n\n// xlsxSheetData collection represents the cell table itself. This collection\n// expresses information about each cell, grouped together by rows in the\n// worksheet.\ntype xlsxSheetData struct {\n\tXMLName xml.Name  `xml:\"sheetData\"`\n\tRow     []xlsxRow `xml:\"row\"`\n}\n\n// xlsxRow directly maps the row element. The element expresses information\n// about an entire row of a worksheet, and contains all cell definitions for a\n// particular row in the worksheet.\ntype xlsxRow struct {\n\tC            []xlsxC  `xml:\"c\"`\n\tR            int      `xml:\"r,attr,omitempty\"`\n\tSpans        string   `xml:\"spans,attr,omitempty\"`\n\tS            int      `xml:\"s,attr,omitempty\"`\n\tCustomFormat bool     `xml:\"customFormat,attr,omitempty\"`\n\tHt           *float64 `xml:\"ht,attr\"`\n\tHidden       bool     `xml:\"hidden,attr,omitempty\"`\n\tCustomHeight bool     `xml:\"customHeight,attr,omitempty\"`\n\tOutlineLevel uint8    `xml:\"outlineLevel,attr,omitempty\"`\n\tCollapsed    bool     `xml:\"collapsed,attr,omitempty\"`\n\tThickTop     bool     `xml:\"thickTop,attr,omitempty\"`\n\tThickBot     bool     `xml:\"thickBot,attr,omitempty\"`\n\tPh           bool     `xml:\"ph,attr,omitempty\"`\n}\n\n// xlsxSortState directly maps the sortState element. This collection\n// preserves the AutoFilter sort state.\ntype xlsxSortState struct {\n\tColumnSort    bool   `xml:\"columnSort,attr,omitempty\"`\n\tCaseSensitive bool   `xml:\"caseSensitive,attr,omitempty\"`\n\tSortMethod    string `xml:\"sortMethod,attr,omitempty\"`\n\tRef           string `xml:\"ref,attr\"`\n\tContent       string `xml:\",innerxml\"`\n}\n\n// xlsxCustomSheetViews directly maps the customSheetViews element. This is a\n// collection of custom sheet views.\ntype xlsxCustomSheetViews struct {\n\tXMLName         xml.Name               `xml:\"customSheetViews\"`\n\tCustomSheetView []*xlsxCustomSheetView `xml:\"customSheetView\"`\n}\n\n// xlsxBrk directly maps the row or column break to use when paginating a\n// worksheet.\ntype xlsxBrk struct {\n\tID  int  `xml:\"id,attr,omitempty\"`\n\tMin int  `xml:\"min,attr,omitempty\"`\n\tMax int  `xml:\"max,attr,omitempty\"`\n\tMan bool `xml:\"man,attr,omitempty\"`\n\tPt  bool `xml:\"pt,attr,omitempty\"`\n}\n\n// xlsxRowBreaks directly maps a collection of the row breaks.\ntype xlsxRowBreaks struct {\n\tXMLName xml.Name `xml:\"rowBreaks\"`\n\txlsxBreaks\n}\n\n// xlsxRowBreaks directly maps a collection of the column breaks.\ntype xlsxColBreaks struct {\n\tXMLName xml.Name `xml:\"colBreaks\"`\n\txlsxBreaks\n}\n\n// xlsxBreaks directly maps a collection of the row or column breaks.\ntype xlsxBreaks struct {\n\tBrk              []*xlsxBrk `xml:\"brk\"`\n\tCount            int        `xml:\"count,attr,omitempty\"`\n\tManualBreakCount int        `xml:\"manualBreakCount,attr,omitempty\"`\n}\n\n// xlsxCustomSheetView directly maps the customSheetView element.\ntype xlsxCustomSheetView struct {\n\tPane           *xlsxPane         `xml:\"pane\"`\n\tSelection      *xlsxSelection    `xml:\"selection\"`\n\tRowBreaks      *xlsxBreaks       `xml:\"rowBreaks\"`\n\tColBreaks      *xlsxBreaks       `xml:\"colBreaks\"`\n\tPageMargins    *xlsxPageMargins  `xml:\"pageMargins\"`\n\tPrintOptions   *xlsxPrintOptions `xml:\"printOptions\"`\n\tPageSetup      *xlsxPageSetUp    `xml:\"pageSetup\"`\n\tHeaderFooter   *xlsxHeaderFooter `xml:\"headerFooter\"`\n\tAutoFilter     *xlsxAutoFilter   `xml:\"autoFilter\"`\n\tExtLst         *xlsxExtLst       `xml:\"extLst\"`\n\tGUID           string            `xml:\"guid,attr\"`\n\tScale          int               `xml:\"scale,attr,omitempty\"`\n\tColorID        int               `xml:\"colorId,attr,omitempty\"`\n\tShowPageBreaks bool              `xml:\"showPageBreaks,attr,omitempty\"`\n\tShowFormulas   bool              `xml:\"showFormulas,attr,omitempty\"`\n\tShowGridLines  bool              `xml:\"showGridLines,attr,omitempty\"`\n\tShowRowCol     bool              `xml:\"showRowCol,attr,omitempty\"`\n\tOutlineSymbols bool              `xml:\"outlineSymbols,attr,omitempty\"`\n\tZeroValues     bool              `xml:\"zeroValues,attr,omitempty\"`\n\tFitToPage      bool              `xml:\"fitToPage,attr,omitempty\"`\n\tPrintArea      bool              `xml:\"printArea,attr,omitempty\"`\n\tFilter         bool              `xml:\"filter,attr,omitempty\"`\n\tShowAutoFilter bool              `xml:\"showAutoFilter,attr,omitempty\"`\n\tHiddenRows     bool              `xml:\"hiddenRows,attr,omitempty\"`\n\tHiddenColumns  bool              `xml:\"hiddenColumns,attr,omitempty\"`\n\tState          string            `xml:\"state,attr,omitempty\"`\n\tFilterUnique   bool              `xml:\"filterUnique,attr,omitempty\"`\n\tView           string            `xml:\"view,attr,omitempty\"`\n\tShowRuler      bool              `xml:\"showRuler,attr,omitempty\"`\n\tTopLeftCell    string            `xml:\"topLeftCell,attr,omitempty\"`\n}\n\n// xlsxMergeCell directly maps the mergeCell element. A single merged cell.\ntype xlsxMergeCell struct {\n\tRef  string `xml:\"ref,attr,omitempty\"`\n\trect []int\n}\n\n// xlsxMergeCells directly maps the mergeCells element. This collection\n// expresses all the merged cells in the sheet.\ntype xlsxMergeCells struct {\n\tXMLName xml.Name         `xml:\"mergeCells\"`\n\tCount   int              `xml:\"count,attr,omitempty\"`\n\tCells   []*xlsxMergeCell `xml:\"mergeCell,omitempty\"`\n}\n\n// xlsxDataValidations expresses all data validation information for cells in a\n// sheet which have data validation features applied.\ntype xlsxDataValidations struct {\n\tXMLName        xml.Name              `xml:\"dataValidations\"`\n\tCount          int                   `xml:\"count,attr,omitempty\"`\n\tDisablePrompts bool                  `xml:\"disablePrompts,attr,omitempty\"`\n\tXWindow        int                   `xml:\"xWindow,attr,omitempty\"`\n\tYWindow        int                   `xml:\"yWindow,attr,omitempty\"`\n\tDataValidation []*xlsxDataValidation `xml:\"dataValidation\"`\n}\n\n// xlsxDataValidation directly maps the single item of data validation defined\n// on a range of the worksheet.\ntype xlsxDataValidation struct {\n\tAllowBlank       bool          `xml:\"allowBlank,attr\"`\n\tError            *string       `xml:\"error,attr\"`\n\tErrorStyle       *string       `xml:\"errorStyle,attr\"`\n\tErrorTitle       *string       `xml:\"errorTitle,attr\"`\n\tOperator         string        `xml:\"operator,attr,omitempty\"`\n\tPrompt           *string       `xml:\"prompt,attr\"`\n\tPromptTitle      *string       `xml:\"promptTitle,attr\"`\n\tShowDropDown     bool          `xml:\"showDropDown,attr,omitempty\"`\n\tShowErrorMessage bool          `xml:\"showErrorMessage,attr,omitempty\"`\n\tShowInputMessage bool          `xml:\"showInputMessage,attr,omitempty\"`\n\tSqref            string        `xml:\"sqref,attr\"`\n\tXMSqref          string        `xml:\"sqref,omitempty\"`\n\tType             string        `xml:\"type,attr,omitempty\"`\n\tFormula1         *xlsxInnerXML `xml:\"formula1\"`\n\tFormula2         *xlsxInnerXML `xml:\"formula2\"`\n}\n\n// xlsxX14DataValidation directly maps the single item of data validation\n// defined on a extLst element of the worksheet.\ntype xlsxX14DataValidation struct {\n\tXMLName          xml.Name      `xml:\"x14:dataValidation\"`\n\tAllowBlank       bool          `xml:\"allowBlank,attr\"`\n\tError            *string       `xml:\"error,attr\"`\n\tErrorStyle       *string       `xml:\"errorStyle,attr\"`\n\tErrorTitle       *string       `xml:\"errorTitle,attr\"`\n\tOperator         string        `xml:\"operator,attr,omitempty\"`\n\tPrompt           *string       `xml:\"prompt,attr\"`\n\tPromptTitle      *string       `xml:\"promptTitle,attr\"`\n\tShowDropDown     bool          `xml:\"showDropDown,attr,omitempty\"`\n\tShowErrorMessage bool          `xml:\"showErrorMessage,attr,omitempty\"`\n\tShowInputMessage bool          `xml:\"showInputMessage,attr,omitempty\"`\n\tSqref            string        `xml:\"sqref,attr\"`\n\tType             string        `xml:\"type,attr,omitempty\"`\n\tFormula1         *xlsxInnerXML `xml:\"x14:formula1\"`\n\tFormula2         *xlsxInnerXML `xml:\"x14:formula2\"`\n\tXMSqref          string        `xml:\"xm:sqref,omitempty\"`\n}\n\n// xlsxX14DataValidations expresses all data validation information for cells in\n// a sheet extLst element which have data validation features applied.\ntype xlsxX14DataValidations struct {\n\tXMLName        xml.Name `xml:\"x14:dataValidations\"`\n\tXMLNSXM        string   `xml:\"xmlns:xm,attr,omitempty\"`\n\tCount          int      `xml:\"count,attr,omitempty\"`\n\tDisablePrompts bool     `xml:\"disablePrompts,attr,omitempty\"`\n\tXWindow        int      `xml:\"xWindow,attr,omitempty\"`\n\tYWindow        int      `xml:\"yWindow,attr,omitempty\"`\n\tDataValidation []*xlsxX14DataValidation\n}\n\n// xlsxC collection represents a cell in the worksheet. Information about the\n// cell's location (reference), value, data type, formatting, and formula is\n// expressed here.\n//\n// This simple type is restricted to the values listed in the following table:\n//\n//\t Enumeration Value         | Description\n//\t---------------------------+---------------------------------\n//\t b (Boolean)               | Cell containing a boolean.\n//\t d (Date)                  | Cell contains a date in the ISO 8601 format.\n//\t e (Error)                 | Cell containing an error.\n//\t inlineStr (Inline String) | Cell containing an (inline) rich string, i.e.,\n//\t                           | one not in the shared string table. If this\n//\t                           | cell type is used, then the cell value is in\n//\t                           | the is element rather than the v element in\n//\t                           | the cell (c element).\n//\t n (Number)                | Cell containing a number.\n//\t s (Shared String)         | Cell containing a shared string.\n//\t str (String)              | Cell containing a formula string.\ntype xlsxC struct {\n\tXMLName  xml.Name `xml:\"c\"`\n\tXMLSpace xml.Attr `xml:\"space,attr,omitempty\"`\n\tR        string   `xml:\"r,attr,omitempty\"` // Cell ID, e.g. A1\n\tS        int      `xml:\"s,attr,omitempty\"` // Style reference\n\tT        string   `xml:\"t,attr,omitempty\"` // Type\n\tCm       *uint    `xml:\"cm,attr\"`\n\tVm       *uint    `xml:\"vm,attr\"`\n\tPh       *bool    `xml:\"ph,attr\"`\n\tF        *xlsxF   `xml:\"f\"`           // Formula\n\tV        string   `xml:\"v,omitempty\"` // Value\n\tIS       *xlsxSI  `xml:\"is\"`\n\tf        string\n}\n\n// xlsxF represents a formula for the cell. The formula expression is\n// contained in the character node of this element.\ntype xlsxF struct {\n\tContent string `xml:\",chardata\"`\n\tT       string `xml:\"t,attr,omitempty\"` // Formula type\n\tAca     bool   `xml:\"aca,attr,omitempty\"`\n\tRef     string `xml:\"ref,attr,omitempty\"` // Shared formula ref\n\tDt2D    bool   `xml:\"dt2D,attr,omitempty\"`\n\tDtr     bool   `xml:\"dtr,attr,omitempty\"`\n\tDel1    bool   `xml:\"del1,attr,omitempty\"`\n\tDel2    bool   `xml:\"del2,attr,omitempty\"`\n\tR1      string `xml:\"r1,attr,omitempty\"`\n\tR2      string `xml:\"r2,attr,omitempty\"`\n\tCa      bool   `xml:\"ca,attr,omitempty\"`\n\tSi      *int   `xml:\"si,attr\"` // Shared formula index\n\tBx      bool   `xml:\"bx,attr,omitempty\"`\n}\n\n// xlsxSheetProtection collection expresses the sheet protection options to\n// enforce when the sheet is protected.\ntype xlsxSheetProtection struct {\n\tXMLName             xml.Name `xml:\"sheetProtection\"`\n\tAlgorithmName       string   `xml:\"algorithmName,attr,omitempty\"`\n\tPassword            string   `xml:\"password,attr,omitempty\"`\n\tHashValue           string   `xml:\"hashValue,attr,omitempty\"`\n\tSaltValue           string   `xml:\"saltValue,attr,omitempty\"`\n\tSpinCount           int      `xml:\"spinCount,attr,omitempty\"`\n\tSheet               bool     `xml:\"sheet,attr\"`\n\tObjects             bool     `xml:\"objects,attr\"`\n\tScenarios           bool     `xml:\"scenarios,attr\"`\n\tFormatCells         bool     `xml:\"formatCells,attr\"`\n\tFormatColumns       bool     `xml:\"formatColumns,attr\"`\n\tFormatRows          bool     `xml:\"formatRows,attr\"`\n\tInsertColumns       bool     `xml:\"insertColumns,attr\"`\n\tInsertRows          bool     `xml:\"insertRows,attr\"`\n\tInsertHyperlinks    bool     `xml:\"insertHyperlinks,attr\"`\n\tDeleteColumns       bool     `xml:\"deleteColumns,attr\"`\n\tDeleteRows          bool     `xml:\"deleteRows,attr\"`\n\tSelectLockedCells   bool     `xml:\"selectLockedCells,attr\"`\n\tSort                bool     `xml:\"sort,attr\"`\n\tAutoFilter          bool     `xml:\"autoFilter,attr\"`\n\tPivotTables         bool     `xml:\"pivotTables,attr\"`\n\tSelectUnlockedCells bool     `xml:\"selectUnlockedCells,attr\"`\n}\n\n// xlsxPhoneticPr (Phonetic Properties) represents a collection of phonetic\n// properties that affect the display of phonetic text for this String Item\n// (si). Phonetic text is used to give hints as to the pronunciation of an East\n// Asian language, and the hints are displayed as text within the spreadsheet\n// cells across the top portion of the cell. Since the phonetic hints are text,\n// every phonetic hint is expressed as a phonetic run (rPh), and these\n// properties specify how to display that phonetic run.\ntype xlsxPhoneticPr struct {\n\tXMLName   xml.Name `xml:\"phoneticPr\"`\n\tAlignment string   `xml:\"alignment,attr,omitempty\"`\n\tFontID    *int     `xml:\"fontId,attr\"`\n\tType      string   `xml:\"type,attr,omitempty\"`\n}\n\n// A Conditional Format is a format, such as cell shading or font color, that a\n// spreadsheet application can automatically apply to cells if a specified\n// condition is true. This collection expresses conditional formatting rules\n// applied to a particular cell or range.\ntype xlsxConditionalFormatting struct {\n\tXMLName xml.Name      `xml:\"conditionalFormatting\"`\n\tPivot   bool          `xml:\"pivot,attr,omitempty\"`\n\tSQRef   string        `xml:\"sqref,attr,omitempty\"`\n\tCfRule  []*xlsxCfRule `xml:\"cfRule\"`\n}\n\n// xlsxCfRule (Conditional Formatting Rule) represents a description of a\n// conditional formatting rule.\ntype xlsxCfRule struct {\n\tType         string          `xml:\"type,attr,omitempty\"`\n\tDxfID        *int            `xml:\"dxfId,attr\"`\n\tPriority     int             `xml:\"priority,attr,omitempty\"`\n\tStopIfTrue   bool            `xml:\"stopIfTrue,attr,omitempty\"`\n\tAboveAverage *bool           `xml:\"aboveAverage,attr\"`\n\tPercent      bool            `xml:\"percent,attr,omitempty\"`\n\tBottom       bool            `xml:\"bottom,attr,omitempty\"`\n\tOperator     string          `xml:\"operator,attr,omitempty\"`\n\tText         string          `xml:\"text,attr,omitempty\"`\n\tTimePeriod   string          `xml:\"timePeriod,attr,omitempty\"`\n\tRank         int             `xml:\"rank,attr,omitempty\"`\n\tStdDev       int             `xml:\"stdDev,attr,omitempty\"`\n\tEqualAverage bool            `xml:\"equalAverage,attr,omitempty\"`\n\tFormula      []string        `xml:\"formula,omitempty\"`\n\tColorScale   *xlsxColorScale `xml:\"colorScale\"`\n\tDataBar      *xlsxDataBar    `xml:\"dataBar\"`\n\tIconSet      *xlsxIconSet    `xml:\"iconSet\"`\n\tExtLst       *xlsxExtLst     `xml:\"extLst\"`\n}\n\n// xlsxColorScale (Color Scale) describes a gradated color scale in this\n// conditional formatting rule.\ntype xlsxColorScale struct {\n\tCfvo  []*xlsxCfvo  `xml:\"cfvo\"`\n\tColor []*xlsxColor `xml:\"color\"`\n}\n\n// dataBar (Data Bar) describes a data bar conditional formatting rule.\ntype xlsxDataBar struct {\n\tMaxLength int          `xml:\"maxLength,attr,omitempty\"`\n\tMinLength int          `xml:\"minLength,attr,omitempty\"`\n\tShowValue *bool        `xml:\"showValue,attr\"`\n\tCfvo      []*xlsxCfvo  `xml:\"cfvo\"`\n\tColor     []*xlsxColor `xml:\"color\"`\n}\n\n// xlsxIconSet (Icon Set) describes an icon set conditional formatting rule.\ntype xlsxIconSet struct {\n\tCfvo      []*xlsxCfvo `xml:\"cfvo\"`\n\tIconSet   string      `xml:\"iconSet,attr,omitempty\"`\n\tShowValue *bool       `xml:\"showValue,attr\"`\n\tPercent   bool        `xml:\"percent,attr,omitempty\"`\n\tReverse   bool        `xml:\"reverse,attr,omitempty\"`\n}\n\n// cfvo (Conditional Format Value Object) describes the values of the\n// interpolation points in a gradient scale.\ntype xlsxCfvo struct {\n\tGte    bool        `xml:\"gte,attr,omitempty\"`\n\tType   string      `xml:\"type,attr,omitempty\"`\n\tVal    string      `xml:\"val,attr,omitempty\"`\n\tExtLst *xlsxExtLst `xml:\"extLst\"`\n}\n\n// xlsxHyperlinks directly maps the hyperlinks element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main - A hyperlink can\n// be stored in a package as a relationship. Hyperlinks shall be identified by\n// containing a target which specifies the destination of the given hyperlink.\ntype xlsxHyperlinks struct {\n\tXMLName   xml.Name        `xml:\"hyperlinks\"`\n\tHyperlink []xlsxHyperlink `xml:\"hyperlink\"`\n}\n\n// xlsxHyperlink directly maps the hyperlink element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main\ntype xlsxHyperlink struct {\n\tRef      string `xml:\"ref,attr\"`\n\tLocation string `xml:\"location,attr,omitempty\"`\n\tDisplay  string `xml:\"display,attr,omitempty\"`\n\tTooltip  string `xml:\"tooltip,attr,omitempty\"`\n\tRID      string `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty\"`\n}\n\n// xlsxTableParts directly maps the tableParts element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main - The table element\n// has several attributes applied to identify the table and the data range it\n// covers. The table id attribute needs to be unique across all table parts, the\n// same goes for the name and displayName. The displayName has the further\n// restriction that it must be unique across all defined names in the workbook.\n// Later on we will see that you can define names for many elements, such as\n// cells or formulas. The name value is used for the object model in Microsoft\n// Office Excel. The displayName is used for references in formulas. The ref\n// attribute is used to identify the cell range that the table covers. This\n// includes not only the table data, but also the table header containing column\n// names.\n// To add columns to your table you add new tableColumn elements to the\n// tableColumns container. Similar to the shared string table the collection\n// keeps a count attribute identifying the number of columns. Besides the table\n// definition in the table part there is also the need to identify which tables\n// are displayed in the worksheet. The worksheet part has a separate element\n// tableParts to store this information. Each table part is referenced through\n// the relationship ID and again a count of the number of table parts is\n// maintained. The following markup sample is taken from the documents\n// accompanying this book. The sheet data element has been removed to reduce the\n// size of the sample. To reference the table, just add the tableParts element,\n// of course after having created and stored the table part. For example:\n//\n//\t   <worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">\n//\t       ...\n//\t       <tableParts count=\"1\">\n//\t\t\t      <tablePart r:id=\"rId1\" />\n//\t       </tableParts>\n//\t   </worksheet>\ntype xlsxTableParts struct {\n\tXMLName    xml.Name         `xml:\"tableParts\"`\n\tCount      int              `xml:\"count,attr,omitempty\"`\n\tTableParts []*xlsxTablePart `xml:\"tablePart\"`\n}\n\n// xlsxTablePart directly maps the tablePart element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main\ntype xlsxTablePart struct {\n\tRID string `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty\"`\n}\n\n// xlsxPicture directly maps the picture element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Background sheet\n// image. For example:\n//\n//\t<picture r:id=\"rId1\"/>\ntype xlsxPicture struct {\n\tXMLName xml.Name `xml:\"picture\"`\n\tRID     string   `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty\"`\n}\n\n// xlsxIgnoredError specifies a single ignored error for a range of cells.\ntype xlsxIgnoredError struct {\n\tXMLName            xml.Name `xml:\"ignoredError\"`\n\tSqref              string   `xml:\"sqref,attr\"`\n\tEvalError          bool     `xml:\"evalError,attr,omitempty\"`\n\tTwoDigitTextYear   bool     `xml:\"twoDigitTextYear,attr,omitempty\"`\n\tNumberStoredAsText bool     `xml:\"numberStoredAsText,attr,omitempty\"`\n\tFormula            bool     `xml:\"formula,attr,omitempty\"`\n\tFormulaRange       bool     `xml:\"formulaRange,attr,omitempty\"`\n\tUnlockedFormula    bool     `xml:\"unlockedFormula,attr,omitempty\"`\n\tEmptyCellReference bool     `xml:\"emptyCellReference,attr,omitempty\"`\n\tListDataValidation bool     `xml:\"listDataValidation,attr,omitempty\"`\n\tCalculatedColumn   bool     `xml:\"calculatedColumn,attr,omitempty\"`\n}\n\n// xlsxIgnoredErrors specifies a collection of ignored errors, by cell range.\ntype xlsxIgnoredErrors struct {\n\tXMLName      xml.Name           `xml:\"ignoredErrors\"`\n\tIgnoredError []xlsxIgnoredError `xml:\"ignoredError\"`\n\tExtLst       *xlsxExtLst        `xml:\"extLst\"`\n}\n\n// xlsxLegacyDrawing directly maps the legacyDrawing element in the namespace\n// http://schemas.openxmlformats.org/spreadsheetml/2006/main - A comment is a\n// rich text note that is attached to, and associated with, a cell, separate\n// from other cell content. Comment content is stored separate from the cell,\n// and is displayed in a drawing object (like a text box) that is separate from,\n// but associated with, a cell. Comments are used as reminders, such as noting\n// how a complex formula works, or to provide feedback to other users. Comments\n// can also be used to explain assumptions made in a formula or to call out\n// something special about the cell.\ntype xlsxLegacyDrawing struct {\n\tXMLName xml.Name `xml:\"legacyDrawing\"`\n\tRID     string   `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty\"`\n}\n\n// xlsxLegacyDrawingHF specifies the explicit relationship to the part\n// containing the VML defining pictures rendered in the header / footer of the\n// sheet.\ntype xlsxLegacyDrawingHF struct {\n\tXMLName xml.Name `xml:\"legacyDrawingHF\"`\n\tRID     string   `xml:\"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty\"`\n}\n\n// decodeX14SparklineGroups directly maps the sparklineGroups element.\ntype decodeX14SparklineGroups struct {\n\tXMLName xml.Name `xml:\"sparklineGroups\"`\n\tXMLNSXM string   `xml:\"xmlns:xm,attr\"`\n\tContent string   `xml:\",innerxml\"`\n}\n\n// decodeX14ConditionalFormattingExt directly maps the ext element.\ntype decodeX14ConditionalFormattingExt struct {\n\tXMLName xml.Name `xml:\"ext\"`\n\tID      string   `xml:\"id\"`\n}\n\n// decodeX14ConditionalFormattings directly maps the conditionalFormattings\n// element.\ntype decodeX14ConditionalFormattings struct {\n\tXMLName xml.Name `xml:\"conditionalFormattings\"`\n\tXMLNSXM string   `xml:\"xmlns:xm,attr\"`\n\tContent string   `xml:\",innerxml\"`\n}\n\n// decodeX14ConditionalFormattingRules directly maps the conditionalFormattings\n// element.\ntype decodeX14ConditionalFormattingRules struct {\n\tXMLName xml.Name                         `xml:\"conditionalFormattings\"`\n\tXMLNSXM string                           `xml:\"xmlns:xm,attr\"`\n\tCondFmt []decodeX14ConditionalFormatting `xml:\"conditionalFormatting\"`\n}\n\n// decodeX14ConditionalFormatting directly maps the conditionalFormatting\n// element.\ntype decodeX14ConditionalFormatting struct {\n\tXMLName xml.Name           `xml:\"conditionalFormatting\"`\n\tPivot   bool               `xml:\"pivot,attr,omitempty\"`\n\tCfRule  []*decodeX14CfRule `xml:\"cfRule\"`\n\tSqref   string             `xml:\"sqref,omitempty\"`\n\tExtLst  *xlsxExtLst        `xml:\"x14:extLst\"`\n}\n\n// decodeX14CfRule directly maps the cfRule element.\ntype decodeX14CfRule struct {\n\tXMLName       xml.Name          `xml:\"cfRule\"`\n\tType          string            `xml:\"type,attr,omitempty\"`\n\tPriority      int               `xml:\"priority,attr,omitempty\"`\n\tStopIfTrue    bool              `xml:\"stopIfTrue,attr,omitempty\"`\n\tAboveAverage  *bool             `xml:\"aboveAverage,attr\"`\n\tPercent       bool              `xml:\"percent,attr,omitempty\"`\n\tBottom        bool              `xml:\"bottom,attr,omitempty\"`\n\tOperator      string            `xml:\"operator,attr,omitempty\"`\n\tText          string            `xml:\"text,attr,omitempty\"`\n\tTimePeriod    string            `xml:\"timePeriod,attr,omitempty\"`\n\tRank          int               `xml:\"rank,attr,omitempty\"`\n\tStdDev        int               `xml:\"stdDev,attr,omitempty\"`\n\tEqualAverage  bool              `xml:\"equalAverage,attr,omitempty\"`\n\tActivePresent bool              `xml:\"activePresent,attr,omitempty\"`\n\tID            string            `xml:\"id,attr,omitempty\"`\n\tF             []string          `xml:\"f\"`\n\tColorScale    *xlsxInnerXML     `xml:\"colorScale\"`\n\tDataBar       *decodeX14DataBar `xml:\"dataBar\"`\n\tIconSet       *decodeX14IconSet `xml:\"iconSet\"`\n\tDxf           *xlsxInnerXML     `xml:\"dxf\"`\n\tExtLst        *xlsxExtLst       `xml:\"extLst\"`\n}\n\n// decodeX14DataBar directly maps the dataBar element.\ntype decodeX14DataBar struct {\n\tXMLName           xml.Name    `xml:\"dataBar\"`\n\tMaxLength         int         `xml:\"maxLength,attr\"`\n\tMinLength         int         `xml:\"minLength,attr\"`\n\tBorder            bool        `xml:\"border,attr,omitempty\"`\n\tGradient          *bool       `xml:\"gradient,attr\"`\n\tShowValue         bool        `xml:\"showValue,attr,omitempty\"`\n\tDirection         string      `xml:\"direction,attr,omitempty\"`\n\tCfvo              []*xlsxCfvo `xml:\"cfvo\"`\n\tBorderColor       *xlsxColor  `xml:\"borderColor\"`\n\tNegativeFillColor *xlsxColor  `xml:\"negativeFillColor\"`\n\tAxisColor         *xlsxColor  `xml:\"axisColor\"`\n}\n\n// decodeX14IconSet directly maps the iconSet element.\ntype decodeX14IconSet struct {\n\tXMLName   xml.Name         `xml:\"iconSet\"`\n\tIconSet   string           `xml:\"iconSet,attr,omitempty\"`\n\tShowValue *bool            `xml:\"showValue,attr\"`\n\tPercent   *bool            `xml:\"percent,attr\"`\n\tReverse   bool             `xml:\"reverse,attr,omitempty\"`\n\tCustom    bool             `xml:\"custom,attr,omitempty\"`\n\tCfvo      []*decodeX14Cfvo `xml:\"cfvo\"`\n\tCfIcon    []*xlsxInnerXML  `xml:\"cfIcon\"`\n}\n\n// decodeX14Cfvo directly maps the cfvo element.\ntype decodeX14Cfvo struct {\n\tXMLName xml.Name    `xml:\"cfvo\"`\n\tType    string      `xml:\"type,attr\"`\n\tGte     *bool       `xml:\"gte,attr\"`\n\tF       string      `xml:\"f\"`\n\tExtLst  *xlsxExtLst `xml:\"extLst\"`\n}\n\n// xlsxX14ConditionalFormattings directly maps the x14:conditionalFormattings\n// element.\ntype xlsxX14ConditionalFormattings struct {\n\tXMLName xml.Name `xml:\"x14:conditionalFormattings\"`\n\tContent string   `xml:\",innerxml\"`\n}\n\n// xlsxX14ConditionalFormatting directly maps the x14:conditionalFormatting\n// element.\ntype xlsxX14ConditionalFormatting struct {\n\tXMLName xml.Name         `xml:\"x14:conditionalFormatting\"`\n\tXMLNSXM string           `xml:\"xmlns:xm,attr\"`\n\tPivot   bool             `xml:\"pivot,attr,omitempty\"`\n\tCfRule  []*xlsxX14CfRule `xml:\"x14:cfRule\"`\n\tSqref   string           `xml:\"xm:sqref,omitempty\"`\n\tExtLst  *xlsxExtLst      `xml:\"x14:extLst\"`\n}\n\n// xlsxX14CfRule directly maps the x14:cfRule element.\ntype xlsxX14CfRule struct {\n\tType          string         `xml:\"type,attr,omitempty\"`\n\tPriority      int            `xml:\"priority,attr,omitempty\"`\n\tStopIfTrue    bool           `xml:\"stopIfTrue,attr,omitempty\"`\n\tAboveAverage  *bool          `xml:\"aboveAverage,attr\"`\n\tPercent       bool           `xml:\"percent,attr,omitempty\"`\n\tBottom        bool           `xml:\"bottom,attr,omitempty\"`\n\tOperator      string         `xml:\"operator,attr,omitempty\"`\n\tText          string         `xml:\"text,attr,omitempty\"`\n\tTimePeriod    string         `xml:\"timePeriod,attr,omitempty\"`\n\tRank          int            `xml:\"rank,attr,omitempty\"`\n\tStdDev        int            `xml:\"stdDev,attr,omitempty\"`\n\tEqualAverage  bool           `xml:\"equalAverage,attr,omitempty\"`\n\tActivePresent bool           `xml:\"activePresent,attr,omitempty\"`\n\tID            string         `xml:\"id,attr,omitempty\"`\n\tF             []string       `xml:\"xm:f\"`\n\tColorScale    *xlsxInnerXML  `xml:\"x14:colorScale\"`\n\tDataBar       *xlsx14DataBar `xml:\"x14:dataBar\"`\n\tIconSet       *xlsx14IconSet `xml:\"x14:iconSet\"`\n\tDxf           *xlsxInnerXML  `xml:\"x14:dxf\"`\n\tExtLst        *xlsxExtLst    `xml:\"x14:extLst\"`\n}\n\n// xlsx14DataBar directly maps the x14:dataBar element.\ntype xlsx14DataBar struct {\n\tMaxLength         int         `xml:\"maxLength,attr\"`\n\tMinLength         int         `xml:\"minLength,attr\"`\n\tBorder            bool        `xml:\"border,attr\"`\n\tGradient          bool        `xml:\"gradient,attr\"`\n\tShowValue         bool        `xml:\"showValue,attr,omitempty\"`\n\tDirection         string      `xml:\"direction,attr,omitempty\"`\n\tCfvo              []*xlsxCfvo `xml:\"x14:cfvo\"`\n\tBorderColor       *xlsxColor  `xml:\"x14:borderColor\"`\n\tNegativeFillColor *xlsxColor  `xml:\"x14:negativeFillColor\"`\n\tAxisColor         *xlsxColor  `xml:\"x14:axisColor\"`\n}\n\n// xlsx14IconSet directly maps the x14:iconSet element. This element specifies\n// the properties of a conditional formatting rule that uses an icon set.\ntype xlsx14IconSet struct {\n\tIconSet   string          `xml:\"iconSet,attr,omitempty\"`\n\tShowValue *bool           `xml:\"showValue,attr\"`\n\tPercent   *bool           `xml:\"percent,attr\"`\n\tReverse   bool            `xml:\"reverse,attr,omitempty\"`\n\tCustom    bool            `xml:\"custom,attr,omitempty\"`\n\tCfvo      []*xlsx14Cfvo   `xml:\"x14:cfvo\"`\n\tCfIcon    []*xlsxInnerXML `xml:\"x14:cfIcon\"`\n}\n\n// xlsx14IconSet directly maps the x14:cfvo element. This element specifies how\n// to calculate a value from the range of cells to which a conditional\n// formatting rule applies.\ntype xlsx14Cfvo struct {\n\tType   string      `xml:\"type,attr\"`\n\tGte    *bool       `xml:\"gte,attr\"`\n\tF      string      `xml:\"xm:f\"`\n\tExtLst *xlsxExtLst `xml:\"x14:extLst\"`\n}\n\n// xlsxX14SparklineGroups directly maps the x14:sparklineGroups element.\ntype xlsxX14SparklineGroups struct {\n\tXMLName         xml.Name                 `xml:\"x14:sparklineGroups\"`\n\tXMLNSXM         string                   `xml:\"xmlns:xm,attr\"`\n\tSparklineGroups []*xlsxX14SparklineGroup `xml:\"x14:sparklineGroup\"`\n\tContent         string                   `xml:\",innerxml\"`\n}\n\n// xlsxX14SparklineGroup directly maps the x14:sparklineGroup element.\ntype xlsxX14SparklineGroup struct {\n\tXMLName             xml.Name          `xml:\"x14:sparklineGroup\"`\n\tManualMax           int               `xml:\"manualMax,attr,omitempty\"`\n\tManualMin           int               `xml:\"manualMin,attr,omitempty\"`\n\tLineWeight          float64           `xml:\"lineWeight,attr,omitempty\"`\n\tType                string            `xml:\"type,attr,omitempty\"`\n\tDateAxis            bool              `xml:\"dateAxis,attr,omitempty\"`\n\tDisplayEmptyCellsAs string            `xml:\"displayEmptyCellsAs,attr,omitempty\"`\n\tMarkers             bool              `xml:\"markers,attr,omitempty\"`\n\tHigh                bool              `xml:\"high,attr,omitempty\"`\n\tLow                 bool              `xml:\"low,attr,omitempty\"`\n\tFirst               bool              `xml:\"first,attr,omitempty\"`\n\tLast                bool              `xml:\"last,attr,omitempty\"`\n\tNegative            bool              `xml:\"negative,attr,omitempty\"`\n\tDisplayXAxis        bool              `xml:\"displayXAxis,attr,omitempty\"`\n\tDisplayHidden       bool              `xml:\"displayHidden,attr,omitempty\"`\n\tMinAxisType         string            `xml:\"minAxisType,attr,omitempty\"`\n\tMaxAxisType         string            `xml:\"maxAxisType,attr,omitempty\"`\n\tRightToLeft         bool              `xml:\"rightToLeft,attr,omitempty\"`\n\tColorSeries         *xlsxColor        `xml:\"x14:colorSeries\"`\n\tColorNegative       *xlsxColor        `xml:\"x14:colorNegative\"`\n\tColorAxis           *xlsxColor        `xml:\"x14:colorAxis\"`\n\tColorMarkers        *xlsxColor        `xml:\"x14:colorMarkers\"`\n\tColorFirst          *xlsxColor        `xml:\"x14:colorFirst\"`\n\tColorLast           *xlsxColor        `xml:\"x14:colorLast\"`\n\tColorHigh           *xlsxColor        `xml:\"x14:colorHigh\"`\n\tColorLow            *xlsxColor        `xml:\"x14:colorLow\"`\n\tSparklines          xlsxX14Sparklines `xml:\"x14:sparklines\"`\n}\n\n// xlsxX14Sparklines directly maps the x14:sparklines element.\ntype xlsxX14Sparklines struct {\n\tSparkline []*xlsxX14Sparkline `xml:\"x14:sparkline\"`\n}\n\n// xlsxX14Sparkline directly maps the x14:sparkline element.\ntype xlsxX14Sparkline struct {\n\tF     string `xml:\"xm:f\"`\n\tSqref string `xml:\"xm:sqref\"`\n}\n\n// DataValidation directly maps the settings of the data validation rule.\ntype DataValidation struct {\n\tAllowBlank       bool\n\tError            *string\n\tErrorStyle       *string\n\tErrorTitle       *string\n\tOperator         string\n\tPrompt           *string\n\tPromptTitle      *string\n\tShowDropDown     bool\n\tShowErrorMessage bool\n\tShowInputMessage bool\n\tSqref            string\n\tType             string\n\tFormula1         string\n\tFormula2         string\n}\n\n// SparklineOptions directly maps the settings of the sparkline.\ntype SparklineOptions struct {\n\tLocation      []string\n\tRange         []string\n\tMax           int\n\tCustMax       int\n\tMin           int\n\tCustMin       int\n\tType          string\n\tWeight        float64\n\tDateAxis      bool\n\tMarkers       bool\n\tHigh          bool\n\tLow           bool\n\tFirst         bool\n\tLast          bool\n\tNegative      bool\n\tAxis          bool\n\tHidden        bool\n\tReverse       bool\n\tStyle         int\n\tSeriesColor   string\n\tNegativeColor string\n\tMarkersColor  string\n\tFirstColor    string\n\tLastColor     string\n\tHightColor    string\n\tLowColor      string\n\tEmptyCells    string\n}\n\n// Selection directly maps the settings of the worksheet selection.\ntype Selection struct {\n\tSQRef      string\n\tActiveCell string\n\tPane       string\n}\n\n// Panes directly maps the settings of the panes.\ntype Panes struct {\n\tFreeze      bool\n\tSplit       bool\n\tXSplit      int\n\tYSplit      int\n\tTopLeftCell string\n\tActivePane  string\n\tSelection   []Selection\n}\n\n// ConditionalFormatOptions directly maps the conditional format settings of the cells.\ntype ConditionalFormatOptions struct {\n\tType           string\n\tAboveAverage   bool\n\tPercent        bool\n\tFormat         *int\n\tCriteria       string\n\tValue          string\n\tMinType        string\n\tMidType        string\n\tMaxType        string\n\tMinValue       string\n\tMidValue       string\n\tMaxValue       string\n\tMinColor       string\n\tMidColor       string\n\tMaxColor       string\n\tBarColor       string\n\tBarBorderColor string\n\tBarDirection   string\n\tBarOnly        bool\n\tBarSolid       bool\n\tIconStyle      string\n\tReverseIcons   bool\n\tIconsOnly      bool\n\tStopIfTrue     bool\n}\n\n// SheetProtectionOptions directly maps the settings of worksheet protection.\ntype SheetProtectionOptions struct {\n\tAlgorithmName       string\n\tAutoFilter          bool\n\tDeleteColumns       bool\n\tDeleteRows          bool\n\tEditObjects         bool\n\tEditScenarios       bool\n\tFormatCells         bool\n\tFormatColumns       bool\n\tFormatRows          bool\n\tInsertColumns       bool\n\tInsertHyperlinks    bool\n\tInsertRows          bool\n\tPassword            string\n\tPivotTables         bool\n\tSelectLockedCells   bool\n\tSelectUnlockedCells bool\n\tSort                bool\n}\n\n// HeaderFooterOptions directly maps the settings of header and footer.\ntype HeaderFooterOptions struct {\n\tAlignWithMargins *bool\n\tDifferentFirst   bool\n\tDifferentOddEven bool\n\tScaleWithDoc     *bool\n\tOddHeader        string\n\tOddFooter        string\n\tEvenHeader       string\n\tEvenFooter       string\n\tFirstHeader      string\n\tFirstFooter      string\n}\n\n// PageLayoutMarginsOptions directly maps the settings of page layout margins.\ntype PageLayoutMarginsOptions struct {\n\tBottom       *float64\n\tFooter       *float64\n\tHeader       *float64\n\tLeft         *float64\n\tRight        *float64\n\tTop          *float64\n\tHorizontally *bool\n\tVertically   *bool\n}\n\n// PageLayoutOptions directly maps the settings of page layout.\ntype PageLayoutOptions struct {\n\t// Size defines the paper size of the worksheet.\n\tSize *int\n\t// Orientation defines the orientation of page layout for a worksheet.\n\tOrientation *string\n\t// FirstPageNumber specified the first printed page number. If no value is\n\t// specified, then 'automatic' is assumed.\n\tFirstPageNumber *uint\n\t// AdjustTo defines the print scaling. This attribute is restricted to\n\t// value ranging from 10 (10%) to 400 (400%). This setting is overridden\n\t// when fitToWidth and/or fitToHeight are in use.\n\tAdjustTo *uint\n\t// FitToHeight specified the number of vertical pages to fit on.\n\tFitToHeight *int\n\t// FitToWidth specified the number of horizontal pages to fit on.\n\tFitToWidth *int\n\t// BlackAndWhite specified print black and white.\n\tBlackAndWhite *bool\n\t// PageOrder specifies the ordering of multiple pages. Values\n\t// accepted: overThenDown and downThenOver\n\tPageOrder *string\n}\n\n// ViewOptions directly maps the settings of sheet view.\ntype ViewOptions struct {\n\t// DefaultGridColor indicating that the consuming application should use\n\t// the default grid lines color(system dependent). Overrides any color\n\t// specified in colorId.\n\tDefaultGridColor *bool\n\t// RightToLeft indicating whether the sheet is in 'right to left' display\n\t// mode. When in this mode, Column A is on the far right, Column B; is one\n\t// column left of Column A, and so on. Also, information in cells is\n\t// displayed in the Right to Left format.\n\tRightToLeft *bool\n\t// ShowFormulas indicating whether this sheet should display formulas.\n\tShowFormulas *bool\n\t// ShowGridLines indicating whether this sheet should display grid lines.\n\tShowGridLines *bool\n\t// ShowRowColHeaders indicating whether the sheet should display row and\n\t// column headings.\n\tShowRowColHeaders *bool\n\t// ShowRuler indicating this sheet should display ruler.\n\tShowRuler *bool\n\t// ShowZeros indicating whether to \"show a zero in cells that have zero\n\t// value\". When using a formula to reference another cell which is empty,\n\t// the referenced value becomes 0 when the flag is true. (Default setting\n\t// is true.)\n\tShowZeros *bool\n\t// TopLeftCell specifies a location of the top left visible cell Location\n\t// of the top left visible cell in the bottom right pane (when in\n\t// Left-to-Right mode).\n\tTopLeftCell *string\n\t// View indicating how sheet is displayed, by default it uses empty string\n\t// available options: normal, pageLayout, pageBreakPreview\n\tView *string\n\t// ZoomScale specifies a window zoom magnification for current view\n\t// representing percent values. This attribute is restricted to values\n\t// ranging from 10 to 400. Horizontal & Vertical scale together.\n\tZoomScale *float64\n}\n\n// SheetPropsOptions provides a function to set worksheet properties. There 4\n// kinds of presets \"Custom Scaling Options\" in the spreadsheet applications, if\n// you need to set those kind of scaling options, please using the\n// \"SetSheetProps\" and \"SetPageLayout\" functions to approach these 4 scaling\n// options:\n//\n// 1. No Scaling (Print sheets at their actual size):\n//\n//\tdisable := false\n//\tif err := f.SetSheetProps(\"Sheet1\", &excelize.SheetPropsOptions{\n//\t    FitToPage: &disable,\n//\t}); err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\n// 2. Fit Sheet on One Page (Shrink the printout so that it fits on one page):\n//\n//\tenable := true\n//\tif err := f.SetSheetProps(\"Sheet1\", &excelize.SheetPropsOptions{\n//\t    FitToPage: &enable,\n//\t}); err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\n// 3. Fit All Columns on One Page (Shrink the printout so that it is one page\n// wide):\n//\n//\tenable, zero := true, 0\n//\tif err := f.SetSheetProps(\"Sheet1\", &excelize.SheetPropsOptions{\n//\t    FitToPage: &enable,\n//\t}); err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\tif err := f.SetPageLayout(\"Sheet1\", &excelize.PageLayoutOptions{\n//\t    FitToHeight: &zero,\n//\t}); err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\n// 4. Fit All Rows on One Page (Shrink the printout so that it is one page\n// high):\n//\n//\tenable, zero := true, 0\n//\tif err := f.SetSheetProps(\"Sheet1\", &excelize.SheetPropsOptions{\n//\t    FitToPage: &enable,\n//\t}); err != nil {\n//\t    fmt.Println(err)\n//\t}\n//\tif err := f.SetPageLayout(\"Sheet1\", &excelize.PageLayoutOptions{\n//\t    FitToWidth: &zero,\n//\t}); err != nil {\n//\t    fmt.Println(err)\n//\t}\ntype SheetPropsOptions struct {\n\t// Specifies a stable name of the sheet, which should not change over time,\n\t// and does not change from user input. This name should be used by code\n\t// to reference a particular sheet.\n\tCodeName *string\n\t// EnableFormatConditionsCalculation indicating whether the conditional\n\t// formatting calculations shall be evaluated. If set to false, then the\n\t// min/max values of color scales or data bars or threshold values in Top N\n\t// rules shall not be updated. Essentially the conditional\n\t// formatting \"calc\" is off.\n\tEnableFormatConditionsCalculation *bool\n\t// Published indicating whether the worksheet is published.\n\tPublished *bool\n\t// AutoPageBreaks indicating whether the sheet displays Automatic Page\n\t// Breaks.\n\tAutoPageBreaks *bool\n\t// FitToPage indicating whether the Fit to Page print option is enabled.\n\tFitToPage *bool\n\t// TabColorIndexed represents the indexed color value.\n\tTabColorIndexed *int\n\t// TabColorRGB represents the standard Alpha Red Green Blue color value.\n\tTabColorRGB *string\n\t// TabColorTheme represents the zero-based index into the collection,\n\t// referencing a particular value expressed in the Theme part.\n\tTabColorTheme *int\n\t// TabColorTint specifies the tint value applied to the color.\n\tTabColorTint *float64\n\t// OutlineSummaryBelow indicating whether summary rows appear below detail\n\t// in an outline, when applying an outline.\n\tOutlineSummaryBelow *bool\n\t// OutlineSummaryRight indicating whether summary columns appear to the\n\t// right of detail in an outline, when applying an outline.\n\tOutlineSummaryRight *bool\n\t// BaseColWidth specifies the number of characters of the maximum digit\n\t// width of the normal style's font. This value does not include margin\n\t// padding or extra padding for grid lines. It is only the number of\n\t// characters.\n\tBaseColWidth *uint8\n\t// DefaultColWidth specifies the default column width measured as the\n\t// number of characters of the maximum digit width of the normal style's\n\t// font.\n\tDefaultColWidth *float64\n\t// DefaultRowHeight specifies the default row height measured in point\n\t// size. Optimization so we don't have to write the height on all rows.\n\t// This can be written out if most rows have custom height, to achieve the\n\t// optimization.\n\tDefaultRowHeight *float64\n\t// CustomHeight specifies the custom height.\n\tCustomHeight *bool\n\t// ZeroHeight specifies if rows are hidden.\n\tZeroHeight *bool\n\t// ThickTop specifies if rows have a thick top border by default.\n\tThickTop *bool\n\t// ThickBottom specifies if rows have a thick bottom border by default.\n\tThickBottom *bool\n}\n"
  }
]