作者 陈志颖

chore:添加vendor目录

要显示太多修改。

为保证性能只显示 12 of 12+ 个文件。

  1 +~$*.xlsx
  2 +test/Test*.xlsx
  3 +*.out
  4 +*.test
  5 +.idea
  1 +language: go
  2 +
  3 +install:
  4 + - go get -d -t -v ./... && go build -v ./...
  5 +
  6 +go:
  7 + - 1.11.x
  8 + - 1.12.x
  9 + - 1.13.x
  10 + - 1.14.x
  11 + - 1.15.x
  12 +
  13 +os:
  14 + - linux
  15 + - osx
  16 +
  17 +env:
  18 + jobs:
  19 + - GOARCH=amd64
  20 + - GOARCH=386
  21 +
  22 +script:
  23 + - env GO111MODULE=on go vet ./...
  24 + - env GO111MODULE=on go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic
  25 +
  26 +after_success:
  27 + - bash <(curl -s https://codecov.io/bash)
  1 +# Contributor Covenant Code of Conduct
  2 +
  3 +## Our Pledge
  4 +
  5 +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
  6 +
  7 +## Our Standards
  8 +
  9 +Examples of behavior that contributes to creating a positive environment include:
  10 +
  11 +* Using welcoming and inclusive language
  12 +* Being respectful of differing viewpoints and experiences
  13 +* Gracefully accepting constructive criticism
  14 +* Focusing on what is best for the community
  15 +* Showing empathy towards other community members
  16 +
  17 +Examples of unacceptable behavior by participants include:
  18 +
  19 +* The use of sexualized language or imagery and unwelcome sexual attention or advances
  20 +* Trolling, insulting/derogatory comments, and personal or political attacks
  21 +* Public or private harassment
  22 +* Publishing others' private information, such as a physical or electronic address, without explicit permission
  23 +* Other conduct which could reasonably be considered inappropriate in a professional setting
  24 +
  25 +## Our Responsibilities
  26 +
  27 +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
  28 +
  29 +Project maintainers 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, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
  30 +
  31 +## Scope
  32 +
  33 +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
  34 +
  35 +## Enforcement
  36 +
  37 +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [xuri.me](https://xuri.me). The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
  38 +
  39 +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
  40 +
  41 +## Attribution
  42 +
  43 +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct][version]
  44 +
  45 +[homepage]: https://www.contributor-covenant.org
  46 +[version]: https://www.contributor-covenant.org/version/2/0/code_of_conduct
  1 +# Contributing to excelize
  2 +
  3 +Want to hack on excelize? Awesome! This page contains information about reporting issues as well as some tips and
  4 +guidelines useful to experienced open source contributors. Finally, make sure
  5 +you read our [community guidelines](#community-guidelines) before you
  6 +start participating.
  7 +
  8 +## Topics
  9 +
  10 +* [Reporting Security Issues](#reporting-security-issues)
  11 +* [Design and Cleanup Proposals](#design-and-cleanup-proposals)
  12 +* [Reporting Issues](#reporting-other-issues)
  13 +* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)
  14 +* [Community Guidelines](#community-guidelines)
  15 +
  16 +## Reporting security issues
  17 +
  18 +The excelize maintainers take security seriously. If you discover a security
  19 +issue, please bring it to their attention right away!
  20 +
  21 +Please **DO NOT** file a public issue, instead send your report privately to
  22 +[xuri.me](https://xuri.me).
  23 +
  24 +Security reports are greatly appreciated and we will publicly thank you for it.
  25 +We currently do not offer a paid security bounty program, but are not
  26 +ruling it out in the future.
  27 +
  28 +## Reporting other issues
  29 +
  30 +A great way to contribute to the project is to send a detailed report when you
  31 +encounter an issue. We always appreciate a well-written, thorough bug report,
  32 +and will thank you for it!
  33 +
  34 +Check that [our issue database](https://github.com/360EntSecGroup-Skylar/excelize/issues)
  35 +doesn't already include that problem or suggestion before submitting an issue.
  36 +If you find a match, you can use the "subscribe" button to get notified on
  37 +updates. Do *not* leave random "+1" or "I have this too" comments, as they
  38 +only clutter the discussion, and don't help resolving it. However, if you
  39 +have ways to reproduce the issue or have additional information that may help
  40 +resolving the issue, please leave a comment.
  41 +
  42 +When reporting issues, always include the output of `go env`.
  43 +
  44 +Also include the steps required to reproduce the problem if possible and
  45 +applicable. This information will help us review and fix your issue faster.
  46 +When sending lengthy log-files, consider posting them as a gist [https://gist.github.com](https://gist.github.com).
  47 +Don't forget to remove sensitive data from your logfiles before posting (you can
  48 +replace those parts with "REDACTED").
  49 +
  50 +## Quick contribution tips and guidelines
  51 +
  52 +This section gives the experienced contributor some tips and guidelines.
  53 +
  54 +### Pull requests are always welcome
  55 +
  56 +Not sure if that typo is worth a pull request? Found a bug and know how to fix
  57 +it? Do it! We will appreciate it. Any significant improvement should be
  58 +documented as [a GitHub issue](https://github.com/360EntSecGroup-Skylar/excelize/issues) before
  59 +anybody starts working on it.
  60 +
  61 +We are always thrilled to receive pull requests. We do our best to process them
  62 +quickly. If your pull request is not accepted on the first try,
  63 +don't get discouraged!
  64 +
  65 +### Design and cleanup proposals
  66 +
  67 +You can propose new designs for existing excelize features. You can also design
  68 +entirely new features. We really appreciate contributors who want to refactor or
  69 +otherwise cleanup our project.
  70 +
  71 +We try hard to keep excelize lean and focused. Excelize can't do everything for
  72 +everybody. This means that we might decide against incorporating a new feature.
  73 +However, there might be a way to implement that feature *on top of* excelize.
  74 +
  75 +### Conventions
  76 +
  77 +Fork the repository and make changes on your fork in a feature branch:
  78 +
  79 +* If it's a bug fix branch, name it XXXX-something where XXXX is the number of
  80 + the issue.
  81 +* If it's a feature branch, create an enhancement issue to announce
  82 + your intentions, and name it XXXX-something where XXXX is the number of the
  83 + issue.
  84 +
  85 +Submit unit tests for your changes. Go has a great test framework built in; use
  86 +it! Take a look at existing tests for inspiration. Run the full test on your branch before
  87 +submitting a pull request.
  88 +
  89 +Update the documentation when creating or modifying features. Test your
  90 +documentation changes for clarity, concision, and correctness, as well as a
  91 +clean documentation build.
  92 +
  93 +Write clean code. Universally formatted code promotes ease of writing, reading,
  94 +and maintenance. Always run `gofmt -s -w file.go` on each changed file before
  95 +committing your changes. Most editors have plug-ins that do this automatically.
  96 +
  97 +Pull request descriptions should be as clear as possible and include a reference
  98 +to all the issues that they address.
  99 +
  100 +### Successful Changes
  101 +
  102 +Before contributing large or high impact changes, make the effort to coordinate
  103 +with the maintainers of the project before submitting a pull request. This
  104 +prevents you from doing extra work that may or may not be merged.
  105 +
  106 +Large PRs that are just submitted without any prior communication are unlikely
  107 +to be successful.
  108 +
  109 +While pull requests are the methodology for submitting changes to code, changes
  110 +are much more likely to be accepted if they are accompanied by additional
  111 +engineering work. While we don't define this explicitly, most of these goals
  112 +are accomplished through communication of the design goals and subsequent
  113 +solutions. Often times, it helps to first state the problem before presenting
  114 +solutions.
  115 +
  116 +Typically, the best methods of accomplishing this are to submit an issue,
  117 +stating the problem. This issue can include a problem statement and a
  118 +checklist with requirements. If solutions are proposed, alternatives should be
  119 +listed and eliminated. Even if the criteria for elimination of a solution is
  120 +frivolous, say so.
  121 +
  122 +Larger changes typically work best with design documents. These are focused on
  123 +providing context to the design at the time the feature was conceived and can
  124 +inform future documentation contributions.
  125 +
  126 +### Commit Messages
  127 +
  128 +Commit messages must start with a capitalized and short summary
  129 +written in the imperative, followed by an optional, more detailed explanatory
  130 +text which is separated from the summary by an empty line.
  131 +
  132 +Commit messages should follow best practices, including explaining the context
  133 +of the problem and how it was solved, including in caveats or follow up changes
  134 +required. They should tell the story of the change and provide readers
  135 +understanding of what led to it.
  136 +
  137 +In practice, the best approach to maintaining a nice commit message is to
  138 +leverage a `git add -p` and `git commit --amend` to formulate a solid
  139 +changeset. This allows one to piece together a change, as information becomes
  140 +available.
  141 +
  142 +If you squash a series of commits, don't just submit that. Re-write the commit
  143 +message, as if the series of commits was a single stroke of brilliance.
  144 +
  145 +That said, there is no requirement to have a single commit for a PR, as long as
  146 +each commit tells the story. For example, if there is a feature that requires a
  147 +package, it might make sense to have the package in a separate commit then have
  148 +a subsequent commit that uses it.
  149 +
  150 +Remember, you're telling part of the story with the commit message. Don't make
  151 +your chapter weird.
  152 +
  153 +### Review
  154 +
  155 +Code review comments may be added to your pull request. Discuss, then make the
  156 +suggested modifications and push additional commits to your feature branch. Post
  157 +a comment after pushing. New commits show up in the pull request automatically,
  158 +but the reviewers are notified only when you comment.
  159 +
  160 +Pull requests must be cleanly rebased on top of master without multiple branches
  161 +mixed into the PR.
  162 +
  163 +**Git tip**: If your PR no longer merges cleanly, use `rebase master` in your
  164 +feature branch to update your pull request rather than `merge master`.
  165 +
  166 +Before you make a pull request, squash your commits into logical units of work
  167 +using `git rebase -i` and `git push -f`. A logical unit of work is a consistent
  168 +set of patches that should be reviewed together: for example, upgrading the
  169 +version of a vendored dependency and taking advantage of its now available new
  170 +feature constitute two separate units of work. Implementing a new function and
  171 +calling it in another file constitute a single logical unit of work. The very
  172 +high majority of submissions should have a single commit, so if in doubt: squash
  173 +down to one.
  174 +
  175 +After every commit, make sure the test passes. Include documentation
  176 +changes in the same pull request so that a revert would remove all traces of
  177 +the feature or fix.
  178 +
  179 +Include an issue reference like `Closes #XXXX` or `Fixes #XXXX` in commits that
  180 +close an issue. Including references automatically closes the issue on a merge.
  181 +
  182 +Please see the [Coding Style](#coding-style) for further guidelines.
  183 +
  184 +### Merge approval
  185 +
  186 +The excelize maintainers use LGTM (Looks Good To Me) in comments on the code review to
  187 +indicate acceptance.
  188 +
  189 +### Sign your work
  190 +
  191 +The sign-off is a simple line at the end of the explanation for the patch. Your
  192 +signature certifies that you wrote the patch or otherwise have the right to pass
  193 +it on as an open-source patch. The rules are pretty simple: if you can certify
  194 +the below (from [developercertificate.org](http://developercertificate.org/)):
  195 +
  196 +```text
  197 +Developer Certificate of Origin
  198 +Version 1.1
  199 +
  200 +Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
  201 +1 Letterman Drive
  202 +Suite D4700
  203 +San Francisco, CA, 94129
  204 +
  205 +Everyone is permitted to copy and distribute verbatim copies of this
  206 +license document, but changing it is not allowed.
  207 +
  208 +Developer's Certificate of Origin 1.1
  209 +
  210 +By making a contribution to this project, I certify that:
  211 +
  212 +(a) The contribution was created in whole or in part by me and I
  213 + have the right to submit it under the open source license
  214 + indicated in the file; or
  215 +
  216 +(b) The contribution is based upon previous work that, to the best
  217 + of my knowledge, is covered under an appropriate open source
  218 + license and I have the right under that license to submit that
  219 + work with modifications, whether created in whole or in part
  220 + by me, under the same open source license (unless I am
  221 + permitted to submit under a different license), as indicated
  222 + in the file; or
  223 +
  224 +(c) The contribution was provided directly to me by some other
  225 + person who certified (a), (b) or (c) and I have not modified
  226 + it.
  227 +
  228 +(d) I understand and agree that this project and the contribution
  229 + are public and that a record of the contribution (including all
  230 + personal information I submit with it, including my sign-off) is
  231 + maintained indefinitely and may be redistributed consistent with
  232 + this project or the open source license(s) involved.
  233 +```
  234 +
  235 +Then you just add a line to every git commit message:
  236 +
  237 +```text
  238 +Signed-off-by: Ri Xu https://xuri.me
  239 +```
  240 +
  241 +Use your real name (sorry, no pseudonyms or anonymous contributions.)
  242 +
  243 +If you set your `user.name` and `user.email` git configs, you can sign your
  244 +commit automatically with `git commit -s`.
  245 +
  246 +### How can I become a maintainer
  247 +
  248 +First, all maintainers have 3 things
  249 +
  250 +* They share responsibility in the project's success.
  251 +* They have made a long-term, recurring time investment to improve the project.
  252 +* They spend that time doing whatever needs to be done, not necessarily what
  253 + is the most interesting or fun.
  254 +
  255 +Maintainers are often under-appreciated, because their work is harder to appreciate.
  256 +It's easy to appreciate a really cool and technically advanced feature. It's harder
  257 +to appreciate the absence of bugs, the slow but steady improvement in stability,
  258 +or the reliability of a release process. But those things distinguish a good
  259 +project from a great one.
  260 +
  261 +Don't forget: being a maintainer is a time investment. Make sure you
  262 +will have time to make yourself available. You don't have to be a
  263 +maintainer to make a difference on the project!
  264 +
  265 +If you want to become a meintainer, contact [xuri.me](https://xuri.me) and given a introduction of you.
  266 +
  267 +## Community guidelines
  268 +
  269 +We want to keep the community awesome, growing and collaborative. We need
  270 +your help to keep it that way. To help with this we've come up with some general
  271 +guidelines for the community as a whole:
  272 +
  273 +* Be nice: Be courteous, respectful and polite to fellow community members:
  274 + no regional, racial, gender, or other abuse will be tolerated. We like
  275 + nice people way better than mean ones!
  276 +
  277 +* Encourage diversity and participation: Make everyone in our community feel
  278 + welcome, regardless of their background and the extent of their
  279 + contributions, and do everything possible to encourage participation in
  280 + our community.
  281 +
  282 +* Keep it legal: Basically, don't get us in trouble. Share only content that
  283 + you own, do not share private or sensitive information, and don't break
  284 + the law.
  285 +
  286 +* Stay on topic: Make sure that you are posting to the correct channel and
  287 + avoid off-topic discussions. Remember when you update an issue or respond
  288 + to an email you are potentially sending to a large number of people. Please
  289 + consider this before you update. Also remember that nobody likes spam.
  290 +
  291 +* Don't send email to the maintainers: There's no need to send email to the
  292 + maintainers to ask them to investigate an issue or to take a look at a
  293 + pull request. Instead of sending an email, GitHub mentions should be
  294 + used to ping maintainers to review a pull request, a proposal or an
  295 + issue.
  296 +
  297 +### Guideline violations — 3 strikes method
  298 +
  299 +The point of this section is not to find opportunities to punish people, but we
  300 +do need a fair way to deal with people who are making our community suck.
  301 +
  302 +1. First occurrence: We'll give you a friendly, but public reminder that the
  303 + behavior is inappropriate according to our guidelines.
  304 +
  305 +2. Second occurrence: We will send you a private message with a warning that
  306 + any additional violations will result in removal from the community.
  307 +
  308 +3. Third occurrence: Depending on the violation, we may need to delete or ban
  309 + your account.
  310 +
  311 +**Notes:**
  312 +
  313 +* Obvious spammers are banned on first occurrence. If we don't do this, we'll
  314 + have spam all over the place.
  315 +
  316 +* Violations are forgiven after 6 months of good behavior, and we won't hold a
  317 + grudge.
  318 +
  319 +* People who commit minor infractions will get some education, rather than
  320 + hammering them in the 3 strikes process.
  321 +
  322 +* The rules apply equally to everyone in the community, no matter how much
  323 + you've contributed.
  324 +
  325 +* Extreme violations of a threatening, abusive, destructive or illegal nature
  326 + will be addressed immediately and are not subject to 3 strikes or forgiveness.
  327 +
  328 +* Contact [xuri.me](https://xuri.me) to report abuse or appeal violations. In the case of
  329 + appeals, we know that mistakes happen, and we'll work with you to come up with a
  330 + fair solution if there has been a misunderstanding.
  331 +
  332 +## Coding Style
  333 +
  334 +Unless explicitly stated, we follow all coding guidelines from the Go
  335 +community. While some of these standards may seem arbitrary, they somehow seem
  336 +to result in a solid, consistent codebase.
  337 +
  338 +It is possible that the code base does not currently comply with these
  339 +guidelines. We are not looking for a massive PR that fixes this, since that
  340 +goes against the spirit of the guidelines. All new contributions should make a
  341 +best effort to clean up and make the code base better than they left it.
  342 +Obviously, apply your best judgement. Remember, the goal here is to make the
  343 +code base easier for humans to navigate and understand. Always keep that in
  344 +mind when nudging others to comply.
  345 +
  346 +The rules:
  347 +
  348 +1. All code should be formatted with `gofmt -s`.
  349 +2. All code should pass the default levels of
  350 + [`golint`](https://github.com/golang/lint).
  351 +3. All code should follow the guidelines covered in [Effective
  352 + Go](http://golang.org/doc/effective_go.html) and [Go Code Review
  353 + Comments](https://github.com/golang/go/wiki/CodeReviewComments).
  354 +4. Comment the code. Tell us the why, the history and the context.
  355 +5. Document _all_ declarations and methods, even private ones. Declare
  356 + expectations, caveats and anything else that may be important. If a type
  357 + gets exported, having the comments already there will ensure it's ready.
  358 +6. Variable name length should be proportional to its context and no longer.
  359 + `noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`.
  360 + In practice, short methods will have short variable names and globals will
  361 + have longer names.
  362 +7. No underscores in package names. If you need a compound name, step back,
  363 + and re-examine why you need a compound name. If you still think you need a
  364 + compound name, lose the underscore.
  365 +8. No utils or helpers packages. If a function is not general enough to
  366 + warrant its own package, it has not been written generally enough to be a
  367 + part of a util package. Just leave it unexported and well-documented.
  368 +9. All tests should run with `go test` and outside tooling should not be
  369 + required. No, we don't need another unit testing framework. Assertion
  370 + packages are acceptable if they provide _real_ incremental value.
  371 +10. Even though we call these "rules" above, they are actually just
  372 + guidelines. Since you've read all the rules, you now know that.
  373 +
  374 +If you are having trouble getting into the mood of idiomatic Go, we recommend
  375 +reading through [Effective Go](https://golang.org/doc/effective_go.html). The
  376 +[Go Blog](https://blog.golang.org) is also a great resource. Drinking the
  377 +kool-aid is a lot easier than going thirsty.
  378 +
  379 +## Code Review Comments and Effective Go Guidelines
  380 +
  381 +[CodeLingo](https://codelingo.io) automatically checks every pull request against the following guidelines from [Effective Go](https://golang.org/doc/effective_go.html) and [Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).
  382 +
  383 +### Package Comment
  384 +
  385 +Every package should have a package comment, a block comment preceding the package clause.
  386 +For multi-file packages, the package comment only needs to be present in one file, and any one will do.
  387 +The package comment should introduce the package and provide information relevant to the package as a
  388 +whole. It will appear first on the godoc page and should set up the detailed documentation that follows.
  389 +
  390 +### Single Method Interface Name
  391 +
  392 +By convention, one-method interfaces are named by the method name plus an -er suffix
  393 +or similar modification to construct an agent noun: Reader, Writer, Formatter, CloseNotifier etc.
  394 +
  395 +There are a number of such names and it's productive to honor them and the function names they capture.
  396 +Read, Write, Close, Flush, String and so on have canonical signatures and meanings. To avoid confusion,
  397 +don't give your method one of those names unless it has the same signature and meaning. Conversely,
  398 +if your type implements a method with the same meaning as a method on a well-known type, give it the
  399 +same name and signature; call your string-converter method String not ToString.
  400 +
  401 +### Avoid Annotations in Comments
  402 +
  403 +Comments do not need extra formatting such as banners of stars. The generated output
  404 +may not even be presented in a fixed-width font, so don't depend on spacing for alignment—godoc,
  405 +like gofmt, takes care of that. The comments are uninterpreted plain text, so HTML and other
  406 +annotations such as _this_ will reproduce verbatim and should not be used. One adjustment godoc
  407 +does do is to display indented text in a fixed-width font, suitable for program snippets.
  408 +The package comment for the fmt package uses this to good effect.
  409 +
  410 +### Comment First Word as Subject
  411 +
  412 +Doc comments work best as complete sentences, which allow a wide variety of automated presentations.
  413 +The first sentence should be a one-sentence summary that starts with the name being declared.
  414 +
  415 +### Good Package Name
  416 +
  417 +It's helpful if everyone using the package can use the same name
  418 +to refer to its contents, which implies that the package name should
  419 +be good: short, concise, evocative. By convention, packages are
  420 +given lower case, single-word names; there should be no need for
  421 +underscores or mixedCaps. Err on the side of brevity, since everyone
  422 +using your package will be typing that name. And don't worry about
  423 +collisions a priori. The package name is only the default name for
  424 +imports; it need not be unique across all source code, and in the
  425 +rare case of a collision the importing package can choose a different
  426 +name to use locally. In any case, confusion is rare because the file
  427 +name in the import determines just which package is being used.
  428 +
  429 +### Avoid Renaming Imports
  430 +
  431 +Avoid renaming imports except to avoid a name collision; good package names
  432 +should not require renaming. In the event of collision, prefer to rename the
  433 +most local or project-specific import.
  434 +
  435 +### Context as First Argument
  436 +
  437 +Values of the context.Context type carry security credentials, tracing information,
  438 +deadlines, and cancellation signals across API and process boundaries. Go programs
  439 +pass Contexts explicitly along the entire function call chain from incoming RPCs
  440 +and HTTP requests to outgoing requests.
  441 +
  442 +Most functions that use a Context should accept it as their first parameter.
  443 +
  444 +### Do Not Discard Errors
  445 +
  446 +Do not discard errors using _ variables. If a function returns an error,
  447 +check it to make sure the function succeeded. Handle the error, return it, or,
  448 +in truly exceptional situations, panic.
  449 +
  450 +### Go Error Format
  451 +
  452 +Error strings should not be capitalized (unless beginning with proper nouns
  453 +or acronyms) or end with punctuation, since they are usually printed following
  454 +other context. That is, use fmt.Errorf("something bad") not fmt.Errorf("Something bad"),
  455 +so that log.Printf("Reading %s: %v", filename, err) formats without a spurious
  456 +capital letter mid-message. This does not apply to logging, which is implicitly
  457 +line-oriented and not combined inside other messages.
  458 +
  459 +### Use Crypto Rand
  460 +
  461 +Do not use package math/rand to generate keys, even
  462 +throwaway ones. Unseeded, the generator is completely predictable.
  463 +Seeded with time.Nanoseconds(), there are just a few bits of entropy.
  464 +Instead, use crypto/rand's Reader, and if you need text, print to
  465 +hexadecimal or base64.
  1 +BSD 3-Clause License
  2 +
  3 +Copyright (c) 2016-2020 The excelize Authors.
  4 +All rights reserved.
  5 +
  6 +Redistribution and use in source and binary forms, with or without
  7 +modification, are permitted provided that the following conditions are met:
  8 +
  9 +* Redistributions of source code must retain the above copyright notice, this
  10 + list of conditions and the following disclaimer.
  11 +
  12 +* Redistributions in binary form must reproduce the above copyright notice,
  13 + this list of conditions and the following disclaimer in the documentation
  14 + and/or other materials provided with the distribution.
  15 +
  16 +* Neither the name of the copyright holder nor the names of its
  17 + contributors may be used to endorse or promote products derived from
  18 + this software without specific prior written permission.
  19 +
  20 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21 +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22 +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  23 +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  24 +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25 +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  26 +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  27 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28 +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  1 +# PR Details
  2 +
  3 +<!--- Provide a general summary of your changes in the Title above -->
  4 +
  5 +## Description
  6 +
  7 +<!--- Describe your changes in detail -->
  8 +
  9 +## Related Issue
  10 +
  11 +<!--- This project only accepts pull requests related to open issues -->
  12 +<!--- If suggesting a new feature or change, please discuss it in an issue first -->
  13 +<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
  14 +<!--- Please link to the issue here: -->
  15 +
  16 +## Motivation and Context
  17 +
  18 +<!--- Why is this change required? What problem does it solve? -->
  19 +
  20 +## How Has This Been Tested
  21 +
  22 +<!--- Please describe in detail how you tested your changes. -->
  23 +<!--- Include details of your testing environment, and the tests you ran to -->
  24 +<!--- see how your change affects other areas of the code, etc. -->
  25 +
  26 +## Types of changes
  27 +
  28 +<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
  29 +
  30 +- [ ] Docs change / refactoring / dependency upgrade
  31 +- [ ] Bug fix (non-breaking change which fixes an issue)
  32 +- [ ] New feature (non-breaking change which adds functionality)
  33 +- [ ] Breaking change (fix or feature that would cause existing functionality to change)
  34 +
  35 +## Checklist
  36 +
  37 +<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
  38 +<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
  39 +
  40 +- [ ] My code follows the code style of this project.
  41 +- [ ] My change requires a change to the documentation.
  42 +- [ ] I have updated the documentation accordingly.
  43 +- [ ] I have read the **CONTRIBUTING** document.
  44 +- [ ] I have added tests to cover my changes.
  45 +- [ ] All new and existing tests passed.
  1 +<p align="center"><img width="650" src="./excelize.svg" alt="Excelize logo"></p>
  2 +
  3 +<p align="center">
  4 + <a href="https://travis-ci.org/360EntSecGroup-Skylar/excelize"><img src="https://travis-ci.org/360EntSecGroup-Skylar/excelize.svg?branch=master" alt="Build Status"></a>
  5 + <a href="https://codecov.io/gh/360EntSecGroup-Skylar/excelize"><img src="https://codecov.io/gh/360EntSecGroup-Skylar/excelize/branch/master/graph/badge.svg" alt="Code Coverage"></a>
  6 + <a href="https://goreportcard.com/report/github.com/360EntSecGroup-Skylar/excelize"><img src="https://goreportcard.com/badge/github.com/360EntSecGroup-Skylar/excelize" alt="Go Report Card"></a>
  7 + <a href="https://pkg.go.dev/github.com/360EntSecGroup-Skylar/excelize/v2?tab=doc"><img src="https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white" alt="go.dev"></a>
  8 + <a href="https://opensource.org/licenses/BSD-3-Clause"><img src="https://img.shields.io/badge/license-bsd-orange.svg" alt="Licenses"></a>
  9 + <a href="https://www.paypal.me/xuri"><img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Donate"></a>
  10 +</p>
  11 +
  12 +# Excelize
  13 +
  14 +## Introduction
  15 +
  16 +Excelize is a library written in pure Go providing a set of functions that allow you to write to and read from XLSX / XLSM / XLTM 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.10 or later. The full API docs can be seen using go's built-in documentation tool, or online at [go.dev](https://pkg.go.dev/github.com/360EntSecGroup-Skylar/excelize/v2?tab=doc) and [docs reference](https://xuri.me/excelize/).
  17 +
  18 +## Basic Usage
  19 +
  20 +### Installation
  21 +
  22 +```bash
  23 +go get github.com/360EntSecGroup-Skylar/excelize
  24 +```
  25 +
  26 +- If your package management with [Go Modules](https://blog.golang.org/using-go-modules), please install with following command.
  27 +
  28 +```bash
  29 +go get github.com/360EntSecGroup-Skylar/excelize/v2
  30 +```
  31 +
  32 +### Create spreadsheet
  33 +
  34 +Here is a minimal example usage that will create spreadsheet file.
  35 +
  36 +```go
  37 +package main
  38 +
  39 +import (
  40 + "fmt"
  41 +
  42 + "github.com/360EntSecGroup-Skylar/excelize"
  43 +)
  44 +
  45 +func main() {
  46 + f := excelize.NewFile()
  47 + // Create a new sheet.
  48 + index := f.NewSheet("Sheet2")
  49 + // Set value of a cell.
  50 + f.SetCellValue("Sheet2", "A2", "Hello world.")
  51 + f.SetCellValue("Sheet1", "B2", 100)
  52 + // Set active sheet of the workbook.
  53 + f.SetActiveSheet(index)
  54 + // Save xlsx file by the given path.
  55 + if err := f.SaveAs("Book1.xlsx"); err != nil {
  56 + fmt.Println(err)
  57 + }
  58 +}
  59 +```
  60 +
  61 +### Reading spreadsheet
  62 +
  63 +The following constitutes the bare to read a spreadsheet document.
  64 +
  65 +```go
  66 +package main
  67 +
  68 +import (
  69 + "fmt"
  70 +
  71 + "github.com/360EntSecGroup-Skylar/excelize"
  72 +)
  73 +
  74 +func main() {
  75 + f, err := excelize.OpenFile("Book1.xlsx")
  76 + if err != nil {
  77 + fmt.Println(err)
  78 + return
  79 + }
  80 + // Get value from cell by given worksheet name and axis.
  81 + cell, err := f.GetCellValue("Sheet1", "B2")
  82 + if err != nil {
  83 + fmt.Println(err)
  84 + return
  85 + }
  86 + fmt.Println(cell)
  87 + // Get all the rows in the Sheet1.
  88 + rows, err := f.GetRows("Sheet1")
  89 + for _, row := range rows {
  90 + for _, colCell := range row {
  91 + fmt.Print(colCell, "\t")
  92 + }
  93 + fmt.Println()
  94 + }
  95 +}
  96 +```
  97 +
  98 +### Add chart to spreadsheet file
  99 +
  100 +With Excelize chart generation and management is as easy as a few lines of code. You can build charts based off data in your worksheet or generate charts without any data in your worksheet at all.
  101 +
  102 +<p align="center"><img width="650" src="./test/images/chart.png" alt="Excelize"></p>
  103 +
  104 +```go
  105 +package main
  106 +
  107 +import (
  108 + "fmt"
  109 +
  110 + "github.com/360EntSecGroup-Skylar/excelize"
  111 +)
  112 +
  113 +func main() {
  114 + categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
  115 + values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
  116 + f := excelize.NewFile()
  117 + for k, v := range categories {
  118 + f.SetCellValue("Sheet1", k, v)
  119 + }
  120 + for k, v := range values {
  121 + f.SetCellValue("Sheet1", k, v)
  122 + }
  123 + if err := f.AddChart("Sheet1", "E1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`); err != nil {
  124 + fmt.Println(err)
  125 + return
  126 + }
  127 + // Save xlsx file by the given path.
  128 + if err := f.SaveAs("Book1.xlsx"); err != nil {
  129 + fmt.Println(err)
  130 + }
  131 +}
  132 +```
  133 +
  134 +### Add picture to spreadsheet file
  135 +
  136 +```go
  137 +package main
  138 +
  139 +import (
  140 + "fmt"
  141 + _ "image/gif"
  142 + _ "image/jpeg"
  143 + _ "image/png"
  144 +
  145 + "github.com/360EntSecGroup-Skylar/excelize"
  146 +)
  147 +
  148 +func main() {
  149 + f, err := excelize.OpenFile("Book1.xlsx")
  150 + if err != nil {
  151 + fmt.Println(err)
  152 + return
  153 + }
  154 + // Insert a picture.
  155 + if err := f.AddPicture("Sheet1", "A2", "image.png", ""); err != nil {
  156 + fmt.Println(err)
  157 + }
  158 + // Insert a picture to worksheet with scaling.
  159 + if err := f.AddPicture("Sheet1", "D2", "image.jpg", `{"x_scale": 0.5, "y_scale": 0.5}`); err != nil {
  160 + fmt.Println(err)
  161 + }
  162 + // Insert a picture offset in the cell with printing support.
  163 + if err := f.AddPicture("Sheet1", "H2", "image.gif", `{"x_offset": 15, "y_offset": 10, "print_obj": true, "lock_aspect_ratio": false, "locked": false}`); err != nil {
  164 + fmt.Println(err)
  165 + }
  166 + // Save the xlsx file with the origin path.
  167 + if err = f.Save(); err != nil {
  168 + fmt.Println(err)
  169 + }
  170 +}
  171 +```
  172 +
  173 +## Contributing
  174 +
  175 +Contributions 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](http://www.ecma-international.org/publications/standards/Ecma-376.htm).
  176 +
  177 +## Licenses
  178 +
  179 +This 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).
  180 +
  181 +The Excel logo is a trademark of [Microsoft Corporation](https://aka.ms/trademarks-usage). This artwork is an adaptation.
  182 +
  183 +gopher.{ai,svg,png} was created by [Takuya Ueda](https://twitter.com/tenntenn). Licensed under the [Creative Commons 3.0 Attributions license](http://creativecommons.org/licenses/by/3.0/).
  1 +<p align="center"><img width="650" src="./excelize.svg" alt="Excelize logo"></p>
  2 +
  3 +<p align="center">
  4 + <a href="https://travis-ci.org/360EntSecGroup-Skylar/excelize"><img src="https://travis-ci.org/360EntSecGroup-Skylar/excelize.svg?branch=master" alt="Build Status"></a>
  5 + <a href="https://codecov.io/gh/360EntSecGroup-Skylar/excelize"><img src="https://codecov.io/gh/360EntSecGroup-Skylar/excelize/branch/master/graph/badge.svg" alt="Code Coverage"></a>
  6 + <a href="https://goreportcard.com/report/github.com/360EntSecGroup-Skylar/excelize"><img src="https://goreportcard.com/badge/github.com/360EntSecGroup-Skylar/excelize" alt="Go Report Card"></a>
  7 + <a href="https://pkg.go.dev/github.com/360EntSecGroup-Skylar/excelize/v2?tab=doc"><img src="https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white" alt="go.dev"></a>
  8 + <a href="https://opensource.org/licenses/BSD-3-Clause"><img src="https://img.shields.io/badge/license-bsd-orange.svg" alt="Licenses"></a>
  9 + <a href="https://www.paypal.me/xuri"><img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Donate"></a>
  10 +</p>
  11 +
  12 +# Excelize
  13 +
  14 +## 简介
  15 +
  16 +Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376,ISO/IEC 29500 国际标准。可以使用它来读取、写入由 Microsoft Excel&trade; 2007 及以上版本创建的电子表格文档。支持 XLSX / XLSM / XLTM 等多种文档格式,高度兼容带有样式、图片(表)、透视表、切片器等复杂组件的文档,并提供流式读写 API,用于处理包含大规模数据的工作簿。可应用于各类报表平台、云计算、边缘计算等系统。使用本类库要求使用的 Go 语言为 1.10 或更高版本,完整的 API 使用文档请访问 [go.dev](https://pkg.go.dev/github.com/360EntSecGroup-Skylar/excelize/v2?tab=doc) 或查看 [参考文档](https://xuri.me/excelize/)
  17 +
  18 +## 快速上手
  19 +
  20 +### 安装
  21 +
  22 +```bash
  23 +go get github.com/360EntSecGroup-Skylar/excelize
  24 +```
  25 +
  26 +- 如果您使用 [Go Modules](https://blog.golang.org/using-go-modules) 管理软件包,请使用下面的命令来安装最新版本。
  27 +
  28 +```bash
  29 +go get github.com/360EntSecGroup-Skylar/excelize/v2
  30 +```
  31 +
  32 +### 创建 Excel 文档
  33 +
  34 +下面是一个创建 Excel 文档的简单例子:
  35 +
  36 +```go
  37 +package main
  38 +
  39 +import (
  40 + "fmt"
  41 +
  42 + "github.com/360EntSecGroup-Skylar/excelize"
  43 +)
  44 +
  45 +func main() {
  46 + f := excelize.NewFile()
  47 + // 创建一个工作表
  48 + index := f.NewSheet("Sheet2")
  49 + // 设置单元格的值
  50 + f.SetCellValue("Sheet2", "A2", "Hello world.")
  51 + f.SetCellValue("Sheet1", "B2", 100)
  52 + // 设置工作簿的默认工作表
  53 + f.SetActiveSheet(index)
  54 + // 根据指定路径保存文件
  55 + if err := f.SaveAs("Book1.xlsx"); err != nil {
  56 + fmt.Println(err)
  57 + }
  58 +}
  59 +```
  60 +
  61 +### 读取 Excel 文档
  62 +
  63 +下面是读取 Excel 文档的例子:
  64 +
  65 +```go
  66 +package main
  67 +
  68 +import (
  69 + "fmt"
  70 +
  71 + "github.com/360EntSecGroup-Skylar/excelize"
  72 +)
  73 +
  74 +func main() {
  75 + f, err := excelize.OpenFile("Book1.xlsx")
  76 + if err != nil {
  77 + fmt.Println(err)
  78 + return
  79 + }
  80 + // 获取工作表中指定单元格的值
  81 + cell, err := f.GetCellValue("Sheet1", "B2")
  82 + if err != nil {
  83 + fmt.Println(err)
  84 + return
  85 + }
  86 + fmt.Println(cell)
  87 + // 获取 Sheet1 上所有单元格
  88 + rows, err := f.GetRows("Sheet1")
  89 + for _, row := range rows {
  90 + for _, colCell := range row {
  91 + fmt.Print(colCell, "\t")
  92 + }
  93 + fmt.Println()
  94 + }
  95 +}
  96 +```
  97 +
  98 +### 在 Excel 文档中创建图表
  99 +
  100 +使用 Excelize 生成图表十分简单,仅需几行代码。您可以根据工作表中的已有数据构建图表,或向工作表中添加数据并创建图表。
  101 +
  102 +<p align="center"><img width="650" src="./test/images/chart.png" alt="Excelize"></p>
  103 +
  104 +```go
  105 +package main
  106 +
  107 +import (
  108 + "fmt"
  109 +
  110 + "github.com/360EntSecGroup-Skylar/excelize"
  111 +)
  112 +
  113 +func main() {
  114 + categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
  115 + values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
  116 + f := excelize.NewFile()
  117 + for k, v := range categories {
  118 + f.SetCellValue("Sheet1", k, v)
  119 + }
  120 + for k, v := range values {
  121 + f.SetCellValue("Sheet1", k, v)
  122 + }
  123 + if err := f.AddChart("Sheet1", "E1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`); err != nil {
  124 + fmt.Println(err)
  125 + return
  126 + }
  127 + // 根据指定路径保存文件
  128 + if err := f.SaveAs("Book1.xlsx"); err != nil {
  129 + fmt.Println(err)
  130 + }
  131 +}
  132 +```
  133 +
  134 +### 向 Excel 文档中插入图片
  135 +
  136 +```go
  137 +package main
  138 +
  139 +import (
  140 + "fmt"
  141 + _ "image/gif"
  142 + _ "image/jpeg"
  143 + _ "image/png"
  144 +
  145 + "github.com/360EntSecGroup-Skylar/excelize"
  146 +)
  147 +
  148 +func main() {
  149 + f, err := excelize.OpenFile("Book1.xlsx")
  150 + if err != nil {
  151 + fmt.Println(err)
  152 + return
  153 + }
  154 + // 插入图片
  155 + if err := f.AddPicture("Sheet1", "A2", "image.png", ""); err != nil {
  156 + fmt.Println(err)
  157 + }
  158 + // 在工作表中插入图片,并设置图片的缩放比例
  159 + if err := f.AddPicture("Sheet1", "D2", "image.jpg", `{"x_scale": 0.5, "y_scale": 0.5}`); err != nil {
  160 + fmt.Println(err)
  161 + }
  162 + // 在工作表中插入图片,并设置图片的打印属性
  163 + if err := f.AddPicture("Sheet1", "H2", "image.gif", `{"x_offset": 15, "y_offset": 10, "print_obj": true, "lock_aspect_ratio": false, "locked": false}`); err != nil {
  164 + fmt.Println(err)
  165 + }
  166 + // 保存文件
  167 + if err = f.Save(); err != nil {
  168 + fmt.Println(err)
  169 + }
  170 +}
  171 +```
  172 +
  173 +## 社区合作
  174 +
  175 +欢迎您为此项目贡献代码,提出建议或问题、修复 Bug 以及参与讨论对新功能的想法。 XML 符合标准: [part 1 of the 5th edition of the ECMA-376 Standard for Office Open XML](http://www.ecma-international.org/publications/standards/Ecma-376.htm)
  176 +
  177 +## 开源许可
  178 +
  179 +本项目遵循 BSD 3-Clause 开源许可协议,访问 [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 查看许可协议文件。
  180 +
  181 +Excel 徽标是 [Microsoft Corporation](https://aka.ms/trademarks-usage) 的商标,项目的图片是一种改编。
  182 +
  183 +gopher.{ai,svg,png} 由 [Takuya Ueda](https://twitter.com/tenntenn) 创作,遵循 [Creative Commons 3.0 Attributions license](http://creativecommons.org/licenses/by/3.0/) 创作共用授权条款。
  1 +# Security Policy
  2 +
  3 +## Supported Versions
  4 +
  5 +We 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.
  6 +
  7 +## Reporting a Vulnerability
  8 +
  9 +Please 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!
  1 +// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
  2 +// this source code is governed by a BSD-style license that can be found in
  3 +// the LICENSE file.
  4 +//
  5 +// Package excelize providing a set of functions that allow you to write to
  6 +// and read from XLSX / XLSM / XLTM files. Supports reading and writing
  7 +// spreadsheet documents generated by Microsoft Exce™ 2007 and later. Supports
  8 +// complex components by high compatibility, and provided streaming API for
  9 +// generating or reading data from a worksheet with huge amounts of data. This
  10 +// library needs Go version 1.10 or later.
  11 +
  12 +package excelize
  13 +
  14 +import (
  15 + "errors"
  16 + "strings"
  17 +)
  18 +
  19 +type adjustDirection bool
  20 +
  21 +const (
  22 + columns adjustDirection = false
  23 + rows adjustDirection = true
  24 +)
  25 +
  26 +// adjustHelper provides a function to adjust rows and columns dimensions,
  27 +// hyperlinks, merged cells and auto filter when inserting or deleting rows or
  28 +// columns.
  29 +//
  30 +// sheet: Worksheet name that we're editing
  31 +// column: Index number of the column we're inserting/deleting before
  32 +// row: Index number of the row we're inserting/deleting before
  33 +// offset: Number of rows/column to insert/delete negative values indicate deletion
  34 +//
  35 +// TODO: adjustPageBreaks, adjustComments, adjustDataValidations, adjustProtectedCells
  36 +//
  37 +func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) error {
  38 + xlsx, err := f.workSheetReader(sheet)
  39 + if err != nil {
  40 + return err
  41 + }
  42 + if dir == rows {
  43 + f.adjustRowDimensions(xlsx, num, offset)
  44 + } else {
  45 + f.adjustColDimensions(xlsx, num, offset)
  46 + }
  47 + f.adjustHyperlinks(xlsx, sheet, dir, num, offset)
  48 + if err = f.adjustMergeCells(xlsx, dir, num, offset); err != nil {
  49 + return err
  50 + }
  51 + if err = f.adjustAutoFilter(xlsx, dir, num, offset); err != nil {
  52 + return err
  53 + }
  54 + if err = f.adjustCalcChain(dir, num, offset); err != nil {
  55 + return err
  56 + }
  57 + checkSheet(xlsx)
  58 + _ = checkRow(xlsx)
  59 +
  60 + if xlsx.MergeCells != nil && len(xlsx.MergeCells.Cells) == 0 {
  61 + xlsx.MergeCells = nil
  62 + }
  63 +
  64 + return nil
  65 +}
  66 +
  67 +// adjustColDimensions provides a function to update column dimensions when
  68 +// inserting or deleting rows or columns.
  69 +func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, col, offset int) {
  70 + for rowIdx := range xlsx.SheetData.Row {
  71 + for colIdx, v := range xlsx.SheetData.Row[rowIdx].C {
  72 + cellCol, cellRow, _ := CellNameToCoordinates(v.R)
  73 + if col <= cellCol {
  74 + if newCol := cellCol + offset; newCol > 0 {
  75 + xlsx.SheetData.Row[rowIdx].C[colIdx].R, _ = CoordinatesToCellName(newCol, cellRow)
  76 + }
  77 + }
  78 + }
  79 + }
  80 +}
  81 +
  82 +// adjustRowDimensions provides a function to update row dimensions when
  83 +// inserting or deleting rows or columns.
  84 +func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, row, offset int) {
  85 + for i := range xlsx.SheetData.Row {
  86 + r := &xlsx.SheetData.Row[i]
  87 + if newRow := r.R + offset; r.R >= row && newRow > 0 {
  88 + f.ajustSingleRowDimensions(r, newRow)
  89 + }
  90 + }
  91 +}
  92 +
  93 +// ajustSingleRowDimensions provides a function to ajust single row dimensions.
  94 +func (f *File) ajustSingleRowDimensions(r *xlsxRow, num int) {
  95 + r.R = num
  96 + for i, col := range r.C {
  97 + colName, _, _ := SplitCellName(col.R)
  98 + r.C[i].R, _ = JoinCellName(colName, num)
  99 + }
  100 +}
  101 +
  102 +// adjustHyperlinks provides a function to update hyperlinks when inserting or
  103 +// deleting rows or columns.
  104 +func (f *File) adjustHyperlinks(xlsx *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) {
  105 + // short path
  106 + if xlsx.Hyperlinks == nil || len(xlsx.Hyperlinks.Hyperlink) == 0 {
  107 + return
  108 + }
  109 +
  110 + // order is important
  111 + if offset < 0 {
  112 + for i := len(xlsx.Hyperlinks.Hyperlink) - 1; i >= 0; i-- {
  113 + linkData := xlsx.Hyperlinks.Hyperlink[i]
  114 + colNum, rowNum, _ := CellNameToCoordinates(linkData.Ref)
  115 +
  116 + if (dir == rows && num == rowNum) || (dir == columns && num == colNum) {
  117 + f.deleteSheetRelationships(sheet, linkData.RID)
  118 + if len(xlsx.Hyperlinks.Hyperlink) > 1 {
  119 + xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink[:i],
  120 + xlsx.Hyperlinks.Hyperlink[i+1:]...)
  121 + } else {
  122 + xlsx.Hyperlinks = nil
  123 + }
  124 + }
  125 + }
  126 + }
  127 +
  128 + if xlsx.Hyperlinks == nil {
  129 + return
  130 + }
  131 +
  132 + for i := range xlsx.Hyperlinks.Hyperlink {
  133 + link := &xlsx.Hyperlinks.Hyperlink[i] // get reference
  134 + colNum, rowNum, _ := CellNameToCoordinates(link.Ref)
  135 +
  136 + if dir == rows {
  137 + if rowNum >= num {
  138 + link.Ref, _ = CoordinatesToCellName(colNum, rowNum+offset)
  139 + }
  140 + } else {
  141 + if colNum >= num {
  142 + link.Ref, _ = CoordinatesToCellName(colNum+offset, rowNum)
  143 + }
  144 + }
  145 + }
  146 +}
  147 +
  148 +// adjustAutoFilter provides a function to update the auto filter when
  149 +// inserting or deleting rows or columns.
  150 +func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) error {
  151 + if xlsx.AutoFilter == nil {
  152 + return nil
  153 + }
  154 +
  155 + coordinates, err := f.areaRefToCoordinates(xlsx.AutoFilter.Ref)
  156 + if err != nil {
  157 + return err
  158 + }
  159 + x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
  160 +
  161 + if (dir == rows && y1 == num && offset < 0) || (dir == columns && x1 == num && x2 == num) {
  162 + xlsx.AutoFilter = nil
  163 + for rowIdx := range xlsx.SheetData.Row {
  164 + rowData := &xlsx.SheetData.Row[rowIdx]
  165 + if rowData.R > y1 && rowData.R <= y2 {
  166 + rowData.Hidden = false
  167 + }
  168 + }
  169 + return nil
  170 + }
  171 +
  172 + coordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset)
  173 + x1, y1, x2, y2 = coordinates[0], coordinates[1], coordinates[2], coordinates[3]
  174 +
  175 + if xlsx.AutoFilter.Ref, err = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}); err != nil {
  176 + return err
  177 + }
  178 + return nil
  179 +}
  180 +
  181 +// adjustAutoFilterHelper provides a function for adjusting auto filter to
  182 +// compare and calculate cell axis by the given adjust direction, operation
  183 +// axis and offset.
  184 +func (f *File) adjustAutoFilterHelper(dir adjustDirection, coordinates []int, num, offset int) []int {
  185 + if dir == rows {
  186 + if coordinates[1] >= num {
  187 + coordinates[1] += offset
  188 + }
  189 + if coordinates[3] >= num {
  190 + coordinates[3] += offset
  191 + }
  192 + } else {
  193 + if coordinates[2] >= num {
  194 + coordinates[2] += offset
  195 + }
  196 + }
  197 + return coordinates
  198 +}
  199 +
  200 +// areaRefToCoordinates provides a function to convert area reference to a
  201 +// pair of coordinates.
  202 +func (f *File) areaRefToCoordinates(ref string) ([]int, error) {
  203 + rng := strings.Split(ref, ":")
  204 + return areaRangeToCoordinates(rng[0], rng[1])
  205 +}
  206 +
  207 +// areaRangeToCoordinates provides a function to convert cell range to a
  208 +// pair of coordinates.
  209 +func areaRangeToCoordinates(firstCell, lastCell string) ([]int, error) {
  210 + coordinates := make([]int, 4)
  211 + var err error
  212 + coordinates[0], coordinates[1], err = CellNameToCoordinates(firstCell)
  213 + if err != nil {
  214 + return coordinates, err
  215 + }
  216 + coordinates[2], coordinates[3], err = CellNameToCoordinates(lastCell)
  217 + return coordinates, err
  218 +}
  219 +
  220 +// sortCoordinates provides a function to correct the coordinate area, such
  221 +// correct C1:B3 to B1:C3.
  222 +func sortCoordinates(coordinates []int) error {
  223 + if len(coordinates) != 4 {
  224 + return errors.New("coordinates length must be 4")
  225 + }
  226 + if coordinates[2] < coordinates[0] {
  227 + coordinates[2], coordinates[0] = coordinates[0], coordinates[2]
  228 + }
  229 + if coordinates[3] < coordinates[1] {
  230 + coordinates[3], coordinates[1] = coordinates[1], coordinates[3]
  231 + }
  232 + return nil
  233 +}
  234 +
  235 +// coordinatesToAreaRef provides a function to convert a pair of coordinates
  236 +// to area reference.
  237 +func (f *File) coordinatesToAreaRef(coordinates []int) (string, error) {
  238 + if len(coordinates) != 4 {
  239 + return "", errors.New("coordinates length must be 4")
  240 + }
  241 + firstCell, err := CoordinatesToCellName(coordinates[0], coordinates[1])
  242 + if err != nil {
  243 + return "", err
  244 + }
  245 + lastCell, err := CoordinatesToCellName(coordinates[2], coordinates[3])
  246 + if err != nil {
  247 + return "", err
  248 + }
  249 + return firstCell + ":" + lastCell, err
  250 +}
  251 +
  252 +// adjustMergeCells provides a function to update merged cells when inserting
  253 +// or deleting rows or columns.
  254 +func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) error {
  255 + if xlsx.MergeCells == nil {
  256 + return nil
  257 + }
  258 +
  259 + for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
  260 + areaData := xlsx.MergeCells.Cells[i]
  261 + coordinates, err := f.areaRefToCoordinates(areaData.Ref)
  262 + if err != nil {
  263 + return err
  264 + }
  265 + x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
  266 + if dir == rows {
  267 + if y1 == num && y2 == num && offset < 0 {
  268 + f.deleteMergeCell(xlsx, i)
  269 + i--
  270 + }
  271 + y1 = f.adjustMergeCellsHelper(y1, num, offset)
  272 + y2 = f.adjustMergeCellsHelper(y2, num, offset)
  273 + } else {
  274 + if x1 == num && x2 == num && offset < 0 {
  275 + f.deleteMergeCell(xlsx, i)
  276 + i--
  277 + }
  278 + x1 = f.adjustMergeCellsHelper(x1, num, offset)
  279 + x2 = f.adjustMergeCellsHelper(x2, num, offset)
  280 + }
  281 + if x1 == x2 && y1 == y2 {
  282 + f.deleteMergeCell(xlsx, i)
  283 + i--
  284 + }
  285 + if areaData.Ref, err = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}); err != nil {
  286 + return err
  287 + }
  288 + }
  289 + return nil
  290 +}
  291 +
  292 +// adjustMergeCellsHelper provides a function for adjusting merge cells to
  293 +// compare and calculate cell axis by the given pivot, operation axis and
  294 +// offset.
  295 +func (f *File) adjustMergeCellsHelper(pivot, num, offset int) int {
  296 + if pivot >= num {
  297 + pivot += offset
  298 + if pivot < 1 {
  299 + return 1
  300 + }
  301 + return pivot
  302 + }
  303 + return pivot
  304 +}
  305 +
  306 +// deleteMergeCell provides a function to delete merged cell by given index.
  307 +func (f *File) deleteMergeCell(sheet *xlsxWorksheet, idx int) {
  308 + if len(sheet.MergeCells.Cells) > idx {
  309 + sheet.MergeCells.Cells = append(sheet.MergeCells.Cells[:idx], sheet.MergeCells.Cells[idx+1:]...)
  310 + sheet.MergeCells.Count = len(sheet.MergeCells.Cells)
  311 + }
  312 +}
  313 +
  314 +// adjustCalcChain provides a function to update the calculation chain when
  315 +// inserting or deleting rows or columns.
  316 +func (f *File) adjustCalcChain(dir adjustDirection, num, offset int) error {
  317 + if f.CalcChain == nil {
  318 + return nil
  319 + }
  320 + for index, c := range f.CalcChain.C {
  321 + colNum, rowNum, err := CellNameToCoordinates(c.R)
  322 + if err != nil {
  323 + return err
  324 + }
  325 + if dir == rows && num <= rowNum {
  326 + if newRow := rowNum + offset; newRow > 0 {
  327 + f.CalcChain.C[index].R, _ = CoordinatesToCellName(colNum, newRow)
  328 + }
  329 + }
  330 + if dir == columns && num <= colNum {
  331 + if newCol := colNum + offset; newCol > 0 {
  332 + f.CalcChain.C[index].R, _ = CoordinatesToCellName(newCol, rowNum)
  333 + }
  334 + }
  335 + }
  336 + return nil
  337 +}
  1 +// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
  2 +// this source code is governed by a BSD-style license that can be found in
  3 +// the LICENSE file.
  4 +//
  5 +// Package excelize providing a set of functions that allow you to write to
  6 +// and read from XLSX / XLSM / XLTM files. Supports reading and writing
  7 +// spreadsheet documents generated by Microsoft Exce™ 2007 and later. Supports
  8 +// complex components by high compatibility, and provided streaming API for
  9 +// generating or reading data from a worksheet with huge amounts of data. This
  10 +// library needs Go version 1.10 or later.
  11 +
  12 +package excelize
  13 +
  14 +import (
  15 + "bytes"
  16 + "container/list"
  17 + "errors"
  18 + "fmt"
  19 + "math"
  20 + "math/rand"
  21 + "reflect"
  22 + "regexp"
  23 + "sort"
  24 + "strconv"
  25 + "strings"
  26 + "time"
  27 +
  28 + "github.com/xuri/efp"
  29 +)
  30 +
  31 +// Excel formula errors
  32 +const (
  33 + formulaErrorDIV = "#DIV/0!"
  34 + formulaErrorNAME = "#NAME?"
  35 + formulaErrorNA = "#N/A"
  36 + formulaErrorNUM = "#NUM!"
  37 + formulaErrorVALUE = "#VALUE!"
  38 + formulaErrorREF = "#REF!"
  39 + formulaErrorNULL = "#NULL"
  40 + formulaErrorSPILL = "#SPILL!"
  41 + formulaErrorCALC = "#CALC!"
  42 + formulaErrorGETTINGDATA = "#GETTING_DATA"
  43 +)
  44 +
  45 +// cellRef defines the structure of a cell reference.
  46 +type cellRef struct {
  47 + Col int
  48 + Row int
  49 + Sheet string
  50 +}
  51 +
  52 +// cellRef defines the structure of a cell range.
  53 +type cellRange struct {
  54 + From cellRef
  55 + To cellRef
  56 +}
  57 +
  58 +// formula criteria condition enumeration.
  59 +const (
  60 + _ byte = iota
  61 + criteriaEq
  62 + criteriaLe
  63 + criteriaGe
  64 + criteriaL
  65 + criteriaG
  66 + criteriaBeg
  67 + criteriaEnd
  68 +)
  69 +
  70 +// formulaCriteria defined formula criteria parser result.
  71 +type formulaCriteria struct {
  72 + Type byte
  73 + Condition string
  74 +}
  75 +
  76 +// ArgType is the type if formula argument type.
  77 +type ArgType byte
  78 +
  79 +// Formula argument types enumeration.
  80 +const (
  81 + ArgUnknown ArgType = iota
  82 + ArgString
  83 + ArgMatrix
  84 +)
  85 +
  86 +// formulaArg is the argument of a formula or function.
  87 +type formulaArg struct {
  88 + String string
  89 + Matrix [][]formulaArg
  90 + Type ArgType
  91 +}
  92 +
  93 +// formulaFuncs is the type of the formula functions.
  94 +type formulaFuncs struct{}
  95 +
  96 +// CalcCellValue provides a function to get calculated cell value. This
  97 +// feature is currently in working processing. Array formula, table formula
  98 +// and some other formulas are not supported currently.
  99 +//
  100 +// Supported formulas:
  101 +//
  102 +// ABS, ACOS, ACOSH, ACOT, ACOTH, ARABIC, ASIN, ASINH, ATAN2, ATANH, BASE,
  103 +// CEILING, CEILING.MATH, CEILING.PRECISE, COMBIN, COMBINA, COS, COSH, COT,
  104 +// COTH, COUNTA, CSC, CSCH, DECIMAL, DEGREES, EVEN, EXP, FACT, FACTDOUBLE,
  105 +// FLOOR, FLOOR.MATH, FLOOR.PRECISE, GCD, INT, ISBLANK, ISERR, ISERROR,
  106 +// ISEVEN, ISNA, ISNONTEXT, ISNUMBER, ISO.CEILING, ISODD, LCM, LN, LOG,
  107 +// LOG10, MDETERM, MEDIAN, MOD, MROUND, MULTINOMIAL, MUNIT, NA, ODD, PI,
  108 +// POWER, PRODUCT, QUOTIENT, RADIANS, RAND, RANDBETWEEN, ROUND, ROUNDDOWN,
  109 +// ROUNDUP, SEC, SECH, SIGN, SIN, SINH, SQRT, SQRTPI, SUM, SUMIF, SUMSQ,
  110 +// TAN, TANH, TRUNC
  111 +//
  112 +func (f *File) CalcCellValue(sheet, cell string) (result string, err error) {
  113 + var (
  114 + formula string
  115 + token efp.Token
  116 + )
  117 + if formula, err = f.GetCellFormula(sheet, cell); err != nil {
  118 + return
  119 + }
  120 + ps := efp.ExcelParser()
  121 + tokens := ps.Parse(formula)
  122 + if tokens == nil {
  123 + return
  124 + }
  125 + if token, err = f.evalInfixExp(sheet, tokens); err != nil {
  126 + return
  127 + }
  128 + result = token.TValue
  129 + return
  130 +}
  131 +
  132 +// getPriority calculate arithmetic operator priority.
  133 +func getPriority(token efp.Token) (pri int) {
  134 + var priority = map[string]int{
  135 + "*": 2,
  136 + "/": 2,
  137 + "+": 1,
  138 + "-": 1,
  139 + }
  140 + pri, _ = priority[token.TValue]
  141 + if token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix {
  142 + pri = 3
  143 + }
  144 + if token.TSubType == efp.TokenSubTypeStart && token.TType == efp.TokenTypeSubexpression { // (
  145 + pri = 0
  146 + }
  147 + return
  148 +}
  149 +
  150 +// evalInfixExp evaluate syntax analysis by given infix expression after
  151 +// lexical analysis. Evaluate an infix expression containing formulas by
  152 +// stacks:
  153 +//
  154 +// opd - Operand
  155 +// opt - Operator
  156 +// opf - Operation formula
  157 +// opfd - Operand of the operation formula
  158 +// opft - Operator of the operation formula
  159 +//
  160 +// Evaluate arguments of the operation formula by list:
  161 +//
  162 +// args - Arguments of the operation formula
  163 +//
  164 +// TODO: handle subtypes: Nothing, Text, Logical, Error, Concatenation, Intersection, Union
  165 +//
  166 +func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error) {
  167 + var err error
  168 + opdStack, optStack, opfStack, opfdStack, opftStack := NewStack(), NewStack(), NewStack(), NewStack(), NewStack()
  169 + argsList := list.New()
  170 + for i := 0; i < len(tokens); i++ {
  171 + token := tokens[i]
  172 +
  173 + // out of function stack
  174 + if opfStack.Len() == 0 {
  175 + if err = f.parseToken(sheet, token, opdStack, optStack); err != nil {
  176 + return efp.Token{}, err
  177 + }
  178 + }
  179 +
  180 + // function start
  181 + if token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStart {
  182 + opfStack.Push(token)
  183 + continue
  184 + }
  185 +
  186 + // in function stack, walk 2 token at once
  187 + if opfStack.Len() > 0 {
  188 + var nextToken efp.Token
  189 + if i+1 < len(tokens) {
  190 + nextToken = tokens[i+1]
  191 + }
  192 +
  193 + // current token is args or range, skip next token, order required: parse reference first
  194 + if token.TSubType == efp.TokenSubTypeRange {
  195 + if !opftStack.Empty() {
  196 + // parse reference: must reference at here
  197 + result, err := f.parseReference(sheet, token.TValue)
  198 + if err != nil {
  199 + return efp.Token{TValue: formulaErrorNAME}, err
  200 + }
  201 + if result.Type != ArgString {
  202 + return efp.Token{}, errors.New(formulaErrorVALUE)
  203 + }
  204 + opfdStack.Push(efp.Token{
  205 + TType: efp.TokenTypeOperand,
  206 + TSubType: efp.TokenSubTypeNumber,
  207 + TValue: result.String,
  208 + })
  209 + continue
  210 + }
  211 + if nextToken.TType == efp.TokenTypeArgument || nextToken.TType == efp.TokenTypeFunction {
  212 + // parse reference: reference or range at here
  213 + result, err := f.parseReference(sheet, token.TValue)
  214 + if err != nil {
  215 + return efp.Token{TValue: formulaErrorNAME}, err
  216 + }
  217 + if result.Type == ArgUnknown {
  218 + return efp.Token{}, errors.New(formulaErrorVALUE)
  219 + }
  220 + argsList.PushBack(result)
  221 + continue
  222 + }
  223 + }
  224 +
  225 + // check current token is opft
  226 + if err = f.parseToken(sheet, token, opfdStack, opftStack); err != nil {
  227 + return efp.Token{}, err
  228 + }
  229 +
  230 + // current token is arg
  231 + if token.TType == efp.TokenTypeArgument {
  232 + for !opftStack.Empty() {
  233 + // calculate trigger
  234 + topOpt := opftStack.Peek().(efp.Token)
  235 + if err := calculate(opfdStack, topOpt); err != nil {
  236 + return efp.Token{}, err
  237 + }
  238 + opftStack.Pop()
  239 + }
  240 + if !opfdStack.Empty() {
  241 + argsList.PushBack(formulaArg{
  242 + String: opfdStack.Pop().(efp.Token).TValue,
  243 + Type: ArgString,
  244 + })
  245 + }
  246 + continue
  247 + }
  248 +
  249 + // current token is logical
  250 + if token.TType == efp.OperatorsInfix && token.TSubType == efp.TokenSubTypeLogical {
  251 + }
  252 +
  253 + // current token is text
  254 + if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeText {
  255 + argsList.PushBack(formulaArg{
  256 + String: token.TValue,
  257 + Type: ArgString,
  258 + })
  259 + }
  260 +
  261 + // current token is function stop
  262 + if token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStop {
  263 + for !opftStack.Empty() {
  264 + // calculate trigger
  265 + topOpt := opftStack.Peek().(efp.Token)
  266 + if err := calculate(opfdStack, topOpt); err != nil {
  267 + return efp.Token{}, err
  268 + }
  269 + opftStack.Pop()
  270 + }
  271 +
  272 + // push opfd to args
  273 + if opfdStack.Len() > 0 {
  274 + argsList.PushBack(formulaArg{
  275 + String: opfdStack.Pop().(efp.Token).TValue,
  276 + Type: ArgString,
  277 + })
  278 + }
  279 + // call formula function to evaluate
  280 + result, err := callFuncByName(&formulaFuncs{}, strings.NewReplacer(
  281 + "_xlfn", "", ".", "").Replace(opfStack.Peek().(efp.Token).TValue),
  282 + []reflect.Value{reflect.ValueOf(argsList)})
  283 + if err != nil {
  284 + return efp.Token{}, err
  285 + }
  286 + argsList.Init()
  287 + opfStack.Pop()
  288 + if opfStack.Len() > 0 { // still in function stack
  289 + opfdStack.Push(efp.Token{TValue: result, TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
  290 + } else {
  291 + opdStack.Push(efp.Token{TValue: result, TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
  292 + }
  293 + }
  294 + }
  295 + }
  296 + for optStack.Len() != 0 {
  297 + topOpt := optStack.Peek().(efp.Token)
  298 + if err = calculate(opdStack, topOpt); err != nil {
  299 + return efp.Token{}, err
  300 + }
  301 + optStack.Pop()
  302 + }
  303 + if opdStack.Len() == 0 {
  304 + return efp.Token{}, errors.New("formula not valid")
  305 + }
  306 + return opdStack.Peek().(efp.Token), err
  307 +}
  308 +
  309 +// calcAdd evaluate addition arithmetic operations.
  310 +func calcAdd(opdStack *Stack) error {
  311 + if opdStack.Len() < 2 {
  312 + return errors.New("formula not valid")
  313 + }
  314 + rOpd := opdStack.Pop().(efp.Token)
  315 + lOpd := opdStack.Pop().(efp.Token)
  316 + lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
  317 + if err != nil {
  318 + return err
  319 + }
  320 + rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
  321 + if err != nil {
  322 + return err
  323 + }
  324 + result := lOpdVal + rOpdVal
  325 + opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
  326 + return nil
  327 +}
  328 +
  329 +// calcSubtract evaluate subtraction arithmetic operations.
  330 +func calcSubtract(opdStack *Stack) error {
  331 + if opdStack.Len() < 2 {
  332 + return errors.New("formula not valid")
  333 + }
  334 + rOpd := opdStack.Pop().(efp.Token)
  335 + lOpd := opdStack.Pop().(efp.Token)
  336 + lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
  337 + if err != nil {
  338 + return err
  339 + }
  340 + rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
  341 + if err != nil {
  342 + return err
  343 + }
  344 + result := lOpdVal - rOpdVal
  345 + opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
  346 + return nil
  347 +}
  348 +
  349 +// calcMultiply evaluate multiplication arithmetic operations.
  350 +func calcMultiply(opdStack *Stack) error {
  351 + if opdStack.Len() < 2 {
  352 + return errors.New("formula not valid")
  353 + }
  354 + rOpd := opdStack.Pop().(efp.Token)
  355 + lOpd := opdStack.Pop().(efp.Token)
  356 + lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
  357 + if err != nil {
  358 + return err
  359 + }
  360 + rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
  361 + if err != nil {
  362 + return err
  363 + }
  364 + result := lOpdVal * rOpdVal
  365 + opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
  366 + return nil
  367 +}
  368 +
  369 +// calcDivide evaluate division arithmetic operations.
  370 +func calcDivide(opdStack *Stack) error {
  371 + if opdStack.Len() < 2 {
  372 + return errors.New("formula not valid")
  373 + }
  374 + rOpd := opdStack.Pop().(efp.Token)
  375 + lOpd := opdStack.Pop().(efp.Token)
  376 + lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
  377 + if err != nil {
  378 + return err
  379 + }
  380 + rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
  381 + if err != nil {
  382 + return err
  383 + }
  384 + result := lOpdVal / rOpdVal
  385 + if rOpdVal == 0 {
  386 + return errors.New(formulaErrorDIV)
  387 + }
  388 + opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
  389 + return nil
  390 +}
  391 +
  392 +// calculate evaluate basic arithmetic operations.
  393 +func calculate(opdStack *Stack, opt efp.Token) error {
  394 + if opt.TValue == "-" && opt.TType == efp.TokenTypeOperatorPrefix {
  395 + if opdStack.Len() < 1 {
  396 + return errors.New("formula not valid")
  397 + }
  398 + opd := opdStack.Pop().(efp.Token)
  399 + opdVal, err := strconv.ParseFloat(opd.TValue, 64)
  400 + if err != nil {
  401 + return err
  402 + }
  403 + result := 0 - opdVal
  404 + opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
  405 + }
  406 +
  407 + if opt.TValue == "+" {
  408 + if err := calcAdd(opdStack); err != nil {
  409 + return err
  410 + }
  411 + }
  412 + if opt.TValue == "-" && opt.TType == efp.TokenTypeOperatorInfix {
  413 + if err := calcSubtract(opdStack); err != nil {
  414 + return err
  415 + }
  416 + }
  417 + if opt.TValue == "*" {
  418 + if err := calcMultiply(opdStack); err != nil {
  419 + return err
  420 + }
  421 + }
  422 + if opt.TValue == "/" {
  423 + if err := calcDivide(opdStack); err != nil {
  424 + return err
  425 + }
  426 + }
  427 + return nil
  428 +}
  429 +
  430 +// parseOperatorPrefixToken parse operator prefix token.
  431 +func (f *File) parseOperatorPrefixToken(optStack, opdStack *Stack, token efp.Token) (err error) {
  432 + if optStack.Len() == 0 {
  433 + optStack.Push(token)
  434 + } else {
  435 + tokenPriority := getPriority(token)
  436 + topOpt := optStack.Peek().(efp.Token)
  437 + topOptPriority := getPriority(topOpt)
  438 + if tokenPriority > topOptPriority {
  439 + optStack.Push(token)
  440 + } else {
  441 + for tokenPriority <= topOptPriority {
  442 + optStack.Pop()
  443 + if err = calculate(opdStack, topOpt); err != nil {
  444 + return
  445 + }
  446 + if optStack.Len() > 0 {
  447 + topOpt = optStack.Peek().(efp.Token)
  448 + topOptPriority = getPriority(topOpt)
  449 + continue
  450 + }
  451 + break
  452 + }
  453 + optStack.Push(token)
  454 + }
  455 + }
  456 + return
  457 +}
  458 +
  459 +// isOperatorPrefixToken determine if the token is parse operator prefix
  460 +// token.
  461 +func isOperatorPrefixToken(token efp.Token) bool {
  462 + if (token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix) ||
  463 + token.TValue == "+" || token.TValue == "-" || token.TValue == "*" || token.TValue == "/" {
  464 + return true
  465 + }
  466 + return false
  467 +}
  468 +
  469 +func (f *File) getDefinedNameRefTo(definedNameName string, currentSheet string) (refTo string) {
  470 + for _, definedName := range f.GetDefinedName() {
  471 + if definedName.Name == definedNameName {
  472 + refTo = definedName.RefersTo
  473 + // worksheet scope takes precedence over scope workbook when both definedNames exist
  474 + if definedName.Scope == currentSheet {
  475 + break
  476 + }
  477 + }
  478 + }
  479 + return refTo
  480 +}
  481 +
  482 +// parseToken parse basic arithmetic operator priority and evaluate based on
  483 +// operators and operands.
  484 +func (f *File) parseToken(sheet string, token efp.Token, opdStack, optStack *Stack) error {
  485 + // parse reference: must reference at here
  486 + if token.TSubType == efp.TokenSubTypeRange {
  487 + refTo := f.getDefinedNameRefTo(token.TValue, sheet)
  488 + if refTo != "" {
  489 + token.TValue = refTo
  490 + }
  491 + result, err := f.parseReference(sheet, token.TValue)
  492 + if err != nil {
  493 + return errors.New(formulaErrorNAME)
  494 + }
  495 + if result.Type != ArgString {
  496 + return errors.New(formulaErrorVALUE)
  497 + }
  498 + token.TValue = result.String
  499 + token.TType = efp.TokenTypeOperand
  500 + token.TSubType = efp.TokenSubTypeNumber
  501 + }
  502 + if isOperatorPrefixToken(token) {
  503 + if err := f.parseOperatorPrefixToken(optStack, opdStack, token); err != nil {
  504 + return err
  505 + }
  506 + }
  507 + if token.TType == efp.TokenTypeSubexpression && token.TSubType == efp.TokenSubTypeStart { // (
  508 + optStack.Push(token)
  509 + }
  510 + if token.TType == efp.TokenTypeSubexpression && token.TSubType == efp.TokenSubTypeStop { // )
  511 + for optStack.Peek().(efp.Token).TSubType != efp.TokenSubTypeStart && optStack.Peek().(efp.Token).TType != efp.TokenTypeSubexpression { // != (
  512 + topOpt := optStack.Peek().(efp.Token)
  513 + if err := calculate(opdStack, topOpt); err != nil {
  514 + return err
  515 + }
  516 + optStack.Pop()
  517 + }
  518 + optStack.Pop()
  519 + }
  520 + // opd
  521 + if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeNumber {
  522 + opdStack.Push(token)
  523 + }
  524 + return nil
  525 +}
  526 +
  527 +// parseReference parse reference and extract values by given reference
  528 +// characters and default sheet name.
  529 +func (f *File) parseReference(sheet, reference string) (arg formulaArg, err error) {
  530 + reference = strings.Replace(reference, "$", "", -1)
  531 + refs, cellRanges, cellRefs := list.New(), list.New(), list.New()
  532 + for _, ref := range strings.Split(reference, ":") {
  533 + tokens := strings.Split(ref, "!")
  534 + cr := cellRef{}
  535 + if len(tokens) == 2 { // have a worksheet name
  536 + cr.Sheet = tokens[0]
  537 + if cr.Col, cr.Row, err = CellNameToCoordinates(tokens[1]); err != nil {
  538 + return
  539 + }
  540 + if refs.Len() > 0 {
  541 + e := refs.Back()
  542 + cellRefs.PushBack(e.Value.(cellRef))
  543 + refs.Remove(e)
  544 + }
  545 + refs.PushBack(cr)
  546 + continue
  547 + }
  548 + if cr.Col, cr.Row, err = CellNameToCoordinates(tokens[0]); err != nil {
  549 + return
  550 + }
  551 + e := refs.Back()
  552 + if e == nil {
  553 + cr.Sheet = sheet
  554 + refs.PushBack(cr)
  555 + continue
  556 + }
  557 + cellRanges.PushBack(cellRange{
  558 + From: e.Value.(cellRef),
  559 + To: cr,
  560 + })
  561 + refs.Remove(e)
  562 + }
  563 + if refs.Len() > 0 {
  564 + e := refs.Back()
  565 + cellRefs.PushBack(e.Value.(cellRef))
  566 + refs.Remove(e)
  567 + }
  568 + arg, err = f.rangeResolver(cellRefs, cellRanges)
  569 + return
  570 +}
  571 +
  572 +// prepareValueRange prepare value range.
  573 +func prepareValueRange(cr cellRange, valueRange []int) {
  574 + if cr.From.Row < valueRange[0] || valueRange[0] == 0 {
  575 + valueRange[0] = cr.From.Row
  576 + }
  577 + if cr.From.Col < valueRange[2] || valueRange[2] == 0 {
  578 + valueRange[2] = cr.From.Col
  579 + }
  580 + if cr.To.Row > valueRange[1] || valueRange[1] == 0 {
  581 + valueRange[1] = cr.To.Row
  582 + }
  583 + if cr.To.Col > valueRange[3] || valueRange[3] == 0 {
  584 + valueRange[3] = cr.To.Col
  585 + }
  586 +}
  587 +
  588 +// prepareValueRef prepare value reference.
  589 +func prepareValueRef(cr cellRef, valueRange []int) {
  590 + if cr.Row < valueRange[0] || valueRange[0] == 0 {
  591 + valueRange[0] = cr.Row
  592 + }
  593 + if cr.Col < valueRange[2] || valueRange[2] == 0 {
  594 + valueRange[2] = cr.Col
  595 + }
  596 + if cr.Row > valueRange[1] || valueRange[1] == 0 {
  597 + valueRange[1] = cr.Row
  598 + }
  599 + if cr.Col > valueRange[3] || valueRange[3] == 0 {
  600 + valueRange[3] = cr.Col
  601 + }
  602 +}
  603 +
  604 +// rangeResolver extract value as string from given reference and range list.
  605 +// This function will not ignore the empty cell. For example, A1:A2:A2:B3 will
  606 +// be reference A1:B3.
  607 +func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (arg formulaArg, err error) {
  608 + // value range order: from row, to row, from column, to column
  609 + valueRange := []int{0, 0, 0, 0}
  610 + var sheet string
  611 + // prepare value range
  612 + for temp := cellRanges.Front(); temp != nil; temp = temp.Next() {
  613 + cr := temp.Value.(cellRange)
  614 + if cr.From.Sheet != cr.To.Sheet {
  615 + err = errors.New(formulaErrorVALUE)
  616 + }
  617 + rng := []int{cr.From.Col, cr.From.Row, cr.To.Col, cr.To.Row}
  618 + sortCoordinates(rng)
  619 + cr.From.Col, cr.From.Row, cr.To.Col, cr.To.Row = rng[0], rng[1], rng[2], rng[3]
  620 + prepareValueRange(cr, valueRange)
  621 + if cr.From.Sheet != "" {
  622 + sheet = cr.From.Sheet
  623 + }
  624 + }
  625 + for temp := cellRefs.Front(); temp != nil; temp = temp.Next() {
  626 + cr := temp.Value.(cellRef)
  627 + if cr.Sheet != "" {
  628 + sheet = cr.Sheet
  629 + }
  630 + prepareValueRef(cr, valueRange)
  631 + }
  632 + // extract value from ranges
  633 + if cellRanges.Len() > 0 {
  634 + arg.Type = ArgMatrix
  635 + for row := valueRange[0]; row <= valueRange[1]; row++ {
  636 + var matrixRow = []formulaArg{}
  637 + for col := valueRange[2]; col <= valueRange[3]; col++ {
  638 + var cell, value string
  639 + if cell, err = CoordinatesToCellName(col, row); err != nil {
  640 + return
  641 + }
  642 + if value, err = f.GetCellValue(sheet, cell); err != nil {
  643 + return
  644 + }
  645 + matrixRow = append(matrixRow, formulaArg{
  646 + String: value,
  647 + Type: ArgString,
  648 + })
  649 + }
  650 + arg.Matrix = append(arg.Matrix, matrixRow)
  651 + }
  652 + return
  653 + }
  654 + // extract value from references
  655 + for temp := cellRefs.Front(); temp != nil; temp = temp.Next() {
  656 + cr := temp.Value.(cellRef)
  657 + var cell string
  658 + if cell, err = CoordinatesToCellName(cr.Col, cr.Row); err != nil {
  659 + return
  660 + }
  661 + if arg.String, err = f.GetCellValue(cr.Sheet, cell); err != nil {
  662 + return
  663 + }
  664 + arg.Type = ArgString
  665 + }
  666 + return
  667 +}
  668 +
  669 +// callFuncByName calls the no error or only error return function with
  670 +// reflect by given receiver, name and parameters.
  671 +func callFuncByName(receiver interface{}, name string, params []reflect.Value) (result string, err error) {
  672 + function := reflect.ValueOf(receiver).MethodByName(name)
  673 + if function.IsValid() {
  674 + rt := function.Call(params)
  675 + if len(rt) == 0 {
  676 + return
  677 + }
  678 + if !rt[1].IsNil() {
  679 + err = rt[1].Interface().(error)
  680 + return
  681 + }
  682 + result = rt[0].Interface().(string)
  683 + return
  684 + }
  685 + err = fmt.Errorf("not support %s function", name)
  686 + return
  687 +}
  688 +
  689 +// formulaCriteriaParser parse formula criteria.
  690 +func formulaCriteriaParser(exp string) (fc *formulaCriteria) {
  691 + fc = &formulaCriteria{}
  692 + if exp == "" {
  693 + return
  694 + }
  695 + if match := regexp.MustCompile(`^([0-9]+)$`).FindStringSubmatch(exp); len(match) > 1 {
  696 + fc.Type, fc.Condition = criteriaEq, match[1]
  697 + return
  698 + }
  699 + if match := regexp.MustCompile(`^=(.*)$`).FindStringSubmatch(exp); len(match) > 1 {
  700 + fc.Type, fc.Condition = criteriaEq, match[1]
  701 + return
  702 + }
  703 + if match := regexp.MustCompile(`^<(.*)$`).FindStringSubmatch(exp); len(match) > 1 {
  704 + fc.Type, fc.Condition = criteriaLe, match[1]
  705 + return
  706 + }
  707 + if match := regexp.MustCompile(`^>(.*)$`).FindStringSubmatch(exp); len(match) > 1 {
  708 + fc.Type, fc.Condition = criteriaGe, match[1]
  709 + return
  710 + }
  711 + if match := regexp.MustCompile(`^<=(.*)$`).FindStringSubmatch(exp); len(match) > 1 {
  712 + fc.Type, fc.Condition = criteriaL, match[1]
  713 + return
  714 + }
  715 + if match := regexp.MustCompile(`^>=(.*)$`).FindStringSubmatch(exp); len(match) > 1 {
  716 + fc.Type, fc.Condition = criteriaG, match[1]
  717 + return
  718 + }
  719 + if strings.Contains(exp, "*") {
  720 + if strings.HasPrefix(exp, "*") {
  721 + fc.Type, fc.Condition = criteriaEnd, strings.TrimPrefix(exp, "*")
  722 + }
  723 + if strings.HasSuffix(exp, "*") {
  724 + fc.Type, fc.Condition = criteriaBeg, strings.TrimSuffix(exp, "*")
  725 + }
  726 + return
  727 + }
  728 + fc.Type, fc.Condition = criteriaEq, exp
  729 + return
  730 +}
  731 +
  732 +// formulaCriteriaEval evaluate formula criteria expression.
  733 +func formulaCriteriaEval(val string, criteria *formulaCriteria) (result bool, err error) {
  734 + var value, expected float64
  735 + var prepareValue = func(val, cond string) (value float64, expected float64, err error) {
  736 + value, _ = strconv.ParseFloat(val, 64)
  737 + if expected, err = strconv.ParseFloat(criteria.Condition, 64); err != nil {
  738 + return
  739 + }
  740 + return
  741 + }
  742 + switch criteria.Type {
  743 + case criteriaEq:
  744 + return val == criteria.Condition, err
  745 + case criteriaLe:
  746 + if value, expected, err = prepareValue(val, criteria.Condition); err != nil {
  747 + return
  748 + }
  749 + return value <= expected, err
  750 + case criteriaGe:
  751 + if value, expected, err = prepareValue(val, criteria.Condition); err != nil {
  752 + return
  753 + }
  754 + return value >= expected, err
  755 + case criteriaL:
  756 + if value, expected, err = prepareValue(val, criteria.Condition); err != nil {
  757 + return
  758 + }
  759 + return value < expected, err
  760 + case criteriaG:
  761 + if value, expected, err = prepareValue(val, criteria.Condition); err != nil {
  762 + return
  763 + }
  764 + return value > expected, err
  765 + case criteriaBeg:
  766 + return strings.HasPrefix(val, criteria.Condition), err
  767 + case criteriaEnd:
  768 + return strings.HasSuffix(val, criteria.Condition), err
  769 + }
  770 + return
  771 +}
  772 +
  773 +// Math and Trigonometric functions
  774 +
  775 +// ABS function returns the absolute value of any supplied number. The syntax
  776 +// of the function is:
  777 +//
  778 +// ABS(number)
  779 +//
  780 +func (fn *formulaFuncs) ABS(argsList *list.List) (result string, err error) {
  781 + if argsList.Len() != 1 {
  782 + err = errors.New("ABS requires 1 numeric argument")
  783 + return
  784 + }
  785 + var val float64
  786 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  787 + err = errors.New(formulaErrorVALUE)
  788 + return
  789 + }
  790 + result = fmt.Sprintf("%g", math.Abs(val))
  791 + return
  792 +}
  793 +
  794 +// ACOS function calculates the arccosine (i.e. the inverse cosine) of a given
  795 +// number, and returns an angle, in radians, between 0 and π. The syntax of
  796 +// the function is:
  797 +//
  798 +// ACOS(number)
  799 +//
  800 +func (fn *formulaFuncs) ACOS(argsList *list.List) (result string, err error) {
  801 + if argsList.Len() != 1 {
  802 + err = errors.New("ACOS requires 1 numeric argument")
  803 + return
  804 + }
  805 + var val float64
  806 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  807 + err = errors.New(formulaErrorVALUE)
  808 + return
  809 + }
  810 + result = fmt.Sprintf("%g", math.Acos(val))
  811 + return
  812 +}
  813 +
  814 +// ACOSH function calculates the inverse hyperbolic cosine of a supplied number.
  815 +// of the function is:
  816 +//
  817 +// ACOSH(number)
  818 +//
  819 +func (fn *formulaFuncs) ACOSH(argsList *list.List) (result string, err error) {
  820 + if argsList.Len() != 1 {
  821 + err = errors.New("ACOSH requires 1 numeric argument")
  822 + return
  823 + }
  824 + var val float64
  825 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  826 + err = errors.New(formulaErrorVALUE)
  827 + return
  828 + }
  829 + result = fmt.Sprintf("%g", math.Acosh(val))
  830 + return
  831 +}
  832 +
  833 +// ACOT function calculates the arccotangent (i.e. the inverse cotangent) of a
  834 +// given number, and returns an angle, in radians, between 0 and π. The syntax
  835 +// of the function is:
  836 +//
  837 +// ACOT(number)
  838 +//
  839 +func (fn *formulaFuncs) ACOT(argsList *list.List) (result string, err error) {
  840 + if argsList.Len() != 1 {
  841 + err = errors.New("ACOT requires 1 numeric argument")
  842 + return
  843 + }
  844 + var val float64
  845 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  846 + err = errors.New(formulaErrorVALUE)
  847 + return
  848 + }
  849 + result = fmt.Sprintf("%g", math.Pi/2-math.Atan(val))
  850 + return
  851 +}
  852 +
  853 +// ACOTH function calculates the hyperbolic arccotangent (coth) of a supplied
  854 +// value. The syntax of the function is:
  855 +//
  856 +// ACOTH(number)
  857 +//
  858 +func (fn *formulaFuncs) ACOTH(argsList *list.List) (result string, err error) {
  859 + if argsList.Len() != 1 {
  860 + err = errors.New("ACOTH requires 1 numeric argument")
  861 + return
  862 + }
  863 + var val float64
  864 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  865 + err = errors.New(formulaErrorVALUE)
  866 + return
  867 + }
  868 + result = fmt.Sprintf("%g", math.Atanh(1/val))
  869 + return
  870 +}
  871 +
  872 +// ARABIC function converts a Roman numeral into an Arabic numeral. The syntax
  873 +// of the function is:
  874 +//
  875 +// ARABIC(text)
  876 +//
  877 +func (fn *formulaFuncs) ARABIC(argsList *list.List) (result string, err error) {
  878 + if argsList.Len() != 1 {
  879 + err = errors.New("ARABIC requires 1 numeric argument")
  880 + return
  881 + }
  882 + charMap := map[rune]float64{'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
  883 + val, last, prefix := 0.0, 0.0, 1.0
  884 + for _, char := range argsList.Front().Value.(formulaArg).String {
  885 + digit := 0.0
  886 + if char == '-' {
  887 + prefix = -1
  888 + continue
  889 + }
  890 + digit, _ = charMap[char]
  891 + val += digit
  892 + switch {
  893 + case last == digit && (last == 5 || last == 50 || last == 500):
  894 + result = formulaErrorVALUE
  895 + return
  896 + case 2*last == digit:
  897 + result = formulaErrorVALUE
  898 + return
  899 + }
  900 + if last < digit {
  901 + val -= 2 * last
  902 + }
  903 + last = digit
  904 + }
  905 + result = fmt.Sprintf("%g", prefix*val)
  906 + return
  907 +}
  908 +
  909 +// ASIN function calculates the arcsine (i.e. the inverse sine) of a given
  910 +// number, and returns an angle, in radians, between -π/2 and π/2. The syntax
  911 +// of the function is:
  912 +//
  913 +// ASIN(number)
  914 +//
  915 +func (fn *formulaFuncs) ASIN(argsList *list.List) (result string, err error) {
  916 + if argsList.Len() != 1 {
  917 + err = errors.New("ASIN requires 1 numeric argument")
  918 + return
  919 + }
  920 + var val float64
  921 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  922 + err = errors.New(formulaErrorVALUE)
  923 + return
  924 + }
  925 + result = fmt.Sprintf("%g", math.Asin(val))
  926 + return
  927 +}
  928 +
  929 +// ASINH function calculates the inverse hyperbolic sine of a supplied number.
  930 +// The syntax of the function is:
  931 +//
  932 +// ASINH(number)
  933 +//
  934 +func (fn *formulaFuncs) ASINH(argsList *list.List) (result string, err error) {
  935 + if argsList.Len() != 1 {
  936 + err = errors.New("ASINH requires 1 numeric argument")
  937 + return
  938 + }
  939 + var val float64
  940 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  941 + err = errors.New(formulaErrorVALUE)
  942 + return
  943 + }
  944 + result = fmt.Sprintf("%g", math.Asinh(val))
  945 + return
  946 +}
  947 +
  948 +// ATAN function calculates the arctangent (i.e. the inverse tangent) of a
  949 +// given number, and returns an angle, in radians, between -π/2 and +π/2. The
  950 +// syntax of the function is:
  951 +//
  952 +// ATAN(number)
  953 +//
  954 +func (fn *formulaFuncs) ATAN(argsList *list.List) (result string, err error) {
  955 + if argsList.Len() != 1 {
  956 + err = errors.New("ATAN requires 1 numeric argument")
  957 + return
  958 + }
  959 + var val float64
  960 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  961 + err = errors.New(formulaErrorVALUE)
  962 + return
  963 + }
  964 + result = fmt.Sprintf("%g", math.Atan(val))
  965 + return
  966 +}
  967 +
  968 +// ATANH function calculates the inverse hyperbolic tangent of a supplied
  969 +// number. The syntax of the function is:
  970 +//
  971 +// ATANH(number)
  972 +//
  973 +func (fn *formulaFuncs) ATANH(argsList *list.List) (result string, err error) {
  974 + if argsList.Len() != 1 {
  975 + err = errors.New("ATANH requires 1 numeric argument")
  976 + return
  977 + }
  978 + var val float64
  979 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  980 + err = errors.New(formulaErrorVALUE)
  981 + return
  982 + }
  983 + result = fmt.Sprintf("%g", math.Atanh(val))
  984 + return
  985 +}
  986 +
  987 +// ATAN2 function calculates the arctangent (i.e. the inverse tangent) of a
  988 +// given set of x and y coordinates, and returns an angle, in radians, between
  989 +// -π/2 and +π/2. The syntax of the function is:
  990 +//
  991 +// ATAN2(x_num,y_num)
  992 +//
  993 +func (fn *formulaFuncs) ATAN2(argsList *list.List) (result string, err error) {
  994 + if argsList.Len() != 2 {
  995 + err = errors.New("ATAN2 requires 2 numeric arguments")
  996 + return
  997 + }
  998 + var x, y float64
  999 + if x, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  1000 + err = errors.New(formulaErrorVALUE)
  1001 + return
  1002 + }
  1003 + if y, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1004 + err = errors.New(formulaErrorVALUE)
  1005 + return
  1006 + }
  1007 + result = fmt.Sprintf("%g", math.Atan2(x, y))
  1008 + return
  1009 +}
  1010 +
  1011 +// BASE function converts a number into a supplied base (radix), and returns a
  1012 +// text representation of the calculated value. The syntax of the function is:
  1013 +//
  1014 +// BASE(number,radix,[min_length])
  1015 +//
  1016 +func (fn *formulaFuncs) BASE(argsList *list.List) (result string, err error) {
  1017 + if argsList.Len() < 2 {
  1018 + err = errors.New("BASE requires at least 2 arguments")
  1019 + return
  1020 + }
  1021 + if argsList.Len() > 3 {
  1022 + err = errors.New("BASE allows at most 3 arguments")
  1023 + return
  1024 + }
  1025 + var number float64
  1026 + var radix, minLength int
  1027 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1028 + err = errors.New(formulaErrorVALUE)
  1029 + return
  1030 + }
  1031 + if radix, err = strconv.Atoi(argsList.Front().Next().Value.(formulaArg).String); err != nil {
  1032 + err = errors.New(formulaErrorVALUE)
  1033 + return
  1034 + }
  1035 + if radix < 2 || radix > 36 {
  1036 + err = errors.New("radix must be an integer >= 2 and <= 36")
  1037 + return
  1038 + }
  1039 + if argsList.Len() > 2 {
  1040 + if minLength, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil {
  1041 + err = errors.New(formulaErrorVALUE)
  1042 + return
  1043 + }
  1044 + }
  1045 + result = strconv.FormatInt(int64(number), radix)
  1046 + if len(result) < minLength {
  1047 + result = strings.Repeat("0", minLength-len(result)) + result
  1048 + }
  1049 + result = strings.ToUpper(result)
  1050 + return
  1051 +}
  1052 +
  1053 +// CEILING function rounds a supplied number away from zero, to the nearest
  1054 +// multiple of a given number. The syntax of the function is:
  1055 +//
  1056 +// CEILING(number,significance)
  1057 +//
  1058 +func (fn *formulaFuncs) CEILING(argsList *list.List) (result string, err error) {
  1059 + if argsList.Len() == 0 {
  1060 + err = errors.New("CEILING requires at least 1 argument")
  1061 + return
  1062 + }
  1063 + if argsList.Len() > 2 {
  1064 + err = errors.New("CEILING allows at most 2 arguments")
  1065 + return
  1066 + }
  1067 + number, significance, res := 0.0, 1.0, 0.0
  1068 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1069 + err = errors.New(formulaErrorVALUE)
  1070 + return
  1071 + }
  1072 + if number < 0 {
  1073 + significance = -1
  1074 + }
  1075 + if argsList.Len() > 1 {
  1076 + if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  1077 + err = errors.New(formulaErrorVALUE)
  1078 + return
  1079 + }
  1080 + }
  1081 + if significance < 0 && number > 0 {
  1082 + err = errors.New("negative sig to CEILING invalid")
  1083 + return
  1084 + }
  1085 + if argsList.Len() == 1 {
  1086 + result = fmt.Sprintf("%g", math.Ceil(number))
  1087 + return
  1088 + }
  1089 + number, res = math.Modf(number / significance)
  1090 + if res > 0 {
  1091 + number++
  1092 + }
  1093 + result = fmt.Sprintf("%g", number*significance)
  1094 + return
  1095 +}
  1096 +
  1097 +// CEILINGMATH function rounds a supplied number up to a supplied multiple of
  1098 +// significance. The syntax of the function is:
  1099 +//
  1100 +// CEILING.MATH(number,[significance],[mode])
  1101 +//
  1102 +func (fn *formulaFuncs) CEILINGMATH(argsList *list.List) (result string, err error) {
  1103 + if argsList.Len() == 0 {
  1104 + err = errors.New("CEILING.MATH requires at least 1 argument")
  1105 + return
  1106 + }
  1107 + if argsList.Len() > 3 {
  1108 + err = errors.New("CEILING.MATH allows at most 3 arguments")
  1109 + return
  1110 + }
  1111 + number, significance, mode := 0.0, 1.0, 1.0
  1112 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1113 + err = errors.New(formulaErrorVALUE)
  1114 + return
  1115 + }
  1116 + if number < 0 {
  1117 + significance = -1
  1118 + }
  1119 + if argsList.Len() > 1 {
  1120 + if significance, err = strconv.ParseFloat(argsList.Front().Next().Value.(formulaArg).String, 64); err != nil {
  1121 + err = errors.New(formulaErrorVALUE)
  1122 + return
  1123 + }
  1124 + }
  1125 + if argsList.Len() == 1 {
  1126 + result = fmt.Sprintf("%g", math.Ceil(number))
  1127 + return
  1128 + }
  1129 + if argsList.Len() > 2 {
  1130 + if mode, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  1131 + err = errors.New(formulaErrorVALUE)
  1132 + return
  1133 + }
  1134 + }
  1135 + val, res := math.Modf(number / significance)
  1136 + if res != 0 {
  1137 + if number > 0 {
  1138 + val++
  1139 + } else if mode < 0 {
  1140 + val--
  1141 + }
  1142 + }
  1143 + result = fmt.Sprintf("%g", val*significance)
  1144 + return
  1145 +}
  1146 +
  1147 +// CEILINGPRECISE function rounds a supplied number up (regardless of the
  1148 +// number's sign), to the nearest multiple of a given number. The syntax of
  1149 +// the function is:
  1150 +//
  1151 +// CEILING.PRECISE(number,[significance])
  1152 +//
  1153 +func (fn *formulaFuncs) CEILINGPRECISE(argsList *list.List) (result string, err error) {
  1154 + if argsList.Len() == 0 {
  1155 + err = errors.New("CEILING.PRECISE requires at least 1 argument")
  1156 + return
  1157 + }
  1158 + if argsList.Len() > 2 {
  1159 + err = errors.New("CEILING.PRECISE allows at most 2 arguments")
  1160 + return
  1161 + }
  1162 + number, significance := 0.0, 1.0
  1163 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1164 + err = errors.New(formulaErrorVALUE)
  1165 + return
  1166 + }
  1167 + if number < 0 {
  1168 + significance = -1
  1169 + }
  1170 + if argsList.Len() == 1 {
  1171 + result = fmt.Sprintf("%g", math.Ceil(number))
  1172 + return
  1173 + }
  1174 + if argsList.Len() > 1 {
  1175 + if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  1176 + err = errors.New(formulaErrorVALUE)
  1177 + return
  1178 + }
  1179 + significance = math.Abs(significance)
  1180 + if significance == 0 {
  1181 + result = "0"
  1182 + return
  1183 + }
  1184 + }
  1185 + val, res := math.Modf(number / significance)
  1186 + if res != 0 {
  1187 + if number > 0 {
  1188 + val++
  1189 + }
  1190 + }
  1191 + result = fmt.Sprintf("%g", val*significance)
  1192 + return
  1193 +}
  1194 +
  1195 +// COMBIN function calculates the number of combinations (in any order) of a
  1196 +// given number objects from a set. The syntax of the function is:
  1197 +//
  1198 +// COMBIN(number,number_chosen)
  1199 +//
  1200 +func (fn *formulaFuncs) COMBIN(argsList *list.List) (result string, err error) {
  1201 + if argsList.Len() != 2 {
  1202 + err = errors.New("COMBIN requires 2 argument")
  1203 + return
  1204 + }
  1205 + number, chosen, val := 0.0, 0.0, 1.0
  1206 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1207 + err = errors.New(formulaErrorVALUE)
  1208 + return
  1209 + }
  1210 + if chosen, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  1211 + err = errors.New(formulaErrorVALUE)
  1212 + return
  1213 + }
  1214 + number, chosen = math.Trunc(number), math.Trunc(chosen)
  1215 + if chosen > number {
  1216 + err = errors.New("COMBIN requires number >= number_chosen")
  1217 + return
  1218 + }
  1219 + if chosen == number || chosen == 0 {
  1220 + result = "1"
  1221 + return
  1222 + }
  1223 + for c := float64(1); c <= chosen; c++ {
  1224 + val *= (number + 1 - c) / c
  1225 + }
  1226 + result = fmt.Sprintf("%g", math.Ceil(val))
  1227 + return
  1228 +}
  1229 +
  1230 +// COMBINA function calculates the number of combinations, with repetitions,
  1231 +// of a given number objects from a set. The syntax of the function is:
  1232 +//
  1233 +// COMBINA(number,number_chosen)
  1234 +//
  1235 +func (fn *formulaFuncs) COMBINA(argsList *list.List) (result string, err error) {
  1236 + if argsList.Len() != 2 {
  1237 + err = errors.New("COMBINA requires 2 argument")
  1238 + return
  1239 + }
  1240 + var number, chosen float64
  1241 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1242 + err = errors.New(formulaErrorVALUE)
  1243 + return
  1244 + }
  1245 + if chosen, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  1246 + err = errors.New(formulaErrorVALUE)
  1247 + return
  1248 + }
  1249 + number, chosen = math.Trunc(number), math.Trunc(chosen)
  1250 + if number < chosen {
  1251 + err = errors.New("COMBINA requires number > number_chosen")
  1252 + return
  1253 + }
  1254 + if number == 0 {
  1255 + result = "0"
  1256 + return
  1257 + }
  1258 + args := list.New()
  1259 + args.PushBack(formulaArg{
  1260 + String: fmt.Sprintf("%g", number+chosen-1),
  1261 + Type: ArgString,
  1262 + })
  1263 + args.PushBack(formulaArg{
  1264 + String: fmt.Sprintf("%g", number-1),
  1265 + Type: ArgString,
  1266 + })
  1267 + return fn.COMBIN(args)
  1268 +}
  1269 +
  1270 +// COS function calculates the cosine of a given angle. The syntax of the
  1271 +// function is:
  1272 +//
  1273 +// COS(number)
  1274 +//
  1275 +func (fn *formulaFuncs) COS(argsList *list.List) (result string, err error) {
  1276 + if argsList.Len() != 1 {
  1277 + err = errors.New("COS requires 1 numeric argument")
  1278 + return
  1279 + }
  1280 + var val float64
  1281 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1282 + err = errors.New(formulaErrorVALUE)
  1283 + return
  1284 + }
  1285 + result = fmt.Sprintf("%g", math.Cos(val))
  1286 + return
  1287 +}
  1288 +
  1289 +// COSH function calculates the hyperbolic cosine (cosh) of a supplied number.
  1290 +// The syntax of the function is:
  1291 +//
  1292 +// COSH(number)
  1293 +//
  1294 +func (fn *formulaFuncs) COSH(argsList *list.List) (result string, err error) {
  1295 + if argsList.Len() != 1 {
  1296 + err = errors.New("COSH requires 1 numeric argument")
  1297 + return
  1298 + }
  1299 + var val float64
  1300 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1301 + err = errors.New(formulaErrorVALUE)
  1302 + return
  1303 + }
  1304 + result = fmt.Sprintf("%g", math.Cosh(val))
  1305 + return
  1306 +}
  1307 +
  1308 +// COT function calculates the cotangent of a given angle. The syntax of the
  1309 +// function is:
  1310 +//
  1311 +// COT(number)
  1312 +//
  1313 +func (fn *formulaFuncs) COT(argsList *list.List) (result string, err error) {
  1314 + if argsList.Len() != 1 {
  1315 + err = errors.New("COT requires 1 numeric argument")
  1316 + return
  1317 + }
  1318 + var val float64
  1319 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1320 + err = errors.New(formulaErrorVALUE)
  1321 + return
  1322 + }
  1323 + if val == 0 {
  1324 + err = errors.New(formulaErrorDIV)
  1325 + return
  1326 + }
  1327 + result = fmt.Sprintf("%g", math.Tan(val))
  1328 + return
  1329 +}
  1330 +
  1331 +// COTH function calculates the hyperbolic cotangent (coth) of a supplied
  1332 +// angle. The syntax of the function is:
  1333 +//
  1334 +// COTH(number)
  1335 +//
  1336 +func (fn *formulaFuncs) COTH(argsList *list.List) (result string, err error) {
  1337 + if argsList.Len() != 1 {
  1338 + err = errors.New("COTH requires 1 numeric argument")
  1339 + return
  1340 + }
  1341 + var val float64
  1342 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1343 + err = errors.New(formulaErrorVALUE)
  1344 + return
  1345 + }
  1346 + if val == 0 {
  1347 + err = errors.New(formulaErrorDIV)
  1348 + return
  1349 + }
  1350 + result = fmt.Sprintf("%g", math.Tanh(val))
  1351 + return
  1352 +}
  1353 +
  1354 +// CSC function calculates the cosecant of a given angle. The syntax of the
  1355 +// function is:
  1356 +//
  1357 +// CSC(number)
  1358 +//
  1359 +func (fn *formulaFuncs) CSC(argsList *list.List) (result string, err error) {
  1360 + if argsList.Len() != 1 {
  1361 + err = errors.New("CSC requires 1 numeric argument")
  1362 + return
  1363 + }
  1364 + var val float64
  1365 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1366 + err = errors.New(formulaErrorVALUE)
  1367 + return
  1368 + }
  1369 + if val == 0 {
  1370 + err = errors.New(formulaErrorDIV)
  1371 + return
  1372 + }
  1373 + result = fmt.Sprintf("%g", 1/math.Sin(val))
  1374 + return
  1375 +}
  1376 +
  1377 +// CSCH function calculates the hyperbolic cosecant (csch) of a supplied
  1378 +// angle. The syntax of the function is:
  1379 +//
  1380 +// CSCH(number)
  1381 +//
  1382 +func (fn *formulaFuncs) CSCH(argsList *list.List) (result string, err error) {
  1383 + if argsList.Len() != 1 {
  1384 + err = errors.New("CSCH requires 1 numeric argument")
  1385 + return
  1386 + }
  1387 + var val float64
  1388 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1389 + err = errors.New(formulaErrorVALUE)
  1390 + return
  1391 + }
  1392 + if val == 0 {
  1393 + err = errors.New(formulaErrorDIV)
  1394 + return
  1395 + }
  1396 + result = fmt.Sprintf("%g", 1/math.Sinh(val))
  1397 + return
  1398 +}
  1399 +
  1400 +// DECIMAL function converts a text representation of a number in a specified
  1401 +// base, into a decimal value. The syntax of the function is:
  1402 +//
  1403 +// DECIMAL(text,radix)
  1404 +//
  1405 +func (fn *formulaFuncs) DECIMAL(argsList *list.List) (result string, err error) {
  1406 + if argsList.Len() != 2 {
  1407 + err = errors.New("DECIMAL requires 2 numeric arguments")
  1408 + return
  1409 + }
  1410 + var text = argsList.Front().Value.(formulaArg).String
  1411 + var radix int
  1412 + if radix, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil {
  1413 + err = errors.New(formulaErrorVALUE)
  1414 + return
  1415 + }
  1416 + if len(text) > 2 && (strings.HasPrefix(text, "0x") || strings.HasPrefix(text, "0X")) {
  1417 + text = text[2:]
  1418 + }
  1419 + val, err := strconv.ParseInt(text, radix, 64)
  1420 + if err != nil {
  1421 + err = errors.New(formulaErrorVALUE)
  1422 + return
  1423 + }
  1424 + result = fmt.Sprintf("%g", float64(val))
  1425 + return
  1426 +}
  1427 +
  1428 +// DEGREES function converts radians into degrees. The syntax of the function
  1429 +// is:
  1430 +//
  1431 +// DEGREES(angle)
  1432 +//
  1433 +func (fn *formulaFuncs) DEGREES(argsList *list.List) (result string, err error) {
  1434 + if argsList.Len() != 1 {
  1435 + err = errors.New("DEGREES requires 1 numeric argument")
  1436 + return
  1437 + }
  1438 + var val float64
  1439 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1440 + err = errors.New(formulaErrorVALUE)
  1441 + return
  1442 + }
  1443 + if val == 0 {
  1444 + err = errors.New(formulaErrorDIV)
  1445 + return
  1446 + }
  1447 + result = fmt.Sprintf("%g", 180.0/math.Pi*val)
  1448 + return
  1449 +}
  1450 +
  1451 +// EVEN function rounds a supplied number away from zero (i.e. rounds a
  1452 +// positive number up and a negative number down), to the next even number.
  1453 +// The syntax of the function is:
  1454 +//
  1455 +// EVEN(number)
  1456 +//
  1457 +func (fn *formulaFuncs) EVEN(argsList *list.List) (result string, err error) {
  1458 + if argsList.Len() != 1 {
  1459 + err = errors.New("EVEN requires 1 numeric argument")
  1460 + return
  1461 + }
  1462 + var number float64
  1463 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1464 + err = errors.New(formulaErrorVALUE)
  1465 + return
  1466 + }
  1467 + sign := math.Signbit(number)
  1468 + m, frac := math.Modf(number / 2)
  1469 + val := m * 2
  1470 + if frac != 0 {
  1471 + if !sign {
  1472 + val += 2
  1473 + } else {
  1474 + val -= 2
  1475 + }
  1476 + }
  1477 + result = fmt.Sprintf("%g", val)
  1478 + return
  1479 +}
  1480 +
  1481 +// EXP function calculates the value of the mathematical constant e, raised to
  1482 +// the power of a given number. The syntax of the function is:
  1483 +//
  1484 +// EXP(number)
  1485 +//
  1486 +func (fn *formulaFuncs) EXP(argsList *list.List) (result string, err error) {
  1487 + if argsList.Len() != 1 {
  1488 + err = errors.New("EXP requires 1 numeric argument")
  1489 + return
  1490 + }
  1491 + var number float64
  1492 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1493 + err = errors.New(formulaErrorVALUE)
  1494 + return
  1495 + }
  1496 + result = strings.ToUpper(fmt.Sprintf("%g", math.Exp(number)))
  1497 + return
  1498 +}
  1499 +
  1500 +// fact returns the factorial of a supplied number.
  1501 +func fact(number float64) float64 {
  1502 + val := float64(1)
  1503 + for i := float64(2); i <= number; i++ {
  1504 + val *= i
  1505 + }
  1506 + return val
  1507 +}
  1508 +
  1509 +// FACT function returns the factorial of a supplied number. The syntax of the
  1510 +// function is:
  1511 +//
  1512 +// FACT(number)
  1513 +//
  1514 +func (fn *formulaFuncs) FACT(argsList *list.List) (result string, err error) {
  1515 + if argsList.Len() != 1 {
  1516 + err = errors.New("FACT requires 1 numeric argument")
  1517 + return
  1518 + }
  1519 + var number float64
  1520 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1521 + err = errors.New(formulaErrorVALUE)
  1522 + return
  1523 + }
  1524 + if number < 0 {
  1525 + err = errors.New(formulaErrorNUM)
  1526 + }
  1527 + result = strings.ToUpper(fmt.Sprintf("%g", fact(number)))
  1528 + return
  1529 +}
  1530 +
  1531 +// FACTDOUBLE function returns the double factorial of a supplied number. The
  1532 +// syntax of the function is:
  1533 +//
  1534 +// FACTDOUBLE(number)
  1535 +//
  1536 +func (fn *formulaFuncs) FACTDOUBLE(argsList *list.List) (result string, err error) {
  1537 + if argsList.Len() != 1 {
  1538 + err = errors.New("FACTDOUBLE requires 1 numeric argument")
  1539 + return
  1540 + }
  1541 + number, val := 0.0, 1.0
  1542 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1543 + err = errors.New(formulaErrorVALUE)
  1544 + return
  1545 + }
  1546 + if number < 0 {
  1547 + err = errors.New(formulaErrorNUM)
  1548 + return
  1549 + }
  1550 + for i := math.Trunc(number); i > 1; i -= 2 {
  1551 + val *= i
  1552 + }
  1553 + result = strings.ToUpper(fmt.Sprintf("%g", val))
  1554 + return
  1555 +}
  1556 +
  1557 +// FLOOR function rounds a supplied number towards zero to the nearest
  1558 +// multiple of a specified significance. The syntax of the function is:
  1559 +//
  1560 +// FLOOR(number,significance)
  1561 +//
  1562 +func (fn *formulaFuncs) FLOOR(argsList *list.List) (result string, err error) {
  1563 + if argsList.Len() != 2 {
  1564 + err = errors.New("FLOOR requires 2 numeric arguments")
  1565 + return
  1566 + }
  1567 + var number, significance float64
  1568 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1569 + err = errors.New(formulaErrorVALUE)
  1570 + return
  1571 + }
  1572 + if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  1573 + err = errors.New(formulaErrorVALUE)
  1574 + return
  1575 + }
  1576 + if significance < 0 && number >= 0 {
  1577 + err = errors.New(formulaErrorNUM)
  1578 + return
  1579 + }
  1580 + val := number
  1581 + val, res := math.Modf(val / significance)
  1582 + if res != 0 {
  1583 + if number < 0 && res < 0 {
  1584 + val--
  1585 + }
  1586 + }
  1587 + result = strings.ToUpper(fmt.Sprintf("%g", val*significance))
  1588 + return
  1589 +}
  1590 +
  1591 +// FLOORMATH function rounds a supplied number down to a supplied multiple of
  1592 +// significance. The syntax of the function is:
  1593 +//
  1594 +// FLOOR.MATH(number,[significance],[mode])
  1595 +//
  1596 +func (fn *formulaFuncs) FLOORMATH(argsList *list.List) (result string, err error) {
  1597 + if argsList.Len() == 0 {
  1598 + err = errors.New("FLOOR.MATH requires at least 1 argument")
  1599 + return
  1600 + }
  1601 + if argsList.Len() > 3 {
  1602 + err = errors.New("FLOOR.MATH allows at most 3 arguments")
  1603 + return
  1604 + }
  1605 + number, significance, mode := 0.0, 1.0, 1.0
  1606 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1607 + err = errors.New(formulaErrorVALUE)
  1608 + return
  1609 + }
  1610 + if number < 0 {
  1611 + significance = -1
  1612 + }
  1613 + if argsList.Len() > 1 {
  1614 + if significance, err = strconv.ParseFloat(argsList.Front().Next().Value.(formulaArg).String, 64); err != nil {
  1615 + err = errors.New(formulaErrorVALUE)
  1616 + return
  1617 + }
  1618 + }
  1619 + if argsList.Len() == 1 {
  1620 + result = fmt.Sprintf("%g", math.Floor(number))
  1621 + return
  1622 + }
  1623 + if argsList.Len() > 2 {
  1624 + if mode, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  1625 + err = errors.New(formulaErrorVALUE)
  1626 + return
  1627 + }
  1628 + }
  1629 + val, res := math.Modf(number / significance)
  1630 + if res != 0 && number < 0 && mode > 0 {
  1631 + val--
  1632 + }
  1633 + result = fmt.Sprintf("%g", val*significance)
  1634 + return
  1635 +}
  1636 +
  1637 +// FLOORPRECISE function rounds a supplied number down to a supplied multiple
  1638 +// of significance. The syntax of the function is:
  1639 +//
  1640 +// FLOOR.PRECISE(number,[significance])
  1641 +//
  1642 +func (fn *formulaFuncs) FLOORPRECISE(argsList *list.List) (result string, err error) {
  1643 + if argsList.Len() == 0 {
  1644 + err = errors.New("FLOOR.PRECISE requires at least 1 argument")
  1645 + return
  1646 + }
  1647 + if argsList.Len() > 2 {
  1648 + err = errors.New("FLOOR.PRECISE allows at most 2 arguments")
  1649 + return
  1650 + }
  1651 + var number, significance float64
  1652 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1653 + err = errors.New(formulaErrorVALUE)
  1654 + return
  1655 + }
  1656 + if number < 0 {
  1657 + significance = -1
  1658 + }
  1659 + if argsList.Len() == 1 {
  1660 + result = fmt.Sprintf("%g", math.Floor(number))
  1661 + return
  1662 + }
  1663 + if argsList.Len() > 1 {
  1664 + if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  1665 + err = errors.New(formulaErrorVALUE)
  1666 + return
  1667 + }
  1668 + significance = math.Abs(significance)
  1669 + if significance == 0 {
  1670 + result = "0"
  1671 + return
  1672 + }
  1673 + }
  1674 + val, res := math.Modf(number / significance)
  1675 + if res != 0 {
  1676 + if number < 0 {
  1677 + val--
  1678 + }
  1679 + }
  1680 + result = fmt.Sprintf("%g", val*significance)
  1681 + return
  1682 +}
  1683 +
  1684 +// gcd returns the greatest common divisor of two supplied integers.
  1685 +func gcd(x, y float64) float64 {
  1686 + x, y = math.Trunc(x), math.Trunc(y)
  1687 + if x == 0 {
  1688 + return y
  1689 + }
  1690 + if y == 0 {
  1691 + return x
  1692 + }
  1693 + for x != y {
  1694 + if x > y {
  1695 + x = x - y
  1696 + } else {
  1697 + y = y - x
  1698 + }
  1699 + }
  1700 + return x
  1701 +}
  1702 +
  1703 +// GCD function returns the greatest common divisor of two or more supplied
  1704 +// integers. The syntax of the function is:
  1705 +//
  1706 +// GCD(number1,[number2],...)
  1707 +//
  1708 +func (fn *formulaFuncs) GCD(argsList *list.List) (result string, err error) {
  1709 + if argsList.Len() == 0 {
  1710 + err = errors.New("GCD requires at least 1 argument")
  1711 + return
  1712 + }
  1713 + var (
  1714 + val float64
  1715 + nums = []float64{}
  1716 + )
  1717 + for arg := argsList.Front(); arg != nil; arg = arg.Next() {
  1718 + token := arg.Value.(formulaArg).String
  1719 + if token == "" {
  1720 + continue
  1721 + }
  1722 + if val, err = strconv.ParseFloat(token, 64); err != nil {
  1723 + err = errors.New(formulaErrorVALUE)
  1724 + return
  1725 + }
  1726 + nums = append(nums, val)
  1727 + }
  1728 + if nums[0] < 0 {
  1729 + err = errors.New("GCD only accepts positive arguments")
  1730 + return
  1731 + }
  1732 + if len(nums) == 1 {
  1733 + result = fmt.Sprintf("%g", nums[0])
  1734 + return
  1735 + }
  1736 + cd := nums[0]
  1737 + for i := 1; i < len(nums); i++ {
  1738 + if nums[i] < 0 {
  1739 + err = errors.New("GCD only accepts positive arguments")
  1740 + return
  1741 + }
  1742 + cd = gcd(cd, nums[i])
  1743 + }
  1744 + result = fmt.Sprintf("%g", cd)
  1745 + return
  1746 +}
  1747 +
  1748 +// INT function truncates a supplied number down to the closest integer. The
  1749 +// syntax of the function is:
  1750 +//
  1751 +// INT(number)
  1752 +//
  1753 +func (fn *formulaFuncs) INT(argsList *list.List) (result string, err error) {
  1754 + if argsList.Len() != 1 {
  1755 + err = errors.New("INT requires 1 numeric argument")
  1756 + return
  1757 + }
  1758 + var number float64
  1759 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1760 + err = errors.New(formulaErrorVALUE)
  1761 + return
  1762 + }
  1763 + val, frac := math.Modf(number)
  1764 + if frac < 0 {
  1765 + val--
  1766 + }
  1767 + result = fmt.Sprintf("%g", val)
  1768 + return
  1769 +}
  1770 +
  1771 +// ISOCEILING function rounds a supplied number up (regardless of the number's
  1772 +// sign), to the nearest multiple of a supplied significance. The syntax of
  1773 +// the function is:
  1774 +//
  1775 +// ISO.CEILING(number,[significance])
  1776 +//
  1777 +func (fn *formulaFuncs) ISOCEILING(argsList *list.List) (result string, err error) {
  1778 + if argsList.Len() == 0 {
  1779 + err = errors.New("ISO.CEILING requires at least 1 argument")
  1780 + return
  1781 + }
  1782 + if argsList.Len() > 2 {
  1783 + err = errors.New("ISO.CEILING allows at most 2 arguments")
  1784 + return
  1785 + }
  1786 + var number, significance float64
  1787 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1788 + err = errors.New(formulaErrorVALUE)
  1789 + return
  1790 + }
  1791 + if number < 0 {
  1792 + significance = -1
  1793 + }
  1794 + if argsList.Len() == 1 {
  1795 + result = fmt.Sprintf("%g", math.Ceil(number))
  1796 + return
  1797 + }
  1798 + if argsList.Len() > 1 {
  1799 + if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  1800 + err = errors.New(formulaErrorVALUE)
  1801 + return
  1802 + }
  1803 + significance = math.Abs(significance)
  1804 + if significance == 0 {
  1805 + result = "0"
  1806 + return
  1807 + }
  1808 + }
  1809 + val, res := math.Modf(number / significance)
  1810 + if res != 0 {
  1811 + if number > 0 {
  1812 + val++
  1813 + }
  1814 + }
  1815 + result = fmt.Sprintf("%g", val*significance)
  1816 + return
  1817 +}
  1818 +
  1819 +// lcm returns the least common multiple of two supplied integers.
  1820 +func lcm(a, b float64) float64 {
  1821 + a = math.Trunc(a)
  1822 + b = math.Trunc(b)
  1823 + if a == 0 && b == 0 {
  1824 + return 0
  1825 + }
  1826 + return a * b / gcd(a, b)
  1827 +}
  1828 +
  1829 +// LCM function returns the least common multiple of two or more supplied
  1830 +// integers. The syntax of the function is:
  1831 +//
  1832 +// LCM(number1,[number2],...)
  1833 +//
  1834 +func (fn *formulaFuncs) LCM(argsList *list.List) (result string, err error) {
  1835 + if argsList.Len() == 0 {
  1836 + err = errors.New("LCM requires at least 1 argument")
  1837 + return
  1838 + }
  1839 + var (
  1840 + val float64
  1841 + nums = []float64{}
  1842 + )
  1843 + for arg := argsList.Front(); arg != nil; arg = arg.Next() {
  1844 + token := arg.Value.(formulaArg).String
  1845 + if token == "" {
  1846 + continue
  1847 + }
  1848 + if val, err = strconv.ParseFloat(token, 64); err != nil {
  1849 + err = errors.New(formulaErrorVALUE)
  1850 + return
  1851 + }
  1852 + nums = append(nums, val)
  1853 + }
  1854 + if nums[0] < 0 {
  1855 + err = errors.New("LCM only accepts positive arguments")
  1856 + return
  1857 + }
  1858 + if len(nums) == 1 {
  1859 + result = fmt.Sprintf("%g", nums[0])
  1860 + return
  1861 + }
  1862 + cm := nums[0]
  1863 + for i := 1; i < len(nums); i++ {
  1864 + if nums[i] < 0 {
  1865 + err = errors.New("LCM only accepts positive arguments")
  1866 + return
  1867 + }
  1868 + cm = lcm(cm, nums[i])
  1869 + }
  1870 + result = fmt.Sprintf("%g", cm)
  1871 + return
  1872 +}
  1873 +
  1874 +// LN function calculates the natural logarithm of a given number. The syntax
  1875 +// of the function is:
  1876 +//
  1877 +// LN(number)
  1878 +//
  1879 +func (fn *formulaFuncs) LN(argsList *list.List) (result string, err error) {
  1880 + if argsList.Len() != 1 {
  1881 + err = errors.New("LN requires 1 numeric argument")
  1882 + return
  1883 + }
  1884 + var number float64
  1885 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1886 + err = errors.New(formulaErrorVALUE)
  1887 + return
  1888 + }
  1889 + result = fmt.Sprintf("%g", math.Log(number))
  1890 + return
  1891 +}
  1892 +
  1893 +// LOG function calculates the logarithm of a given number, to a supplied
  1894 +// base. The syntax of the function is:
  1895 +//
  1896 +// LOG(number,[base])
  1897 +//
  1898 +func (fn *formulaFuncs) LOG(argsList *list.List) (result string, err error) {
  1899 + if argsList.Len() == 0 {
  1900 + err = errors.New("LOG requires at least 1 argument")
  1901 + return
  1902 + }
  1903 + if argsList.Len() > 2 {
  1904 + err = errors.New("LOG allows at most 2 arguments")
  1905 + return
  1906 + }
  1907 + number, base := 0.0, 10.0
  1908 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1909 + err = errors.New(formulaErrorVALUE)
  1910 + return
  1911 + }
  1912 + if argsList.Len() > 1 {
  1913 + if base, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  1914 + err = errors.New(formulaErrorVALUE)
  1915 + return
  1916 + }
  1917 + }
  1918 + if number == 0 {
  1919 + err = errors.New(formulaErrorNUM)
  1920 + return
  1921 + }
  1922 + if base == 0 {
  1923 + err = errors.New(formulaErrorNUM)
  1924 + return
  1925 + }
  1926 + if base == 1 {
  1927 + err = errors.New(formulaErrorDIV)
  1928 + return
  1929 + }
  1930 + result = fmt.Sprintf("%g", math.Log(number)/math.Log(base))
  1931 + return
  1932 +}
  1933 +
  1934 +// LOG10 function calculates the base 10 logarithm of a given number. The
  1935 +// syntax of the function is:
  1936 +//
  1937 +// LOG10(number)
  1938 +//
  1939 +func (fn *formulaFuncs) LOG10(argsList *list.List) (result string, err error) {
  1940 + if argsList.Len() != 1 {
  1941 + err = errors.New("LOG10 requires 1 numeric argument")
  1942 + return
  1943 + }
  1944 + var number float64
  1945 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  1946 + err = errors.New(formulaErrorVALUE)
  1947 + return
  1948 + }
  1949 + result = fmt.Sprintf("%g", math.Log10(number))
  1950 + return
  1951 +}
  1952 +
  1953 +func minor(sqMtx [][]float64, idx int) [][]float64 {
  1954 + ret := [][]float64{}
  1955 + for i := range sqMtx {
  1956 + if i == 0 {
  1957 + continue
  1958 + }
  1959 + row := []float64{}
  1960 + for j := range sqMtx {
  1961 + if j == idx {
  1962 + continue
  1963 + }
  1964 + row = append(row, sqMtx[i][j])
  1965 + }
  1966 + ret = append(ret, row)
  1967 + }
  1968 + return ret
  1969 +}
  1970 +
  1971 +// det determinant of the 2x2 matrix.
  1972 +func det(sqMtx [][]float64) float64 {
  1973 + if len(sqMtx) == 2 {
  1974 + m00 := sqMtx[0][0]
  1975 + m01 := sqMtx[0][1]
  1976 + m10 := sqMtx[1][0]
  1977 + m11 := sqMtx[1][1]
  1978 + return m00*m11 - m10*m01
  1979 + }
  1980 + var res, sgn float64 = 0, 1
  1981 + for j := range sqMtx {
  1982 + res += sgn * sqMtx[0][j] * det(minor(sqMtx, j))
  1983 + sgn *= -1
  1984 + }
  1985 + return res
  1986 +}
  1987 +
  1988 +// MDETERM calculates the determinant of a square matrix. The
  1989 +// syntax of the function is:
  1990 +//
  1991 +// MDETERM(array)
  1992 +//
  1993 +func (fn *formulaFuncs) MDETERM(argsList *list.List) (result string, err error) {
  1994 + var num float64
  1995 + var numMtx = [][]float64{}
  1996 + var strMtx = argsList.Front().Value.(formulaArg).Matrix
  1997 + if argsList.Len() < 1 {
  1998 + return
  1999 + }
  2000 + var rows = len(strMtx)
  2001 + for _, row := range argsList.Front().Value.(formulaArg).Matrix {
  2002 + if len(row) != rows {
  2003 + err = errors.New(formulaErrorVALUE)
  2004 + return
  2005 + }
  2006 + numRow := []float64{}
  2007 + for _, ele := range row {
  2008 + if num, err = strconv.ParseFloat(ele.String, 64); err != nil {
  2009 + return
  2010 + }
  2011 + numRow = append(numRow, num)
  2012 + }
  2013 + numMtx = append(numMtx, numRow)
  2014 + }
  2015 + result = fmt.Sprintf("%g", det(numMtx))
  2016 + return
  2017 +}
  2018 +
  2019 +// MOD function returns the remainder of a division between two supplied
  2020 +// numbers. The syntax of the function is:
  2021 +//
  2022 +// MOD(number,divisor)
  2023 +//
  2024 +func (fn *formulaFuncs) MOD(argsList *list.List) (result string, err error) {
  2025 + if argsList.Len() != 2 {
  2026 + err = errors.New("MOD requires 2 numeric arguments")
  2027 + return
  2028 + }
  2029 + var number, divisor float64
  2030 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2031 + err = errors.New(formulaErrorVALUE)
  2032 + return
  2033 + }
  2034 + if divisor, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  2035 + err = errors.New(formulaErrorVALUE)
  2036 + return
  2037 + }
  2038 + if divisor == 0 {
  2039 + err = errors.New(formulaErrorDIV)
  2040 + return
  2041 + }
  2042 + trunc, rem := math.Modf(number / divisor)
  2043 + if rem < 0 {
  2044 + trunc--
  2045 + }
  2046 + result = fmt.Sprintf("%g", number-divisor*trunc)
  2047 + return
  2048 +}
  2049 +
  2050 +// MROUND function rounds a supplied number up or down to the nearest multiple
  2051 +// of a given number. The syntax of the function is:
  2052 +//
  2053 +// MOD(number,multiple)
  2054 +//
  2055 +func (fn *formulaFuncs) MROUND(argsList *list.List) (result string, err error) {
  2056 + if argsList.Len() != 2 {
  2057 + err = errors.New("MROUND requires 2 numeric arguments")
  2058 + return
  2059 + }
  2060 + var number, multiple float64
  2061 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2062 + err = errors.New(formulaErrorVALUE)
  2063 + return
  2064 + }
  2065 + if multiple, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  2066 + err = errors.New(formulaErrorVALUE)
  2067 + return
  2068 + }
  2069 + if multiple == 0 {
  2070 + err = errors.New(formulaErrorNUM)
  2071 + return
  2072 + }
  2073 + if multiple < 0 && number > 0 ||
  2074 + multiple > 0 && number < 0 {
  2075 + err = errors.New(formulaErrorNUM)
  2076 + return
  2077 + }
  2078 + number, res := math.Modf(number / multiple)
  2079 + if math.Trunc(res+0.5) > 0 {
  2080 + number++
  2081 + }
  2082 + result = fmt.Sprintf("%g", number*multiple)
  2083 + return
  2084 +}
  2085 +
  2086 +// MULTINOMIAL function calculates the ratio of the factorial of a sum of
  2087 +// supplied values to the product of factorials of those values. The syntax of
  2088 +// the function is:
  2089 +//
  2090 +// MULTINOMIAL(number1,[number2],...)
  2091 +//
  2092 +func (fn *formulaFuncs) MULTINOMIAL(argsList *list.List) (result string, err error) {
  2093 + val, num, denom := 0.0, 0.0, 1.0
  2094 + for arg := argsList.Front(); arg != nil; arg = arg.Next() {
  2095 + token := arg.Value.(formulaArg)
  2096 + if token.String == "" {
  2097 + continue
  2098 + }
  2099 + if val, err = strconv.ParseFloat(token.String, 64); err != nil {
  2100 + err = errors.New(formulaErrorVALUE)
  2101 + return
  2102 + }
  2103 + num += val
  2104 + denom *= fact(val)
  2105 + }
  2106 + result = fmt.Sprintf("%g", fact(num)/denom)
  2107 + return
  2108 +}
  2109 +
  2110 +// MUNIT function returns the unit matrix for a specified dimension. The
  2111 +// syntax of the function is:
  2112 +//
  2113 +// MUNIT(dimension)
  2114 +//
  2115 +func (fn *formulaFuncs) MUNIT(argsList *list.List) (result string, err error) {
  2116 + if argsList.Len() != 1 {
  2117 + err = errors.New("MUNIT requires 1 numeric argument")
  2118 + return
  2119 + }
  2120 + var dimension int
  2121 + if dimension, err = strconv.Atoi(argsList.Front().Value.(formulaArg).String); err != nil {
  2122 + err = errors.New(formulaErrorVALUE)
  2123 + return
  2124 + }
  2125 + matrix := make([][]float64, 0, dimension)
  2126 + for i := 0; i < dimension; i++ {
  2127 + row := make([]float64, dimension)
  2128 + for j := 0; j < dimension; j++ {
  2129 + if i == j {
  2130 + row[j] = float64(1.0)
  2131 + } else {
  2132 + row[j] = float64(0.0)
  2133 + }
  2134 + }
  2135 + matrix = append(matrix, row)
  2136 + }
  2137 + return
  2138 +}
  2139 +
  2140 +// ODD function ounds a supplied number away from zero (i.e. rounds a positive
  2141 +// number up and a negative number down), to the next odd number. The syntax
  2142 +// of the function is:
  2143 +//
  2144 +// ODD(number)
  2145 +//
  2146 +func (fn *formulaFuncs) ODD(argsList *list.List) (result string, err error) {
  2147 + if argsList.Len() != 1 {
  2148 + err = errors.New("ODD requires 1 numeric argument")
  2149 + return
  2150 + }
  2151 + var number float64
  2152 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2153 + err = errors.New(formulaErrorVALUE)
  2154 + return
  2155 + }
  2156 + if number == 0 {
  2157 + result = "1"
  2158 + return
  2159 + }
  2160 + sign := math.Signbit(number)
  2161 + m, frac := math.Modf((number - 1) / 2)
  2162 + val := m*2 + 1
  2163 + if frac != 0 {
  2164 + if !sign {
  2165 + val += 2
  2166 + } else {
  2167 + val -= 2
  2168 + }
  2169 + }
  2170 + result = fmt.Sprintf("%g", val)
  2171 + return
  2172 +}
  2173 +
  2174 +// PI function returns the value of the mathematical constant π (pi), accurate
  2175 +// to 15 digits (14 decimal places). The syntax of the function is:
  2176 +//
  2177 +// PI()
  2178 +//
  2179 +func (fn *formulaFuncs) PI(argsList *list.List) (result string, err error) {
  2180 + if argsList.Len() != 0 {
  2181 + err = errors.New("PI accepts no arguments")
  2182 + return
  2183 + }
  2184 + result = fmt.Sprintf("%g", math.Pi)
  2185 + return
  2186 +}
  2187 +
  2188 +// POWER function calculates a given number, raised to a supplied power.
  2189 +// The syntax of the function is:
  2190 +//
  2191 +// POWER(number,power)
  2192 +//
  2193 +func (fn *formulaFuncs) POWER(argsList *list.List) (result string, err error) {
  2194 + if argsList.Len() != 2 {
  2195 + err = errors.New("POWER requires 2 numeric arguments")
  2196 + return
  2197 + }
  2198 + var x, y float64
  2199 + if x, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2200 + err = errors.New(formulaErrorVALUE)
  2201 + return
  2202 + }
  2203 + if y, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  2204 + err = errors.New(formulaErrorVALUE)
  2205 + return
  2206 + }
  2207 + if x == 0 && y == 0 {
  2208 + err = errors.New(formulaErrorNUM)
  2209 + return
  2210 + }
  2211 + if x == 0 && y < 0 {
  2212 + err = errors.New(formulaErrorDIV)
  2213 + return
  2214 + }
  2215 + result = fmt.Sprintf("%g", math.Pow(x, y))
  2216 + return
  2217 +}
  2218 +
  2219 +// PRODUCT function returns the product (multiplication) of a supplied set of
  2220 +// numerical values. The syntax of the function is:
  2221 +//
  2222 +// PRODUCT(number1,[number2],...)
  2223 +//
  2224 +func (fn *formulaFuncs) PRODUCT(argsList *list.List) (result string, err error) {
  2225 + val, product := 0.0, 1.0
  2226 + for arg := argsList.Front(); arg != nil; arg = arg.Next() {
  2227 + token := arg.Value.(formulaArg)
  2228 + switch token.Type {
  2229 + case ArgUnknown:
  2230 + continue
  2231 + case ArgString:
  2232 + if token.String == "" {
  2233 + continue
  2234 + }
  2235 + if val, err = strconv.ParseFloat(token.String, 64); err != nil {
  2236 + err = errors.New(formulaErrorVALUE)
  2237 + return
  2238 + }
  2239 + product = product * val
  2240 + case ArgMatrix:
  2241 + for _, row := range token.Matrix {
  2242 + for _, value := range row {
  2243 + if value.String == "" {
  2244 + continue
  2245 + }
  2246 + if val, err = strconv.ParseFloat(value.String, 64); err != nil {
  2247 + err = errors.New(formulaErrorVALUE)
  2248 + return
  2249 + }
  2250 + product = product * val
  2251 + }
  2252 + }
  2253 + }
  2254 + }
  2255 + result = fmt.Sprintf("%g", product)
  2256 + return
  2257 +}
  2258 +
  2259 +// QUOTIENT function returns the integer portion of a division between two
  2260 +// supplied numbers. The syntax of the function is:
  2261 +//
  2262 +// QUOTIENT(numerator,denominator)
  2263 +//
  2264 +func (fn *formulaFuncs) QUOTIENT(argsList *list.List) (result string, err error) {
  2265 + if argsList.Len() != 2 {
  2266 + err = errors.New("QUOTIENT requires 2 numeric arguments")
  2267 + return
  2268 + }
  2269 + var x, y float64
  2270 + if x, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2271 + err = errors.New(formulaErrorVALUE)
  2272 + return
  2273 + }
  2274 + if y, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  2275 + err = errors.New(formulaErrorVALUE)
  2276 + return
  2277 + }
  2278 + if y == 0 {
  2279 + err = errors.New(formulaErrorDIV)
  2280 + return
  2281 + }
  2282 + result = fmt.Sprintf("%g", math.Trunc(x/y))
  2283 + return
  2284 +}
  2285 +
  2286 +// RADIANS function converts radians into degrees. The syntax of the function is:
  2287 +//
  2288 +// RADIANS(angle)
  2289 +//
  2290 +func (fn *formulaFuncs) RADIANS(argsList *list.List) (result string, err error) {
  2291 + if argsList.Len() != 1 {
  2292 + err = errors.New("RADIANS requires 1 numeric argument")
  2293 + return
  2294 + }
  2295 + var angle float64
  2296 + if angle, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2297 + err = errors.New(formulaErrorVALUE)
  2298 + return
  2299 + }
  2300 + result = fmt.Sprintf("%g", math.Pi/180.0*angle)
  2301 + return
  2302 +}
  2303 +
  2304 +// RAND function generates a random real number between 0 and 1. The syntax of
  2305 +// the function is:
  2306 +//
  2307 +// RAND()
  2308 +//
  2309 +func (fn *formulaFuncs) RAND(argsList *list.List) (result string, err error) {
  2310 + if argsList.Len() != 0 {
  2311 + err = errors.New("RAND accepts no arguments")
  2312 + return
  2313 + }
  2314 + result = fmt.Sprintf("%g", rand.New(rand.NewSource(time.Now().UnixNano())).Float64())
  2315 + return
  2316 +}
  2317 +
  2318 +// RANDBETWEEN function generates a random integer between two supplied
  2319 +// integers. The syntax of the function is:
  2320 +//
  2321 +// RANDBETWEEN(bottom,top)
  2322 +//
  2323 +func (fn *formulaFuncs) RANDBETWEEN(argsList *list.List) (result string, err error) {
  2324 + if argsList.Len() != 2 {
  2325 + err = errors.New("RANDBETWEEN requires 2 numeric arguments")
  2326 + return
  2327 + }
  2328 + var bottom, top int64
  2329 + if bottom, err = strconv.ParseInt(argsList.Front().Value.(formulaArg).String, 10, 64); err != nil {
  2330 + err = errors.New(formulaErrorVALUE)
  2331 + return
  2332 + }
  2333 + if top, err = strconv.ParseInt(argsList.Back().Value.(formulaArg).String, 10, 64); err != nil {
  2334 + err = errors.New(formulaErrorVALUE)
  2335 + return
  2336 + }
  2337 + if top < bottom {
  2338 + err = errors.New(formulaErrorNUM)
  2339 + return
  2340 + }
  2341 + result = fmt.Sprintf("%g", float64(rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(top-bottom+1)+bottom))
  2342 + return
  2343 +}
  2344 +
  2345 +// romanNumerals defined a numeral system that originated in ancient Rome and
  2346 +// remained the usual way of writing numbers throughout Europe well into the
  2347 +// Late Middle Ages.
  2348 +type romanNumerals struct {
  2349 + n float64
  2350 + s string
  2351 +}
  2352 +
  2353 +var romanTable = [][]romanNumerals{{{1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"}, {100, "C"}, {90, "XC"}, {50, "L"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}},
  2354 + {{1000, "M"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {95, "VC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}},
  2355 + {{1000, "M"}, {990, "XM"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {490, "XD"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {99, "IC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}},
  2356 + {{1000, "M"}, {995, "VM"}, {990, "XM"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {495, "VD"}, {490, "XD"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {99, "IC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}},
  2357 + {{1000, "M"}, {999, "IM"}, {995, "VM"}, {990, "XM"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {499, "ID"}, {495, "VD"}, {490, "XD"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {99, "IC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}}}
  2358 +
  2359 +// ROMAN function converts an arabic number to Roman. I.e. for a supplied
  2360 +// integer, the function returns a text string depicting the roman numeral
  2361 +// form of the number. The syntax of the function is:
  2362 +//
  2363 +// ROMAN(number,[form])
  2364 +//
  2365 +func (fn *formulaFuncs) ROMAN(argsList *list.List) (result string, err error) {
  2366 + if argsList.Len() == 0 {
  2367 + err = errors.New("ROMAN requires at least 1 argument")
  2368 + return
  2369 + }
  2370 + if argsList.Len() > 2 {
  2371 + err = errors.New("ROMAN allows at most 2 arguments")
  2372 + return
  2373 + }
  2374 + var number float64
  2375 + var form int
  2376 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2377 + err = errors.New(formulaErrorVALUE)
  2378 + return
  2379 + }
  2380 + if argsList.Len() > 1 {
  2381 + if form, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil {
  2382 + err = errors.New(formulaErrorVALUE)
  2383 + return
  2384 + }
  2385 + if form < 0 {
  2386 + form = 0
  2387 + } else if form > 4 {
  2388 + form = 4
  2389 + }
  2390 + }
  2391 + decimalTable := romanTable[0]
  2392 + switch form {
  2393 + case 1:
  2394 + decimalTable = romanTable[1]
  2395 + case 2:
  2396 + decimalTable = romanTable[2]
  2397 + case 3:
  2398 + decimalTable = romanTable[3]
  2399 + case 4:
  2400 + decimalTable = romanTable[4]
  2401 + }
  2402 + val := math.Trunc(number)
  2403 + buf := bytes.Buffer{}
  2404 + for _, r := range decimalTable {
  2405 + for val >= r.n {
  2406 + buf.WriteString(r.s)
  2407 + val -= r.n
  2408 + }
  2409 + }
  2410 + result = buf.String()
  2411 + return
  2412 +}
  2413 +
  2414 +type roundMode byte
  2415 +
  2416 +const (
  2417 + closest roundMode = iota
  2418 + down
  2419 + up
  2420 +)
  2421 +
  2422 +// round rounds a supplied number up or down.
  2423 +func (fn *formulaFuncs) round(number, digits float64, mode roundMode) float64 {
  2424 + var significance float64
  2425 + if digits > 0 {
  2426 + significance = math.Pow(1/10.0, digits)
  2427 + } else {
  2428 + significance = math.Pow(10.0, -digits)
  2429 + }
  2430 + val, res := math.Modf(number / significance)
  2431 + switch mode {
  2432 + case closest:
  2433 + const eps = 0.499999999
  2434 + if res >= eps {
  2435 + val++
  2436 + } else if res <= -eps {
  2437 + val--
  2438 + }
  2439 + case down:
  2440 + case up:
  2441 + if res > 0 {
  2442 + val++
  2443 + } else if res < 0 {
  2444 + val--
  2445 + }
  2446 + }
  2447 + return val * significance
  2448 +}
  2449 +
  2450 +// ROUND function rounds a supplied number up or down, to a specified number
  2451 +// of decimal places. The syntax of the function is:
  2452 +//
  2453 +// ROUND(number,num_digits)
  2454 +//
  2455 +func (fn *formulaFuncs) ROUND(argsList *list.List) (result string, err error) {
  2456 + if argsList.Len() != 2 {
  2457 + err = errors.New("ROUND requires 2 numeric arguments")
  2458 + return
  2459 + }
  2460 + var number, digits float64
  2461 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2462 + err = errors.New(formulaErrorVALUE)
  2463 + return
  2464 + }
  2465 + if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  2466 + err = errors.New(formulaErrorVALUE)
  2467 + return
  2468 + }
  2469 + result = fmt.Sprintf("%g", fn.round(number, digits, closest))
  2470 + return
  2471 +}
  2472 +
  2473 +// ROUNDDOWN function rounds a supplied number down towards zero, to a
  2474 +// specified number of decimal places. The syntax of the function is:
  2475 +//
  2476 +// ROUNDDOWN(number,num_digits)
  2477 +//
  2478 +func (fn *formulaFuncs) ROUNDDOWN(argsList *list.List) (result string, err error) {
  2479 + if argsList.Len() != 2 {
  2480 + err = errors.New("ROUNDDOWN requires 2 numeric arguments")
  2481 + return
  2482 + }
  2483 + var number, digits float64
  2484 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2485 + err = errors.New(formulaErrorVALUE)
  2486 + return
  2487 + }
  2488 + if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  2489 + err = errors.New(formulaErrorVALUE)
  2490 + return
  2491 + }
  2492 + result = fmt.Sprintf("%g", fn.round(number, digits, down))
  2493 + return
  2494 +}
  2495 +
  2496 +// ROUNDUP function rounds a supplied number up, away from zero, to a
  2497 +// specified number of decimal places. The syntax of the function is:
  2498 +//
  2499 +// ROUNDUP(number,num_digits)
  2500 +//
  2501 +func (fn *formulaFuncs) ROUNDUP(argsList *list.List) (result string, err error) {
  2502 + if argsList.Len() != 2 {
  2503 + err = errors.New("ROUNDUP requires 2 numeric arguments")
  2504 + return
  2505 + }
  2506 + var number, digits float64
  2507 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2508 + err = errors.New(formulaErrorVALUE)
  2509 + return
  2510 + }
  2511 + if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  2512 + err = errors.New(formulaErrorVALUE)
  2513 + return
  2514 + }
  2515 + result = fmt.Sprintf("%g", fn.round(number, digits, up))
  2516 + return
  2517 +}
  2518 +
  2519 +// SEC function calculates the secant of a given angle. The syntax of the
  2520 +// function is:
  2521 +//
  2522 +// SEC(number)
  2523 +//
  2524 +func (fn *formulaFuncs) SEC(argsList *list.List) (result string, err error) {
  2525 + if argsList.Len() != 1 {
  2526 + err = errors.New("SEC requires 1 numeric argument")
  2527 + return
  2528 + }
  2529 + var number float64
  2530 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2531 + err = errors.New(formulaErrorVALUE)
  2532 + return
  2533 + }
  2534 + result = fmt.Sprintf("%g", math.Cos(number))
  2535 + return
  2536 +}
  2537 +
  2538 +// SECH function calculates the hyperbolic secant (sech) of a supplied angle.
  2539 +// The syntax of the function is:
  2540 +//
  2541 +// SECH(number)
  2542 +//
  2543 +func (fn *formulaFuncs) SECH(argsList *list.List) (result string, err error) {
  2544 + if argsList.Len() != 1 {
  2545 + err = errors.New("SECH requires 1 numeric argument")
  2546 + return
  2547 + }
  2548 + var number float64
  2549 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2550 + err = errors.New(formulaErrorVALUE)
  2551 + return
  2552 + }
  2553 + result = fmt.Sprintf("%g", 1/math.Cosh(number))
  2554 + return
  2555 +}
  2556 +
  2557 +// SIGN function returns the arithmetic sign (+1, -1 or 0) of a supplied
  2558 +// number. I.e. if the number is positive, the Sign function returns +1, if
  2559 +// the number is negative, the function returns -1 and if the number is 0
  2560 +// (zero), the function returns 0. The syntax of the function is:
  2561 +//
  2562 +// SIGN(number)
  2563 +//
  2564 +func (fn *formulaFuncs) SIGN(argsList *list.List) (result string, err error) {
  2565 + if argsList.Len() != 1 {
  2566 + err = errors.New("SIGN requires 1 numeric argument")
  2567 + return
  2568 + }
  2569 + var val float64
  2570 + if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2571 + err = errors.New(formulaErrorVALUE)
  2572 + return
  2573 + }
  2574 + if val < 0 {
  2575 + result = "-1"
  2576 + return
  2577 + }
  2578 + if val > 0 {
  2579 + result = "1"
  2580 + return
  2581 + }
  2582 + result = "0"
  2583 + return
  2584 +}
  2585 +
  2586 +// SIN function calculates the sine of a given angle. The syntax of the
  2587 +// function is:
  2588 +//
  2589 +// SIN(number)
  2590 +//
  2591 +func (fn *formulaFuncs) SIN(argsList *list.List) (result string, err error) {
  2592 + if argsList.Len() != 1 {
  2593 + err = errors.New("SIN requires 1 numeric argument")
  2594 + return
  2595 + }
  2596 + var number float64
  2597 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2598 + err = errors.New(formulaErrorVALUE)
  2599 + return
  2600 + }
  2601 + result = fmt.Sprintf("%g", math.Sin(number))
  2602 + return
  2603 +}
  2604 +
  2605 +// SINH function calculates the hyperbolic sine (sinh) of a supplied number.
  2606 +// The syntax of the function is:
  2607 +//
  2608 +// SINH(number)
  2609 +//
  2610 +func (fn *formulaFuncs) SINH(argsList *list.List) (result string, err error) {
  2611 + if argsList.Len() != 1 {
  2612 + err = errors.New("SINH requires 1 numeric argument")
  2613 + return
  2614 + }
  2615 + var number float64
  2616 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2617 + err = errors.New(formulaErrorVALUE)
  2618 + return
  2619 + }
  2620 + result = fmt.Sprintf("%g", math.Sinh(number))
  2621 + return
  2622 +}
  2623 +
  2624 +// SQRT function calculates the positive square root of a supplied number. The
  2625 +// syntax of the function is:
  2626 +//
  2627 +// SQRT(number)
  2628 +//
  2629 +func (fn *formulaFuncs) SQRT(argsList *list.List) (result string, err error) {
  2630 + if argsList.Len() != 1 {
  2631 + err = errors.New("SQRT requires 1 numeric argument")
  2632 + return
  2633 + }
  2634 + var res float64
  2635 + var value = argsList.Front().Value.(formulaArg).String
  2636 + if value == "" {
  2637 + result = "0"
  2638 + return
  2639 + }
  2640 + if res, err = strconv.ParseFloat(value, 64); err != nil {
  2641 + err = errors.New(formulaErrorVALUE)
  2642 + return
  2643 + }
  2644 + if res < 0 {
  2645 + err = errors.New(formulaErrorNUM)
  2646 + return
  2647 + }
  2648 + result = fmt.Sprintf("%g", math.Sqrt(res))
  2649 + return
  2650 +}
  2651 +
  2652 +// SQRTPI function returns the square root of a supplied number multiplied by
  2653 +// the mathematical constant, π. The syntax of the function is:
  2654 +//
  2655 +// SQRTPI(number)
  2656 +//
  2657 +func (fn *formulaFuncs) SQRTPI(argsList *list.List) (result string, err error) {
  2658 + if argsList.Len() != 1 {
  2659 + err = errors.New("SQRTPI requires 1 numeric argument")
  2660 + return
  2661 + }
  2662 + var number float64
  2663 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2664 + err = errors.New(formulaErrorVALUE)
  2665 + return
  2666 + }
  2667 + result = fmt.Sprintf("%g", math.Sqrt(number*math.Pi))
  2668 + return
  2669 +}
  2670 +
  2671 +// SUM function adds together a supplied set of numbers and returns the sum of
  2672 +// these values. The syntax of the function is:
  2673 +//
  2674 +// SUM(number1,[number2],...)
  2675 +//
  2676 +func (fn *formulaFuncs) SUM(argsList *list.List) (result string, err error) {
  2677 + var val, sum float64
  2678 + for arg := argsList.Front(); arg != nil; arg = arg.Next() {
  2679 + token := arg.Value.(formulaArg)
  2680 + switch token.Type {
  2681 + case ArgUnknown:
  2682 + continue
  2683 + case ArgString:
  2684 + if token.String == "" {
  2685 + continue
  2686 + }
  2687 + if val, err = strconv.ParseFloat(token.String, 64); err != nil {
  2688 + err = errors.New(formulaErrorVALUE)
  2689 + return
  2690 + }
  2691 + sum += val
  2692 + case ArgMatrix:
  2693 + for _, row := range token.Matrix {
  2694 + for _, value := range row {
  2695 + if value.String == "" {
  2696 + continue
  2697 + }
  2698 + if val, err = strconv.ParseFloat(value.String, 64); err != nil {
  2699 + err = errors.New(formulaErrorVALUE)
  2700 + return
  2701 + }
  2702 + sum += val
  2703 + }
  2704 + }
  2705 + }
  2706 + }
  2707 + result = fmt.Sprintf("%g", sum)
  2708 + return
  2709 +}
  2710 +
  2711 +// SUMIF function finds the values in a supplied array, that satisfy a given
  2712 +// criteria, and returns the sum of the corresponding values in a second
  2713 +// supplied array. The syntax of the function is:
  2714 +//
  2715 +// SUMIF(range,criteria,[sum_range])
  2716 +//
  2717 +func (fn *formulaFuncs) SUMIF(argsList *list.List) (result string, err error) {
  2718 + if argsList.Len() < 2 {
  2719 + err = errors.New("SUMIF requires at least 2 argument")
  2720 + return
  2721 + }
  2722 + var criteria = formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg).String)
  2723 + var rangeMtx = argsList.Front().Value.(formulaArg).Matrix
  2724 + var sumRange [][]formulaArg
  2725 + if argsList.Len() == 3 {
  2726 + sumRange = argsList.Back().Value.(formulaArg).Matrix
  2727 + }
  2728 + var sum, val float64
  2729 + for rowIdx, row := range rangeMtx {
  2730 + for colIdx, col := range row {
  2731 + var ok bool
  2732 + fromVal := col.String
  2733 + if col.String == "" {
  2734 + continue
  2735 + }
  2736 + if ok, err = formulaCriteriaEval(fromVal, criteria); err != nil {
  2737 + return
  2738 + }
  2739 + if ok {
  2740 + if argsList.Len() == 3 {
  2741 + if len(sumRange) <= rowIdx || len(sumRange[rowIdx]) <= colIdx {
  2742 + continue
  2743 + }
  2744 + fromVal = sumRange[rowIdx][colIdx].String
  2745 + }
  2746 + if val, err = strconv.ParseFloat(fromVal, 64); err != nil {
  2747 + err = errors.New(formulaErrorVALUE)
  2748 + return
  2749 + }
  2750 + sum += val
  2751 + }
  2752 + }
  2753 + }
  2754 + result = fmt.Sprintf("%g", sum)
  2755 + return
  2756 +}
  2757 +
  2758 +// SUMSQ function returns the sum of squares of a supplied set of values. The
  2759 +// syntax of the function is:
  2760 +//
  2761 +// SUMSQ(number1,[number2],...)
  2762 +//
  2763 +func (fn *formulaFuncs) SUMSQ(argsList *list.List) (result string, err error) {
  2764 + var val, sq float64
  2765 + for arg := argsList.Front(); arg != nil; arg = arg.Next() {
  2766 + token := arg.Value.(formulaArg)
  2767 + switch token.Type {
  2768 + case ArgString:
  2769 + if token.String == "" {
  2770 + continue
  2771 + }
  2772 + if val, err = strconv.ParseFloat(token.String, 64); err != nil {
  2773 + err = errors.New(formulaErrorVALUE)
  2774 + return
  2775 + }
  2776 + sq += val * val
  2777 + case ArgMatrix:
  2778 + for _, row := range token.Matrix {
  2779 + for _, value := range row {
  2780 + if value.String == "" {
  2781 + continue
  2782 + }
  2783 + if val, err = strconv.ParseFloat(value.String, 64); err != nil {
  2784 + err = errors.New(formulaErrorVALUE)
  2785 + return
  2786 + }
  2787 + sq += val * val
  2788 + }
  2789 + }
  2790 + }
  2791 + }
  2792 + result = fmt.Sprintf("%g", sq)
  2793 + return
  2794 +}
  2795 +
  2796 +// TAN function calculates the tangent of a given angle. The syntax of the
  2797 +// function is:
  2798 +//
  2799 +// TAN(number)
  2800 +//
  2801 +func (fn *formulaFuncs) TAN(argsList *list.List) (result string, err error) {
  2802 + if argsList.Len() != 1 {
  2803 + err = errors.New("TAN requires 1 numeric argument")
  2804 + return
  2805 + }
  2806 + var number float64
  2807 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2808 + err = errors.New(formulaErrorVALUE)
  2809 + return
  2810 + }
  2811 + result = fmt.Sprintf("%g", math.Tan(number))
  2812 + return
  2813 +}
  2814 +
  2815 +// TANH function calculates the hyperbolic tangent (tanh) of a supplied
  2816 +// number. The syntax of the function is:
  2817 +//
  2818 +// TANH(number)
  2819 +//
  2820 +func (fn *formulaFuncs) TANH(argsList *list.List) (result string, err error) {
  2821 + if argsList.Len() != 1 {
  2822 + err = errors.New("TANH requires 1 numeric argument")
  2823 + return
  2824 + }
  2825 + var number float64
  2826 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2827 + err = errors.New(formulaErrorVALUE)
  2828 + return
  2829 + }
  2830 + result = fmt.Sprintf("%g", math.Tanh(number))
  2831 + return
  2832 +}
  2833 +
  2834 +// TRUNC function truncates a supplied number to a specified number of decimal
  2835 +// places. The syntax of the function is:
  2836 +//
  2837 +// TRUNC(number,[number_digits])
  2838 +//
  2839 +func (fn *formulaFuncs) TRUNC(argsList *list.List) (result string, err error) {
  2840 + if argsList.Len() == 0 {
  2841 + err = errors.New("TRUNC requires at least 1 argument")
  2842 + return
  2843 + }
  2844 + var number, digits, adjust, rtrim float64
  2845 + if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil {
  2846 + err = errors.New(formulaErrorVALUE)
  2847 + return
  2848 + }
  2849 + if argsList.Len() > 1 {
  2850 + if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  2851 + err = errors.New(formulaErrorVALUE)
  2852 + return
  2853 + }
  2854 + digits = math.Floor(digits)
  2855 + }
  2856 + adjust = math.Pow(10, digits)
  2857 + x := int((math.Abs(number) - math.Abs(float64(int(number)))) * adjust)
  2858 + if x != 0 {
  2859 + if rtrim, err = strconv.ParseFloat(strings.TrimRight(strconv.Itoa(x), "0"), 64); err != nil {
  2860 + return
  2861 + }
  2862 + }
  2863 + if (digits > 0) && (rtrim < adjust/10) {
  2864 + result = fmt.Sprintf("%g", number)
  2865 + return
  2866 + }
  2867 + result = fmt.Sprintf("%g", float64(int(number*adjust))/adjust)
  2868 + return
  2869 +}
  2870 +
  2871 +// Statistical functions
  2872 +
  2873 +// COUNTA function returns the number of non-blanks within a supplied set of
  2874 +// cells or values. The syntax of the function is:
  2875 +//
  2876 +// COUNTA(value1,[value2],...)
  2877 +//
  2878 +func (fn *formulaFuncs) COUNTA(argsList *list.List) (result string, err error) {
  2879 + var count int
  2880 + for token := argsList.Front(); token != nil; token = token.Next() {
  2881 + arg := token.Value.(formulaArg)
  2882 + switch arg.Type {
  2883 + case ArgString:
  2884 + if arg.String != "" {
  2885 + count++
  2886 + }
  2887 + case ArgMatrix:
  2888 + for _, row := range arg.Matrix {
  2889 + for _, value := range row {
  2890 + if value.String != "" {
  2891 + count++
  2892 + }
  2893 + }
  2894 + }
  2895 + }
  2896 + }
  2897 + result = fmt.Sprintf("%d", count)
  2898 + return
  2899 +}
  2900 +
  2901 +// MEDIAN function returns the statistical median (the middle value) of a list
  2902 +// of supplied numbers. The syntax of the function is:
  2903 +//
  2904 +// MEDIAN(number1,[number2],...)
  2905 +//
  2906 +func (fn *formulaFuncs) MEDIAN(argsList *list.List) (result string, err error) {
  2907 + if argsList.Len() == 0 {
  2908 + err = errors.New("MEDIAN requires at least 1 argument")
  2909 + return
  2910 + }
  2911 + values := []float64{}
  2912 + var median, digits float64
  2913 + for token := argsList.Front(); token != nil; token = token.Next() {
  2914 + arg := token.Value.(formulaArg)
  2915 + switch arg.Type {
  2916 + case ArgString:
  2917 + if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil {
  2918 + err = errors.New(formulaErrorVALUE)
  2919 + return
  2920 + }
  2921 + values = append(values, digits)
  2922 + case ArgMatrix:
  2923 + for _, row := range arg.Matrix {
  2924 + for _, value := range row {
  2925 + if value.String == "" {
  2926 + continue
  2927 + }
  2928 + if digits, err = strconv.ParseFloat(value.String, 64); err != nil {
  2929 + err = errors.New(formulaErrorVALUE)
  2930 + return
  2931 + }
  2932 + values = append(values, digits)
  2933 + }
  2934 + }
  2935 + }
  2936 + }
  2937 + sort.Float64s(values)
  2938 + if len(values)%2 == 0 {
  2939 + median = (values[len(values)/2-1] + values[len(values)/2]) / 2
  2940 + } else {
  2941 + median = values[len(values)/2]
  2942 + }
  2943 + result = fmt.Sprintf("%g", median)
  2944 + return
  2945 +}
  2946 +
  2947 +// Information functions
  2948 +
  2949 +// ISBLANK function tests if a specified cell is blank (empty) and if so,
  2950 +// returns TRUE; Otherwise the function returns FALSE. The syntax of the
  2951 +// function is:
  2952 +//
  2953 +// ISBLANK(value)
  2954 +//
  2955 +func (fn *formulaFuncs) ISBLANK(argsList *list.List) (result string, err error) {
  2956 + if argsList.Len() != 1 {
  2957 + err = errors.New("ISBLANK requires 1 argument")
  2958 + return
  2959 + }
  2960 + token := argsList.Front().Value.(formulaArg)
  2961 + result = "FALSE"
  2962 + switch token.Type {
  2963 + case ArgUnknown:
  2964 + result = "TRUE"
  2965 + case ArgString:
  2966 + if token.String == "" {
  2967 + result = "TRUE"
  2968 + }
  2969 + }
  2970 + return
  2971 +}
  2972 +
  2973 +// ISERR function tests if an initial supplied expression (or value) returns
  2974 +// any Excel Error, except the #N/A error. If so, the function returns the
  2975 +// logical value TRUE; If the supplied value is not an error or is the #N/A
  2976 +// error, the ISERR function returns FALSE. The syntax of the function is:
  2977 +//
  2978 +// ISERR(value)
  2979 +//
  2980 +func (fn *formulaFuncs) ISERR(argsList *list.List) (result string, err error) {
  2981 + if argsList.Len() != 1 {
  2982 + err = errors.New("ISERR requires 1 argument")
  2983 + return
  2984 + }
  2985 + token := argsList.Front().Value.(formulaArg)
  2986 + result = "FALSE"
  2987 + if token.Type == ArgString {
  2988 + for _, errType := range []string{formulaErrorDIV, formulaErrorNAME, formulaErrorNUM, formulaErrorVALUE, formulaErrorREF, formulaErrorNULL, formulaErrorSPILL, formulaErrorCALC, formulaErrorGETTINGDATA} {
  2989 + if errType == token.String {
  2990 + result = "TRUE"
  2991 + }
  2992 + }
  2993 + }
  2994 + return
  2995 +}
  2996 +
  2997 +// ISERROR function tests if an initial supplied expression (or value) returns
  2998 +// an Excel Error, and if so, returns the logical value TRUE; Otherwise the
  2999 +// function returns FALSE. The syntax of the function is:
  3000 +//
  3001 +// ISERROR(value)
  3002 +//
  3003 +func (fn *formulaFuncs) ISERROR(argsList *list.List) (result string, err error) {
  3004 + if argsList.Len() != 1 {
  3005 + err = errors.New("ISERROR requires 1 argument")
  3006 + return
  3007 + }
  3008 + token := argsList.Front().Value.(formulaArg)
  3009 + result = "FALSE"
  3010 + if token.Type == ArgString {
  3011 + for _, errType := range []string{formulaErrorDIV, formulaErrorNAME, formulaErrorNA, formulaErrorNUM, formulaErrorVALUE, formulaErrorREF, formulaErrorNULL, formulaErrorSPILL, formulaErrorCALC, formulaErrorGETTINGDATA} {
  3012 + if errType == token.String {
  3013 + result = "TRUE"
  3014 + }
  3015 + }
  3016 + }
  3017 + return
  3018 +}
  3019 +
  3020 +// ISEVEN function tests if a supplied number (or numeric expression)
  3021 +// evaluates to an even number, and if so, returns TRUE; Otherwise, the
  3022 +// function returns FALSE. The syntax of the function is:
  3023 +//
  3024 +// ISEVEN(value)
  3025 +//
  3026 +func (fn *formulaFuncs) ISEVEN(argsList *list.List) (result string, err error) {
  3027 + if argsList.Len() != 1 {
  3028 + err = errors.New("ISEVEN requires 1 argument")
  3029 + return
  3030 + }
  3031 + token := argsList.Front().Value.(formulaArg)
  3032 + result = "FALSE"
  3033 + var numeric int
  3034 + if token.Type == ArgString {
  3035 + if numeric, err = strconv.Atoi(token.String); err != nil {
  3036 + err = errors.New(formulaErrorVALUE)
  3037 + return
  3038 + }
  3039 + if numeric == numeric/2*2 {
  3040 + result = "TRUE"
  3041 + return
  3042 + }
  3043 + }
  3044 + return
  3045 +}
  3046 +
  3047 +// ISNA function tests if an initial supplied expression (or value) returns
  3048 +// the Excel #N/A Error, and if so, returns TRUE; Otherwise the function
  3049 +// returns FALSE. The syntax of the function is:
  3050 +//
  3051 +// ISNA(value)
  3052 +//
  3053 +func (fn *formulaFuncs) ISNA(argsList *list.List) (result string, err error) {
  3054 + if argsList.Len() != 1 {
  3055 + err = errors.New("ISNA requires 1 argument")
  3056 + return
  3057 + }
  3058 + token := argsList.Front().Value.(formulaArg)
  3059 + result = "FALSE"
  3060 + if token.Type == ArgString && token.String == formulaErrorNA {
  3061 + result = "TRUE"
  3062 + }
  3063 + return
  3064 +}
  3065 +
  3066 +// ISNONTEXT function function tests if a supplied value is text. If not, the
  3067 +// function returns TRUE; If the supplied value is text, the function returns
  3068 +// FALSE. The syntax of the function is:
  3069 +//
  3070 +// ISNONTEXT(value)
  3071 +//
  3072 +func (fn *formulaFuncs) ISNONTEXT(argsList *list.List) (result string, err error) {
  3073 + if argsList.Len() != 1 {
  3074 + err = errors.New("ISNONTEXT requires 1 argument")
  3075 + return
  3076 + }
  3077 + token := argsList.Front().Value.(formulaArg)
  3078 + result = "TRUE"
  3079 + if token.Type == ArgString && token.String != "" {
  3080 + result = "FALSE"
  3081 + }
  3082 + return
  3083 +}
  3084 +
  3085 +// ISNUMBER function function tests if a supplied value is a number. If so,
  3086 +// the function returns TRUE; Otherwise it returns FALSE. The syntax of the
  3087 +// function is:
  3088 +//
  3089 +// ISNUMBER(value)
  3090 +//
  3091 +func (fn *formulaFuncs) ISNUMBER(argsList *list.List) (result string, err error) {
  3092 + if argsList.Len() != 1 {
  3093 + err = errors.New("ISNUMBER requires 1 argument")
  3094 + return
  3095 + }
  3096 + token := argsList.Front().Value.(formulaArg)
  3097 + result = "FALSE"
  3098 + if token.Type == ArgString && token.String != "" {
  3099 + if _, err = strconv.Atoi(token.String); err == nil {
  3100 + result = "TRUE"
  3101 + }
  3102 + err = nil
  3103 + }
  3104 + return
  3105 +}
  3106 +
  3107 +// ISODD function tests if a supplied number (or numeric expression) evaluates
  3108 +// to an odd number, and if so, returns TRUE; Otherwise, the function returns
  3109 +// FALSE. The syntax of the function is:
  3110 +//
  3111 +// ISODD(value)
  3112 +//
  3113 +func (fn *formulaFuncs) ISODD(argsList *list.List) (result string, err error) {
  3114 + if argsList.Len() != 1 {
  3115 + err = errors.New("ISODD requires 1 argument")
  3116 + return
  3117 + }
  3118 + token := argsList.Front().Value.(formulaArg)
  3119 + result = "FALSE"
  3120 + var numeric int
  3121 + if token.Type == ArgString {
  3122 + if numeric, err = strconv.Atoi(token.String); err != nil {
  3123 + err = errors.New(formulaErrorVALUE)
  3124 + return
  3125 + }
  3126 + if numeric != numeric/2*2 {
  3127 + result = "TRUE"
  3128 + return
  3129 + }
  3130 + }
  3131 + return
  3132 +}
  3133 +
  3134 +// NA function returns the Excel #N/A error. This error message has the
  3135 +// meaning 'value not available' and is produced when an Excel Formula is
  3136 +// unable to find a value that it needs. The syntax of the function is:
  3137 +//
  3138 +// NA()
  3139 +//
  3140 +func (fn *formulaFuncs) NA(argsList *list.List) (result string, err error) {
  3141 + if argsList.Len() != 0 {
  3142 + err = errors.New("NA accepts no arguments")
  3143 + return
  3144 + }
  3145 + result = formulaErrorNA
  3146 + return
  3147 +}
  1 +// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
  2 +// this source code is governed by a BSD-style license that can be found in
  3 +// the LICENSE file.
  4 +//
  5 +// Package excelize providing a set of functions that allow you to write to
  6 +// and read from XLSX / XLSM / XLTM files. Supports reading and writing
  7 +// spreadsheet documents generated by Microsoft Exce™ 2007 and later. Supports
  8 +// complex components by high compatibility, and provided streaming API for
  9 +// generating or reading data from a worksheet with huge amounts of data. This
  10 +// library needs Go version 1.10 or later.
  11 +
  12 +package excelize
  13 +
  14 +import (
  15 + "bytes"
  16 + "encoding/xml"
  17 + "io"
  18 + "log"
  19 +)
  20 +
  21 +// calcChainReader provides a function to get the pointer to the structure
  22 +// after deserialization of xl/calcChain.xml.
  23 +func (f *File) calcChainReader() *xlsxCalcChain {
  24 + var err error
  25 +
  26 + if f.CalcChain == nil {
  27 + f.CalcChain = new(xlsxCalcChain)
  28 + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/calcChain.xml")))).
  29 + Decode(f.CalcChain); err != nil && err != io.EOF {
  30 + log.Printf("xml decode error: %s", err)
  31 + }
  32 + }
  33 +
  34 + return f.CalcChain
  35 +}
  36 +
  37 +// calcChainWriter provides a function to save xl/calcChain.xml after
  38 +// serialize structure.
  39 +func (f *File) calcChainWriter() {
  40 + if f.CalcChain != nil && f.CalcChain.C != nil {
  41 + output, _ := xml.Marshal(f.CalcChain)
  42 + f.saveFileList("xl/calcChain.xml", output)
  43 + }
  44 +}
  45 +
  46 +// deleteCalcChain provides a function to remove cell reference on the
  47 +// calculation chain.
  48 +func (f *File) deleteCalcChain(index int, axis string) {
  49 + calc := f.calcChainReader()
  50 + if calc != nil {
  51 + calc.C = xlsxCalcChainCollection(calc.C).Filter(func(c xlsxCalcChainC) bool {
  52 + return !((c.I == index && c.R == axis) || (c.I == index && axis == ""))
  53 + })
  54 + }
  55 + if len(calc.C) == 0 {
  56 + f.CalcChain = nil
  57 + delete(f.XLSX, "xl/calcChain.xml")
  58 + content := f.contentTypesReader()
  59 + for k, v := range content.Overrides {
  60 + if v.PartName == "/xl/calcChain.xml" {
  61 + content.Overrides = append(content.Overrides[:k], content.Overrides[k+1:]...)
  62 + }
  63 + }
  64 + }
  65 +}
  66 +
  67 +type xlsxCalcChainCollection []xlsxCalcChainC
  68 +
  69 +// Filter provides a function to filter calculation chain.
  70 +func (c xlsxCalcChainCollection) Filter(fn func(v xlsxCalcChainC) bool) []xlsxCalcChainC {
  71 + var results []xlsxCalcChainC
  72 + for _, v := range c {
  73 + if fn(v) {
  74 + results = append(results, v)
  75 + }
  76 + }
  77 + return results
  78 +}