diff --git a/.gitattributes b/.gitattributes index a0c478da1fc..c22d136ec50 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,5 @@ *.gno linguist-language=Go *.pb.go linguist-generated merge=ours -diff go.sum linguist-generated text +gnovm/stdlibs/native.go linguist-generated +gnovm/tests/stdlibs/native.go linguist-generated diff --git a/.github/codecov.yml b/.github/codecov.yml index ba70a236dc9..8dbcf376892 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -38,3 +38,7 @@ flags: paths: - gno.land after_n_builds: 3 + misc: + paths: + - misc + after_n_builds: 1 diff --git a/.github/workflows/db-tests.yml b/.github/workflows/db-tests.yml index 081a7517725..ed34b4cf215 100644 --- a/.github/workflows/db-tests.yml +++ b/.github/workflows/db-tests.yml @@ -7,7 +7,7 @@ on: - "go.sum" - ".github/workflows/db-tests.yml" push: - branches: [ "master" ] + branches: [ "patch/gnochess" ] concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 139c2569dc4..9300f12ecf9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -7,7 +7,7 @@ on: - "!docs/**" - "!**.md" push: - branches: [ "master" ] + branches: [ "patch/gnochess" ] concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -25,25 +25,8 @@ jobs: owner: ${{ github.repository_owner }} reponame: ${{ github.event.repository.name }} run: | - docker build -t ghcr.io/${owner}/${reponame} . - docker tag ghcr.io/${owner}/${reponame}:latest ghcr.io/${owner}/${reponame}:${GITHUB_SHA::8} - - - name: Build slim Docker images - env: - owner: ${{ github.repository_owner }} - reponame: ${{ github.event.repository.name }} - run: | - docker build --target=gnoland-slim -t ghcr.io/${owner}/${reponame}/gnoland-slim . - docker build --target=gnokey-slim -t ghcr.io/${owner}/${reponame}/gnokey-slim . - docker build --target=gno-slim -t ghcr.io/${owner}/${reponame}/gno-slim . - docker build --target=gnofaucet-slim -t ghcr.io/${owner}/${reponame}/gnofaucet-slim . - docker build --target=gnoweb-slim -t ghcr.io/${owner}/${reponame}/gnoweb-slim . - - docker tag ghcr.io/${owner}/${reponame}/gnoland-slim:latest ghcr.io/${owner}/${reponame}/gnoland-slim:${GITHUB_SHA::8} - docker tag ghcr.io/${owner}/${reponame}/gnokey-slim:latest ghcr.io/${owner}/${reponame}/gnokey-slim:${GITHUB_SHA::8} - docker tag ghcr.io/${owner}/${reponame}/gno-slim:latest ghcr.io/${owner}/${reponame}/gno-slim:${GITHUB_SHA::8} - docker tag ghcr.io/${owner}/${reponame}/gnofaucet-slim:latest ghcr.io/${owner}/${reponame}/gnofaucet-slim:${GITHUB_SHA::8} - docker tag ghcr.io/${owner}/${reponame}/gnoweb-slim:latest ghcr.io/${owner}/${reponame}/gnoweb-slim:${GITHUB_SHA::8} + docker build -t ghcr.io/${owner}/${reponame}/gnochess . + docker tag ghcr.io/${owner}/${reponame}/gnochess:latest ghcr.io/${owner}/${reponame}/gnochess:${GITHUB_SHA::8} - name: List docker images run: | @@ -63,20 +46,5 @@ jobs: owner: ${{ github.repository_owner }} reponame: ${{ github.event.repository.name }} run: | - docker push ghcr.io/${owner}/${reponame}:latest - docker push ghcr.io/${owner}/${reponame}:${GITHUB_SHA::8} - - docker push ghcr.io/${owner}/${reponame}/gnoland-slim:latest - docker push ghcr.io/${owner}/${reponame}/gnoland-slim:${GITHUB_SHA::8} - - docker push ghcr.io/${owner}/${reponame}/gnokey-slim:latest - docker push ghcr.io/${owner}/${reponame}/gnokey-slim:${GITHUB_SHA::8} - - docker push ghcr.io/${owner}/${reponame}/gno-slim:latest - docker push ghcr.io/${owner}/${reponame}/gno-slim:${GITHUB_SHA::8} - - docker push ghcr.io/${owner}/${reponame}/gnofaucet-slim:latest - docker push ghcr.io/${owner}/${reponame}/gnofaucet-slim:${GITHUB_SHA::8} - - docker push ghcr.io/${owner}/${reponame}/gnoweb-slim:latest - docker push ghcr.io/${owner}/${reponame}/gnoweb-slim:${GITHUB_SHA::8} + docker push ghcr.io/${owner}/${reponame}/gnochess:latest + docker push ghcr.io/${owner}/${reponame}/gnochess:${GITHUB_SHA::8} diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 4353682122a..3cfdad8d3bb 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -10,7 +10,7 @@ on: - "examples/**.gno" - ".github/workflows/examples.yml" push: - branches: [ "master" ] + branches: [ "patch/gnochess" ] concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/gnoland.yml b/.github/workflows/gnoland.yml index 902d0f41954..af9be8876c9 100644 --- a/.github/workflows/gnoland.yml +++ b/.github/workflows/gnoland.yml @@ -10,7 +10,7 @@ on: - "gno.land/**" - ".github/workflows/gnovm.yml" push: - branches: [ "master" ] + branches: [ "patch/gnochess" ] concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/gnovm.yml b/.github/workflows/gnovm.yml index e545e4ac6db..799b7b32ae1 100644 --- a/.github/workflows/gnovm.yml +++ b/.github/workflows/gnovm.yml @@ -12,7 +12,7 @@ on: - "tm2/**.go" - ".github/workflows/gnovm.yml" push: - branches: [ "master" ] + branches: [ "patch/gnochess" ] concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000000..33b3b4407d9 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,97 @@ +name: lint + +on: + push: + branches: [ "patch/gnochess" ] + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.x + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Lint + uses: golangci/golangci-lint-action@v3 + with: + # sync with misc/devdeps/go.mod + version: v1.54 + args: + --config=./.github/golangci.yml + fmt: + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.x + + - name: Install make + run: sudo apt-get install -y make + + - name: Checkout code + uses: actions/checkout@v4 + + # prefill dependencies so that mod messages don't show up in make output + - name: Fetch dependencies + run: go mod download -modfile ./misc/devdeps/go.mod -x + + # inspired by: + # https://github.com/Jerome1337/gofmt-action/blob/d5eabd189843f1d568286a54578159978b7c0fb1/entrypoint.sh + - name: Check gofumpt + run: | + output="$(GOFMT_FLAGS=-l make -s fmt)" + if [ ! -z "$output" ]; then + echo "The following files are not properly formatted; run 'make fmt' to format them." + echo "$output" + exit 1 + else + echo 'Succeeded.' + fi + modtidy: + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.x + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check go.mods + run: | + sums="$(sha256sum go.mod misc/devdeps/go.mod)" + for path in . ./misc/devdeps; do + env -C $path go mod tidy -v || exit 1 + done + echo "$sums" | sha256sum -c + generated: + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.x + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check generated files are up to date + run: | + go generate -x ./... + if [ "$(git status -s)" != "" ]; then + echo "command 'go generate' creates file that differ from git tree, please run 'go generate' and commit:" + git status -s + exit 1 + fi diff --git a/.github/workflows/misc.yml b/.github/workflows/misc.yml index 7ece5b5158d..5e557448ada 100644 --- a/.github/workflows/misc.yml +++ b/.github/workflows/misc.yml @@ -1,78 +1,66 @@ +# tests the "misc" directory & tools +# (not meant for miscellaneous workflows) name: misc on: - push: - branches: [ "master" ] pull_request: + paths: + - "misc/genstd/**.go" + - "misc/Makefile" + - ".github/workflows/misc.yml" + push: + branches: [ "patch/gnochess" ] concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: - lint: + build: + strategy: + fail-fast: false + matrix: + go-version: # two latest versions + - "1.21.x" + program: + - "genstd" runs-on: ubuntu-latest + timeout-minutes: 5 steps: - - name: Install Go - uses: actions/setup-go@v4 + - uses: actions/setup-go@v4 with: - go-version: 1.21.x + go-version: ${{ matrix.go-version }} + - uses: actions/checkout@v4 + - name: go install + working-directory: misc + run: go install ./${{ matrix.program }} - - name: Checkout code - uses: actions/checkout@v4 - - - name: Lint - uses: golangci/golangci-lint-action@v3 - with: - # sync with misc/devdeps/go.mod - version: v1.54 - args: - --config=./.github/golangci.yml - fmt: + test: + strategy: + fail-fast: false + matrix: + go-version: + - "1.21.x" + args: + - _test.genstd runs-on: ubuntu-latest + timeout-minutes: 15 steps: - - name: Install Go - uses: actions/setup-go@v4 + - uses: actions/setup-go@v4 with: - go-version: 1.20.x - - - name: Install make - run: sudo apt-get install -y make - - - name: Checkout code - uses: actions/checkout@v4 - - # prefill dependencies so that mod messages don't show up in make output - - name: Fetch dependencies - run: go mod download -modfile ./misc/devdeps/go.mod -x - - # inspired by: - # https://github.com/Jerome1337/gofmt-action/blob/d5eabd189843f1d568286a54578159978b7c0fb1/entrypoint.sh - - name: Check gofumpt + go-version: ${{ matrix.go-version }} + - uses: actions/checkout@v4 + - name: test + working-directory: misc run: | - output="$(GOFMT_FLAGS=-l make -s fmt)" - if [ ! -z "$output" ]; then - echo "The following files are not properly formatted; run 'make fmt' to format them." - echo "$output" - exit 1 - else - echo 'Succeeded.' - fi - modtidy: - runs-on: ubuntu-latest - steps: - - name: Install Go - uses: actions/setup-go@v4 + export GOPATH=$HOME/go + export GOTEST_FLAGS="-v -p 1 -timeout=30m -coverprofile=coverage.out -covermode=atomic" + make ${{ matrix.args }} + - if: runner.os == 'Linux' + uses: codecov/codecov-action@v3 with: - go-version: 1.20.x - - - name: Checkout code - uses: actions/checkout@v4 - - - name: Check go.mods - run: | - sums="$(sha256sum go.mod misc/devdeps/go.mod)" - for path in . ./misc/devdeps; do - env -C $path go mod tidy -v || exit 1 - done - echo "$sums" | sha256sum -c + token: ${{ secrets.CODECOV_TOKEN }} + name: misc + flags: misc,misc-${{matrix.args}},go-${{ matrix.go-version }} + files: ./misc/coverage.out + fail_ci_if_error: false # temporarily diff --git a/.github/workflows/tm2.yml b/.github/workflows/tm2.yml index 1be5bedc6c9..d8661aade26 100644 --- a/.github/workflows/tm2.yml +++ b/.github/workflows/tm2.yml @@ -8,7 +8,7 @@ on: - "tm2/**.go" - ".github/workflows/tm2.yml" push: - branches: [ "master" ] + branches: [ "patch/gnochess" ] concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/Makefile b/Makefile index 358094e5c4a..80c9bef2ed7 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ test.components: $(MAKE) --no-print-directory -C gnovm test $(MAKE) --no-print-directory -C gno.land test $(MAKE) --no-print-directory -C examples test + $(MAKE) --no-print-directory -C misc test .PHONY: test.docker test.docker: diff --git a/docs/stdlibs.md b/docs/stdlibs.md new file mode 100644 index 00000000000..b46327153cd --- /dev/null +++ b/docs/stdlibs.md @@ -0,0 +1,187 @@ +# Standard Libraries + +Gno comes with a set of standard libraries which are included whenever you +execute Gno code. These are distinguishable from imports of packages from the +chain by not referring to a "domain" as the first part of their import path. For +instance, `import "encoding/binary"` refers to a standard library, while +`import "gno.land/p/demo/avl"` refers to an on-chain package. + +Standard libraries packages follow the same semantics as on-chain packages (ie. +they don't persist state like realms do) and come as a part of the Gno +programming language rather than with the Gno.land chain. + +Many standard libaries are near-identical copies of the equivalent Go standard +libraries; in fact, you can check the current status of implementation of each +Go standard libarary on [Go\<\>Gno compatibility](go-gno-compatibility.md). + +## Gathering documentation + +At the time being, there is no "list" of the available standard libraries +available from Gno tooling or documentation, but you can obtain a list of all +the available packages with the following commands: + +```console +$ cd gnovm/stdlibs # go to correct directory +$ find -type d +./testing +./math +./crypto +./crypto/chacha20 +./crypto/chacha20/chacha +./crypto/chacha20/rand +./crypto/sha256 +./crypto/cipher +... +``` + +All of the packages have automatic, generated documentation through the use of +`gno doc`, which has similar functionality and features to `go doc`: + +```console +$ gno doc encoding/binary +package binary // import "encoding/binary" + +Package binary implements simple translation between numbers and byte sequences +and encoding and decoding of varints. + +[...] + +var BigEndian bigEndian +var LittleEndian littleEndian +type AppendByteOrder interface{ ... } +type ByteOrder interface{ ... } +$ gno doc -u -src encoding/binary littleEndian.AppendUint16 +package binary // import "encoding/binary" + +func (littleEndian) AppendUint16(b []byte, v uint16) []byte { + return append(b, + byte(v), + byte(v>>8), + ) +} +``` + +`gno doc` will work automatically when used within the Gno repository or any +repository which has a `go.mod` dependency on `github.com/gnolang/gno`, which +can be a simple way to set up your Gno repositories to automatically support +`gno` commands (aside from `doc`, also `test`, `run`, etc.). + +Another alternative is setting your enviornment variable `GNOROOT` to point to +where you cloned the Gno repository. You can set this in your `~/.profile` file +to be automatically set up in your console: + +```sh +export GNOROOT=$HOME/gno +``` + +## Test standard libraries + +There are some additional standard library functions and packages which are +currently available only in `_test.gno` and `_filetest.gno` files. At the time +of writing, these are only some additions in the `std` package to support +changing some values in test functions. + +`gno doc` currently doesn't support reading from the test standard libraries, +though support is planned to be added. For now, you can inspect the directory +`gnovm/tests/stdlibs`. + +## Adding new standard libraries + +New standard libraries may be added by simply creating a new directory (whose +path relative to the `stdlibs` directory will be the import path used in Gno +programs). Following that, the suggested approach for adding a Go standard +libary is to copy the original files from the Go source tree, and renaming their +extensions from `.go` to `.gno`. + +> As a small aid, this bash one-liner can be useful to convert all the file +> extensions: +> +> ```sh +> for i in *.go; do mv $i "$(echo $i | sed 's/\.go$/.gno/')"; done +> ``` + +Following that, the suggested approach is to iteratively try running `gno test .`, +while fixing any errors that may come out of trying to test the package. + +Some things to keep in mind: + +- Gno doesn't support assembly functions and build tags. Some Go packages may + contain assembly versions for different architecture and a `generic.go` file + containing the architecture-independent version. The general approach is that + of removing everything architecture/os-specific except for the `generic.go` file. +- Gno doesn't support reflection at the time of writing, which means that for + now many packages which rely heavily on reflection have to be delayed or + reduced while we figure out the details on how to implement reflection. + Aside from the `reflect` package itself, this also translates to very common + packages still not available in Gno, such as `fmt` or `encoding/json`. + +If you intend to create a PR to add a new standard library, remember to update +[Go\<\>Gno compatibility](go-gno-compatibility.md) accordingly. + +## Native bindings + +Gno has support for "natively-defined functions" exclusively within the standard +libaries. These are functions which are _declared_ in Gno code, but only _defined_ +in Go. There are generally three reasons why a function should be natively +defined: + +1. It relies on inspecting the Gno Virtual Machine itself.\ + For example: `std.AssertOriginCall`, `std.GetBanker`. +2. It relies on `unsafe`, or other features which are not planned to be + available in the GnoVM.\ + For example: `math.Float64frombits`. +3. Its native Go performance significantly outperforms the Gno counterpart by + several orders of magnitude, and it is used in crucial code or hot paths in + many programs.\ + For example: `sha256.Sum256`. + +The takeaway here is that native bindings are a special feature which can be +useful to overcome pure Gno limitations, but it is not a substitute for writing +standard libaries in Gno. + +There are three components to a natively bound function in Gno: + +1. The Gno function definition, which must be a top-level function with no body + (and no brackets).\ + For example: `crypto/sha256/sha256.gno`. +2. The Go function declaration, which must be a top-level function with the same + name and signature.\ + For example: `crypto/sha256/sha256.go`. +3. When the two above are present and valid, the native binding can be created + by executing the code generator: either execute `go generate` from the + `stdlibs` directory, or run `make generate` from the `gnovm` directory.\ + This generates the `native.go` file available in the `stdlibs` directory, + which provides the binding itself to then be used by the GnoVM. + +The code generator in question is available in the `misc/genstd` directory. +There are some quirks and features that must be kept in mind when writing native +bindings, which are the following: + +- Unexported functions (for instance, `func sum256(b []byte)`) must have their + Go counterpart prefixed with `X_` in order to make the functions exported (ie. + `func X_sum256(b []byte)`). +- The Go function declaration may specify as the first argument + `m *gno.Machine`, where `gno` is an import for + `github.com/gnolang/gno/gnovm/pkg/gnolang`. This gives the function access to + the Virtual Machine state, and is used by functions like `std.AssertOriginCall()`. +- The Go function may change the type of any parameter or result to + `gno.TypedValue` (where `gno` is an import for the above import path). This + means that the `native.go` generated code will not attempt to automatically + convert the Gno value into the Go value, and can be useful for unsupported + conversions like interface values. +- A small set of named types are "linked" between their Gno version and Go + counterpart. For instance, `std.Address` in Gno is + `(".../tm2/pkg/crypto").Bech32Address` in Go. A list of these can be found in + `misc/genstd/mapping.go`. +- Not all type literals are currently supported when converting from their Gno + version to their Go counterpart. Notable omissions at the time of writing + include struct and map literals. If you intend to use these, modify the code + generator to support them. +- The code generator does not inspect any imported packages from the Go native code; + as a consequence, either the import contains an identifier (ie. + `import gno "github.com/gnolang/gno/gnovm/pkg/gnolang"`), or the last element + in the import path is assumed to be the identifier used in the file. Note that + this is not always the case (the default identifier is the same as the + declared package name in the `package` clause of each Go file), so when the + last element and the package name differ, remember to specify an identifier in + the import. diff --git a/examples/gno.land/p/demo/avl/z_0_filetest.gno b/examples/gno.land/p/demo/avl/z_0_filetest.gno index 814e19d6d49..e91788ac8eb 100644 --- a/examples/gno.land/p/demo/avl/z_0_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_0_filetest.gno @@ -267,6 +267,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -301,6 +303,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/examples/gno.land/p/demo/avl/z_1_filetest.gno b/examples/gno.land/p/demo/avl/z_1_filetest.gno index 410e9e93601..cdd56a5ad89 100644 --- a/examples/gno.land/p/demo/avl/z_1_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_1_filetest.gno @@ -291,6 +291,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -325,6 +327,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/examples/gno.land/r/demo/boards/z_0_a_filetest.gno b/examples/gno.land/r/demo/boards/z_0_a_filetest.gno index 872d01dd793..ae1724dd86a 100644 --- a/examples/gno.land/r/demo/boards/z_0_a_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_0_a_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main import ( "gno.land/r/demo/boards" diff --git a/examples/gno.land/r/demo/boards/z_0_b_filetest.gno b/examples/gno.land/r/demo/boards/z_0_b_filetest.gno index 9ada8abd0c0..70f040eb7b2 100644 --- a/examples/gno.land/r/demo/boards/z_0_b_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_0_b_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 199000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_0_c_filetest.gno b/examples/gno.land/r/demo/boards/z_0_c_filetest.gno index 36b7bcfda04..216e4e4397f 100644 --- a/examples/gno.land/r/demo/boards/z_0_c_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_0_c_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_0_d_filetest.gno b/examples/gno.land/r/demo/boards/z_0_d_filetest.gno index 2e9e3f1436f..9539a9769a4 100644 --- a/examples/gno.land/r/demo/boards/z_0_d_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_0_d_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_0_e_filetest.gno b/examples/gno.land/r/demo/boards/z_0_e_filetest.gno index a06713fcbe3..3411891f465 100644 --- a/examples/gno.land/r/demo/boards/z_0_e_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_0_e_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_0_filetest.gno b/examples/gno.land/r/demo/boards/z_0_filetest.gno index 5c3fc7b204e..801231b67ee 100644 --- a/examples/gno.land/r/demo/boards/z_0_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_0_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_10_a_filetest.gno b/examples/gno.land/r/demo/boards/z_10_a_filetest.gno index 8ec2e45421a..02abd7dc143 100644 --- a/examples/gno.land/r/demo/boards/z_10_a_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_10_a_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_10_b_filetest.gno b/examples/gno.land/r/demo/boards/z_10_b_filetest.gno index 0981d85a57f..45a06ccc04c 100644 --- a/examples/gno.land/r/demo/boards/z_10_b_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_10_b_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 2000000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_10_c_filetest.gno b/examples/gno.land/r/demo/boards/z_10_c_filetest.gno index f161c8cedee..7f2a180b015 100644 --- a/examples/gno.land/r/demo/boards/z_10_c_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_10_c_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_10_filetest.gno b/examples/gno.land/r/demo/boards/z_10_filetest.gno index 7a24efe09ca..6f9264d8db9 100644 --- a/examples/gno.land/r/demo/boards/z_10_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_10_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_11_a_filetest.gno b/examples/gno.land/r/demo/boards/z_11_a_filetest.gno index fd62a884efa..65edb0ddeea 100644 --- a/examples/gno.land/r/demo/boards/z_11_a_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_11_a_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 2000000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_11_b_filetest.gno b/examples/gno.land/r/demo/boards/z_11_b_filetest.gno index 63907146f82..f1547e085d5 100644 --- a/examples/gno.land/r/demo/boards/z_11_b_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_11_b_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 2000000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_11_c_filetest.gno b/examples/gno.land/r/demo/boards/z_11_c_filetest.gno index ba40fa39021..9bec2d58447 100644 --- a/examples/gno.land/r/demo/boards/z_11_c_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_11_c_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 2000000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_11_d_filetest.gno b/examples/gno.land/r/demo/boards/z_11_d_filetest.gno index a519d4f6a94..7c4abfa36c0 100644 --- a/examples/gno.land/r/demo/boards/z_11_d_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_11_d_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 2000000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_11_filetest.gno b/examples/gno.land/r/demo/boards/z_11_filetest.gno index 2d96d46dc98..d38b0223cd3 100644 --- a/examples/gno.land/r/demo/boards/z_11_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_11_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 2000000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_1_filetest.gno b/examples/gno.land/r/demo/boards/z_1_filetest.gno index b5c7bb863e1..13bea83ae41 100644 --- a/examples/gno.land/r/demo/boards/z_1_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_1_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_2_filetest.gno b/examples/gno.land/r/demo/boards/z_2_filetest.gno index 1e0b55a3952..547757ef0a6 100644 --- a/examples/gno.land/r/demo/boards/z_2_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_2_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_3_filetest.gno b/examples/gno.land/r/demo/boards/z_3_filetest.gno index f4160e16126..757f9430c67 100644 --- a/examples/gno.land/r/demo/boards/z_3_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_3_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_4_filetest.gno b/examples/gno.land/r/demo/boards/z_4_filetest.gno index 749566ea5bc..68b1955ae35 100644 --- a/examples/gno.land/r/demo/boards/z_4_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_4_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot @@ -44,929 +43,3 @@ func main() { // // > Second reply of the second post // > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/4) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=4&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=4)] - -// Realm: -// switchrealm["gno.land/r/demo/users"] -// switchrealm["gno.land/r/demo/users"] -// switchrealm["gno.land/r/demo/boards"] -// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:101]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "0000000003" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:102" -// } -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "64" -// } -// }, -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:101", -// "ModTime": "109", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:109", -// "RefCount": "1" -// } -// } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:110]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "0000000004" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111" -// } -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "64" -// } -// }, -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:110", -// "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:109", -// "RefCount": "1" -// } -// } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:109]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "0000000004" -// } -// }, -// {}, -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "64" -// } -// }, -// { -// "N": "AgAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "b58581159917d8d7ad0992009d7184fc8ca00fcc", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:101" -// } -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "fb593e86d35aaf607e0d21e6bd4f84519c44585f", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:110" -// } -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:109", -// "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:96", -// "RefCount": "1" -// } -// } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:112]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:112", -// "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", -// "RefCount": "1" -// } -// } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:113]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:113", -// "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", -// "RefCount": "1" -// } -// } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:114]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:114", -// "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", -// "RefCount": "1" -// } -// } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:115]={ -// "Fields": [ -// { -// "N": "AAAAgJSeXbo=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "65536" -// } -// }, -// { -// "N": "AbSNdvQQIhE=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "1024" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "time.Location" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "336074805fc853987abe6f7fe3ad97a6a6f3077a:2" -// }, -// "Index": "188", -// "TV": null -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:115", -// "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", -// "RefCount": "1" -// } -// } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:116]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "65536" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "1024" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "time.Location" -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:116", -// "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", -// "RefCount": "1" -// } -// } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Board" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Board" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:81" -// } -// } -// } -// }, -// { -// "N": "BAAAAAAAAAA=", -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.PostID" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "std.Address" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "Second reply of the second post" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Tree" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "130542396d7549d1d516a3ef4a63bb44ef3da06f", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:112" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Tree" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "80acd8746478317194b8546170335c796a4dfb3f", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:113" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Tree" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "c1a8f769f3b9d52dd38ac4759116edaca287636f", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:114" -// } -// }, -// { -// "N": "AgAAAAAAAAA=", -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.PostID" -// } -// }, -// { -// "N": "AgAAAAAAAAA=", -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.PostID" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.BoardID" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "time.Time" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "8164abed5231309c88497013f7da72a1b5d427b0", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:115" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "time.Time" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "c3a60b602b564d07677a212372f4ac1cae4270fd", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:116" -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", -// "IsEscaped": true, -// "ModTime": "0", -// "RefCount": "2" -// } -// } -// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:108]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "0000000003" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:102" -// } -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "64" -// } -// }, -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:108", -// "ModTime": "117", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:117", -// "RefCount": "1" -// } -// } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:118]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "0000000004" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111" -// } -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "64" -// } -// }, -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:118", -// "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:117", -// "RefCount": "1" -// } -// } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:117]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "0000000004" -// } -// }, -// {}, -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "64" -// } -// }, -// { -// "N": "AgAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "6a86bc7763703c8f2b9d286368921159d6db121c", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:108" -// } -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "52faa8a2dfefd4b6b6249eff2f9c123ad455e81d", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:118" -// } -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:117", -// "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:97", -// "RefCount": "1" -// } -// } -// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:81]={ -// "Fields": [ -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.BoardID" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "/r/demo/boards:test_board" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "test_board" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "std.Address" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Tree" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "5b4b593f1d4b37cb99166247ea28174f91087fdd", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:82" -// } -// }, -// { -// "N": "BAAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "65536" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "time.Time" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "7e9fd9bb5e90a06c7751585cd80f23aedddde25b", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:83" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Tree" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "edb1857302fa916c562cd077cdf2a3626e29ae2b", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:84" -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:81", -// "IsEscaped": true, -// "ModTime": "108", -// "RefCount": "6" -// } -// } -// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:96]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "609e7f519c65f94503427a14f973b4b83989cdc8", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:109" -// } -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:96", -// "ModTime": "108", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:95", -// "RefCount": "1" -// } -// } -// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:97]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "6760340f5b40e05221dc530940683b0b9a422503", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:117" -// } -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:97", -// "ModTime": "108", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:95", -// "RefCount": "1" -// } -// } -// switchrealm["gno.land/r/demo/boards"] -// switchrealm["gno.land/r/demo/users"] -// switchrealm["gno.land/r/demo/users"] -// switchrealm["gno.land/r/demo/users"] -// switchrealm["gno.land/r/demo/users"] -// switchrealm["gno.land/r/demo/users"] -// switchrealm["gno.land/r/demo/users"] -// switchrealm["gno.land/r/demo/users"] -// switchrealm["gno.land/r/demo/users"] -// switchrealm["gno.land/r/demo/users"] -// switchrealm["gno.land/r/demo/boards"] -// switchrealm["gno.land/r/boards_test"] diff --git a/examples/gno.land/r/demo/boards/z_5_filetest.gno b/examples/gno.land/r/demo/boards/z_5_filetest.gno index 421d9440785..d9ebf69e0d1 100644 --- a/examples/gno.land/r/demo/boards/z_5_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_5_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_6_filetest.gno b/examples/gno.land/r/demo/boards/z_6_filetest.gno index 2a392808d30..66478488947 100644 --- a/examples/gno.land/r/demo/boards/z_6_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_6_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_7_filetest.gno b/examples/gno.land/r/demo/boards/z_7_filetest.gno index 1294f72c1be..bb46372ec7f 100644 --- a/examples/gno.land/r/demo/boards/z_7_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_7_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 2000000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_8_filetest.gno b/examples/gno.land/r/demo/boards/z_8_filetest.gno index 5ba0b34ad30..c4e6c33643b 100644 --- a/examples/gno.land/r/demo/boards/z_8_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_8_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 2000000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_9_a_filetest.gno b/examples/gno.land/r/demo/boards/z_9_a_filetest.gno index 9ba9786c519..351235fba31 100644 --- a/examples/gno.land/r/demo/boards/z_9_a_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_9_a_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_9_b_filetest.gno b/examples/gno.land/r/demo/boards/z_9_b_filetest.gno index 4d53f60f10f..77154565641 100644 --- a/examples/gno.land/r/demo/boards/z_9_b_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_9_b_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/boards/z_9_filetest.gno b/examples/gno.land/r/demo/boards/z_9_filetest.gno index 66f8950732b..7461a3b67c3 100644 --- a/examples/gno.land/r/demo/boards/z_9_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_9_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/boards_test -package boards_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/groups/z_0_a_filetest.gno b/examples/gno.land/r/demo/groups/z_0_a_filetest.gno index 41d515b9d0a..95c5c2d0e6b 100644 --- a/examples/gno.land/r/demo/groups/z_0_a_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_0_a_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/groups_test -package groups_test +package main import ( "gno.land/r/demo/groups" diff --git a/examples/gno.land/r/demo/groups/z_0_b_filetest.gno b/examples/gno.land/r/demo/groups/z_0_b_filetest.gno index ca74c599130..047a5915bb1 100644 --- a/examples/gno.land/r/demo/groups/z_0_b_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_0_b_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/groups_test -package groups_test +package main import ( "gno.land/r/demo/groups" diff --git a/examples/gno.land/r/demo/groups/z_0_c_filetest.gno b/examples/gno.land/r/demo/groups/z_0_c_filetest.gno index 10a4771c529..92fc4123749 100644 --- a/examples/gno.land/r/demo/groups/z_0_c_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_0_c_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/groups_test -package groups_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/groups/z_1_a_filetest.gno b/examples/gno.land/r/demo/groups/z_1_a_filetest.gno index 460fb79afbb..8a957f1de16 100644 --- a/examples/gno.land/r/demo/groups/z_1_a_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_1_a_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/groups_test -package groups_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/groups/z_1_b_filetest.gno b/examples/gno.land/r/demo/groups/z_1_b_filetest.gno index 3271e74e287..99ed8e39b15 100644 --- a/examples/gno.land/r/demo/groups/z_1_b_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_1_b_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/groups_test -package groups_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/groups/z_1_c_filetest.gno b/examples/gno.land/r/demo/groups/z_1_c_filetest.gno index 93cd3b39d5f..e2988c7d9d9 100644 --- a/examples/gno.land/r/demo/groups/z_1_c_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_1_c_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/groups_test -package groups_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/groups/z_2_a_filetest.gno b/examples/gno.land/r/demo/groups/z_2_a_filetest.gno index f4a96905fc8..7136ae04330 100644 --- a/examples/gno.land/r/demo/groups/z_2_a_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_a_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/groups_test -package groups_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/groups/z_2_b_filetest.gno b/examples/gno.land/r/demo/groups/z_2_b_filetest.gno index bf10939c66f..5614c8039be 100644 --- a/examples/gno.land/r/demo/groups/z_2_b_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_b_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/groups_test -package groups_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/groups/z_2_d_filetest.gno b/examples/gno.land/r/demo/groups/z_2_d_filetest.gno index 95f22f4ec4b..c45e6c3b2af 100644 --- a/examples/gno.land/r/demo/groups/z_2_d_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_d_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/groups_test -package groups_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/groups/z_2_e_filetest.gno b/examples/gno.land/r/demo/groups/z_2_e_filetest.gno index 270ddb8aa34..6ebc4d8bbeb 100644 --- a/examples/gno.land/r/demo/groups/z_2_e_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_e_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/groups_test -package groups_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/groups/z_2_f_filetest.gno b/examples/gno.land/r/demo/groups/z_2_f_filetest.gno index 4ec0ec211f2..9c51e31edf5 100644 --- a/examples/gno.land/r/demo/groups/z_2_f_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_f_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/groups_test -package groups_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/groups/z_2_g_filetest.gno b/examples/gno.land/r/demo/groups/z_2_g_filetest.gno index 8f98cfeb2dd..cd5ebf62c88 100644 --- a/examples/gno.land/r/demo/groups/z_2_g_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_g_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/groups_test -package groups_test +package main // SEND: 200000000ugnot diff --git a/examples/gno.land/r/demo/tests/subtests/subtests.gno b/examples/gno.land/r/demo/tests/subtests/subtests.gno index e8796a73081..5cc4d1bea16 100644 --- a/examples/gno.land/r/demo/tests/subtests/subtests.gno +++ b/examples/gno.land/r/demo/tests/subtests/subtests.gno @@ -15,3 +15,11 @@ func GetPrevRealm() std.Realm { func Exec(fn func()) { fn() } + +func AssertOriginCall() { + std.AssertOriginCall() +} + +func IsOriginCall() bool { + return std.IsOriginCall() +} diff --git a/examples/gno.land/r/demo/tests/tests.gno b/examples/gno.land/r/demo/tests/tests.gno index fb49b2273ae..67ae4bc9176 100644 --- a/examples/gno.land/r/demo/tests/tests.gno +++ b/examples/gno.land/r/demo/tests/tests.gno @@ -28,6 +28,14 @@ func IsOriginCall() bool { return std.IsOriginCall() } +func RSubtestsAssertOriginCall() { + rsubtests.AssertOriginCall() +} + +func RSubtestsIsOriginCall() bool { + return rsubtests.IsOriginCall() +} + //---------------------------------------- // Test structure to ensure cross-realm modification is prevented. diff --git a/examples/gno.land/r/demo/tests/tests_test.gno b/examples/gno.land/r/demo/tests/tests_test.gno index 3dcbeecf18c..3c92613d3d5 100644 --- a/examples/gno.land/r/demo/tests/tests_test.gno +++ b/examples/gno.land/r/demo/tests/tests_test.gno @@ -22,14 +22,10 @@ func TestAssertOriginCall(t *testing.T) { t.Errorf("expected panic with '%v', got '%v'", expectedReason, r) } }() - func() { - // if called inside a function literal, this is no longer an origin call - // because there's one additional frame (the function literal). - if IsOriginCall() { - t.Errorf("expected IsOriginCall=false but got true") - } - AssertOriginCall() - }() + if RSubtestsIsOriginCall() { + t.Errorf("expected RSubtestsIsOriginCall=false but got true") + } + RSubtestsAssertOriginCall() } func TestPrevRealm(t *testing.T) { diff --git a/examples/gno.land/r/demo/tests/z0_filetest.gno b/examples/gno.land/r/demo/tests/z0_filetest.gno index c4beb8e2005..2f5ab9fa05e 100644 --- a/examples/gno.land/r/demo/tests/z0_filetest.gno +++ b/examples/gno.land/r/demo/tests/z0_filetest.gno @@ -9,16 +9,12 @@ func main() { tests.AssertOriginCall() println("AssertOriginCall doesn't panic when called directly") - func() { - // if called inside a function literal, this is no longer an origin call - // because there's one additional frame (the function literal). - println("IsOriginCall:", tests.IsOriginCall()) - defer func() { - r := recover() - println("AssertOriginCall panics if when called inside a function literal:", r) - }() - tests.AssertOriginCall() + println("IsOriginCall:", tests.RSubtestsIsOriginCall()) + defer func() { + r := recover() + println("AssertOriginCall panics if when called inside a function literal:", r) }() + tests.RSubtestsAssertOriginCall() } // Output: diff --git a/examples/gno.land/r/demo/users/z_10_filetest.gno b/examples/gno.land/r/demo/users/z_10_filetest.gno index 0065d907e47..607ced8e2db 100644 --- a/examples/gno.land/r/demo/users/z_10_filetest.gno +++ b/examples/gno.land/r/demo/users/z_10_filetest.gno @@ -1,5 +1,4 @@ -// PKGPATH: gno.land/r/users_test -package users_test +package main import ( "std" diff --git a/gno.land/cmd/gnokey/main.go b/gno.land/cmd/gnokey/main.go index 1f3414dc893..28cb665eac1 100644 --- a/gno.land/cmd/gnokey/main.go +++ b/gno.land/cmd/gnokey/main.go @@ -5,11 +5,12 @@ import ( "fmt" "os" + "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" ) func main() { - cmd := client.NewRootCmd() + cmd := client.NewRootCmd(commands.NewDefaultIO()) if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil { _, _ = fmt.Fprintf(os.Stderr, "%+v\n", err) diff --git a/gno.land/cmd/gnoland/integration.go b/gno.land/cmd/gnoland/integration.go new file mode 100644 index 00000000000..06ab7d0f9a3 --- /dev/null +++ b/gno.land/cmd/gnoland/integration.go @@ -0,0 +1 @@ +package main diff --git a/gno.land/cmd/gnoland/integration_test.go b/gno.land/cmd/gnoland/integration_test.go new file mode 100644 index 00000000000..b629dff2b92 --- /dev/null +++ b/gno.land/cmd/gnoland/integration_test.go @@ -0,0 +1,12 @@ +package main + +import ( + "testing" + + integration "github.com/gnolang/gno/gno.land/pkg/integration" + "github.com/rogpeppe/go-internal/testscript" +) + +func TestTestdata(t *testing.T) { + testscript.Run(t, integration.SetupGnolandTestScript(t, "testdata")) +} diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index b2134d86ea9..c3d605736f5 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -194,10 +194,10 @@ func makeGenesisDoc( gen.ConsensusParams = abci.ConsensusParams{ Block: &abci.BlockParams{ // TODO: update limits. - MaxTxBytes: 1000000, // 1MB, - MaxDataBytes: 2000000, // 2MB, - MaxGas: 10000000, // 10M gas - TimeIotaMS: 100, // 100ms + MaxTxBytes: 10_000_000, // 10MB, + MaxDataBytes: 20_000_000, // 20MB, + MaxGas: 100_000_000, // 100M gas + TimeIotaMS: 100, // 100ms }, } gen.Validators = []bft.GenesisValidator{ diff --git a/gno.land/cmd/gnoland/testdata/addpkg.txtar b/gno.land/cmd/gnoland/testdata/addpkg.txtar new file mode 100644 index 00000000000..5e871b058ac --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/addpkg.txtar @@ -0,0 +1,26 @@ +# test for add package + +## start a new node +gnoland start + +## add bar.gno package located in $WORK directory as gno.land/r/foobar/bar +gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 + +## execute Render +gnokey maketx call -pkgpath gno.land/r/foobar/bar -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 + +## compare render +cmp stdout stdout.golden + +-- bar.gno -- +package bar + +func Render(path string) string { + return "hello from foo" +} + +-- stdout.golden -- +("hello from foo" string) +OK! +GAS WANTED: 2000000 +GAS USED: 69163 \ No newline at end of file diff --git a/gno.land/cmd/gnoland/testdata/basic_commands.txtar b/gno.land/cmd/gnoland/testdata/basic_commands.txtar new file mode 100644 index 00000000000..ee97d417d80 --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/basic_commands.txtar @@ -0,0 +1,22 @@ +# test basic gnoland integrations commands + +## no arguments should fail +! gnoland + +## should be able to start +gnoland start + +## should not be able to start a node twice +! gnoland start + +## test1 account should be available on default +gnokey query auth/accounts/${USER_ADDR_test1} + +## invalid gnokey command should raise an error +! gnokey query foo/bar + +## should be able to stop default +gnoland stop + +## should not be able to stop a node twice +! gnoland stop diff --git a/gno.land/cmd/gnoland/testdata/bug-gnochess-97.txtar b/gno.land/cmd/gnoland/testdata/bug-gnochess-97.txtar new file mode 100644 index 00000000000..a467859f5ad --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/bug-gnochess-97.txtar @@ -0,0 +1,42 @@ +# test for https://github.com/gnolang/gnochess/issues/97 + +## start a new node +gnoland start + +gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/demo/bug97 -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 + +gnokey maketx call -pkgpath 'gno.land/r/demo/bug97' -func 'RealmCall1' -gas-fee 1000000ugnot -gas-wanted 2000000 -send '' -broadcast -chainid='tendermint_test' test1 +stdout 'OK!' + +gnokey maketx call -pkgpath 'gno.land/r/demo/bug97' -func 'RealmCall2' -gas-fee 1000000ugnot -gas-wanted 2000000 -send '' -broadcast -chainid='tendermint_test' test1 +stdout 'OK!' + +gnokey maketx call -pkgpath 'gno.land/r/demo/bug97' -func 'RealmCall1' -gas-fee 1000000ugnot -gas-wanted 2000000 -send '' -broadcast -chainid='tendermint_test' test1 +stdout 'OK!' + +-- bug97.gno -- +package bug97 + +var x = [3]int{1, 2, 3} + +func newX() [3]int { return x} + +type S struct { + Arr [3]int +} + +func NewS() S { + return S{Arr: x} +} + +var s S + +func RealmCall1() { + s = NewS() +} + +func RealmCall2() { + arr2 := s.Arr + arr2[0] = 8 + s = S{Arr: arr2} +} diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index b10f251b115..dad87eaf44d 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -2,6 +2,8 @@ package gnoland import ( "fmt" + "os" + "os/exec" "path/filepath" "strings" @@ -20,12 +22,44 @@ import ( "github.com/gnolang/gno/tm2/pkg/store/iavl" ) +type CustomAppConfig struct { + DB dbm.DB + GnoRootDir string + SkipFailingGenesisTxs bool + Logger log.Logger + MaxCycles int64 +} + +func (c *CustomAppConfig) ApplyDefault() { + if c.Logger == nil { + c.Logger = log.NewNopLogger() + } + + if c.DB == nil { + c.DB = dbm.NewMemDB() + } + + if c.GnoRootDir == "" { + c.GnoRootDir = guessGnoRootDir() + } +} + +func (c *CustomAppConfig) validate() error { + if c.Logger == nil { + return fmt.Errorf("no logger provided") + } + + if c.DB == nil { + return fmt.Errorf("no db provided") + } + + return nil +} + // NewApp creates the GnoLand application. -func NewApp(rootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCycles int64) (abci.Application, error) { - // Get main DB. - db, err := dbm.NewDB("gnolang", dbm.GoLevelDBBackend, filepath.Join(rootDir, "data")) - if err != nil { - return nil, fmt.Errorf("error initializing database %q using path %q: %w", dbm.GoLevelDBBackend, rootDir, err) +func NewCustomApp(cfg CustomAppConfig) (abci.Application, error) { + if err := cfg.validate(); err != nil { + return nil, err } // Capabilities keys. @@ -33,21 +67,21 @@ func NewApp(rootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCy baseKey := store.NewStoreKey("base") // Create BaseApp. - baseApp := sdk.NewBaseApp("gnoland", logger, db, baseKey, mainKey) + baseApp := sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey) baseApp.SetAppVersion("dev") // Set mounts for BaseApp's MultiStore. - baseApp.MountStoreWithDB(mainKey, iavl.StoreConstructor, db) - baseApp.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, db) + baseApp.MountStoreWithDB(mainKey, iavl.StoreConstructor, cfg.DB) + baseApp.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, cfg.DB) // Construct keepers. acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) bankKpr := bank.NewBankKeeper(acctKpr) - stdlibsDir := filepath.Join("..", "gnovm", "stdlibs") - vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, maxCycles) + stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs") + vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles) // Set InitChainer - baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, skipFailingGenesisTxs)) + baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, cfg.SkipFailingGenesisTxs)) // Set AnteHandler authOptions := auth.AnteOptions{ @@ -88,6 +122,24 @@ func NewApp(rootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCy return baseApp, nil } +// NewApp creates the GnoLand application. +func NewApp(dataRootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCycles int64) (abci.Application, error) { + var err error + + var cfg CustomAppConfig + cfg.ApplyDefault() + + // Get main DB. + cfg.DB, err = dbm.NewDB("gnolang", dbm.GoLevelDBBackend, filepath.Join(dataRootDir, "data")) + if err != nil { + return nil, fmt.Errorf("error initializing database %q using path %q: %w", dbm.GoLevelDBBackend, dataRootDir, err) + } + + cfg.Logger = logger + + return NewCustomApp(cfg) +} + // InitChainer returns a function that can initialize the chain with genesis. func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank.BankKeeperI, skipFailingGenesisTxs bool) func(sdk.Context, abci.RequestInitChain) abci.ResponseInitChain { return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { @@ -107,14 +159,15 @@ func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank for i, tx := range genState.Txs { res := baseApp.Deliver(tx) if res.IsErr() { - fmt.Println("ERROR LOG:", res.Log) - fmt.Println("#", i, string(amino.MustMarshalJSON(tx))) + ctx.Logger().Error("LOG", res.Log) + ctx.Logger().Error("#", i, string(amino.MustMarshalJSON(tx))) + // NOTE: comment out to ignore. if !skipFailingGenesisTxs { panic(res.Error) } } else { - fmt.Println("SUCCESS:", string(amino.MustMarshalJSON(tx))) + ctx.Logger().Info("SUCCESS:", string(amino.MustMarshalJSON(tx))) } } // Done! @@ -146,3 +199,25 @@ func EndBlocker(vmk vm.VMKeeperI) func(ctx sdk.Context, req abci.RequestEndBlock return abci.ResponseEndBlock{} } } + +func guessGnoRootDir() string { + var rootdir string + + // first try to get the root directory from the GNOROOT environment variable. + if rootdir = os.Getenv("GNOROOT"); rootdir != "" { + return filepath.Clean(rootdir) + } + + if gobin, err := exec.LookPath("go"); err == nil { + // if GNOROOT is not set, try to guess the root directory using the `go list` command. + cmd := exec.Command(gobin, "list", "-m", "-mod=mod", "-f", "{{.Dir}}", "github.com/gnolang/gno") + out, err := cmd.CombinedOutput() + if err != nil { + panic(fmt.Errorf("invalid gno directory %q: %w", rootdir, err)) + } + + return strings.TrimSpace(string(out)) + } + + panic("no go binary available, unable to determine gno root-dir path") +} diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go new file mode 100644 index 00000000000..d5fd4ae0853 --- /dev/null +++ b/gno.land/pkg/integration/testing_integration.go @@ -0,0 +1,534 @@ +// ------------------------------------------------------------------------------------------------ +// WARNING: TEMPORARY CODE +// +// This file is a rapid prototype to meet immediate project needs and is not intended for long-term +// use in its current form. It requires review, possible refactoring, and thorough testing. +// Use at your own risk. The author(s) accept no liability for any issues arising from its use. +// ------------------------------------------------------------------------------------------------ + +package integration + +import ( + "context" + "flag" + "fmt" + "hash/crc32" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/gnomod" + "github.com/gnolang/gno/tm2/pkg/amino" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + "github.com/gnolang/gno/tm2/pkg/bft/config" + "github.com/gnolang/gno/tm2/pkg/bft/node" + "github.com/gnolang/gno/tm2/pkg/bft/privval" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" + "github.com/gnolang/gno/tm2/pkg/db" + "github.com/gnolang/gno/tm2/pkg/log" + osm "github.com/gnolang/gno/tm2/pkg/os" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/jaekwon/testify/require" + "github.com/rogpeppe/go-internal/testscript" +) + +// XXX: should be centralize somewhere +const ( + test1Addr = "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5" + test1Seed = "source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast" +) + +func TestTestdata(t *testing.T) { + testscript.Run(t, SetupGnolandTestScript(t, "testdata")) +} + +type IntegrationConfig struct { + skipFailingGenesisTxs bool + skipStart bool + genesisBalancesFile string + genesisTxsFile string + chainID string + genesisRemote string + rootDir string + genesisMaxVMCycles int64 + config string +} + +func (c *IntegrationConfig) RegisterFlags(fs *flag.FlagSet) { + fs.BoolVar( + &c.skipFailingGenesisTxs, + "skip-failing-genesis-txs", + false, + "don't panic when replaying invalid genesis txs", + ) + + fs.BoolVar( + &c.skipStart, + "skip-start", + false, + "quit after initialization, don't start the node", + ) + + fs.StringVar( + &c.genesisBalancesFile, + "genesis-balances-file", + "./genesis/genesis_balances.txt", + "initial distribution file", + ) + + fs.StringVar( + &c.genesisTxsFile, + "genesis-txs-file", + "./genesis/genesis_txs.txt", + "initial txs to replay", + ) + + fs.StringVar( + &c.chainID, + "chainid", + "dev", + "the ID of the chain", + ) + + fs.StringVar( + &c.rootDir, + "root-dir", + "testdir", + "directory for config and data", + ) + + fs.StringVar( + &c.genesisRemote, + "genesis-remote", + "localhost:26657", + "replacement for '%%REMOTE%%' in genesis", + ) + + fs.Int64Var( + &c.genesisMaxVMCycles, + "genesis-max-vm-cycles", + 10_000_000, + "set maximum allowed vm cycles per operation. Zero means no limit.", + ) + + fs.StringVar( + &c.config, + "config", + "", + "config file (optional)", + ) +} + +func SetupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { + t.Helper() + + cmd := exec.Command("go", "list", "-m", "-mod=mod", "-f", "{{.Dir}}", "github.com/gnolang/gno") + out, err := cmd.CombinedOutput() + require.NoError(t, err) + + gnoRootDir := strings.TrimSpace(string(out)) + gnoHomeDir := filepath.Join(t.TempDir(), "gno") + gnoDataDir := filepath.Join(t.TempDir(), "data") + + var muNodes sync.Mutex + nodes := map[string]*node.Node{} + t.Cleanup(func() { + for id, n := range nodes { + if err := n.Stop(); err != nil { + panic(fmt.Errorf("node %q was unable to stop: %w", id, err)) + } + } + }) + + return testscript.Params{ + Dir: txtarDir, + Setup: func(env *testscript.Env) error { + kb, err := keys.NewKeyBaseFromDir(gnoHomeDir) + if err != nil { + return err + } + + kb.CreateAccount("test1", test1Seed, "", "", 0, 0) + env.Setenv("USER_SEED_test1", test1Seed) + env.Setenv("USER_ADDR_test1", test1Addr) + + env.Setenv("GNOROOT", gnoRootDir) + env.Setenv("GNOHOME", gnoHomeDir) + + return nil + }, + Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){ + "gnoland": func(ts *testscript.TestScript, neg bool, args []string) { + muNodes.Lock() + defer muNodes.Unlock() + + if len(args) == 0 { + tsValidateError(ts, "gnoland", neg, fmt.Errorf("use gnoland [start|stop] command")) + return + } + + sid := getSessionID(ts) + + var cmd string + cmd, args = args[0], args[1:] + + var err error + switch cmd { + case "start": + if _, ok := nodes[sid]; ok { + err = fmt.Errorf("node %q already started", sid) + break + } + + dataDir := filepath.Join(gnoDataDir, sid) + var node *node.Node + if node, err = execTestingGnoland(t, dataDir, gnoRootDir, args); err == nil { + // XXX need mutex ? + nodes[sid] = node + + // get listen addr environement + // should have been updated with the right port on start + laddr := node.Config().RPC.ListenAddress + + // add default environement + ts.Setenv("RPC_ADDR", laddr) + ts.Setenv("GNODATA", gnoDataDir) + + // XXX: Use something similar to `require.Eventually` to check for node + // availability. For now, if this sleep duration is too short, the + // subsequent command might fail with an [internal error]. + time.Sleep(time.Millisecond * 300) + } + case "stop": + n, ok := nodes[sid] + if !ok { + err = fmt.Errorf("node %q not started cannot be stop", sid) + break + } + + if err = n.Stop(); err != nil { + delete(nodes, sid) + + // unset env dirs + ts.Setenv("RPC_ADDR", "") + ts.Setenv("GNODATA", "") + } + } + + tsValidateError(ts, "gnoland "+cmd, neg, err) + }, + "gnokey": func(ts *testscript.TestScript, neg bool, args []string) { + muNodes.Lock() + defer muNodes.Unlock() + + sid := getSessionID(ts) + + // Setup io command + io := commands.NewTestIO() + io.SetOut(commands.WriteNopCloser(ts.Stdout())) + io.SetErr(commands.WriteNopCloser(ts.Stderr())) + cmd := client.NewRootCmd(io) + + io.SetIn(strings.NewReader("\n")) // inject empty password to stdin + defaultArgs := []string{ + "-home", gnoHomeDir, + "-insecure-password-stdin=true", // there no use to not have this param by default + } + + if n, ok := nodes[sid]; ok { + if raddr := n.Config().RPC.ListenAddress; raddr != "" { + defaultArgs = append(defaultArgs, "-remote", raddr) + } + } + + // inject default argument, if duplicate + // arguments, it should be override by the ones + // user provided + args = append(defaultArgs, args...) + + err := cmd.ParseAndRun(context.Background(), args) + tsValidateError(ts, "gnokey", neg, err) + }, + }, + } +} + +func execTestingGnoland(t *testing.T, gnoDataDir, gnoRootDir string, args []string) (*node.Node, error) { + t.Helper() + + // Setup logger + logger := log.NewNopLogger() + + // Setup start config + icfg := &IntegrationConfig{} + { + fs := flag.NewFlagSet("start", flag.ExitOnError) + icfg.RegisterFlags(fs) + + // Override default value for flags + fs.VisitAll(func(f *flag.Flag) { + switch f.Name { + case "root-dir": + f.DefValue = gnoDataDir + case "chainid": + f.DefValue = "tendermint_test" + case "genesis-balances-file": + f.DefValue = filepath.Join(gnoRootDir, "gno.land", "genesis", "genesis_balances.txt") + case "genesis-txs-file": + f.DefValue = filepath.Join(gnoRootDir, "gno.land", "genesis", "genesis_txs.txt") + default: + return + } + + f.Value.Set(f.DefValue) + }) + + if err := fs.Parse(args); err != nil { + return nil, fmt.Errorf("unable to parse flags: %w", err) + } + } + + // Setup testing config + cfg := config.TestConfig().SetRootDir(gnoDataDir) + { + cfg.EnsureDirs() + cfg.Consensus.CreateEmptyBlocks = true + cfg.Consensus.CreateEmptyBlocksInterval = time.Duration(0) + cfg.RPC.ListenAddress = "tcp://127.0.0.1:0" + cfg.P2P.ListenAddress = "tcp://127.0.0.1:0" + } + + // Prepare genesis + if err := setupTestingGenesis(gnoDataDir, cfg, icfg, gnoRootDir); err != nil { + return nil, err + } + + // Create application and node + return createAppAndNode(cfg, logger, gnoRootDir, icfg) +} + +func getSessionID(ts *testscript.TestScript) string { + works := ts.Getenv("WORK") + sum := crc32.ChecksumIEEE([]byte(works)) + return strconv.FormatUint(uint64(sum), 16) +} + +func setupTestingGenesis(gnoDataDir string, cfg *config.Config, icfg *IntegrationConfig, gnoRootDir string) error { + newPrivValKey := cfg.PrivValidatorKeyFile() + newPrivValState := cfg.PrivValidatorStateFile() + priv := privval.LoadOrGenFilePV(newPrivValKey, newPrivValState) + + genesisFilePath := filepath.Join(gnoDataDir, cfg.Genesis) + osm.EnsureDir(filepath.Dir(genesisFilePath), 0o700) + if !osm.FileExists(genesisFilePath) { + genesisTxs := loadGenesisTxs(icfg.genesisTxsFile, icfg.chainID, icfg.genesisRemote) + pvPub := priv.GetPubKey() + + gen := &bft.GenesisDoc{ + GenesisTime: time.Now(), + ChainID: icfg.chainID, + ConsensusParams: abci.ConsensusParams{ + Block: &abci.BlockParams{ + // TODO: update limits. + MaxTxBytes: 1000000, // 1MB, + MaxDataBytes: 2000000, // 2MB, + MaxGas: 10000000, // 10M gas + TimeIotaMS: 100, // 100ms + }, + }, + Validators: []bft.GenesisValidator{ + { + Address: pvPub.Address(), + PubKey: pvPub, + Power: 10, + Name: "testvalidator", + }, + }, + } + + // Load distribution. + balances := loadGenesisBalances(icfg.genesisBalancesFile) + + // Load initial packages from examples. + // XXX: we should be able to config this + test1 := crypto.MustAddressFromString(test1Addr) + txs := []std.Tx{} + + // List initial packages to load from examples. + // println(filepath.Join(gnoRootDir, "examples")) + + pkgs, err := gnomod.ListPkgs(filepath.Join(gnoRootDir, "examples")) + if err != nil { + return fmt.Errorf("listing gno packages: %w", err) + } + + // Sort packages by dependencies. + sortedPkgs, err := pkgs.Sort() + if err != nil { + return fmt.Errorf("sorting packages: %w", err) + } + + // Filter out draft packages. + nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() + + for _, pkg := range nonDraftPkgs { + // open files in directory as MemPackage. + memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) + + var tx std.Tx + tx.Msgs = []std.Msg{ + vmm.MsgAddPackage{ + Creator: test1, + Package: memPkg, + Deposit: nil, + }, + } + + // XXX: add fee flag ? + // or maybe reduce fee to the minimum ? + tx.Fee = std.NewFee(50000, std.MustParseCoin("1000000ugnot")) + tx.Signatures = make([]std.Signature, len(tx.GetSigners())) + txs = append(txs, tx) + } + + // load genesis txs from file. + txs = append(txs, genesisTxs...) + + // construct genesis AppState. + gen.AppState = gnoland.GnoGenesisState{ + Balances: balances, + Txs: txs, + } + + writeGenesisFile(gen, genesisFilePath) + } + + return nil +} + +func createAppAndNode(cfg *config.Config, logger log.Logger, gnoRootDir string, icfg *IntegrationConfig) (*node.Node, error) { + gnoApp, err := gnoland.NewCustomApp(gnoland.CustomAppConfig{ + Logger: logger, + GnoRootDir: gnoRootDir, + SkipFailingGenesisTxs: icfg.skipFailingGenesisTxs, + MaxCycles: icfg.genesisMaxVMCycles, + DB: db.NewMemDB(), + }) + if err != nil { + return nil, fmt.Errorf("error in creating new app: %w", err) + } + + cfg.LocalApp = gnoApp + node, err := node.DefaultNewNode(cfg, logger) + if err != nil { + return nil, fmt.Errorf("error in creating node: %w", err) + } + + return node, node.Start() +} + +func tsValidateError(ts *testscript.TestScript, cmd string, neg bool, err error) { + if err != nil { + ts.Logf("%s error: %v\n", cmd, err) + if !neg { + ts.Fatalf("unexpected %s command failure", cmd) + } + } else { + if neg { + ts.Fatalf("unexpected %s command success", cmd) + } + } +} + +func loadGenesisTxs( + path string, + chainID string, + genesisRemote string, +) []std.Tx { + txs := []std.Tx{} + txsBz := osm.MustReadFile(path) + txsLines := strings.Split(string(txsBz), "\n") + for _, txLine := range txsLines { + if txLine == "" { + continue // skip empty line + } + + // patch the TX + txLine = strings.ReplaceAll(txLine, "%%CHAINID%%", chainID) + txLine = strings.ReplaceAll(txLine, "%%REMOTE%%", genesisRemote) + + var tx std.Tx + amino.MustUnmarshalJSON([]byte(txLine), &tx) + txs = append(txs, tx) + } + + return txs +} + +func loadGenesisBalances(path string) []string { + // each balance is in the form: g1xxxxxxxxxxxxxxxx=100000ugnot + balances := []string{} + content := osm.MustReadFile(path) + lines := strings.Split(string(content), "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + + // remove comments. + line = strings.Split(line, "#")[0] + line = strings.TrimSpace(line) + + // skip empty lines. + if line == "" { + continue + } + + parts := strings.Split(line, "=") + if len(parts) != 2 { + panic("invalid genesis_balance line: " + line) + } + + balances = append(balances, line) + } + return balances +} + +func writeGenesisFile(gen *bft.GenesisDoc, filePath string) { + err := gen.SaveAs(filePath) + if err != nil { + panic(err) + } +} + +func guessGnoRootDir() string { + var rootdir string + + // first try to get the root directory from the GNOROOT environment variable. + if rootdir = os.Getenv("GNOROOT"); rootdir != "" { + return filepath.Clean(rootdir) + } + + if gobin, err := exec.LookPath("go"); err == nil { + // if GNOROOT is not set, try to guess the root directory using the `go list` command. + cmd := exec.Command(gobin, "list", "-m", "-mod=mod", "-f", "{{.Dir}}", "github.com/gnolang/gno") + out, err := cmd.CombinedOutput() + if err != nil { + panic(fmt.Errorf("invalid gno directory %q: %w", rootdir, err)) + } + + return strings.TrimSpace(string(out)) + } + + panic("no go binary available, unable to determine gno root-dir path") +} diff --git a/gno.land/pkg/sdk/vm/builtins.go b/gno.land/pkg/sdk/vm/builtins.go index 270a4f7c37e..ef2ac93f617 100644 --- a/gno.land/pkg/sdk/vm/builtins.go +++ b/gno.land/pkg/sdk/vm/builtins.go @@ -40,47 +40,10 @@ func (vm *VMKeeper) initBuiltinPackagesAndTypes(store gno.Store) { return m2.RunMemPackage(memPkg, true) } store.SetPackageGetter(getPackage) - store.SetPackageInjector(vm.packageInjector) + store.SetNativeStore(stdlibs.NativeStore) stdlibs.InjectNativeMappings(store) } -func (vm *VMKeeper) packageInjector(store gno.Store, pn *gno.PackageNode) { - // Also inject stdlibs native functions. - stdlibs.InjectPackage(store, pn) - // vm (this package) specific injections: - switch pn.PkgPath { - case "std": - /* XXX deleteme - // Also see stdlibs/InjectPackage. - pn.DefineNative("AssertOriginCall", - gno.Flds( // params - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - isOrigin := len(m.Frames) == 2 - if !isOrigin { - panic("invalid non-origin call") - } - }, - ) - pn.DefineNative("IsOriginCall", - gno.Flds( // params - ), - gno.Flds( // results - "isOrigin", "bool", - ), - func(m *gno.Machine) { - isOrigin := len(m.Frames) == 2 - res0 := gno.TypedValue{T: gno.BoolType} - res0.SetBool(isOrigin) - m.PushValue(res0) - }, - ) - */ - } -} - // ---------------------------------------- // SDKBanker diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 28531f0a773..6f695e98558 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -188,7 +188,8 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error { }) defer m2.Release() m2.RunMemPackage(memPkg, true) - fmt.Println("CPUCYCLES addpkg", m2.Cycles) + + ctx.Logger().Info("CPUCYCLES", "addpkg", m2.Cycles) return nil } @@ -270,7 +271,7 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) { m.Release() }() rtvs := m.Eval(xn) - fmt.Println("CPUCYCLES call", m.Cycles) + ctx.Logger().Info("CPUCYCLES call: ", m.Cycles) for i, rtv := range rtvs { res = res + rtv.String() if i < len(rtvs)-1 { diff --git a/gnovm/Makefile b/gnovm/Makefile index 5fcacf94f62..18655bb8c6b 100644 --- a/gnovm/Makefile +++ b/gnovm/Makefile @@ -58,6 +58,11 @@ _test.gnolang.stdlibs.sync:; go test $(GOTEST_FLAGS) tests/*.go -test.short -run ######################################## # Code gen +# TODO: move _dev.stringer to go:generate instructions, simplify generate +# to just go generate. +.PHONY: generate +generate: _dev.stringer _dev.generate + stringer_cmd=$(rundep) golang.org/x/tools/cmd/stringer .PHONY: _dev.stringer _dev.stringer: @@ -68,5 +73,9 @@ _dev.stringer: $(stringer_cmd) -type=VPType ./pkg/gnolang $(stringer_cmd) -type=Word ./pkg/gnolang +.PHONY: _dev.generate +_dev.generate: + go generate -x ./... + # genproto: # see top-level Makefile. diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar b/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar index f376e61d9a4..6ac909116af 100644 --- a/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar @@ -55,6 +55,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/x", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar b/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar index 3922c34fec8..38794a3e645 100644 --- a/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar @@ -7,7 +7,7 @@ stderr '=== RUN file/x_filetest.gno' stderr 'panic: fail on x_filetest.gno: diff:' stderr '--- Expected' stderr '\+\+\+ Actual' -stderr '@@ -1 \+1,64 @@' +stderr '@@ -1 \+1,66 @@' stderr '-xxx' stderr '\+switchrealm\["gno.land/r/x"\]' diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar b/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar index 918fb0b88c9..104d4bb1064 100644 --- a/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar @@ -70,6 +70,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/x", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/docs/go-gno-compatibility.md b/gnovm/docs/go-gno-compatibility.md index d5448ab2779..467dcbbfb78 100644 --- a/gnovm/docs/go-gno-compatibility.md +++ b/gnovm/docs/go-gno-compatibility.md @@ -259,9 +259,9 @@ Additional native types: | log/slog/internal | TBD | | log/syslog | TBD | | maps | TBD | -| math | partial | +| math | complete | | math/big | TBD | -| math/bits | TBD | +| math/bits | complete | | math/cmplx | TBD | | math/rand | TBD | | mime | TBD | diff --git a/gnovm/pkg/gnolang/gno_test.go b/gnovm/pkg/gnolang/gno_test.go index 5ed5971c836..1a965a4d37e 100644 --- a/gnovm/pkg/gnolang/gno_test.go +++ b/gnovm/pkg/gnolang/gno_test.go @@ -14,7 +14,9 @@ import ( // run empty main(). func TestRunEmptyMain(t *testing.T) { m := NewMachine("test", nil) - main := FuncD("main", nil, nil, nil) + // []Stmt{} != nil, as nil means that in the source code not even the + // brackets are present and is reserved for external (ie. native) functions. + main := FuncD("main", nil, nil, []Stmt{}) m.RunDeclaration(main) m.RunMain() } diff --git a/gnovm/pkg/gnolang/go2gno.go b/gnovm/pkg/gnolang/go2gno.go index c3ab4e95b73..ee82cb39555 100644 --- a/gnovm/pkg/gnolang/go2gno.go +++ b/gnovm/pkg/gnolang/go2gno.go @@ -96,9 +96,11 @@ func MustParseExpr(expr string) Expr { return x } -// filename must not include the path. +// ParseFile uses the Go parser to parse body. It then runs [Go2Gno] on the +// resulting AST -- the resulting FileNode is returned, together with any other +// error (including panics, which are recovered) from [Go2Gno]. func ParseFile(filename string, body string) (fn *FileNode, err error) { - // Parse src but stop after processing the imports. + // Use go parser to parse the body. fs := token.NewFileSet() f, err := parser.ParseFile(fs, filename, body, parser.ParseComments|parser.DeclarationErrors) if err != nil { @@ -112,9 +114,9 @@ func ParseFile(filename string, body string) (fn *FileNode, err error) { defer func() { if r := recover(); r != nil { if rerr, ok := r.(error); ok { - err = rerr + err = errors.Wrap(rerr, "parsing file") } else { - err = errors.New(fmt.Sprintf("%v", r)) + err = errors.New(fmt.Sprintf("%v", r)).Stacktrace() } return } @@ -431,7 +433,10 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) { } name := toName(gon.Name) type_ := Go2Gno(fs, gon.Type).(*FuncTypeExpr) - body := Go2Gno(fs, gon.Body).(*BlockStmt).Body + var body []Stmt + if gon.Body != nil { + body = Go2Gno(fs, gon.Body).(*BlockStmt).Body + } return &FuncDecl{ IsMethod: isMethod, Recv: recv, diff --git a/gnovm/pkg/gnolang/gonative.go b/gnovm/pkg/gnolang/gonative.go index 69e468f755a..129cac1af84 100644 --- a/gnovm/pkg/gnolang/gonative.go +++ b/gnovm/pkg/gnolang/gonative.go @@ -676,6 +676,9 @@ func go2GnoValueUpdate(alloc *Allocator, rlm *Realm, lvl int, tv *TypedValue, rv return } +// used for direct comparison to error types +var tError = reflect.TypeOf(new(error)).Elem() + // If recursive is false, this function is like go2GnoValue() but less lazy // (but still not recursive/eager). When recursive is false, it is for // converting Go types to Gno types upon an explicit conversion (via @@ -757,6 +760,15 @@ func go2GnoValue2(alloc *Allocator, store Store, rv reflect.Value, recursive boo // regardless. tv.V = alloc.NewNative(rv) case reflect.Interface: + // special case for errors, which are very often used especially in + // native bindings + if rv.Type() == tError { + tv.T = gErrorType + if !rv.IsNil() { + tv.V = alloc.NewNative(rv.Elem()) + } + return + } panic("not yet implemented") case reflect.Map: panic("not yet implemented") @@ -851,7 +863,7 @@ func gno2GoType(t Type) reflect.Type { fn := string(field.Name) pkgPath := "" if !isUpper(fn) { - pkgPath = ct.PkgPath + fn = "X" + fn } gfs[i] = reflect.StructField{ Name: fn, diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 60008d3c34d..f64a7f3def8 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -46,14 +46,15 @@ type Machine struct { Context interface{} } -// machine.Release() must be called on objects -// created via this constructor -// Machine with new package of given path. -// Creates a new MemRealmer for any new realms. -// Looks in store for package of pkgPath; if not found, -// creates new instances as necessary. -// If pkgPath is zero, the machine has no active package -// and one must be set prior to usage. +// NewMachine initializes a new gno virtual machine, acting as a shorthand +// for [NewMachineWithOptions], setting the given options PkgPath and Store. +// +// The machine will run on the package at the given path, which will be +// retrieved through the given store. If it is not set, the machine has no +// active package, and one must be set prior to usage. +// +// Like for [NewMachineWithOptions], Machines initialized through this +// constructor must be finalized with [Machine.Release]. func NewMachine(pkgPath string, store Store) *Machine { return NewMachineWithOptions( MachineOptions{ @@ -62,12 +63,14 @@ func NewMachine(pkgPath string, store Store) *Machine { }) } +// MachineOptions is used to pass options to [NewMachineWithOptions]. type MachineOptions struct { + // Active package of the given machine; must be set before execution. PkgPath string CheckTypes bool // not yet used ReadOnly bool - Output io.Writer - Store Store + Output io.Writer // default os.Stdout + Store Store // default NewStore(Alloc, nil, nil) Context interface{} Alloc *Allocator // or see MaxAllocBytes. MaxAllocBytes int64 // or 0 for no limit. @@ -87,6 +90,11 @@ var machinePool = sync.Pool{ }, } +// NewMachineWithOptions initializes a new gno virtual machine with the given +// options. +// +// Machines initialized through this constructor must be finalized with +// [Machine.Release]. func NewMachineWithOptions(opts MachineOptions) *Machine { checkTypes := opts.CheckTypes readOnly := opts.ReadOnly @@ -148,13 +156,11 @@ var ( valueZeroed [ValueSize]TypedValue ) -// m should not be used after this call -// if m is nil, this will panic -// this is on purpose, to discourage misuse -// and prevent objects that were not taken from -// the pool, to call Release +// Release resets some of the values of *Machine and puts back m into the +// machine pool; for this reason, Release() should be called as a finalizer, +// and m should not be used after this call. Only Machines initialized with this +// package's constructors should be released. func (m *Machine) Release() { - // copy() // here we zero in the values for the next user m.NumOps = 0 m.NumValues = 0 @@ -181,10 +187,19 @@ func (m *Machine) SetActivePackage(pv *PackageValue) { // Upon restart, preprocess all MemPackage and save blocknodes. // This is a temporary measure until we optimize/make-lazy. +// +// NOTE: package paths not beginning with gno.land will be allowed to override, +// to support cases of stdlibs processed through [RunMemPackagesWithOverrides]. func (m *Machine) PreprocessAllFilesAndSaveBlockNodes() { ch := m.Store.IterMemPackage() for memPkg := range ch { fset := ParseMemPackage(memPkg) + // Implicitly do filterDuplicates for stdlibs. + // This is done as this function is at the time of writing only used + // in tests, ie. where we use the "override" feature for stdlibs. + if !strings.HasPrefix(memPkg.Path, "gno.land/") { + filterDuplicates(fset) + } pn := NewPackageNode(Name(memPkg.Name), memPkg.Path, fset) m.Store.SetBlockNode(pn) PredefineFileSet(m.Store, pn, fset) @@ -215,8 +230,23 @@ func (m *Machine) PreprocessAllFilesAndSaveBlockNodes() { // and corresponding package node, package value, and types to store. Save // is set to false for tests where package values may be native. func (m *Machine) RunMemPackage(memPkg *std.MemPackage, save bool) (*PackageNode, *PackageValue) { + return m.runMemPackage(memPkg, save, false) +} + +// RunMemPackageWithOverrides works as [RunMemPackage], however after parsing, +// declarations are filtered removing duplicate declarations. +// To control which declaration overrides which, use [ReadMemPackageFromList], +// putting the overrides at the top of the list. +func (m *Machine) RunMemPackageWithOverrides(memPkg *std.MemPackage, save bool) (*PackageNode, *PackageValue) { + return m.runMemPackage(memPkg, save, true) +} + +func (m *Machine) runMemPackage(memPkg *std.MemPackage, save, overrides bool) (*PackageNode, *PackageValue) { // parse files. files := ParseMemPackage(memPkg) + if overrides { + filterDuplicates(files) + } // make and set package if doesn't exist. pn := (*PackageNode)(nil) pv := (*PackageValue)(nil) @@ -243,6 +273,69 @@ func (m *Machine) RunMemPackage(memPkg *std.MemPackage, save bool) (*PackageNode return pn, pv } +func filterDuplicates(fset *FileSet) { + defined := make(map[Name]struct{}, 128) + for _, f := range fset.Files { + // returns true if name already defined + addName := func(n Name) bool { + if _, ok := defined[n]; ok { + return true // i-- + } + defined[n] = struct{}{} + return false + } + for i := 0; i < len(f.Decls); i++ { + d := f.Decls[i] + var name Name + switch d := d.(type) { + case *FuncDecl: + name = d.Name + if d.IsMethod { + name = Name(derefStar(d.Recv.Type).String()) + "." + name + } + case *TypeDecl: + name = d.Name + case *ValueDecl: + // simpler, most common case + if len(d.NameExprs) == 1 { + name = d.NameExprs[0].Name + break + } + + // more complex: filter out the NameExprs which are not + // redeclarations, and set them in d.NameExprs + newNames := make([]NameExpr, 0, len(d.NameExprs)) + for _, nx := range d.NameExprs { + if !addName(nx.Name) { + newNames = append(newNames, nx) + } + } + // all new names are re-declarations, remove + if len(newNames) == 0 { + f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) + i-- + continue + } + d.NameExprs = newNames + continue + default: + continue + } + if addName(name) { + f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) + i-- + } + } + } +} + +func derefStar(x Expr) Expr { + if x, ok := x.(*StarExpr); ok { + return x.X + } + return x +} + // Tests all test files in a mempackage. // Assumes that the importing of packages is handled elsewhere. // The resulting package value and node become injected with TestMethods and diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 46308fb3a02..803a8698a22 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -1093,13 +1093,21 @@ func PackageNameFromFileBody(name, body string) Name { return Name(astFile.Name.Name) } -// NOTE: panics if package name is invalid. -func ReadMemPackage(dir string, pkgPath string) *std.MemPackage { +// ReadMemPackage initializes a new MemPackage by reading the OS directory +// at dir, and saving it with the given pkgPath (import path). +// The resulting MemPackage will contain the names and content of all *.gno files, +// and additionally README.md, LICENSE, and gno.mod. +// +// ReadMemPackage does not perform validation aside from the package's name; +// the files are not parsed but their contents are merely stored inside a MemFile. +// +// NOTE: panics if package name is invalid (characters must be alphanumeric or _, +// lowercase, and must start with a letter). +func ReadMemPackage(dir, pkgPath string) *std.MemPackage { files, err := ioutil.ReadDir(dir) if err != nil { panic(err) } - memPkg := &std.MemPackage{Path: pkgPath} allowedFiles := []string{ // make case insensitive? "gno.mod", "LICENSE", @@ -1108,27 +1116,43 @@ func ReadMemPackage(dir string, pkgPath string) *std.MemPackage { allowedFileExtensions := []string{ ".gno", } - var pkgName Name + list := make([]string, 0, len(files)) for _, file := range files { if file.IsDir() || strings.HasPrefix(file.Name(), ".") || (!endsWith(file.Name(), allowedFileExtensions) && !contains(allowedFiles, file.Name())) { continue } - fpath := filepath.Join(dir, file.Name()) + list = append(list, filepath.Join(dir, file.Name())) + } + return ReadMemPackageFromList(list, pkgPath) +} + +// ReadMemPackageFromList creates a new [std.MemPackage] with the specified pkgPath, +// containing the contents of all the files provided in the list slice. +// No parsing or validation is done on the filenames. +// +// NOTE: panics if package name is invalid (characters must be alphanumeric or _, +// lowercase, and must start with a letter). +func ReadMemPackageFromList(list []string, pkgPath string) *std.MemPackage { + memPkg := &std.MemPackage{Path: pkgPath} + var pkgName Name + for _, fpath := range list { + fname := filepath.Base(fpath) bz, err := os.ReadFile(fpath) if err != nil { panic(err) } - if pkgName == "" && strings.HasSuffix(file.Name(), ".gno") { - pkgName = PackageNameFromFileBody(file.Name(), string(bz)) + // XXX: should check that all pkg names are the same (else package is invalid) + if pkgName == "" && strings.HasSuffix(fname, ".gno") { + pkgName = PackageNameFromFileBody(fname, string(bz)) if strings.HasSuffix(string(pkgName), "_test") { pkgName = pkgName[:len(pkgName)-len("_test")] } } memPkg.Files = append(memPkg.Files, &std.MemFile{ - Name: file.Name(), + Name: fname, Body: string(bz), }) } @@ -1142,33 +1166,29 @@ func ReadMemPackage(dir string, pkgPath string) *std.MemPackage { return memPkg } -func PrecompileMemPackage(memPkg *std.MemPackage) error { - return nil -} - -// Returns the code fileset minus any spurious or test files. +// ParseMemPackage executes [ParseFile] on each file of the memPkg, excluding +// test and spurious (non-gno) files. The resulting *FileSet is returned. +// +// If one of the files has a different package name than memPkg.Name, +// or [ParseFile] returns an error, ParseMemPackage panics. func ParseMemPackage(memPkg *std.MemPackage) (fset *FileSet) { fset = &FileSet{} for _, mfile := range memPkg.Files { - if !strings.HasSuffix(mfile.Name, ".gno") { - continue // skip spurious file. + if !strings.HasSuffix(mfile.Name, ".gno") || + endsWith(mfile.Name, []string{"_test.gno", "_filetest.gno"}) { + continue // skip spurious or test file. } n, err := ParseFile(mfile.Name, mfile.Body) if err != nil { panic(errors.Wrap(err, "parsing file "+mfile.Name)) } - if strings.HasSuffix(mfile.Name, "_test.gno") { - // skip test file. - } else if strings.HasSuffix(mfile.Name, "_filetest.gno") { - // skip test file. - } else if memPkg.Name == string(n.PkgName) { - // add package file. - fset.AddFiles(n) - } else { + if memPkg.Name != string(n.PkgName) { panic(fmt.Sprintf( "expected package name [%s] but got [%s]", memPkg.Name, n.PkgName)) } + // add package file. + fset.AddFiles(n) } return fset } @@ -1378,6 +1398,7 @@ func (x *PackageNode) DefineNative(n Name, ps, rs FieldTypeExprs, native func(*M if native == nil { panic("DefineNative expects a function, but got nil") } + fd := FuncD(n, ps, rs, nil) fd = Preprocess(nil, x, fd).(*FuncDecl) ft := evalStaticType(nil, x, &fd.Type).(*FuncType) @@ -1657,7 +1678,6 @@ func (sb *StaticBlock) GetStaticTypeOfAt(store Store, path ValuePath) Type { path.Depth -= 1 } } - panic("should not happen") } // Implements BlockNode. @@ -2025,6 +2045,7 @@ const ( ) // TODO: consider length restrictions. +// If this function is changed, ReadMemPackage's documentation should be updated accordingly. func validatePkgName(name string) { if nameOK, _ := regexp.MatchString( `^[a-z][a-z0-9_]+$`, name); !nameOK { diff --git a/gnovm/pkg/gnolang/op_binary.go b/gnovm/pkg/gnolang/op_binary.go index 13156133e38..ddab8302d2d 100644 --- a/gnovm/pkg/gnolang/op_binary.go +++ b/gnovm/pkg/gnolang/op_binary.go @@ -489,7 +489,7 @@ func isEql(store Store, lv, rv *TypedValue) bool { func isLss(lv, rv *TypedValue) bool { switch lv.T.Kind() { case StringKind: - return (lv.V.(StringValue) < rv.V.(StringValue)) + return (lv.GetString() < rv.GetString()) case IntKind: return (lv.GetInt() < rv.GetInt()) case Int8Kind: @@ -533,7 +533,7 @@ func isLss(lv, rv *TypedValue) bool { func isLeq(lv, rv *TypedValue) bool { switch lv.T.Kind() { case StringKind: - return (lv.V.(StringValue) <= rv.V.(StringValue)) + return (lv.GetString() <= rv.GetString()) case IntKind: return (lv.GetInt() <= rv.GetInt()) case Int8Kind: @@ -577,7 +577,7 @@ func isLeq(lv, rv *TypedValue) bool { func isGtr(lv, rv *TypedValue) bool { switch lv.T.Kind() { case StringKind: - return (lv.V.(StringValue) > rv.V.(StringValue)) + return (lv.GetString() > rv.GetString()) case IntKind: return (lv.GetInt() > rv.GetInt()) case Int8Kind: @@ -621,7 +621,7 @@ func isGtr(lv, rv *TypedValue) bool { func isGeq(lv, rv *TypedValue) bool { switch lv.T.Kind() { case StringKind: - return (lv.V.(StringValue) >= rv.V.(StringValue)) + return (lv.GetString() >= rv.GetString()) case IntKind: return (lv.GetInt() >= rv.GetInt()) case Int8Kind: diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index f43a593c50e..b01169bf466 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -58,6 +58,13 @@ func (m *Machine) doOpCall() { clo := fr.Func.GetClosure(m.Store) b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo) m.PushBlock(b) + if fv.nativeBody == nil && fv.NativePkg != "" { + // native function, unmarshaled so doesn't have nativeBody yet + fv.nativeBody = m.Store.GetNative(fv.NativePkg, fv.NativeName) + if fv.nativeBody == nil { + panic(fmt.Sprintf("natively defined function (%q).%s could not be resolved", fv.NativePkg, fv.NativeName)) + } + } if fv.nativeBody == nil { fbody := fv.GetBodyFromSource(m.Store) if len(ft.Results) == 0 { diff --git a/gnovm/pkg/gnolang/op_eval.go b/gnovm/pkg/gnolang/op_eval.go index 83dd3737b6b..22daf0b886c 100644 --- a/gnovm/pkg/gnolang/op_eval.go +++ b/gnovm/pkg/gnolang/op_eval.go @@ -5,6 +5,7 @@ import ( "fmt" "math" "math/big" + "os" "regexp" "strconv" "strings" @@ -12,10 +13,14 @@ import ( "github.com/cockroachdb/apd" ) +var printEval = os.Getenv("DEBUG_PRINT_EVAL") == "1" + func (m *Machine) doOpEval() { x := m.PeekExpr(1) - if debug { - debug.Printf("EVAL: %v\n", x) + if printEval { + fmt.Fprintf(os.Stderr, "EVAL: (%T) %v\n", x, x) + } else if debug { + debug.Printf("EVAL: (%T) %v\n", x, x) // fmt.Println(m.String()) } // This case moved out of switch for performance. diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index b46e343b00e..b3bf240aea1 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -78,8 +78,12 @@ func (m *Machine) doOpIndex2() { func (m *Machine) doOpSelector() { sx := m.PopExpr().(*SelectorExpr) xv := m.PeekValue(1) - res := xv.GetPointerTo(m.Alloc, m.Store, sx.Path) - *xv = res.Deref() // reuse as result + res := xv.GetPointerTo(m.Alloc, m.Store, sx.Path).Deref() + if debug { + m.Printf("-v[S] %v\n", xv) + m.Printf("+v[S] %v\n", res) + } + *xv = res // reuse as result } func (m *Machine) doOpSlice() { diff --git a/gnovm/pkg/gnolang/op_inc_dec.go b/gnovm/pkg/gnolang/op_inc_dec.go index 80987707035..70886497d5e 100644 --- a/gnovm/pkg/gnolang/op_inc_dec.go +++ b/gnovm/pkg/gnolang/op_inc_dec.go @@ -1,5 +1,12 @@ package gnolang +import ( + "fmt" + "math/big" + + "github.com/cockroachdb/apd" +) + func (m *Machine) doOpInc() { s := m.PopStmt().(*IncDecStmt) @@ -42,8 +49,26 @@ func (m *Machine) doOpInc() { lv.SetUint32(lv.GetUint32() + 1) case Uint64Type: lv.SetUint64(lv.GetUint64() + 1) + case Float32Type: + lv.SetFloat32(lv.GetFloat32() + 1) + case Float64Type: + lv.SetFloat64(lv.GetFloat64() + 1) + case BigintType, UntypedBigintType: + lb := lv.GetBigInt() + lb = big.NewInt(0).Add(lb, big.NewInt(1)) + lv.V = BigintValue{V: lb} + case BigdecType, UntypedBigdecType: + lb := lv.GetBigDec() + sum := apd.New(0, 0) + cond, err := apd.BaseContext.WithPrecision(0).Add(sum, lb, apd.New(1, 0)) + if err != nil { + panic(fmt.Sprintf("bigdec addition error: %v", err)) + } else if cond.Inexact() { + panic(fmt.Sprintf("bigdec addition inexact: %v + 1", lb)) + } + lv.V = BigdecValue{V: sum} default: - panic("unexpected type in in operation") + panic(fmt.Sprintf("unexpected type %s in inc/dec operation", lv.T)) } // Mark dirty in realm. @@ -94,8 +119,26 @@ func (m *Machine) doOpDec() { lv.SetUint32(lv.GetUint32() - 1) case Uint64Type: lv.SetUint64(lv.GetUint64() - 1) + case Float32Type: + lv.SetFloat32(lv.GetFloat32() - 1) + case Float64Type: + lv.SetFloat64(lv.GetFloat64() - 1) + case BigintType, UntypedBigintType: + lb := lv.GetBigInt() + lb = big.NewInt(0).Sub(lb, big.NewInt(1)) + lv.V = BigintValue{V: lb} + case BigdecType, UntypedBigdecType: + lb := lv.GetBigDec() + sum := apd.New(0, 0) + cond, err := apd.BaseContext.WithPrecision(0).Sub(sum, lb, apd.New(1, 0)) + if err != nil { + panic(fmt.Sprintf("bigdec addition error: %v", err)) + } else if cond.Inexact() { + panic(fmt.Sprintf("bigdec addition inexact: %v + 1", lb)) + } + lv.V = BigdecValue{V: sum} default: - panic("unexpected type in in operation") + panic(fmt.Sprintf("unexpected type %s in inc/dec operation", lv.T)) } // Mark dirty in realm. diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index e02a158fcf1..44457ad0cb0 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -3122,19 +3122,30 @@ func tryPredefine(store Store, last BlockNode, d Decl) (un Name) { } // define a FuncValue w/ above type as d.Name. // fill in later during *FuncDecl:BLOCK. + fv := &FuncValue{ + Type: ft, + IsMethod: false, + Source: d, + Name: d.Name, + Closure: nil, // set lazily. + FileName: fileNameOf(last), + PkgPath: pkg.PkgPath, + body: d.Body, + nativeBody: nil, + } + // NOTE: fv.body == nil means no body (ie. not even curly braces) + // len(fv.body) == 0 could mean also {} (ie. no statements inside) + if fv.body == nil && store != nil { + fv.nativeBody = store.GetNative(pkg.PkgPath, d.Name) + if fv.nativeBody == nil { + panic(fmt.Sprintf("function %s does not have a body but is not natively defined", d.Name)) + } + fv.NativePkg = pkg.PkgPath + fv.NativeName = d.Name + } pkg.Define(d.Name, TypedValue{ T: ft, - V: &FuncValue{ - Type: ft, - IsMethod: false, - Source: d, - Name: d.Name, - Closure: nil, // set lazily. - FileName: fileNameOf(last), - PkgPath: pkg.PkgPath, - body: d.Body, - nativeBody: nil, - }, + V: fv, }) if d.Name == "init" { // init functions can't be referenced. diff --git a/gnovm/pkg/gnolang/realm.go b/gnovm/pkg/gnolang/realm.go index 567aea58284..d10a9280301 100644 --- a/gnovm/pkg/gnolang/realm.go +++ b/gnovm/pkg/gnolang/realm.go @@ -1129,18 +1129,23 @@ func copyValueWithRefs(parent Object, val Value) Value { if cv.Closure != nil { closure = toRefValue(parent, cv.Closure) } - if cv.nativeBody != nil { + // nativeBody funcs which don't come from NativeStore (and thus don't + // have NativePkg/Name) can't be persisted, and should not be able + // to get here anyway. + if cv.nativeBody != nil && cv.NativePkg == "" { panic("should not happen") } ft := copyTypeWithRefs(cv.Type) return &FuncValue{ - Type: ft, - IsMethod: cv.IsMethod, - Source: source, - Name: cv.Name, - Closure: closure, - FileName: cv.FileName, - PkgPath: cv.PkgPath, + Type: ft, + IsMethod: cv.IsMethod, + Source: source, + Name: cv.Name, + Closure: closure, + FileName: cv.FileName, + PkgPath: cv.PkgPath, + NativePkg: cv.NativePkg, + NativeName: cv.NativeName, } case *BoundMethodValue: fnc := copyValueWithRefs(cv, cv.Func).(*FuncValue) diff --git a/gnovm/pkg/gnolang/store.go b/gnovm/pkg/gnolang/store.go index d3628edf216..c43d5a57e62 100644 --- a/gnovm/pkg/gnolang/store.go +++ b/gnovm/pkg/gnolang/store.go @@ -19,6 +19,9 @@ type PackageGetter func(pkgPath string) (*PackageNode, *PackageValue) // inject natives into a new or loaded package (value and node) type PackageInjector func(store Store, pn *PackageNode) +// NativeStore is a function which can retrieve native bodies of native functions. +type NativeStore func(pkgName string, name Name) func(m *Machine) + type Store interface { // STABLE SetPackageGetter(PackageGetter) @@ -50,10 +53,12 @@ type Store interface { GetMemPackage(path string) *std.MemPackage GetMemFile(path string, name string) *std.MemFile IterMemPackage() <-chan *std.MemPackage - ClearObjectCache() // for each delivertx. - Fork() Store // for checktx, simulate, and queries. - SwapStores(baseStore, iavlStore store.Store) // for gas wrappers. - SetPackageInjector(PackageInjector) // for natives + ClearObjectCache() // for each delivertx. + Fork() Store // for checktx, simulate, and queries. + SwapStores(baseStore, iavlStore store.Store) // for gas wrappers. + SetPackageInjector(PackageInjector) // for natives + SetNativeStore(NativeStore) // for "new" natives XXX + GetNative(pkgPath string, name Name) func(m *Machine) // for "new" natives XXX SetLogStoreOps(enabled bool) SprintStoreOps() string LogSwitchRealm(rlmpath string) // to mark change of realm boundaries @@ -72,6 +77,7 @@ type defaultStore struct { baseStore store.Store // for objects, types, nodes iavlStore store.Store // for escaped object hashes pkgInjector PackageInjector // for injecting natives + nativeStore NativeStore // for injecting natives go2gnoMap map[string]string // go pkgpath.name -> gno pkgpath.name go2gnoStrict bool // if true, native->gno type conversion must be registered. @@ -603,6 +609,7 @@ func (ds *defaultStore) Fork() Store { baseStore: ds.baseStore, iavlStore: ds.iavlStore, pkgInjector: ds.pkgInjector, + nativeStore: ds.nativeStore, go2gnoMap: ds.go2gnoMap, go2gnoStrict: ds.go2gnoStrict, opslog: nil, // new ops log. @@ -622,6 +629,17 @@ func (ds *defaultStore) SetPackageInjector(inj PackageInjector) { ds.pkgInjector = inj } +func (ds *defaultStore) SetNativeStore(ns NativeStore) { + ds.nativeStore = ns +} + +func (ds *defaultStore) GetNative(pkgPath string, name Name) func(m *Machine) { + if ds.nativeStore != nil { + return ds.nativeStore(pkgPath, name) + } + return nil +} + func (ds *defaultStore) Flush() { // XXX } diff --git a/gnovm/pkg/gnolang/uverse.go b/gnovm/pkg/gnolang/uverse.go index 80f8a751e57..57f8f6d393d 100644 --- a/gnovm/pkg/gnolang/uverse.go +++ b/gnovm/pkg/gnolang/uverse.go @@ -550,7 +550,7 @@ func UverseNode() *PackageNode { if xt.Elem().Kind() == Uint8Kind { // TODO this might be faster if reflect supports // appending this way without first converting to a slice. - argrv := reflect.ValueOf([]byte(arg1.TV.V.(StringValue))) + argrv := reflect.ValueOf([]byte(arg1.TV.GetString())) resrv := reflect.AppendSlice(sv, argrv) m.PushValue(TypedValue{ T: xt, diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 3de74ac0130..f0b363b7bac 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -518,13 +518,15 @@ func (sv *StructValue) Copy(alloc *Allocator) *StructValue { // makes construction TypedValue{T:*FuncType{},V:*FuncValue{}} // faster. type FuncValue struct { - Type Type // includes unbound receiver(s) - IsMethod bool // is an (unbound) method - Source BlockNode // for block mem allocation - Name Name // name of function/method - Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy) - FileName Name // file name where declared - PkgPath string + Type Type // includes unbound receiver(s) + IsMethod bool // is an (unbound) method + Source BlockNode // for block mem allocation + Name Name // name of function/method + Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy) + FileName Name // file name where declared + PkgPath string + NativePkg string // for native bindings through NativeStore + NativeName Name // not redundant with Name; this cannot be changed in userspace body []Stmt // function body nativeBody func(*Machine) // alternative to Body @@ -540,6 +542,8 @@ func (fv *FuncValue) Copy(alloc *Allocator) *FuncValue { Closure: fv.Closure, FileName: fv.FileName, PkgPath: fv.PkgPath, + NativePkg: fv.NativePkg, + NativeName: fv.NativeName, body: fv.body, nativeBody: fv.nativeBody, } @@ -2312,12 +2316,8 @@ func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue { // the generation for uverse is 0. If path.Depth is // 0, it implies that b == uverse, and the condition // would fail as if it were 1. - i := uint8(1) -LOOP: - if i < path.Depth { + for i := uint8(1); i < path.Depth; i++ { b = b.GetParent(store) - i++ - goto LOOP } return b.GetPointerToInt(store, int(path.Index)) } diff --git a/gnovm/stdlibs/crypto/sha256/sha256.gno b/gnovm/stdlibs/crypto/sha256/sha256.gno index efb8ebb8e37..c36313f5482 100644 --- a/gnovm/stdlibs/crypto/sha256/sha256.gno +++ b/gnovm/stdlibs/crypto/sha256/sha256.gno @@ -1,12 +1,8 @@ package sha256 -import ( - isha256 "internal/crypto/sha256" -) - const Size = 32 // Sum returns the SHA-256 checksum of the data. -func Sum256(data []byte) [Size]byte { - return isha256.Sum256(data) -} +func Sum256(data []byte) [Size]byte { return sum256(data) } + +func sum256(data []byte) [32]byte // injected diff --git a/gnovm/stdlibs/crypto/sha256/sha256.go b/gnovm/stdlibs/crypto/sha256/sha256.go new file mode 100644 index 00000000000..c3aac1106e2 --- /dev/null +++ b/gnovm/stdlibs/crypto/sha256/sha256.go @@ -0,0 +1,7 @@ +package sha256 + +import "crypto/sha256" + +func X_sum256(data []byte) [32]byte { + return sha256.Sum256(data) +} diff --git a/gnovm/stdlibs/encoding/base64/base64.gno b/gnovm/stdlibs/encoding/base64/base64.gno index 7889a548832..015d7a825bb 100644 --- a/gnovm/stdlibs/encoding/base64/base64.gno +++ b/gnovm/stdlibs/encoding/base64/base64.gno @@ -496,7 +496,7 @@ func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { _ = enc.decodeMap si := 0 - for strconv.IntSize >= 64 && len(src)-si >= 8 && len(dst)-n >= 8 { + for len(src)-si >= 8 && len(dst)-n >= 8 { src2 := src[si : si+8] if dn, ok := assemble64( enc.decodeMap[src2[0]], diff --git a/gnovm/stdlibs/internal/crypto/sha256/sha256.gno b/gnovm/stdlibs/internal/crypto/sha256/sha256.gno deleted file mode 100644 index 458b2123f59..00000000000 --- a/gnovm/stdlibs/internal/crypto/sha256/sha256.gno +++ /dev/null @@ -1,3 +0,0 @@ -package sha256 - -// XXX injected via stdlibs/stdlibs.go diff --git a/gnovm/stdlibs/internal/math/math.gno b/gnovm/stdlibs/internal/math/math.gno deleted file mode 100644 index 42245392caf..00000000000 --- a/gnovm/stdlibs/internal/math/math.gno +++ /dev/null @@ -1,3 +0,0 @@ -package math - -// XXX injected via stdlibs/stdlibs.go diff --git a/gnovm/stdlibs/math/abs.gno b/gnovm/stdlibs/math/abs.gno index 7a55c0c5bd2..08be14548dd 100644 --- a/gnovm/stdlibs/math/abs.gno +++ b/gnovm/stdlibs/math/abs.gno @@ -4,10 +4,6 @@ package math -import ( - imath "internal/math" // XXX -) - // Abs returns the absolute value of x. // // Special cases are: @@ -15,5 +11,5 @@ import ( // Abs(±Inf) = +Inf // Abs(NaN) = NaN func Abs(x float64) float64 { - return imath.Float64frombits(imath.Float64bits(x) &^ (1 << 63)) + return Float64frombits(Float64bits(x) &^ (1 << 63)) } diff --git a/gnovm/stdlibs/math/acosh.gno b/gnovm/stdlibs/math/acosh.gno new file mode 100644 index 00000000000..261f25ce9f9 --- /dev/null +++ b/gnovm/stdlibs/math/acosh.gno @@ -0,0 +1,62 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/e_acosh.c +// and came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// +// __ieee754_acosh(x) +// Method : +// Based on +// acosh(x) = log [ x + sqrt(x*x-1) ] +// we have +// acosh(x) := log(x)+ln2, if x is large; else +// acosh(x) := log(2x-1/(sqrt(x*x-1)+x)) if x>2; else +// acosh(x) := log1p(t+sqrt(2.0*t+t*t)); where t=x-1. +// +// Special cases: +// acosh(x) is NaN with signal if x<1. +// acosh(NaN) is NaN without signal. +// + +// Acosh returns the inverse hyperbolic cosine of x. +// +// Special cases are: +// +// Acosh(+Inf) = +Inf +// Acosh(x) = NaN if x < 1 +// Acosh(NaN) = NaN +func Acosh(x float64) float64 { + return acosh(x) +} + +func acosh(x float64) float64 { + const Large = 1 << 28 // 2**28 + // first case is special case + switch { + case x < 1 || IsNaN(x): + return NaN() + case x == 1: + return 0 + case x >= Large: + return Log(x) + Ln2 // x > 2**28 + case x > 2: + return Log(2*x - 1/(x+Sqrt(x*x-1))) // 2**28 > x > 2 + } + t := x - 1 + return Log1p(t + Sqrt(2*t+t*t)) // 2 >= x > 1 +} diff --git a/gnovm/stdlibs/math/all_test.gno b/gnovm/stdlibs/math/all_test.gno index ba7b1528d23..54f8a1e16b6 100644 --- a/gnovm/stdlibs/math/all_test.gno +++ b/gnovm/stdlibs/math/all_test.gno @@ -5,28 +5,581 @@ package math import ( + "fmt" "testing" ) +var _vf = []float64{ + 4.9790119248836735e+00, + 7.7388724745781045e+00, + -2.7688005719200159e-01, + -5.0106036182710749e+00, + 9.6362937071984173e+00, + 2.9263772392439646e+00, + 5.2290834314593066e+00, + 2.7279399104360102e+00, + 1.8253080916808550e+00, + -8.6859247685756013e+00, +} + // The expected results below were computed by the high precision calculators -// at https://keisan.casio.com/. More exact input values (array vf[], above) +// at https://keisan.casio.com/. More exact input values (array _vf[], above) // were obtained by printing them with "%.26f". The answers were calculated // to 26 digits (by using the "Digit number" drop-down control of each // calculator). -var vf = []float64{ - 4.9790119248836735e+00, - 7.7388724745781045e+00, +var _acos = []float64{ + 1.0496193546107222142571536e+00, + 6.8584012813664425171660692e-01, + 1.5984878714577160325521819e+00, + 2.0956199361475859327461799e+00, + 2.7053008467824138592616927e-01, + 1.2738121680361776018155625e+00, + 1.0205369421140629186287407e+00, + 1.2945003481781246062157835e+00, + 1.3872364345374451433846657e+00, + 2.6231510803970463967294145e+00, +} + +var _acosh = []float64{ + 2.4743347004159012494457618e+00, + 2.8576385344292769649802701e+00, + 7.2796961502981066190593175e-01, + 2.4796794418831451156471977e+00, + 3.0552020742306061857212962e+00, + 2.044238592688586588942468e+00, + 2.5158701513104513595766636e+00, + 1.99050839282411638174299e+00, + 1.6988625798424034227205445e+00, + 2.9611454842470387925531875e+00, +} + +var _asin = []float64{ + 5.2117697218417440497416805e-01, + 8.8495619865825236751471477e-01, + -02.769154466281941332086016e-02, + -5.2482360935268931351485822e-01, + 1.3002662421166552333051524e+00, + 2.9698415875871901741575922e-01, + 5.5025938468083370060258102e-01, + 2.7629597861677201301553823e-01, + 1.83559892257451475846656e-01, + -1.0523547536021497774980928e+00, +} + +var _asinh = []float64{ + 2.3083139124923523427628243e+00, + 2.743551594301593620039021e+00, + -2.7345908534880091229413487e-01, + -2.3145157644718338650499085e+00, + 2.9613652154015058521951083e+00, + 1.7949041616585821933067568e+00, + 2.3564032905983506405561554e+00, + 1.7287118790768438878045346e+00, + 1.3626658083714826013073193e+00, + -2.8581483626513914445234004e+00, +} + +var _atan = []float64{ + 1.372590262129621651920085e+00, + 1.442290609645298083020664e+00, + -2.7011324359471758245192595e-01, + -1.3738077684543379452781531e+00, + 1.4673921193587666049154681e+00, + 1.2415173565870168649117764e+00, + 1.3818396865615168979966498e+00, + 1.2194305844639670701091426e+00, + 1.0696031952318783760193244e+00, + -1.4561721938838084990898679e+00, +} + +var _atanh = []float64{ + 5.4651163712251938116878204e-01, + 1.0299474112843111224914709e+00, + -2.7695084420740135145234906e-02, + -5.5072096119207195480202529e-01, + 1.9943940993171843235906642e+00, + 3.01448604578089708203017e-01, + 5.8033427206942188834370595e-01, + 2.7987997499441511013958297e-01, + 1.8459947964298794318714228e-01, + -1.3273186910532645867272502e+00, +} + +var _atan2 = []float64{ + 1.1088291730037004444527075e+00, + 9.1218183188715804018797795e-01, + 1.5984772603216203736068915e+00, + 2.0352918654092086637227327e+00, + 8.0391819139044720267356014e-01, + 1.2861075249894661588866752e+00, + 1.0889904479131695712182587e+00, + 1.3044821793397925293797357e+00, + 1.3902530903455392306872261e+00, + 2.2859857424479142655411058e+00, +} + +var _cbrt = []float64{ + 1.7075799841925094446722675e+00, + 1.9779982212970353936691498e+00, + -6.5177429017779910853339447e-01, + -1.7111838886544019873338113e+00, + 2.1279920909827937423960472e+00, + 1.4303536770460741452312367e+00, + 1.7357021059106154902341052e+00, + 1.3972633462554328350552916e+00, + 1.2221149580905388454977636e+00, + -2.0556003730500069110343596e+00, +} + +var _ceil = []float64{ + 5.0000000000000000e+00, + 8.0000000000000000e+00, + Copysign(0, -1), + -5.0000000000000000e+00, + 1.0000000000000000e+01, + 3.0000000000000000e+00, + 6.0000000000000000e+00, + 3.0000000000000000e+00, + 2.0000000000000000e+00, + -8.0000000000000000e+00, +} + +var _copysign = []float64{ + -4.9790119248836735e+00, + -7.7388724745781045e+00, -2.7688005719200159e-01, -5.0106036182710749e+00, + -9.6362937071984173e+00, + -2.9263772392439646e+00, + -5.2290834314593066e+00, + -2.7279399104360102e+00, + -1.8253080916808550e+00, + -8.6859247685756013e+00, +} + +var _testCos = []float64{ + 2.634752140995199110787593e-01, + 1.148551260848219865642039e-01, + 9.6191297325640768154550453e-01, + 2.938141150061714816890637e-01, + -9.777138189897924126294461e-01, + -9.7693041344303219127199518e-01, + 4.940088096948647263961162e-01, + -9.1565869021018925545016502e-01, + -2.517729313893103197176091e-01, + -7.39241351595676573201918e-01, +} + +// Results for 100000 * Pi + _vf[i] +var _testCosLarge = []float64{ + 2.634752141185559426744e-01, + 1.14855126055543100712e-01, + 9.61912973266488928113e-01, + 2.9381411499556122552e-01, + -9.777138189880161924641e-01, + -9.76930413445147608049e-01, + 4.940088097314976789841e-01, + -9.15658690217517835002e-01, + -2.51772931436786954751e-01, + -7.3924135157173099849e-01, +} + +var _testCosh = []float64{ + 7.2668796942212842775517446e+01, + 1.1479413465659254502011135e+03, + 1.0385767908766418550935495e+00, + 7.5000957789658051428857788e+01, + 7.655246669605357888468613e+03, + 9.3567491758321272072888257e+00, + 9.331351599270605471131735e+01, + 7.6833430994624643209296404e+00, + 3.1829371625150718153881164e+00, + 2.9595059261916188501640911e+03, +} + +var _erf = []float64{ + 5.1865354817738701906913566e-01, + 7.2623875834137295116929844e-01, + -3.123458688281309990629839e-02, + -5.2143121110253302920437013e-01, + 8.2704742671312902508629582e-01, + 3.2101767558376376743993945e-01, + 5.403990312223245516066252e-01, + 3.0034702916738588551174831e-01, + 2.0369924417882241241559589e-01, + -7.8069386968009226729944677e-01, +} + +var _erfc = []float64{ + 4.8134645182261298093086434e-01, + 2.7376124165862704883070156e-01, + 1.0312345868828130999062984e+00, + 1.5214312111025330292043701e+00, + 1.7295257328687097491370418e-01, + 6.7898232441623623256006055e-01, + 4.596009687776754483933748e-01, + 6.9965297083261411448825169e-01, + 7.9630075582117758758440411e-01, + 1.7806938696800922672994468e+00, +} + +var _erfinv = []float64{ + 4.746037673358033586786350696e-01, + 8.559054432692110956388764172e-01, + -2.45427830571707336251331946e-02, + -4.78116683518973366268905506e-01, + 1.479804430319470983648120853e+00, + 2.654485787128896161882650211e-01, + 5.027444534221520197823192493e-01, + 2.466703532707627818954585670e-01, + 1.632011465103005426240343116e-01, + -1.06672334642196900710000389e+00, +} + +var _exp = []float64{ + 1.4533071302642137507696589e+02, + 2.2958822575694449002537581e+03, + 7.5814542574851666582042306e-01, + 6.6668778421791005061482264e-03, + 1.5310493273896033740861206e+04, + 1.8659907517999328638667732e+01, + 1.8662167355098714543942057e+02, + 1.5301332413189378961665788e+01, + 6.2047063430646876349125085e+00, + 1.6894712385826521111610438e-04, +} + +var _expm1 = []float64{ + 5.105047796122957327384770212e-02, + 8.046199708567344080562675439e-02, + -2.764970978891639815187418703e-03, + -4.8871434888875355394330300273e-02, + 1.0115864277221467777117227494e-01, + 2.969616407795910726014621657e-02, + 5.368214487944892300914037972e-02, + 2.765488851131274068067445335e-02, + 1.842068661871398836913874273e-02, + -8.3193870863553801814961137573e-02, +} + +var _expm1Large = []float64{ + 4.2031418113550844e+21, + 4.0690789717473863e+33, + -0.9372627915981363e+00, + -1.0, + 7.077694784145933e+41, + 5.117936223839153e+12, + 5.124137759001189e+22, + 7.03546003972584e+11, + 8.456921800389698e+07, + -1.0, +} + +var _exp2 = []float64{ + 3.1537839463286288034313104e+01, + 2.1361549283756232296144849e+02, + 8.2537402562185562902577219e-01, + 3.1021158628740294833424229e-02, + 7.9581744110252191462569661e+02, + 7.6019905892596359262696423e+00, + 3.7506882048388096973183084e+01, + 6.6250893439173561733216375e+00, + 3.5438267900243941544605339e+00, + 2.4281533133513300984289196e-03, +} + +var _fabs = []float64{ + 4.9790119248836735e+00, + 7.7388724745781045e+00, + 2.7688005719200159e-01, + 5.0106036182710749e+00, 9.6362937071984173e+00, 2.9263772392439646e+00, 5.2290834314593066e+00, 2.7279399104360102e+00, 1.8253080916808550e+00, - -8.6859247685756013e+00, + 8.6859247685756013e+00, +} + +var _fdim = []float64{ + 4.9790119248836735e+00, + 7.7388724745781045e+00, + 0.0000000000000000e+00, + 0.0000000000000000e+00, + 9.6362937071984173e+00, + 2.9263772392439646e+00, + 5.2290834314593066e+00, + 2.7279399104360102e+00, + 1.8253080916808550e+00, + 0.0000000000000000e+00, +} + +var _floor = []float64{ + 4.0000000000000000e+00, + 7.0000000000000000e+00, + -1.0000000000000000e+00, + -6.0000000000000000e+00, + 9.0000000000000000e+00, + 2.0000000000000000e+00, + 5.0000000000000000e+00, + 2.0000000000000000e+00, + 1.0000000000000000e+00, + -9.0000000000000000e+00, +} + +var _fmod = []float64{ + 4.197615023265299782906368e-02, + 2.261127525421895434476482e+00, + 3.231794108794261433104108e-02, + 4.989396381728925078391512e+00, + 3.637062928015826201999516e-01, + 1.220868282268106064236690e+00, + 4.770916568540693347699744e+00, + 1.816180268691969246219742e+00, + 8.734595415957246977711748e-01, + 1.314075231424398637614104e+00, +} + +type fi struct { + f float64 + i int +} + +var _frexp = []fi{ + {6.2237649061045918750e-01, 3}, + {9.6735905932226306250e-01, 3}, + {-5.5376011438400318000e-01, -1}, + {-6.2632545228388436250e-01, 3}, + {6.02268356699901081250e-01, 4}, + {7.3159430981099115000e-01, 2}, + {6.5363542893241332500e-01, 3}, + {6.8198497760900255000e-01, 2}, + {9.1265404584042750000e-01, 1}, + {-5.4287029803597508250e-01, 4}, +} + +var _gamma = []float64{ + 2.3254348370739963835386613898e+01, + 2.991153837155317076427529816e+03, + -4.561154336726758060575129109e+00, + 7.719403468842639065959210984e-01, + 1.6111876618855418534325755566e+05, + 1.8706575145216421164173224946e+00, + 3.4082787447257502836734201635e+01, + 1.579733951448952054898583387e+00, + 9.3834586598354592860187267089e-01, + -2.093995902923148389186189429e-05, +} + +var _j0 = []float64{ + -1.8444682230601672018219338e-01, + 2.27353668906331975435892e-01, + 9.809259936157051116270273e-01, + -1.741170131426226587841181e-01, + -2.1389448451144143352039069e-01, + -2.340905848928038763337414e-01, + -1.0029099691890912094586326e-01, + -1.5466726714884328135358907e-01, + 3.252650187653420388714693e-01, + -8.72218484409407250005360235e-03, +} + +var _j1 = []float64{ + -3.251526395295203422162967e-01, + 1.893581711430515718062564e-01, + -1.3711761352467242914491514e-01, + 3.287486536269617297529617e-01, + 1.3133899188830978473849215e-01, + 3.660243417832986825301766e-01, + -3.4436769271848174665420672e-01, + 4.329481396640773768835036e-01, + 5.8181350531954794639333955e-01, + -2.7030574577733036112996607e-01, +} + +var _j2 = []float64{ + 5.3837518920137802565192769e-02, + -1.7841678003393207281244667e-01, + 9.521746934916464142495821e-03, + 4.28958355470987397983072e-02, + 2.4115371837854494725492872e-01, + 4.842458532394520316844449e-01, + -3.142145220618633390125946e-02, + 4.720849184745124761189957e-01, + 3.122312022520957042957497e-01, + 7.096213118930231185707277e-02, +} + +var _jM3 = []float64{ + -3.684042080996403091021151e-01, + 2.8157665936340887268092661e-01, + 4.401005480841948348343589e-04, + 3.629926999056814081597135e-01, + 3.123672198825455192489266e-02, + -2.958805510589623607540455e-01, + -3.2033177696533233403289416e-01, + -2.592737332129663376736604e-01, + -1.0241334641061485092351251e-01, + -2.3762660886100206491674503e-01, +} + +var _lgamma = []fi{ + {3.146492141244545774319734e+00, 1}, + {8.003414490659126375852113e+00, 1}, + {1.517575735509779707488106e+00, -1}, + {-2.588480028182145853558748e-01, 1}, + {1.1989897050205555002007985e+01, 1}, + {6.262899811091257519386906e-01, 1}, + {3.5287924899091566764846037e+00, 1}, + {4.5725644770161182299423372e-01, 1}, + {-6.363667087767961257654854e-02, 1}, + {-1.077385130910300066425564e+01, -1}, +} + +var _log = []float64{ + 1.605231462693062999102599e+00, + 2.0462560018708770653153909e+00, + -1.2841708730962657801275038e+00, + 1.6115563905281545116286206e+00, + 2.2655365644872016636317461e+00, + 1.0737652208918379856272735e+00, + 1.6542360106073546632707956e+00, + 1.0035467127723465801264487e+00, + 6.0174879014578057187016475e-01, + 2.161703872847352815363655e+00, +} + +var _logb = []float64{ + 2.0000000000000000e+00, + 2.0000000000000000e+00, + -2.0000000000000000e+00, + 2.0000000000000000e+00, + 3.0000000000000000e+00, + 1.0000000000000000e+00, + 2.0000000000000000e+00, + 1.0000000000000000e+00, + 0.0000000000000000e+00, + 3.0000000000000000e+00, +} + +var _log10 = []float64{ + 6.9714316642508290997617083e-01, + 8.886776901739320576279124e-01, + -5.5770832400658929815908236e-01, + 6.998900476822994346229723e-01, + 9.8391002850684232013281033e-01, + 4.6633031029295153334285302e-01, + 7.1842557117242328821552533e-01, + 4.3583479968917773161304553e-01, + 2.6133617905227038228626834e-01, + 9.3881606348649405716214241e-01, +} + +var _log1p = []float64{ + 4.8590257759797794104158205e-02, + 7.4540265965225865330849141e-02, + -2.7726407903942672823234024e-03, + -5.1404917651627649094953380e-02, + 9.1998280672258624681335010e-02, + 2.8843762576593352865894824e-02, + 5.0969534581863707268992645e-02, + 2.6913947602193238458458594e-02, + 1.8088493239630770262045333e-02, + -9.0865245631588989681559268e-02, } -var signbit = []bool{ +var _log2 = []float64{ + 2.3158594707062190618898251e+00, + 2.9521233862883917703341018e+00, + -1.8526669502700329984917062e+00, + 2.3249844127278861543568029e+00, + 3.268478366538305087466309e+00, + 1.5491157592596970278166492e+00, + 2.3865580889631732407886495e+00, + 1.447811865817085365540347e+00, + 8.6813999540425116282815557e-01, + 3.118679457227342224364709e+00, +} + +var _modf = [][2]float64{ + {4.0000000000000000e+00, 9.7901192488367350108546816e-01}, + {7.0000000000000000e+00, 7.3887247457810456552351752e-01}, + {Copysign(0, -1), -2.7688005719200159404635997e-01}, + {-5.0000000000000000e+00, -1.060361827107492160848778e-02}, + {9.0000000000000000e+00, 6.3629370719841737980004837e-01}, + {2.0000000000000000e+00, 9.2637723924396464525443662e-01}, + {5.0000000000000000e+00, 2.2908343145930665230025625e-01}, + {2.0000000000000000e+00, 7.2793991043601025126008608e-01}, + {1.0000000000000000e+00, 8.2530809168085506044576505e-01}, + {-8.0000000000000000e+00, -6.8592476857560136238589621e-01}, +} + +var _nextafter32 = []float32{ + 4.979012489318848e+00, + 7.738873004913330e+00, + -2.768800258636475e-01, + -5.010602951049805e+00, + 9.636294364929199e+00, + 2.926377534866333e+00, + 5.229084014892578e+00, + 2.727940082550049e+00, + 1.825308203697205e+00, + -8.685923576354980e+00, +} + +var _nextafter64 = []float64{ + 4.97901192488367438926388786e+00, + 7.73887247457810545370193722e+00, + -2.7688005719200153853520874e-01, + -5.01060361827107403343006808e+00, + 9.63629370719841915615688777e+00, + 2.92637723924396508934364647e+00, + 5.22908343145930754047867595e+00, + 2.72793991043601069534929593e+00, + 1.82530809168085528249036997e+00, + -8.68592476857559958602905681e+00, +} + +var _pow = []float64{ + 9.5282232631648411840742957e+04, + 5.4811599352999901232411871e+07, + 5.2859121715894396531132279e-01, + 9.7587991957286474464259698e-06, + 4.328064329346044846740467e+09, + 8.4406761805034547437659092e+02, + 1.6946633276191194947742146e+05, + 5.3449040147551939075312879e+02, + 6.688182138451414936380374e+01, + 2.0609869004248742886827439e-09, +} + +var _remainder = []float64{ + 4.197615023265299782906368e-02, + 2.261127525421895434476482e+00, + 3.231794108794261433104108e-02, + -2.120723654214984321697556e-02, + 3.637062928015826201999516e-01, + 1.220868282268106064236690e+00, + -4.581668629186133046005125e-01, + -9.117596417440410050403443e-01, + 8.734595415957246977711748e-01, + 1.314075231424398637614104e+00, +} + +var _round = []float64{ + 5, + 8, + Copysign(0, -1), + -5, + 10, + 3, + 5, + 3, + 2, + -9, +} + +var _signbit = []bool{ false, false, true, @@ -39,7 +592,7 @@ var signbit = []bool{ true, } -var sin = []float64{ +var _sinTest = []float64{ -9.6466616586009283766724726e-01, 9.9338225271646545763467022e-01, -2.7335587039794393342449301e-01, @@ -52,7 +605,199 @@ var sin = []float64{ -6.734405869050344734943028e-01, } -var vfsignbitSC = []float64{ +// Results for 100000 * Pi + _vf[i] +var _sinTestLarge = []float64{ + -9.646661658548936063912e-01, + 9.933822527198506903752e-01, + -2.7335587036246899796e-01, + 9.55862576853689321268e-01, + -2.099421066862688873691e-01, + 2.13557878070308981163e-01, + -8.694568970959221300497e-01, + 4.01956668098863248917e-01, + 9.67786335404528727927e-01, + -6.7344058693131973066e-01, +} + +var _sinTesth = []float64{ + 7.2661916084208532301448439e+01, + 1.1479409110035194500526446e+03, + -2.8043136512812518927312641e-01, + -7.499429091181587232835164e+01, + 7.6552466042906758523925934e+03, + 9.3031583421672014313789064e+00, + 9.330815755828109072810322e+01, + 7.6179893137269146407361477e+00, + 3.021769180549615819524392e+00, + -2.95950575724449499189888e+03, +} + +var _sqrt = []float64{ + 2.2313699659365484748756904e+00, + 2.7818829009464263511285458e+00, + 5.2619393496314796848143251e-01, + 2.2384377628763938724244104e+00, + 3.1042380236055381099288487e+00, + 1.7106657298385224403917771e+00, + 2.286718922705479046148059e+00, + 1.6516476350711159636222979e+00, + 1.3510396336454586262419247e+00, + 2.9471892997524949215723329e+00, +} + +var _tan = []float64{ + -3.661316565040227801781974e+00, + 8.64900232648597589369854e+00, + -2.8417941955033612725238097e-01, + 3.253290185974728640827156e+00, + 2.147275640380293804770778e-01, + -2.18600910711067004921551e-01, + -1.760002817872367935518928e+00, + -4.389808914752818126249079e-01, + -3.843885560201130679995041e+00, + 9.10988793377685105753416e-01, +} + +// Results for 100000 * Pi + _vf[i] +var _tanLarge = []float64{ + -3.66131656475596512705e+00, + 8.6490023287202547927e+00, + -2.841794195104782406e-01, + 3.2532901861033120983e+00, + 2.14727564046880001365e-01, + -2.18600910700688062874e-01, + -1.760002817699722747043e+00, + -4.38980891453536115952e-01, + -3.84388555942723509071e+00, + 9.1098879344275101051e-01, +} + +var _tanh = []float64{ + 9.9990531206936338549262119e-01, + 9.9999962057085294197613294e-01, + -2.7001505097318677233756845e-01, + -9.9991110943061718603541401e-01, + 9.9999999146798465745022007e-01, + 9.9427249436125236705001048e-01, + 9.9994257600983138572705076e-01, + 9.9149409509772875982054701e-01, + 9.4936501296239685514466577e-01, + -9.9999994291374030946055701e-01, +} + +var _trunc = []float64{ + 4.0000000000000000e+00, + 7.0000000000000000e+00, + Copysign(0, -1), + -5.0000000000000000e+00, + 9.0000000000000000e+00, + 2.0000000000000000e+00, + 5.0000000000000000e+00, + 2.0000000000000000e+00, + 1.0000000000000000e+00, + -8.0000000000000000e+00, +} + +var _y0 = []float64{ + -3.053399153780788357534855e-01, + 1.7437227649515231515503649e-01, + -8.6221781263678836910392572e-01, + -3.100664880987498407872839e-01, + 1.422200649300982280645377e-01, + 4.000004067997901144239363e-01, + -3.3340749753099352392332536e-01, + 4.5399790746668954555205502e-01, + 4.8290004112497761007536522e-01, + 2.7036697826604756229601611e-01, +} + +var _y1 = []float64{ + 0.15494213737457922210218611, + -0.2165955142081145245075746, + -2.4644949631241895201032829, + 0.1442740489541836405154505, + 0.2215379960518984777080163, + 0.3038800915160754150565448, + 0.0691107642452362383808547, + 0.2380116417809914424860165, + -0.20849492979459761009678934, + 0.0242503179793232308250804, +} + +var _y2 = []float64{ + 0.3675780219390303613394936, + -0.23034826393250119879267257, + -16.939677983817727205631397, + 0.367653980523052152867791, + -0.0962401471767804440353136, + -0.1923169356184851105200523, + 0.35984072054267882391843766, + -0.2794987252299739821654982, + -0.7113490692587462579757954, + -0.2647831587821263302087457, +} + +var _yM3 = []float64{ + -0.14035984421094849100895341, + -0.097535139617792072703973, + 242.25775994555580176377379, + -0.1492267014802818619511046, + 0.26148702629155918694500469, + 0.56675383593895176530394248, + -0.206150264009006981070575, + 0.64784284687568332737963658, + 1.3503631555901938037008443, + 0.1461869756579956803341844, +} + +// arguments and expected results for special cases +var _vfacosSC = []float64{ + -Pi, + 1, + Pi, + NaN(), +} + +var _acosSC = []float64{ + NaN(), + 0, + NaN(), + NaN(), +} + +var _vfacoshSC = []float64{ + Inf(-1), + 0.5, + 1, + Inf(1), + NaN(), +} + +var _acoshSC = []float64{ + NaN(), + NaN(), + 0, + Inf(1), + NaN(), +} + +var _vfasinSC = []float64{ + -Pi, + Copysign(0, -1), + 0, + Pi, + NaN(), +} + +var _asinSC = []float64{ + NaN(), + Copysign(0, -1), + 0, + NaN(), + NaN(), +} + +var _vfasinhSC = []float64{ Inf(-1), Copysign(0, -1), 0, @@ -60,87 +805,3078 @@ var vfsignbitSC = []float64{ NaN(), } -var signbitSC = []bool{ - true, - true, - false, - false, - false, +var _asinhSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), +} + +var _vfatanSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), } -var vfsinSC = []float64{ +var _atanSC = []float64{ + -Pi / 2, + Copysign(0, -1), + 0, + Pi / 2, + NaN(), +} + +var _vfatanhSC = []float64{ Inf(-1), + -Pi, + -1, Copysign(0, -1), 0, + 1, + Pi, Inf(1), NaN(), } -var sinSC = []float64{ +var _atanhSC = []float64{ + NaN(), NaN(), + Inf(-1), Copysign(0, -1), 0, + Inf(1), + NaN(), NaN(), NaN(), } -func tolerance(a, b, e float64) bool { - // Multiplying by e here can underflow denormal values to zero. - // Check a==b so that at least if a and b are small and identical - // we say they match. - if a == b { - return true - } - d := a - b - if d < 0 { - d = -d - } +var _vfatan2SC = [][2]float64{ + {Inf(-1), Inf(-1)}, + {Inf(-1), -Pi}, + {Inf(-1), 0}, + {Inf(-1), +Pi}, + {Inf(-1), Inf(1)}, + {Inf(-1), NaN()}, + {-Pi, Inf(-1)}, + {-Pi, 0}, + {-Pi, Inf(1)}, + {-Pi, NaN()}, + {Copysign(0, -1), Inf(-1)}, + {Copysign(0, -1), -Pi}, + {Copysign(0, -1), Copysign(0, -1)}, + {Copysign(0, -1), 0}, + {Copysign(0, -1), +Pi}, + {Copysign(0, -1), Inf(1)}, + {Copysign(0, -1), NaN()}, + {0, Inf(-1)}, + {0, -Pi}, + {0, Copysign(0, -1)}, + {0, 0}, + {0, +Pi}, + {0, Inf(1)}, + {0, NaN()}, + {+Pi, Inf(-1)}, + {+Pi, 0}, + {+Pi, Inf(1)}, + {1.0, Inf(1)}, + {-1.0, Inf(1)}, + {+Pi, NaN()}, + {Inf(1), Inf(-1)}, + {Inf(1), -Pi}, + {Inf(1), 0}, + {Inf(1), +Pi}, + {Inf(1), Inf(1)}, + {Inf(1), NaN()}, + {NaN(), NaN()}, +} - // note: b is correct (expected) value, a is actual value. - // make error tolerance a fraction of b, not a. - if b != 0 { - e = e * b - if e < 0 { - e = -e - } - } - return d < e +var _atan2SC = []float64{ + -3.0 * Pi / 4, // atan2(-Inf, -Inf) + -Pi / 2, // atan2(-Inf, -Pi) + -Pi / 2, // atan2(-Inf, +0) + -Pi / 2, // atan2(-Inf, +Pi) + -Pi / 4, // atan2(-Inf, +Inf) + NaN(), // atan2(-Inf, NaN) + -Pi, // atan2(-Pi, -Inf) + -Pi / 2, // atan2(-Pi, +0) + Copysign(0, -1), // atan2(-Pi, Inf) + NaN(), // atan2(-Pi, NaN) + -Pi, // atan2(-0, -Inf) + -Pi, // atan2(-0, -Pi) + -Pi, // atan2(-0, -0) + Copysign(0, -1), // atan2(-0, +0) + Copysign(0, -1), // atan2(-0, +Pi) + Copysign(0, -1), // atan2(-0, +Inf) + NaN(), // atan2(-0, NaN) + Pi, // atan2(+0, -Inf) + Pi, // atan2(+0, -Pi) + Pi, // atan2(+0, -0) + 0, // atan2(+0, +0) + 0, // atan2(+0, +Pi) + 0, // atan2(+0, +Inf) + NaN(), // atan2(+0, NaN) + Pi, // atan2(+Pi, -Inf) + Pi / 2, // atan2(+Pi, +0) + 0, // atan2(+Pi, +Inf) + 0, // atan2(+1, +Inf) + Copysign(0, -1), // atan2(-1, +Inf) + NaN(), // atan2(+Pi, NaN) + 3 * Pi / 4, // atan2(+Inf, -Inf) + Pi / 2, // atan2(+Inf, -Pi) + Pi / 2, // atan2(+Inf, +0) + Pi / 2, // atan2(+Inf, +Pi) + Pi / 4, // atan2(+Inf, +Inf) + NaN(), // atan2(+Inf, NaN) + NaN(), // atan2(NaN, NaN) } -func close(a, b float64) bool { return tolerance(a, b, 1e-14) } -func veryclose(a, b float64) bool { return tolerance(a, b, 4e-16) } -func soclose(a, b, e float64) bool { return tolerance(a, b, e) } -func alike(a, b float64) bool { - switch { - case IsNaN(a) && IsNaN(b): - return true - case a == b: - return Signbit(a) == Signbit(b) - } - return false + +var _vfcbrtSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), } -func TestSin(t *testing.T) { - for i := 0; i < len(vf); i++ { - if f := Sin(vf[i]); !veryclose(sin[i], f) { - t.Errorf("Sin(%g) = %g, want %g", vf[i], f, sin[i]) - } - } - for i := 0; i < len(vfsinSC); i++ { - if f := Sin(vfsinSC[i]); !alike(sinSC[i], f) { - t.Errorf("Sin(%g) = %g, want %g", vfsinSC[i], f, sinSC[i]) - } - } +var _cbrtSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), } -func TestSignbit(t *testing.T) { - for i := 0; i < len(vf); i++ { - if f := Signbit(vf[i]); signbit[i] != f { - t.Errorf("Signbit(%g) = %t, want %t", vf[i], f, signbit[i]) - } - } - for i := 0; i < len(vfsignbitSC); i++ { - if f := Signbit(vfsignbitSC[i]); signbitSC[i] != f { - t.Errorf("Signbit(%g) = %t, want %t", vfsignbitSC[i], f, signbitSC[i]) - } +var _vfceilSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), +} + +var _ceilSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), +} + +var _vfcopysignSC = []float64{ + Inf(-1), + Inf(1), + NaN(), +} + +var _copysignSC = []float64{ + Inf(-1), + Inf(-1), + NaN(), +} + +var _vfcosSC = []float64{ + Inf(-1), + Inf(1), + NaN(), +} + +var _testCosSC = []float64{ + NaN(), + NaN(), + NaN(), +} + +var _vfcoshSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), +} + +var _testCoshSC = []float64{ + Inf(1), + 1, + 1, + Inf(1), + NaN(), +} + +var _vferfSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), + -1000, + 1000, +} + +var _erfSC = []float64{ + -1, + Copysign(0, -1), + 0, + 1, + NaN(), + -1, + 1, +} + +var _vferfcSC = []float64{ + Inf(-1), + Inf(1), + NaN(), + -1000, + 1000, +} + +var _erfcSC = []float64{ + 2, + 0, + NaN(), + 2, + 0, +} + +var _vferfinvSC = []float64{ + 1, + -1, + 0, + Inf(-1), + Inf(1), + NaN(), +} + +var _erfinvSC = []float64{ + Inf(+1), + Inf(-1), + 0, + NaN(), + NaN(), + NaN(), +} + +var _vferfcinvSC = []float64{ + 0, + 2, + 1, + Inf(1), + Inf(-1), + NaN(), +} + +var _erfcinvSC = []float64{ + Inf(+1), + Inf(-1), + 0, + NaN(), + NaN(), + NaN(), +} + +var _vfexpSC = []float64{ + Inf(-1), + -2000, + 2000, + Inf(1), + NaN(), + // smallest float64 that overflows Exp(x) + 7.097827128933841e+02, + // Issue 18912 + 1.48852223e+09, + 1.4885222e+09, + 1, + // near zero + 3.725290298461915e-09, + // denormal + -740, +} + +var _expSC = []float64{ + 0, + 0, + Inf(1), + Inf(1), + NaN(), + Inf(1), + Inf(1), + Inf(1), + 2.718281828459045, + 1.0000000037252903, + 4.2e-322, +} + +var _vfexp2SC = []float64{ + Inf(-1), + -2000, + 2000, + Inf(1), + NaN(), + // smallest float64 that overflows Exp2(x) + 1024, + // near underflow + -1.07399999999999e+03, + // near zero + 3.725290298461915e-09, +} + +var _exp2SC = []float64{ + 0, + 0, + Inf(1), + Inf(1), + NaN(), + Inf(1), + 5e-324, + 1.0000000025821745, +} + +var _vfexpm1SC = []float64{ + Inf(-1), + -710, + Copysign(0, -1), + 0, + 710, + Inf(1), + NaN(), +} + +var _expm1SC = []float64{ + -1, + -1, + Copysign(0, -1), + 0, + Inf(1), + Inf(1), + NaN(), +} + +var _vffabsSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), +} + +var _fabsSC = []float64{ + Inf(1), + 0, + 0, + Inf(1), + NaN(), +} + +var _vffdimSC = [][2]float64{ + {Inf(-1), Inf(-1)}, + {Inf(-1), Inf(1)}, + {Inf(-1), NaN()}, + {Copysign(0, -1), Copysign(0, -1)}, + {Copysign(0, -1), 0}, + {0, Copysign(0, -1)}, + {0, 0}, + {Inf(1), Inf(-1)}, + {Inf(1), Inf(1)}, + {Inf(1), NaN()}, + {NaN(), Inf(-1)}, + {NaN(), Copysign(0, -1)}, + {NaN(), 0}, + {NaN(), Inf(1)}, + {NaN(), NaN()}, +} + +var ( + nan = Float64frombits(0xFFF8000000000000) // SSE2 DIVSD 0/0 + _vffdim2SC = [][2]float64{ + {Inf(-1), Inf(-1)}, + {Inf(-1), Inf(1)}, + {Inf(-1), nan}, + {Copysign(0, -1), Copysign(0, -1)}, + {Copysign(0, -1), 0}, + {0, Copysign(0, -1)}, + {0, 0}, + {Inf(1), Inf(-1)}, + {Inf(1), Inf(1)}, + {Inf(1), nan}, + {nan, Inf(-1)}, + {nan, Copysign(0, -1)}, + {nan, 0}, + {nan, Inf(1)}, + {nan, nan}, + } +) + +var _fdimSC = []float64{ + NaN(), + 0, + NaN(), + 0, + 0, + 0, + 0, + Inf(1), + NaN(), + NaN(), + NaN(), + NaN(), + NaN(), + NaN(), + NaN(), +} + +var _fmaxSC = []float64{ + Inf(-1), + Inf(1), + NaN(), + Copysign(0, -1), + 0, + 0, + 0, + Inf(1), + Inf(1), + Inf(1), + NaN(), + NaN(), + NaN(), + Inf(1), + NaN(), +} + +var _fminSC = []float64{ + Inf(-1), + Inf(-1), + Inf(-1), + Copysign(0, -1), + Copysign(0, -1), + Copysign(0, -1), + 0, + Inf(-1), + Inf(1), + NaN(), + Inf(-1), + NaN(), + NaN(), + NaN(), + NaN(), +} + +var _vffmodSC = [][2]float64{ + {Inf(-1), Inf(-1)}, + {Inf(-1), -Pi}, + {Inf(-1), 0}, + {Inf(-1), Pi}, + {Inf(-1), Inf(1)}, + {Inf(-1), NaN()}, + {-Pi, Inf(-1)}, + {-Pi, 0}, + {-Pi, Inf(1)}, + {-Pi, NaN()}, + {Copysign(0, -1), Inf(-1)}, + {Copysign(0, -1), 0}, + {Copysign(0, -1), Inf(1)}, + {Copysign(0, -1), NaN()}, + {0, Inf(-1)}, + {0, 0}, + {0, Inf(1)}, + {0, NaN()}, + {Pi, Inf(-1)}, + {Pi, 0}, + {Pi, Inf(1)}, + {Pi, NaN()}, + {Inf(1), Inf(-1)}, + {Inf(1), -Pi}, + {Inf(1), 0}, + {Inf(1), Pi}, + {Inf(1), Inf(1)}, + {Inf(1), NaN()}, + {NaN(), Inf(-1)}, + {NaN(), -Pi}, + {NaN(), 0}, + {NaN(), Pi}, + {NaN(), Inf(1)}, + {NaN(), NaN()}, +} + +var _fmodSC = []float64{ + NaN(), // fmod(-Inf, -Inf) + NaN(), // fmod(-Inf, -Pi) + NaN(), // fmod(-Inf, 0) + NaN(), // fmod(-Inf, Pi) + NaN(), // fmod(-Inf, +Inf) + NaN(), // fmod(-Inf, NaN) + -Pi, // fmod(-Pi, -Inf) + NaN(), // fmod(-Pi, 0) + -Pi, // fmod(-Pi, +Inf) + NaN(), // fmod(-Pi, NaN) + Copysign(0, -1), // fmod(-0, -Inf) + NaN(), // fmod(-0, 0) + Copysign(0, -1), // fmod(-0, Inf) + NaN(), // fmod(-0, NaN) + 0, // fmod(0, -Inf) + NaN(), // fmod(0, 0) + 0, // fmod(0, +Inf) + NaN(), // fmod(0, NaN) + Pi, // fmod(Pi, -Inf) + NaN(), // fmod(Pi, 0) + Pi, // fmod(Pi, +Inf) + NaN(), // fmod(Pi, NaN) + NaN(), // fmod(+Inf, -Inf) + NaN(), // fmod(+Inf, -Pi) + NaN(), // fmod(+Inf, 0) + NaN(), // fmod(+Inf, Pi) + NaN(), // fmod(+Inf, +Inf) + NaN(), // fmod(+Inf, NaN) + NaN(), // fmod(NaN, -Inf) + NaN(), // fmod(NaN, -Pi) + NaN(), // fmod(NaN, 0) + NaN(), // fmod(NaN, Pi) + NaN(), // fmod(NaN, +Inf) + NaN(), // fmod(NaN, NaN) +} + +var _vffrexpSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), +} + +var _frexpSC = []fi{ + {Inf(-1), 0}, + {Copysign(0, -1), 0}, + {0, 0}, + {Inf(1), 0}, + {NaN(), 0}, +} + +var _vfgamma = [][2]float64{ + {Inf(1), Inf(1)}, + {Inf(-1), NaN()}, + {0, Inf(1)}, + {Copysign(0, -1), Inf(-1)}, + {NaN(), NaN()}, + {-1, NaN()}, + {-2, NaN()}, + {-3, NaN()}, + {-1e16, NaN()}, + {-1e300, NaN()}, + {1.7e308, Inf(1)}, + + // Test inputs inspired by Python test suite. + // Outputs computed at high precision by PARI/GP. + // If recomputing table entries, be careful to use + // high-precision (%.1000g) formatting of the float64 inputs. + // For example, -2.0000000000000004 is the float64 with exact value + // -2.00000000000000044408920985626161695, and + // gamma(-2.0000000000000004) = -1249999999999999.5386078562728167651513, while + // gamma(-2.00000000000000044408920985626161695) = -1125899906826907.2044875028130093136826. + // Thus the table lists -1.1258999068426235e+15 as the answer. + {0.5, 1.772453850905516}, + {1.5, 0.886226925452758}, + {2.5, 1.329340388179137}, + {3.5, 3.3233509704478426}, + {-0.5, -3.544907701811032}, + {-1.5, 2.363271801207355}, + {-2.5, -0.9453087204829419}, + {-3.5, 0.2700882058522691}, + {0.1, 9.51350769866873}, + {0.01, 99.4325851191506}, + {1e-08, 9.999999942278434e+07}, + {1e-16, 1e+16}, + {0.001, 999.4237724845955}, + {1e-16, 1e+16}, + {1e-308, 1e+308}, + {5.6e-309, 1.7857142857142864e+308}, + {5.5e-309, Inf(1)}, + {1e-309, Inf(1)}, + {1e-323, Inf(1)}, + {5e-324, Inf(1)}, + {-0.1, -10.686287021193193}, + {-0.01, -100.58719796441078}, + {-1e-08, -1.0000000057721567e+08}, + {-1e-16, -1e+16}, + {-0.001, -1000.5782056293586}, + {-1e-16, -1e+16}, + {-1e-308, -1e+308}, + {-5.6e-309, -1.7857142857142864e+308}, + {-5.5e-309, Inf(-1)}, + {-1e-309, Inf(-1)}, + {-1e-323, Inf(-1)}, + {-5e-324, Inf(-1)}, + {-0.9999999999999999, -9.007199254740992e+15}, + {-1.0000000000000002, 4.5035996273704955e+15}, + {-1.9999999999999998, 2.2517998136852485e+15}, + {-2.0000000000000004, -1.1258999068426235e+15}, + {-100.00000000000001, -7.540083334883109e-145}, + {-99.99999999999999, 7.540083334884096e-145}, + {17, 2.0922789888e+13}, + {171, 7.257415615307999e+306}, + {171.6, 1.5858969096672565e+308}, + {171.624, 1.7942117599248104e+308}, + {171.625, Inf(1)}, + {172, Inf(1)}, + {2000, Inf(1)}, + {-100.5, -3.3536908198076787e-159}, + {-160.5, -5.255546447007829e-286}, + {-170.5, -3.3127395215386074e-308}, + {-171.5, 1.9316265431712e-310}, + {-176.5, -1.196e-321}, + {-177.5, 5e-324}, + {-178.5, Copysign(0, -1)}, + {-179.5, 0}, + {-201.0001, 0}, + {-202.9999, Copysign(0, -1)}, + {-1000.5, Copysign(0, -1)}, + {-1.0000000003e+09, Copysign(0, -1)}, + {-4.5035996273704955e+15, 0}, + {-63.349078729022985, 4.177797167776188e-88}, + {-127.45117632943295, 1.183111089623681e-214}, +} + +var _vfhypotSC = [][2]float64{ + {Inf(-1), Inf(-1)}, + {Inf(-1), 0}, + {Inf(-1), Inf(1)}, + {Inf(-1), NaN()}, + {Copysign(0, -1), Copysign(0, -1)}, + {Copysign(0, -1), 0}, + {0, Copysign(0, -1)}, + {0, 0}, // +0, +0 + {0, Inf(-1)}, + {0, Inf(1)}, + {0, NaN()}, + {Inf(1), Inf(-1)}, + {Inf(1), 0}, + {Inf(1), Inf(1)}, + {Inf(1), NaN()}, + {NaN(), Inf(-1)}, + {NaN(), 0}, + {NaN(), Inf(1)}, + {NaN(), NaN()}, +} + +var _hypotSC = []float64{ + Inf(1), + Inf(1), + Inf(1), + Inf(1), + 0, + 0, + 0, + 0, + Inf(1), + Inf(1), + NaN(), + Inf(1), + Inf(1), + Inf(1), + Inf(1), + Inf(1), + NaN(), + Inf(1), + NaN(), +} + +var _ilogbSC = []int{ + MaxInt32, + MinInt32, + MaxInt32, + MaxInt32, +} + +var _vfj0SC = []float64{ + Inf(-1), + 0, + Inf(1), + NaN(), +} + +var _j0SC = []float64{ + 0, + 1, + 0, + NaN(), +} + +var _j1SC = []float64{ + 0, + 0, + 0, + NaN(), +} + +var _j2SC = []float64{ + 0, + 0, + 0, + NaN(), +} + +var _jM3SC = []float64{ + 0, + 0, + 0, + NaN(), +} + +var _vfldexpSC = []fi{ + {0, 0}, + {0, -1075}, + {0, 1024}, + {Copysign(0, -1), 0}, + {Copysign(0, -1), -1075}, + {Copysign(0, -1), 1024}, + {Inf(1), 0}, + {Inf(1), -1024}, + {Inf(-1), 0}, + {Inf(-1), -1024}, + {NaN(), -1024}, + {10, int(1) << (uint64(7) * 8)}, + {10, -(int(1) << (uint64(7) * 8))}, +} + +var _ldexpSC = []float64{ + 0, + 0, + 0, + Copysign(0, -1), + Copysign(0, -1), + Copysign(0, -1), + Inf(1), + Inf(1), + Inf(-1), + Inf(-1), + NaN(), + Inf(1), + 0, +} + +var _vflgammaSC = []float64{ + Inf(-1), + -3, + 0, + 1, + 2, + Inf(1), + NaN(), +} + +var _lgammaSC = []fi{ + {Inf(-1), 1}, + {Inf(1), 1}, + {Inf(1), 1}, + {0, 1}, + {0, 1}, + {Inf(1), 1}, + {NaN(), 1}, +} + +var _vflogSC = []float64{ + Inf(-1), + -Pi, + Copysign(0, -1), + 0, + 1, + Inf(1), + NaN(), +} + +var _logSC = []float64{ + NaN(), + NaN(), + Inf(-1), + Inf(-1), + 0, + Inf(1), + NaN(), +} + +var _vflogbSC = []float64{ + Inf(-1), + 0, + Inf(1), + NaN(), +} + +var _logbSC = []float64{ + Inf(1), + Inf(-1), + Inf(1), + NaN(), +} + +var _vflog1pSC = []float64{ + Inf(-1), + -Pi, + -1, + Copysign(0, -1), + 0, + Inf(1), + NaN(), + 4503599627370496.5, // Issue #29488 +} + +var _log1pSC = []float64{ + NaN(), + NaN(), + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), + 36.04365338911715, // Issue #29488 +} + +var _vfmodfSC = []float64{ + Inf(-1), + Copysign(0, -1), + Inf(1), + NaN(), +} + +var _modfSC = [][2]float64{ + {Inf(-1), NaN()}, // [2]float64{Copysign(0, -1), Inf(-1)}, + {Copysign(0, -1), Copysign(0, -1)}, + {Inf(1), NaN()}, // [2]float64{0, Inf(1)}, + {NaN(), NaN()}, +} + +var _vfnextafter32SC = [][2]float32{ + {0, 0}, + {0, float32(Copysign(0, -1))}, + {0, -1}, + {0, float32(NaN())}, + {float32(Copysign(0, -1)), 1}, + {float32(Copysign(0, -1)), 0}, + {float32(Copysign(0, -1)), float32(Copysign(0, -1))}, + {float32(Copysign(0, -1)), -1}, + {float32(NaN()), 0}, + {float32(NaN()), float32(NaN())}, +} + +var _nextafter32SC = []float32{ + 0, + 0, + -1.401298464e-45, // Float32frombits(0x80000001) + float32(NaN()), + 1.401298464e-45, // Float32frombits(0x00000001) + float32(Copysign(0, -1)), + float32(Copysign(0, -1)), + -1.401298464e-45, // Float32frombits(0x80000001) + float32(NaN()), + float32(NaN()), +} + +var _vfnextafter64SC = [][2]float64{ + {0, 0}, + {0, Copysign(0, -1)}, + {0, -1}, + {0, NaN()}, + {Copysign(0, -1), 1}, + {Copysign(0, -1), 0}, + {Copysign(0, -1), Copysign(0, -1)}, + {Copysign(0, -1), -1}, + {NaN(), 0}, + {NaN(), NaN()}, +} + +var _nextafter64SC = []float64{ + 0, + 0, + -4.9406564584124654418e-324, // Float64frombits(0x8000000000000001) + NaN(), + 4.9406564584124654418e-324, // Float64frombits(0x0000000000000001) + Copysign(0, -1), + Copysign(0, -1), + -4.9406564584124654418e-324, // Float64frombits(0x8000000000000001) + NaN(), + NaN(), +} + +var _vfpowSC = [][2]float64{ + {Inf(-1), -Pi}, + {Inf(-1), -3}, + {Inf(-1), Copysign(0, -1)}, + {Inf(-1), 0}, + {Inf(-1), 1}, + {Inf(-1), 3}, + {Inf(-1), Pi}, + {Inf(-1), 0.5}, + {Inf(-1), NaN()}, + + {-Pi, Inf(-1)}, + {-Pi, -Pi}, + {-Pi, Copysign(0, -1)}, + {-Pi, 0}, + {-Pi, 1}, + {-Pi, Pi}, + {-Pi, Inf(1)}, + {-Pi, NaN()}, + + {-1, Inf(-1)}, + {-1, Inf(1)}, + {-1, NaN()}, + {-0.5, Inf(-1)}, + {-0.5, Inf(1)}, + {Copysign(0, -1), Inf(-1)}, + {Copysign(0, -1), -Pi}, + {Copysign(0, -1), -0.5}, + {Copysign(0, -1), -3}, + {Copysign(0, -1), 3}, + {Copysign(0, -1), Pi}, + {Copysign(0, -1), 0.5}, + {Copysign(0, -1), Inf(1)}, + + {0, Inf(-1)}, + {0, -Pi}, + {0, -3}, + {0, Copysign(0, -1)}, + {0, 0}, + {0, 3}, + {0, Pi}, + {0, Inf(1)}, + {0, NaN()}, + + {0.5, Inf(-1)}, + {0.5, Inf(1)}, + {1, Inf(-1)}, + {1, Inf(1)}, + {1, NaN()}, + + {Pi, Inf(-1)}, + {Pi, Copysign(0, -1)}, + {Pi, 0}, + {Pi, 1}, + {Pi, Inf(1)}, + {Pi, NaN()}, + {Inf(1), -Pi}, + {Inf(1), Copysign(0, -1)}, + {Inf(1), 0}, + {Inf(1), 1}, + {Inf(1), Pi}, + {Inf(1), NaN()}, + {NaN(), -Pi}, + {NaN(), Copysign(0, -1)}, + {NaN(), 0}, + {NaN(), 1}, + {NaN(), Pi}, + {NaN(), NaN()}, + + // Issue #7394 overflow checks + {2, float64(1 << 32)}, + {2, -float64(1 << 32)}, + {-2, float64(1<<32 + 1)}, + {0.5, float64(1 << 45)}, + {0.5, -float64(1 << 45)}, + // https://github.com/gnolang/gno/issues/1085 + {Nextafter(1, 2), float1Shift63}, + {Nextafter(1, -2), float1Shift63}, + {Nextafter(-1, 2), float1Shift63}, + {Nextafter(-1, -2), float1Shift63}, +} + +const float1Shift63 float64 = 1 << 63 + +var _powSC = []float64{ + 0, // pow(-Inf, -Pi) + Copysign(0, -1), // pow(-Inf, -3) + 1, // pow(-Inf, -0) + 1, // pow(-Inf, +0) + Inf(-1), // pow(-Inf, 1) + Inf(-1), // pow(-Inf, 3) + Inf(1), // pow(-Inf, Pi) + Inf(1), // pow(-Inf, 0.5) + NaN(), // pow(-Inf, NaN) + 0, // pow(-Pi, -Inf) + NaN(), // pow(-Pi, -Pi) + 1, // pow(-Pi, -0) + 1, // pow(-Pi, +0) + -Pi, // pow(-Pi, 1) + NaN(), // pow(-Pi, Pi) + Inf(1), // pow(-Pi, +Inf) + NaN(), // pow(-Pi, NaN) + 1, // pow(-1, -Inf) IEEE 754-2008 + 1, // pow(-1, +Inf) IEEE 754-2008 + NaN(), // pow(-1, NaN) + Inf(1), // pow(-1/2, -Inf) + 0, // pow(-1/2, +Inf) + Inf(1), // pow(-0, -Inf) + Inf(1), // pow(-0, -Pi) + Inf(1), // pow(-0, -0.5) + Inf(-1), // pow(-0, -3) IEEE 754-2008 + Copysign(0, -1), // pow(-0, 3) IEEE 754-2008 + 0, // pow(-0, +Pi) + 0, // pow(-0, 0.5) + 0, // pow(-0, +Inf) + Inf(1), // pow(+0, -Inf) + Inf(1), // pow(+0, -Pi) + Inf(1), // pow(+0, -3) + 1, // pow(+0, -0) + 1, // pow(+0, +0) + 0, // pow(+0, 3) + 0, // pow(+0, +Pi) + 0, // pow(+0, +Inf) + NaN(), // pow(+0, NaN) + Inf(1), // pow(1/2, -Inf) + 0, // pow(1/2, +Inf) + 1, // pow(1, -Inf) IEEE 754-2008 + 1, // pow(1, +Inf) IEEE 754-2008 + 1, // pow(1, NaN) IEEE 754-2008 + 0, // pow(+Pi, -Inf) + 1, // pow(+Pi, -0) + 1, // pow(+Pi, +0) + Pi, // pow(+Pi, 1) + Inf(1), // pow(+Pi, +Inf) + NaN(), // pow(+Pi, NaN) + 0, // pow(+Inf, -Pi) + 1, // pow(+Inf, -0) + 1, // pow(+Inf, +0) + Inf(1), // pow(+Inf, 1) + Inf(1), // pow(+Inf, Pi) + NaN(), // pow(+Inf, NaN) + NaN(), // pow(NaN, -Pi) + 1, // pow(NaN, -0) + 1, // pow(NaN, +0) + NaN(), // pow(NaN, 1) + NaN(), // pow(NaN, +Pi) + NaN(), // pow(NaN, NaN) + + // Issue #7394 overflow checks + Inf(1), // pow(2, float64(1 << 32)) + 0, // pow(2, -float64(1 << 32)) + Inf(-1), // pow(-2, float64(1<<32 + 1)) + 0, // pow(1/2, float64(1 << 45)) + Inf(1), // pow(1/2, -float64(1 << 45)) + Inf(1), // pow(Nextafter(1, 2), float64(1 << 63)) + 0, // pow(Nextafter(1, -2), float64(1 << 63)) + 0, // pow(Nextafter(-1, 2), float64(1 << 63)) + Inf(1), // pow(Nextafter(-1, -2), float64(1 << 63)) +} + +var _vfpow10SC = []int{ + MinInt32, + -324, + -323, + -50, + -22, + -1, + 0, + 1, + 22, + 50, + 100, + 200, + 308, + 309, + MaxInt32, +} + +var _pow10SC = []float64{ + 0, // pow10(MinInt32) + 0, // pow10(-324) + 1.0e-323, // pow10(-323) + 1.0e-50, // pow10(-50) + 1.0e-22, // pow10(-22) + 1.0e-1, // pow10(-1) + 1.0e0, // pow10(0) + 1.0e1, // pow10(1) + 1.0e22, // pow10(22) + 1.0e50, // pow10(50) + 1.0e100, // pow10(100) + 1.0e200, // pow10(200) + 1.0e308, // pow10(308) + Inf(1), // pow10(309) + Inf(1), // pow10(MaxInt32) +} + +var _vfroundSC = [][2]float64{ + {0, 0}, + {1.390671161567e-309, 0}, // denormal + {0.49999999999999994, 0}, // 0.5-epsilon + {0.5, 1}, + {0.5000000000000001, 1}, // 0.5+epsilon + {-1.5, -2}, + {-2.5, -3}, + {NaN(), NaN()}, + {Inf(1), Inf(1)}, + {2251799813685249.5, 2251799813685250}, // 1 bit fraction + {2251799813685250.5, 2251799813685251}, + {4503599627370495.5, 4503599627370496}, // 1 bit fraction, rounding to 0 bit fraction + {4503599627370497, 4503599627370497}, // large integer +} + +var _vfroundEvenSC = [][2]float64{ + {0, 0}, + {1.390671161567e-309, 0}, // denormal + {0.49999999999999994, 0}, // 0.5-epsilon + {0.5, 0}, + {0.5000000000000001, 1}, // 0.5+epsilon + {-1.5, -2}, + {-2.5, -2}, + {NaN(), NaN()}, + {Inf(1), Inf(1)}, + {2251799813685249.5, 2251799813685250}, // 1 bit fraction + {2251799813685250.5, 2251799813685250}, + {4503599627370495.5, 4503599627370496}, // 1 bit fraction, rounding to 0 bit fraction + {4503599627370497, 4503599627370497}, // large integer +} + +var _vfsignbitSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), +} + +var _signbitSC = []bool{ + true, + true, + false, + false, + false, +} + +var _vfsinSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), +} + +var _sinTestSC = []float64{ + NaN(), + Copysign(0, -1), + 0, + NaN(), + NaN(), +} + +var _vfsinhSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), +} + +var _sinTesthSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), +} + +var _vfsqrtSC = []float64{ + Inf(-1), + -Pi, + Copysign(0, -1), + 0, + Inf(1), + NaN(), + Float64frombits(2), // subnormal; see https://golang.org/issue/13013 +} + +var _sqrtSC = []float64{ + NaN(), + NaN(), + Copysign(0, -1), + 0, + Inf(1), + NaN(), + 3.1434555694052576e-162, +} + +var _vftanhSC = []float64{ + Inf(-1), + Copysign(0, -1), + 0, + Inf(1), + NaN(), +} + +var _tanhSC = []float64{ + -1, + Copysign(0, -1), + 0, + 1, + NaN(), +} + +var _vfy0SC = []float64{ + Inf(-1), + 0, + Inf(1), + NaN(), + -1, +} + +var _y0SC = []float64{ + NaN(), + Inf(-1), + 0, + NaN(), + NaN(), +} + +var _y1SC = []float64{ + NaN(), + Inf(-1), + 0, + NaN(), + NaN(), +} + +var _y2SC = []float64{ + NaN(), + Inf(-1), + 0, + NaN(), + NaN(), +} + +var _yM3SC = []float64{ + NaN(), + Inf(1), + 0, + NaN(), + NaN(), +} + +// arguments and expected results for boundary cases +const ( + SmallestNormalFloat64 = 2.2250738585072014e-308 // 2**-1022 + LargestSubnormalFloat64 = SmallestNormalFloat64 - SmallestNonzeroFloat64 +) + +var _vffrexpBC = []float64{ + SmallestNormalFloat64, + LargestSubnormalFloat64, + SmallestNonzeroFloat64, + MaxFloat64, + -SmallestNormalFloat64, + -LargestSubnormalFloat64, + -SmallestNonzeroFloat64, + -MaxFloat64, +} + +var _frexpBC = []fi{ + {0.5, -1021}, + {0.99999999999999978, -1022}, + {0.5, -1073}, + {0.99999999999999989, 1024}, + {-0.5, -1021}, + {-0.99999999999999978, -1022}, + {-0.5, -1073}, + {-0.99999999999999989, 1024}, +} + +var _vfldexpBC = []fi{ + {SmallestNormalFloat64, -52}, + {LargestSubnormalFloat64, -51}, + {SmallestNonzeroFloat64, 1074}, + {MaxFloat64, -(1023 + 1074)}, + {1, -1075}, + {-1, -1075}, + {1, 1024}, + {-1, 1024}, + {1.0000000000000002, -1075}, + {1, -1075}, +} + +var _ldexpBC = []float64{ + SmallestNonzeroFloat64, + 1e-323, // 2**-1073 + 1, + 1e-323, // 2**-1073 + 0, + Copysign(0, -1), + Inf(1), + Inf(-1), + SmallestNonzeroFloat64, + 0, +} + +var _logbBC = []float64{ + -1022, + -1023, + -1074, + 1023, + -1022, + -1023, + -1074, + 1023, +} + +// Test cases were generated with Berkeley TestFloat-3e/testfloat_gen. +// http://www.jhauser.us/arithmetic/TestFloat.html. +// The default rounding mode is selected (nearest/even), and exception flags are ignored. +var _fmaC = []struct{ x, y, z, want float64 }{ + // Large exponent spread + {-3.999999999999087, -1.1123914289620494e-16, -7.999877929687506, -7.999877929687505}, + {-262112.0000004768, -0.06251525855623184, 1.1102230248837136e-16, 16385.99945072085}, + {-6.462348523533467e-27, -2.3763644720331857e-211, 4.000000000931324, 4.000000000931324}, + + // Effective addition + {-2.0000000037252907, 6.7904383376e-313, -3.3951933161e-313, -1.697607001654e-312}, + {-0.12499999999999999, 512.007568359375, -1.4193627164960366e-16, -64.00094604492188}, + {-2.7550648847397148e-39, -3.4028301595800694e+38, 0.9960937495343386, 1.9335955376735676}, + {5.723369164769208e+24, 3.8149300927159385e-06, 1.84489958778182e+19, 4.028324913621874e+19}, + {-0.4843749999990904, -3.6893487872543293e+19, 9.223653786709391e+18, 2.7093936974938993e+19}, + {-3.8146972665201165e-06, 4.2949672959999385e+09, -2.2204460489938386e-16, -16384.000003844263}, + {6.98156394130982e-309, -1.1072962560000002e+09, -4.4414561548793455e-308, -7.73065965765153e-300}, + + // Effective subtraction + {5e-324, 4.5, -2e-323, 0}, + {5e-324, 7, -3.5e-323, 0}, + {5e-324, 0.5000000000000001, -5e-324, Copysign(0, -1)}, + {-2.1240680525e-314, -1.233647078189316e+308, -0.25781249999954525, -0.25780987964919844}, + {8.579992955364441e-308, 0.6037391876780558, -4.4501307410480706e-308, 7.29947236107098e-309}, + {-4.450143471986689e-308, -0.9960937499927239, -4.450419332475649e-308, -1.7659233458788e-310}, + {1.4932076393918112, -2.2248022430460833e-308, 4.449875571054211e-308, 1.127783865601762e-308}, + + // Overflow + {-2.288020632214759e+38, -8.98846570988901e+307, 1.7696041796300924e+308, Inf(0)}, + {1.4888652783208255e+308, -9.007199254742012e+15, -6.807282911929205e+38, Inf(-1)}, + {9.142703268902826e+192, -1.3504889569802838e+296, -1.9082200803806996e-89, Inf(-1)}, + + // Finite x and y, but non-finite z. + {31.99218749627471, -1.7976930544991702e+308, Inf(0), Inf(0)}, + {-1.7976931281784667e+308, -2.0009765625002265, Inf(-1), Inf(-1)}, + + // Special + {0, 0, 0, 0}, + {-1.1754226043408471e-38, NaN(), Inf(0), NaN()}, + {0, 0, 2.22507385643494e-308, 2.22507385643494e-308}, + {-8.65697792e+09, NaN(), -7.516192799999999e+09, NaN()}, + {-0.00012207403779029757, 3.221225471996093e+09, NaN(), NaN()}, + {Inf(-1), 0.1252441407414153, -1.387184532981584e-76, Inf(-1)}, + {Inf(0), 1.525878907671432e-05, -9.214364835452549e+18, Inf(0)}, + + // Random + {0.1777916152213626, -32.000015266239636, -2.2204459148334633e-16, -5.689334401293007}, + {-2.0816681711722314e-16, -0.4997558592585846, -0.9465627129124969, -0.9465627129124968}, + {-1.9999997615814211, 1.8518819259933516e+19, 16.874999999999996, -3.703763410463646e+19}, + {-0.12499994039717421, 32767.99999976135, -2.0752587082923246e+19, -2.075258708292325e+19}, + {7.705600568510257e-34, -1.801432979000528e+16, -0.17224197722973714, -0.17224197722973716}, + {3.8988133103758913e-308, -0.9848632812499999, 3.893879244098556e-308, 5.40811742605814e-310}, + {-0.012651981190687427, 6.911985574912436e+38, 6.669240527007144e+18, -8.745031148409496e+36}, + {4.612811918325842e+18, 1.4901161193847641e-08, 2.6077032311277997e-08, 6.873625395187494e+10}, + {-9.094947033611148e-13, 4.450691014249257e-308, 2.086006742350485e-308, 2.086006742346437e-308}, + {-7.751454006381804e-05, 5.588653777189071e-308, -2.2207280111272877e-308, -2.2211612130544025e-308}, +} + +var _sqrt32 = []float32{ + 0, + float32(Copysign(0, -1)), + float32(NaN()), + float32(Inf(1)), + float32(Inf(-1)), + 1, + 2, + -2, + 4.9790119248836735e+00, + 7.7388724745781045e+00, + -2.7688005719200159e-01, + -5.0106036182710749e+00, +} + +func tolerance(a, b, e float64) bool { + // Multiplying by e here can underflow denormal values to zero. + // Check a==b so that at least if a and b are small and identical + // we say they match. + if a == b { + return true + } + d := a - b + if d < 0 { + d = -d + } + + // note: b is correct (expected) value, a is actual value. + // make error tolerance a fraction of b, not a. + if b != 0 { + e = e * b + if e < 0 { + e = -e + } + } + return d < e +} +func close(a, b float64) bool { return tolerance(a, b, 1e-14) } +func veryclose(a, b float64) bool { return tolerance(a, b, 4e-16) } +func soclose(a, b, e float64) bool { return tolerance(a, b, e) } +func alike(a, b float64) bool { + switch { + case IsNaN(a) && IsNaN(b): + return true + case a == b: + return Signbit(a) == Signbit(b) + } + return false +} + +func TestNaN(t *testing.T) { + f64 := NaN() + if f64 == f64 { + t.Fatalf("NaN() returns %g, expected NaN", f64) + } + f32 := float32(f64) + if f32 == f32 { + t.Fatalf("float32(NaN()) is %g, expected NaN", f32) + } +} + +func TestAcos(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := _vf[i] / 10 + if f := Acos(a); !close(_acos[i], f) { + t.Errorf("Acos(%g) = %g, want %g", a, f, _acos[i]) + } + } + for i := 0; i < len(_vfacosSC); i++ { + if f := Acos(_vfacosSC[i]); !alike(_acosSC[i], f) { + t.Errorf("Acos(%g) = %g, want %g", _vfacosSC[i], f, _acosSC[i]) + } + } +} + +func TestAcosh(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := 1 + Abs(_vf[i]) + if f := Acosh(a); !veryclose(_acosh[i], f) { + t.Errorf("Acosh(%g) = %g, want %g", a, f, _acosh[i]) + } + } + for i := 0; i < len(_vfacoshSC); i++ { + if f := Acosh(_vfacoshSC[i]); !alike(_acoshSC[i], f) { + t.Errorf("Acosh(%g) = %g, want %g", _vfacoshSC[i], f, _acoshSC[i]) + } + } +} + +func TestAsin(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := _vf[i] / 10 + if f := Asin(a); !veryclose(_asin[i], f) { + t.Errorf("Asin(%g) = %g, want %g", a, f, _asin[i]) + } + } + for i := 0; i < len(_vfasinSC); i++ { + if f := Asin(_vfasinSC[i]); !alike(_asinSC[i], f) { + t.Errorf("Asin(%g) = %g, want %g", _vfasinSC[i], f, _asinSC[i]) + } + } +} + +func TestAsinh(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Asinh(_vf[i]); !veryclose(_asinh[i], f) { + t.Errorf("Asinh(%g) = %g, want %g", _vf[i], f, _asinh[i]) + } + } + for i := 0; i < len(_vfasinhSC); i++ { + if f := Asinh(_vfasinhSC[i]); !alike(_asinhSC[i], f) { + t.Errorf("Asinh(%g) = %g, want %g", _vfasinhSC[i], f, _asinhSC[i]) + } + } +} + +func TestAtan(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Atan(_vf[i]); !veryclose(_atan[i], f) { + t.Errorf("Atan(%g) = %g, want %g", _vf[i], f, _atan[i]) + } + } + for i := 0; i < len(_vfatanSC); i++ { + if f := Atan(_vfatanSC[i]); !alike(_atanSC[i], f) { + t.Errorf("Atan(%g) = %g, want %g", _vfatanSC[i], f, _atanSC[i]) + } + } +} + +func TestAtanh(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := _vf[i] / 10 + if f := Atanh(a); !veryclose(_atanh[i], f) { + t.Errorf("Atanh(%g) = %g, want %g", a, f, _atanh[i]) + } + } + for i := 0; i < len(_vfatanhSC); i++ { + if f := Atanh(_vfatanhSC[i]); !alike(_atanhSC[i], f) { + t.Errorf("Atanh(%g) = %g, want %g", _vfatanhSC[i], f, _atanhSC[i]) + } + } +} + +func TestAtan2(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Atan2(10, _vf[i]); !veryclose(_atan2[i], f) { + t.Errorf("Atan2(10, %g) = %g, want %g", _vf[i], f, _atan2[i]) + } + } + for i := 0; i < len(_vfatan2SC); i++ { + if f := Atan2(_vfatan2SC[i][0], _vfatan2SC[i][1]); !alike(_atan2SC[i], f) { + t.Errorf("Atan2(%g, %g) = %g, want %g", _vfatan2SC[i][0], _vfatan2SC[i][1], f, _atan2SC[i]) + } + } +} + +func TestCbrt(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Cbrt(_vf[i]); !veryclose(_cbrt[i], f) { + t.Errorf("Cbrt(%g) = %g, want %g", _vf[i], f, _cbrt[i]) + } + } + for i := 0; i < len(_vfcbrtSC); i++ { + if f := Cbrt(_vfcbrtSC[i]); !alike(_cbrtSC[i], f) { + t.Errorf("Cbrt(%g) = %g, want %g", _vfcbrtSC[i], f, _cbrtSC[i]) + } + } +} + +func TestCeil(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Ceil(_vf[i]); !alike(_ceil[i], f) { + t.Errorf("Ceil(%g) = %g, want %g", _vf[i], f, _ceil[i]) + } + } + for i := 0; i < len(_vfceilSC); i++ { + if f := Ceil(_vfceilSC[i]); !alike(_ceilSC[i], f) { + t.Errorf("Ceil(%g) = %g, want %g", _vfceilSC[i], f, _ceilSC[i]) + } + } +} + +func TestCopysign(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Copysign(_vf[i], -1); _copysign[i] != f { + t.Errorf("Copysign(%g, -1) = %g, want %g", _vf[i], f, _copysign[i]) + } + } + for i := 0; i < len(_vf); i++ { + if f := Copysign(_vf[i], 1); -_copysign[i] != f { + t.Errorf("Copysign(%g, 1) = %g, want %g", _vf[i], f, -_copysign[i]) + } + } + for i := 0; i < len(_vfcopysignSC); i++ { + if f := Copysign(_vfcopysignSC[i], -1); !alike(_copysignSC[i], f) { + t.Errorf("Copysign(%g, -1) = %g, want %g", _vfcopysignSC[i], f, _copysignSC[i]) + } + } +} + +func TestCos(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Cos(_vf[i]); !veryclose(_testCos[i], f) { + t.Errorf("Cos(%g) = %g, want %g", _vf[i], f, _testCos[i]) + } + } + for i := 0; i < len(_vfcosSC); i++ { + if f := Cos(_vfcosSC[i]); !alike(_testCosSC[i], f) { + t.Errorf("Cos(%g) = %g, want %g", _vfcosSC[i], f, _testCosSC[i]) + } + } +} + +func TestCosh(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Cosh(_vf[i]); !close(_testCosh[i], f) { + t.Errorf("Cosh(%g) = %g, want %g", _vf[i], f, _testCosh[i]) + } + } + for i := 0; i < len(_vfcoshSC); i++ { + if f := Cosh(_vfcoshSC[i]); !alike(_testCoshSC[i], f) { + t.Errorf("Cosh(%g) = %g, want %g", _vfcoshSC[i], f, _testCoshSC[i]) + } + } +} + +func TestErf(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := _vf[i] / 10 + if f := Erf(a); !veryclose(_erf[i], f) { + t.Errorf("Erf(%g) = %g, want %g", a, f, _erf[i]) + } + } + for i := 0; i < len(_vferfSC); i++ { + if f := Erf(_vferfSC[i]); !alike(_erfSC[i], f) { + t.Errorf("Erf(%g) = %g, want %g", _vferfSC[i], f, _erfSC[i]) + } + } +} + +func TestErfc(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := _vf[i] / 10 + if f := Erfc(a); !veryclose(_erfc[i], f) { + t.Errorf("Erfc(%g) = %g, want %g", a, f, _erfc[i]) + } + } + for i := 0; i < len(_vferfcSC); i++ { + if f := Erfc(_vferfcSC[i]); !alike(_erfcSC[i], f) { + t.Errorf("Erfc(%g) = %g, want %g", _vferfcSC[i], f, _erfcSC[i]) + } + } +} + +func TestErfinv(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := _vf[i] / 10 + if f := Erfinv(a); !veryclose(_erfinv[i], f) { + t.Errorf("Erfinv(%g) = %g, want %g", a, f, _erfinv[i]) + } + } + for i := 0; i < len(_vferfinvSC); i++ { + if f := Erfinv(_vferfinvSC[i]); !alike(_erfinvSC[i], f) { + t.Errorf("Erfinv(%g) = %g, want %g", _vferfinvSC[i], f, _erfinvSC[i]) + } + } + for x := -0.9; x <= 0.90; x += 1e-2 { + if f := Erf(Erfinv(x)); !close(x, f) { + t.Errorf("Erf(Erfinv(%g)) = %g, want %g", x, f, x) + } + } + for x := -0.9; x <= 0.90; x += 1e-2 { + if f := Erfinv(Erf(x)); !close(x, f) { + t.Errorf("Erfinv(Erf(%g)) = %g, want %g", x, f, x) + } + } +} + +func TestErfcinv(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := 1.0 - (_vf[i] / 10) + if f := Erfcinv(a); !veryclose(_erfinv[i], f) { + t.Errorf("Erfcinv(%g) = %g, want %g", a, f, _erfinv[i]) + } + } + for i := 0; i < len(_vferfcinvSC); i++ { + if f := Erfcinv(_vferfcinvSC[i]); !alike(_erfcinvSC[i], f) { + t.Errorf("Erfcinv(%g) = %g, want %g", _vferfcinvSC[i], f, _erfcinvSC[i]) + } + } + for x := 0.1; x <= 1.9; x += 1e-2 { + if f := Erfc(Erfcinv(x)); !close(x, f) { + t.Errorf("Erfc(Erfcinv(%g)) = %g, want %g", x, f, x) + } + } + for x := 0.1; x <= 1.9; x += 1e-2 { + if f := Erfcinv(Erfc(x)); !close(x, f) { + t.Errorf("Erfcinv(Erfc(%g)) = %g, want %g", x, f, x) + } + } +} + +func TestExp(t *testing.T) { + testExp(t, Exp, "Exp") +} + +func testExp(t *testing.T, Exp func(float64) float64, name string) { + for i := 0; i < len(_vf); i++ { + if f := Exp(_vf[i]); !veryclose(_exp[i], f) { + t.Errorf("%s(%g) = %g, want %g", name, _vf[i], f, _exp[i]) + } + } + for i := 0; i < len(_vfexpSC); i++ { + if f := Exp(_vfexpSC[i]); !alike(_expSC[i], f) { + t.Errorf("%s(%g) = %g, want %g", name, _vfexpSC[i], f, _expSC[i]) + } + } +} + +func TestExpm1(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := _vf[i] / 100 + if f := Expm1(a); !veryclose(_expm1[i], f) { + t.Errorf("Expm1(%g) = %g, want %g", a, f, _expm1[i]) + } + } + for i := 0; i < len(_vf); i++ { + a := _vf[i] * 10 + if f := Expm1(a); !close(_expm1Large[i], f) { + t.Errorf("Expm1(%g) = %g, want %g", a, f, _expm1Large[i]) + } + } + for i := 0; i < len(_vfexpm1SC); i++ { + if f := Expm1(_vfexpm1SC[i]); !alike(_expm1SC[i], f) { + t.Errorf("Expm1(%g) = %g, want %g", _vfexpm1SC[i], f, _expm1SC[i]) + } + } +} + +func TestExp2(t *testing.T) { + testExp2(t, Exp2, "Exp2") +} + +func testExp2(t *testing.T, Exp2 func(float64) float64, name string) { + for i := 0; i < len(_vf); i++ { + if f := Exp2(_vf[i]); !close(_exp2[i], f) { + t.Errorf("%s(%g) = %g, want %g", name, _vf[i], f, _exp2[i]) + } + } + for i := 0; i < len(_vfexp2SC); i++ { + if f := Exp2(_vfexp2SC[i]); !alike(_exp2SC[i], f) { + t.Errorf("%s(%g) = %g, want %g", name, _vfexp2SC[i], f, _exp2SC[i]) + } + } + for n := -1074; n < 1024; n++ { + f := Exp2(float64(n)) + vf := Ldexp(1, n) + if f != vf { + t.Errorf("%s(%d) = %g, want %g", name, n, f, vf) + } + } +} + +func TestAbs(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Abs(_vf[i]); _fabs[i] != f { + t.Errorf("Abs(%g) = %g, want %g", _vf[i], f, _fabs[i]) + } + } + for i := 0; i < len(_vffabsSC); i++ { + if f := Abs(_vffabsSC[i]); !alike(_fabsSC[i], f) { + t.Errorf("Abs(%g) = %g, want %g", _vffabsSC[i], f, _fabsSC[i]) + } + } +} + +func TestDim(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Dim(_vf[i], 0); _fdim[i] != f { + t.Errorf("Dim(%g, %g) = %g, want %g", _vf[i], 0.0, f, _fdim[i]) + } + } + for i := 0; i < len(_vffdimSC); i++ { + if f := Dim(_vffdimSC[i][0], _vffdimSC[i][1]); !alike(_fdimSC[i], f) { + t.Errorf("Dim(%g, %g) = %g, want %g", _vffdimSC[i][0], _vffdimSC[i][1], f, _fdimSC[i]) + } + } + for i := 0; i < len(_vffdim2SC); i++ { + if f := Dim(_vffdim2SC[i][0], _vffdim2SC[i][1]); !alike(_fdimSC[i], f) { + t.Errorf("Dim(%g, %g) = %g, want %g", _vffdim2SC[i][0], _vffdim2SC[i][1], f, _fdimSC[i]) + } + } +} + +func TestFloor(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Floor(_vf[i]); !alike(_floor[i], f) { + t.Errorf("Floor(%g) = %g, want %g", _vf[i], f, _floor[i]) + } + } + for i := 0; i < len(_vfceilSC); i++ { + if f := Floor(_vfceilSC[i]); !alike(_ceilSC[i], f) { + t.Errorf("Floor(%g) = %g, want %g", _vfceilSC[i], f, _ceilSC[i]) + } + } +} + +func TestMax(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Max(_vf[i], _ceil[i]); _ceil[i] != f { + t.Errorf("Max(%g, %g) = %g, want %g", _vf[i], _ceil[i], f, _ceil[i]) + } + } + for i := 0; i < len(_vffdimSC); i++ { + if f := Max(_vffdimSC[i][0], _vffdimSC[i][1]); !alike(_fmaxSC[i], f) { + t.Errorf("Max(%g, %g) = %g, want %g", _vffdimSC[i][0], _vffdimSC[i][1], f, _fmaxSC[i]) + } + } + for i := 0; i < len(_vffdim2SC); i++ { + if f := Max(_vffdim2SC[i][0], _vffdim2SC[i][1]); !alike(_fmaxSC[i], f) { + t.Errorf("Max(%g, %g) = %g, want %g", _vffdim2SC[i][0], _vffdim2SC[i][1], f, _fmaxSC[i]) + } + } +} + +func TestMin(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Min(_vf[i], _floor[i]); _floor[i] != f { + t.Errorf("Min(%g, %g) = %g, want %g", _vf[i], _floor[i], f, _floor[i]) + } + } + for i := 0; i < len(_vffdimSC); i++ { + if f := Min(_vffdimSC[i][0], _vffdimSC[i][1]); !alike(_fminSC[i], f) { + t.Errorf("Min(%g, %g) = %g, want %g", _vffdimSC[i][0], _vffdimSC[i][1], f, _fminSC[i]) + } + } + for i := 0; i < len(_vffdim2SC); i++ { + if f := Min(_vffdim2SC[i][0], _vffdim2SC[i][1]); !alike(_fminSC[i], f) { + t.Errorf("Min(%g, %g) = %g, want %g", _vffdim2SC[i][0], _vffdim2SC[i][1], f, _fminSC[i]) + } + } +} + +func TestMod(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Mod(10, _vf[i]); _fmod[i] != f { + t.Errorf("Mod(10, %g) = %g, want %g", _vf[i], f, _fmod[i]) + } + } + for i := 0; i < len(_vffmodSC); i++ { + if f := Mod(_vffmodSC[i][0], _vffmodSC[i][1]); !alike(_fmodSC[i], f) { + t.Errorf("Mod(%g, %g) = %g, want %g", _vffmodSC[i][0], _vffmodSC[i][1], f, _fmodSC[i]) + } + } + // verify precision of result for extreme inputs + if f := Mod(5.9790119248836734e+200, 1.1258465975523544); 0.6447968302508578 != f { + t.Errorf("Remainder(5.9790119248836734e+200, 1.1258465975523544) = %g, want 0.6447968302508578", f) + } +} + +func TestFrexp(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f, j := Frexp(_vf[i]); !veryclose(_frexp[i].f, f) || _frexp[i].i != j { + t.Errorf("Frexp(%g) = %g, %d, want %g, %d", _vf[i], f, j, _frexp[i].f, _frexp[i].i) + } + } + for i := 0; i < len(_vffrexpSC); i++ { + if f, j := Frexp(_vffrexpSC[i]); !alike(_frexpSC[i].f, f) || _frexpSC[i].i != j { + t.Errorf("Frexp(%g) = %g, %d, want %g, %d", _vffrexpSC[i], f, j, _frexpSC[i].f, _frexpSC[i].i) + } + } + for i := 0; i < len(_vffrexpBC); i++ { + if f, j := Frexp(_vffrexpBC[i]); !alike(_frexpBC[i].f, f) || _frexpBC[i].i != j { + t.Errorf("Frexp(%g) = %g, %d, want %g, %d", _vffrexpBC[i], f, j, _frexpBC[i].f, _frexpBC[i].i) + } + } +} + +func TestGamma(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Gamma(_vf[i]); !close(_gamma[i], f) { + t.Errorf("Gamma(%g) = %g, want %g", _vf[i], f, _gamma[i]) + } + } + for _, g := range _vfgamma { + f := Gamma(g[0]) + var ok bool + if IsNaN(g[1]) || IsInf(g[1], 0) || g[1] == 0 || f == 0 { + ok = alike(g[1], f) + } else if g[0] > -50 && g[0] <= 171 { + ok = veryclose(g[1], f) + } else { + ok = close(g[1], f) + } + if !ok { + t.Errorf("Gamma(%g) = %g, want %g", g[0], f, g[1]) + } + } +} + +func TestHypot(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := Abs(1e200 * _tanh[i] * Sqrt(2)) + if f := Hypot(1e200*_tanh[i], 1e200*_tanh[i]); !veryclose(a, f) { + t.Errorf("Hypot(%g, %g) = %g, want %g", 1e200*_tanh[i], 1e200*_tanh[i], f, a) + } + } + for i := 0; i < len(_vfhypotSC); i++ { + if f := Hypot(_vfhypotSC[i][0], _vfhypotSC[i][1]); !alike(_hypotSC[i], f) { + t.Errorf("Hypot(%g, %g) = %g, want %g", _vfhypotSC[i][0], _vfhypotSC[i][1], f, _hypotSC[i]) + } + } +} + +func TestIlogb(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := _frexp[i].i - 1 // adjust because fr in the interval [½, 1) + if e := Ilogb(_vf[i]); a != e { + t.Errorf("Ilogb(%g) = %d, want %d", _vf[i], e, a) + } + } + for i := 0; i < len(_vflogbSC); i++ { + if e := Ilogb(_vflogbSC[i]); _ilogbSC[i] != e { + t.Errorf("Ilogb(%g) = %d, want %d", _vflogbSC[i], e, _ilogbSC[i]) + } + } + for i := 0; i < len(_vffrexpBC); i++ { + if e := Ilogb(_vffrexpBC[i]); int(_logbBC[i]) != e { + t.Errorf("Ilogb(%g) = %d, want %d", _vffrexpBC[i], e, int(_logbBC[i])) + } + } +} + +func TestJ0(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := J0(_vf[i]); !soclose(_j0[i], f, 4e-14) { + t.Errorf("J0(%g) = %g, want %g", _vf[i], f, _j0[i]) + } + } + for i := 0; i < len(_vfj0SC); i++ { + if f := J0(_vfj0SC[i]); !alike(_j0SC[i], f) { + t.Errorf("J0(%g) = %g, want %g", _vfj0SC[i], f, _j0SC[i]) + } + } +} + +func TestJ1(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := J1(_vf[i]); !close(_j1[i], f) { + t.Errorf("J1(%g) = %g, want %g", _vf[i], f, _j1[i]) + } + } + for i := 0; i < len(_vfj0SC); i++ { + if f := J1(_vfj0SC[i]); !alike(_j1SC[i], f) { + t.Errorf("J1(%g) = %g, want %g", _vfj0SC[i], f, _j1SC[i]) + } + } +} + +func TestJn(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Jn(2, _vf[i]); !close(_j2[i], f) { + t.Errorf("Jn(2, %g) = %g, want %g", _vf[i], f, _j2[i]) + } + if f := Jn(-3, _vf[i]); !close(_jM3[i], f) { + t.Errorf("Jn(-3, %g) = %g, want %g", _vf[i], f, _jM3[i]) + } + } + for i := 0; i < len(_vfj0SC); i++ { + if f := Jn(2, _vfj0SC[i]); !alike(_j2SC[i], f) { + t.Errorf("Jn(2, %g) = %g, want %g", _vfj0SC[i], f, _j2SC[i]) + } + if f := Jn(-3, _vfj0SC[i]); !alike(_jM3SC[i], f) { + t.Errorf("Jn(-3, %g) = %g, want %g", _vfj0SC[i], f, _jM3SC[i]) + } + } +} + +func TestLdexp(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Ldexp(_frexp[i].f, _frexp[i].i); !veryclose(_vf[i], f) { + t.Errorf("Ldexp(%g, %d) = %g, want %g", _frexp[i].f, _frexp[i].i, f, _vf[i]) + } + } + for i := 0; i < len(_vffrexpSC); i++ { + if f := Ldexp(_frexpSC[i].f, _frexpSC[i].i); !alike(_vffrexpSC[i], f) { + t.Errorf("Ldexp(%g, %d) = %g, want %g", _frexpSC[i].f, _frexpSC[i].i, f, _vffrexpSC[i]) + } + } + for i := 0; i < len(_vfldexpSC); i++ { + if f := Ldexp(_vfldexpSC[i].f, _vfldexpSC[i].i); !alike(_ldexpSC[i], f) { + t.Errorf("Ldexp(%g, %d) = %g, want %g", _vfldexpSC[i].f, _vfldexpSC[i].i, f, _ldexpSC[i]) + } + } + for i := 0; i < len(_vffrexpBC); i++ { + if f := Ldexp(_frexpBC[i].f, _frexpBC[i].i); !alike(_vffrexpBC[i], f) { + t.Errorf("Ldexp(%g, %d) = %g, want %g", _frexpBC[i].f, _frexpBC[i].i, f, _vffrexpBC[i]) + } + } + for i := 0; i < len(_vfldexpBC); i++ { + if f := Ldexp(_vfldexpBC[i].f, _vfldexpBC[i].i); !alike(_ldexpBC[i], f) { + t.Errorf("Ldexp(%g, %d) = %g, want %g", _vfldexpBC[i].f, _vfldexpBC[i].i, f, _ldexpBC[i]) + } + } +} + +func TestLgamma(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f, s := Lgamma(_vf[i]); !close(_lgamma[i].f, f) || _lgamma[i].i != s { + t.Errorf("Lgamma(%g) = %g, %d, want %g, %d", _vf[i], f, s, _lgamma[i].f, _lgamma[i].i) + } + } + for i := 0; i < len(_vflgammaSC); i++ { + if f, s := Lgamma(_vflgammaSC[i]); !alike(_lgammaSC[i].f, f) || _lgammaSC[i].i != s { + t.Errorf("Lgamma(%g) = %g, %d, want %g, %d", _vflgammaSC[i], f, s, _lgammaSC[i].f, _lgammaSC[i].i) + } + } +} + +func TestLog(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := Abs(_vf[i]) + if f := Log(a); _log[i] != f { + t.Errorf("Log(%g) = %g, want %g", a, f, _log[i]) + } + } + if f := Log(10); f != Ln10 { + t.Errorf("Log(%g) = %g, want %g", 10.0, f, Ln10) + } + for i := 0; i < len(_vflogSC); i++ { + if f := Log(_vflogSC[i]); !alike(_logSC[i], f) { + t.Errorf("Log(%g) = %g, want %g", _vflogSC[i], f, _logSC[i]) + } + } +} + +func TestLogb(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Logb(_vf[i]); _logb[i] != f { + t.Errorf("Logb(%g) = %g, want %g", _vf[i], f, _logb[i]) + } + } + for i := 0; i < len(_vflogbSC); i++ { + if f := Logb(_vflogbSC[i]); !alike(_logbSC[i], f) { + t.Errorf("Logb(%g) = %g, want %g", _vflogbSC[i], f, _logbSC[i]) + } + } + for i := 0; i < len(_vffrexpBC); i++ { + if f := Logb(_vffrexpBC[i]); !alike(_logbBC[i], f) { + t.Errorf("Logb(%g) = %g, want %g", _vffrexpBC[i], f, _logbBC[i]) + } + } +} + +func TestLog10(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := Abs(_vf[i]) + if f := Log10(a); !veryclose(_log10[i], f) { + t.Errorf("Log10(%g) = %g, want %g", a, f, _log10[i]) + } + } + if f := Log10(E); f != Log10E { + t.Errorf("Log10(%g) = %g, want %g", E, f, Log10E) + } + for i := 0; i < len(_vflogSC); i++ { + if f := Log10(_vflogSC[i]); !alike(_logSC[i], f) { + t.Errorf("Log10(%g) = %g, want %g", _vflogSC[i], f, _logSC[i]) + } + } +} + +func TestLog1p(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := _vf[i] / 100 + if f := Log1p(a); !veryclose(_log1p[i], f) { + t.Errorf("Log1p(%g) = %g, want %g", a, f, _log1p[i]) + } + } + a := 9.0 + if f := Log1p(a); f != Ln10 { + t.Errorf("Log1p(%g) = %g, want %g", a, f, Ln10) + } + for i := 0; i < len(_vflogSC); i++ { + if f := Log1p(_vflog1pSC[i]); !alike(_log1pSC[i], f) { + t.Errorf("Log1p(%g) = %g, want %g", _vflog1pSC[i], f, _log1pSC[i]) + } + } +} + +func TestLog2(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := Abs(_vf[i]) + if f := Log2(a); !veryclose(_log2[i], f) { + t.Errorf("Log2(%g) = %g, want %g", a, f, _log2[i]) + } + } + if f := Log2(E); f != Log2E { + t.Errorf("Log2(%g) = %g, want %g", E, f, Log2E) + } + for i := 0; i < len(_vflogSC); i++ { + if f := Log2(_vflogSC[i]); !alike(_logSC[i], f) { + t.Errorf("Log2(%g) = %g, want %g", _vflogSC[i], f, _logSC[i]) + } + } + for i := -1074; i <= 1023; i++ { + f := Ldexp(1, i) + l := Log2(f) + if l != float64(i) { + t.Errorf("Log2(2**%d) = %g, want %d", i, l, i) + } + } +} + +func TestModf(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f, g := Modf(_vf[i]); !veryclose(_modf[i][0], f) || !veryclose(_modf[i][1], g) { + t.Errorf("Modf(%g) = %g, %g, want %g, %g", _vf[i], f, g, _modf[i][0], _modf[i][1]) + } + } + for i := 0; i < len(_vfmodfSC); i++ { + if f, g := Modf(_vfmodfSC[i]); !alike(_modfSC[i][0], f) || !alike(_modfSC[i][1], g) { + t.Errorf("Modf(%g) = %g, %g, want %g, %g", _vfmodfSC[i], f, g, _modfSC[i][0], _modfSC[i][1]) + } + } +} + +func TestNextafter32(t *testing.T) { + for i := 0; i < len(_vf); i++ { + vfi := float32(_vf[i]) + if f := Nextafter32(vfi, 10); _nextafter32[i] != f { + t.Errorf("Nextafter32(%g, %g) = %g want %g", vfi, 10.0, f, _nextafter32[i]) + } + } + for i := 0; i < len(_vfnextafter32SC); i++ { + if f := Nextafter32(_vfnextafter32SC[i][0], _vfnextafter32SC[i][1]); !alike(float64(_nextafter32SC[i]), float64(f)) { + t.Errorf("Nextafter32(%g, %g) = %g want %g", _vfnextafter32SC[i][0], _vfnextafter32SC[i][1], f, _nextafter32SC[i]) + } + } +} + +func TestNextafter64(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Nextafter(_vf[i], 10); _nextafter64[i] != f { + t.Errorf("Nextafter64(%g, %g) = %g want %g", _vf[i], 10.0, f, _nextafter64[i]) + } + } + for i := 0; i < len(_vfnextafter64SC); i++ { + if f := Nextafter(_vfnextafter64SC[i][0], _vfnextafter64SC[i][1]); !alike(_nextafter64SC[i], f) { + t.Errorf("Nextafter64(%g, %g) = %g want %g", _vfnextafter64SC[i][0], _vfnextafter64SC[i][1], f, _nextafter64SC[i]) + } + } +} + +func TestPow(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Pow(10, _vf[i]); !close(_pow[i], f) { + t.Errorf("Pow(10, %g) = %g, want %g", _vf[i], f, _pow[i]) + } + } + for i := 0; i < len(_vfpowSC); i++ { + if f := Pow(_vfpowSC[i][0], _vfpowSC[i][1]); !alike(_powSC[i], f) { + t.Errorf("Pow(%g, %g) = %g, want %g", _vfpowSC[i][0], _vfpowSC[i][1], f, _powSC[i]) + } + } +} + +func TestPow10(t *testing.T) { + for i := 0; i < len(_vfpow10SC); i++ { + if f := Pow10(_vfpow10SC[i]); !alike(_pow10SC[i], f) { + t.Errorf("Pow10(%d) = %g, want %g", _vfpow10SC[i], f, _pow10SC[i]) + } + } +} + +func TestRemainder(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Remainder(10, _vf[i]); _remainder[i] != f { + t.Errorf("Remainder(10, %g) = %g, want %g", _vf[i], f, _remainder[i]) + } + } + for i := 0; i < len(_vffmodSC); i++ { + if f := Remainder(_vffmodSC[i][0], _vffmodSC[i][1]); !alike(_fmodSC[i], f) { + t.Errorf("Remainder(%g, %g) = %g, want %g", _vffmodSC[i][0], _vffmodSC[i][1], f, _fmodSC[i]) + } + } + // verify precision of result for extreme inputs + if f := Remainder(5.9790119248836734e+200, 1.1258465975523544); -0.4810497673014966 != f { + t.Errorf("Remainder(5.9790119248836734e+200, 1.1258465975523544) = %g, want -0.4810497673014966", f) + } + // verify that sign is correct when r == 0. + test := func(x, y float64) { + if r := Remainder(x, y); r == 0 && Signbit(r) != Signbit(x) { + t.Errorf("Remainder(x=%f, y=%f) = %f, sign of (zero) result should agree with sign of x", x, y, r) + } + } + for x := 0.0; x <= 3.0; x += 1 { + for y := 1.0; y <= 3.0; y += 1 { + test(x, y) + test(x, -y) + test(-x, y) + test(-x, -y) + } + } +} + +func TestRound(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Round(_vf[i]); !alike(_round[i], f) { + t.Errorf("Round(%g) = %g, want %g", _vf[i], f, _round[i]) + } + } + for i := 0; i < len(_vfroundSC); i++ { + if f := Round(_vfroundSC[i][0]); !alike(_vfroundSC[i][1], f) { + t.Errorf("Round(%g) = %g, want %g", _vfroundSC[i][0], f, _vfroundSC[i][1]) + } + } +} + +func TestRoundToEven(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := RoundToEven(_vf[i]); !alike(_round[i], f) { + t.Errorf("RoundToEven(%g) = %g, want %g", _vf[i], f, _round[i]) + } + } + for i := 0; i < len(_vfroundEvenSC); i++ { + if f := RoundToEven(_vfroundEvenSC[i][0]); !alike(_vfroundEvenSC[i][1], f) { + t.Errorf("RoundToEven(%g) = %g, want %g", _vfroundEvenSC[i][0], f, _vfroundEvenSC[i][1]) + } + } +} + +func TestSignbit(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Signbit(_vf[i]); _signbit[i] != f { + t.Errorf("Signbit(%g) = %t, want %t", _vf[i], f, _signbit[i]) + } + } + for i := 0; i < len(_vfsignbitSC); i++ { + if f := Signbit(_vfsignbitSC[i]); _signbitSC[i] != f { + t.Errorf("Signbit(%g) = %t, want %t", _vfsignbitSC[i], f, _signbitSC[i]) + } + } +} + +func TestSin(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Sin(_vf[i]); !veryclose(_sinTest[i], f) { + t.Errorf("Sin(%g) = %g, want %g", _vf[i], f, _sinTest[i]) + } + } + for i := 0; i < len(_vfsinSC); i++ { + if f := Sin(_vfsinSC[i]); !alike(_sinTestSC[i], f) { + t.Errorf("Sin(%g) = %g, want %g", _vfsinSC[i], f, _sinTestSC[i]) + } + } +} + +func TestSincos(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if s, c := Sincos(_vf[i]); !veryclose(_sinTest[i], s) || !veryclose(_testCos[i], c) { + t.Errorf("Sincos(%g) = %g, %g want %g, %g", _vf[i], s, c, _sinTest[i], _testCos[i]) + } + } +} + +func TestSinh(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Sinh(_vf[i]); !close(_sinTesth[i], f) { + t.Errorf("Sinh(%g) = %g, want %g", _vf[i], f, _sinTesth[i]) + } + } + for i := 0; i < len(_vfsinhSC); i++ { + if f := Sinh(_vfsinhSC[i]); !alike(_sinTesthSC[i], f) { + t.Errorf("Sinh(%g) = %g, want %g", _vfsinhSC[i], f, _sinTesthSC[i]) + } + } +} + +func TestSqrt(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := Abs(_vf[i]) + if f := Sqrt(a); _sqrt[i] != f { + t.Errorf("Sqrt(%g) = %g, want %g", a, f, _sqrt[i]) + } + } + for i := 0; i < len(_vfsqrtSC); i++ { + if f := Sqrt(_vfsqrtSC[i]); !alike(_sqrtSC[i], f) { + t.Errorf("Sqrt(%g) = %g, want %g", _vfsqrtSC[i], f, _sqrtSC[i]) + } + } +} + +func TestTan(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Tan(_vf[i]); !veryclose(_tan[i], f) { + t.Errorf("Tan(%g) = %g, want %g", _vf[i], f, _tan[i]) + } + } + // same special cases as Sin + for i := 0; i < len(_vfsinSC); i++ { + if f := Tan(_vfsinSC[i]); !alike(_sinTestSC[i], f) { + t.Errorf("Tan(%g) = %g, want %g", _vfsinSC[i], f, _sinTestSC[i]) + } + } +} + +func TestTanh(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Tanh(_vf[i]); !veryclose(_tanh[i], f) { + t.Errorf("Tanh(%g) = %g, want %g", _vf[i], f, _tanh[i]) + } + } + for i := 0; i < len(_vftanhSC); i++ { + if f := Tanh(_vftanhSC[i]); !alike(_tanhSC[i], f) { + t.Errorf("Tanh(%g) = %g, want %g", _vftanhSC[i], f, _tanhSC[i]) + } + } +} + +func TestTrunc(t *testing.T) { + for i := 0; i < len(_vf); i++ { + if f := Trunc(_vf[i]); !alike(_trunc[i], f) { + t.Errorf("Trunc(%g) = %g, want %g", _vf[i], f, _trunc[i]) + } + } + for i := 0; i < len(_vfceilSC); i++ { + if f := Trunc(_vfceilSC[i]); !alike(_ceilSC[i], f) { + t.Errorf("Trunc(%g) = %g, want %g", _vfceilSC[i], f, _ceilSC[i]) + } + } +} + +func TestY0(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := Abs(_vf[i]) + if f := Y0(a); !close(_y0[i], f) { + t.Errorf("Y0(%g) = %g, want %g", a, f, _y0[i]) + } + } + for i := 0; i < len(_vfy0SC); i++ { + if f := Y0(_vfy0SC[i]); !alike(_y0SC[i], f) { + t.Errorf("Y0(%g) = %g, want %g", _vfy0SC[i], f, _y0SC[i]) + } + } +} + +func TestY1(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := Abs(_vf[i]) + if f := Y1(a); !soclose(_y1[i], f, 2e-14) { + t.Errorf("Y1(%g) = %g, want %g", a, f, _y1[i]) + } + } + for i := 0; i < len(_vfy0SC); i++ { + if f := Y1(_vfy0SC[i]); !alike(_y1SC[i], f) { + t.Errorf("Y1(%g) = %g, want %g", _vfy0SC[i], f, _y1SC[i]) + } + } +} + +func TestYn(t *testing.T) { + for i := 0; i < len(_vf); i++ { + a := Abs(_vf[i]) + if f := Yn(2, a); !close(_y2[i], f) { + t.Errorf("Yn(2, %g) = %g, want %g", a, f, _y2[i]) + } + if f := Yn(-3, a); !close(_yM3[i], f) { + t.Errorf("Yn(-3, %g) = %g, want %g", a, f, _yM3[i]) + } + } + for i := 0; i < len(_vfy0SC); i++ { + if f := Yn(2, _vfy0SC[i]); !alike(_y2SC[i], f) { + t.Errorf("Yn(2, %g) = %g, want %g", _vfy0SC[i], f, _y2SC[i]) + } + if f := Yn(-3, _vfy0SC[i]); !alike(_yM3SC[i], f) { + t.Errorf("Yn(-3, %g) = %g, want %g", _vfy0SC[i], f, _yM3SC[i]) + } + } + if f := Yn(0, 0); !alike(Inf(-1), f) { + t.Errorf("Yn(0, 0) = %g, want %g", f, Inf(-1)) + } +} + +func TestFMA(t *testing.T) { + for _, c := range _fmaC { + got := FMA(c.x, c.y, c.z) + if !alike(got, c.want) { + t.Errorf("FMA(%g,%g,%g) == %g; want %g", c.x, c.y, c.z, got, c.want) + } + } +} + +// Check that math functions of high angle values +// return accurate results. [Since (_vf[i] + large) - large != _vf[i], +// testing for Trig(_vf[i] + large) == Trig(_vf[i]), where large is +// a multiple of 2*Pi, is misleading.] +func TestLargeCos(t *testing.T) { + large := float64(100000 * Pi) + for i := 0; i < len(_vf); i++ { + f1 := _testCosLarge[i] + f2 := Cos(_vf[i] + large) + if !close(f1, f2) { + t.Errorf("Cos(%g) = %g, want %g", _vf[i]+large, f2, f1) + } + } +} + +func TestLargeSin(t *testing.T) { + large := float64(100000 * Pi) + for i := 0; i < len(_vf); i++ { + f1 := _sinTestLarge[i] + f2 := Sin(_vf[i] + large) + if !close(f1, f2) { + t.Errorf("Sin(%g) = %g, want %g", _vf[i]+large, f2, f1) + } + } +} + +func TestLargeSincos(t *testing.T) { + large := float64(100000 * Pi) + for i := 0; i < len(_vf); i++ { + f1, g1 := _sinTestLarge[i], _testCosLarge[i] + f2, g2 := Sincos(_vf[i] + large) + if !close(f1, f2) || !close(g1, g2) { + t.Errorf("Sincos(%g) = %g, %g, want %g, %g", _vf[i]+large, f2, g2, f1, g1) + } + } +} + +func TestLargeTan(t *testing.T) { + large := float64(100000 * Pi) + for i := 0; i < len(_vf); i++ { + f1 := _tanLarge[i] + f2 := Tan(_vf[i] + large) + if !close(f1, f2) { + t.Errorf("Tan(%g) = %g, want %g", _vf[i]+large, f2, f1) + } + } +} + +// Check that trigReduce matches the standard reduction results for input values +// below reduceThreshold. +func TestTrigReduce(t *testing.T) { + inputs := make([]float64, len(_vf)) + // all of the standard inputs + copy(inputs, _vf) + // all of the large inputs + large := float64(100000 * Pi) + for _, v := range _vf { + inputs = append(inputs, v+large) + } + // Also test some special inputs, Pi and right below the reduceThreshold + inputs = append(inputs, float64(Pi), Nextafter(ReduceThreshold, 0)) + for _, x := range inputs { + // reduce the value to compare + j, z := TrigReduce(x) + xred := float64(j)*(Pi/4) + z + + if f, fred := Sin(x), Sin(xred); !close(f, fred) { + t.Errorf("Sin(trigReduce(%g)) != Sin(%g), got %g, want %g", x, x, fred, f) + } + if f, fred := Cos(x), Cos(xred); !close(f, fred) { + t.Errorf("Cos(trigReduce(%g)) != Cos(%g), got %g, want %g", x, x, fred, f) + } + if f, fred := Tan(x), Tan(xred); !close(f, fred) { + t.Errorf(" Tan(trigReduce(%g)) != Tan(%g), got %g, want %g", x, x, fred, f) + } + f, g := Sincos(x) + fred, gred := Sincos(xred) + if !close(f, fred) || !close(g, gred) { + t.Errorf(" Sincos(trigReduce(%g)) != Sincos(%g), got %g, %g, want %g, %g", x, x, fred, gred, f, g) + } + } +} + +// Check that math constants are accepted by compiler +// and have right value (assumes strconv.ParseFloat works). +// https://golang.org/issue/201 + +type floatTest struct { + val interface{} + name string + str string +} + +var floatTests = []floatTest{ + {float64(MaxFloat64), "MaxFloat64", "1.7976931348623157e+308"}, + {float64(SmallestNonzeroFloat64), "SmallestNonzeroFloat64", "5e-324"}, + {float32(MaxFloat32), "MaxFloat32", "3.4028235e+38"}, + {float32(SmallestNonzeroFloat32), "SmallestNonzeroFloat32", "1e-45"}, +} + +func TestFloatMinMax(t *testing.T) { + for _, tt := range floatTests { + s := fmt.Sprint(tt.val) + if s != tt.str { + t.Errorf("Sprint(%v) = %s, want %s", tt.name, s, tt.str) + } + } +} + +func TestFloatMinima(t *testing.T) { + if q := float32(SmallestNonzeroFloat32 / 2); q != 0 { + t.Errorf("float32(SmallestNonzeroFloat32 / 2) = %g, want 0", q) + } + // convert to variable first: https://github.com/gnolang/gno/issues/1150 + smf64 := SmallestNonzeroFloat64 + if q := float64(smf64 / 2); q != 0 { + t.Errorf("float64(SmallestNonzeroFloat64 / 2) = %g, want 0", q) + } +} + +var indirectSqrt = Sqrt + +// TestFloat32Sqrt checks the correctness of the float32 square root optimization result. +func TestFloat32Sqrt(t *testing.T) { + for _, v := range _sqrt32 { + want := float32(indirectSqrt(float64(v))) + got := float32(Sqrt(float64(v))) + if IsNaN(float64(want)) { + if !IsNaN(float64(got)) { + t.Errorf("got=%#v want=NaN, v=%#v", got, v) + } + continue + } + if got != want { + t.Errorf("got=%#v want=%#v, v=%#v", got, want, v) + } + } +} + +// Benchmarks + +// Global exported variables are used to store the +// return values of functions measured in the benchmarks. +// Storing the results in these variables prevents the compiler +// from completely optimizing the benchmarked functions away. +var ( + GlobalI int + GlobalB bool + GlobalF float64 +) + +func BenchmarkAcos(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Acos(.5) + } + GlobalF = x +} + +func BenchmarkAcosh(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Acosh(1.5) + } + GlobalF = x +} + +func BenchmarkAsin(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Asin(.5) + } + GlobalF = x +} + +func BenchmarkAsinh(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Asinh(.5) + } + GlobalF = x +} + +func BenchmarkAtan(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Atan(.5) + } + GlobalF = x +} + +func BenchmarkAtanh(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Atanh(.5) + } + GlobalF = x +} + +func BenchmarkAtan2(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Atan2(.5, 1) + } + GlobalF = x +} + +func BenchmarkCbrt(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Cbrt(10) + } + GlobalF = x +} + +func BenchmarkCeil(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Ceil(.5) + } + GlobalF = x +} + +var copysignNeg = -1.0 + +func BenchmarkCopysign(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Copysign(.5, copysignNeg) + } + GlobalF = x +} + +func BenchmarkCos(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Cos(.5) + } + GlobalF = x +} + +func BenchmarkCosh(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Cosh(2.5) + } + GlobalF = x +} + +func BenchmarkErf(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Erf(.5) + } + GlobalF = x +} + +func BenchmarkErfc(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Erfc(.5) + } + GlobalF = x +} + +func BenchmarkErfinv(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Erfinv(.5) + } + GlobalF = x +} + +func BenchmarkErfcinv(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Erfcinv(.5) + } + GlobalF = x +} + +func BenchmarkExp(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Exp(.5) + } + GlobalF = x +} + +func BenchmarkExpm1(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Expm1(.5) + } + GlobalF = x +} + +func BenchmarkExp2(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Exp2(.5) + } + GlobalF = x +} + +var absPos = .5 + +func BenchmarkAbs(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Abs(absPos) + } + GlobalF = x +} + +func BenchmarkDim(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Dim(GlobalF, x) + } + GlobalF = x +} + +func BenchmarkFloor(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Floor(.5) + } + GlobalF = x +} + +func BenchmarkMax(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Max(10, 3) + } + GlobalF = x +} + +func BenchmarkMin(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Min(10, 3) + } + GlobalF = x +} + +func BenchmarkMod(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Mod(10, 3) + } + GlobalF = x +} + +func BenchmarkFrexp(b *testing.B) { + x := 0.0 + y := 0 + for i := 0; i < b.N; i++ { + x, y = Frexp(8) + } + GlobalF = x + GlobalI = y +} + +func BenchmarkGamma(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Gamma(2.5) + } + GlobalF = x +} + +func BenchmarkHypot(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Hypot(3, 4) + } + GlobalF = x +} + +func BenchmarkIlogb(b *testing.B) { + x := 0 + for i := 0; i < b.N; i++ { + x = Ilogb(.5) + } + GlobalI = x +} + +func BenchmarkJ0(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = J0(2.5) + } + GlobalF = x +} + +func BenchmarkJ1(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = J1(2.5) + } + GlobalF = x +} + +func BenchmarkJn(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Jn(2, 2.5) + } + GlobalF = x +} + +func BenchmarkLdexp(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Ldexp(.5, 2) + } + GlobalF = x +} + +func BenchmarkLgamma(b *testing.B) { + x := 0.0 + y := 0 + for i := 0; i < b.N; i++ { + x, y = Lgamma(2.5) + } + GlobalF = x + GlobalI = y +} + +func BenchmarkLog(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Log(.5) + } + GlobalF = x +} + +func BenchmarkLogb(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Logb(.5) + } + GlobalF = x +} + +func BenchmarkLog1p(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Log1p(.5) + } + GlobalF = x +} + +func BenchmarkLog10(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Log10(.5) + } + GlobalF = x +} + +func BenchmarkLog2(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Log2(.5) + } + GlobalF += x +} + +func BenchmarkModf(b *testing.B) { + x := 0.0 + y := 0.0 + for i := 0; i < b.N; i++ { + x, y = Modf(1.5) + } + GlobalF += x + GlobalF += y +} + +func BenchmarkNextafter32(b *testing.B) { + x := float32(0.0) + for i := 0; i < b.N; i++ { + x = Nextafter32(.5, 1) + } + GlobalF = float64(x) +} + +func BenchmarkNextafter64(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Nextafter(.5, 1) + } + GlobalF = x +} + +func BenchmarkPowInt(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Pow(2, 2) + } + GlobalF = x +} + +func BenchmarkPowFrac(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Pow(2.5, 1.5) + } + GlobalF = x +} + +var pow10pos = int(300) + +func BenchmarkPow10Pos(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Pow10(pow10pos) + } + GlobalF = x +} + +var pow10neg = int(-300) + +func BenchmarkPow10Neg(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Pow10(pow10neg) + } + GlobalF = x +} + +var roundNeg = float64(-2.5) + +func BenchmarkRound(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Round(roundNeg) + } + GlobalF = x +} + +func BenchmarkRoundToEven(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = RoundToEven(roundNeg) + } + GlobalF = x +} + +func BenchmarkRemainder(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Remainder(10, 3) + } + GlobalF = x +} + +var signbitPos = 2.5 + +func BenchmarkSignbit(b *testing.B) { + x := false + for i := 0; i < b.N; i++ { + x = Signbit(signbitPos) + } + GlobalB = x +} + +func BenchmarkSin(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Sin(.5) + } + GlobalF = x +} + +func BenchmarkSincos(b *testing.B) { + x := 0.0 + y := 0.0 + for i := 0; i < b.N; i++ { + x, y = Sincos(.5) + } + GlobalF += x + GlobalF += y +} + +func BenchmarkSinh(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Sinh(2.5) + } + GlobalF = x +} + +func BenchmarkSqrtIndirect(b *testing.B) { + x, y := 0.0, 10.0 + f := Sqrt + for i := 0; i < b.N; i++ { + x += f(y) + } + GlobalF = x +} + +func BenchmarkSqrtLatency(b *testing.B) { + x := 10.0 + for i := 0; i < b.N; i++ { + x = Sqrt(x) + } + GlobalF = x +} + +func BenchmarkSqrtIndirectLatency(b *testing.B) { + x := 10.0 + f := Sqrt + for i := 0; i < b.N; i++ { + x = f(x) + } + GlobalF = x +} + +func isPrime(i int) bool { + // Yes, this is a dumb way to write this code, + // but calling Sqrt repeatedly in this way demonstrates + // the benefit of using a direct SQRT instruction on systems + // that have one, whereas the obvious loop seems not to + // demonstrate such a benefit. + for j := 2; float64(j) <= Sqrt(float64(i)); j++ { + if i%j == 0 { + return false + } + } + return true +} + +func BenchmarkSqrtPrime(b *testing.B) { + x := false + for i := 0; i < b.N; i++ { + x = isPrime(100003) + } + GlobalB = x +} + +func BenchmarkTan(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Tan(.5) + } + GlobalF = x +} + +func BenchmarkTanh(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Tanh(2.5) + } + GlobalF = x +} + +func BenchmarkTrunc(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Trunc(.5) + } + GlobalF = x +} + +func BenchmarkY0(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Y0(2.5) + } + GlobalF = x +} + +func BenchmarkY1(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Y1(2.5) + } + GlobalF = x +} + +func BenchmarkYn(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Yn(2, 2.5) + } + GlobalF = x +} + +func BenchmarkFloat64bits(b *testing.B) { + y := uint64(0) + for i := 0; i < b.N; i++ { + y = Float64bits(roundNeg) + } + GlobalI = int(y) +} + +var _roundUint64 = uint64(5) + +func BenchmarkFloat64frombits(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = Float64frombits(_roundUint64) + } + GlobalF = x +} + +func BenchmarkFMA(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = FMA(E, Pi, x) } + GlobalF = x } diff --git a/gnovm/stdlibs/math/asin.gno b/gnovm/stdlibs/math/asin.gno new file mode 100644 index 00000000000..d99f5664332 --- /dev/null +++ b/gnovm/stdlibs/math/asin.gno @@ -0,0 +1,61 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point arcsine and arccosine. + + They are implemented by computing the arctangent + after appropriate range reduction. +*/ + +// Asin returns the arcsine, in radians, of x. +// +// Special cases are: +// +// Asin(±0) = ±0 +// Asin(x) = NaN if x < -1 or x > 1 +func Asin(x float64) float64 { + return asin(x) +} + +func asin(x float64) float64 { + if x == 0 { + return x // special case + } + sign := false + if x < 0 { + x = -x + sign = true + } + if x > 1 { + return NaN() // special case + } + + temp := Sqrt(1 - x*x) + if x > 0.7 { + temp = Pi/2 - satan(temp/x) + } else { + temp = satan(x / temp) + } + + if sign { + temp = -temp + } + return temp +} + +// Acos returns the arccosine, in radians, of x. +// +// Special case is: +// +// Acos(x) = NaN if x < -1 or x > 1 +func Acos(x float64) float64 { + return acos(x) +} + +func acos(x float64) float64 { + return Pi/2 - Asin(x) +} diff --git a/gnovm/stdlibs/math/asinh.gno b/gnovm/stdlibs/math/asinh.gno new file mode 100644 index 00000000000..e51f0757e62 --- /dev/null +++ b/gnovm/stdlibs/math/asinh.gno @@ -0,0 +1,74 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/s_asinh.c +// and came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// +// asinh(x) +// Method : +// Based on +// asinh(x) = sign(x) * log [ |x| + sqrt(x*x+1) ] +// we have +// asinh(x) := x if 1+x*x=1, +// := sign(x)*(log(x)+ln2)) for large |x|, else +// := sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) if|x|>2, else +// := sign(x)*log1p(|x| + x**2/(1 + sqrt(1+x**2))) +// + +// Asinh returns the inverse hyperbolic sine of x. +// +// Special cases are: +// +// Asinh(±0) = ±0 +// Asinh(±Inf) = ±Inf +// Asinh(NaN) = NaN +func Asinh(x float64) float64 { + return asinh(x) +} + +func asinh(x float64) float64 { + const ( + Ln2 = 6.93147180559945286227e-01 // 0x3FE62E42FEFA39EF + NearZero = 1.0 / (1 << 28) // 2**-28 + Large = 1 << 28 // 2**28 + ) + // special cases + if IsNaN(x) || IsInf(x, 0) { + return x + } + sign := false + if x < 0 { + x = -x + sign = true + } + var temp float64 + switch { + case x > Large: + temp = Log(x) + Ln2 // |x| > 2**28 + case x > 2: + temp = Log(2*x + 1/(Sqrt(x*x+1)+x)) // 2**28 > |x| > 2.0 + case x < NearZero: + temp = x // |x| < 2**-28 + default: + temp = Log1p(x + x*x/(1+Sqrt(1+x*x))) // 2.0 > |x| > 2**-28 + } + if sign { + temp = -temp + } + return temp +} diff --git a/gnovm/stdlibs/math/atan.gno b/gnovm/stdlibs/math/atan.gno new file mode 100644 index 00000000000..23cae2dbfec --- /dev/null +++ b/gnovm/stdlibs/math/atan.gno @@ -0,0 +1,108 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point arctangent. +*/ + +// The original C code, the long comment, and the constants below were +// from http://netlib.sandia.gov/cephes/cmath/atan.c, available from +// http://www.netlib.org/cephes/cmath.tgz. +// The go code is a version of the original C. +// +// atan.c +// Inverse circular tangent (arctangent) +// +// SYNOPSIS: +// double x, y, atan(); +// y = atan( x ); +// +// DESCRIPTION: +// Returns radian angle between -pi/2 and +pi/2 whose tangent is x. +// +// Range reduction is from three intervals into the interval from zero to 0.66. +// The approximant uses a rational function of degree 4/5 of the form +// x + x**3 P(x)/Q(x). +// +// ACCURACY: +// Relative error: +// arithmetic domain # trials peak rms +// DEC -10, 10 50000 2.4e-17 8.3e-18 +// IEEE -10, 10 10^6 1.8e-16 5.0e-17 +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov + +// xatan evaluates a series valid in the range [0, 0.66]. +func xatan(x float64) float64 { + const ( + P0 = -8.750608600031904122785e-01 + P1 = -1.615753718733365076637e+01 + P2 = -7.500855792314704667340e+01 + P3 = -1.228866684490136173410e+02 + P4 = -6.485021904942025371773e+01 + Q0 = +2.485846490142306297962e+01 + Q1 = +1.650270098316988542046e+02 + Q2 = +4.328810604912902668951e+02 + Q3 = +4.853903996359136964868e+02 + Q4 = +1.945506571482613964425e+02 + ) + z := x * x + z = z * ((((P0*z+P1)*z+P2)*z+P3)*z + P4) / (((((z+Q0)*z+Q1)*z+Q2)*z+Q3)*z + Q4) + z = x*z + x + return z +} + +// satan reduces its argument (known to be positive) +// to the range [0, 0.66] and calls xatan. +func satan(x float64) float64 { + const ( + Morebits = 6.123233995736765886130e-17 // pi/2 = PIO2 + Morebits + Tan3pio8 = 2.41421356237309504880 // tan(3*pi/8) + ) + if x <= 0.66 { + return xatan(x) + } + if x > Tan3pio8 { + return Pi/2 - xatan(1/x) + Morebits + } + return Pi/4 + xatan((x-1)/(x+1)) + 0.5*Morebits +} + +// Atan returns the arctangent, in radians, of x. +// +// Special cases are: +// +// Atan(±0) = ±0 +// Atan(±Inf) = ±Pi/2 +func Atan(x float64) float64 { + return atan(x) +} + +func atan(x float64) float64 { + if x == 0 { + return x + } + if x > 0 { + return satan(x) + } + return -satan(-x) +} diff --git a/gnovm/stdlibs/math/atan2.gno b/gnovm/stdlibs/math/atan2.gno new file mode 100644 index 00000000000..f474cf11e03 --- /dev/null +++ b/gnovm/stdlibs/math/atan2.gno @@ -0,0 +1,74 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Atan2 returns the arc tangent of y/x, using +// the signs of the two to determine the quadrant +// of the return value. +// +// Special cases are (in order): +// +// Atan2(y, NaN) = NaN +// Atan2(NaN, x) = NaN +// Atan2(+0, x>=0) = +0 +// Atan2(-0, x>=0) = -0 +// Atan2(+0, x<=-0) = +Pi +// Atan2(-0, x<=-0) = -Pi +// Atan2(y>0, 0) = +Pi/2 +// Atan2(y<0, 0) = -Pi/2 +// Atan2(+Inf, +Inf) = +Pi/4 +// Atan2(-Inf, +Inf) = -Pi/4 +// Atan2(+Inf, -Inf) = 3Pi/4 +// Atan2(-Inf, -Inf) = -3Pi/4 +// Atan2(y, +Inf) = 0 +// Atan2(y>0, -Inf) = +Pi +// Atan2(y<0, -Inf) = -Pi +// Atan2(+Inf, x) = +Pi/2 +// Atan2(-Inf, x) = -Pi/2 +func Atan2(y, x float64) float64 { + return atan2(y, x) +} + +func atan2(y, x float64) float64 { + // special cases + switch { + case IsNaN(y) || IsNaN(x): + return NaN() + case y == 0: + if x >= 0 && !Signbit(x) { + return Copysign(0, y) + } + return Copysign(Pi, y) + case x == 0: + return Copysign(Pi/2, y) + case IsInf(x, 0): + if IsInf(x, 1) { + switch { + case IsInf(y, 0): + return Copysign(Pi/4, y) + default: + return Copysign(0, y) + } + } + switch { + case IsInf(y, 0): + return Copysign(3*Pi/4, y) + default: + return Copysign(Pi, y) + } + case IsInf(y, 0): + return Copysign(Pi/2, y) + } + + // Call atan and determine the quadrant. + q := Atan(y / x) + if x < 0 { + if q <= 0 { + return q + Pi + } + return q - Pi + } + return q +} diff --git a/gnovm/stdlibs/math/atanh.gno b/gnovm/stdlibs/math/atanh.gno new file mode 100644 index 00000000000..dcfe7e56382 --- /dev/null +++ b/gnovm/stdlibs/math/atanh.gno @@ -0,0 +1,82 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/e_atanh.c +// and came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// +// __ieee754_atanh(x) +// Method : +// 1. Reduce x to positive by atanh(-x) = -atanh(x) +// 2. For x>=0.5 +// 1 2x x +// atanh(x) = --- * log(1 + -------) = 0.5 * log1p(2 * --------) +// 2 1 - x 1 - x +// +// For x<0.5 +// atanh(x) = 0.5*log1p(2x+2x*x/(1-x)) +// +// Special cases: +// atanh(x) is NaN if |x| > 1 with signal; +// atanh(NaN) is that NaN with no signal; +// atanh(+-1) is +-INF with signal. +// + +// Atanh returns the inverse hyperbolic tangent of x. +// +// Special cases are: +// +// Atanh(1) = +Inf +// Atanh(±0) = ±0 +// Atanh(-1) = -Inf +// Atanh(x) = NaN if x < -1 or x > 1 +// Atanh(NaN) = NaN +func Atanh(x float64) float64 { + return atanh(x) +} + +func atanh(x float64) float64 { + const NearZero = 1.0 / (1 << 28) // 2**-28 + // special cases + switch { + case x < -1 || x > 1 || IsNaN(x): + return NaN() + case x == 1: + return Inf(1) + case x == -1: + return Inf(-1) + } + sign := false + if x < 0 { + x = -x + sign = true + } + var temp float64 + switch { + case x < NearZero: + temp = x + case x < 0.5: + temp = x + x + temp = 0.5 * Log1p(temp+temp*x/(1-x)) + default: + temp = 0.5 * Log1p((x+x)/(1-x)) + } + if sign { + temp = -temp + } + return temp +} diff --git a/gnovm/stdlibs/math/bits.gno b/gnovm/stdlibs/math/bits.gno index 6cf646d2f5e..c5cb93b1594 100644 --- a/gnovm/stdlibs/math/bits.gno +++ b/gnovm/stdlibs/math/bits.gno @@ -4,10 +4,6 @@ package math -import ( - imath "internal/math" -) - const ( uvnan = 0x7FF8000000000001 uvinf = 0x7FF0000000000000 @@ -28,11 +24,11 @@ func Inf(sign int) float64 { } else { v = uvneginf } - return imath.Float64frombits(v) + return Float64frombits(v) } // NaN returns an IEEE 754 “not-a-number” value. -func NaN() float64 { return imath.Float64frombits(uvnan) } +func NaN() float64 { return Float64frombits(uvnan) } // IsNaN reports whether f is an IEEE 754 “not-a-number” value. func IsNaN(f float64) (is bool) { diff --git a/gnovm/stdlibs/math/bits/bits.gno b/gnovm/stdlibs/math/bits/bits.gno new file mode 100644 index 00000000000..381244a1118 --- /dev/null +++ b/gnovm/stdlibs/math/bits/bits.gno @@ -0,0 +1,604 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run make_tables.go + +// Package bits implements bit counting and manipulation +// functions for the predeclared unsigned integer types. +// +// Functions in this package may be implemented directly by +// the compiler, for better performance. For those functions +// the code in this package will not be used. Which +// functions are implemented by the compiler depends on the +// architecture and the Go release. +package bits + +const uintSize = 32 << (^uint(0) >> 63) // 32 or 64 + +// UintSize is the size of a uint in bits. +const UintSize = uintSize + +// --- LeadingZeros --- + +// LeadingZeros returns the number of leading zero bits in x; the result is UintSize for x == 0. +func LeadingZeros(x uint) int { return UintSize - Len(x) } + +// LeadingZeros8 returns the number of leading zero bits in x; the result is 8 for x == 0. +func LeadingZeros8(x uint8) int { return 8 - Len8(x) } + +// LeadingZeros16 returns the number of leading zero bits in x; the result is 16 for x == 0. +func LeadingZeros16(x uint16) int { return 16 - Len16(x) } + +// LeadingZeros32 returns the number of leading zero bits in x; the result is 32 for x == 0. +func LeadingZeros32(x uint32) int { return 32 - Len32(x) } + +// LeadingZeros64 returns the number of leading zero bits in x; the result is 64 for x == 0. +func LeadingZeros64(x uint64) int { return 64 - Len64(x) } + +// --- TrailingZeros --- + +// See http://supertech.csail.mit.edu/papers/debruijn.pdf +const deBruijn32 = 0x077CB531 + +var deBruijn32tab = [32]byte{ + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9, +} + +const deBruijn64 = 0x03f79d71b4ca8b09 + +var deBruijn64tab = [64]byte{ + 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, + 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, + 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, + 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, +} + +// TrailingZeros returns the number of trailing zero bits in x; the result is UintSize for x == 0. +func TrailingZeros(x uint) int { + if UintSize == 32 { + return TrailingZeros32(uint32(x)) + } + return TrailingZeros64(uint64(x)) +} + +// TrailingZeros8 returns the number of trailing zero bits in x; the result is 8 for x == 0. +func TrailingZeros8(x uint8) int { + return int(ntz8tab[x]) +} + +// TrailingZeros16 returns the number of trailing zero bits in x; the result is 16 for x == 0. +func TrailingZeros16(x uint16) int { + if x == 0 { + return 16 + } + // see comment in TrailingZeros64 + return int(deBruijn32tab[uint32(x&-x)*deBruijn32>>(32-5)]) +} + +// TrailingZeros32 returns the number of trailing zero bits in x; the result is 32 for x == 0. +func TrailingZeros32(x uint32) int { + if x == 0 { + return 32 + } + // see comment in TrailingZeros64 + return int(deBruijn32tab[(x&-x)*deBruijn32>>(32-5)]) +} + +// TrailingZeros64 returns the number of trailing zero bits in x; the result is 64 for x == 0. +func TrailingZeros64(x uint64) int { + if x == 0 { + return 64 + } + // If popcount is fast, replace code below with return popcount(^x & (x - 1)). + // + // x & -x leaves only the right-most bit set in the word. Let k be the + // index of that bit. Since only a single bit is set, the value is two + // to the power of k. Multiplying by a power of two is equivalent to + // left shifting, in this case by k bits. The de Bruijn (64 bit) constant + // is such that all six bit, consecutive substrings are distinct. + // Therefore, if we have a left shifted version of this constant we can + // find by how many bits it was shifted by looking at which six bit + // substring ended up at the top of the word. + // (Knuth, volume 4, section 7.3.1) + return int(deBruijn64tab[(x&-x)*deBruijn64>>(64-6)]) +} + +// --- OnesCount --- + +const ( + m0 = 0x5555555555555555 // 01010101 ... + m1 = 0x3333333333333333 // 00110011 ... + m2 = 0x0f0f0f0f0f0f0f0f // 00001111 ... + m3 = 0x00ff00ff00ff00ff // etc. + m4 = 0x0000ffff0000ffff +) + +// OnesCount returns the number of one bits ("population count") in x. +func OnesCount(x uint) int { + if UintSize == 32 { + return OnesCount32(uint32(x)) + } + return OnesCount64(uint64(x)) +} + +// OnesCount8 returns the number of one bits ("population count") in x. +func OnesCount8(x uint8) int { + return int(pop8tab[x]) +} + +// OnesCount16 returns the number of one bits ("population count") in x. +func OnesCount16(x uint16) int { + return int(pop8tab[x>>8] + pop8tab[x&0xff]) +} + +// OnesCount32 returns the number of one bits ("population count") in x. +func OnesCount32(x uint32) int { + return int(pop8tab[x>>24] + pop8tab[x>>16&0xff] + pop8tab[x>>8&0xff] + pop8tab[x&0xff]) +} + +// OnesCount64 returns the number of one bits ("population count") in x. +func OnesCount64(x uint64) int { + // Implementation: Parallel summing of adjacent bits. + // See "Hacker's Delight", Chap. 5: Counting Bits. + // The following pattern shows the general approach: + // + // x = x>>1&(m0&m) + x&(m0&m) + // x = x>>2&(m1&m) + x&(m1&m) + // x = x>>4&(m2&m) + x&(m2&m) + // x = x>>8&(m3&m) + x&(m3&m) + // x = x>>16&(m4&m) + x&(m4&m) + // x = x>>32&(m5&m) + x&(m5&m) + // return int(x) + // + // Masking (& operations) can be left away when there's no + // danger that a field's sum will carry over into the next + // field: Since the result cannot be > 64, 8 bits is enough + // and we can ignore the masks for the shifts by 8 and up. + // Per "Hacker's Delight", the first line can be simplified + // more, but it saves at best one instruction, so we leave + // it alone for clarity. + const m = 1<<64 - 1 + x = x>>1&(m0&m) + x&(m0&m) + x = x>>2&(m1&m) + x&(m1&m) + x = (x>>4 + x) & (m2 & m) + x += x >> 8 + x += x >> 16 + x += x >> 32 + return int(x) & (1<<7 - 1) +} + +// --- RotateLeft --- + +// RotateLeft returns the value of x rotated left by (k mod UintSize) bits. +// To rotate x right by k bits, call RotateLeft(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft(x uint, k int) uint { + if UintSize == 32 { + return uint(RotateLeft32(uint32(x), k)) + } + return uint(RotateLeft64(uint64(x), k)) +} + +// RotateLeft8 returns the value of x rotated left by (k mod 8) bits. +// To rotate x right by k bits, call RotateLeft8(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft8(x uint8, k int) uint8 { + const n = 8 + s := uint(k) & (n - 1) + return x<>(n-s) +} + +// RotateLeft16 returns the value of x rotated left by (k mod 16) bits. +// To rotate x right by k bits, call RotateLeft16(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft16(x uint16, k int) uint16 { + const n = 16 + s := uint(k) & (n - 1) + return x<>(n-s) +} + +// RotateLeft32 returns the value of x rotated left by (k mod 32) bits. +// To rotate x right by k bits, call RotateLeft32(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft32(x uint32, k int) uint32 { + const n = 32 + s := uint(k) & (n - 1) + return x<>(n-s) +} + +// RotateLeft64 returns the value of x rotated left by (k mod 64) bits. +// To rotate x right by k bits, call RotateLeft64(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft64(x uint64, k int) uint64 { + const n = 64 + s := uint(k) & (n - 1) + return x<>(n-s) +} + +// --- Reverse --- + +// Reverse returns the value of x with its bits in reversed order. +func Reverse(x uint) uint { + if UintSize == 32 { + return uint(Reverse32(uint32(x))) + } + return uint(Reverse64(uint64(x))) +} + +// Reverse8 returns the value of x with its bits in reversed order. +func Reverse8(x uint8) uint8 { + return rev8tab[x] +} + +// Reverse16 returns the value of x with its bits in reversed order. +func Reverse16(x uint16) uint16 { + return uint16(rev8tab[x>>8]) | uint16(rev8tab[x&0xff])<<8 +} + +// Reverse32 returns the value of x with its bits in reversed order. +func Reverse32(x uint32) uint32 { + const m = 1<<32 - 1 + x = x>>1&(m0&m) | x&(m0&m)<<1 + x = x>>2&(m1&m) | x&(m1&m)<<2 + x = x>>4&(m2&m) | x&(m2&m)<<4 + return ReverseBytes32(x) +} + +// Reverse64 returns the value of x with its bits in reversed order. +func Reverse64(x uint64) uint64 { + const m = 1<<64 - 1 + x = x>>1&(m0&m) | x&(m0&m)<<1 + x = x>>2&(m1&m) | x&(m1&m)<<2 + x = x>>4&(m2&m) | x&(m2&m)<<4 + return ReverseBytes64(x) +} + +// --- ReverseBytes --- + +// ReverseBytes returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +func ReverseBytes(x uint) uint { + if UintSize == 32 { + return uint(ReverseBytes32(uint32(x))) + } + return uint(ReverseBytes64(uint64(x))) +} + +// ReverseBytes16 returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +func ReverseBytes16(x uint16) uint16 { + return x>>8 | x<<8 +} + +// ReverseBytes32 returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +func ReverseBytes32(x uint32) uint32 { + const m = 1<<32 - 1 + x = x>>8&(m3&m) | x&(m3&m)<<8 + return x>>16 | x<<16 +} + +// ReverseBytes64 returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +func ReverseBytes64(x uint64) uint64 { + const m = 1<<64 - 1 + x = x>>8&(m3&m) | x&(m3&m)<<8 + x = x>>16&(m4&m) | x&(m4&m)<<16 + return x>>32 | x<<32 +} + +// --- Len --- + +// Len returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len(x uint) int { + if UintSize == 32 { + return Len32(uint32(x)) + } + return Len64(uint64(x)) +} + +// Len8 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len8(x uint8) int { + return int(len8tab[x]) +} + +// Len16 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len16(x uint16) (n int) { + if x >= 1<<8 { + x >>= 8 + n = 8 + } + return n + int(len8tab[x]) +} + +// Len32 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len32(x uint32) (n int) { + if x >= 1<<16 { + x >>= 16 + n = 16 + } + if x >= 1<<8 { + x >>= 8 + n += 8 + } + return n + int(len8tab[x]) +} + +// Len64 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len64(x uint64) (n int) { + if x >= 1<<32 { + x >>= 32 + n = 32 + } + if x >= 1<<16 { + x >>= 16 + n += 16 + } + if x >= 1<<8 { + x >>= 8 + n += 8 + } + return n + int(len8tab[x]) +} + +// --- Add with carry --- + +// Add returns the sum with carry of x, y and carry: sum = x + y + carry. +// The carry input must be 0 or 1; otherwise the behavior is undefined. +// The carryOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Add(x, y, carry uint) (sum, carryOut uint) { + if UintSize == 32 { + s32, c32 := Add32(uint32(x), uint32(y), uint32(carry)) + return uint(s32), uint(c32) + } + s64, c64 := Add64(uint64(x), uint64(y), uint64(carry)) + return uint(s64), uint(c64) +} + +// Add32 returns the sum with carry of x, y and carry: sum = x + y + carry. +// The carry input must be 0 or 1; otherwise the behavior is undefined. +// The carryOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Add32(x, y, carry uint32) (sum, carryOut uint32) { + sum64 := uint64(x) + uint64(y) + uint64(carry) + sum = uint32(sum64) + carryOut = uint32(sum64 >> 32) + return +} + +// Add64 returns the sum with carry of x, y and carry: sum = x + y + carry. +// The carry input must be 0 or 1; otherwise the behavior is undefined. +// The carryOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Add64(x, y, carry uint64) (sum, carryOut uint64) { + sum = x + y + carry + // The sum will overflow if both top bits are set (x & y) or if one of them + // is (x | y), and a carry from the lower place happened. If such a carry + // happens, the top bit will be 1 + 0 + 1 = 0 (&^ sum). + carryOut = ((x & y) | ((x | y) &^ sum)) >> 63 + return +} + +// --- Subtract with borrow --- + +// Sub returns the difference of x, y and borrow: diff = x - y - borrow. +// The borrow input must be 0 or 1; otherwise the behavior is undefined. +// The borrowOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Sub(x, y, borrow uint) (diff, borrowOut uint) { + if UintSize == 32 { + d32, b32 := Sub32(uint32(x), uint32(y), uint32(borrow)) + return uint(d32), uint(b32) + } + d64, b64 := Sub64(uint64(x), uint64(y), uint64(borrow)) + return uint(d64), uint(b64) +} + +// Sub32 returns the difference of x, y and borrow, diff = x - y - borrow. +// The borrow input must be 0 or 1; otherwise the behavior is undefined. +// The borrowOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Sub32(x, y, borrow uint32) (diff, borrowOut uint32) { + diff = x - y - borrow + // The difference will underflow if the top bit of x is not set and the top + // bit of y is set (^x & y) or if they are the same (^(x ^ y)) and a borrow + // from the lower place happens. If that borrow happens, the result will be + // 1 - 1 - 1 = 0 - 0 - 1 = 1 (& diff). + borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 31 + return +} + +// Sub64 returns the difference of x, y and borrow: diff = x - y - borrow. +// The borrow input must be 0 or 1; otherwise the behavior is undefined. +// The borrowOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Sub64(x, y, borrow uint64) (diff, borrowOut uint64) { + diff = x - y - borrow + // See Sub32 for the bit logic. + borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63 + return +} + +// --- Full-width multiply --- + +// Mul returns the full-width product of x and y: (hi, lo) = x * y +// with the product bits' upper half returned in hi and the lower +// half returned in lo. +// +// This function's execution time does not depend on the inputs. +func Mul(x, y uint) (hi, lo uint) { + if UintSize == 32 { + h, l := Mul32(uint32(x), uint32(y)) + return uint(h), uint(l) + } + h, l := Mul64(uint64(x), uint64(y)) + return uint(h), uint(l) +} + +// Mul32 returns the 64-bit product of x and y: (hi, lo) = x * y +// with the product bits' upper half returned in hi and the lower +// half returned in lo. +// +// This function's execution time does not depend on the inputs. +func Mul32(x, y uint32) (hi, lo uint32) { + tmp := uint64(x) * uint64(y) + hi, lo = uint32(tmp>>32), uint32(tmp) + return +} + +// Mul64 returns the 128-bit product of x and y: (hi, lo) = x * y +// with the product bits' upper half returned in hi and the lower +// half returned in lo. +// +// This function's execution time does not depend on the inputs. +func Mul64(x, y uint64) (hi, lo uint64) { + const mask32 = 1<<32 - 1 + x0 := x & mask32 + x1 := x >> 32 + y0 := y & mask32 + y1 := y >> 32 + w0 := x0 * y0 + t := x1*y0 + w0>>32 + w1 := t & mask32 + w2 := t >> 32 + w1 += x0 * y1 + hi = x1*y1 + w2 + w1>>32 + lo = x * y + return +} + +// --- Full-width divide --- + +// Div returns the quotient and remainder of (hi, lo) divided by y: +// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper +// half in parameter hi and the lower half in parameter lo. +// Div panics for y == 0 (division by zero) or y <= hi (quotient overflow). +func Div(hi, lo, y uint) (quo, rem uint) { + if UintSize == 32 { + q, r := Div32(uint32(hi), uint32(lo), uint32(y)) + return uint(q), uint(r) + } + q, r := Div64(uint64(hi), uint64(lo), uint64(y)) + return uint(q), uint(r) +} + +// Div32 returns the quotient and remainder of (hi, lo) divided by y: +// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper +// half in parameter hi and the lower half in parameter lo. +// Div32 panics for y == 0 (division by zero) or y <= hi (quotient overflow). +func Div32(hi, lo, y uint32) (quo, rem uint32) { + if y != 0 && y <= hi { + panic(overflowError) + } + if y == 0 { + panic(divideError) + } + z := uint64(hi)<<32 | uint64(lo) + quo, rem = uint32(z/uint64(y)), uint32(z%uint64(y)) + return +} + +// Div64 returns the quotient and remainder of (hi, lo) divided by y: +// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper +// half in parameter hi and the lower half in parameter lo. +// Div64 panics for y == 0 (division by zero) or y <= hi (quotient overflow). +func Div64(hi, lo, y uint64) (quo, rem uint64) { + if y == 0 { + panic(divideError) + } + if y <= hi { + panic(overflowError) + } + + // If high part is zero, we can directly return the results. + if hi == 0 { + return lo / y, lo % y + } + + s := uint(LeadingZeros64(y)) + y <<= s + + const ( + two32 = 1 << 32 + mask32 = two32 - 1 + ) + yn1 := y >> 32 + yn0 := y & mask32 + un32 := hi<>(64-s) + un10 := lo << s + un1 := un10 >> 32 + un0 := un10 & mask32 + q1 := un32 / yn1 + rhat := un32 - q1*yn1 + + for q1 >= two32 || q1*yn0 > two32*rhat+un1 { + q1-- + rhat += yn1 + if rhat >= two32 { + break + } + } + + un21 := un32*two32 + un1 - q1*y + q0 := un21 / yn1 + rhat = un21 - q0*yn1 + + for q0 >= two32 || q0*yn0 > two32*rhat+un0 { + q0-- + rhat += yn1 + if rhat >= two32 { + break + } + } + + return q1*two32 + q0, (un21*two32 + un0 - q0*y) >> s +} + +// Rem returns the remainder of (hi, lo) divided by y. Rem panics for +// y == 0 (division by zero) but, unlike Div, it doesn't panic on a +// quotient overflow. +func Rem(hi, lo, y uint) uint { + if UintSize == 32 { + return uint(Rem32(uint32(hi), uint32(lo), uint32(y))) + } + return uint(Rem64(uint64(hi), uint64(lo), uint64(y))) +} + +// Rem32 returns the remainder of (hi, lo) divided by y. Rem32 panics +// for y == 0 (division by zero) but, unlike Div32, it doesn't panic +// on a quotient overflow. +func Rem32(hi, lo, y uint32) uint32 { + return uint32((uint64(hi)<<32 | uint64(lo)) % uint64(y)) +} + +// Rem64 returns the remainder of (hi, lo) divided by y. Rem64 panics +// for y == 0 (division by zero) but, unlike Div64, it doesn't panic +// on a quotient overflow. +func Rem64(hi, lo, y uint64) uint64 { + // We scale down hi so that hi < y, then use Div64 to compute the + // rem with the guarantee that it won't panic on quotient overflow. + // Given that + // hi ≡ hi%y (mod y) + // we have + // hi<<64 + lo ≡ (hi%y)<<64 + lo (mod y) + _, rem := Div64(hi%y, lo, y) + return rem +} diff --git a/gnovm/stdlibs/math/bits/bits_errors.gno b/gnovm/stdlibs/math/bits/bits_errors.gno new file mode 100644 index 00000000000..cf8a5198b2f --- /dev/null +++ b/gnovm/stdlibs/math/bits/bits_errors.gno @@ -0,0 +1,13 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bits + +import ( + "errors" +) + +var overflowError = errors.New("math/bits: integer overflow") + +var divideError = errors.New("math/bits: integer divide by zero") diff --git a/gnovm/stdlibs/math/bits/bits_tables.gno b/gnovm/stdlibs/math/bits/bits_tables.gno new file mode 100644 index 00000000000..f869b8d5c37 --- /dev/null +++ b/gnovm/stdlibs/math/bits/bits_tables.gno @@ -0,0 +1,79 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by go run make_tables.go. DO NOT EDIT. + +package bits + +const ntz8tab = "" + + "\x08\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x06\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x07\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x06\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + +const pop8tab = "" + + "\x00\x01\x01\x02\x01\x02\x02\x03\x01\x02\x02\x03\x02\x03\x03\x04" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x04\x05\x05\x06\x05\x06\x06\x07\x05\x06\x06\x07\x06\x07\x07\x08" + +const rev8tab = "" + + "\x00\x80\x40\xc0\x20\xa0\x60\xe0\x10\x90\x50\xd0\x30\xb0\x70\xf0" + + "\x08\x88\x48\xc8\x28\xa8\x68\xe8\x18\x98\x58\xd8\x38\xb8\x78\xf8" + + "\x04\x84\x44\xc4\x24\xa4\x64\xe4\x14\x94\x54\xd4\x34\xb4\x74\xf4" + + "\x0c\x8c\x4c\xcc\x2c\xac\x6c\xec\x1c\x9c\x5c\xdc\x3c\xbc\x7c\xfc" + + "\x02\x82\x42\xc2\x22\xa2\x62\xe2\x12\x92\x52\xd2\x32\xb2\x72\xf2" + + "\x0a\x8a\x4a\xca\x2a\xaa\x6a\xea\x1a\x9a\x5a\xda\x3a\xba\x7a\xfa" + + "\x06\x86\x46\xc6\x26\xa6\x66\xe6\x16\x96\x56\xd6\x36\xb6\x76\xf6" + + "\x0e\x8e\x4e\xce\x2e\xae\x6e\xee\x1e\x9e\x5e\xde\x3e\xbe\x7e\xfe" + + "\x01\x81\x41\xc1\x21\xa1\x61\xe1\x11\x91\x51\xd1\x31\xb1\x71\xf1" + + "\x09\x89\x49\xc9\x29\xa9\x69\xe9\x19\x99\x59\xd9\x39\xb9\x79\xf9" + + "\x05\x85\x45\xc5\x25\xa5\x65\xe5\x15\x95\x55\xd5\x35\xb5\x75\xf5" + + "\x0d\x8d\x4d\xcd\x2d\xad\x6d\xed\x1d\x9d\x5d\xdd\x3d\xbd\x7d\xfd" + + "\x03\x83\x43\xc3\x23\xa3\x63\xe3\x13\x93\x53\xd3\x33\xb3\x73\xf3" + + "\x0b\x8b\x4b\xcb\x2b\xab\x6b\xeb\x1b\x9b\x5b\xdb\x3b\xbb\x7b\xfb" + + "\x07\x87\x47\xc7\x27\xa7\x67\xe7\x17\x97\x57\xd7\x37\xb7\x77\xf7" + + "\x0f\x8f\x4f\xcf\x2f\xaf\x6f\xef\x1f\x9f\x5f\xdf\x3f\xbf\x7f\xff" + +const len8tab = "" + + "\x00\x01\x02\x02\x03\x03\x03\x03\x04\x04\x04\x04\x04\x04\x04\x04" + + "\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05" + + "\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06" + + "\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" diff --git a/gnovm/stdlibs/math/bits/bits_test.gno b/gnovm/stdlibs/math/bits/bits_test.gno new file mode 100644 index 00000000000..2efa8874c66 --- /dev/null +++ b/gnovm/stdlibs/math/bits/bits_test.gno @@ -0,0 +1,1332 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bits + +import ( + "testing" +) + +func TestLeadingZeros(t *testing.T) { + for i := 0; i < 256; i++ { + nlz := tab[i].nlz + for k := 0; k < 64-8; k++ { + x := uint64(i) << uint(k) + if x <= 1<<8-1 { + got := LeadingZeros8(uint8(x)) + want := nlz - k + (8 - 8) + if x == 0 { + want = 8 + } + if got != want { + t.Fatalf("LeadingZeros8(%#02x) == %d; want %d", x, got, want) + } + } + + if x <= 1<<16-1 { + got := LeadingZeros16(uint16(x)) + want := nlz - k + (16 - 8) + if x == 0 { + want = 16 + } + if got != want { + t.Fatalf("LeadingZeros16(%#04x) == %d; want %d", x, got, want) + } + } + + if x <= 1<<32-1 { + got := LeadingZeros32(uint32(x)) + want := nlz - k + (32 - 8) + if x == 0 { + want = 32 + } + if got != want { + t.Fatalf("LeadingZeros32(%#08x) == %d; want %d", x, got, want) + } + if UintSize == 32 { + got = LeadingZeros(uint(x)) + if got != want { + t.Fatalf("LeadingZeros(%#08x) == %d; want %d", x, got, want) + } + } + } + + if x <= 1<<64-1 { + got := LeadingZeros64(uint64(x)) + want := nlz - k + (64 - 8) + if x == 0 { + want = 64 + } + if got != want { + t.Fatalf("LeadingZeros64(%#016x) == %d; want %d", x, got, want) + } + if UintSize == 64 { + got = LeadingZeros(uint(x)) + if got != want { + t.Fatalf("LeadingZeros(%#016x) == %d; want %d", x, got, want) + } + } + } + } + } +} + +// Exported (global) variable serving as input for some +// of the benchmarks to ensure side-effect free calls +// are not optimized away. +var Input uint64 = DeBruijn64 + +// Exported (global) variable to store function results +// during benchmarking to ensure side-effect free calls +// are not optimized away. +var Output int + +func BenchmarkLeadingZeros(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += LeadingZeros(uint(Input) >> (uint(i) % UintSize)) + } + Output = s +} + +func BenchmarkLeadingZeros8(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += LeadingZeros8(uint8(Input) >> (uint(i) % 8)) + } + Output = s +} + +func BenchmarkLeadingZeros16(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += LeadingZeros16(uint16(Input) >> (uint(i) % 16)) + } + Output = s +} + +func BenchmarkLeadingZeros32(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += LeadingZeros32(uint32(Input) >> (uint(i) % 32)) + } + Output = s +} + +func BenchmarkLeadingZeros64(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += LeadingZeros64(uint64(Input) >> (uint(i) % 64)) + } + Output = s +} + +func TestTrailingZeros(t *testing.T) { + for i := 0; i < 256; i++ { + ntz := tab[i].ntz + for k := 0; k < 64-8; k++ { + x := uint64(i) << uint(k) + want := ntz + k + if x <= 1<<8-1 { + got := TrailingZeros8(uint8(x)) + if x == 0 { + want = 8 + } + if got != want { + t.Fatalf("TrailingZeros8(%#02x) == %d; want %d", x, got, want) + } + } + + if x <= 1<<16-1 { + got := TrailingZeros16(uint16(x)) + if x == 0 { + want = 16 + } + if got != want { + t.Fatalf("TrailingZeros16(%#04x) == %d; want %d", x, got, want) + } + } + + if x <= 1<<32-1 { + got := TrailingZeros32(uint32(x)) + if x == 0 { + want = 32 + } + if got != want { + t.Fatalf("TrailingZeros32(%#08x) == %d; want %d", x, got, want) + } + if UintSize == 32 { + got = TrailingZeros(uint(x)) + if got != want { + t.Fatalf("TrailingZeros(%#08x) == %d; want %d", x, got, want) + } + } + } + + if x <= 1<<64-1 { + got := TrailingZeros64(uint64(x)) + if x == 0 { + want = 64 + } + if got != want { + t.Fatalf("TrailingZeros64(%#016x) == %d; want %d", x, got, want) + } + if UintSize == 64 { + got = TrailingZeros(uint(x)) + if got != want { + t.Fatalf("TrailingZeros(%#016x) == %d; want %d", x, got, want) + } + } + } + } + } +} + +func BenchmarkTrailingZeros(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += TrailingZeros(uint(Input) << (uint(i) % UintSize)) + } + Output = s +} + +func BenchmarkTrailingZeros8(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += TrailingZeros8(uint8(Input) << (uint(i) % 8)) + } + Output = s +} + +func BenchmarkTrailingZeros16(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += TrailingZeros16(uint16(Input) << (uint(i) % 16)) + } + Output = s +} + +func BenchmarkTrailingZeros32(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += TrailingZeros32(uint32(Input) << (uint(i) % 32)) + } + Output = s +} + +func BenchmarkTrailingZeros64(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += TrailingZeros64(uint64(Input) << (uint(i) % 64)) + } + Output = s +} + +func TestOnesCount(t *testing.T) { + var x uint64 + for i := 0; i <= 64; i++ { + testOnesCount(t, x, i) + x = x<<1 | 1 + } + + for i := 64; i >= 0; i-- { + testOnesCount(t, x, i) + x = x << 1 + } + + for i := 0; i < 256; i++ { + for k := 0; k < 64-8; k++ { + testOnesCount(t, uint64(i)<>(8-k&0x7) + if got8 != want8 { + t.Fatalf("RotateLeft8(%#02x, %d) == %#02x; want %#02x", x8, k, got8, want8) + } + got8 = RotateLeft8(want8, -int(k)) + if got8 != x8 { + t.Fatalf("RotateLeft8(%#02x, -%d) == %#02x; want %#02x", want8, k, got8, x8) + } + + x16 := uint16(m) + got16 := RotateLeft16(x16, int(k)) + want16 := x16<<(k&0xf) | x16>>(16-k&0xf) + if got16 != want16 { + t.Fatalf("RotateLeft16(%#04x, %d) == %#04x; want %#04x", x16, k, got16, want16) + } + got16 = RotateLeft16(want16, -int(k)) + if got16 != x16 { + t.Fatalf("RotateLeft16(%#04x, -%d) == %#04x; want %#04x", want16, k, got16, x16) + } + + x32 := uint32(m) + got32 := RotateLeft32(x32, int(k)) + want32 := x32<<(k&0x1f) | x32>>(32-k&0x1f) + if got32 != want32 { + t.Fatalf("RotateLeft32(%#08x, %d) == %#08x; want %#08x", x32, k, got32, want32) + } + got32 = RotateLeft32(want32, -int(k)) + if got32 != x32 { + t.Fatalf("RotateLeft32(%#08x, -%d) == %#08x; want %#08x", want32, k, got32, x32) + } + if UintSize == 32 { + x := uint(m) + got := RotateLeft(x, int(k)) + want := x<<(k&0x1f) | x>>(32-k&0x1f) + if got != want { + t.Fatalf("RotateLeft(%#08x, %d) == %#08x; want %#08x", x, k, got, want) + } + got = RotateLeft(want, -int(k)) + if got != x { + t.Fatalf("RotateLeft(%#08x, -%d) == %#08x; want %#08x", want, k, got, x) + } + } + + x64 := uint64(m) + got64 := RotateLeft64(x64, int(k)) + want64 := x64<<(k&0x3f) | x64>>(64-k&0x3f) + if got64 != want64 { + t.Fatalf("RotateLeft64(%#016x, %d) == %#016x; want %#016x", x64, k, got64, want64) + } + got64 = RotateLeft64(want64, -int(k)) + if got64 != x64 { + t.Fatalf("RotateLeft64(%#016x, -%d) == %#016x; want %#016x", want64, k, got64, x64) + } + if UintSize == 64 { + x := uint(m) + got := RotateLeft(x, int(k)) + want := x<<(k&0x3f) | x>>(64-k&0x3f) + if got != want { + t.Fatalf("RotateLeft(%#016x, %d) == %#016x; want %#016x", x, k, got, want) + } + got = RotateLeft(want, -int(k)) + if got != x { + t.Fatalf("RotateLeft(%#08x, -%d) == %#08x; want %#08x", want, k, got, x) + } + } + } +} + +func BenchmarkRotateLeft(b *testing.B) { + var s uint + for i := 0; i < b.N; i++ { + s += RotateLeft(uint(Input), i) + } + Output = int(s) +} + +func BenchmarkRotateLeft8(b *testing.B) { + var s uint8 + for i := 0; i < b.N; i++ { + s += RotateLeft8(uint8(Input), i) + } + Output = int(s) +} + +func BenchmarkRotateLeft16(b *testing.B) { + var s uint16 + for i := 0; i < b.N; i++ { + s += RotateLeft16(uint16(Input), i) + } + Output = int(s) +} + +func BenchmarkRotateLeft32(b *testing.B) { + var s uint32 + for i := 0; i < b.N; i++ { + s += RotateLeft32(uint32(Input), i) + } + Output = int(s) +} + +func BenchmarkRotateLeft64(b *testing.B) { + var s uint64 + for i := 0; i < b.N; i++ { + s += RotateLeft64(uint64(Input), i) + } + Output = int(s) +} + +func TestReverse(t *testing.T) { + // test each bit + for i := uint(0); i < 64; i++ { + testReverse(t, uint64(1)<> (64 - 8)) + if got8 != want8 { + t.Fatalf("Reverse8(%#02x) == %#02x; want %#02x", x8, got8, want8) + } + + x16 := uint16(x64) + got16 := Reverse16(x16) + want16 := uint16(want64 >> (64 - 16)) + if got16 != want16 { + t.Fatalf("Reverse16(%#04x) == %#04x; want %#04x", x16, got16, want16) + } + + x32 := uint32(x64) + got32 := Reverse32(x32) + want32 := uint32(want64 >> (64 - 32)) + if got32 != want32 { + t.Fatalf("Reverse32(%#08x) == %#08x; want %#08x", x32, got32, want32) + } + if UintSize == 32 { + x := uint(x32) + got := Reverse(x) + want := uint(want32) + if got != want { + t.Fatalf("Reverse(%#08x) == %#08x; want %#08x", x, got, want) + } + } + + got64 := Reverse64(x64) + if got64 != want64 { + t.Fatalf("Reverse64(%#016x) == %#016x; want %#016x", x64, got64, want64) + } + if UintSize == 64 { + x := uint(x64) + got := Reverse(x) + want := uint(want64) + if got != want { + t.Fatalf("Reverse(%#08x) == %#016x; want %#016x", x, got, want) + } + } +} + +func BenchmarkReverse(b *testing.B) { + var s uint + for i := 0; i < b.N; i++ { + s += Reverse(uint(i)) + } + Output = int(s) +} + +func BenchmarkReverse8(b *testing.B) { + var s uint8 + for i := 0; i < b.N; i++ { + s += Reverse8(uint8(i)) + } + Output = int(s) +} + +func BenchmarkReverse16(b *testing.B) { + var s uint16 + for i := 0; i < b.N; i++ { + s += Reverse16(uint16(i)) + } + Output = int(s) +} + +func BenchmarkReverse32(b *testing.B) { + var s uint32 + for i := 0; i < b.N; i++ { + s += Reverse32(uint32(i)) + } + Output = int(s) +} + +func BenchmarkReverse64(b *testing.B) { + var s uint64 + for i := 0; i < b.N; i++ { + s += Reverse64(uint64(i)) + } + Output = int(s) +} + +func TestReverseBytes(t *testing.T) { + for _, test := range []struct { + x, r uint64 + }{ + {0, 0}, + {0x01, 0x01 << 56}, + {0x0123, 0x2301 << 48}, + {0x012345, 0x452301 << 40}, + {0x01234567, 0x67452301 << 32}, + {0x0123456789, 0x8967452301 << 24}, + {0x0123456789ab, 0xab8967452301 << 16}, + {0x0123456789abcd, 0xcdab8967452301 << 8}, + {0x0123456789abcdef, 0xefcdab8967452301 << 0}, + } { + testReverseBytes(t, test.x, test.r) + testReverseBytes(t, test.r, test.x) + } +} + +func testReverseBytes(t *testing.T, x64, want64 uint64) { + x16 := uint16(x64) + got16 := ReverseBytes16(x16) + want16 := uint16(want64 >> (64 - 16)) + if got16 != want16 { + t.Fatalf("ReverseBytes16(%#04x) == %#04x; want %#04x", x16, got16, want16) + } + + x32 := uint32(x64) + got32 := ReverseBytes32(x32) + want32 := uint32(want64 >> (64 - 32)) + if got32 != want32 { + t.Fatalf("ReverseBytes32(%#08x) == %#08x; want %#08x", x32, got32, want32) + } + if UintSize == 32 { + x := uint(x32) + got := ReverseBytes(x) + want := uint(want32) + if got != want { + t.Fatalf("ReverseBytes(%#08x) == %#08x; want %#08x", x, got, want) + } + } + + got64 := ReverseBytes64(x64) + if got64 != want64 { + t.Fatalf("ReverseBytes64(%#016x) == %#016x; want %#016x", x64, got64, want64) + } + if UintSize == 64 { + x := uint(x64) + got := ReverseBytes(x) + want := uint(want64) + if got != want { + t.Fatalf("ReverseBytes(%#016x) == %#016x; want %#016x", x, got, want) + } + } +} + +func BenchmarkReverseBytes(b *testing.B) { + var s uint + for i := 0; i < b.N; i++ { + s += ReverseBytes(uint(i)) + } + Output = int(s) +} + +func BenchmarkReverseBytes16(b *testing.B) { + var s uint16 + for i := 0; i < b.N; i++ { + s += ReverseBytes16(uint16(i)) + } + Output = int(s) +} + +func BenchmarkReverseBytes32(b *testing.B) { + var s uint32 + for i := 0; i < b.N; i++ { + s += ReverseBytes32(uint32(i)) + } + Output = int(s) +} + +func BenchmarkReverseBytes64(b *testing.B) { + var s uint64 + for i := 0; i < b.N; i++ { + s += ReverseBytes64(uint64(i)) + } + Output = int(s) +} + +func TestLen(t *testing.T) { + for i := 0; i < 256; i++ { + len := 8 - tab[i].nlz + for k := 0; k < 64-8; k++ { + x := uint64(i) << uint(k) + want := 0 + if x != 0 { + want = len + k + } + if x <= 1<<8-1 { + got := Len8(uint8(x)) + if got != want { + t.Fatalf("Len8(%#02x) == %d; want %d", x, got, want) + } + } + + if x <= 1<<16-1 { + got := Len16(uint16(x)) + if got != want { + t.Fatalf("Len16(%#04x) == %d; want %d", x, got, want) + } + } + + if x <= 1<<32-1 { + got := Len32(uint32(x)) + if got != want { + t.Fatalf("Len32(%#08x) == %d; want %d", x, got, want) + } + if UintSize == 32 { + got := Len(uint(x)) + if got != want { + t.Fatalf("Len(%#08x) == %d; want %d", x, got, want) + } + } + } + + if x <= 1<<64-1 { + got := Len64(uint64(x)) + if got != want { + t.Fatalf("Len64(%#016x) == %d; want %d", x, got, want) + } + if UintSize == 64 { + got := Len(uint(x)) + if got != want { + t.Fatalf("Len(%#016x) == %d; want %d", x, got, want) + } + } + } + } + } +} + +const ( + _M = 1< 0 { + panic("overflow") + } + return x + }, + func(a, b uint64) uint64 { + x, c := Add64(a, b, 0) + if c != 0 { + panic("overflow") + } + return x + }, + func(a, b uint64) uint64 { + x, c := Add64(a, b, 0) + if c == 1 { + panic("overflow") + } + return x + }, + func(a, b uint64) uint64 { + x, c := Add64(a, b, 0) + if c != 1 { + return x + } + panic("overflow") + }, + func(a, b uint64) uint64 { + x, c := Add64(a, b, 0) + if c == 0 { + return x + } + panic("overflow") + }, + } + for _, test := range tests { + shouldPanic := func(f func()) { + defer func() { + if err := recover(); err == nil { + t.Fatalf("expected panic") + } + }() + f() + } + + // overflow + shouldPanic(func() { test(_M64, 1) }) + shouldPanic(func() { test(1, _M64) }) + shouldPanic(func() { test(_M64, _M64) }) + + // no overflow + test(_M64, 0) + test(0, 0) + test(1, 1) + } +} + +func TestSub64OverflowPanic(t *testing.T) { + // Test that 64-bit overflow panics fire correctly. + // These are designed to improve coverage of compiler intrinsics. + tests := []func(uint64, uint64) uint64{ + func(a, b uint64) uint64 { + x, c := Sub64(a, b, 0) + if c > 0 { + panic("overflow") + } + return x + }, + func(a, b uint64) uint64 { + x, c := Sub64(a, b, 0) + if c != 0 { + panic("overflow") + } + return x + }, + func(a, b uint64) uint64 { + x, c := Sub64(a, b, 0) + if c == 1 { + panic("overflow") + } + return x + }, + func(a, b uint64) uint64 { + x, c := Sub64(a, b, 0) + if c != 1 { + return x + } + panic("overflow") + }, + func(a, b uint64) uint64 { + x, c := Sub64(a, b, 0) + if c == 0 { + return x + } + panic("overflow") + }, + } + for _, test := range tests { + shouldPanic := func(f func()) { + defer func() { + if err := recover(); err == nil { + t.Fatalf("expected panic") + } + }() + f() + } + + // overflow + shouldPanic(func() { test(0, 1) }) + shouldPanic(func() { test(1, _M64) }) + shouldPanic(func() { test(_M64-1, _M64) }) + + // no overflow + test(_M64, 0) + test(0, 0) + test(1, 1) + } +} + +func TestMulDiv(t *testing.T) { + testMul := func(msg string, f func(x, y uint) (hi, lo uint), x, y, hi, lo uint) { + hi1, lo1 := f(x, y) + if hi1 != hi || lo1 != lo { + t.Errorf("%s: got hi:lo = %#x:%#x; want %#x:%#x", msg, hi1, lo1, hi, lo) + } + } + testDiv := func(msg string, f func(hi, lo, y uint) (q, r uint), hi, lo, y, q, r uint) { + q1, r1 := f(hi, lo, y) + if q1 != q || r1 != r { + t.Errorf("%s: got q:r = %#x:%#x; want %#x:%#x", msg, q1, r1, q, r) + } + } + for _, a := range []struct { + x, y uint + hi, lo, r uint + }{ + {1 << (UintSize - 1), 2, 1, 0, 1}, + {_M, _M, _M - 1, 1, 42}, + } { + testMul("Mul", Mul, a.x, a.y, a.hi, a.lo) + testMul("Mul symmetric", Mul, a.y, a.x, a.hi, a.lo) + testDiv("Div", Div, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div symmetric", Div, a.hi, a.lo+a.r, a.x, a.y, a.r) + // The above code can't test intrinsic implementation, because the passed function is not called directly. + // The following code uses a closure to test the intrinsic version in case the function is intrinsified. + testMul("Mul intrinsic", func(x, y uint) (uint, uint) { return Mul(x, y) }, a.x, a.y, a.hi, a.lo) + testMul("Mul intrinsic symmetric", func(x, y uint) (uint, uint) { return Mul(x, y) }, a.y, a.x, a.hi, a.lo) + testDiv("Div intrinsic", func(hi, lo, y uint) (uint, uint) { return Div(hi, lo, y) }, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div intrinsic symmetric", func(hi, lo, y uint) (uint, uint) { return Div(hi, lo, y) }, a.hi, a.lo+a.r, a.x, a.y, a.r) + } +} + +func TestMulDiv32(t *testing.T) { + testMul := func(msg string, f func(x, y uint32) (hi, lo uint32), x, y, hi, lo uint32) { + hi1, lo1 := f(x, y) + if hi1 != hi || lo1 != lo { + t.Errorf("%s: got hi:lo = %#x:%#x; want %#x:%#x", msg, hi1, lo1, hi, lo) + } + } + testDiv := func(msg string, f func(hi, lo, y uint32) (q, r uint32), hi, lo, y, q, r uint32) { + q1, r1 := f(hi, lo, y) + if q1 != q || r1 != r { + t.Errorf("%s: got q:r = %#x:%#x; want %#x:%#x", msg, q1, r1, q, r) + } + } + for _, a := range []struct { + x, y uint32 + hi, lo, r uint32 + }{ + {1 << 31, 2, 1, 0, 1}, + {0xc47dfa8c, 50911, 0x98a4, 0x998587f4, 13}, + {_M32, _M32, _M32 - 1, 1, 42}, + } { + testMul("Mul32", Mul32, a.x, a.y, a.hi, a.lo) + testMul("Mul32 symmetric", Mul32, a.y, a.x, a.hi, a.lo) + testDiv("Div32", Div32, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div32 symmetric", Div32, a.hi, a.lo+a.r, a.x, a.y, a.r) + } +} + +func TestMulDiv64(t *testing.T) { + testMul := func(msg string, f func(x, y uint64) (hi, lo uint64), x, y, hi, lo uint64) { + hi1, lo1 := f(x, y) + if hi1 != hi || lo1 != lo { + t.Errorf("%s: got hi:lo = %#x:%#x; want %#x:%#x", msg, hi1, lo1, hi, lo) + } + } + testDiv := func(msg string, f func(hi, lo, y uint64) (q, r uint64), hi, lo, y, q, r uint64) { + q1, r1 := f(hi, lo, y) + if q1 != q || r1 != r { + t.Errorf("%s: got q:r = %#x:%#x; want %#x:%#x", msg, q1, r1, q, r) + } + } + for _, a := range []struct { + x, y uint64 + hi, lo, r uint64 + }{ + {1 << 63, 2, 1, 0, 1}, + {0x3626229738a3b9, 0xd8988a9f1cc4a61, 0x2dd0712657fe8, 0x9dd6a3364c358319, 13}, + {_M64, _M64, _M64 - 1, 1, 42}, + } { + testMul("Mul64", Mul64, a.x, a.y, a.hi, a.lo) + testMul("Mul64 symmetric", Mul64, a.y, a.x, a.hi, a.lo) + testDiv("Div64", Div64, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div64 symmetric", Div64, a.hi, a.lo+a.r, a.x, a.y, a.r) + // The above code can't test intrinsic implementation, because the passed function is not called directly. + // The following code uses a closure to test the intrinsic version in case the function is intrinsified. + testMul("Mul64 intrinsic", func(x, y uint64) (uint64, uint64) { return Mul64(x, y) }, a.x, a.y, a.hi, a.lo) + testMul("Mul64 intrinsic symmetric", func(x, y uint64) (uint64, uint64) { return Mul64(x, y) }, a.y, a.x, a.hi, a.lo) + testDiv("Div64 intrinsic", func(hi, lo, y uint64) (uint64, uint64) { return Div64(hi, lo, y) }, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div64 intrinsic symmetric", func(hi, lo, y uint64) (uint64, uint64) { return Div64(hi, lo, y) }, a.hi, a.lo+a.r, a.x, a.y, a.r) + } +} + +func TestDivPanicOverflow(t *testing.T) { + // Expect a panic + defer func() { + if err := recover(); err == nil { + t.Error("Div should have panicked when y<=hi") + } else if err != overflowError { + t.Errorf("Div expected panic: %q, got: %v ", overflowError, err) + } + }() + q, r := Div(1, 0, 1) + t.Errorf("undefined q, r = %v, %v calculated when Div should have panicked", q, r) +} + +func TestDiv32PanicOverflow(t *testing.T) { + // Expect a panic + defer func() { + if err := recover(); err == nil { + t.Error("Div32 should have panicked when y<=hi") + } else if err != overflowError { + t.Errorf("Div32 expected panic: %q, got: %v ", overflowError, err) + } + }() + q, r := Div32(1, 0, 1) + t.Errorf("undefined q, r = %v, %v calculated when Div32 should have panicked", q, r) +} + +func TestDiv64PanicOverflow(t *testing.T) { + // Expect a panic + defer func() { + if err := recover(); err == nil { + t.Error("Div64 should have panicked when y<=hi") + } else if err != overflowError { + t.Errorf("Div64 expected panic: %q, got: %v ", overflowError, err) + } + }() + q, r := Div64(1, 0, 1) + t.Errorf("undefined q, r = %v, %v calculated when Div64 should have panicked", q, r) +} + +func TestDivPanicZero(t *testing.T) { + // Expect a panic + defer func() { + if err := recover(); err == nil { + t.Error("Div should have panicked when y==0") + } else if err != divideError { + t.Errorf("Div expected panic: %q, got: %q ", divideError, err) + } + }() + q, r := Div(1, 1, 0) + t.Errorf("undefined q, r = %v, %v calculated when Div should have panicked", q, r) +} + +func TestDiv32PanicZero(t *testing.T) { + // Expect a panic + defer func() { + if err := recover(); err == nil { + t.Error("Div32 should have panicked when y==0") + } else if err != divideError { + t.Errorf("Div32 expected panic: %q, got: %q ", divideError, err) + } + }() + q, r := Div32(1, 1, 0) + t.Errorf("undefined q, r = %v, %v calculated when Div32 should have panicked", q, r) +} + +func TestDiv64PanicZero(t *testing.T) { + // Expect a panic + defer func() { + if err := recover(); err == nil { + t.Error("Div64 should have panicked when y==0") + } else if err != divideError { + t.Errorf("Div64 expected panic: %q, got: %q ", divideError, err) + } + }() + q, r := Div64(1, 1, 0) + t.Errorf("undefined q, r = %v, %v calculated when Div64 should have panicked", q, r) +} + +func TestRem32(t *testing.T) { + // Sanity check: for non-oveflowing dividends, the result is the + // same as the rem returned by Div32 + hi, lo, y := uint32(510510), uint32(9699690), uint32(510510+1) // ensure hi < y + for i := 0; i < 1000; i++ { + r := Rem32(hi, lo, y) + _, r2 := Div32(hi, lo, y) + if r != r2 { + t.Errorf("Rem32(%v, %v, %v) returned %v, but Div32 returned rem %v", hi, lo, y, r, r2) + } + y += 13 + } +} + +func TestRem32Overflow(t *testing.T) { + // To trigger a quotient overflow, we need y <= hi + hi, lo, y := uint32(510510), uint32(9699690), uint32(7) + for i := 0; i < 1000; i++ { + r := Rem32(hi, lo, y) + _, r2 := Div64(0, uint64(hi)<<32|uint64(lo), uint64(y)) + if r != uint32(r2) { + t.Errorf("Rem32(%v, %v, %v) returned %v, but Div64 returned rem %v", hi, lo, y, r, r2) + } + y += 13 + } +} + +func TestRem64(t *testing.T) { + // Sanity check: for non-oveflowing dividends, the result is the + // same as the rem returned by Div64 + hi, lo, y := uint64(510510), uint64(9699690), uint64(510510+1) // ensure hi < y + for i := 0; i < 1000; i++ { + r := Rem64(hi, lo, y) + _, r2 := Div64(hi, lo, y) + if r != r2 { + t.Errorf("Rem64(%v, %v, %v) returned %v, but Div64 returned rem %v", hi, lo, y, r, r2) + } + y += 13 + } +} + +func TestRem64Overflow(t *testing.T) { + Rem64Tests := []struct { + hi, lo, y uint64 + rem uint64 + }{ + // Testcases computed using Python 3, as: + // >>> hi = 42; lo = 1119; y = 42 + // >>> ((hi<<64)+lo) % y + {42, 1119, 42, 27}, + {42, 1119, 38, 9}, + {42, 1119, 26, 23}, + {469, 0, 467, 271}, + {469, 0, 113, 58}, + {111111, 111111, 1171, 803}, + {3968194946088682615, 3192705705065114702, 1000037, 56067}, + } + + for _, rt := range Rem64Tests { + if rt.hi < rt.y { + t.Fatalf("Rem64(%v, %v, %v) is not a test with quo overflow", rt.hi, rt.lo, rt.y) + } + rem := Rem64(rt.hi, rt.lo, rt.y) + if rem != rt.rem { + t.Errorf("Rem64(%v, %v, %v) returned %v, wanted %v", + rt.hi, rt.lo, rt.y, rem, rt.rem) + } + } +} + +func BenchmarkAdd(b *testing.B) { + var z, c uint + for i := 0; i < b.N; i++ { + z, c = Add(uint(Input), uint(i), c) + } + Output = int(z + c) +} + +func BenchmarkAdd32(b *testing.B) { + var z, c uint32 + for i := 0; i < b.N; i++ { + z, c = Add32(uint32(Input), uint32(i), c) + } + Output = int(z + c) +} + +func BenchmarkAdd64(b *testing.B) { + var z, c uint64 + for i := 0; i < b.N; i++ { + z, c = Add64(uint64(Input), uint64(i), c) + } + Output = int(z + c) +} + +func BenchmarkAdd64multiple(b *testing.B) { + z0 := uint64(Input) + z1 := uint64(Input) + z2 := uint64(Input) + z3 := uint64(Input) + for i := 0; i < b.N; i++ { + var c uint64 + z0, c = Add64(z0, uint64(i), c) + z1, c = Add64(z1, uint64(i), c) + z2, c = Add64(z2, uint64(i), c) + z3, _ = Add64(z3, uint64(i), c) + } + Output = int(z0 + z1 + z2 + z3) +} + +func BenchmarkSub(b *testing.B) { + var z, c uint + for i := 0; i < b.N; i++ { + z, c = Sub(uint(Input), uint(i), c) + } + Output = int(z + c) +} + +func BenchmarkSub32(b *testing.B) { + var z, c uint32 + for i := 0; i < b.N; i++ { + z, c = Sub32(uint32(Input), uint32(i), c) + } + Output = int(z + c) +} + +func BenchmarkSub64(b *testing.B) { + var z, c uint64 + for i := 0; i < b.N; i++ { + z, c = Sub64(uint64(Input), uint64(i), c) + } + Output = int(z + c) +} + +func BenchmarkSub64multiple(b *testing.B) { + z0 := uint64(Input) + z1 := uint64(Input) + z2 := uint64(Input) + z3 := uint64(Input) + for i := 0; i < b.N; i++ { + var c uint64 + z0, c = Sub64(z0, uint64(i), c) + z1, c = Sub64(z1, uint64(i), c) + z2, c = Sub64(z2, uint64(i), c) + z3, _ = Sub64(z3, uint64(i), c) + } + Output = int(z0 + z1 + z2 + z3) +} + +func BenchmarkMul(b *testing.B) { + var hi, lo uint + for i := 0; i < b.N; i++ { + hi, lo = Mul(uint(Input), uint(i)) + } + Output = int(hi + lo) +} + +func BenchmarkMul32(b *testing.B) { + var hi, lo uint32 + for i := 0; i < b.N; i++ { + hi, lo = Mul32(uint32(Input), uint32(i)) + } + Output = int(hi + lo) +} + +func BenchmarkMul64(b *testing.B) { + var hi, lo uint64 + for i := 0; i < b.N; i++ { + hi, lo = Mul64(uint64(Input), uint64(i)) + } + Output = int(hi + lo) +} + +func BenchmarkDiv(b *testing.B) { + var q, r uint + for i := 0; i < b.N; i++ { + q, r = Div(1, uint(i), uint(Input)) + } + Output = int(q + r) +} + +func BenchmarkDiv32(b *testing.B) { + var q, r uint32 + for i := 0; i < b.N; i++ { + q, r = Div32(1, uint32(i), uint32(Input)) + } + Output = int(q + r) +} + +func BenchmarkDiv64(b *testing.B) { + var q, r uint64 + for i := 0; i < b.N; i++ { + q, r = Div64(1, uint64(i), uint64(Input)) + } + Output = int(q + r) +} + +// ---------------------------------------------------------------------------- +// Testing support + +type entry = struct { + nlz, ntz, pop int +} + +// tab contains results for all uint8 values +var tab [256]entry + +func init() { + tab[0] = entry{8, 8, 0} + for i := 1; i < len(tab); i++ { + // nlz + x := i // x != 0 + n := 0 + for x&0x80 == 0 { + n++ + x <<= 1 + } + tab[i].nlz = n + + // ntz + x = i // x != 0 + n = 0 + for x&1 == 0 { + n++ + x >>= 1 + } + tab[i].ntz = n + + // pop + x = i // x != 0 + n = 0 + for x != 0 { + n += int(x & 1) + x >>= 1 + } + tab[i].pop = n + } +} diff --git a/gnovm/stdlibs/math/bits/export_test.gno b/gnovm/stdlibs/math/bits/export_test.gno new file mode 100644 index 00000000000..8c6f9332cca --- /dev/null +++ b/gnovm/stdlibs/math/bits/export_test.gno @@ -0,0 +1,7 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bits + +const DeBruijn64 = deBruijn64 diff --git a/gnovm/stdlibs/math/cbrt.gno b/gnovm/stdlibs/math/cbrt.gno new file mode 100644 index 00000000000..b8e6c3e8012 --- /dev/null +++ b/gnovm/stdlibs/math/cbrt.gno @@ -0,0 +1,82 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The go code is a modified version of the original C code from +// http://www.netlib.org/fdlibm/s_cbrt.c and came with this notice. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunSoft, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== + +// Cbrt returns the cube root of x. +// +// Special cases are: +// +// Cbrt(±0) = ±0 +// Cbrt(±Inf) = ±Inf +// Cbrt(NaN) = NaN +func Cbrt(x float64) float64 { + return cbrt(x) +} + +func cbrt(x float64) float64 { + const ( + B1 = 715094163 // (682-0.03306235651)*2**20 + B2 = 696219795 // (664-0.03306235651)*2**20 + C = 5.42857142857142815906e-01 // 19/35 = 0x3FE15F15F15F15F1 + D = -7.05306122448979611050e-01 // -864/1225 = 0xBFE691DE2532C834 + E = 1.41428571428571436819e+00 // 99/70 = 0x3FF6A0EA0EA0EA0F + F = 1.60714285714285720630e+00 // 45/28 = 0x3FF9B6DB6DB6DB6E + G = 3.57142857142857150787e-01 // 5/14 = 0x3FD6DB6DB6DB6DB7 + SmallestNormal = 2.22507385850720138309e-308 // 2**-1022 = 0x0010000000000000 + ) + // special cases + switch { + case x == 0 || IsNaN(x) || IsInf(x, 0): + return x + } + + sign := false + if x < 0 { + x = -x + sign = true + } + + // rough cbrt to 5 bits + t := Float64frombits(Float64bits(x)/3 + B1<<32) + if x < SmallestNormal { + // subnormal number + t = float64(1 << 54) // set t= 2**54 + t *= x + t = Float64frombits(Float64bits(t)/3 + B2<<32) + } + + // new cbrt to 23 bits + r := t * t / x + s := C + r*t + t *= G + F/(s+E+D/s) + + // chop to 22 bits, make larger than cbrt(x) + t = Float64frombits(Float64bits(t)&(0xFFFFFFFFC<<28) + 1<<30) + + // one step newton iteration to 53 bits with error less than 0.667ulps + s = t * t // t*t is exact + r = x / s + w := t + t + r = (r - t) / (w + r) // r-s is exact + t = t + t*r + + // restore the sign bit + if sign { + t = -t + } + return t +} diff --git a/gnovm/stdlibs/math/const_test.gno b/gnovm/stdlibs/math/const_test.gno new file mode 100644 index 00000000000..fb8412311f8 --- /dev/null +++ b/gnovm/stdlibs/math/const_test.gno @@ -0,0 +1,48 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +import ( + "testing" +) + +func TestMaxUint(t *testing.T) { + // https://github.com/gnolang/gno/issues/1085 + var v uint = MaxUint + if v+1 != 0 { + t.Errorf("MaxUint should wrap around to zero: %d", v+1) + } + if v := uint8(MaxUint8); v+1 != 0 { + t.Errorf("MaxUint8 should wrap around to zero: %d", v+1) + } + if v := uint16(MaxUint16); v+1 != 0 { + t.Errorf("MaxUint16 should wrap around to zero: %d", v+1) + } + if v := uint32(MaxUint32); v+1 != 0 { + t.Errorf("MaxUint32 should wrap around to zero: %d", v+1) + } + var v1 uint64 = MaxUint64 + if v1+1 != 0 { + t.Errorf("MaxUint64 should wrap around to zero: %d", v1+1) + } +} + +func TestMaxInt(t *testing.T) { + if v := int(MaxInt); v+1 != MinInt { + t.Errorf("MaxInt should wrap around to MinInt: %d", v+1) + } + if v := int8(MaxInt8); v+1 != MinInt8 { + t.Errorf("MaxInt8 should wrap around to MinInt8: %d", v+1) + } + if v := int16(MaxInt16); v+1 != MinInt16 { + t.Errorf("MaxInt16 should wrap around to MinInt16: %d", v+1) + } + if v := int32(MaxInt32); v+1 != MinInt32 { + t.Errorf("MaxInt32 should wrap around to MinInt32: %d", v+1) + } + if v := int64(MaxInt64); v+1 != MinInt64 { + t.Errorf("MaxInt64 should wrap around to MinInt64: %d", v+1) + } +} diff --git a/gnovm/stdlibs/math/copysign.gno b/gnovm/stdlibs/math/copysign.gno index a5e05427c6f..3a30afb4136 100644 --- a/gnovm/stdlibs/math/copysign.gno +++ b/gnovm/stdlibs/math/copysign.gno @@ -4,13 +4,9 @@ package math -import ( - imath "internal/math" -) - // Copysign returns a value with the magnitude of f // and the sign of sign. func Copysign(f, sign float64) float64 { const signBit = 1 << 63 - return imath.Float64frombits(imath.Float64bits(f)&^signBit | imath.Float64bits(sign)&signBit) + return Float64frombits(Float64bits(f)&^signBit | Float64bits(sign)&signBit) } diff --git a/gnovm/stdlibs/math/dim.gno b/gnovm/stdlibs/math/dim.gno new file mode 100644 index 00000000000..92d0eaa1434 --- /dev/null +++ b/gnovm/stdlibs/math/dim.gno @@ -0,0 +1,88 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Dim returns the maximum of x-y or 0. +// +// Special cases are: +// +// Dim(+Inf, +Inf) = NaN +// Dim(-Inf, -Inf) = NaN +// Dim(x, NaN) = Dim(NaN, x) = NaN +func Dim(x, y float64) float64 { + // The special cases result in NaN after the subtraction: + // +Inf - +Inf = NaN + // -Inf - -Inf = NaN + // NaN - y = NaN + // x - NaN = NaN + v := x - y + if v <= 0 { + // v is negative or 0 + return 0 + } + // v is positive or NaN + return v +} + +// Max returns the larger of x or y. +// +// Special cases are: +// +// Max(x, +Inf) = Max(+Inf, x) = +Inf +// Max(x, NaN) = Max(NaN, x) = NaN +// Max(+0, ±0) = Max(±0, +0) = +0 +// Max(-0, -0) = -0 +func Max(x, y float64) float64 { + return max(x, y) +} + +func max(x, y float64) float64 { + // special cases + switch { + case IsInf(x, 1) || IsInf(y, 1): + return Inf(1) + case IsNaN(x) || IsNaN(y): + return NaN() + case x == 0 && x == y: + if Signbit(x) { + return y + } + return x + } + if x > y { + return x + } + return y +} + +// Min returns the smaller of x or y. +// +// Special cases are: +// +// Min(x, -Inf) = Min(-Inf, x) = -Inf +// Min(x, NaN) = Min(NaN, x) = NaN +// Min(-0, ±0) = Min(±0, -0) = -0 +func Min(x, y float64) float64 { + return min(x, y) +} + +func min(x, y float64) float64 { + // special cases + switch { + case IsInf(x, -1) || IsInf(y, -1): + return Inf(-1) + case IsNaN(x) || IsNaN(y): + return NaN() + case x == 0 && x == y: + if Signbit(x) { + return x + } + return y + } + if x < y { + return x + } + return y +} diff --git a/gnovm/stdlibs/math/erf.gno b/gnovm/stdlibs/math/erf.gno new file mode 100644 index 00000000000..717a812caa1 --- /dev/null +++ b/gnovm/stdlibs/math/erf.gno @@ -0,0 +1,345 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point error function and complementary error function. +*/ + +// The original C code and the long comment below are +// from FreeBSD's /usr/src/lib/msun/src/s_erf.c and +// came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// +// double erf(double x) +// double erfc(double x) +// x +// 2 |\ +// erf(x) = --------- | exp(-t*t)dt +// sqrt(pi) \| +// 0 +// +// erfc(x) = 1-erf(x) +// Note that +// erf(-x) = -erf(x) +// erfc(-x) = 2 - erfc(x) +// +// Method: +// 1. For |x| in [0, 0.84375] +// erf(x) = x + x*R(x**2) +// erfc(x) = 1 - erf(x) if x in [-.84375,0.25] +// = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375] +// where R = P/Q where P is an odd poly of degree 8 and +// Q is an odd poly of degree 10. +// -57.90 +// | R - (erf(x)-x)/x | <= 2 +// +// +// Remark. The formula is derived by noting +// erf(x) = (2/sqrt(pi))*(x - x**3/3 + x**5/10 - x**7/42 + ....) +// and that +// 2/sqrt(pi) = 1.128379167095512573896158903121545171688 +// is close to one. The interval is chosen because the fix +// point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is +// near 0.6174), and by some experiment, 0.84375 is chosen to +// guarantee the error is less than one ulp for erf. +// +// 2. For |x| in [0.84375,1.25], let s = |x| - 1, and +// c = 0.84506291151 rounded to single (24 bits) +// erf(x) = sign(x) * (c + P1(s)/Q1(s)) +// erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0 +// 1+(c+P1(s)/Q1(s)) if x < 0 +// |P1/Q1 - (erf(|x|)-c)| <= 2**-59.06 +// Remark: here we use the taylor series expansion at x=1. +// erf(1+s) = erf(1) + s*Poly(s) +// = 0.845.. + P1(s)/Q1(s) +// That is, we use rational approximation to approximate +// erf(1+s) - (c = (single)0.84506291151) +// Note that |P1/Q1|< 0.078 for x in [0.84375,1.25] +// where +// P1(s) = degree 6 poly in s +// Q1(s) = degree 6 poly in s +// +// 3. For x in [1.25,1/0.35(~2.857143)], +// erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1) +// erf(x) = 1 - erfc(x) +// where +// R1(z) = degree 7 poly in z, (z=1/x**2) +// S1(z) = degree 8 poly in z +// +// 4. For x in [1/0.35,28] +// erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0 +// = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6 x >= 28 +// erf(x) = sign(x) *(1 - tiny) (raise inexact) +// erfc(x) = tiny*tiny (raise underflow) if x > 0 +// = 2 - tiny if x<0 +// +// 7. Special case: +// erf(0) = 0, erf(inf) = 1, erf(-inf) = -1, +// erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2, +// erfc/erf(NaN) is NaN + +const ( + erx = 8.45062911510467529297e-01 // 0x3FEB0AC160000000 + // Coefficients for approximation to erf in [0, 0.84375] + efx = 1.28379167095512586316e-01 // 0x3FC06EBA8214DB69 + efx8 = 1.02703333676410069053e+00 // 0x3FF06EBA8214DB69 + pp0 = 1.28379167095512558561e-01 // 0x3FC06EBA8214DB68 + pp1 = -3.25042107247001499370e-01 // 0xBFD4CD7D691CB913 + pp2 = -2.84817495755985104766e-02 // 0xBF9D2A51DBD7194F + pp3 = -5.77027029648944159157e-03 // 0xBF77A291236668E4 + pp4 = -2.37630166566501626084e-05 // 0xBEF8EAD6120016AC + qq1 = 3.97917223959155352819e-01 // 0x3FD97779CDDADC09 + qq2 = 6.50222499887672944485e-02 // 0x3FB0A54C5536CEBA + qq3 = 5.08130628187576562776e-03 // 0x3F74D022C4D36B0F + qq4 = 1.32494738004321644526e-04 // 0x3F215DC9221C1A10 + qq5 = -3.96022827877536812320e-06 // 0xBED09C4342A26120 + // Coefficients for approximation to erf in [0.84375, 1.25] + pa0 = -2.36211856075265944077e-03 // 0xBF6359B8BEF77538 + pa1 = 4.14856118683748331666e-01 // 0x3FDA8D00AD92B34D + pa2 = -3.72207876035701323847e-01 // 0xBFD7D240FBB8C3F1 + pa3 = 3.18346619901161753674e-01 // 0x3FD45FCA805120E4 + pa4 = -1.10894694282396677476e-01 // 0xBFBC63983D3E28EC + pa5 = 3.54783043256182359371e-02 // 0x3FA22A36599795EB + pa6 = -2.16637559486879084300e-03 // 0xBF61BF380A96073F + qa1 = 1.06420880400844228286e-01 // 0x3FBB3E6618EEE323 + qa2 = 5.40397917702171048937e-01 // 0x3FE14AF092EB6F33 + qa3 = 7.18286544141962662868e-02 // 0x3FB2635CD99FE9A7 + qa4 = 1.26171219808761642112e-01 // 0x3FC02660E763351F + qa5 = 1.36370839120290507362e-02 // 0x3F8BEDC26B51DD1C + qa6 = 1.19844998467991074170e-02 // 0x3F888B545735151D + // Coefficients for approximation to erfc in [1.25, 1/0.35] + ra0 = -9.86494403484714822705e-03 // 0xBF843412600D6435 + ra1 = -6.93858572707181764372e-01 // 0xBFE63416E4BA7360 + ra2 = -1.05586262253232909814e+01 // 0xC0251E0441B0E726 + ra3 = -6.23753324503260060396e+01 // 0xC04F300AE4CBA38D + ra4 = -1.62396669462573470355e+02 // 0xC0644CB184282266 + ra5 = -1.84605092906711035994e+02 // 0xC067135CEBCCABB2 + ra6 = -8.12874355063065934246e+01 // 0xC054526557E4D2F2 + ra7 = -9.81432934416914548592e+00 // 0xC023A0EFC69AC25C + sa1 = 1.96512716674392571292e+01 // 0x4033A6B9BD707687 + sa2 = 1.37657754143519042600e+02 // 0x4061350C526AE721 + sa3 = 4.34565877475229228821e+02 // 0x407B290DD58A1A71 + sa4 = 6.45387271733267880336e+02 // 0x40842B1921EC2868 + sa5 = 4.29008140027567833386e+02 // 0x407AD02157700314 + sa6 = 1.08635005541779435134e+02 // 0x405B28A3EE48AE2C + sa7 = 6.57024977031928170135e+00 // 0x401A47EF8E484A93 + sa8 = -6.04244152148580987438e-02 // 0xBFAEEFF2EE749A62 + // Coefficients for approximation to erfc in [1/.35, 28] + rb0 = -9.86494292470009928597e-03 // 0xBF84341239E86F4A + rb1 = -7.99283237680523006574e-01 // 0xBFE993BA70C285DE + rb2 = -1.77579549177547519889e+01 // 0xC031C209555F995A + rb3 = -1.60636384855821916062e+02 // 0xC064145D43C5ED98 + rb4 = -6.37566443368389627722e+02 // 0xC083EC881375F228 + rb5 = -1.02509513161107724954e+03 // 0xC09004616A2E5992 + rb6 = -4.83519191608651397019e+02 // 0xC07E384E9BDC383F + sb1 = 3.03380607434824582924e+01 // 0x403E568B261D5190 + sb2 = 3.25792512996573918826e+02 // 0x40745CAE221B9F0A + sb3 = 1.53672958608443695994e+03 // 0x409802EB189D5118 + sb4 = 3.19985821950859553908e+03 // 0x40A8FFB7688C246A + sb5 = 2.55305040643316442583e+03 // 0x40A3F219CEDF3BE6 + sb6 = 4.74528541206955367215e+02 // 0x407DA874E79FE763 + sb7 = -2.24409524465858183362e+01 // 0xC03670E242712D62 +) + +// Erf returns the error function of x. +// +// Special cases are: +// +// Erf(+Inf) = 1 +// Erf(-Inf) = -1 +// Erf(NaN) = NaN +func Erf(x float64) float64 { + return erf(x) +} + +func erf(x float64) float64 { + const ( + VeryTiny = 2.848094538889218e-306 // 0x0080000000000000 + Small = 1.0 / (1 << 28) // 2**-28 + ) + // special cases + switch { + case IsNaN(x): + return NaN() + case IsInf(x, 1): + return 1 + case IsInf(x, -1): + return -1 + } + sign := false + if x < 0 { + x = -x + sign = true + } + if x < 0.84375 { // |x| < 0.84375 + var temp float64 + if x < Small { // |x| < 2**-28 + if x < VeryTiny { + temp = 0.125 * (8.0*x + efx8*x) // avoid underflow + } else { + temp = x + efx*x + } + } else { + z := x * x + r := pp0 + z*(pp1+z*(pp2+z*(pp3+z*pp4))) + s := 1 + z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))) + y := r / s + temp = x + x*y + } + if sign { + return -temp + } + return temp + } + if x < 1.25 { // 0.84375 <= |x| < 1.25 + s := x - 1 + P := pa0 + s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))) + Q := 1 + s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))) + if sign { + return -erx - P/Q + } + return erx + P/Q + } + if x >= 6 { // inf > |x| >= 6 + if sign { + return -1 + } + return 1 + } + s := 1 / (x * x) + var R, S float64 + if x < 1/0.35 { // |x| < 1 / 0.35 ~ 2.857143 + R = ra0 + s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(ra5+s*(ra6+s*ra7)))))) + S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8))))))) + } else { // |x| >= 1 / 0.35 ~ 2.857143 + R = rb0 + s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(rb5+s*rb6))))) + S = 1 + s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(sb5+s*(sb6+s*sb7)))))) + } + z := Float64frombits(Float64bits(x) & 0xffffffff00000000) // pseudo-single (20-bit) precision x + r := Exp(-z*z-0.5625) * Exp((z-x)*(z+x)+R/S) + if sign { + return r/x - 1 + } + return 1 - r/x +} + +// Erfc returns the complementary error function of x. +// +// Special cases are: +// +// Erfc(+Inf) = 0 +// Erfc(-Inf) = 2 +// Erfc(NaN) = NaN +func Erfc(x float64) float64 { + return erfc(x) +} + +func erfc(x float64) float64 { + const Tiny = 1.0 / (1 << 56) // 2**-56 + // special cases + switch { + case IsNaN(x): + return NaN() + case IsInf(x, 1): + return 0 + case IsInf(x, -1): + return 2 + } + sign := false + if x < 0 { + x = -x + sign = true + } + if x < 0.84375 { // |x| < 0.84375 + var temp float64 + if x < Tiny { // |x| < 2**-56 + temp = x + } else { + z := x * x + r := pp0 + z*(pp1+z*(pp2+z*(pp3+z*pp4))) + s := 1 + z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))) + y := r / s + if x < 0.25 { // |x| < 1/4 + temp = x + x*y + } else { + temp = 0.5 + (x*y + (x - 0.5)) + } + } + if sign { + return 1 + temp + } + return 1 - temp + } + if x < 1.25 { // 0.84375 <= |x| < 1.25 + s := x - 1 + P := pa0 + s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))) + Q := 1 + s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))) + if sign { + return 1 + erx + P/Q + } + return 1 - erx - P/Q + + } + if x < 28 { // |x| < 28 + s := 1 / (x * x) + var R, S float64 + if x < 1/0.35 { // |x| < 1 / 0.35 ~ 2.857143 + R = ra0 + s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(ra5+s*(ra6+s*ra7)))))) + S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8))))))) + } else { // |x| >= 1 / 0.35 ~ 2.857143 + if sign && x > 6 { + return 2 // x < -6 + } + R = rb0 + s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(rb5+s*rb6))))) + S = 1 + s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(sb5+s*(sb6+s*sb7)))))) + } + z := Float64frombits(Float64bits(x) & 0xffffffff00000000) // pseudo-single (20-bit) precision x + r := Exp(-z*z-0.5625) * Exp((z-x)*(z+x)+R/S) + if sign { + return 2 - r/x + } + return r / x + } + if sign { + return 2 + } + return 0 +} diff --git a/gnovm/stdlibs/math/erfinv.gno b/gnovm/stdlibs/math/erfinv.gno new file mode 100644 index 00000000000..eed0feb42dd --- /dev/null +++ b/gnovm/stdlibs/math/erfinv.gno @@ -0,0 +1,129 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Inverse of the floating-point error function. +*/ + +// This implementation is based on the rational approximation +// of percentage points of normal distribution available from +// https://www.jstor.org/stable/2347330. + +const ( + // Coefficients for approximation to erf in |x| <= 0.85 + a0 = 1.1975323115670912564578e0 + a1 = 4.7072688112383978012285e1 + a2 = 6.9706266534389598238465e2 + a3 = 4.8548868893843886794648e3 + a4 = 1.6235862515167575384252e4 + a5 = 2.3782041382114385731252e4 + a6 = 1.1819493347062294404278e4 + a7 = 8.8709406962545514830200e2 + b0 = 1.0000000000000000000e0 + b1 = 4.2313330701600911252e1 + b2 = 6.8718700749205790830e2 + b3 = 5.3941960214247511077e3 + b4 = 2.1213794301586595867e4 + b5 = 3.9307895800092710610e4 + b6 = 2.8729085735721942674e4 + b7 = 5.2264952788528545610e3 + // Coefficients for approximation to erf in 0.85 < |x| <= 1-2*exp(-25) + c0 = 1.42343711074968357734e0 + c1 = 4.63033784615654529590e0 + c2 = 5.76949722146069140550e0 + c3 = 3.64784832476320460504e0 + c4 = 1.27045825245236838258e0 + c5 = 2.41780725177450611770e-1 + c6 = 2.27238449892691845833e-2 + c7 = 7.74545014278341407640e-4 + d0 = 1.4142135623730950488016887e0 + d1 = 2.9036514445419946173133295e0 + d2 = 2.3707661626024532365971225e0 + d3 = 9.7547832001787427186894837e-1 + d4 = 2.0945065210512749128288442e-1 + d5 = 2.1494160384252876777097297e-2 + d6 = 7.7441459065157709165577218e-4 + d7 = 1.4859850019840355905497876e-9 + // Coefficients for approximation to erf in 1-2*exp(-25) < |x| < 1 + e0 = 6.65790464350110377720e0 + e1 = 5.46378491116411436990e0 + e2 = 1.78482653991729133580e0 + e3 = 2.96560571828504891230e-1 + e4 = 2.65321895265761230930e-2 + e5 = 1.24266094738807843860e-3 + e6 = 2.71155556874348757815e-5 + e7 = 2.01033439929228813265e-7 + f0 = 1.414213562373095048801689e0 + f1 = 8.482908416595164588112026e-1 + f2 = 1.936480946950659106176712e-1 + f3 = 2.103693768272068968719679e-2 + f4 = 1.112800997078859844711555e-3 + f5 = 2.611088405080593625138020e-5 + f6 = 2.010321207683943062279931e-7 + f7 = 2.891024605872965461538222e-15 +) + +// Erfinv returns the inverse error function of x. +// +// Special cases are: +// +// Erfinv(1) = +Inf +// Erfinv(-1) = -Inf +// Erfinv(x) = NaN if x < -1 or x > 1 +// Erfinv(NaN) = NaN +func Erfinv(x float64) float64 { + // special cases + if IsNaN(x) || x <= -1 || x >= 1 { + if x == -1 || x == 1 { + return Inf(int(x)) + } + return NaN() + } + + sign := false + if x < 0 { + x = -x + sign = true + } + + var ans float64 + if x <= 0.85 { // |x| <= 0.85 + r := 0.180625 - 0.25*x*x + z1 := ((((((a7*r+a6)*r+a5)*r+a4)*r+a3)*r+a2)*r+a1)*r + a0 + z2 := ((((((b7*r+b6)*r+b5)*r+b4)*r+b3)*r+b2)*r+b1)*r + b0 + ans = (x * z1) / z2 + } else { + var z1, z2 float64 + r := Sqrt(Ln2 - Log(1.0-x)) + if r <= 5.0 { + r -= 1.6 + z1 = ((((((c7*r+c6)*r+c5)*r+c4)*r+c3)*r+c2)*r+c1)*r + c0 + z2 = ((((((d7*r+d6)*r+d5)*r+d4)*r+d3)*r+d2)*r+d1)*r + d0 + } else { + r -= 5.0 + z1 = ((((((e7*r+e6)*r+e5)*r+e4)*r+e3)*r+e2)*r+e1)*r + e0 + z2 = ((((((f7*r+f6)*r+f5)*r+f4)*r+f3)*r+f2)*r+f1)*r + f0 + } + ans = z1 / z2 + } + + if sign { + return -ans + } + return ans +} + +// Erfcinv returns the inverse of Erfc(x). +// +// Special cases are: +// +// Erfcinv(0) = +Inf +// Erfcinv(2) = -Inf +// Erfcinv(x) = NaN if x < 0 or x > 2 +// Erfcinv(NaN) = NaN +func Erfcinv(x float64) float64 { + return Erfinv(1 - x) +} diff --git a/gnovm/stdlibs/math/exp.gno b/gnovm/stdlibs/math/exp.gno index a4112208b62..4ddec051cc7 100644 --- a/gnovm/stdlibs/math/exp.gno +++ b/gnovm/stdlibs/math/exp.gno @@ -14,11 +14,6 @@ package math // Very large values overflow to 0 or +Inf. // Very small values underflow to 1. func Exp(x float64) float64 { - /* XXX - if haveArchExp { - return archExp(x) - } - */ return exp(x) } @@ -142,11 +137,6 @@ func exp(x float64) float64 { // // Special cases are the same as Exp. func Exp2(x float64) float64 { - /* XXX - if haveArchExp2 { - return archExp2(x) - } - */ return exp2(x) } diff --git a/gnovm/stdlibs/math/expm1.gno b/gnovm/stdlibs/math/expm1.gno new file mode 100644 index 00000000000..f1e9e9b6463 --- /dev/null +++ b/gnovm/stdlibs/math/expm1.gno @@ -0,0 +1,241 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/s_expm1.c +// and came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// expm1(x) +// Returns exp(x)-1, the exponential of x minus 1. +// +// Method +// 1. Argument reduction: +// Given x, find r and integer k such that +// +// x = k*ln2 + r, |r| <= 0.5*ln2 ~ 0.34658 +// +// Here a correction term c will be computed to compensate +// the error in r when rounded to a floating-point number. +// +// 2. Approximating expm1(r) by a special rational function on +// the interval [0,0.34658]: +// Since +// r*(exp(r)+1)/(exp(r)-1) = 2+ r**2/6 - r**4/360 + ... +// we define R1(r*r) by +// r*(exp(r)+1)/(exp(r)-1) = 2+ r**2/6 * R1(r*r) +// That is, +// R1(r**2) = 6/r *((exp(r)+1)/(exp(r)-1) - 2/r) +// = 6/r * ( 1 + 2.0*(1/(exp(r)-1) - 1/r)) +// = 1 - r**2/60 + r**4/2520 - r**6/100800 + ... +// We use a special Reme algorithm on [0,0.347] to generate +// a polynomial of degree 5 in r*r to approximate R1. The +// maximum error of this polynomial approximation is bounded +// by 2**-61. In other words, +// R1(z) ~ 1.0 + Q1*z + Q2*z**2 + Q3*z**3 + Q4*z**4 + Q5*z**5 +// where Q1 = -1.6666666666666567384E-2, +// Q2 = 3.9682539681370365873E-4, +// Q3 = -9.9206344733435987357E-6, +// Q4 = 2.5051361420808517002E-7, +// Q5 = -6.2843505682382617102E-9; +// (where z=r*r, and the values of Q1 to Q5 are listed below) +// with error bounded by +// | 5 | -61 +// | 1.0+Q1*z+...+Q5*z - R1(z) | <= 2 +// | | +// +// expm1(r) = exp(r)-1 is then computed by the following +// specific way which minimize the accumulation rounding error: +// 2 3 +// r r [ 3 - (R1 + R1*r/2) ] +// expm1(r) = r + --- + --- * [--------------------] +// 2 2 [ 6 - r*(3 - R1*r/2) ] +// +// To compensate the error in the argument reduction, we use +// expm1(r+c) = expm1(r) + c + expm1(r)*c +// ~ expm1(r) + c + r*c +// Thus c+r*c will be added in as the correction terms for +// expm1(r+c). Now rearrange the term to avoid optimization +// screw up: +// ( 2 2 ) +// ({ ( r [ R1 - (3 - R1*r/2) ] ) } r ) +// expm1(r+c)~r - ({r*(--- * [--------------------]-c)-c} - --- ) +// ({ ( 2 [ 6 - r*(3 - R1*r/2) ] ) } 2 ) +// ( ) +// +// = r - E +// 3. Scale back to obtain expm1(x): +// From step 1, we have +// expm1(x) = either 2**k*[expm1(r)+1] - 1 +// = or 2**k*[expm1(r) + (1-2**-k)] +// 4. Implementation notes: +// (A). To save one multiplication, we scale the coefficient Qi +// to Qi*2**i, and replace z by (x**2)/2. +// (B). To achieve maximum accuracy, we compute expm1(x) by +// (i) if x < -56*ln2, return -1.0, (raise inexact if x!=inf) +// (ii) if k=0, return r-E +// (iii) if k=-1, return 0.5*(r-E)-0.5 +// (iv) if k=1 if r < -0.25, return 2*((r+0.5)- E) +// else return 1.0+2.0*(r-E); +// (v) if (k<-2||k>56) return 2**k(1-(E-r)) - 1 (or exp(x)-1) +// (vi) if k <= 20, return 2**k((1-2**-k)-(E-r)), else +// (vii) return 2**k(1-((E+2**-k)-r)) +// +// Special cases: +// expm1(INF) is INF, expm1(NaN) is NaN; +// expm1(-INF) is -1, and +// for finite argument, only expm1(0)=0 is exact. +// +// Accuracy: +// according to an error analysis, the error is always less than +// 1 ulp (unit in the last place). +// +// Misc. info. +// For IEEE double +// if x > 7.09782712893383973096e+02 then expm1(x) overflow +// +// Constants: +// The hexadecimal values are the intended ones for the following +// constants. The decimal values may be used, provided that the +// compiler will convert from decimal to binary accurately enough +// to produce the hexadecimal values shown. +// + +// Expm1 returns e**x - 1, the base-e exponential of x minus 1. +// It is more accurate than Exp(x) - 1 when x is near zero. +// +// Special cases are: +// +// Expm1(+Inf) = +Inf +// Expm1(-Inf) = -1 +// Expm1(NaN) = NaN +// +// Very large values overflow to -1 or +Inf. +func Expm1(x float64) float64 { + return expm1(x) +} + +func expm1(x float64) float64 { + const ( + Othreshold = 7.09782712893383973096e+02 // 0x40862E42FEFA39EF + Ln2X56 = 3.88162421113569373274e+01 // 0x4043687a9f1af2b1 + Ln2HalfX3 = 1.03972077083991796413e+00 // 0x3ff0a2b23f3bab73 + Ln2Half = 3.46573590279972654709e-01 // 0x3fd62e42fefa39ef + Ln2Hi = 6.93147180369123816490e-01 // 0x3fe62e42fee00000 + Ln2Lo = 1.90821492927058770002e-10 // 0x3dea39ef35793c76 + InvLn2 = 1.44269504088896338700e+00 // 0x3ff71547652b82fe + Tiny = 1.0 / (1 << 54) // 2**-54 = 0x3c90000000000000 + // scaled coefficients related to expm1 + Q1 = -3.33333333333331316428e-02 // 0xBFA11111111110F4 + Q2 = 1.58730158725481460165e-03 // 0x3F5A01A019FE5585 + Q3 = -7.93650757867487942473e-05 // 0xBF14CE199EAADBB7 + Q4 = 4.00821782732936239552e-06 // 0x3ED0CFCA86E65239 + Q5 = -2.01099218183624371326e-07 // 0xBE8AFDB76E09C32D + ) + + // special cases + switch { + case IsInf(x, 1) || IsNaN(x): + return x + case IsInf(x, -1): + return -1 + } + + absx := x + sign := false + if x < 0 { + absx = -absx + sign = true + } + + // filter out huge argument + if absx >= Ln2X56 { // if |x| >= 56 * ln2 + if sign { + return -1 // x < -56*ln2, return -1 + } + if absx >= Othreshold { // if |x| >= 709.78... + return Inf(1) + } + } + + // argument reduction + var c float64 + var k int + if absx > Ln2Half { // if |x| > 0.5 * ln2 + var hi, lo float64 + if absx < Ln2HalfX3 { // and |x| < 1.5 * ln2 + if !sign { + hi = x - Ln2Hi + lo = Ln2Lo + k = 1 + } else { + hi = x + Ln2Hi + lo = -Ln2Lo + k = -1 + } + } else { + if !sign { + k = int(InvLn2*x + 0.5) + } else { + k = int(InvLn2*x - 0.5) + } + t := float64(k) + hi = x - t*Ln2Hi // t * Ln2Hi is exact here + lo = t * Ln2Lo + } + x = hi - lo + c = (hi - x) - lo + } else if absx < Tiny { // when |x| < 2**-54, return x + return x + } else { + k = 0 + } + + // x is now in primary range + hfx := 0.5 * x + hxs := x * hfx + r1 := 1 + hxs*(Q1+hxs*(Q2+hxs*(Q3+hxs*(Q4+hxs*Q5)))) + t := 3 - r1*hfx + e := hxs * ((r1 - t) / (6.0 - x*t)) + if k == 0 { + return x - (x*e - hxs) // c is 0 + } + e = (x*(e-c) - c) + e -= hxs + switch { + case k == -1: + return 0.5*(x-e) - 0.5 + case k == 1: + if x < -0.25 { + return -2 * (e - (x + 0.5)) + } + return 1 + 2*(x-e) + case k <= -2 || k > 56: // suffice to return exp(x)-1 + y := 1 - (e - x) + y = Float64frombits(Float64bits(y) + uint64(k)<<52) // add k to y's exponent + return y - 1 + } + if k < 20 { + t := Float64frombits(0x3ff0000000000000 - (0x20000000000000 >> uint(k))) // t=1-2**-k + y := t - (e - x) + y = Float64frombits(Float64bits(y) + uint64(k)<<52) // add k to y's exponent + return y + } + t = Float64frombits(uint64(0x3ff-k) << 52) // 2**-k + y := x - (e + t) + y += 1 + y = Float64frombits(Float64bits(y) + uint64(k)<<52) // add k to y's exponent + return y +} diff --git a/gnovm/stdlibs/math/export_test.gno b/gnovm/stdlibs/math/export_test.gno new file mode 100644 index 00000000000..8b2e36d0788 --- /dev/null +++ b/gnovm/stdlibs/math/export_test.gno @@ -0,0 +1,16 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Export internal functions for testing. +var ( + ExpGo = exp + Exp2Go = exp2 + HypotGo = hypot + SqrtGo = sqrt + TrigReduce = trigReduce +) + +const ReduceThreshold = reduceThreshold diff --git a/gnovm/stdlibs/math/floor.gno b/gnovm/stdlibs/math/floor.gno index 7d6320a1983..a3b98644856 100644 --- a/gnovm/stdlibs/math/floor.gno +++ b/gnovm/stdlibs/math/floor.gno @@ -4,10 +4,6 @@ package math -import ( - imath "internal/math" -) - // Floor returns the greatest integer value less than or equal to x. // // Special cases are: @@ -16,11 +12,6 @@ import ( // Floor(±Inf) = ±Inf // Floor(NaN) = NaN func Floor(x float64) float64 { - /* XXX - if haveArchFloor { - return archFloor(x) - } - */ return floor(x) } @@ -47,11 +38,6 @@ func floor(x float64) float64 { // Ceil(±Inf) = ±Inf // Ceil(NaN) = NaN func Ceil(x float64) float64 { - /* XXX - if haveArchCeil { - return archCeil(x) - } - */ return ceil(x) } @@ -67,11 +53,6 @@ func ceil(x float64) float64 { // Trunc(±Inf) = ±Inf // Trunc(NaN) = NaN func Trunc(x float64) float64 { - /* XXX - if haveArchTrunc { - return archTrunc(x) - } - */ return trunc(x) } @@ -100,7 +81,7 @@ func Round(x float64) float64 { // } // return t // } - bits := imath.Float64bits(x) + bits := Float64bits(x) e := uint(bits>>shift) & mask if e < bias { // Round abs(x) < 1 including denormals. @@ -118,7 +99,7 @@ func Round(x float64) float64 { bits += half >> e bits &^= fracMask >> e } - return imath.Float64frombits(bits) + return Float64frombits(bits) } // RoundToEven returns the nearest integer, rounding ties to even. @@ -139,7 +120,7 @@ func RoundToEven(x float64) float64 { // } // return t // } - bits := imath.Float64bits(x) + bits := Float64bits(x) e := uint(bits>>shift) & mask if e >= bias { // Round abs(x) >= 1. @@ -157,5 +138,5 @@ func RoundToEven(x float64) float64 { // Round abs(x) <= 0.5 including denormals. bits &= signMask // +-0 } - return imath.Float64frombits(bits) + return Float64frombits(bits) } diff --git a/gnovm/stdlibs/math/fma.gno b/gnovm/stdlibs/math/fma.gno new file mode 100644 index 00000000000..7239d41fac4 --- /dev/null +++ b/gnovm/stdlibs/math/fma.gno @@ -0,0 +1,172 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +import ( + "math/bits" +) + +func zero(x uint64) uint64 { + if x == 0 { + return 1 + } + return 0 + // branchless: + // return ((x>>1 | x&1) - 1) >> 63 +} + +func nonzero(x uint64) uint64 { + if x != 0 { + return 1 + } + return 0 + // branchless: + // return 1 - ((x>>1|x&1)-1)>>63 +} + +func shl(u1, u2 uint64, n uint) (r1, r2 uint64) { + r1 = u1<>(64-n) | u2<<(n-64) + r2 = u2 << n + return +} + +func shr(u1, u2 uint64, n uint) (r1, r2 uint64) { + r2 = u2>>n | u1<<(64-n) | u1>>(n-64) + r1 = u1 >> n + return +} + +// shrcompress compresses the bottom n+1 bits of the two-word +// value into a single bit. the result is equal to the value +// shifted to the right by n, except the result's 0th bit is +// set to the bitwise OR of the bottom n+1 bits. +func shrcompress(u1, u2 uint64, n uint) (r1, r2 uint64) { + // TODO: Performance here is really sensitive to the + // order/placement of these branches. n == 0 is common + // enough to be in the fast path. Perhaps more measurement + // needs to be done to find the optimal order/placement? + switch { + case n == 0: + return u1, u2 + case n == 64: + return 0, u1 | nonzero(u2) + case n >= 128: + return 0, nonzero(u1 | u2) + case n < 64: + r1, r2 = shr(u1, u2, n) + r2 |= nonzero(u2 & (1<> 63) + exp = int32(b>>52) & mask + mantissa = b & fracMask + + if exp == 0 { + // Normalize value if subnormal. + shift := uint(bits.LeadingZeros64(mantissa) - 11) + mantissa <<= shift + exp = 1 - int32(shift) + } else { + // Add implicit 1 bit + mantissa |= 1 << 52 + } + return +} + +// FMA returns x * y + z, computed with only one rounding. +// (That is, FMA returns the fused multiply-add of x, y, and z.) +func FMA(x, y, z float64) float64 { + bx, by, bz := Float64bits(x), Float64bits(y), Float64bits(z) + + // Inf or NaN or zero involved. At most one rounding will occur. + if x == 0.0 || y == 0.0 || z == 0.0 || bx&uvinf == uvinf || by&uvinf == uvinf { + return x*y + z + } + // Handle non-finite z separately. Evaluating x*y+z where + // x and y are finite, but z is infinite, should always result in z. + if bz&uvinf == uvinf { + return z + } + + // Inputs are (sub)normal. + // Split x, y, z into sign, exponent, mantissa. + xs, xe, xm := split(bx) + ys, ye, ym := split(by) + zs, ze, zm := split(bz) + + // Compute product p = x*y as sign, exponent, two-word mantissa. + // Start with exponent. "is normal" bit isn't subtracted yet. + pe := xe + ye - bias + 1 + + // pm1:pm2 is the double-word mantissa for the product p. + // Shift left to leave top bit in product. Effectively + // shifts the 106-bit product to the left by 21. + pm1, pm2 := bits.Mul64(xm<<10, ym<<11) + zm1, zm2 := zm<<10, uint64(0) + ps := xs ^ ys // product sign + + // normalize to 62nd bit + is62zero := uint((^pm1 >> 62) & 1) + pm1, pm2 = shl(pm1, pm2, is62zero) + pe -= int32(is62zero) + + // Swap addition operands so |p| >= |z| + if pe < ze || pe == ze && pm1 < zm1 { + ps, pe, pm1, pm2, zs, ze, zm1, zm2 = zs, ze, zm1, zm2, ps, pe, pm1, pm2 + } + + // Align significands + zm1, zm2 = shrcompress(zm1, zm2, uint(pe-ze)) + + // Compute resulting significands, normalizing if necessary. + var m, c uint64 + if ps == zs { + // Adding (pm1:pm2) + (zm1:zm2) + pm2, c = bits.Add64(pm2, zm2, 0) + pm1, _ = bits.Add64(pm1, zm1, c) + pe -= int32(^pm1 >> 63) + pm1, m = shrcompress(pm1, pm2, uint(64+pm1>>63)) + } else { + // Subtracting (pm1:pm2) - (zm1:zm2) + // TODO: should we special-case cancellation? + pm2, c = bits.Sub64(pm2, zm2, 0) + pm1, _ = bits.Sub64(pm1, zm1, c) + nz := lz(pm1, pm2) + pe -= nz + m, pm2 = shl(pm1, pm2, uint(nz-1)) + m |= nonzero(pm2) + } + + // Round and break ties to even + if pe > 1022+bias || pe == 1022+bias && (m+1<<9)>>63 == 1 { + // rounded value overflows exponent range + return Float64frombits(uint64(ps)<<63 | uvinf) + } + if pe < 0 { + n := uint(-pe) + m = m>>n | nonzero(m&(1<> 10) & ^zero((m&(1<<10-1))^1<<9) + pe &= -int32(nonzero(m)) + return Float64frombits(uint64(ps)<<63 + uint64(pe)<<52 + m) +} diff --git a/gnovm/stdlibs/math/frexp.gno b/gnovm/stdlibs/math/frexp.gno new file mode 100644 index 00000000000..7a196abd01d --- /dev/null +++ b/gnovm/stdlibs/math/frexp.gno @@ -0,0 +1,36 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Frexp breaks f into a normalized fraction +// and an integral power of two. +// It returns frac and exp satisfying f == frac × 2**exp, +// with the absolute value of frac in the interval [½, 1). +// +// Special cases are: +// +// Frexp(±0) = ±0, 0 +// Frexp(±Inf) = ±Inf, 0 +// Frexp(NaN) = NaN, 0 +func Frexp(f float64) (frac float64, exp int) { + return frexp(f) +} + +func frexp(f float64) (frac float64, exp int) { + // special cases + switch { + case f == 0: + return f, 0 // correctly return -0 + case IsInf(f, 0) || IsNaN(f): + return f, 0 + } + f, exp = normalize(f) + x := Float64bits(f) + exp += int((x>>shift)&mask) - bias + 1 + x &^= mask << shift + x |= (-1 + bias) << shift + frac = Float64frombits(x) + return +} diff --git a/gnovm/stdlibs/math/gamma.gno b/gnovm/stdlibs/math/gamma.gno new file mode 100644 index 00000000000..99f3bd0241f --- /dev/null +++ b/gnovm/stdlibs/math/gamma.gno @@ -0,0 +1,224 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below are from http://netlib.sandia.gov/cephes/cprob/gamma.c. +// The go code is a simplified version of the original C. +// +// tgamma.c +// +// Gamma function +// +// SYNOPSIS: +// +// double x, y, tgamma(); +// extern int signgam; +// +// y = tgamma( x ); +// +// DESCRIPTION: +// +// Returns gamma function of the argument. The result is +// correctly signed, and the sign (+1 or -1) is also +// returned in a global (extern) variable named signgam. +// This variable is also filled in by the logarithmic gamma +// function lgamma(). +// +// Arguments |x| <= 34 are reduced by recurrence and the function +// approximated by a rational function of degree 6/7 in the +// interval (2,3). Large arguments are handled by Stirling's +// formula. Large negative arguments are made positive using +// a reflection formula. +// +// ACCURACY: +// +// Relative error: +// arithmetic domain # trials peak rms +// DEC -34, 34 10000 1.3e-16 2.5e-17 +// IEEE -170,-33 20000 2.3e-15 3.3e-16 +// IEEE -33, 33 20000 9.4e-16 2.2e-16 +// IEEE 33, 171.6 20000 2.3e-15 3.2e-16 +// +// Error for arguments outside the test range will be larger +// owing to error amplification by the exponential function. +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov + +var _gamP = [...]float64{ + 1.60119522476751861407e-04, + 1.19135147006586384913e-03, + 1.04213797561761569935e-02, + 4.76367800457137231464e-02, + 2.07448227648435975150e-01, + 4.94214826801497100753e-01, + 9.99999999999999996796e-01, +} + +var _gamQ = [...]float64{ + -2.31581873324120129819e-05, + 5.39605580493303397842e-04, + -4.45641913851797240494e-03, + 1.18139785222060435552e-02, + 3.58236398605498653373e-02, + -2.34591795718243348568e-01, + 7.14304917030273074085e-02, + 1.00000000000000000320e+00, +} + +var _gamS = [...]float64{ + 7.87311395793093628397e-04, + -2.29549961613378126380e-04, + -2.68132617805781232825e-03, + 3.47222221605458667310e-03, + 8.33333333333482257126e-02, +} + +// Gamma function computed by Stirling's formula. +// The pair of results must be multiplied together to get the actual answer. +// The multiplication is left to the caller so that, if careful, the caller can avoid +// infinity for 172 <= x <= 180. +// The polynomial is valid for 33 <= x <= 172; larger values are only used +// in reciprocal and produce denormalized floats. The lower precision there +// masks any imprecision in the polynomial. +func stirling(x float64) (float64, float64) { + if x > 200 { + return Inf(1), 1 + } + const ( + SqrtTwoPi = 2.506628274631000502417 + MaxStirling = 143.01608 + ) + w := 1 / x + w = 1 + w*((((_gamS[0]*w+_gamS[1])*w+_gamS[2])*w+_gamS[3])*w+_gamS[4]) + y1 := Exp(x) + y2 := 1.0 + if x > MaxStirling { // avoid Pow() overflow + v := Pow(x, 0.5*x-0.25) + y1, y2 = v, v/y1 + } else { + y1 = Pow(x, x-0.5) / y1 + } + return y1, SqrtTwoPi * w * y2 +} + +// Gamma returns the Gamma function of x. +// +// Special cases are: +// +// Gamma(+Inf) = +Inf +// Gamma(+0) = +Inf +// Gamma(-0) = -Inf +// Gamma(x) = NaN for integer x < 0 +// Gamma(-Inf) = NaN +// Gamma(NaN) = NaN +func Gamma(x float64) float64 { + const Euler = 0.57721566490153286060651209008240243104215933593992 // A001620 + // special cases + switch { + case isNegInt(x) || IsInf(x, -1) || IsNaN(x): + return NaN() + case IsInf(x, 1): + return Inf(1) + case x == 0: + if Signbit(x) { + return Inf(-1) + } + return Inf(1) + } + q := Abs(x) + p := Floor(q) + if q > 33 { + if x >= 0 { + y1, y2 := stirling(x) + return y1 * y2 + } + // Note: x is negative but (checked above) not a negative integer, + // so x must be small enough to be in range for conversion to int64. + // If |x| were >= 2⁶³ it would have to be an integer. + signgam := 1 + if ip := int64(p); ip&1 == 0 { + signgam = -1 + } + z := q - p + if z > 0.5 { + p = p + 1 + z = q - p + } + z = q * Sin(Pi*z) + if z == 0 { + return Inf(signgam) + } + sq1, sq2 := stirling(q) + absz := Abs(z) + d := absz * sq1 * sq2 + if IsInf(d, 0) { + z = Pi / absz / sq1 / sq2 + } else { + z = Pi / d + } + return float64(signgam) * z + } + + // Reduce argument + z := 1.0 + for x >= 3 { + x = x - 1 + z = z * x + } + for x < 0 { + if x > -1e-09 { + goto small + } + z = z / x + x = x + 1 + } + for x < 2 { + if x < 1e-09 { + goto small + } + z = z / x + x = x + 1 + } + + if x == 2 { + return z + } + + x = x - 2 + p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6] + q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7] + return z * p / q + +small: + if x == 0 { + return Inf(1) + } + return z / ((1 + Euler*x) * x) +} + +func isNegInt(x float64) bool { + if x < 0 { + _, xf := Modf(x) + return xf == 0 + } + return false +} diff --git a/gnovm/stdlibs/math/hypot.gno b/gnovm/stdlibs/math/hypot.gno new file mode 100644 index 00000000000..a57835a2601 --- /dev/null +++ b/gnovm/stdlibs/math/hypot.gno @@ -0,0 +1,41 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Hypot -- sqrt(p*p + q*q), but overflows only if the result does. +*/ + +// Hypot returns Sqrt(p*p + q*q), taking care to avoid +// unnecessary overflow and underflow. +// +// Special cases are: +// +// Hypot(±Inf, q) = +Inf +// Hypot(p, ±Inf) = +Inf +// Hypot(NaN, q) = NaN +// Hypot(p, NaN) = NaN +func Hypot(p, q float64) float64 { + return hypot(p, q) +} + +func hypot(p, q float64) float64 { + p, q = Abs(p), Abs(q) + // special cases + switch { + case IsInf(p, 1) || IsInf(q, 1): + return Inf(1) + case IsNaN(p) || IsNaN(q): + return NaN() + } + if p < q { + p, q = q, p + } + if p == 0 { + return 0 + } + q = q / p + return p * Sqrt(1+q*q) +} diff --git a/gnovm/stdlibs/math/j0.gno b/gnovm/stdlibs/math/j0.gno new file mode 100644 index 00000000000..a99e629f042 --- /dev/null +++ b/gnovm/stdlibs/math/j0.gno @@ -0,0 +1,437 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Bessel function of the first and second kinds of order zero. +*/ + +// The original C code and the long comment below are +// from FreeBSD's /usr/src/lib/msun/src/e_j0.c and +// came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_j0(x), __ieee754_y0(x) +// Bessel function of the first and second kinds of order zero. +// Method -- j0(x): +// 1. For tiny x, we use j0(x) = 1 - x**2/4 + x**4/64 - ... +// 2. Reduce x to |x| since j0(x)=j0(-x), and +// for x in (0,2) +// j0(x) = 1-z/4+ z**2*R0/S0, where z = x*x; +// (precision: |j0-1+z/4-z**2R0/S0 |<2**-63.67 ) +// for x in (2,inf) +// j0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)-q0(x)*sin(x0)) +// where x0 = x-pi/4. It is better to compute sin(x0),cos(x0) +// as follow: +// cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4) +// = 1/sqrt(2) * (cos(x) + sin(x)) +// sin(x0) = sin(x)cos(pi/4)-cos(x)sin(pi/4) +// = 1/sqrt(2) * (sin(x) - cos(x)) +// (To avoid cancellation, use +// sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) +// to compute the worse one.) +// +// 3 Special cases +// j0(nan)= nan +// j0(0) = 1 +// j0(inf) = 0 +// +// Method -- y0(x): +// 1. For x<2. +// Since +// y0(x) = 2/pi*(j0(x)*(ln(x/2)+Euler) + x**2/4 - ...) +// therefore y0(x)-2/pi*j0(x)*ln(x) is an even function. +// We use the following function to approximate y0, +// y0(x) = U(z)/V(z) + (2/pi)*(j0(x)*ln(x)), z= x**2 +// where +// U(z) = u00 + u01*z + ... + u06*z**6 +// V(z) = 1 + v01*z + ... + v04*z**4 +// with absolute approximation error bounded by 2**-72. +// Note: For tiny x, U/V = u0 and j0(x)~1, hence +// y0(tiny) = u0 + (2/pi)*ln(tiny), (choose tiny<2**-27) +// 2. For x>=2. +// y0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)+q0(x)*sin(x0)) +// where x0 = x-pi/4. It is better to compute sin(x0),cos(x0) +// by the method mentioned above. +// 3. Special cases: y0(0)=-inf, y0(x<0)=NaN, y0(inf)=0. +// + +// J0 returns the order-zero Bessel function of the first kind. +// +// Special cases are: +// +// J0(±Inf) = 0 +// J0(0) = 1 +// J0(NaN) = NaN +func J0(x float64) float64 { + const ( + Huge = 1e300 + TwoM27 = 1.0 / (1 << 27) // 2**-27 0x3e40000000000000 + TwoM13 = 1.0 / (1 << 13) // 2**-13 0x3f20000000000000 + Two129 = 1 << 129 // 2**129 0x4800000000000000 + // R0/S0 on [0, 2] + R02 = 1.56249999999999947958e-02 // 0x3F8FFFFFFFFFFFFD + R03 = -1.89979294238854721751e-04 // 0xBF28E6A5B61AC6E9 + R04 = 1.82954049532700665670e-06 // 0x3EBEB1D10C503919 + R05 = -4.61832688532103189199e-09 // 0xBE33D5E773D63FCE + S01 = 1.56191029464890010492e-02 // 0x3F8FFCE882C8C2A4 + S02 = 1.16926784663337450260e-04 // 0x3F1EA6D2DD57DBF4 + S03 = 5.13546550207318111446e-07 // 0x3EA13B54CE84D5A9 + S04 = 1.16614003333790000205e-09 // 0x3E1408BCF4745D8F + ) + // special cases + switch { + case IsNaN(x): + return x + case IsInf(x, 0): + return 0 + case x == 0: + return 1 + } + + x = Abs(x) + if x >= 2 { + s, c := Sincos(x) + ss := s - c + cc := s + c + + // make sure x+x does not overflow + if x < MaxFloat64/2 { + z := -Cos(x + x) + if s*c < 0 { + cc = z / ss + } else { + ss = z / cc + } + } + + // j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x) + // y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x) + + var z float64 + if x > Two129 { // |x| > ~6.8056e+38 + z = (1 / SqrtPi) * cc / Sqrt(x) + } else { + u := pzero(x) + v := qzero(x) + z = (1 / SqrtPi) * (u*cc - v*ss) / Sqrt(x) + } + return z // |x| >= 2.0 + } + if x < TwoM13 { // |x| < ~1.2207e-4 + if x < TwoM27 { + return 1 // |x| < ~7.4506e-9 + } + return 1 - 0.25*x*x // ~7.4506e-9 < |x| < ~1.2207e-4 + } + z := x * x + r := z * (R02 + z*(R03+z*(R04+z*R05))) + s := 1 + z*(S01+z*(S02+z*(S03+z*S04))) + if x < 1 { + return 1 + z*(-0.25+(r/s)) // |x| < 1.00 + } + u := 0.5 * x + return (1+u)*(1-u) + z*(r/s) // 1.0 < |x| < 2.0 +} + +// Y0 returns the order-zero Bessel function of the second kind. +// +// Special cases are: +// +// Y0(+Inf) = 0 +// Y0(0) = -Inf +// Y0(x < 0) = NaN +// Y0(NaN) = NaN +func Y0(x float64) float64 { + const ( + TwoM27 = 1.0 / (1 << 27) // 2**-27 0x3e40000000000000 + Two129 = 1 << 129 // 2**129 0x4800000000000000 + U00 = -7.38042951086872317523e-02 // 0xBFB2E4D699CBD01F + U01 = 1.76666452509181115538e-01 // 0x3FC69D019DE9E3FC + U02 = -1.38185671945596898896e-02 // 0xBF8C4CE8B16CFA97 + U03 = 3.47453432093683650238e-04 // 0x3F36C54D20B29B6B + U04 = -3.81407053724364161125e-06 // 0xBECFFEA773D25CAD + U05 = 1.95590137035022920206e-08 // 0x3E5500573B4EABD4 + U06 = -3.98205194132103398453e-11 // 0xBDC5E43D693FB3C8 + V01 = 1.27304834834123699328e-02 // 0x3F8A127091C9C71A + V02 = 7.60068627350353253702e-05 // 0x3F13ECBBF578C6C1 + V03 = 2.59150851840457805467e-07 // 0x3E91642D7FF202FD + V04 = 4.41110311332675467403e-10 // 0x3DFE50183BD6D9EF + ) + // special cases + switch { + case x < 0 || IsNaN(x): + return NaN() + case IsInf(x, 1): + return 0 + case x == 0: + return Inf(-1) + } + + if x >= 2 { // |x| >= 2.0 + + // y0(x) = sqrt(2/(pi*x))*(p0(x)*sin(x0)+q0(x)*cos(x0)) + // where x0 = x-pi/4 + // Better formula: + // cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4) + // = 1/sqrt(2) * (sin(x) + cos(x)) + // sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + // = 1/sqrt(2) * (sin(x) - cos(x)) + // To avoid cancellation, use + // sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + // to compute the worse one. + + s, c := Sincos(x) + ss := s - c + cc := s + c + + // j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x) + // y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x) + + // make sure x+x does not overflow + if x < MaxFloat64/2 { + z := -Cos(x + x) + if s*c < 0 { + cc = z / ss + } else { + ss = z / cc + } + } + var z float64 + if x > Two129 { // |x| > ~6.8056e+38 + z = (1 / SqrtPi) * ss / Sqrt(x) + } else { + u := pzero(x) + v := qzero(x) + z = (1 / SqrtPi) * (u*ss + v*cc) / Sqrt(x) + } + return z // |x| >= 2.0 + } + if x <= TwoM27 { + return U00 + (2/Pi)*Log(x) // |x| < ~7.4506e-9 + } + z := x * x + u := U00 + z*(U01+z*(U02+z*(U03+z*(U04+z*(U05+z*U06))))) + v := 1 + z*(V01+z*(V02+z*(V03+z*V04))) + return u/v + (2/Pi)*J0(x)*Log(x) // ~7.4506e-9 < |x| < 2.0 +} + +// The asymptotic expansions of pzero is +// 1 - 9/128 s**2 + 11025/98304 s**4 - ..., where s = 1/x. +// For x >= 2, We approximate pzero by +// pzero(x) = 1 + (R/S) +// where R = pR0 + pR1*s**2 + pR2*s**4 + ... + pR5*s**10 +// S = 1 + pS0*s**2 + ... + pS4*s**10 +// and +// | pzero(x)-1-R/S | <= 2 ** ( -60.26) + +// for x in [inf, 8]=1/[0,0.125] +var p0R8 = [6]float64{ + 0.00000000000000000000e+00, // 0x0000000000000000 + -7.03124999999900357484e-02, // 0xBFB1FFFFFFFFFD32 + -8.08167041275349795626e+00, // 0xC02029D0B44FA779 + -2.57063105679704847262e+02, // 0xC07011027B19E863 + -2.48521641009428822144e+03, // 0xC0A36A6ECD4DCAFC + -5.25304380490729545272e+03, // 0xC0B4850B36CC643D +} + +var p0S8 = [5]float64{ + 1.16534364619668181717e+02, // 0x405D223307A96751 + 3.83374475364121826715e+03, // 0x40ADF37D50596938 + 4.05978572648472545552e+04, // 0x40E3D2BB6EB6B05F + 1.16752972564375915681e+05, // 0x40FC810F8F9FA9BD + 4.76277284146730962675e+04, // 0x40E741774F2C49DC +} + +// for x in [8,4.5454]=1/[0.125,0.22001] +var p0R5 = [6]float64{ + -1.14125464691894502584e-11, // 0xBDA918B147E495CC + -7.03124940873599280078e-02, // 0xBFB1FFFFE69AFBC6 + -4.15961064470587782438e+00, // 0xC010A370F90C6BBF + -6.76747652265167261021e+01, // 0xC050EB2F5A7D1783 + -3.31231299649172967747e+02, // 0xC074B3B36742CC63 + -3.46433388365604912451e+02, // 0xC075A6EF28A38BD7 +} + +var p0S5 = [5]float64{ + 6.07539382692300335975e+01, // 0x404E60810C98C5DE + 1.05125230595704579173e+03, // 0x40906D025C7E2864 + 5.97897094333855784498e+03, // 0x40B75AF88FBE1D60 + 9.62544514357774460223e+03, // 0x40C2CCB8FA76FA38 + 2.40605815922939109441e+03, // 0x40A2CC1DC70BE864 +} + +// for x in [4.547,2.8571]=1/[0.2199,0.35001] +var p0R3 = [6]float64{ + -2.54704601771951915620e-09, // 0xBE25E1036FE1AA86 + -7.03119616381481654654e-02, // 0xBFB1FFF6F7C0E24B + -2.40903221549529611423e+00, // 0xC00345B2AEA48074 + -2.19659774734883086467e+01, // 0xC035F74A4CB94E14 + -5.80791704701737572236e+01, // 0xC04D0A22420A1A45 + -3.14479470594888503854e+01, // 0xC03F72ACA892D80F +} + +var p0S3 = [5]float64{ + 3.58560338055209726349e+01, // 0x4041ED9284077DD3 + 3.61513983050303863820e+02, // 0x40769839464A7C0E + 1.19360783792111533330e+03, // 0x4092A66E6D1061D6 + 1.12799679856907414432e+03, // 0x40919FFCB8C39B7E + 1.73580930813335754692e+02, // 0x4065B296FC379081 +} + +// for x in [2.8570,2]=1/[0.3499,0.5] +var p0R2 = [6]float64{ + -8.87534333032526411254e-08, // 0xBE77D316E927026D + -7.03030995483624743247e-02, // 0xBFB1FF62495E1E42 + -1.45073846780952986357e+00, // 0xBFF736398A24A843 + -7.63569613823527770791e+00, // 0xC01E8AF3EDAFA7F3 + -1.11931668860356747786e+01, // 0xC02662E6C5246303 + -3.23364579351335335033e+00, // 0xC009DE81AF8FE70F +} + +var p0S2 = [5]float64{ + 2.22202997532088808441e+01, // 0x40363865908B5959 + 1.36206794218215208048e+02, // 0x4061069E0EE8878F + 2.70470278658083486789e+02, // 0x4070E78642EA079B + 1.53875394208320329881e+02, // 0x40633C033AB6FAFF + 1.46576176948256193810e+01, // 0x402D50B344391809 +} + +func pzero(x float64) float64 { + var p *[6]float64 + var q *[5]float64 + if x >= 8 { + p = &p0R8 + q = &p0S8 + } else if x >= 4.5454 { + p = &p0R5 + q = &p0S5 + } else if x >= 2.8571 { + p = &p0R3 + q = &p0S3 + } else if x >= 2 { + p = &p0R2 + q = &p0S2 + } + z := 1 / (x * x) + r := p[0] + z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))) + s := 1 + z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4])))) + return 1 + r/s +} + +// For x >= 8, the asymptotic expansions of qzero is +// -1/8 s + 75/1024 s**3 - ..., where s = 1/x. +// We approximate pzero by +// qzero(x) = s*(-1.25 + (R/S)) +// where R = qR0 + qR1*s**2 + qR2*s**4 + ... + qR5*s**10 +// S = 1 + qS0*s**2 + ... + qS5*s**12 +// and +// | qzero(x)/s +1.25-R/S | <= 2**(-61.22) + +// for x in [inf, 8]=1/[0,0.125] +var q0R8 = [6]float64{ + 0.00000000000000000000e+00, // 0x0000000000000000 + 7.32421874999935051953e-02, // 0x3FB2BFFFFFFFFE2C + 1.17682064682252693899e+01, // 0x402789525BB334D6 + 5.57673380256401856059e+02, // 0x40816D6315301825 + 8.85919720756468632317e+03, // 0x40C14D993E18F46D + 3.70146267776887834771e+04, // 0x40E212D40E901566 +} + +var q0S8 = [6]float64{ + 1.63776026895689824414e+02, // 0x406478D5365B39BC + 8.09834494656449805916e+03, // 0x40BFA2584E6B0563 + 1.42538291419120476348e+05, // 0x4101665254D38C3F + 8.03309257119514397345e+05, // 0x412883DA83A52B43 + 8.40501579819060512818e+05, // 0x4129A66B28DE0B3D + -3.43899293537866615225e+05, // 0xC114FD6D2C9530C5 +} + +// for x in [8,4.5454]=1/[0.125,0.22001] +var q0R5 = [6]float64{ + 1.84085963594515531381e-11, // 0x3DB43D8F29CC8CD9 + 7.32421766612684765896e-02, // 0x3FB2BFFFD172B04C + 5.83563508962056953777e+00, // 0x401757B0B9953DD3 + 1.35111577286449829671e+02, // 0x4060E3920A8788E9 + 1.02724376596164097464e+03, // 0x40900CF99DC8C481 + 1.98997785864605384631e+03, // 0x409F17E953C6E3A6 +} + +var q0S5 = [6]float64{ + 8.27766102236537761883e+01, // 0x4054B1B3FB5E1543 + 2.07781416421392987104e+03, // 0x40A03BA0DA21C0CE + 1.88472887785718085070e+04, // 0x40D267D27B591E6D + 5.67511122894947329769e+04, // 0x40EBB5E397E02372 + 3.59767538425114471465e+04, // 0x40E191181F7A54A0 + -5.35434275601944773371e+03, // 0xC0B4EA57BEDBC609 +} + +// for x in [4.547,2.8571]=1/[0.2199,0.35001] +var q0R3 = [6]float64{ + 4.37741014089738620906e-09, // 0x3E32CD036ADECB82 + 7.32411180042911447163e-02, // 0x3FB2BFEE0E8D0842 + 3.34423137516170720929e+00, // 0x400AC0FC61149CF5 + 4.26218440745412650017e+01, // 0x40454F98962DAEDD + 1.70808091340565596283e+02, // 0x406559DBE25EFD1F + 1.66733948696651168575e+02, // 0x4064D77C81FA21E0 +} + +var q0S3 = [6]float64{ + 4.87588729724587182091e+01, // 0x40486122BFE343A6 + 7.09689221056606015736e+02, // 0x40862D8386544EB3 + 3.70414822620111362994e+03, // 0x40ACF04BE44DFC63 + 6.46042516752568917582e+03, // 0x40B93C6CD7C76A28 + 2.51633368920368957333e+03, // 0x40A3A8AAD94FB1C0 + -1.49247451836156386662e+02, // 0xC062A7EB201CF40F +} + +// for x in [2.8570,2]=1/[0.3499,0.5] +var q0R2 = [6]float64{ + 1.50444444886983272379e-07, // 0x3E84313B54F76BDB + 7.32234265963079278272e-02, // 0x3FB2BEC53E883E34 + 1.99819174093815998816e+00, // 0x3FFFF897E727779C + 1.44956029347885735348e+01, // 0x402CFDBFAAF96FE5 + 3.16662317504781540833e+01, // 0x403FAA8E29FBDC4A + 1.62527075710929267416e+01, // 0x403040B171814BB4 +} + +var q0S2 = [6]float64{ + 3.03655848355219184498e+01, // 0x403E5D96F7C07AED + 2.69348118608049844624e+02, // 0x4070D591E4D14B40 + 8.44783757595320139444e+02, // 0x408A664522B3BF22 + 8.82935845112488550512e+02, // 0x408B977C9C5CC214 + 2.12666388511798828631e+02, // 0x406A95530E001365 + -5.31095493882666946917e+00, // 0xC0153E6AF8B32931 +} + +func qzero(x float64) float64 { + var p, q *[6]float64 + if x >= 8 { + p = &q0R8 + q = &q0S8 + } else if x >= 4.5454 { + p = &q0R5 + q = &q0S5 + } else if x >= 2.8571 { + p = &q0R3 + q = &q0S3 + } else if x >= 2 { + p = &q0R2 + q = &q0S2 + } + z := 1 / (x * x) + r := p[0] + z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))) + s := 1 + z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))) + return (-0.125 + r/s) / x +} diff --git a/gnovm/stdlibs/math/j1.gno b/gnovm/stdlibs/math/j1.gno new file mode 100644 index 00000000000..1e30a24ab4f --- /dev/null +++ b/gnovm/stdlibs/math/j1.gno @@ -0,0 +1,432 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Bessel function of the first and second kinds of order one. +*/ + +// The original C code and the long comment below are +// from FreeBSD's /usr/src/lib/msun/src/e_j1.c and +// came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_j1(x), __ieee754_y1(x) +// Bessel function of the first and second kinds of order one. +// Method -- j1(x): +// 1. For tiny x, we use j1(x) = x/2 - x**3/16 + x**5/384 - ... +// 2. Reduce x to |x| since j1(x)=-j1(-x), and +// for x in (0,2) +// j1(x) = x/2 + x*z*R0/S0, where z = x*x; +// (precision: |j1/x - 1/2 - R0/S0 |<2**-61.51 ) +// for x in (2,inf) +// j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1)) +// y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1)) +// where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1) +// as follow: +// cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4) +// = 1/sqrt(2) * (sin(x) - cos(x)) +// sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) +// = -1/sqrt(2) * (sin(x) + cos(x)) +// (To avoid cancellation, use +// sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) +// to compute the worse one.) +// +// 3 Special cases +// j1(nan)= nan +// j1(0) = 0 +// j1(inf) = 0 +// +// Method -- y1(x): +// 1. screen out x<=0 cases: y1(0)=-inf, y1(x<0)=NaN +// 2. For x<2. +// Since +// y1(x) = 2/pi*(j1(x)*(ln(x/2)+Euler)-1/x-x/2+5/64*x**3-...) +// therefore y1(x)-2/pi*j1(x)*ln(x)-1/x is an odd function. +// We use the following function to approximate y1, +// y1(x) = x*U(z)/V(z) + (2/pi)*(j1(x)*ln(x)-1/x), z= x**2 +// where for x in [0,2] (abs err less than 2**-65.89) +// U(z) = U0[0] + U0[1]*z + ... + U0[4]*z**4 +// V(z) = 1 + v0[0]*z + ... + v0[4]*z**5 +// Note: For tiny x, 1/x dominate y1 and hence +// y1(tiny) = -2/pi/tiny, (choose tiny<2**-54) +// 3. For x>=2. +// y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1)) +// where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1) +// by method mentioned above. + +// J1 returns the order-one Bessel function of the first kind. +// +// Special cases are: +// +// J1(±Inf) = 0 +// J1(NaN) = NaN +func J1(x float64) float64 { + const ( + TwoM27 = 1.0 / (1 << 27) // 2**-27 0x3e40000000000000 + Two129 = 1 << 129 // 2**129 0x4800000000000000 + // R0/S0 on [0, 2] + R00 = -6.25000000000000000000e-02 // 0xBFB0000000000000 + R01 = 1.40705666955189706048e-03 // 0x3F570D9F98472C61 + R02 = -1.59955631084035597520e-05 // 0xBEF0C5C6BA169668 + R03 = 4.96727999609584448412e-08 // 0x3E6AAAFA46CA0BD9 + S01 = 1.91537599538363460805e-02 // 0x3F939D0B12637E53 + S02 = 1.85946785588630915560e-04 // 0x3F285F56B9CDF664 + S03 = 1.17718464042623683263e-06 // 0x3EB3BFF8333F8498 + S04 = 5.04636257076217042715e-09 // 0x3E35AC88C97DFF2C + S05 = 1.23542274426137913908e-11 // 0x3DAB2ACFCFB97ED8 + ) + // special cases + switch { + case IsNaN(x): + return x + case IsInf(x, 0) || x == 0: + return 0 + } + + sign := false + if x < 0 { + x = -x + sign = true + } + if x >= 2 { + s, c := Sincos(x) + ss := -s - c + cc := s - c + + // make sure x+x does not overflow + if x < MaxFloat64/2 { + z := Cos(x + x) + if s*c > 0 { + cc = z / ss + } else { + ss = z / cc + } + } + + // j1(x) = 1/sqrt(pi) * (P(1,x)*cc - Q(1,x)*ss) / sqrt(x) + // y1(x) = 1/sqrt(pi) * (P(1,x)*ss + Q(1,x)*cc) / sqrt(x) + + var z float64 + if x > Two129 { + z = (1 / SqrtPi) * cc / Sqrt(x) + } else { + u := pone(x) + v := qone(x) + z = (1 / SqrtPi) * (u*cc - v*ss) / Sqrt(x) + } + if sign { + return -z + } + return z + } + if x < TwoM27 { // |x|<2**-27 + return 0.5 * x // inexact if x!=0 necessary + } + z := x * x + r := z * (R00 + z*(R01+z*(R02+z*R03))) + s := 1.0 + z*(S01+z*(S02+z*(S03+z*(S04+z*S05)))) + r *= x + z = 0.5*x + r/s + if sign { + return -z + } + return z +} + +// Y1 returns the order-one Bessel function of the second kind. +// +// Special cases are: +// +// Y1(+Inf) = 0 +// Y1(0) = -Inf +// Y1(x < 0) = NaN +// Y1(NaN) = NaN +func Y1(x float64) float64 { + const ( + TwoM54 = 1.0 / (1 << 54) // 2**-54 0x3c90000000000000 + Two129 = 1 << 129 // 2**129 0x4800000000000000 + U00 = -1.96057090646238940668e-01 // 0xBFC91866143CBC8A + U01 = 5.04438716639811282616e-02 // 0x3FA9D3C776292CD1 + U02 = -1.91256895875763547298e-03 // 0xBF5F55E54844F50F + U03 = 2.35252600561610495928e-05 // 0x3EF8AB038FA6B88E + U04 = -9.19099158039878874504e-08 // 0xBE78AC00569105B8 + V00 = 1.99167318236649903973e-02 // 0x3F94650D3F4DA9F0 + V01 = 2.02552581025135171496e-04 // 0x3F2A8C896C257764 + V02 = 1.35608801097516229404e-06 // 0x3EB6C05A894E8CA6 + V03 = 6.22741452364621501295e-09 // 0x3E3ABF1D5BA69A86 + V04 = 1.66559246207992079114e-11 // 0x3DB25039DACA772A + ) + // special cases + switch { + case x < 0 || IsNaN(x): + return NaN() + case IsInf(x, 1): + return 0 + case x == 0: + return Inf(-1) + } + + if x >= 2 { + s, c := Sincos(x) + ss := -s - c + cc := s - c + + // make sure x+x does not overflow + if x < MaxFloat64/2 { + z := Cos(x + x) + if s*c > 0 { + cc = z / ss + } else { + ss = z / cc + } + } + // y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x0)+q1(x)*cos(x0)) + // where x0 = x-3pi/4 + // Better formula: + // cos(x0) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4) + // = 1/sqrt(2) * (sin(x) - cos(x)) + // sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + // = -1/sqrt(2) * (cos(x) + sin(x)) + // To avoid cancellation, use + // sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + // to compute the worse one. + + var z float64 + if x > Two129 { + z = (1 / SqrtPi) * ss / Sqrt(x) + } else { + u := pone(x) + v := qone(x) + z = (1 / SqrtPi) * (u*ss + v*cc) / Sqrt(x) + } + return z + } + if x <= TwoM54 { // x < 2**-54 + return -(2 / Pi) / x + } + z := x * x + u := U00 + z*(U01+z*(U02+z*(U03+z*U04))) + v := 1 + z*(V00+z*(V01+z*(V02+z*(V03+z*V04)))) + return x*(u/v) + (2/Pi)*(J1(x)*Log(x)-1/x) +} + +// For x >= 8, the asymptotic expansions of pone is +// 1 + 15/128 s**2 - 4725/2**15 s**4 - ..., where s = 1/x. +// We approximate pone by +// pone(x) = 1 + (R/S) +// where R = pr0 + pr1*s**2 + pr2*s**4 + ... + pr5*s**10 +// S = 1 + ps0*s**2 + ... + ps4*s**10 +// and +// | pone(x)-1-R/S | <= 2**(-60.06) + +// for x in [inf, 8]=1/[0,0.125] +var p1R8 = [6]float64{ + 0.00000000000000000000e+00, // 0x0000000000000000 + 1.17187499999988647970e-01, // 0x3FBDFFFFFFFFFCCE + 1.32394806593073575129e+01, // 0x402A7A9D357F7FCE + 4.12051854307378562225e+02, // 0x4079C0D4652EA590 + 3.87474538913960532227e+03, // 0x40AE457DA3A532CC + 7.91447954031891731574e+03, // 0x40BEEA7AC32782DD +} + +var p1S8 = [5]float64{ + 1.14207370375678408436e+02, // 0x405C8D458E656CAC + 3.65093083420853463394e+03, // 0x40AC85DC964D274F + 3.69562060269033463555e+04, // 0x40E20B8697C5BB7F + 9.76027935934950801311e+04, // 0x40F7D42CB28F17BB + 3.08042720627888811578e+04, // 0x40DE1511697A0B2D +} + +// for x in [8,4.5454] = 1/[0.125,0.22001] +var p1R5 = [6]float64{ + 1.31990519556243522749e-11, // 0x3DAD0667DAE1CA7D + 1.17187493190614097638e-01, // 0x3FBDFFFFE2C10043 + 6.80275127868432871736e+00, // 0x401B36046E6315E3 + 1.08308182990189109773e+02, // 0x405B13B9452602ED + 5.17636139533199752805e+02, // 0x40802D16D052D649 + 5.28715201363337541807e+02, // 0x408085B8BB7E0CB7 +} + +var p1S5 = [5]float64{ + 5.92805987221131331921e+01, // 0x404DA3EAA8AF633D + 9.91401418733614377743e+02, // 0x408EFB361B066701 + 5.35326695291487976647e+03, // 0x40B4E9445706B6FB + 7.84469031749551231769e+03, // 0x40BEA4B0B8A5BB15 + 1.50404688810361062679e+03, // 0x40978030036F5E51 +} + +// for x in[4.5453,2.8571] = 1/[0.2199,0.35001] +var p1R3 = [6]float64{ + 3.02503916137373618024e-09, // 0x3E29FC21A7AD9EDD + 1.17186865567253592491e-01, // 0x3FBDFFF55B21D17B + 3.93297750033315640650e+00, // 0x400F76BCE85EAD8A + 3.51194035591636932736e+01, // 0x40418F489DA6D129 + 9.10550110750781271918e+01, // 0x4056C3854D2C1837 + 4.85590685197364919645e+01, // 0x4048478F8EA83EE5 +} + +var p1S3 = [5]float64{ + 3.47913095001251519989e+01, // 0x40416549A134069C + 3.36762458747825746741e+02, // 0x40750C3307F1A75F + 1.04687139975775130551e+03, // 0x40905B7C5037D523 + 8.90811346398256432622e+02, // 0x408BD67DA32E31E9 + 1.03787932439639277504e+02, // 0x4059F26D7C2EED53 +} + +// for x in [2.8570,2] = 1/[0.3499,0.5] +var p1R2 = [6]float64{ + 1.07710830106873743082e-07, // 0x3E7CE9D4F65544F4 + 1.17176219462683348094e-01, // 0x3FBDFF42BE760D83 + 2.36851496667608785174e+00, // 0x4002F2B7F98FAEC0 + 1.22426109148261232917e+01, // 0x40287C377F71A964 + 1.76939711271687727390e+01, // 0x4031B1A8177F8EE2 + 5.07352312588818499250e+00, // 0x40144B49A574C1FE +} + +var p1S2 = [5]float64{ + 2.14364859363821409488e+01, // 0x40356FBD8AD5ECDC + 1.25290227168402751090e+02, // 0x405F529314F92CD5 + 2.32276469057162813669e+02, // 0x406D08D8D5A2DBD9 + 1.17679373287147100768e+02, // 0x405D6B7ADA1884A9 + 8.36463893371618283368e+00, // 0x4020BAB1F44E5192 +} + +func pone(x float64) float64 { + var p *[6]float64 + var q *[5]float64 + if x >= 8 { + p = &p1R8 + q = &p1S8 + } else if x >= 4.5454 { + p = &p1R5 + q = &p1S5 + } else if x >= 2.8571 { + p = &p1R3 + q = &p1S3 + } else if x >= 2 { + p = &p1R2 + q = &p1S2 + } + z := 1 / (x * x) + r := p[0] + z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))) + s := 1.0 + z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4])))) + return 1 + r/s +} + +// For x >= 8, the asymptotic expansions of qone is +// 3/8 s - 105/1024 s**3 - ..., where s = 1/x. +// We approximate qone by +// qone(x) = s*(0.375 + (R/S)) +// where R = qr1*s**2 + qr2*s**4 + ... + qr5*s**10 +// S = 1 + qs1*s**2 + ... + qs6*s**12 +// and +// | qone(x)/s -0.375-R/S | <= 2**(-61.13) + +// for x in [inf, 8] = 1/[0,0.125] +var q1R8 = [6]float64{ + 0.00000000000000000000e+00, // 0x0000000000000000 + -1.02539062499992714161e-01, // 0xBFBA3FFFFFFFFDF3 + -1.62717534544589987888e+01, // 0xC0304591A26779F7 + -7.59601722513950107896e+02, // 0xC087BCD053E4B576 + -1.18498066702429587167e+04, // 0xC0C724E740F87415 + -4.84385124285750353010e+04, // 0xC0E7A6D065D09C6A +} + +var q1S8 = [6]float64{ + 1.61395369700722909556e+02, // 0x40642CA6DE5BCDE5 + 7.82538599923348465381e+03, // 0x40BE9162D0D88419 + 1.33875336287249578163e+05, // 0x4100579AB0B75E98 + 7.19657723683240939863e+05, // 0x4125F65372869C19 + 6.66601232617776375264e+05, // 0x412457D27719AD5C + -2.94490264303834643215e+05, // 0xC111F9690EA5AA18 +} + +// for x in [8,4.5454] = 1/[0.125,0.22001] +var q1R5 = [6]float64{ + -2.08979931141764104297e-11, // 0xBDB6FA431AA1A098 + -1.02539050241375426231e-01, // 0xBFBA3FFFCB597FEF + -8.05644828123936029840e+00, // 0xC0201CE6CA03AD4B + -1.83669607474888380239e+02, // 0xC066F56D6CA7B9B0 + -1.37319376065508163265e+03, // 0xC09574C66931734F + -2.61244440453215656817e+03, // 0xC0A468E388FDA79D +} + +var q1S5 = [6]float64{ + 8.12765501384335777857e+01, // 0x405451B2FF5A11B2 + 1.99179873460485964642e+03, // 0x409F1F31E77BF839 + 1.74684851924908907677e+04, // 0x40D10F1F0D64CE29 + 4.98514270910352279316e+04, // 0x40E8576DAABAD197 + 2.79480751638918118260e+04, // 0x40DB4B04CF7C364B + -4.71918354795128470869e+03, // 0xC0B26F2EFCFFA004 +} + +// for x in [4.5454,2.8571] = 1/[0.2199,0.35001] ??? +var q1R3 = [6]float64{ + -5.07831226461766561369e-09, // 0xBE35CFA9D38FC84F + -1.02537829820837089745e-01, // 0xBFBA3FEB51AEED54 + -4.61011581139473403113e+00, // 0xC01270C23302D9FF + -5.78472216562783643212e+01, // 0xC04CEC71C25D16DA + -2.28244540737631695038e+02, // 0xC06C87D34718D55F + -2.19210128478909325622e+02, // 0xC06B66B95F5C1BF6 +} + +var q1S3 = [6]float64{ + 4.76651550323729509273e+01, // 0x4047D523CCD367E4 + 6.73865112676699709482e+02, // 0x40850EEBC031EE3E + 3.38015286679526343505e+03, // 0x40AA684E448E7C9A + 5.54772909720722782367e+03, // 0x40B5ABBAA61D54A6 + 1.90311919338810798763e+03, // 0x409DBC7A0DD4DF4B + -1.35201191444307340817e+02, // 0xC060E670290A311F +} + +// for x in [2.8570,2] = 1/[0.3499,0.5] +var q1R2 = [6]float64{ + -1.78381727510958865572e-07, // 0xBE87F12644C626D2 + -1.02517042607985553460e-01, // 0xBFBA3E8E9148B010 + -2.75220568278187460720e+00, // 0xC006048469BB4EDA + -1.96636162643703720221e+01, // 0xC033A9E2C168907F + -4.23253133372830490089e+01, // 0xC04529A3DE104AAA + -2.13719211703704061733e+01, // 0xC0355F3639CF6E52 +} + +var q1S2 = [6]float64{ + 2.95333629060523854548e+01, // 0x403D888A78AE64FF + 2.52981549982190529136e+02, // 0x406F9F68DB821CBA + 7.57502834868645436472e+02, // 0x4087AC05CE49A0F7 + 7.39393205320467245656e+02, // 0x40871B2548D4C029 + 1.55949003336666123687e+02, // 0x40637E5E3C3ED8D4 + -4.95949898822628210127e+00, // 0xC013D686E71BE86B +} + +func qone(x float64) float64 { + var p, q *[6]float64 + if x >= 8 { + p = &q1R8 + q = &q1S8 + } else if x >= 4.5454 { + p = &q1R5 + q = &q1S5 + } else if x >= 2.8571 { + p = &q1R3 + q = &q1S3 + } else if x >= 2 { + p = &q1R2 + q = &q1S2 + } + z := 1 / (x * x) + r := p[0] + z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))) + s := 1 + z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))) + return (0.375 + r/s) / x +} diff --git a/gnovm/stdlibs/math/jn.gno b/gnovm/stdlibs/math/jn.gno new file mode 100644 index 00000000000..3491692a96c --- /dev/null +++ b/gnovm/stdlibs/math/jn.gno @@ -0,0 +1,306 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Bessel function of the first and second kinds of order n. +*/ + +// The original C code and the long comment below are +// from FreeBSD's /usr/src/lib/msun/src/e_jn.c and +// came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_jn(n, x), __ieee754_yn(n, x) +// floating point Bessel's function of the 1st and 2nd kind +// of order n +// +// Special cases: +// y0(0)=y1(0)=yn(n,0) = -inf with division by zero signal; +// y0(-ve)=y1(-ve)=yn(n,-ve) are NaN with invalid signal. +// Note 2. About jn(n,x), yn(n,x) +// For n=0, j0(x) is called, +// for n=1, j1(x) is called, +// for nx, a continued fraction approximation to +// j(n,x)/j(n-1,x) is evaluated and then backward +// recursion is used starting from a supposed value +// for j(n,x). The resulting value of j(0,x) is +// compared with the actual value to correct the +// supposed value of j(n,x). +// +// yn(n,x) is similar in all respects, except +// that forward recursion is used for all +// values of n>1. + +// Jn returns the order-n Bessel function of the first kind. +// +// Special cases are: +// +// Jn(n, ±Inf) = 0 +// Jn(n, NaN) = NaN +func Jn(n int, x float64) float64 { + const ( + TwoM29 = 1.0 / (1 << 29) // 2**-29 0x3e10000000000000 + Two302 = 1 << 302 // 2**302 0x52D0000000000000 + ) + // special cases + switch { + case IsNaN(x): + return x + case IsInf(x, 0): + return 0 + } + // J(-n, x) = (-1)**n * J(n, x), J(n, -x) = (-1)**n * J(n, x) + // Thus, J(-n, x) = J(n, -x) + + if n == 0 { + return J0(x) + } + if x == 0 { + return 0 + } + if n < 0 { + n, x = -n, -x + } + if n == 1 { + return J1(x) + } + sign := false + if x < 0 { + x = -x + if n&1 == 1 { + sign = true // odd n and negative x + } + } + var b float64 + if float64(n) <= x { + // Safe to use J(n+1,x)=2n/x *J(n,x)-J(n-1,x) + if x >= Two302 { // x > 2**302 + + // (x >> n**2) + // Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi) + // Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi) + // Let s=sin(x), c=cos(x), + // xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then + // + // n sin(xn)*sqt2 cos(xn)*sqt2 + // ---------------------------------- + // 0 s-c c+s + // 1 -s-c -c+s + // 2 -s+c -c-s + // 3 s+c c-s + + var temp float64 + switch s, c := Sincos(x); n & 3 { + case 0: + temp = c + s + case 1: + temp = -c + s + case 2: + temp = -c - s + case 3: + temp = c - s + } + b = (1 / SqrtPi) * temp / Sqrt(x) + } else { + b = J1(x) + for i, a := 1, J0(x); i < n; i++ { + a, b = b, b*(float64(i+i)/x)-a // avoid underflow + } + } + } else { + if x < TwoM29 { // x < 2**-29 + // x is tiny, return the first Taylor expansion of J(n,x) + // J(n,x) = 1/n!*(x/2)**n - ... + + if n > 33 { // underflow + b = 0 + } else { + temp := x * 0.5 + b = temp + a := 1.0 + for i := 2; i <= n; i++ { + a *= float64(i) // a = n! + b *= temp // b = (x/2)**n + } + b /= a + } + } else { + // use backward recurrence + // x x**2 x**2 + // J(n,x)/J(n-1,x) = ---- ------ ------ ..... + // 2n - 2(n+1) - 2(n+2) + // + // 1 1 1 + // (for large x) = ---- ------ ------ ..... + // 2n 2(n+1) 2(n+2) + // -- - ------ - ------ - + // x x x + // + // Let w = 2n/x and h=2/x, then the above quotient + // is equal to the continued fraction: + // 1 + // = ----------------------- + // 1 + // w - ----------------- + // 1 + // w+h - --------- + // w+2h - ... + // + // To determine how many terms needed, let + // Q(0) = w, Q(1) = w(w+h) - 1, + // Q(k) = (w+k*h)*Q(k-1) - Q(k-2), + // When Q(k) > 1e4 good for single + // When Q(k) > 1e9 good for double + // When Q(k) > 1e17 good for quadruple + + // determine k + w := float64(n+n) / x + h := 2 / x + q0 := w + z := w + h + q1 := w*z - 1 + k := 1 + for q1 < 1e9 { + k++ + z += h + q0, q1 = q1, z*q1-q0 + } + m := n + n + t := 0.0 + for i := 2 * (n + k); i >= m; i -= 2 { + t = 1 / (float64(i)/x - t) + } + a := t + b = 1 + // estimate log((2/x)**n*n!) = n*log(2/x)+n*ln(n) + // Hence, if n*(log(2n/x)) > ... + // single 8.8722839355e+01 + // double 7.09782712893383973096e+02 + // long double 1.1356523406294143949491931077970765006170e+04 + // then recurrent value may overflow and the result is + // likely underflow to zero + + tmp := float64(n) + v := 2 / x + tmp = tmp * Log(Abs(v*tmp)) + if tmp < 7.09782712893383973096e+02 { + for i := n - 1; i > 0; i-- { + di := float64(i + i) + a, b = b, b*di/x-a + } + } else { + for i := n - 1; i > 0; i-- { + di := float64(i + i) + a, b = b, b*di/x-a + // scale b to avoid spurious overflow + if b > 1e100 { + a /= b + t /= b + b = 1 + } + } + } + b = t * J0(x) / b + } + } + if sign { + return -b + } + return b +} + +// Yn returns the order-n Bessel function of the second kind. +// +// Special cases are: +// +// Yn(n, +Inf) = 0 +// Yn(n ≥ 0, 0) = -Inf +// Yn(n < 0, 0) = +Inf if n is odd, -Inf if n is even +// Yn(n, x < 0) = NaN +// Yn(n, NaN) = NaN +func Yn(n int, x float64) float64 { + const Two302 = 1 << 302 // 2**302 0x52D0000000000000 + // special cases + switch { + case x < 0 || IsNaN(x): + return NaN() + case IsInf(x, 1): + return 0 + } + + if n == 0 { + return Y0(x) + } + if x == 0 { + if n < 0 && n&1 == 1 { + return Inf(1) + } + return Inf(-1) + } + sign := false + if n < 0 { + n = -n + if n&1 == 1 { + sign = true // sign true if n < 0 && |n| odd + } + } + if n == 1 { + if sign { + return -Y1(x) + } + return Y1(x) + } + var b float64 + if x >= Two302 { // x > 2**302 + // (x >> n**2) + // Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi) + // Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi) + // Let s=sin(x), c=cos(x), + // xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then + // + // n sin(xn)*sqt2 cos(xn)*sqt2 + // ---------------------------------- + // 0 s-c c+s + // 1 -s-c -c+s + // 2 -s+c -c-s + // 3 s+c c-s + + var temp float64 + switch s, c := Sincos(x); n & 3 { + case 0: + temp = s - c + case 1: + temp = -s - c + case 2: + temp = -s + c + case 3: + temp = s + c + } + b = (1 / SqrtPi) * temp / Sqrt(x) + } else { + a := Y0(x) + b = Y1(x) + // quit if b is -inf + for i := 1; i < n && !IsInf(b, -1); i++ { + a, b = b, (float64(i+i)/x)*b-a + } + } + if sign { + return -b + } + return b +} diff --git a/gnovm/stdlibs/math/ldexp.gno b/gnovm/stdlibs/math/ldexp.gno index 5af0cff2c3b..d82114d5181 100644 --- a/gnovm/stdlibs/math/ldexp.gno +++ b/gnovm/stdlibs/math/ldexp.gno @@ -4,10 +4,6 @@ package math -import ( - imath "internal/math" -) - // Ldexp is the inverse of Frexp. // It returns frac × 2**exp. // @@ -17,11 +13,6 @@ import ( // Ldexp(±Inf, exp) = ±Inf // Ldexp(NaN, exp) = NaN func Ldexp(frac float64, exp int) float64 { - /* XXX - if haveArchLdexp { - return archLdexp(frac, exp) - } - */ return ldexp(frac, exp) } @@ -35,7 +26,7 @@ func ldexp(frac float64, exp int) float64 { } frac, e := normalize(frac) exp += e - x := imath.Float64bits(frac) + x := Float64bits(frac) exp += int(x>>shift)&mask - bias if exp < -1075 { return Copysign(0, frac) // underflow @@ -53,5 +44,5 @@ func ldexp(frac float64, exp int) float64 { } x &^= mask << shift x |= uint64(exp+bias) << shift - return m * imath.Float64frombits(x) + return m * Float64frombits(x) } diff --git a/gnovm/stdlibs/math/lgamma.gno b/gnovm/stdlibs/math/lgamma.gno new file mode 100644 index 00000000000..1fa22f9d5d0 --- /dev/null +++ b/gnovm/stdlibs/math/lgamma.gno @@ -0,0 +1,372 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point logarithm of the Gamma function. +*/ + +// The original C code and the long comment below are +// from FreeBSD's /usr/src/lib/msun/src/e_lgamma_r.c and +// came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_lgamma_r(x, signgamp) +// Reentrant version of the logarithm of the Gamma function +// with user provided pointer for the sign of Gamma(x). +// +// Method: +// 1. Argument Reduction for 0 < x <= 8 +// Since gamma(1+s)=s*gamma(s), for x in [0,8], we may +// reduce x to a number in [1.5,2.5] by +// lgamma(1+s) = log(s) + lgamma(s) +// for example, +// lgamma(7.3) = log(6.3) + lgamma(6.3) +// = log(6.3*5.3) + lgamma(5.3) +// = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3) +// 2. Polynomial approximation of lgamma around its +// minimum (ymin=1.461632144968362245) to maintain monotonicity. +// On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use +// Let z = x-ymin; +// lgamma(x) = -1.214862905358496078218 + z**2*poly(z) +// poly(z) is a 14 degree polynomial. +// 2. Rational approximation in the primary interval [2,3] +// We use the following approximation: +// s = x-2.0; +// lgamma(x) = 0.5*s + s*P(s)/Q(s) +// with accuracy +// |P/Q - (lgamma(x)-0.5s)| < 2**-61.71 +// Our algorithms are based on the following observation +// +// zeta(2)-1 2 zeta(3)-1 3 +// lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ... +// 2 3 +// +// where Euler = 0.5772156649... is the Euler constant, which +// is very close to 0.5. +// +// 3. For x>=8, we have +// lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+.... +// (better formula: +// lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...) +// Let z = 1/x, then we approximation +// f(z) = lgamma(x) - (x-0.5)(log(x)-1) +// by +// 3 5 11 +// w = w0 + w1*z + w2*z + w3*z + ... + w6*z +// where +// |w - f(z)| < 2**-58.74 +// +// 4. For negative x, since (G is gamma function) +// -x*G(-x)*G(x) = pi/sin(pi*x), +// we have +// G(x) = pi/(sin(pi*x)*(-x)*G(-x)) +// since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0 +// Hence, for x<0, signgam = sign(sin(pi*x)) and +// lgamma(x) = log(|Gamma(x)|) +// = log(pi/(|x*sin(pi*x)|)) - lgamma(-x); +// Note: one should avoid computing pi*(-x) directly in the +// computation of sin(pi*(-x)). +// +// 5. Special Cases +// lgamma(2+s) ~ s*(1-Euler) for tiny s +// lgamma(1)=lgamma(2)=0 +// lgamma(x) ~ -log(x) for tiny x +// lgamma(0) = lgamma(inf) = inf +// lgamma(-integer) = +-inf +// +// + +var _lgamA = [...]float64{ + 7.72156649015328655494e-02, // 0x3FB3C467E37DB0C8 + 3.22467033424113591611e-01, // 0x3FD4A34CC4A60FAD + 6.73523010531292681824e-02, // 0x3FB13E001A5562A7 + 2.05808084325167332806e-02, // 0x3F951322AC92547B + 7.38555086081402883957e-03, // 0x3F7E404FB68FEFE8 + 2.89051383673415629091e-03, // 0x3F67ADD8CCB7926B + 1.19270763183362067845e-03, // 0x3F538A94116F3F5D + 5.10069792153511336608e-04, // 0x3F40B6C689B99C00 + 2.20862790713908385557e-04, // 0x3F2CF2ECED10E54D + 1.08011567247583939954e-04, // 0x3F1C5088987DFB07 + 2.52144565451257326939e-05, // 0x3EFA7074428CFA52 + 4.48640949618915160150e-05, // 0x3F07858E90A45837 +} + +var _lgamR = [...]float64{ + 1.0, // placeholder + 1.39200533467621045958e+00, // 0x3FF645A762C4AB74 + 7.21935547567138069525e-01, // 0x3FE71A1893D3DCDC + 1.71933865632803078993e-01, // 0x3FC601EDCCFBDF27 + 1.86459191715652901344e-02, // 0x3F9317EA742ED475 + 7.77942496381893596434e-04, // 0x3F497DDACA41A95B + 7.32668430744625636189e-06, // 0x3EDEBAF7A5B38140 +} + +var _lgamS = [...]float64{ + -7.72156649015328655494e-02, // 0xBFB3C467E37DB0C8 + 2.14982415960608852501e-01, // 0x3FCB848B36E20878 + 3.25778796408930981787e-01, // 0x3FD4D98F4F139F59 + 1.46350472652464452805e-01, // 0x3FC2BB9CBEE5F2F7 + 2.66422703033638609560e-02, // 0x3F9B481C7E939961 + 1.84028451407337715652e-03, // 0x3F5E26B67368F239 + 3.19475326584100867617e-05, // 0x3F00BFECDD17E945 +} + +var _lgamT = [...]float64{ + 4.83836122723810047042e-01, // 0x3FDEF72BC8EE38A2 + -1.47587722994593911752e-01, // 0xBFC2E4278DC6C509 + 6.46249402391333854778e-02, // 0x3FB08B4294D5419B + -3.27885410759859649565e-02, // 0xBFA0C9A8DF35B713 + 1.79706750811820387126e-02, // 0x3F9266E7970AF9EC + -1.03142241298341437450e-02, // 0xBF851F9FBA91EC6A + 6.10053870246291332635e-03, // 0x3F78FCE0E370E344 + -3.68452016781138256760e-03, // 0xBF6E2EFFB3E914D7 + 2.25964780900612472250e-03, // 0x3F6282D32E15C915 + -1.40346469989232843813e-03, // 0xBF56FE8EBF2D1AF1 + 8.81081882437654011382e-04, // 0x3F4CDF0CEF61A8E9 + -5.38595305356740546715e-04, // 0xBF41A6109C73E0EC + 3.15632070903625950361e-04, // 0x3F34AF6D6C0EBBF7 + -3.12754168375120860518e-04, // 0xBF347F24ECC38C38 + 3.35529192635519073543e-04, // 0x3F35FD3EE8C2D3F4 +} + +var _lgamU = [...]float64{ + -7.72156649015328655494e-02, // 0xBFB3C467E37DB0C8 + 6.32827064025093366517e-01, // 0x3FE4401E8B005DFF + 1.45492250137234768737e+00, // 0x3FF7475CD119BD6F + 9.77717527963372745603e-01, // 0x3FEF497644EA8450 + 2.28963728064692451092e-01, // 0x3FCD4EAEF6010924 + 1.33810918536787660377e-02, // 0x3F8B678BBF2BAB09 +} + +var _lgamV = [...]float64{ + 1.0, + 2.45597793713041134822e+00, // 0x4003A5D7C2BD619C + 2.12848976379893395361e+00, // 0x40010725A42B18F5 + 7.69285150456672783825e-01, // 0x3FE89DFBE45050AF + 1.04222645593369134254e-01, // 0x3FBAAE55D6537C88 + 3.21709242282423911810e-03, // 0x3F6A5ABB57D0CF61 +} + +var _lgamW = [...]float64{ + 4.18938533204672725052e-01, // 0x3FDACFE390C97D69 + 8.33333333333329678849e-02, // 0x3FB555555555553B + -2.77777777728775536470e-03, // 0xBF66C16C16B02E5C + 7.93650558643019558500e-04, // 0x3F4A019F98CF38B6 + -5.95187557450339963135e-04, // 0xBF4380CB8C0FE741 + 8.36339918996282139126e-04, // 0x3F4B67BA4CDAD5D1 + -1.63092934096575273989e-03, // 0xBF5AB89D0B9E43E4 +} + +// Lgamma returns the natural logarithm and sign (-1 or +1) of Gamma(x). +// +// Special cases are: +// +// Lgamma(+Inf) = +Inf +// Lgamma(0) = +Inf +// Lgamma(-integer) = +Inf +// Lgamma(-Inf) = -Inf +// Lgamma(NaN) = NaN +func Lgamma(x float64) (lgamma float64, sign int) { + const ( + Ymin = 1.461632144968362245 + Two52 = 1 << 52 // 0x4330000000000000 ~4.5036e+15 + Two53 = 1 << 53 // 0x4340000000000000 ~9.0072e+15 + Two58 = 1 << 58 // 0x4390000000000000 ~2.8823e+17 + Tiny = 1.0 / (1 << 70) // 0x3b90000000000000 ~8.47033e-22 + Tc = 1.46163214496836224576e+00 // 0x3FF762D86356BE3F + Tf = -1.21486290535849611461e-01 // 0xBFBF19B9BCC38A42 + // Tt = -(tail of Tf) + Tt = -3.63867699703950536541e-18 // 0xBC50C7CAA48A971F + ) + // special cases + sign = 1 + switch { + case IsNaN(x): + lgamma = x + return + case IsInf(x, 0): + lgamma = x + return + case x == 0: + lgamma = Inf(1) + return + } + + neg := false + if x < 0 { + x = -x + neg = true + } + + if x < Tiny { // if |x| < 2**-70, return -log(|x|) + if neg { + sign = -1 + } + lgamma = -Log(x) + return + } + var nadj float64 + if neg { + if x >= Two52 { // |x| >= 2**52, must be -integer + lgamma = Inf(1) + return + } + t := sinPi(x) + if t == 0 { + lgamma = Inf(1) // -integer + return + } + nadj = Log(Pi / Abs(t*x)) + if t < 0 { + sign = -1 + } + } + + switch { + case x == 1 || x == 2: // purge off 1 and 2 + lgamma = 0 + return + case x < 2: // use lgamma(x) = lgamma(x+1) - log(x) + var y float64 + var i int + if x <= 0.9 { + lgamma = -Log(x) + switch { + case x >= (Ymin - 1 + 0.27): // 0.7316 <= x <= 0.9 + y = 1 - x + i = 0 + case x >= (Ymin - 1 - 0.27): // 0.2316 <= x < 0.7316 + y = x - (Tc - 1) + i = 1 + default: // 0 < x < 0.2316 + y = x + i = 2 + } + } else { + lgamma = 0 + switch { + case x >= (Ymin + 0.27): // 1.7316 <= x < 2 + y = 2 - x + i = 0 + case x >= (Ymin - 0.27): // 1.2316 <= x < 1.7316 + y = x - Tc + i = 1 + default: // 0.9 < x < 1.2316 + y = x - 1 + i = 2 + } + } + switch i { + case 0: + z := y * y + p1 := _lgamA[0] + z*(_lgamA[2]+z*(_lgamA[4]+z*(_lgamA[6]+z*(_lgamA[8]+z*_lgamA[10])))) + p2 := z * (_lgamA[1] + z*(+_lgamA[3]+z*(_lgamA[5]+z*(_lgamA[7]+z*(_lgamA[9]+z*_lgamA[11]))))) + p := y*p1 + p2 + lgamma += (p - 0.5*y) + case 1: + z := y * y + w := z * y + p1 := _lgamT[0] + w*(_lgamT[3]+w*(_lgamT[6]+w*(_lgamT[9]+w*_lgamT[12]))) // parallel comp + p2 := _lgamT[1] + w*(_lgamT[4]+w*(_lgamT[7]+w*(_lgamT[10]+w*_lgamT[13]))) + p3 := _lgamT[2] + w*(_lgamT[5]+w*(_lgamT[8]+w*(_lgamT[11]+w*_lgamT[14]))) + p := z*p1 - (Tt - w*(p2+y*p3)) + lgamma += (Tf + p) + case 2: + p1 := y * (_lgamU[0] + y*(_lgamU[1]+y*(_lgamU[2]+y*(_lgamU[3]+y*(_lgamU[4]+y*_lgamU[5]))))) + p2 := 1 + y*(_lgamV[1]+y*(_lgamV[2]+y*(_lgamV[3]+y*(_lgamV[4]+y*_lgamV[5])))) + lgamma += (-0.5*y + p1/p2) + } + case x < 8: // 2 <= x < 8 + i := int(x) + y := x - float64(i) + p := y * (_lgamS[0] + y*(_lgamS[1]+y*(_lgamS[2]+y*(_lgamS[3]+y*(_lgamS[4]+y*(_lgamS[5]+y*_lgamS[6])))))) + q := 1 + y*(_lgamR[1]+y*(_lgamR[2]+y*(_lgamR[3]+y*(_lgamR[4]+y*(_lgamR[5]+y*_lgamR[6]))))) + lgamma = 0.5*y + p/q + z := 1.0 // Lgamma(1+s) = Log(s) + Lgamma(s) + switch i { + case 7: + z *= (y + 6) + fallthrough + case 6: + z *= (y + 5) + fallthrough + case 5: + z *= (y + 4) + fallthrough + case 4: + z *= (y + 3) + fallthrough + case 3: + z *= (y + 2) + lgamma += Log(z) + } + case x < Two58: // 8 <= x < 2**58 + t := Log(x) + z := 1 / x + y := z * z + w := _lgamW[0] + z*(_lgamW[1]+y*(_lgamW[2]+y*(_lgamW[3]+y*(_lgamW[4]+y*(_lgamW[5]+y*_lgamW[6]))))) + lgamma = (x-0.5)*(t-1) + w + default: // 2**58 <= x <= Inf + lgamma = x * (Log(x) - 1) + } + if neg { + lgamma = nadj - lgamma + } + return +} + +// sinPi(x) is a helper function for negative x +func sinPi(x float64) float64 { + const ( + Two52 = 1 << 52 // 0x4330000000000000 ~4.5036e+15 + Two53 = 1 << 53 // 0x4340000000000000 ~9.0072e+15 + ) + if x < 0.25 { + return -Sin(Pi * x) + } + + // argument reduction + z := Floor(x) + var n int + if z != x { // inexact + x = Mod(x, 2) + n = int(x * 4) + } else { + if x >= Two53 { // x must be even + x = 0 + n = 0 + } else { + if x < Two52 { + z = x + Two52 // exact + } + n = int(1 & Float64bits(z)) + x = float64(n) + n <<= 2 + } + } + switch n { + case 0: + x = Sin(Pi * x) + case 1, 2: + x = Cos(Pi * (0.5 - x)) + case 3, 4: + x = Sin(Pi * (1 - x)) + case 5, 6: + x = -Cos(Pi * (x - 1.5)) + default: + x = Sin(Pi * (x - 2)) + } + return -x +} diff --git a/gnovm/stdlibs/math/log.gno b/gnovm/stdlibs/math/log.gno new file mode 100644 index 00000000000..5bcb1600ff9 --- /dev/null +++ b/gnovm/stdlibs/math/log.gno @@ -0,0 +1,126 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point logarithm. +*/ + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/e_log.c +// and came with this notice. The go code is a simpler +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_log(x) +// Return the logarithm of x +// +// Method : +// 1. Argument Reduction: find k and f such that +// x = 2**k * (1+f), +// where sqrt(2)/2 < 1+f < sqrt(2) . +// +// 2. Approximation of log(1+f). +// Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) +// = 2s + 2/3 s**3 + 2/5 s**5 + ....., +// = 2s + s*R +// We use a special Reme algorithm on [0,0.1716] to generate +// a polynomial of degree 14 to approximate R. The maximum error +// of this polynomial approximation is bounded by 2**-58.45. In +// other words, +// 2 4 6 8 10 12 14 +// R(z) ~ L1*s +L2*s +L3*s +L4*s +L5*s +L6*s +L7*s +// (the values of L1 to L7 are listed in the program) and +// | 2 14 | -58.45 +// | L1*s +...+L7*s - R(z) | <= 2 +// | | +// Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. +// In order to guarantee error in log below 1ulp, we compute log by +// log(1+f) = f - s*(f - R) (if f is not too large) +// log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) +// +// 3. Finally, log(x) = k*Ln2 + log(1+f). +// = k*Ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*Ln2_lo))) +// Here Ln2 is split into two floating point number: +// Ln2_hi + Ln2_lo, +// where n*Ln2_hi is always exact for |n| < 2000. +// +// Special cases: +// log(x) is NaN with signal if x < 0 (including -INF) ; +// log(+INF) is +INF; log(0) is -INF with signal; +// log(NaN) is that NaN with no signal. +// +// Accuracy: +// according to an error analysis, the error is always less than +// 1 ulp (unit in the last place). +// +// Constants: +// The hexadecimal values are the intended ones for the following +// constants. The decimal values may be used, provided that the +// compiler will convert from decimal to binary accurately enough +// to produce the hexadecimal values shown. + +// Log returns the natural logarithm of x. +// +// Special cases are: +// +// Log(+Inf) = +Inf +// Log(0) = -Inf +// Log(x < 0) = NaN +// Log(NaN) = NaN +func Log(x float64) float64 { + return log(x) +} + +func log(x float64) float64 { + const ( + Ln2Hi = 6.93147180369123816490e-01 /* 3fe62e42 fee00000 */ + Ln2Lo = 1.90821492927058770002e-10 /* 3dea39ef 35793c76 */ + L1 = 6.666666666666735130e-01 /* 3FE55555 55555593 */ + L2 = 3.999999999940941908e-01 /* 3FD99999 9997FA04 */ + L3 = 2.857142874366239149e-01 /* 3FD24924 94229359 */ + L4 = 2.222219843214978396e-01 /* 3FCC71C5 1D8E78AF */ + L5 = 1.818357216161805012e-01 /* 3FC74664 96CB03DE */ + L6 = 1.531383769920937332e-01 /* 3FC39A09 D078C69F */ + L7 = 1.479819860511658591e-01 /* 3FC2F112 DF3E5244 */ + ) + + // special cases + switch { + case IsNaN(x) || IsInf(x, 1): + return x + case x < 0: + return NaN() + case x == 0: + return Inf(-1) + } + + // reduce + f1, ki := Frexp(x) + if f1 < Sqrt2/2 { + f1 *= 2 + ki-- + } + f := f1 - 1 + k := float64(ki) + + // compute + s := f / (2 + f) + s2 := s * s + s4 := s2 * s2 + t1 := s2 * (L1 + s4*(L3+s4*(L5+s4*L7))) + t2 := s4 * (L2 + s4*(L4+s4*L6)) + R := t1 + t2 + hfsq := 0.5 * f * f + return k*Ln2Hi - ((hfsq - (s*(hfsq+R) + k*Ln2Lo)) - f) +} diff --git a/gnovm/stdlibs/math/log10.gno b/gnovm/stdlibs/math/log10.gno new file mode 100644 index 00000000000..7c7d6cf5d3c --- /dev/null +++ b/gnovm/stdlibs/math/log10.gno @@ -0,0 +1,31 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Log10 returns the decimal logarithm of x. +// The special cases are the same as for Log. +func Log10(x float64) float64 { + return log10(x) +} + +func log10(x float64) float64 { + return Log(x) * (1 / Ln10) +} + +// Log2 returns the binary logarithm of x. +// The special cases are the same as for Log. +func Log2(x float64) float64 { + return log2(x) +} + +func log2(x float64) float64 { + frac, exp := Frexp(x) + // Make sure exact powers of two give an exact answer. + // Don't depend on Log(0.5)*(1/Ln2)+exp being exactly exp-1. + if frac == 0.5 { + return float64(exp - 1) + } + return Log(frac)*(1/Ln2) + float64(exp) +} diff --git a/gnovm/stdlibs/math/log1p.gno b/gnovm/stdlibs/math/log1p.gno new file mode 100644 index 00000000000..819bf123504 --- /dev/null +++ b/gnovm/stdlibs/math/log1p.gno @@ -0,0 +1,200 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/s_log1p.c +// and came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// +// double log1p(double x) +// +// Method : +// 1. Argument Reduction: find k and f such that +// 1+x = 2**k * (1+f), +// where sqrt(2)/2 < 1+f < sqrt(2) . +// +// Note. If k=0, then f=x is exact. However, if k!=0, then f +// may not be representable exactly. In that case, a correction +// term is need. Let u=1+x rounded. Let c = (1+x)-u, then +// log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u), +// and add back the correction term c/u. +// (Note: when x > 2**53, one can simply return log(x)) +// +// 2. Approximation of log1p(f). +// Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) +// = 2s + 2/3 s**3 + 2/5 s**5 + ....., +// = 2s + s*R +// We use a special Reme algorithm on [0,0.1716] to generate +// a polynomial of degree 14 to approximate R The maximum error +// of this polynomial approximation is bounded by 2**-58.45. In +// other words, +// 2 4 6 8 10 12 14 +// R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s +Lp6*s +Lp7*s +// (the values of Lp1 to Lp7 are listed in the program) +// and +// | 2 14 | -58.45 +// | Lp1*s +...+Lp7*s - R(z) | <= 2 +// | | +// Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. +// In order to guarantee error in log below 1ulp, we compute log +// by +// log1p(f) = f - (hfsq - s*(hfsq+R)). +// +// 3. Finally, log1p(x) = k*ln2 + log1p(f). +// = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) +// Here ln2 is split into two floating point number: +// ln2_hi + ln2_lo, +// where n*ln2_hi is always exact for |n| < 2000. +// +// Special cases: +// log1p(x) is NaN with signal if x < -1 (including -INF) ; +// log1p(+INF) is +INF; log1p(-1) is -INF with signal; +// log1p(NaN) is that NaN with no signal. +// +// Accuracy: +// according to an error analysis, the error is always less than +// 1 ulp (unit in the last place). +// +// Constants: +// The hexadecimal values are the intended ones for the following +// constants. The decimal values may be used, provided that the +// compiler will convert from decimal to binary accurately enough +// to produce the hexadecimal values shown. +// +// Note: Assuming log() return accurate answer, the following +// algorithm can be used to compute log1p(x) to within a few ULP: +// +// u = 1+x; +// if(u==1.0) return x ; else +// return log(u)*(x/(u-1.0)); +// +// See HP-15C Advanced Functions Handbook, p.193. + +// Log1p returns the natural logarithm of 1 plus its argument x. +// It is more accurate than Log(1 + x) when x is near zero. +// +// Special cases are: +// +// Log1p(+Inf) = +Inf +// Log1p(±0) = ±0 +// Log1p(-1) = -Inf +// Log1p(x < -1) = NaN +// Log1p(NaN) = NaN +func Log1p(x float64) float64 { + return log1p(x) +} + +func log1p(x float64) float64 { + const ( + Sqrt2M1 = 4.142135623730950488017e-01 // Sqrt(2)-1 = 0x3fda827999fcef34 + Sqrt2HalfM1 = -2.928932188134524755992e-01 // Sqrt(2)/2-1 = 0xbfd2bec333018866 + Small = 1.0 / (1 << 29) // 2**-29 = 0x3e20000000000000 + Tiny = 1.0 / (1 << 54) // 2**-54 + Two53 = 1 << 53 // 2**53 + Ln2Hi = 6.93147180369123816490e-01 // 3fe62e42fee00000 + Ln2Lo = 1.90821492927058770002e-10 // 3dea39ef35793c76 + Lp1 = 6.666666666666735130e-01 // 3FE5555555555593 + Lp2 = 3.999999999940941908e-01 // 3FD999999997FA04 + Lp3 = 2.857142874366239149e-01 // 3FD2492494229359 + Lp4 = 2.222219843214978396e-01 // 3FCC71C51D8E78AF + Lp5 = 1.818357216161805012e-01 // 3FC7466496CB03DE + Lp6 = 1.531383769920937332e-01 // 3FC39A09D078C69F + Lp7 = 1.479819860511658591e-01 // 3FC2F112DF3E5244 + ) + + // special cases + switch { + case x < -1 || IsNaN(x): // includes -Inf + return NaN() + case x == -1: + return Inf(-1) + case IsInf(x, 1): + return Inf(1) + } + + absx := Abs(x) + + var f float64 + var iu uint64 + k := 1 + if absx < Sqrt2M1 { // |x| < Sqrt(2)-1 + if absx < Small { // |x| < 2**-29 + if absx < Tiny { // |x| < 2**-54 + return x + } + return x - x*x*0.5 + } + if x > Sqrt2HalfM1 { // Sqrt(2)/2-1 < x + // (Sqrt(2)/2-1) < x < (Sqrt(2)-1) + k = 0 + f = x + iu = 1 + } + } + var c float64 + if k != 0 { + var u float64 + if absx < Two53 { // 1<<53 + u = 1.0 + x + iu = Float64bits(u) + k = int((iu >> 52) - 1023) + // correction term + if k > 0 { + c = 1.0 - (u - x) + } else { + c = x - (u - 1.0) + } + c /= u + } else { + u = x + iu = Float64bits(u) + k = int((iu >> 52) - 1023) + c = 0 + } + iu &= 0x000fffffffffffff + if iu < 0x0006a09e667f3bcd { // mantissa of Sqrt(2) + u = Float64frombits(iu | 0x3ff0000000000000) // normalize u + } else { + k++ + u = Float64frombits(iu | 0x3fe0000000000000) // normalize u/2 + iu = (0x0010000000000000 - iu) >> 2 + } + f = u - 1.0 // Sqrt(2)/2 < u < Sqrt(2) + } + hfsq := 0.5 * f * f + var s, R, z float64 + if iu == 0 { // |f| < 2**-20 + if f == 0 { + if k == 0 { + return 0 + } + c += float64(k) * Ln2Lo + return float64(k)*Ln2Hi + c + } + R = hfsq * (1.0 - 0.66666666666666666*f) // avoid division + if k == 0 { + return f - R + } + return float64(k)*Ln2Hi - ((R - (float64(k)*Ln2Lo + c)) - f) + } + s = f / (2.0 + f) + z = s * s + R = z * (Lp1 + z*(Lp2+z*(Lp3+z*(Lp4+z*(Lp5+z*(Lp6+z*Lp7)))))) + if k == 0 { + return f - (hfsq - s*(hfsq+R)) + } + return float64(k)*Ln2Hi - ((hfsq - (s*(hfsq+R) + (float64(k)*Ln2Lo + c))) - f) +} diff --git a/gnovm/stdlibs/math/logb.gno b/gnovm/stdlibs/math/logb.gno new file mode 100644 index 00000000000..1a46464127c --- /dev/null +++ b/gnovm/stdlibs/math/logb.gno @@ -0,0 +1,52 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Logb returns the binary exponent of x. +// +// Special cases are: +// +// Logb(±Inf) = +Inf +// Logb(0) = -Inf +// Logb(NaN) = NaN +func Logb(x float64) float64 { + // special cases + switch { + case x == 0: + return Inf(-1) + case IsInf(x, 0): + return Inf(1) + case IsNaN(x): + return x + } + return float64(ilogb(x)) +} + +// Ilogb returns the binary exponent of x as an integer. +// +// Special cases are: +// +// Ilogb(±Inf) = MaxInt32 +// Ilogb(0) = MinInt32 +// Ilogb(NaN) = MaxInt32 +func Ilogb(x float64) int { + // special cases + switch { + case x == 0: + return MinInt32 + case IsNaN(x): + return MaxInt32 + case IsInf(x, 0): + return MaxInt32 + } + return ilogb(x) +} + +// ilogb returns the binary exponent of x. It assumes x is finite and +// non-zero. +func ilogb(x float64) int { + x, exp := normalize(x) + return int((Float64bits(x)>>shift)&mask) - bias + exp +} diff --git a/gnovm/stdlibs/math/mod.gno b/gnovm/stdlibs/math/mod.gno new file mode 100644 index 00000000000..54cf0b88d2a --- /dev/null +++ b/gnovm/stdlibs/math/mod.gno @@ -0,0 +1,49 @@ +// Copyright 2009-2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point mod function. +*/ + +// Mod returns the floating-point remainder of x/y. +// The magnitude of the result is less than y and its +// sign agrees with that of x. +// +// Special cases are: +// +// Mod(±Inf, y) = NaN +// Mod(NaN, y) = NaN +// Mod(x, 0) = NaN +// Mod(x, ±Inf) = x +// Mod(x, NaN) = NaN +func Mod(x, y float64) float64 { + return mod(x, y) +} + +func mod(x, y float64) float64 { + if y == 0 || IsInf(x, 0) || IsNaN(x) || IsNaN(y) { + return NaN() + } + y = Abs(y) + + yfr, yexp := Frexp(y) + r := x + if x < 0 { + r = -x + } + + for r >= y { + rfr, rexp := Frexp(r) + if rfr < yfr { + rexp = rexp - 1 + } + r = r - Ldexp(y, rexp-yexp) + } + if x < 0 { + r = -r + } + return r +} diff --git a/gnovm/stdlibs/math/modf.gno b/gnovm/stdlibs/math/modf.gno index d0fb27ef007..868d2c6f5f9 100644 --- a/gnovm/stdlibs/math/modf.gno +++ b/gnovm/stdlibs/math/modf.gno @@ -4,10 +4,6 @@ package math -import ( - imath "internal/math" -) - // Modf returns integer and fractional floating-point numbers // that sum to f. Both values have the same sign as f. // @@ -16,11 +12,6 @@ import ( // Modf(±Inf) = ±Inf, NaN // Modf(NaN) = NaN, NaN func Modf(f float64) (int float64, frac float64) { - /* XXX - if haveArchModf { - return archModf(f) - } - */ return modf(f) } @@ -36,14 +27,14 @@ func modf(f float64) (int float64, frac float64) { return 0, f } - x := imath.Float64bits(f) + x := Float64bits(f) e := uint(x>>shift)&mask - bias // Keep the top 12+e bits, the integer part; clear the rest. if e < 64-12 { x &^= 1<<(64-12-e) - 1 } - int = imath.Float64frombits(x) + int = Float64frombits(x) frac = f - int return } diff --git a/gnovm/stdlibs/math/native.gno b/gnovm/stdlibs/math/native.gno new file mode 100644 index 00000000000..b1b5684f9af --- /dev/null +++ b/gnovm/stdlibs/math/native.gno @@ -0,0 +1,21 @@ +package math + +// Float32bits returns the IEEE 754 binary representation of f, with the sign +// bit of f and the result in the same bit position. +// Float32bits(Float32frombits(x)) == x. +func Float32bits(f float32) uint32 // injected + +// Float32frombits returns the floating-point number corresponding to the IEEE +// 754 binary representation b, with the sign bit of b and the result in the +// same bit position. Float32frombits(Float32bits(x)) == x. +func Float32frombits(b uint32) float32 // injected + +// Float64bits returns the IEEE 754 binary representation of f, with the sign +// bit of f and the result in the same bit position. +// Float64bits(Float64frombits(x)) == x. +func Float64bits(f float64) uint64 // injected + +// Float64frombits returns the floating-point number corresponding to the IEEE +// 754 binary representation b, with the sign bit of b and the result in the +// same bit position. Float64frombits(Float64bits(x)) == x. +func Float64frombits(b uint64) float64 // injected diff --git a/gnovm/stdlibs/math/native.go b/gnovm/stdlibs/math/native.go new file mode 100644 index 00000000000..21021085f6d --- /dev/null +++ b/gnovm/stdlibs/math/native.go @@ -0,0 +1,8 @@ +package math + +import "math" + +func Float32bits(f float32) uint32 { return math.Float32bits(f) } +func Float32frombits(b uint32) float32 { return math.Float32frombits(b) } +func Float64bits(f float64) uint64 { return math.Float64bits(f) } +func Float64frombits(b uint64) float64 { return math.Float64frombits(b) } diff --git a/gnovm/stdlibs/math/nextafter.gno b/gnovm/stdlibs/math/nextafter.gno new file mode 100644 index 00000000000..ec18d542d9c --- /dev/null +++ b/gnovm/stdlibs/math/nextafter.gno @@ -0,0 +1,51 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Nextafter32 returns the next representable float32 value after x towards y. +// +// Special cases are: +// +// Nextafter32(x, x) = x +// Nextafter32(NaN, y) = NaN +// Nextafter32(x, NaN) = NaN +func Nextafter32(x, y float32) (r float32) { + switch { + case IsNaN(float64(x)) || IsNaN(float64(y)): // special case + r = float32(NaN()) + case x == y: + r = x + case x == 0: + r = float32(Copysign(float64(Float32frombits(1)), float64(y))) + case (y > x) == (x > 0): + r = Float32frombits(Float32bits(x) + 1) + default: + r = Float32frombits(Float32bits(x) - 1) + } + return +} + +// Nextafter returns the next representable float64 value after x towards y. +// +// Special cases are: +// +// Nextafter(x, x) = x +// Nextafter(NaN, y) = NaN +// Nextafter(x, NaN) = NaN +func Nextafter(x, y float64) (r float64) { + switch { + case IsNaN(x) || IsNaN(y): // special case + r = NaN() + case x == y: + r = x + case x == 0: + r = Copysign(Float64frombits(1), y) + case (y > x) == (x > 0): + r = Float64frombits(Float64bits(x) + 1) + default: + r = Float64frombits(Float64bits(x) - 1) + } + return +} diff --git a/gnovm/stdlibs/math/pow.gno b/gnovm/stdlibs/math/pow.gno new file mode 100644 index 00000000000..2351aeb440f --- /dev/null +++ b/gnovm/stdlibs/math/pow.gno @@ -0,0 +1,154 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +func isOddInt(x float64) bool { + xi, xf := Modf(x) + return xf == 0 && int64(xi)&1 == 1 +} + +// Special cases taken from FreeBSD's /usr/src/lib/msun/src/e_pow.c +// updated by IEEE Std. 754-2008 "Section 9.2.1 Special values". + +// Pow returns x**y, the base-x exponential of y. +// +// Special cases are (in order): +// +// Pow(x, ±0) = 1 for any x +// Pow(1, y) = 1 for any y +// Pow(x, 1) = x for any x +// Pow(NaN, y) = NaN +// Pow(x, NaN) = NaN +// Pow(±0, y) = ±Inf for y an odd integer < 0 +// Pow(±0, -Inf) = +Inf +// Pow(±0, +Inf) = +0 +// Pow(±0, y) = +Inf for finite y < 0 and not an odd integer +// Pow(±0, y) = ±0 for y an odd integer > 0 +// Pow(±0, y) = +0 for finite y > 0 and not an odd integer +// Pow(-1, ±Inf) = 1 +// Pow(x, +Inf) = +Inf for |x| > 1 +// Pow(x, -Inf) = +0 for |x| > 1 +// Pow(x, +Inf) = +0 for |x| < 1 +// Pow(x, -Inf) = +Inf for |x| < 1 +// Pow(+Inf, y) = +Inf for y > 0 +// Pow(+Inf, y) = +0 for y < 0 +// Pow(-Inf, y) = Pow(-0, -y) +// Pow(x, y) = NaN for finite x < 0 and finite non-integer y +func Pow(x, y float64) float64 { + return pow(x, y) +} + +func pow(x, y float64) float64 { + switch { + case y == 0 || x == 1: + return 1 + case y == 1: + return x + case IsNaN(x) || IsNaN(y): + return NaN() + case x == 0: + switch { + case y < 0: + if isOddInt(y) { + return Copysign(Inf(1), x) + } + return Inf(1) + case y > 0: + if isOddInt(y) { + return x + } + return 0 + } + case IsInf(y, 0): + switch { + case x == -1: + return 1 + case (Abs(x) < 1) == IsInf(y, 1): + return 0 + default: + return Inf(1) + } + case IsInf(x, 0): + if IsInf(x, -1) { + return Pow(1/x, -y) // Pow(-0, -y) + } + switch { + case y < 0: + return 0 + case y > 0: + return Inf(1) + } + case y == 0.5: + return Sqrt(x) + case y == -0.5: + return 1 / Sqrt(x) + } + + yi, yf := Modf(Abs(y)) + if yf != 0 && x < 0 { + return NaN() + } + if yi >= 1<<63 { + // yi is a large even int that will lead to overflow (or underflow to 0) + // for all x except -1 (x == 1 was handled earlier) + switch { + case x == -1: + return 1 + case (Abs(x) < 1) == (y > 0): + return 0 + default: + return Inf(1) + } + } + + // ans = a1 * 2**ae (= 1 for now). + a1 := 1.0 + ae := 0 + + // ans *= x**yf + if yf != 0 { + if yf > 0.5 { + yf -= 1 + yi += 1 + } + a1 = Exp(yf * Log(x)) + } + + // ans *= x**yi + // by multiplying in successive squarings + // of x according to bits of yi. + // accumulate powers of two into exp. + x1, xe := Frexp(x) + for i := int64(yi); i != 0; i >>= 1 { + if xe < -1<<12 || 1<<12 < xe { + // catch xe before it overflows the left shift below + // Since i !=0 it has at least one bit still set, so ae will accumulate xe + // on at least one more iteration, ae += xe is a lower bound on ae + // the lower bound on ae exceeds the size of a float64 exp + // so the final call to Ldexp will produce under/overflow (0/Inf) + ae += xe + break + } + if i&1 == 1 { + a1 *= x1 + ae += xe + } + x1 *= x1 + xe <<= 1 + if x1 < .5 { + x1 += x1 + xe-- + } + } + + // ans = a1*2**ae + // if y < 0 { ans = 1 / ans } + // but in the opposite order + if y < 0 { + a1 = 1 / a1 + ae = -ae + } + return Ldexp(a1, ae) +} diff --git a/gnovm/stdlibs/math/pow10.gno b/gnovm/stdlibs/math/pow10.gno new file mode 100644 index 00000000000..c31ad8dbc77 --- /dev/null +++ b/gnovm/stdlibs/math/pow10.gno @@ -0,0 +1,47 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// pow10tab stores the pre-computed values 10**i for i < 32. +var pow10tab = [...]float64{ + 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, + 1e30, 1e31, +} + +// pow10postab32 stores the pre-computed value for 10**(i*32) at index i. +var pow10postab32 = [...]float64{ + 1e00, 1e32, 1e64, 1e96, 1e128, 1e160, 1e192, 1e224, 1e256, 1e288, +} + +// pow10negtab32 stores the pre-computed value for 10**(-i*32) at index i. +var pow10negtab32 = [...]float64{ + 1e-00, 1e-32, 1e-64, 1e-96, 1e-128, 1e-160, 1e-192, 1e-224, 1e-256, 1e-288, 1e-320, +} + +// Pow10 returns 10**n, the base-10 exponential of n. +// +// Special cases are: +// +// Pow10(n) = 0 for n < -323 +// Pow10(n) = +Inf for n > 308 +func Pow10(n int) float64 { + if 0 <= n && n <= 308 { + return pow10postab32[uint(n)/32] * pow10tab[uint(n)%32] + } + + if -323 <= n && n <= 0 { + return pow10negtab32[uint(-n)/32] / pow10tab[uint(-n)%32] + } + + // n < -323 || 308 < n + if n > 0 { + return Inf(1) + } + + // n < -323 + return 0 +} diff --git a/gnovm/stdlibs/math/remainder.gno b/gnovm/stdlibs/math/remainder.gno new file mode 100644 index 00000000000..436dc595fa1 --- /dev/null +++ b/gnovm/stdlibs/math/remainder.gno @@ -0,0 +1,92 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code and the comment below are from +// FreeBSD's /usr/src/lib/msun/src/e_remainder.c and came +// with this notice. The go code is a simplified version of +// the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_remainder(x,y) +// Return : +// returns x REM y = x - [x/y]*y as if in infinite +// precision arithmetic, where [x/y] is the (infinite bit) +// integer nearest x/y (in half way cases, choose the even one). +// Method : +// Based on Mod() returning x - [x/y]chopped * y exactly. + +// Remainder returns the IEEE 754 floating-point remainder of x/y. +// +// Special cases are: +// +// Remainder(±Inf, y) = NaN +// Remainder(NaN, y) = NaN +// Remainder(x, 0) = NaN +// Remainder(x, ±Inf) = x +// Remainder(x, NaN) = NaN +func Remainder(x, y float64) float64 { + return remainder(x, y) +} + +func remainder(x, y float64) float64 { + const ( + Tiny = 4.45014771701440276618e-308 // 0x0020000000000000 + HalfMax = MaxFloat64 / 2 + ) + // special cases + switch { + case IsNaN(x) || IsNaN(y) || IsInf(x, 0) || y == 0: + return NaN() + case IsInf(y, 0): + return x + } + sign := false + if x < 0 { + x = -x + sign = true + } + if y < 0 { + y = -y + } + if x == y { + if sign { + zero := 0.0 + return -zero + } + return 0 + } + if y <= HalfMax { + x = Mod(x, y+y) // now x < 2y + } + if y < Tiny { + if x+x > y { + x -= y + if x+x >= y { + x -= y + } + } + } else { + yHalf := 0.5 * y + if x > yHalf { + x -= y + if x >= yHalf { + x -= y + } + } + } + if sign { + x = -x + } + return x +} diff --git a/gnovm/stdlibs/math/signbit.gno b/gnovm/stdlibs/math/signbit.gno index 38c2178932e..6ce44bb1a67 100644 --- a/gnovm/stdlibs/math/signbit.gno +++ b/gnovm/stdlibs/math/signbit.gno @@ -1,10 +1,6 @@ package math -import ( - imath "internal/math" -) - // Signbit reports whether x is negative or negative zero. func Signbit(x float64) bool { - return imath.Float64bits(x)&(1<<63) != 0 + return Float64bits(x)&(1<<63) != 0 } diff --git a/gnovm/stdlibs/math/sincos.gno b/gnovm/stdlibs/math/sincos.gno new file mode 100644 index 00000000000..e3fb96094fa --- /dev/null +++ b/gnovm/stdlibs/math/sincos.gno @@ -0,0 +1,73 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Coefficients _sin[] and _cos[] are found in pkg/math/sin.go. + +// Sincos returns Sin(x), Cos(x). +// +// Special cases are: +// +// Sincos(±0) = ±0, 1 +// Sincos(±Inf) = NaN, NaN +// Sincos(NaN) = NaN, NaN +func Sincos(x float64) (sin, cos float64) { + const ( + PI4A = 7.85398125648498535156e-1 // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B = 3.77489470793079817668e-8 // 0x3e64442d00000000, + PI4C = 2.69515142907905952645e-15 // 0x3ce8469898cc5170, + ) + // special cases + switch { + case x == 0: + return x, 1 // return ±0.0, 1.0 + case IsNaN(x) || IsInf(x, 0): + return NaN(), NaN() + } + + // make argument positive + sinSign, cosSign := false, false + if x < 0 { + x = -x + sinSign = true + } + + var j uint64 + var y, z float64 + if x >= reduceThreshold { + j, z = trigReduce(x) + } else { + j = uint64(x * (4 / Pi)) // integer part of x/(Pi/4), as integer for tests on the phase angle + y = float64(j) // integer part of x/(Pi/4), as float + + if j&1 == 1 { // map zeros to origin + j++ + y++ + } + j &= 7 // octant modulo 2Pi radians (360 degrees) + z = ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic + } + if j > 3 { // reflect in x axis + j -= 4 + sinSign, cosSign = !sinSign, !cosSign + } + if j > 1 { + cosSign = !cosSign + } + + zz := z * z + cos = 1.0 - 0.5*zz + zz*zz*((((((_cos[0]*zz)+_cos[1])*zz+_cos[2])*zz+_cos[3])*zz+_cos[4])*zz+_cos[5]) + sin = z + z*zz*((((((_sin[0]*zz)+_sin[1])*zz+_sin[2])*zz+_sin[3])*zz+_sin[4])*zz+_sin[5]) + if j == 1 || j == 2 { + sin, cos = cos, sin + } + if cosSign { + cos = -cos + } + if sinSign { + sin = -sin + } + return +} diff --git a/gnovm/stdlibs/math/sinh.gno b/gnovm/stdlibs/math/sinh.gno new file mode 100644 index 00000000000..b52149e2752 --- /dev/null +++ b/gnovm/stdlibs/math/sinh.gno @@ -0,0 +1,87 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point hyperbolic sine and cosine. + + The exponential func is called for arguments + greater in magnitude than 0.5. + + A series is used for arguments smaller in magnitude than 0.5. + + Cosh(x) is computed from the exponential func for + all arguments. +*/ + +// Sinh returns the hyperbolic sine of x. +// +// Special cases are: +// +// Sinh(±0) = ±0 +// Sinh(±Inf) = ±Inf +// Sinh(NaN) = NaN +func Sinh(x float64) float64 { + return sinh(x) +} + +func sinh(x float64) float64 { + // The coefficients are #2029 from Hart & Cheney. (20.36D) + const ( + P0 = -0.6307673640497716991184787251e+6 + P1 = -0.8991272022039509355398013511e+5 + P2 = -0.2894211355989563807284660366e+4 + P3 = -0.2630563213397497062819489e+2 + Q0 = -0.6307673640497716991212077277e+6 + Q1 = 0.1521517378790019070696485176e+5 + Q2 = -0.173678953558233699533450911e+3 + ) + + sign := false + if x < 0 { + x = -x + sign = true + } + + var temp float64 + switch { + case x > 21: + temp = Exp(x) * 0.5 + + case x > 0.5: + ex := Exp(x) + temp = (ex - 1/ex) * 0.5 + + default: + sq := x * x + temp = (((P3*sq+P2)*sq+P1)*sq + P0) * x + temp = temp / (((sq+Q2)*sq+Q1)*sq + Q0) + } + + if sign { + temp = -temp + } + return temp +} + +// Cosh returns the hyperbolic cosine of x. +// +// Special cases are: +// +// Cosh(±0) = 1 +// Cosh(±Inf) = +Inf +// Cosh(NaN) = NaN +func Cosh(x float64) float64 { + return cosh(x) +} + +func cosh(x float64) float64 { + x = Abs(x) + if x > 21 { + return Exp(x) * 0.5 + } + ex := Exp(x) + return (ex + 1/ex) * 0.5 +} diff --git a/gnovm/stdlibs/math/sqrt.gno b/gnovm/stdlibs/math/sqrt.gno new file mode 100644 index 00000000000..4f0f660660e --- /dev/null +++ b/gnovm/stdlibs/math/sqrt.gno @@ -0,0 +1,146 @@ +// Copied from go source code, 1.20 +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code and the long comment below are +// from FreeBSD's /usr/src/lib/msun/src/e_sqrt.c and +// came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_sqrt(x) +// Return correctly rounded sqrt. +// ----------------------------------------- +// | Use the hardware sqrt if you have one | +// ----------------------------------------- +// Method: +// Bit by bit method using integer arithmetic. (Slow, but portable) +// 1. Normalization +// Scale x to y in [1,4) with even powers of 2: +// find an integer k such that 1 <= (y=x*2**(2k)) < 4, then +// sqrt(x) = 2**k * sqrt(y) +// 2. Bit by bit computation +// Let q = sqrt(y) truncated to i bit after binary point (q = 1), +// i 0 +// i+1 2 +// s = 2*q , and y = 2 * ( y - q ). (1) +// i i i i +// +// To compute q from q , one checks whether +// i+1 i +// +// -(i+1) 2 +// (q + 2 ) <= y. (2) +// i +// -(i+1) +// If (2) is false, then q = q ; otherwise q = q + 2 . +// i+1 i i+1 i +// +// With some algebraic manipulation, it is not difficult to see +// that (2) is equivalent to +// -(i+1) +// s + 2 <= y (3) +// i i +// +// The advantage of (3) is that s and y can be computed by +// i i +// the following recurrence formula: +// if (3) is false +// +// s = s , y = y ; (4) +// i+1 i i+1 i +// +// otherwise, +// -i -(i+1) +// s = s + 2 , y = y - s - 2 (5) +// i+1 i i+1 i i +// +// One may easily use induction to prove (4) and (5). +// Note. Since the left hand side of (3) contain only i+2 bits, +// it is not necessary to do a full (53-bit) comparison +// in (3). +// 3. Final rounding +// After generating the 53 bits result, we compute one more bit. +// Together with the remainder, we can decide whether the +// result is exact, bigger than 1/2ulp, or less than 1/2ulp +// (it will never equal to 1/2ulp). +// The rounding mode can be detected by checking whether +// huge + tiny is equal to huge, and whether huge - tiny is +// equal to huge for some floating point number "huge" and "tiny". +// +// +// Notes: Rounding mode detection omitted. The constants "mask", "shift", +// and "bias" are found in src/math/bits.go + +// Sqrt returns the square root of x. +// +// Special cases are: +// +// Sqrt(+Inf) = +Inf +// Sqrt(±0) = ±0 +// Sqrt(x < 0) = NaN +// Sqrt(NaN) = NaN +func Sqrt(x float64) float64 { + return sqrt(x) +} + +// Note: On systems where Sqrt is a single instruction, the compiler +// may turn a direct call into a direct use of that instruction instead. + +func sqrt(x float64) float64 { + // special cases + switch { + case x == 0 || IsNaN(x) || IsInf(x, 1): + return x + case x < 0: + return NaN() + } + ix := Float64bits(x) + // normalize x + exp := int((ix >> shift) & mask) + if exp == 0 { // subnormal x + for ix&(1<>= 1 // exp = exp/2, exponent of square root + // generate sqrt(x) bit by bit + ix <<= 1 + var q, s uint64 // q = sqrt(x) + r := uint64(1 << (shift + 1)) // r = moving bit from MSB to LSB + for r != 0 { + t := s + r + if t <= ix { + s = t + r + ix -= t + q += r + } + ix <<= 1 + r >>= 1 + } + // final rounding + if ix != 0 { // remainder, result not exact + q += q & 1 // round according to extra bit + } + ix = q>>1 + uint64(exp-1+bias)< 2**49 = 5.6e14. +// [Accuracy loss statement from sin.go comments.] +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov + +// tan coefficients +var _tanP = [...]float64{ + -1.30936939181383777646e4, // 0xc0c992d8d24f3f38 + 1.15351664838587416140e6, // 0x413199eca5fc9ddd + -1.79565251976484877988e7, // 0xc1711fead3299176 +} + +var _tanQ = [...]float64{ + 1.00000000000000000000e0, + 1.36812963470692954678e4, // 0x40cab8a5eeb36572 + -1.32089234440210967447e6, // 0xc13427bc582abc96 + 2.50083801823357915839e7, // 0x4177d98fc2ead8ef + -5.38695755929454629881e7, // 0xc189afe03cbe5a31 +} + +// Tan returns the tangent of the radian argument x. +// +// Special cases are: +// +// Tan(±0) = ±0 +// Tan(±Inf) = NaN +// Tan(NaN) = NaN +func Tan(x float64) float64 { + return tan(x) +} + +func tan(x float64) float64 { + const ( + PI4A = 7.85398125648498535156e-1 // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B = 3.77489470793079817668e-8 // 0x3e64442d00000000, + PI4C = 2.69515142907905952645e-15 // 0x3ce8469898cc5170, + ) + // special cases + switch { + case x == 0 || IsNaN(x): + return x // return ±0 || NaN() + case IsInf(x, 0): + return NaN() + } + + // make argument positive but save the sign + sign := false + if x < 0 { + x = -x + sign = true + } + var j uint64 + var y, z float64 + if x >= reduceThreshold { + j, z = trigReduce(x) + } else { + j = uint64(x * (4 / Pi)) // integer part of x/(Pi/4), as integer for tests on the phase angle + y = float64(j) // integer part of x/(Pi/4), as float + + /* map zeros and singularities to origin */ + if j&1 == 1 { + j++ + y++ + } + + z = ((x - y*PI4A) - y*PI4B) - y*PI4C + } + zz := z * z + + if zz > 1e-14 { + y = z + z*(zz*(((_tanP[0]*zz)+_tanP[1])*zz+_tanP[2])/((((zz+_tanQ[1])*zz+_tanQ[2])*zz+_tanQ[3])*zz+_tanQ[4])) + } else { + y = z + } + if j&2 == 2 { + y = -1 / y + } + if sign { + y = -y + } + return y +} diff --git a/gnovm/stdlibs/math/tanh.gno b/gnovm/stdlibs/math/tanh.gno new file mode 100644 index 00000000000..4bb9d5d43d1 --- /dev/null +++ b/gnovm/stdlibs/math/tanh.gno @@ -0,0 +1,103 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below were from http://netlib.sandia.gov/cephes/cmath/sin.c, +// available from http://www.netlib.org/cephes/cmath.tgz. +// The go code is a simplified version of the original C. +// tanh.c +// +// Hyperbolic tangent +// +// SYNOPSIS: +// +// double x, y, tanh(); +// +// y = tanh( x ); +// +// DESCRIPTION: +// +// Returns hyperbolic tangent of argument in the range MINLOG to MAXLOG. +// MAXLOG = 8.8029691931113054295988e+01 = log(2**127) +// MINLOG = -8.872283911167299960540e+01 = log(2**-128) +// +// A rational function is used for |x| < 0.625. The form +// x + x**3 P(x)/Q(x) of Cody & Waite is employed. +// Otherwise, +// tanh(x) = sinh(x)/cosh(x) = 1 - 2/(exp(2x) + 1). +// +// ACCURACY: +// +// Relative error: +// arithmetic domain # trials peak rms +// IEEE -2,2 30000 2.5e-16 5.8e-17 +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov +// + +var tanhP = [...]float64{ + -9.64399179425052238628e-1, + -9.92877231001918586564e1, + -1.61468768441708447952e3, +} + +var tanhQ = [...]float64{ + 1.12811678491632931402e2, + 2.23548839060100448583e3, + 4.84406305325125486048e3, +} + +// Tanh returns the hyperbolic tangent of x. +// +// Special cases are: +// +// Tanh(±0) = ±0 +// Tanh(±Inf) = ±1 +// Tanh(NaN) = NaN +func Tanh(x float64) float64 { + return tanh(x) +} + +func tanh(x float64) float64 { + const MAXLOG = 8.8029691931113054295988e+01 // log(2**127) + z := Abs(x) + switch { + case z > 0.5*MAXLOG: + if x < 0 { + return -1 + } + return 1 + case z >= 0.625: + s := Exp(2 * z) + z = 1 - 2/(s+1) + if x < 0 { + z = -z + } + default: + if x == 0 { + return x + } + s := x * x + z = x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2]) + } + return z +} diff --git a/gnovm/stdlibs/math/trig_reduce.gno b/gnovm/stdlibs/math/trig_reduce.gno new file mode 100644 index 00000000000..5ecdd8375e3 --- /dev/null +++ b/gnovm/stdlibs/math/trig_reduce.gno @@ -0,0 +1,102 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +import ( + "math/bits" +) + +// reduceThreshold is the maximum value of x where the reduction using Pi/4 +// in 3 float64 parts still gives accurate results. This threshold +// is set by y*C being representable as a float64 without error +// where y is given by y = floor(x * (4 / Pi)) and C is the leading partial +// terms of 4/Pi. Since the leading terms (PI4A and PI4B in sin.go) have 30 +// and 32 trailing zero bits, y should have less than 30 significant bits. +// +// y < 1<<30 -> floor(x*4/Pi) < 1<<30 -> x < (1<<30 - 1) * Pi/4 +// +// So, conservatively we can take x < 1<<29. +// Above this threshold Payne-Hanek range reduction must be used. +const reduceThreshold = 1 << 29 + +// trigReduce implements Payne-Hanek range reduction by Pi/4 +// for x > 0. It returns the integer part mod 8 (j) and +// the fractional part (z) of x / (Pi/4). +// The implementation is based on: +// "ARGUMENT REDUCTION FOR HUGE ARGUMENTS: Good to the Last Bit" +// K. C. Ng et al, March 24, 1992 +// The simulated multi-precision calculation of x*B uses 64-bit integer arithmetic. +func trigReduce(x float64) (j uint64, z float64) { + const PI4 = Pi / 4 + if x < PI4 { + return 0, x + } + // Extract out the integer and exponent such that, + // x = ix * 2 ** exp. + ix := Float64bits(x) + exp := int(ix>>shift&mask) - bias - shift + ix &^= mask << shift + ix |= 1 << shift + // Use the exponent to extract the 3 appropriate uint64 digits from mPi4, + // B ~ (z0, z1, z2), such that the product leading digit has the exponent -61. + // Note, exp >= -53 since x >= PI4 and exp < 971 for maximum float64. + digit, bitshift := uint(exp+61)/64, uint(exp+61)%64 + z0 := (mPi4[digit] << bitshift) | (mPi4[digit+1] >> (64 - bitshift)) + z1 := (mPi4[digit+1] << bitshift) | (mPi4[digit+2] >> (64 - bitshift)) + z2 := (mPi4[digit+2] << bitshift) | (mPi4[digit+3] >> (64 - bitshift)) + // Multiply mantissa by the digits and extract the upper two digits (hi, lo). + z2hi, _ := bits.Mul64(z2, ix) + z1hi, z1lo := bits.Mul64(z1, ix) + z0lo := z0 * ix + lo, c := bits.Add64(z1lo, z2hi, 0) + hi, _ := bits.Add64(z0lo, z1hi, c) + // The top 3 bits are j. + j = hi >> 61 + // Extract the fraction and find its magnitude. + hi = hi<<3 | lo>>61 + lz := uint(bits.LeadingZeros64(hi)) + e := uint64(bias - (lz + 1)) + // Clear implicit mantissa bit and shift into place. + hi = (hi << (lz + 1)) | (lo >> (64 - (lz + 1))) + hi >>= 64 - shift + // Include the exponent and convert to a float. + hi |= e << shift + z = Float64frombits(hi) + // Map zeros to origin. + if j&1 == 1 { + j++ + j &= 7 + z-- + } + // Multiply the fractional part by pi/4. + return j, z * PI4 +} + +// mPi4 is the binary digits of 4/pi as a uint64 array, +// that is, 4/pi = Sum mPi4[i]*2^(-64*i) +// 19 64-bit digits and the leading one bit give 1217 bits +// of precision to handle the largest possible float64 exponent. +var mPi4 = [...]uint64{ + 0x0000000000000001, + 0x45f306dc9c882a53, + 0xf84eafa3ea69bb81, + 0xb6c52b3278872083, + 0xfca2c757bd778ac3, + 0x6e48dc74849ba5c0, + 0x0c925dd413a32439, + 0xfc3bd63962534e7d, + 0xd1046bea5d768909, + 0xd338e04d68befc82, + 0x7323ac7306a673e9, + 0x3908bf177bf25076, + 0x3ff12fffbc0b301f, + 0xde5e2316b414da3e, + 0xda6cfd9e4f96136e, + 0x9e8c7ecd3cbfd45a, + 0xea4f758fd7cbe2f6, + 0x7a0e73ef14a525d4, + 0xd7f6bf623f1aba10, + 0xac06608df8f6d757, +} diff --git a/gnovm/stdlibs/native.go b/gnovm/stdlibs/native.go new file mode 100644 index 00000000000..1b16fc21040 --- /dev/null +++ b/gnovm/stdlibs/native.go @@ -0,0 +1,803 @@ +// This file is autogenerated from the genstd tool (@/misc/genstd); do not edit. +// To regenerate it, run `go generate` from this directory. + +package stdlibs + +import ( + "reflect" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + libs_crypto_sha256 "github.com/gnolang/gno/gnovm/stdlibs/crypto/sha256" + libs_math "github.com/gnolang/gno/gnovm/stdlibs/math" + libs_std "github.com/gnolang/gno/gnovm/stdlibs/std" + libs_strconv "github.com/gnolang/gno/gnovm/stdlibs/strconv" + libs_time "github.com/gnolang/gno/gnovm/stdlibs/time" + tm2_crypto "github.com/gnolang/gno/tm2/pkg/crypto" +) + +type nativeFunc struct { + gnoPkg string + gnoFunc gno.Name + params []gno.FieldTypeExpr + results []gno.FieldTypeExpr + f func(m *gno.Machine) +} + +var nativeFuncs = [...]nativeFunc{ + { + "crypto/sha256", + "sum256", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("[]byte")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("[32]byte")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 []byte + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_crypto_sha256.X_sum256(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "math", + "Float32bits", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("float32")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("uint32")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 float32 + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_math.Float32bits(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "math", + "Float32frombits", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("uint32")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("float32")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 uint32 + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_math.Float32frombits(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "math", + "Float64bits", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("float64")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("uint64")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 float64 + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_math.Float64bits(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "math", + "Float64frombits", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("uint64")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("float64")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 uint64 + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_math.Float64frombits(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "AssertOriginCall", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + libs_std.AssertOriginCall( + m, + ) + }, + }, + { + "std", + "IsOriginCall", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("bool")}, + }, + func(m *gno.Machine) { + r0 := libs_std.IsOriginCall( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "CurrentRealmPath", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + r0 := libs_std.CurrentRealmPath( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetChainID", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + r0 := libs_std.GetChainID( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetHeight", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("int64")}, + }, + func(m *gno.Machine) { + r0 := libs_std.GetHeight( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetOrigSend", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Coins")}, + }, + func(m *gno.Machine) { + r0 := libs_std.GetOrigSend( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetOrigCaller", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Address")}, + }, + func(m *gno.Machine) { + r0 := libs_std.GetOrigCaller( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "CurrentRealm", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Realm")}, + }, + func(m *gno.Machine) { + r0 := libs_std.CurrentRealm( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "PrevRealm", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Realm")}, + }, + func(m *gno.Machine) { + r0 := libs_std.PrevRealm( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetOrigPkgAddr", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Address")}, + }, + func(m *gno.Machine) { + r0 := libs_std.GetOrigPkgAddr( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetCallerAt", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Address")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 int + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_std.GetCallerAt( + m, + p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetBanker", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("BankerType")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Banker")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 libs_std.BankerType + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_std.GetBanker( + m, + p0) + + m.PushValue(r0) + }, + }, + { + "std", + "DerivePkgAddr", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Address")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_std.DerivePkgAddr(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "EncodeBech32", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + {Name: gno.N("p1"), Type: gno.X("[20]byte")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Address")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + p1 [20]byte + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + r0 := libs_std.EncodeBech32(p0, p1) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "DecodeBech32", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("Address")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + {Name: gno.N("r1"), Type: gno.X("[20]byte")}, + {Name: gno.N("r2"), Type: gno.X("bool")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 tm2_crypto.Bech32Address + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0, r1, r2 := libs_std.DecodeBech32(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r1).Elem(), + )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r2).Elem(), + )) + }, + }, + { + "strconv", + "Itoa", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 int + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_strconv.Itoa(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "AppendUint", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("[]byte")}, + {Name: gno.N("p1"), Type: gno.X("uint64")}, + {Name: gno.N("p2"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("[]byte")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 []byte + rp0 = reflect.ValueOf(&p0).Elem() + p1 uint64 + rp1 = reflect.ValueOf(&p1).Elem() + p2 int + rp2 = reflect.ValueOf(&p2).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 2, "")).TV, rp2) + + r0 := libs_strconv.AppendUint(p0, p1, p2) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "Atoi", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("int")}, + {Name: gno.N("r1"), Type: gno.X("error")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0, r1 := libs_strconv.Atoi(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r1).Elem(), + )) + }, + }, + { + "strconv", + "CanBackquote", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("bool")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_strconv.CanBackquote(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "FormatInt", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("int64")}, + {Name: gno.N("p1"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 int64 + rp0 = reflect.ValueOf(&p0).Elem() + p1 int + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + r0 := libs_strconv.FormatInt(p0, p1) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "FormatUint", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("uint64")}, + {Name: gno.N("p1"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 uint64 + rp0 = reflect.ValueOf(&p0).Elem() + p1 int + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + r0 := libs_strconv.FormatUint(p0, p1) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "FormatFloat", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("float64")}, + {Name: gno.N("p1"), Type: gno.X("byte")}, + {Name: gno.N("p2"), Type: gno.X("int")}, + {Name: gno.N("p3"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 float64 + rp0 = reflect.ValueOf(&p0).Elem() + p1 byte + rp1 = reflect.ValueOf(&p1).Elem() + p2 int + rp2 = reflect.ValueOf(&p2).Elem() + p3 int + rp3 = reflect.ValueOf(&p3).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 2, "")).TV, rp2) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 3, "")).TV, rp3) + + r0 := libs_strconv.FormatFloat(p0, p1, p2, p3) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "Quote", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_strconv.Quote(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "QuoteToASCII", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_strconv.QuoteToASCII(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "time", + "now", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("int64")}, + {Name: gno.N("r1"), Type: gno.X("int32")}, + {Name: gno.N("r2"), Type: gno.X("int64")}, + }, + func(m *gno.Machine) { + r0, r1, r2 := libs_time.X_now( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r1).Elem(), + )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r2).Elem(), + )) + }, + }, +} diff --git a/gnovm/stdlibs/banker.go b/gnovm/stdlibs/std/banker.go similarity index 97% rename from gnovm/stdlibs/banker.go rename to gnovm/stdlibs/std/banker.go index 82bf1bad42a..7653f2a519f 100644 --- a/gnovm/stdlibs/banker.go +++ b/gnovm/stdlibs/std/banker.go @@ -1,4 +1,4 @@ -package stdlibs +package std import ( "fmt" @@ -21,10 +21,10 @@ type Banker interface { } // Used in std.GetBanker(options). -// Also available as Gno in stdlibs/std/banker.go +// Also available as Gno in stdlibs/std/banker.gno type BankerType uint8 -// Also available as Gno in stdlibs/std/banker.go +// Also available as Gno in stdlibs/std/banker.gno const ( // Can only read state. BankerTypeReadonly BankerType = iota diff --git a/gnovm/stdlibs/context.go b/gnovm/stdlibs/std/context.go similarity index 96% rename from gnovm/stdlibs/context.go rename to gnovm/stdlibs/std/context.go index 5f140c344d4..c50e2e5e1b9 100644 --- a/gnovm/stdlibs/context.go +++ b/gnovm/stdlibs/std/context.go @@ -1,4 +1,4 @@ -package stdlibs +package std import ( "github.com/gnolang/gno/tm2/pkg/crypto" diff --git a/gnovm/stdlibs/frame.go b/gnovm/stdlibs/std/frame.go similarity index 79% rename from gnovm/stdlibs/frame.go rename to gnovm/stdlibs/std/frame.go index e428bb1776d..9c94d0ccddd 100644 --- a/gnovm/stdlibs/frame.go +++ b/gnovm/stdlibs/std/frame.go @@ -1,6 +1,8 @@ -package stdlibs +package std -import "github.com/gnolang/gno/tm2/pkg/crypto" +import ( + "github.com/gnolang/gno/tm2/pkg/crypto" +) type Realm struct { addr crypto.Bech32Address diff --git a/gnovm/stdlibs/std/frame_test.go b/gnovm/stdlibs/std/frame_test.go new file mode 100644 index 00000000000..de10b67ed87 --- /dev/null +++ b/gnovm/stdlibs/std/frame_test.go @@ -0,0 +1,125 @@ +package std + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" +) + +func TestPrevRealmIsOrigin(t *testing.T) { + var ( + user = gno.DerivePkgAddr("user1.gno").Bech32() + ctx = ExecContext{ + OrigCaller: user, + } + ) + tests := []struct { + name string + machine *gno.Machine + expectedRealm Realm + expectedIsOriginCall bool + }{ + { + name: "no frames", + machine: &gno.Machine{ + Context: ctx, + Frames: []gno.Frame{}, + }, + expectedRealm: Realm{ + addr: user, + pkgPath: "", + }, + expectedIsOriginCall: true, + }, + { + name: "one frame w/o LastPackage", + machine: &gno.Machine{ + Context: ctx, + Frames: []gno.Frame{ + {LastPackage: nil}, + }, + }, + expectedRealm: Realm{ + addr: user, + pkgPath: "", + }, + expectedIsOriginCall: true, + }, + { + name: "one non-realm frame", + machine: &gno.Machine{ + Context: ctx, + Frames: []gno.Frame{ + {LastPackage: &gno.PackageValue{PkgPath: "gno.land/p/xxx"}}, + }, + }, + expectedRealm: Realm{ + addr: user, + pkgPath: "", + }, + expectedIsOriginCall: true, + }, + { + name: "one realm frame", + machine: &gno.Machine{ + Context: ctx, + Frames: []gno.Frame{ + {LastPackage: &gno.PackageValue{PkgPath: "gno.land/r/xxx"}}, + }, + }, + expectedRealm: Realm{ + addr: user, + pkgPath: "", + }, + expectedIsOriginCall: true, + }, + { + name: "multiple frames with one realm", + machine: &gno.Machine{ + Context: ctx, + Frames: []gno.Frame{ + {LastPackage: &gno.PackageValue{PkgPath: "gno.land/p/xxx"}}, + {LastPackage: &gno.PackageValue{PkgPath: "gno.land/p/xxx"}}, + {LastPackage: &gno.PackageValue{PkgPath: "gno.land/r/xxx"}}, + }, + }, + expectedRealm: Realm{ + addr: user, + pkgPath: "", + }, + expectedIsOriginCall: true, + }, + { + name: "multiple frames with multiple realms", + machine: &gno.Machine{ + Context: ctx, + Frames: []gno.Frame{ + {LastPackage: &gno.PackageValue{PkgPath: "gno.land/r/zzz"}}, + {LastPackage: &gno.PackageValue{PkgPath: "gno.land/r/zzz"}}, + {LastPackage: &gno.PackageValue{PkgPath: "gno.land/r/yyy"}}, + {LastPackage: &gno.PackageValue{PkgPath: "gno.land/p/yyy"}}, + {LastPackage: &gno.PackageValue{PkgPath: "gno.land/p/xxx"}}, + {LastPackage: &gno.PackageValue{PkgPath: "gno.land/r/xxx"}}, + }, + }, + expectedRealm: Realm{ + addr: gno.DerivePkgAddr("gno.land/r/yyy").Bech32(), + pkgPath: "gno.land/r/yyy", + }, + expectedIsOriginCall: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + + realm := PrevRealm(tt.machine) + isOrigin := IsOriginCall(tt.machine) + + assert.Equal(tt.expectedRealm, realm) + assert.Equal(tt.expectedIsOriginCall, isOrigin) + }) + } +} diff --git a/gnovm/stdlibs/std/native.gno b/gnovm/stdlibs/std/native.gno new file mode 100644 index 00000000000..2f7da810bcb --- /dev/null +++ b/gnovm/stdlibs/std/native.gno @@ -0,0 +1,18 @@ +package std + +func AssertOriginCall() // injected +func IsOriginCall() bool // injected +func CurrentRealmPath() string // injected +func GetChainID() string // injected +func GetHeight() int64 // injected +func GetOrigSend() Coins // injected +func GetOrigCaller() Address // injected +func CurrentRealm() Realm // injected +func PrevRealm() Realm // injected +func GetOrigPkgAddr() Address // injected +func GetCallerAt(n int) Address // injected +func GetBanker(bt BankerType) Banker // injected +func DerivePkgAddr(pkgPath string) Address // injected + +func EncodeBech32(prefix string, bz [20]byte) Address // injected +func DecodeBech32(addr Address) (prefix string, bz [20]byte, ok bool) // injected diff --git a/gnovm/stdlibs/std/native.go b/gnovm/stdlibs/std/native.go new file mode 100644 index 00000000000..bee8d4886c1 --- /dev/null +++ b/gnovm/stdlibs/std/native.go @@ -0,0 +1,176 @@ +package std + +import ( + "reflect" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/tm2/pkg/bech32" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/std" +) + +func AssertOriginCall(m *gno.Machine) { + if !IsOriginCall(m) { + m.Panic(typedString("invalid non-origin call")) + } +} + +func IsOriginCall(m *gno.Machine) bool { + return PrevRealm(m).addr == m.Context.(ExecContext).OrigCaller +} + +func CurrentRealmPath(m *gno.Machine) string { + if m.Realm != nil { + return m.Realm.Path + } + return "" +} + +func GetChainID(m *gno.Machine) string { + return m.Context.(ExecContext).ChainID +} + +func GetHeight(m *gno.Machine) int64 { + return m.Context.(ExecContext).Height +} + +func GetOrigSend(m *gno.Machine) std.Coins { + return m.Context.(ExecContext).OrigSend +} + +func GetOrigCaller(m *gno.Machine) crypto.Bech32Address { + return m.Context.(ExecContext).OrigCaller +} + +func CurrentRealm(m *gno.Machine) Realm { + var ( + ctx = m.Context.(ExecContext) + // Default lastCaller is OrigCaller, the signer of the tx + lastCaller = ctx.OrigCaller + lastPkgPath = "" + ) + + for i := m.NumFrames() - 1; i > 0; i-- { + fr := m.Frames[i] + if fr.LastPackage != nil && fr.LastPackage.IsRealm() { + lastCaller = fr.LastPackage.GetPkgAddr().Bech32() + lastPkgPath = fr.LastPackage.PkgPath + break + } + } + + return Realm{ + addr: lastCaller, + pkgPath: lastPkgPath, + } +} + +// PrevRealm loops on frames and returns the second realm found in the calling +// order. If no such realm was found, returns the tx signer (aka OrigCaller). +func PrevRealm(m *gno.Machine) Realm { + var lastRealmPath string + for i := m.NumFrames() - 1; i > 0; i-- { + fr := m.Frames[i] + if fr.LastPackage == nil || !fr.LastPackage.IsRealm() { + // Ignore non-realm frame + continue + } + realmPath := fr.LastPackage.PkgPath + if lastRealmPath == "" { + // Record the path of the first encountered realm and continue + lastRealmPath = realmPath + continue + } + if lastRealmPath != realmPath { + // Second realm detected, return it. + return Realm{ + addr: fr.LastPackage.GetPkgAddr().Bech32(), + pkgPath: realmPath, + } + } + } + // No second realm found, return the tx signer. + return Realm{ + addr: m.Context.(ExecContext).OrigCaller, + pkgPath: "", // empty for users + } +} + +func GetOrigPkgAddr(m *gno.Machine) crypto.Bech32Address { + return m.Context.(ExecContext).OrigPkgAddr +} + +func GetCallerAt(m *gno.Machine, n int) crypto.Bech32Address { + if n <= 0 { + m.Panic(typedString("GetCallerAt requires positive arg")) + return "" + } + if n > m.NumFrames() { + // NOTE: the last frame's LastPackage + // is set to the original non-frame + // package, so need this check. + m.Panic(typedString("frame not found")) + return "" + } + if n == m.NumFrames() { + // This makes it consistent with GetOrigCaller. + ctx := m.Context.(ExecContext) + return ctx.OrigCaller + } + return m.LastCallFrame(n).LastPackage.GetPkgAddr().Bech32() +} + +func GetBanker(m *gno.Machine, bankerType BankerType) gno.TypedValue { + ctx := m.Context.(ExecContext) + banker := ctx.Banker + switch bankerType { + case BankerTypeReadonly: + banker = NewReadonlyBanker(banker) + case BankerTypeOrigSend: + banker = NewOrigSendBanker(banker, ctx.OrigPkgAddr, ctx.OrigSend, ctx.OrigSendSpent) + case BankerTypeRealmSend: + banker = NewRealmSendBanker(banker, ctx.OrigPkgAddr) + case BankerTypeRealmIssue: + banker = banker + default: + panic("should not happen") // defensive + } + m.Alloc.AllocateStruct() // defensive; native space not allocated. + m.Alloc.AllocateStructFields(10) // defensive 10; native space not allocated. + + // make gno bankAdapter{rv} + btv := gno.Go2GnoNativeValue(m.Alloc, reflect.ValueOf(banker)) + bsv := m.Alloc.NewStructWithFields(btv) + bankAdapterType := m.Store.GetType(gno.DeclaredTypeID("std", "bankAdapter")) + res0 := gno.TypedValue{T: bankAdapterType, V: bsv} + + return res0 +} + +func EncodeBech32(prefix string, bytes [20]byte) crypto.Bech32Address { + b32, err := bech32.ConvertAndEncode(prefix, bytes[:]) + if err != nil { + panic(err) // should not happen + } + return crypto.Bech32Address(b32) +} + +func DerivePkgAddr(pkgPath string) crypto.Bech32Address { + return gno.DerivePkgAddr(pkgPath).Bech32() +} + +func DecodeBech32(addr crypto.Bech32Address) (prefix string, bytes [20]byte, ok bool) { + prefix, bz, err := bech32.Decode(string(addr)) + if err != nil || len(bz) != 20 { + return "", [20]byte{}, false + } + // TODO: can be simplified when we switch to go1.20 in go mod to be a simple [20]byte(bz) + copy(bytes[:], bz) + return prefix, bytes, true +} + +func typedString(s gno.StringValue) gno.TypedValue { + tv := gno.TypedValue{T: gno.StringType} + tv.SetString(s) + return tv +} diff --git a/gnovm/stdlibs/stdlibs.go b/gnovm/stdlibs/stdlibs.go index fb230a0cf86..22054613c03 100644 --- a/gnovm/stdlibs/stdlibs.go +++ b/gnovm/stdlibs/stdlibs.go @@ -1,593 +1,30 @@ package stdlibs +//go:generate go run github.com/gnolang/gno/misc/genstd + import ( - "crypto/sha256" - "math" "reflect" - "strconv" - "time" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/tm2/pkg/bech32" + libsstd "github.com/gnolang/gno/gnovm/stdlibs/std" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/std" ) +type ExecContext = libsstd.ExecContext + func InjectNativeMappings(store gno.Store) { store.AddGo2GnoMapping(reflect.TypeOf(crypto.Bech32Address("")), "std", "Address") store.AddGo2GnoMapping(reflect.TypeOf(std.Coins{}), "std", "Coins") store.AddGo2GnoMapping(reflect.TypeOf(std.Coin{}), "std", "Coin") - store.AddGo2GnoMapping(reflect.TypeOf(Realm{}), "std", "Realm") + store.AddGo2GnoMapping(reflect.TypeOf(libsstd.Realm{}), "std", "Realm") } -func InjectPackage(store gno.Store, pn *gno.PackageNode) { - switch pn.PkgPath { - case "internal/crypto/sha256": - pn.DefineNative("Sum256", - gno.Flds( // params - "data", "[]byte", - ), - gno.Flds( // results - "bz", "[32]byte", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - bz := []byte(nil) - - if arg0.V != nil { - slice := arg0.V.(*gno.SliceValue) - array := slice.GetBase(m.Store) - bz = array.GetReadonlyBytes()[:slice.Length] - } - - hash := sha256.Sum256(bz) - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(hash), - ) - m.PushValue(res0) - }, - ) - case "internal/math": - pn.DefineNative("Float32bits", - gno.Flds( // params - "f", "float32", - ), - gno.Flds( // results - "b", "uint32", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - res0 := typedUint32(math.Float32bits(arg0.GetFloat32())) - m.PushValue(res0) - }, - ) - pn.DefineNative("Float32frombits", - gno.Flds( // params - "b", "uint32", - ), - gno.Flds( // results - "f", "float32", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - res0 := typedFloat32(math.Float32frombits(arg0.GetUint32())) - m.PushValue(res0) - }, - ) - pn.DefineNative("Float64bits", - gno.Flds( // params - "f", "float64", - ), - gno.Flds( // results - "b", "uint64", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - res0 := typedUint64(math.Float64bits(arg0.GetFloat64())) - m.PushValue(res0) - }, - ) - pn.DefineNative("Float64frombits", - gno.Flds( // params - "b", "uint64", - ), - gno.Flds( // results - "f", "float64", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - res0 := typedFloat64(math.Float64frombits(arg0.GetUint64())) - m.PushValue(res0) - }, - ) - case "internal/os": - pn.DefineNative("Now", - gno.Flds( // params - ), - gno.Flds( // results - "sec", "int64", - "nsec", "int32", - "mono", "int64", - ), - func(m *gno.Machine) { - if m.Context == nil { - res0 := typedInt64(0) - res1 := typedInt32(0) - res2 := typedInt64(0) - m.PushValue(res0) - m.PushValue(res1) - m.PushValue(res2) - } else { - ctx := m.Context.(ExecContext) - res0 := typedInt64(ctx.Timestamp) - res1 := typedInt32(int32(ctx.TimestampNano)) - res2 := typedInt64(ctx.Timestamp*int64(time.Second) + ctx.TimestampNano) - m.PushValue(res0) - m.PushValue(res1) - m.PushValue(res2) - } - }, - ) - // case "internal/os_test": - // XXX defined in tests/imports.go - case "strconv": - pn.DefineGoNativeValue("Itoa", strconv.Itoa) - pn.DefineGoNativeValue("Atoi", strconv.Atoi) - pn.DefineGoNativeValue("FormatInt", strconv.FormatInt) - pn.DefineGoNativeValue("FormatUint", strconv.FormatUint) - pn.DefineGoNativeValue("Quote", strconv.Quote) - pn.DefineGoNativeValue("QuoteToASCII", strconv.QuoteToASCII) - pn.DefineGoNativeValue("CanBackquote", strconv.CanBackquote) - pn.DefineGoNativeValue("IntSize", strconv.IntSize) - pn.DefineGoNativeValue("AppendUint", strconv.AppendUint) - case "std": - // NOTE: some of these are overridden in tests/imports.go - // Also see stdlibs/InjectPackage. - pn.DefineNative("AssertOriginCall", - gno.Flds( // params - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - isOrigin := len(m.Frames) == 2 - if !isOrigin { - m.Panic(typedString("invalid non-origin call")) - return - } - }, - ) - pn.DefineNative("IsOriginCall", - gno.Flds( // params - ), - gno.Flds( // results - "isOrigin", "bool", - ), - func(m *gno.Machine) { - isOrigin := len(m.Frames) == 2 - res0 := gno.TypedValue{T: gno.BoolType} - res0.SetBool(isOrigin) - m.PushValue(res0) - }, - ) - pn.DefineNative("CurrentRealmPath", - gno.Flds( // params - ), - gno.Flds( // results - "", "string", - ), - func(m *gno.Machine) { - realmPath := "" - if m.Realm != nil { - realmPath = m.Realm.Path - } - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(realmPath), - ) - m.PushValue(res0) - }, - ) - pn.DefineNative("GetChainID", - gno.Flds( // params - ), - gno.Flds( // results - "", "string", - ), - func(m *gno.Machine) { - ctx := m.Context.(ExecContext) - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(ctx.ChainID), - ) - m.PushValue(res0) - }, - ) - pn.DefineNative("GetHeight", - gno.Flds( // params - ), - gno.Flds( // results - "", "int64", - ), - func(m *gno.Machine) { - ctx := m.Context.(ExecContext) - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(ctx.Height), - ) - m.PushValue(res0) - }, - ) - pn.DefineNative("GetOrigSend", - gno.Flds( // params - ), - gno.Flds( // results - "", "Coins", - ), - func(m *gno.Machine) { - ctx := m.Context.(ExecContext) - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(ctx.OrigSend), - ) - coinT := store.GetType(gno.DeclaredTypeID("std", "Coin")) - coinsT := store.GetType(gno.DeclaredTypeID("std", "Coins")) - res0.T = coinsT - av := res0.V.(*gno.SliceValue).Base.(*gno.ArrayValue) - for i := range av.List { - av.List[i].T = coinT - } - m.PushValue(res0) - }, - ) - pn.DefineNative("GetOrigCaller", - gno.Flds( // params - ), - gno.Flds( // results - "", "Address", - ), - func(m *gno.Machine) { - ctx := m.Context.(ExecContext) - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(ctx.OrigCaller), - ) - addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) - res0.T = addrT - m.PushValue(res0) - }, - ) - pn.DefineNative("CurrentRealm", - gno.Flds( // params - ), - gno.Flds( // results - "", "Realm", - ), - func(m *gno.Machine) { - var ( - ctx = m.Context.(ExecContext) - // Default lastCaller is OrigCaller, the signer of the tx - lastCaller = ctx.OrigCaller - lastPkgPath = "" - ) - - for i := m.NumFrames() - 1; i > 0; i-- { - fr := m.Frames[i] - if fr.LastPackage != nil && fr.LastPackage.IsRealm() { - lastCaller = fr.LastPackage.GetPkgAddr().Bech32() - lastPkgPath = fr.LastPackage.PkgPath - break - } - } - - // Return the result - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(Realm{ - addr: lastCaller, - pkgPath: lastPkgPath, - }), - ) - - realmT := store.GetType(gno.DeclaredTypeID("std", "Realm")) - res0.T = realmT - m.PushValue(res0) - }, - ) - pn.DefineNative("PrevRealm", - gno.Flds( // params - ), - gno.Flds( // results - "", "Realm", - ), - func(m *gno.Machine) { - var ( - ctx = m.Context.(ExecContext) - // Default lastCaller is OrigCaller, the signer of the tx - lastCaller = ctx.OrigCaller - lastPkgPath = "" - ) - - for i := m.NumFrames() - 1; i > 0; i-- { - fr := m.Frames[i] - if fr.LastPackage == nil || !fr.LastPackage.IsRealm() { - // Ignore non-realm frame - continue - } - pkgPath := fr.LastPackage.PkgPath - // The first realm we encounter will be the one calling - // this function; to get the calling realm determine the first frame - // where fr.LastPackage changes. - if lastPkgPath == "" { - lastPkgPath = pkgPath - } else if lastPkgPath == pkgPath { - continue - } else { - lastCaller = fr.LastPackage.GetPkgAddr().Bech32() - lastPkgPath = pkgPath - break - } - } - - // Empty the pkgPath if we return a user - if ctx.OrigCaller == lastCaller { - lastPkgPath = "" - } - - // Return the result - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(Realm{ - addr: lastCaller, - pkgPath: lastPkgPath, - }), - ) - - realmT := store.GetType(gno.DeclaredTypeID("std", "Realm")) - res0.T = realmT - m.PushValue(res0) - }, - ) - pn.DefineNative("GetOrigPkgAddr", - gno.Flds( // params - ), - gno.Flds( // results - "", "Address", - ), - func(m *gno.Machine) { - ctx := m.Context.(ExecContext) - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(ctx.OrigPkgAddr), - ) - addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) - res0.T = addrT - m.PushValue(res0) - }, - ) - pn.DefineNative("GetCallerAt", - gno.Flds( // params - "n", "int", - ), - gno.Flds( // results - "", "Address", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - n := arg0.GetInt() - if n <= 0 { - m.Panic(typedString("GetCallerAt requires positive arg")) - return - } - if n > m.NumFrames() { - // NOTE: the last frame's LastPackage - // is set to the original non-frame - // package, so need this check. - m.Panic(typedString("frame not found")) - return - } - var pkgAddr string - if n == m.NumFrames() { - // This makes it consistent with GetOrigCaller. - ctx := m.Context.(ExecContext) - pkgAddr = string(ctx.OrigCaller) - } else { - pkgAddr = string(m.LastCallFrame(n).LastPackage.GetPkgAddr().Bech32()) - } - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(pkgAddr), - ) - addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) - res0.T = addrT - m.PushValue(res0) - }, - ) - pn.DefineNative("GetBanker", - gno.Flds( // params - "bankerType", "BankerType", - ), - gno.Flds( // results - "", "Banker", - ), - func(m *gno.Machine) { - ctx := m.Context.(ExecContext) - arg0 := m.LastBlock().GetParams1().TV - bankerType := BankerType(arg0.GetUint8()) - banker := ctx.Banker - switch bankerType { - case BankerTypeReadonly: - banker = NewReadonlyBanker(banker) - case BankerTypeOrigSend: - banker = NewOrigSendBanker(banker, ctx.OrigPkgAddr, ctx.OrigSend, ctx.OrigSendSpent) - case BankerTypeRealmSend: - banker = NewRealmSendBanker(banker, ctx.OrigPkgAddr) - case BankerTypeRealmIssue: - banker = banker - default: - panic("should not happen") // defensive - } - rv := reflect.ValueOf(banker) - m.Alloc.AllocateStruct() // defensive; native space not allocated. - m.Alloc.AllocateStructFields(10) // defensive 10; native space not allocated. - - // make gno bankAdapter{rv} - btv := gno.Go2GnoNativeValue(m.Alloc, rv) - bsv := m.Alloc.NewStructWithFields(btv) - bankAdapterType := store.GetType(gno.DeclaredTypeID("std", "bankAdapter")) - res0 := gno.TypedValue{T: bankAdapterType, V: bsv} - m.PushValue(res0) - }, - ) - pn.DefineNative("EncodeBech32", - gno.Flds( // params - "prefix", "string", - "bytes", "[20]byte", - ), - gno.Flds( // results - "addr", "Address", - ), - func(m *gno.Machine) { - arg0, arg1 := m.LastBlock().GetParams2() - prefix := arg0.TV.GetString() - bz := arg1.TV.V.(*gno.ArrayValue).GetReadonlyBytes() - if len(bz) != crypto.AddressSize { - panic("should not happen") - } - b32, err := bech32.ConvertAndEncode(prefix, bz) - if err != nil { - panic(err) // should not happen - } - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(b32), - ) - addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) - res0.T = addrT - m.PushValue(res0) - }, - ) - pn.DefineNative("DecodeBech32", - gno.Flds( // params - "addr", "Address", - ), - gno.Flds( // results - "prefix", "string", - "bytes", "[20]byte", - "ok", "bool", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1() - addr := arg0.TV.GetString() - prefix, bz, err := bech32.Decode(addr) - if err != nil || len(bz) != 20 { - m.PushValue(typedString(m.Alloc.NewString(""))) - m.PushValue(typedByteArray(20, m.Alloc.NewDataArray(20))) - m.PushValue(typedBool(false)) - } else { - m.PushValue(typedString(m.Alloc.NewString(prefix))) - m.PushValue(typedByteArray(20, m.Alloc.NewArrayFromData(bz))) - m.PushValue(typedBool(true)) - } - }, - ) - pn.DefineNative("DerivePkgAddr", - gno.Flds( // params - "pkgPath", "string", - ), - gno.Flds( // results - "addr", "Address", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - pkgPath := arg0.GetString() - pkgAddr := gno.DerivePkgAddr(pkgPath).Bech32() - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(pkgAddr), - ) - addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) - res0.T = addrT - m.PushValue(res0) - }, - ) +func NativeStore(pkgPath string, name gno.Name) func(*gno.Machine) { + for _, nf := range nativeFuncs { + if nf.gnoPkg == pkgPath && name == nf.gnoFunc { + return nf.f + } } -} - -func typedInt32(i32 int32) gno.TypedValue { - tv := gno.TypedValue{T: gno.Int32Type} - tv.SetInt32(i32) - return tv -} - -func typedInt64(i64 int64) gno.TypedValue { - tv := gno.TypedValue{T: gno.Int64Type} - tv.SetInt64(i64) - return tv -} - -func typedUint32(u32 uint32) gno.TypedValue { - tv := gno.TypedValue{T: gno.Uint32Type} - tv.SetUint32(u32) - return tv -} - -func typedUint64(u64 uint64) gno.TypedValue { - tv := gno.TypedValue{T: gno.Uint64Type} - tv.SetUint64(u64) - return tv -} - -func typedFloat32(f32 float32) gno.TypedValue { - tv := gno.TypedValue{T: gno.Float32Type} - tv.SetFloat32(f32) - return tv -} - -func typedFloat64(f64 float64) gno.TypedValue { - tv := gno.TypedValue{T: gno.Float64Type} - tv.SetFloat64(f64) - return tv -} - -func typedString(s gno.StringValue) gno.TypedValue { - tv := gno.TypedValue{T: gno.StringType} - tv.SetString(s) - return tv -} - -func typedBool(b bool) gno.TypedValue { - tv := gno.TypedValue{T: gno.BoolType} - tv.SetBool(b) - return tv -} - -func typedByteArray(ln int, bz *gno.ArrayValue) gno.TypedValue { - if bz != nil && bz.GetLength() != ln { - panic("array length mismatch") - } - tv := gno.TypedValue{T: &gno.ArrayType{Len: ln, Elt: gno.Uint8Type}, V: bz} - return tv -} - -func typedByteSlice(bz *gno.SliceValue) gno.TypedValue { - tv := gno.TypedValue{T: &gno.SliceType{Elt: gno.Uint8Type}, V: bz} - return tv -} - -func typedNil(t gno.Type) gno.TypedValue { - tv := gno.TypedValue{T: t, V: nil} - return tv + return nil } diff --git a/gnovm/stdlibs/strconv/strconv.gno b/gnovm/stdlibs/strconv/strconv.gno index dce62b890e3..63036219d5d 100644 --- a/gnovm/stdlibs/strconv/strconv.gno +++ b/gnovm/stdlibs/strconv/strconv.gno @@ -1,4 +1,11 @@ package strconv -// NOTE: currently these are implemented as native functions. -// See InjectNatives(). +func Itoa(n int) string // injected +func AppendUint(dst []byte, i uint64, base int) []byte // injected +func Atoi(s string) (int, error) // injected +func CanBackquote(s string) bool // injected +func FormatInt(i int64, base int) string // injected +func FormatUint(i uint64, base int) string // injected +func FormatFloat(f float64, fmt byte, prec, bitSize int) string // injected +func Quote(s string) string // injected +func QuoteToASCII(s string) string // injected diff --git a/gnovm/stdlibs/strconv/strconv.go b/gnovm/stdlibs/strconv/strconv.go new file mode 100644 index 00000000000..d94c8c4fb82 --- /dev/null +++ b/gnovm/stdlibs/strconv/strconv.go @@ -0,0 +1,15 @@ +package strconv + +import "strconv" + +func Itoa(n int) string { return strconv.Itoa(n) } +func AppendUint(dst []byte, i uint64, base int) []byte { return strconv.AppendUint(dst, i, base) } +func Atoi(s string) (int, error) { return strconv.Atoi(s) } +func CanBackquote(s string) bool { return strconv.CanBackquote(s) } +func FormatInt(i int64, base int) string { return strconv.FormatInt(i, base) } +func FormatUint(i uint64, base int) string { return strconv.FormatUint(i, base) } +func FormatFloat(f float64, fmt byte, prec, bitSize int) string { + return strconv.FormatFloat(f, fmt, prec, bitSize) +} +func Quote(s string) string { return strconv.Quote(s) } +func QuoteToASCII(r string) string { return strconv.QuoteToASCII(r) } diff --git a/gnovm/stdlibs/time/time.gno b/gnovm/stdlibs/time/time.gno index 7b7e45ba9b6..ceed70452f6 100644 --- a/gnovm/stdlibs/time/time.gno +++ b/gnovm/stdlibs/time/time.gno @@ -80,8 +80,6 @@ package time import ( "errors" - - ios "internal/os" // XXX to access time. // XXX _ "unsafe" // for go:linkname ) @@ -1071,17 +1069,13 @@ func daysSinceEpoch(year int) uint64 { return d } -/* XXX replaced with ios.Now() -// Provided by package runtime. -func now() (sec int64, nsec int32, mono int64) -*/ +func now() (sec int64, nsec int32, mono int64) // injected -// XXX SHIM // runtimeNano returns the current value of the runtime clock in nanoseconds. // //go:linkname runtimeNano runtime.nanotime func runtimeNano() int64 { - _, _, mono := ios.Now() // XXX now() + _, _, mono := now() return mono } @@ -1095,7 +1089,7 @@ var startNano int64 = runtimeNano() - 1 // Now returns the current local time. func Now() Time { - sec, nsec, mono := ios.Now() // XXX now() + sec, nsec, mono := now() mono -= startNano sec += unixToInternal - minWall if uint64(sec)>>33 != 0 { diff --git a/gnovm/stdlibs/time/time.go b/gnovm/stdlibs/time/time.go new file mode 100644 index 00000000000..8c1c768715e --- /dev/null +++ b/gnovm/stdlibs/time/time.go @@ -0,0 +1,17 @@ +package time + +import ( + "time" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/stdlibs/std" +) + +func X_now(m *gno.Machine) (sec int64, nsec int32, mono int64) { + if m == nil || m.Context == nil { + return 0, 0, 0 + } + + ctx := m.Context.(std.ExecContext) + return ctx.Timestamp, int32(ctx.TimestampNano), ctx.Timestamp*int64(time.Second) + ctx.TimestampNano +} diff --git a/gnovm/tests/files/comp3.gno b/gnovm/tests/files/comp3.gno new file mode 100644 index 00000000000..a39788a2110 --- /dev/null +++ b/gnovm/tests/files/comp3.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var x string + y := "Hello" + if x < y { + println("x < y") + } + if y < x { + println("y < x") + } + if x == "" { + println("x empty") + } + if y == "" { + println("y empty") + } +} + +// Output: +// x < y +// x empty diff --git a/gnovm/tests/files/float5_native.gno b/gnovm/tests/files/float5_native.gno index 1050d277605..b2d9228c0bc 100644 --- a/gnovm/tests/files/float5_native.gno +++ b/gnovm/tests/files/float5_native.gno @@ -1,13 +1,13 @@ package main import ( - imath "internal/math" + "math" ) func main() { // test float64 f := float64(0.3) - x := imath.Float64bits(f) + x := math.Float64bits(f) e := uint(40) println(f, x, e, (1 << (64 - e))) diff --git a/gnovm/tests/files/float5_stdlibs.gno b/gnovm/tests/files/float5_stdlibs.gno index 8449ddff820..b3d8cd84713 100644 --- a/gnovm/tests/files/float5_stdlibs.gno +++ b/gnovm/tests/files/float5_stdlibs.gno @@ -2,13 +2,11 @@ package main import ( "math" - - imath "internal/math" ) func main() { - println(math.MaxFloat32, imath.Float32bits(math.MaxFloat32)) - println(math.MaxFloat64, imath.Float64bits(math.MaxFloat64)) + println(math.MaxFloat32, math.Float32bits(math.MaxFloat32)) + println(math.MaxFloat64, math.Float64bits(math.MaxFloat64)) } // Output: diff --git a/gnovm/tests/files/inc.gno b/gnovm/tests/files/inc0.gno similarity index 100% rename from gnovm/tests/files/inc.gno rename to gnovm/tests/files/inc0.gno diff --git a/gnovm/tests/files/inc1.gno b/gnovm/tests/files/inc1.gno new file mode 100644 index 00000000000..f997b3b9a00 --- /dev/null +++ b/gnovm/tests/files/inc1.gno @@ -0,0 +1,15 @@ +package main + +func main() { + var i float64 = 899 + i++ + println(i) + + j := float32(901) + j-- + println(j) +} + +// Output: +// 900 +// 900 diff --git a/gnovm/tests/files/std9.gno b/gnovm/tests/files/std9.gno deleted file mode 100644 index 95ccfb2c8a4..00000000000 --- a/gnovm/tests/files/std9.gno +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import "std" - -func main() { - defer func() { - // assert panic is recoverable - println(recover()) - }() - std.AssertOriginCall() -} - -// Output: -// invalid non-origin call diff --git a/gnovm/tests/files/zrealm0.gno b/gnovm/tests/files/zrealm0.gno index 1b8f37540b3..7578781e503 100644 --- a/gnovm/tests/files/zrealm0.gno +++ b/gnovm/tests/files/zrealm0.gno @@ -56,6 +56,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm1.gno b/gnovm/tests/files/zrealm1.gno index 1dea983a49d..d90c5e8621a 100644 --- a/gnovm/tests/files/zrealm1.gno +++ b/gnovm/tests/files/zrealm1.gno @@ -170,6 +170,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm2.gno b/gnovm/tests/files/zrealm2.gno index bf321c42d31..67ba2f5a768 100644 --- a/gnovm/tests/files/zrealm2.gno +++ b/gnovm/tests/files/zrealm2.gno @@ -173,6 +173,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.3", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -207,6 +209,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm3.gno b/gnovm/tests/files/zrealm3.gno index 4ff8dd1a531..da8a581375c 100644 --- a/gnovm/tests/files/zrealm3.gno +++ b/gnovm/tests/files/zrealm3.gno @@ -172,6 +172,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.2", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -206,6 +208,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm4.gno b/gnovm/tests/files/zrealm4.gno index 2e2fa4e8d09..dc3c48c774b 100644 --- a/gnovm/tests/files/zrealm4.gno +++ b/gnovm/tests/files/zrealm4.gno @@ -114,6 +114,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -148,6 +150,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm5.gno b/gnovm/tests/files/zrealm5.gno index 8ad3e7400b3..e65b089c18d 100644 --- a/gnovm/tests/files/zrealm5.gno +++ b/gnovm/tests/files/zrealm5.gno @@ -185,6 +185,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -219,6 +221,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm6.gno b/gnovm/tests/files/zrealm6.gno index fbe320ad962..20615fa7d39 100644 --- a/gnovm/tests/files/zrealm6.gno +++ b/gnovm/tests/files/zrealm6.gno @@ -257,6 +257,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -291,6 +293,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm7.gno b/gnovm/tests/files/zrealm7.gno index 689d55d3916..9decb0dae10 100644 --- a/gnovm/tests/files/zrealm7.gno +++ b/gnovm/tests/files/zrealm7.gno @@ -329,6 +329,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -363,6 +365,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm_avl0.gno b/gnovm/tests/files/zrealm_avl0.gno index 814e19d6d49..e91788ac8eb 100644 --- a/gnovm/tests/files/zrealm_avl0.gno +++ b/gnovm/tests/files/zrealm_avl0.gno @@ -267,6 +267,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -301,6 +303,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm_avl1.gno b/gnovm/tests/files/zrealm_avl1.gno index 410e9e93601..cdd56a5ad89 100644 --- a/gnovm/tests/files/zrealm_avl1.gno +++ b/gnovm/tests/files/zrealm_avl1.gno @@ -291,6 +291,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -325,6 +327,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm_natbind0.gno b/gnovm/tests/files/zrealm_natbind0.gno new file mode 100644 index 00000000000..60e0d448202 --- /dev/null +++ b/gnovm/tests/files/zrealm_natbind0.gno @@ -0,0 +1,197 @@ +// PKGPATH: gno.land/r/test +package test + +import ( + "std" +) + +var node interface{} + +func init() { + node = std.GetOrigCaller +} + +func main() { + f := node.(func() std.Address) + println(f()) + node = std.DerivePkgAddr + g := node.(func(path string) std.Address) + println(g("x")) +} + +// Output: +// g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm +// g19kt9e22k34ny5jf5plrjdltmws0jc0qqd2cwky + +// Realm: +// switchrealm["gno.land/r/test"] +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ +// "Blank": {}, +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "IsEscaped": true, +// "ModTime": "3", +// "RefCount": "2" +// }, +// "Parent": null, +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "", +// "Line": "0", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Values": [ +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "10", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "main", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "14", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [ +// { +// "Embedded": false, +// "Name": "pkgPath", +// "Tag": "", +// "Type": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// } +// } +// ], +// "Results": [ +// { +// "Embedded": false, +// "Name": "", +// "Tag": "", +// "Type": { +// "@type": "/gno.RefType", +// "ID": "std.Address" +// } +// } +// ] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a7f5397443359ea76c50be82c77f1f893a060925:5" +// }, +// "FileName": "native.gno", +// "IsMethod": false, +// "Name": "DerivePkgAddr", +// "NativeName": "DerivePkgAddr", +// "NativePkg": "std", +// "PkgPath": "std", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "native.gno", +// "Line": "15", +// "Nonce": "0", +// "PkgPath": "std" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [ +// { +// "Embedded": false, +// "Name": "pkgPath", +// "Tag": "", +// "Type": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// } +// } +// ], +// "Results": [ +// { +// "Embedded": false, +// "Name": "", +// "Tag": "", +// "Type": { +// "@type": "/gno.RefType", +// "ID": "std.Address" +// } +// } +// ] +// } +// } +// } +// ] +// } diff --git a/gnovm/tests/files/zrealm_tests0.gno b/gnovm/tests/files/zrealm_tests0.gno index cfb1f08c6f4..4441a9a38c7 100644 --- a/gnovm/tests/files/zrealm_tests0.gno +++ b/gnovm/tests/files/zrealm_tests0.gno @@ -239,13 +239,15 @@ func main() { // "FileName": "tests.gno", // "IsMethod": true, // "Name": "Modify", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", // "BlockNode": null, // "Location": { // "File": "tests.gno", -// "Line": "42", +// "Line": "50", // "Nonce": "0", // "PkgPath": "gno.land/r/demo/tests" // } @@ -295,13 +297,15 @@ func main() { // "FileName": "tests.gno", // "IsMethod": true, // "Name": "Modify", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", // "BlockNode": null, // "Location": { // "File": "tests.gno", -// "Line": "42", +// "Line": "50", // "Nonce": "0", // "PkgPath": "gno.land/r/demo/tests" // } @@ -399,6 +403,8 @@ func main() { // "FileName": "interfaces.gno", // "IsMethod": false, // "Name": "AddStringer", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -463,6 +469,8 @@ func main() { // "FileName": "interfaces.gno", // "IsMethod": false, // "Name": "Render", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -517,6 +525,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "IncCounter", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -561,6 +571,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "Counter", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -615,6 +627,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "CurrentRealmPath", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -659,6 +673,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "AssertOriginCall", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -703,6 +719,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "IsOriginCall", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -734,6 +752,98 @@ func main() { // { // "T": { // "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:4" +// }, +// "FileName": "tests.gno", +// "IsMethod": false, +// "Name": "RSubtestsAssertOriginCall", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/demo/tests", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "tests.gno", +// "Line": "31", +// "Nonce": "0", +// "PkgPath": "gno.land/r/demo/tests" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [ +// { +// "Embedded": false, +// "Name": "", +// "Tag": "", +// "Type": { +// "@type": "/gno.PrimitiveType", +// "value": "4" +// } +// } +// ] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:4" +// }, +// "FileName": "tests.gno", +// "IsMethod": false, +// "Name": "RSubtestsIsOriginCall", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/demo/tests", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "tests.gno", +// "Line": "35", +// "Nonce": "0", +// "PkgPath": "gno.land/r/demo/tests" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [ +// { +// "Embedded": false, +// "Name": "", +// "Tag": "", +// "Type": { +// "@type": "/gno.PrimitiveType", +// "value": "4" +// } +// } +// ] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", // "Params": [ // { // "Embedded": false, @@ -760,13 +870,15 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "ModifyTestRealmObject", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", // "BlockNode": null, // "Location": { // "File": "tests.gno", -// "Line": "38", +// "Line": "46", // "Nonce": "0", // "PkgPath": "gno.land/r/demo/tests" // } @@ -807,13 +919,15 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "InitTestNodes", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", // "BlockNode": null, // "Location": { // "File": "tests.gno", -// "Line": "60", +// "Line": "68", // "Nonce": "0", // "PkgPath": "gno.land/r/demo/tests" // } @@ -841,13 +955,15 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "ModTestNodes", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", // "BlockNode": null, // "Location": { // "File": "tests.gno", -// "Line": "65", +// "Line": "73", // "Nonce": "0", // "PkgPath": "gno.land/r/demo/tests" // } @@ -875,13 +991,15 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "PrintTestNodes", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", // "BlockNode": null, // "Location": { // "File": "tests.gno", -// "Line": "73", +// "Line": "81", // "Nonce": "0", // "PkgPath": "gno.land/r/demo/tests" // } @@ -919,13 +1037,15 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "GetPrevRealm", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", // "BlockNode": null, // "Location": { // "File": "tests.gno", -// "Line": "77", +// "Line": "85", // "Nonce": "0", // "PkgPath": "gno.land/r/demo/tests" // } @@ -973,13 +1093,15 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "GetRSubtestsPrevRealm", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", // "BlockNode": null, // "Location": { // "File": "tests.gno", -// "Line": "81", +// "Line": "89", // "Nonce": "0", // "PkgPath": "gno.land/r/demo/tests" // } @@ -1028,13 +1150,15 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "Exec", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", // "BlockNode": null, // "Location": { // "File": "tests.gno", -// "Line": "85", +// "Line": "93", // "Nonce": "0", // "PkgPath": "gno.land/r/demo/tests" // } diff --git a/gnovm/tests/imports.go b/gnovm/tests/imports.go index fc2820ce00e..75a9c975c6f 100644 --- a/gnovm/tests/imports.go +++ b/gnovm/tests/imports.go @@ -27,6 +27,7 @@ import ( "math/rand" "net" "net/url" + "os" "path/filepath" "reflect" "sort" @@ -34,17 +35,15 @@ import ( "strings" "sync" "sync/atomic" - "testing" "text/template" "time" "unicode/utf8" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" "github.com/gnolang/gno/gnovm/stdlibs" - "github.com/gnolang/gno/tm2/pkg/crypto" + teststdlibs "github.com/gnolang/gno/gnovm/tests/stdlibs" dbm "github.com/gnolang/gno/tm2/pkg/db" osm "github.com/gnolang/gno/tm2/pkg/os" - "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store/dbadapter" "github.com/gnolang/gno/tm2/pkg/store/iavl" stypes "github.com/gnolang/gno/tm2/pkg/store/types" @@ -52,15 +51,16 @@ import ( type importMode uint64 +// Import modes to control the import behaviour of TestStore. const ( + // use stdlibs/* only (except a few exceptions). for stdlibs/* and examples/* testing. ImportModeStdlibsOnly importMode = iota + // use stdlibs/* if present, otherwise use native. used in files/tests, excluded for *_native.go ImportModeStdlibsPreferred + // do not use stdlibs/* if native registered. used in files/tests, excluded for *_stdlibs.go ImportModeNativePreferred ) -// ImportModeStdlibsOnly: use stdlibs/* only (except a few exceptions). for stdlibs/* and examples/* testing. -// ImportModeStdlibsPreferred: use stdlibs/* if present, otherwise use native. for files/tests2/*. -// ImportModeNativePreferred: do not use stdlibs/* if native registered. for files/tests/*. // NOTE: this isn't safe, should only be used for testing. func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Writer, mode importMode) (store gno.Store) { getPackage := func(pkgPath string) (pn *gno.PackageNode, pv *gno.PackageValue) { @@ -94,23 +94,9 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri // if stdlibs package is preferred , try to load it first. if mode == ImportModeStdlibsOnly || mode == ImportModeStdlibsPreferred { - stdlibPath := filepath.Join(rootDir, "gnovm", "stdlibs", pkgPath) - if osm.DirExists(stdlibPath) { - memPkg := gno.ReadMemPackage(stdlibPath, pkgPath) - if !memPkg.IsEmpty() { - m2 := gno.NewMachineWithOptions(gno.MachineOptions{ - // NOTE: see also pkgs/sdk/vm/builtins.go - // XXX: why does this fail when just pkgPath? - PkgPath: "gno.land/r/stdlibs/" + pkgPath, - Output: stdout, - Store: store, - }) - save := pkgPath != "testing" // never save the "testing" package - return m2.RunMemPackage(memPkg, save) - } - - // There is no package there, but maybe we have a - // native counterpart below. + pn, pv = loadStdlib(rootDir, pkgPath, store, stdout) + if pn != nil { + return } } @@ -121,12 +107,10 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri pkgPath == "crypto/rand" || pkgPath == "crypto/md5" || pkgPath == "crypto/sha1" || - pkgPath == "encoding/base64" || pkgPath == "encoding/binary" || pkgPath == "encoding/json" || pkgPath == "encoding/xml" || pkgPath == "internal/os_test" || - pkgPath == "math" || pkgPath == "math/big" || pkgPath == "math/rand" || mode == ImportModeStdlibsPreferred || @@ -216,17 +200,6 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri pkg.DefineGoNativeType(reflect.TypeOf(net.TCPAddr{})) pkg.DefineGoNativeValue("IPv4", net.IPv4) return pkg, pkg.NewPackage() - case "net/http": - // XXX UNSAFE - // There's no reason why we can't replace these with safer alternatives. - panic("just say gno") - /* - pkg := gno.NewPackageNode("http", pkgPath, nil) - pkg.DefineGoNativeType(reflect.TypeOf(http.Request{})) - pkg.DefineGoNativeValue("DefaultClient", http.DefaultClient) - pkg.DefineGoNativeType(reflect.TypeOf(http.Client{})) - return pkg, pkg.NewPackage() - */ case "net/url": pkg := gno.NewPackageNode("url", pkgPath, nil) pkg.DefineGoNativeType(reflect.TypeOf(url.Values{})) @@ -278,6 +251,8 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri pkg.DefineGoNativeValue("Abs", math.Abs) pkg.DefineGoNativeValue("Cos", math.Cos) pkg.DefineGoNativeValue("Pi", math.Pi) + pkg.DefineGoNativeValue("Float64bits", math.Float64bits) + pkg.DefineGoNativeValue("Pi", math.Pi) pkg.DefineGoNativeValue("MaxFloat32", math.MaxFloat32) pkg.DefineGoNativeValue("MaxFloat64", math.MaxFloat64) return pkg, pkg.NewPackage() @@ -389,25 +364,6 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri pkg := gno.NewPackageNode("fnv", pkgPath, nil) pkg.DefineGoNativeValue("New32a", fnv.New32a) return pkg, pkg.NewPackage() - /* XXX support somehow for speed. for now, generic implemented in stdlibs. - case "internal/bytealg": - pkg := gno.NewPackageNode("bytealg", pkgPath, nil) - pkg.DefineGoNativeValue("Compare", bytealg.Compare) - pkg.DefineGoNativeValue("CountString", bytealg.CountString) - pkg.DefineGoNativeValue("Cutover", bytealg.Cutover) - pkg.DefineGoNativeValue("Equal", bytealg.Equal) - pkg.DefineGoNativeValue("HashStr", bytealg.HashStr) - pkg.DefineGoNativeValue("HashStrBytes", bytealg.HashStrBytes) - pkg.DefineGoNativeValue("HashStrRev", bytealg.HashStrRev) - pkg.DefineGoNativeValue("HashStrRevBytes", bytealg.HashStrRevBytes) - pkg.DefineGoNativeValue("Index", bytealg.Index) - pkg.DefineGoNativeValue("IndexByte", bytealg.IndexByte) - pkg.DefineGoNativeValue("IndexByteString", bytealg.IndexByteString) - pkg.DefineGoNativeValue("IndexRabinKarp", bytealg.IndexRabinKarp) - pkg.DefineGoNativeValue("IndexRabinKarpBytes", bytealg.IndexRabinKarpBytes) - pkg.DefineGoNativeValue("IndexString", bytealg.IndexString) - return pkg, pkg.NewPackage() - */ default: // continue on... } @@ -415,19 +371,8 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri // if native package is preferred, try to load stdlibs/* as backup. if mode == ImportModeNativePreferred { - stdlibPath := filepath.Join(rootDir, "gnovm", "stdlibs", pkgPath) - if osm.DirExists(stdlibPath) { - memPkg := gno.ReadMemPackage(stdlibPath, pkgPath) - if memPkg.IsEmpty() { - panic(fmt.Sprintf("found an empty package %q", pkgPath)) - } - - m2 := gno.NewMachineWithOptions(gno.MachineOptions{ - PkgPath: "test", - Output: stdout, - Store: store, - }) - pn, pv = m2.RunMemPackage(memPkg, true) + pn, pv = loadStdlib(rootDir, pkgPath, store, stdout) + if pn != nil { return } } @@ -456,6 +401,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri iavlStore := iavl.StoreConstructor(db, stypes.StoreOptions{}) store = gno.NewStore(nil, baseStore, iavlStore) store.SetPackageGetter(getPackage) + store.SetNativeStore(teststdlibs.NativeStore) store.SetPackageInjector(testPackageInjector) store.SetStrictGo2GnoMapping(false) // native mappings @@ -463,30 +409,49 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri return } -//---------------------------------------- -// testInjectNatives -// analogous to stdlibs.InjectNatives, but with -// native methods suitable for the testing environment. - -func testPackageInjector(store gno.Store, pn *gno.PackageNode) { - // Also inject stdlibs native functions. - stdlibs.InjectPackage(store, pn) - isOriginCall := func(m *gno.Machine) bool { - tname := m.Frames[0].Func.Name - switch tname { - case "main": // test is a _filetest - return len(m.Frames) == 3 - case "runtest": // test is a _test - return len(m.Frames) == 7 +func loadStdlib(rootDir, pkgPath string, store gno.Store, stdout io.Writer) (*gno.PackageNode, *gno.PackageValue) { + dirs := [...]string{ + // override path. definitions here should take precedence. + filepath.Join(rootDir, "gnovm", "tests", "stdlibs", pkgPath), + // normal stdlib path. + filepath.Join(rootDir, "gnovm", "stdlibs", pkgPath), + } + files := make([]string, 0, 32) // pre-alloc 32 as a likely high number of files + for _, path := range dirs { + dl, err := os.ReadDir(path) + if err != nil { + if os.IsNotExist(err) { + continue + } + panic(fmt.Errorf("could not access dir %q: %w", path, err)) } - // support init() in _filetest - // XXX do we need to distinguish from 'runtest'/_test? - // XXX pretty hacky even if not. - if strings.HasPrefix(string(tname), "init.") { - return len(m.Frames) == 3 + + for _, f := range dl { + // NOTE: RunMemPackage has other rules; those should be mostly useful + // for on-chain packages (ie. include README and gno.mod). + if !f.IsDir() && strings.HasSuffix(f.Name(), ".gno") { + files = append(files, filepath.Join(path, f.Name())) + } } - panic("unable to determine if test is a _test or a _filetest") } + if len(files) == 0 { + return nil, nil + } + + memPkg := gno.ReadMemPackageFromList(files, pkgPath) + m2 := gno.NewMachineWithOptions(gno.MachineOptions{ + // NOTE: see also pkgs/sdk/vm/builtins.go + // Needs PkgPath != its name because TestStore.getPackage is the package + // getter for the store, which calls loadStdlib, so it would be recursively called. + PkgPath: "stdlibload", + Output: stdout, + Store: store, + }) + save := pkgPath != "testing" // never save the "testing" package + return m2.RunMemPackageWithOverrides(memPkg, save) +} + +func testPackageInjector(store gno.Store, pn *gno.PackageNode) { // Test specific injections: switch pn.PkgPath { case "strconv": @@ -494,203 +459,6 @@ func testPackageInjector(store gno.Store, pn *gno.PackageNode) { // from stdlibs.InjectNatives. pn.DefineGoNativeType(reflect.TypeOf(strconv.NumError{})) pn.DefineGoNativeValue("ParseInt", strconv.ParseInt) - case "std": - // NOTE: some of these are overrides. - // Also see stdlibs/InjectPackage. - pn.DefineNativeOverride("AssertOriginCall", - /* - gno.Flds( // params - ), - gno.Flds( // results - ), - */ - func(m *gno.Machine) { - if !isOriginCall(m) { - m.Panic(typedString("invalid non-origin call")) - return - } - }, - ) - pn.DefineNativeOverride("IsOriginCall", - /* - gno.Flds( // params - ), - gno.Flds( // results - "isOrigin", "bool", - ), - */ - func(m *gno.Machine) { - res0 := gno.TypedValue{T: gno.BoolType} - res0.SetBool(isOriginCall(m)) - m.PushValue(res0) - }, - ) - pn.DefineNativeOverride("GetCallerAt", - /* - gno.Flds( // params - "n", "int", - ), - gno.Flds( // results - "", "Address", - ), - */ - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - n := arg0.GetInt() - if n <= 0 { - m.Panic(typedString("GetCallerAt requires positive arg")) - return - } - if n > m.NumFrames()-1 { - // NOTE: the last frame's LastPackage - // is set to the original non-frame - // package, so need this check. - m.Panic(typedString("frame not found")) - return - } - var pkgAddr string - if n == m.NumFrames()-1 { - // This makes it consistent with GetOrigCaller and TestSetOrigCaller. - ctx := m.Context.(stdlibs.ExecContext) - pkgAddr = string(ctx.OrigCaller) - } else { - pkgAddr = string(m.LastCallFrame(n).LastPackage.GetPkgAddr().Bech32()) - } - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(pkgAddr), - ) - addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) - res0.T = addrT - m.PushValue(res0) - }, - ) - pn.DefineNative("TestSetOrigCaller", - gno.Flds( // params - "", "Address", - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - addr := arg0.GetString() - // overwrite context - ctx := m.Context.(stdlibs.ExecContext) - ctx.OrigCaller = crypto.Bech32Address(addr) - m.Context = ctx - }, - ) - pn.DefineNative("TestSetOrigPkgAddr", - gno.Flds( // params - "", "Address", - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - addr := crypto.Bech32Address(arg0.GetString()) - // overwrite context - ctx := m.Context.(stdlibs.ExecContext) - ctx.OrigPkgAddr = addr - m.Context = ctx - }, - ) - pn.DefineNative("TestSetOrigSend", - gno.Flds( // params - "sent", "Coins", - "spent", "Coins", - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - arg0, arg1 := m.LastBlock().GetParams2() - var sent std.Coins - rvSent := reflect.ValueOf(&sent).Elem() - gno.Gno2GoValue(arg0.TV, rvSent) - sent = rvSent.Interface().(std.Coins) // needed? - var spent std.Coins - rvSpent := reflect.ValueOf(&spent).Elem() - gno.Gno2GoValue(arg1.TV, rvSpent) - spent = rvSpent.Interface().(std.Coins) // needed? - // overwrite context. - ctx := m.Context.(stdlibs.ExecContext) - ctx.OrigSend = sent - ctx.OrigSendSpent = &spent - m.Context = ctx - }, - ) - pn.DefineNative("TestIssueCoins", - gno.Flds( // params - "addr", "Address", - "coins", "Coins", - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - arg0, arg1 := m.LastBlock().GetParams2() - addr := crypto.Bech32Address(arg0.TV.GetString()) - var coins std.Coins - rvCoins := reflect.ValueOf(&coins).Elem() - gno.Gno2GoValue(arg1.TV, rvCoins) - coins = rvCoins.Interface().(std.Coins) // needed? - // overwrite context. - ctx := m.Context.(stdlibs.ExecContext) - banker := ctx.Banker - for _, coin := range coins { - banker.IssueCoin(addr, coin.Denom, coin.Amount) - } - }, - ) - pn.DefineNative("TestCurrentRealm", - gno.Flds( // params - ), - gno.Flds( // results - "realm", "string", - ), - func(m *gno.Machine) { - rlmpath := m.Realm.Path - m.PushValue(typedString(rlmpath)) - }, - ) - pn.DefineNative("TestSkipHeights", - gno.Flds( // params - "count", "int64", - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - count := arg0.GetInt64() - - ctx := m.Context.(stdlibs.ExecContext) - ctx.Height += count - m.Context = ctx - }, - ) - // TODO: move elsewhere. - pn.DefineNative("ClearStoreCache", - gno.Flds( // params - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - if gno.IsDebug() && testing.Verbose() { - store.Print() - fmt.Println("========================================") - fmt.Println("CLEAR CACHE (RUNTIME)") - fmt.Println("========================================") - } - m.Store.ClearCache() - m.PreprocessAllFilesAndSaveBlockNodes() - if gno.IsDebug() && testing.Verbose() { - store.Print() - fmt.Println("========================================") - fmt.Println("CLEAR CACHE DONE") - fmt.Println("========================================") - } - }, - ) } } diff --git a/gnovm/tests/stdlibs/README.md b/gnovm/tests/stdlibs/README.md new file mode 100644 index 00000000000..16d5d171342 --- /dev/null +++ b/gnovm/tests/stdlibs/README.md @@ -0,0 +1,6 @@ +# tests/stdlibs + +This directory contains test-specific standard libraries. These are only +available when testing gno code in `_test.gno` and `_filetest.gno` files. +Re-declarations of functions already existing override the definitions of the +normal stdlibs directory. diff --git a/gnovm/tests/stdlibs/native.go b/gnovm/tests/stdlibs/native.go new file mode 100644 index 00000000000..9fdc558e6c1 --- /dev/null +++ b/gnovm/tests/stdlibs/native.go @@ -0,0 +1,196 @@ +// This file is autogenerated from the genstd tool (@/misc/genstd); do not edit. +// To regenerate it, run `go generate` from this directory. + +package stdlibs + +import ( + "reflect" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + testlibs_std "github.com/gnolang/gno/gnovm/tests/stdlibs/std" + tm2_crypto "github.com/gnolang/gno/tm2/pkg/crypto" + tm2_std "github.com/gnolang/gno/tm2/pkg/std" +) + +type nativeFunc struct { + gnoPkg string + gnoFunc gno.Name + params []gno.FieldTypeExpr + results []gno.FieldTypeExpr + f func(m *gno.Machine) +} + +var nativeFuncs = [...]nativeFunc{ + { + "std", + "TestCurrentRealm", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + r0 := testlibs_std.TestCurrentRealm( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "TestSkipHeights", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("int64")}, + }, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 int64 + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + testlibs_std.TestSkipHeights( + m, + p0) + }, + }, + { + "std", + "ClearStoreCache", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + testlibs_std.ClearStoreCache( + m, + ) + }, + }, + { + "std", + "GetCallerAt", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Address")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 int + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := testlibs_std.GetCallerAt( + m, + p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "TestSetOrigCaller", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("Address")}, + }, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 tm2_crypto.Bech32Address + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + testlibs_std.TestSetOrigCaller( + m, + p0) + }, + }, + { + "std", + "TestSetOrigPkgAddr", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("Address")}, + }, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 tm2_crypto.Bech32Address + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + testlibs_std.TestSetOrigPkgAddr( + m, + p0) + }, + }, + { + "std", + "TestSetOrigSend", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("Coins")}, + {Name: gno.N("p1"), Type: gno.X("Coins")}, + }, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 tm2_std.Coins + rp0 = reflect.ValueOf(&p0).Elem() + p1 tm2_std.Coins + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + testlibs_std.TestSetOrigSend( + m, + p0, p1) + }, + }, + { + "std", + "TestIssueCoins", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("Address")}, + {Name: gno.N("p1"), Type: gno.X("Coins")}, + }, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 tm2_crypto.Bech32Address + rp0 = reflect.ValueOf(&p0).Elem() + p1 tm2_std.Coins + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + testlibs_std.TestIssueCoins( + m, + p0, p1) + }, + }, +} diff --git a/gnovm/tests/stdlibs/std/std.gno b/gnovm/tests/stdlibs/std/std.gno new file mode 100644 index 00000000000..1e91be3c6c0 --- /dev/null +++ b/gnovm/tests/stdlibs/std/std.gno @@ -0,0 +1,10 @@ +package std + +func TestCurrentRealm() string // injected +func TestSkipHeights(count int64) // injected +func ClearStoreCache() // injected +func GetCallerAt(n int) Address // injected +func TestSetOrigCaller(addr Address) // injected +func TestSetOrigPkgAddr(addr Address) // injected +func TestSetOrigSend(sent, spent Coins) // injected +func TestIssueCoins(addr Address, coins Coins) // injected diff --git a/gnovm/tests/stdlibs/std/std.go b/gnovm/tests/stdlibs/std/std.go new file mode 100644 index 00000000000..41fe281c66b --- /dev/null +++ b/gnovm/tests/stdlibs/std/std.go @@ -0,0 +1,92 @@ +package std + +import ( + "fmt" + "testing" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/stdlibs" + "github.com/gnolang/gno/gnovm/stdlibs/std" + "github.com/gnolang/gno/tm2/pkg/crypto" + tm2std "github.com/gnolang/gno/tm2/pkg/std" +) + +func typedString(s gno.StringValue) gno.TypedValue { + tv := gno.TypedValue{T: gno.StringType} + tv.SetString(s) + return tv +} + +func TestCurrentRealm(m *gno.Machine) string { + return m.Realm.Path +} + +func TestSkipHeights(m *gno.Machine, count int64) { + ctx := m.Context.(std.ExecContext) + ctx.Height += count + m.Context = ctx +} + +func ClearStoreCache(m *gno.Machine) { + if gno.IsDebug() && testing.Verbose() { + m.Store.Print() + fmt.Println("========================================") + fmt.Println("CLEAR CACHE (RUNTIME)") + fmt.Println("========================================") + } + m.Store.ClearCache() + m.PreprocessAllFilesAndSaveBlockNodes() + if gno.IsDebug() && testing.Verbose() { + m.Store.Print() + fmt.Println("========================================") + fmt.Println("CLEAR CACHE DONE") + fmt.Println("========================================") + } +} + +func GetCallerAt(m *gno.Machine, n int) crypto.Bech32Address { + if n <= 0 { + m.Panic(typedString("GetCallerAt requires positive arg")) + return "" + } + if n > m.NumFrames()-1 { + // NOTE: the last frame's LastPackage + // is set to the original non-frame + // package, so need this check. + m.Panic(typedString("frame not found")) + return "" + } + if n == m.NumFrames()-1 { + // This makes it consistent with GetOrigCaller and TestSetOrigCaller. + ctx := m.Context.(stdlibs.ExecContext) + return ctx.OrigCaller + } + return m.LastCallFrame(n).LastPackage.GetPkgAddr().Bech32() +} + +func TestSetOrigCaller(m *gno.Machine, addr crypto.Bech32Address) { + ctx := m.Context.(std.ExecContext) + ctx.OrigCaller = addr + m.Context = ctx +} + +func TestSetOrigPkgAddr(m *gno.Machine, addr crypto.Bech32Address) { + ctx := m.Context.(stdlibs.ExecContext) + ctx.OrigPkgAddr = addr + m.Context = ctx +} + +func TestSetOrigSend(m *gno.Machine, sent, spent tm2std.Coins) { + ctx := m.Context.(stdlibs.ExecContext) + ctx.OrigSend = sent + ctx.OrigSendSpent = &spent + m.Context = ctx +} + +func TestIssueCoins(m *gno.Machine, addr crypto.Bech32Address, coins tm2std.Coins) { + ctx := m.Context.(stdlibs.ExecContext) + banker := ctx.Banker + for _, coin := range coins { + banker.IssueCoin(addr, coin.Denom, coin.Amount) + } +} diff --git a/gnovm/tests/stdlibs/stdlibs.go b/gnovm/tests/stdlibs/stdlibs.go new file mode 100644 index 00000000000..b0a1050af41 --- /dev/null +++ b/gnovm/tests/stdlibs/stdlibs.go @@ -0,0 +1,18 @@ +// Package stdlibs provides supplemental stdlibs for the testing environment. +package stdlibs + +import ( + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/stdlibs" +) + +//go:generate go run github.com/gnolang/gno/misc/genstd + +func NativeStore(pkgPath string, name gno.Name) func(*gno.Machine) { + for _, nf := range nativeFuncs { + if nf.gnoPkg == pkgPath && name == nf.gnoFunc { + return nf.f + } + } + return stdlibs.NativeStore(pkgPath, name) +} diff --git a/misc/Makefile b/misc/Makefile new file mode 100644 index 00000000000..84acc40e387 --- /dev/null +++ b/misc/Makefile @@ -0,0 +1,28 @@ +.PHONY: help +help: + @echo "Available make commands:" + @cat Makefile | grep '^[a-z][^:]*:' | cut -d: -f1 | sort | sed 's/^/ /' + +rundep=go run -modfile ./devdeps/go.mod + +######################################## +# Dev tools +.PHONY: lint +lint: + $(rundep) github.com/golangci/golangci-lint/cmd/golangci-lint run --config ../.github/golangci.yml ./... + +.PHONY: fmt +GOFMT_FLAGS ?= -w +fmt: + $(rundep) mvdan.cc/gofumpt $(GOFMT_FLAGS) . + +######################################## +# Test suite +.PHONY: test +test: _test.genstd + +GOTEST_FLAGS ?= -v -p 1 -timeout=30m + +.PHONY: _test.genstd +_test.genstd: + go test ./genstd/... $(GOTEST_FLAGS) diff --git a/misc/docker-integration/integration_test.go b/misc/docker-integration/integration_test.go index 6ae0ea1d36e..cf3f09a0401 100644 --- a/misc/docker-integration/integration_test.go +++ b/misc/docker-integration/integration_test.go @@ -149,6 +149,7 @@ func startGnoland(t *testing.T) { "docker", "run", "-d", "--name", gnolandContainerName, + "-e", "GNOROOT=/opt/gno/src", "-w", "/opt/gno/src/gno.land", "gno:integration", "gnoland", diff --git a/misc/genstd/exprstring.go b/misc/genstd/exprstring.go new file mode 100644 index 00000000000..a93d7afbbb1 --- /dev/null +++ b/misc/genstd/exprstring.go @@ -0,0 +1,289 @@ +// Forked from go/types (go 1.20.3) to implement support for *linkedIdent. + +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements printing of expressions. + +package main + +import ( + "bytes" + "fmt" + "go/ast" + "go/types" +) + +const ( + printerModeGoQualified = iota + printerModeGnoType +) + +type exprPrinter struct { + mode int +} + +// ExprString returns the (possibly shortened) string representation for x. +// Shortened representations are suitable for user interfaces but may not +// necessarily follow Go syntax. +// +// ExprString is identical to [types.ExprString] with the difference that it +// supports *linkedIdent. +func (ep *exprPrinter) ExprString(x ast.Expr) string { + var buf bytes.Buffer + ep.WriteExpr(&buf, x) + return buf.String() +} + +// WriteExpr writes the (possibly shortened) string representation for x to buf. +// Shortened representations are suitable for user interfaces but may not +// necessarily follow Go syntax. +// +// WriteExpr is identical to [types.WriteExpr] with the difference that it +// supports *linkedIdent. +func (ep *exprPrinter) WriteExpr(buf *bytes.Buffer, x ast.Expr) { + // The AST preserves source-level parentheses so there is + // no need to introduce them here to correct for different + // operator precedences. (This assumes that the AST was + // generated by a Go parser.) + + switch x := x.(type) { + default: + // fallback to go original -- for all non-recursive ast.Expr types + types.WriteExpr(buf, x) + + case *linkedIdent: + switch ep.mode { + case printerModeGoQualified: + n := pkgNameFromPath(x.lt.goPackage) + buf.WriteString(n) + buf.WriteByte('.') + buf.WriteString(x.lt.goName) + case printerModeGnoType: + buf.WriteString(x.lt.gnoName) + default: + panic(fmt.Errorf("invalid mode %d", ep.mode)) + } + + case *ast.Ellipsis: + buf.WriteString("...") + if x.Elt != nil { + ep.WriteExpr(buf, x.Elt) + } + + case *ast.FuncLit: + buf.WriteByte('(') + ep.WriteExpr(buf, x.Type) + buf.WriteString(" literal)") // shortened + + case *ast.CompositeLit: + ep.WriteExpr(buf, x.Type) + buf.WriteByte('{') + if len(x.Elts) > 0 { + buf.WriteString("…") + } + buf.WriteByte('}') + + case *ast.ParenExpr: + buf.WriteByte('(') + ep.WriteExpr(buf, x.X) + buf.WriteByte(')') + + case *ast.SelectorExpr: + ep.WriteExpr(buf, x.X) + buf.WriteByte('.') + buf.WriteString(x.Sel.Name) + + case *ast.IndexExpr, *ast.IndexListExpr: + ix := tpUnpackIndexExpr(x) + ep.WriteExpr(buf, ix.X) + buf.WriteByte('[') + ep.writeExprList(buf, ix.Indices) + buf.WriteByte(']') + + case *ast.SliceExpr: + ep.WriteExpr(buf, x.X) + buf.WriteByte('[') + if x.Low != nil { + ep.WriteExpr(buf, x.Low) + } + buf.WriteByte(':') + if x.High != nil { + ep.WriteExpr(buf, x.High) + } + if x.Slice3 { + buf.WriteByte(':') + if x.Max != nil { + ep.WriteExpr(buf, x.Max) + } + } + buf.WriteByte(']') + + case *ast.TypeAssertExpr: + ep.WriteExpr(buf, x.X) + buf.WriteString(".(") + ep.WriteExpr(buf, x.Type) + buf.WriteByte(')') + + case *ast.CallExpr: + ep.WriteExpr(buf, x.Fun) + buf.WriteByte('(') + ep.writeExprList(buf, x.Args) + if x.Ellipsis.IsValid() { + buf.WriteString("...") + } + buf.WriteByte(')') + + case *ast.StarExpr: + buf.WriteByte('*') + ep.WriteExpr(buf, x.X) + + case *ast.UnaryExpr: + buf.WriteString(x.Op.String()) + ep.WriteExpr(buf, x.X) + + case *ast.BinaryExpr: + ep.WriteExpr(buf, x.X) + buf.WriteByte(' ') + buf.WriteString(x.Op.String()) + buf.WriteByte(' ') + ep.WriteExpr(buf, x.Y) + + case *ast.ArrayType: + buf.WriteByte('[') + if x.Len != nil { + ep.WriteExpr(buf, x.Len) + } + buf.WriteByte(']') + ep.WriteExpr(buf, x.Elt) + + case *ast.StructType: + buf.WriteString("struct{") + ep.writeFieldList(buf, x.Fields.List, "; ", false) + buf.WriteByte('}') + + case *ast.FuncType: + buf.WriteString("func") + ep.writeSigExpr(buf, x) + + case *ast.InterfaceType: + buf.WriteString("interface{") + ep.writeFieldList(buf, x.Methods.List, "; ", true) + buf.WriteByte('}') + + case *ast.MapType: + buf.WriteString("map[") + ep.WriteExpr(buf, x.Key) + buf.WriteByte(']') + ep.WriteExpr(buf, x.Value) + + case *ast.ChanType: + var s string + switch x.Dir { + case ast.SEND: + s = "chan<- " + case ast.RECV: + s = "<-chan " + default: + s = "chan " + } + buf.WriteString(s) + ep.WriteExpr(buf, x.Value) + } +} + +func (ep *exprPrinter) writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) { + buf.WriteByte('(') + ep.writeFieldList(buf, sig.Params.List, ", ", false) + buf.WriteByte(')') + + res := sig.Results + n := res.NumFields() + if n == 0 { + // no result + return + } + + buf.WriteByte(' ') + if n == 1 && len(res.List[0].Names) == 0 { + // single unnamed result + ep.WriteExpr(buf, res.List[0].Type) + return + } + + // multiple or named result(s) + buf.WriteByte('(') + ep.writeFieldList(buf, res.List, ", ", false) + buf.WriteByte(')') +} + +func (ep *exprPrinter) writeFieldList(buf *bytes.Buffer, list []*ast.Field, sep string, iface bool) { + for i, f := range list { + if i > 0 { + buf.WriteString(sep) + } + + // field list names + ep.writeIdentList(buf, f.Names) + + // types of interface methods consist of signatures only + if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface { + ep.writeSigExpr(buf, sig) + continue + } + + // named fields are separated with a blank from the field type + if len(f.Names) > 0 { + buf.WriteByte(' ') + } + + ep.WriteExpr(buf, f.Type) + + // ignore tag + } +} + +func (ep *exprPrinter) writeIdentList(buf *bytes.Buffer, list []*ast.Ident) { + for i, x := range list { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(x.Name) + } +} + +func (ep *exprPrinter) writeExprList(buf *bytes.Buffer, list []ast.Expr) { + for i, x := range list { + if i > 0 { + buf.WriteString(", ") + } + ep.WriteExpr(buf, x) + } +} + +// The following are copied from go/internal/typeparams. +// We cannot use the original directly as it comes from an "internal" package. + +// tpIndexExpr wraps an ast.IndexExpr or ast.IndexListExpr. +// +// Orig holds the original ast.Expr from which this IndexExpr was derived. +type tpIndexExpr struct { + Orig ast.Expr // the wrapped expr, which may be distinct from the IndexListExpr below. + *ast.IndexListExpr +} + +func tpUnpackIndexExpr(n ast.Node) *tpIndexExpr { + switch e := n.(type) { + case *ast.IndexExpr: + return &tpIndexExpr{e, &ast.IndexListExpr{ + X: e.X, + Lbrack: e.Lbrack, + Indices: []ast.Expr{e.Index}, + Rbrack: e.Rbrack, + }} + case *ast.IndexListExpr: + return &tpIndexExpr{e, e} + } + return nil +} diff --git a/misc/genstd/genstd.go b/misc/genstd/genstd.go new file mode 100644 index 00000000000..318a63e5ee8 --- /dev/null +++ b/misc/genstd/genstd.go @@ -0,0 +1,210 @@ +// Command genstd provides static code generation for standard library native +// bindings. +package main + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "io/fs" + "os" + "path/filepath" + "strconv" + "strings" + "text/template" + + _ "embed" +) + +func main() { + path := "." + if len(os.Args) > 1 { + path = os.Args[1] + } + if err := _main(path); err != nil { + fmt.Fprintf(os.Stderr, "%+v\n", err) + os.Exit(1) + } +} + +func _main(stdlibsPath string) error { + stdlibsPath = filepath.Clean(stdlibsPath) + if s, err := os.Stat(stdlibsPath); err != nil { + return err + } else if !s.IsDir() { + return fmt.Errorf("not a directory: %q", stdlibsPath) + } + + // Gather data about each package, getting functions of interest + // (gno bodyless + go exported). + pkgs, err := walkStdlibs(stdlibsPath) + if err != nil { + return err + } + + // Link up each Gno function with its matching Go function. + mappings := linkFunctions(pkgs) + + // Create generated file. + f, err := os.Create("native.go") + if err != nil { + return fmt.Errorf("create native.go: %w", err) + } + defer f.Close() + + // Execute template. + td := &tplData{ + Mappings: mappings, + } + if err := tpl.Execute(f, td); err != nil { + return fmt.Errorf("execute template: %w", err) + } + if err := f.Close(); err != nil { + return err + } + + // gofumpt doesn't do "import fixing" like goimports: + // https://github.com/mvdan/gofumpt#frequently-asked-questions + if err := runTool("golang.org/x/tools/cmd/goimports"); err != nil { + return err + } + return runTool("mvdan.cc/gofumpt") +} + +type pkgData struct { + importPath string + fsDir string + gnoBodyless []funcDecl + goExported []funcDecl +} + +type funcDecl struct { + *ast.FuncDecl + imports []*ast.ImportSpec +} + +func addImports(fds []*ast.FuncDecl, imports []*ast.ImportSpec) []funcDecl { + r := make([]funcDecl, len(fds)) + for i, fd := range fds { + r[i] = funcDecl{fd, imports} + } + return r +} + +// walkStdlibs does a BFS walk through the given directory, expected to be a +// "stdlib" directory, parsing and keeping track of Go and Gno functions of +// interest. +func walkStdlibs(stdlibsPath string) ([]*pkgData, error) { + pkgs := make([]*pkgData, 0, 64) + err := filepath.WalkDir(stdlibsPath, func(fpath string, d fs.DirEntry, err error) error { + // skip dirs and top-level directory. + if d.IsDir() || filepath.Dir(fpath) == stdlibsPath { + return nil + } + + // skip non-source and test files. + ext := filepath.Ext(fpath) + noExt := fpath[:len(fpath)-len(ext)] + if (ext != ".go" && ext != ".gno") || strings.HasSuffix(noExt, "_test") { + return nil + } + + dir := filepath.Dir(fpath) + var pkg *pkgData + // because of bfs, we know that if we've already been in this directory + // in a previous file, it must be in the last entry of pkgs. + if len(pkgs) == 0 || pkgs[len(pkgs)-1].fsDir != dir { + pkg = &pkgData{ + importPath: strings.ReplaceAll(strings.TrimPrefix(dir, stdlibsPath+"/"), string(filepath.Separator), "/"), + fsDir: dir, + } + pkgs = append(pkgs, pkg) + } else { + pkg = pkgs[len(pkgs)-1] + } + fs := token.NewFileSet() + f, err := parser.ParseFile(fs, fpath, nil, parser.SkipObjectResolution) + if err != nil { + return err + } + if ext == ".go" { + // keep track of exported function declarations. + // warn about all exported type, const and var declarations. + if exp := filterExported(f); len(exp) > 0 { + pkg.goExported = append(pkg.goExported, addImports(exp, f.Imports)...) + } + } else if bd := filterBodylessFuncDecls(f); len(bd) > 0 { + // gno file -- keep track of function declarations without body. + pkg.gnoBodyless = append(pkg.gnoBodyless, addImports(bd, f.Imports)...) + } + return nil + }) + return pkgs, err +} + +// filterBodylessFuncDecls returns the function declarations in the given file +// which don't contain a body. +func filterBodylessFuncDecls(f *ast.File) (bodyless []*ast.FuncDecl) { + for _, decl := range f.Decls { + fd, ok := decl.(*ast.FuncDecl) + if !ok || fd.Body != nil { + continue + } + bodyless = append(bodyless, fd) + } + return +} + +// filterExported returns the exported function declarations of the given file. +func filterExported(f *ast.File) (exported []*ast.FuncDecl) { + for _, decl := range f.Decls { + switch d := decl.(type) { + case *ast.GenDecl: + // TODO: complain if there are exported types/vars/consts + continue + case *ast.FuncDecl: + if d.Name.IsExported() { + exported = append(exported, d) + } + } + } + return +} + +//go:embed template.tmpl +var templateText string + +var tpl = template.Must(template.New("").Parse(templateText)) + +// tplData is the data passed to the template. +type tplData struct { + Mappings []mapping +} + +type tplImport struct{ Name, Path string } + +func (t tplData) Imports() (res []tplImport) { + add := func(path string) { + for _, v := range res { + if v.Path == path { + return + } + } + res = append(res, tplImport{Name: pkgNameFromPath(path), Path: path}) + } + for _, m := range t.Mappings { + add(m.GoImportPath) + // There might be a bit more than we need - but we run goimports to fix that. + for _, v := range m.goImports { + s, err := strconv.Unquote(v.Path.Value) + if err != nil { + panic(fmt.Errorf("could not unquote go import string literal: %s", v.Path.Value)) + } + add(s) + } + } + return +} + +func (tplData) PkgName(path string) string { return pkgNameFromPath(path) } diff --git a/misc/genstd/mapping.go b/misc/genstd/mapping.go new file mode 100644 index 00000000000..0e4034a1ab9 --- /dev/null +++ b/misc/genstd/mapping.go @@ -0,0 +1,471 @@ +package main + +import ( + "errors" + "fmt" + "go/ast" + "path" + "strconv" +) + +const gnoPackagePath = "github.com/gnolang/gno/gnovm/pkg/gnolang" + +type mapping struct { + GnoImportPath string // time + GnoFunc string // now + GoImportPath string // github.com/gnolang/gno/gnovm/stdlibs/time + GoFunc string // X_now + Params []mappingType + Results []mappingType + MachineParam bool + + gnoImports []*ast.ImportSpec + goImports []*ast.ImportSpec +} + +type mappingType struct { + // type of ast.Expr is from the normal ast.Expr types + // + *linkedIdent. + Type ast.Expr + + // IsTypedValue is set to true if the parameter or result in go is of type + // gno.TypedValue. This prevents the generated code from performing + // Go2Gno/Gno2Go reflection-based conversion. + IsTypedValue bool +} + +func (mt mappingType) GoQualifiedName() string { + return (&exprPrinter{ + mode: printerModeGoQualified, + }).ExprString(mt.Type) +} + +func (mt mappingType) GnoType() string { + return (&exprPrinter{ + mode: printerModeGnoType, + }).ExprString(mt.Type) +} + +type linkedIdent struct { + ast.BadExpr // Unused, but it makes *linkedIdent implement ast.Expr + + lt linkedType +} + +func linkFunctions(pkgs []*pkgData) []mapping { + var mappings []mapping + for _, pkg := range pkgs { + for _, gb := range pkg.gnoBodyless { + nameWant := gb.Name.Name + if !gb.Name.IsExported() { + nameWant = "X_" + nameWant + } + fn := findFuncByName(pkg.goExported, nameWant) + if fn.FuncDecl == nil { + panic( + fmt.Errorf("package %q: no matching go function declaration (%q) exists for function %q", + pkg.importPath, nameWant, gb.Name.Name), + ) + } + mp := mapping{ + GnoImportPath: pkg.importPath, + GnoFunc: gb.Name.Name, + GoImportPath: "github.com/gnolang/gno/" + relPath() + "/" + pkg.importPath, + GoFunc: fn.Name.Name, + + gnoImports: gb.imports, + goImports: fn.imports, + } + if !mp.signaturesMatch(gb, fn) { + panic( + fmt.Errorf("package %q: signature of gno function %s doesn't match signature of go function %s", + pkg.importPath, gb.Name.Name, fn.Name.Name), + ) + } + mp.loadParamsResults(gb, fn) + mappings = append(mappings, mp) + } + } + return mappings +} + +func findFuncByName(fns []funcDecl, name string) funcDecl { + for _, fn := range fns { + if fn.Name.Name == name { + return fn + } + } + return funcDecl{} +} + +func (m *mapping) loadParamsResults(gnof, gof funcDecl) { + // initialise with lengths + m.Params = make([]mappingType, 0, gnof.Type.Params.NumFields()) + m.Results = make([]mappingType, 0, gnof.Type.Results.NumFields()) + + gofpl := gof.Type.Params.List + if m.MachineParam { + // skip machine parameter + gofpl = gofpl[1:] + } + if gnof.Type.Params != nil { + m._loadParamsResults(&m.Params, gnof.Type.Params.List, gofpl) + } + if gnof.Type.Results != nil { + m._loadParamsResults(&m.Results, gnof.Type.Results.List, gof.Type.Results.List) + } +} + +func (m *mapping) _loadParamsResults(dst *[]mappingType, gnol, gol []*ast.Field) { + iterFields(gnol, gol, func(gnoe, goe ast.Expr) error { + if m.isTypedValue(goe) { + *dst = append(*dst, mappingType{Type: gnoe, IsTypedValue: true}) + } else { + merged := m.mergeTypes(gnoe, goe) + *dst = append(*dst, mappingType{Type: merged}) + } + return nil + }) +} + +// isGnoMachine checks whether field is of type *gno.Machine, +// and it has at most 1 name. +func (m *mapping) isGnoMachine(field *ast.Field) bool { + if len(field.Names) > 1 { + return false + } + + return m.isGnoType(field.Type, true, "Machine") +} + +// isTypedValue checks whether e is type gno.TypedValue. +func (m *mapping) isTypedValue(e ast.Expr) bool { + return m.isGnoType(e, false, "TypedValue") +} + +func (m *mapping) isGnoType(e ast.Expr, star bool, typeName string) bool { + if star { + px, ok := e.(*ast.StarExpr) + if !ok { + return false + } + e = px.X + } + + sx, ok := e.(*ast.SelectorExpr) + if !ok { + return false + } + + imp := resolveSelectorImport(m.goImports, sx) + return imp == gnoPackagePath && sx.Sel.Name == typeName +} + +// iterFields iterates over gnol and gol, calling callback for each matching +// parameter. iterFields assumes the caller already checked for the "true" number +// of parameters in the two arrays to be equal (can be checked using +// (*ast.FieldList).NumFields()). +// +// If callback returns an error, iterFields returns that error immediately. +// No errors are otherwise generated. +func iterFields(gnol, gol []*ast.Field, callback func(gnoType, goType ast.Expr) error) error { + var goIdx, goNameIdx int + + for _, l := range gnol { + n := len(l.Names) + if n == 0 { + n = 1 + } + gnoe := l.Type + for i := 0; i < n; i++ { + goe := gol[goIdx].Type + + if err := callback(gnoe, goe); err != nil { + return err + } + + goNameIdx++ + if goNameIdx >= len(gol[goIdx].Names) { + goIdx++ + goNameIdx = 0 + } + } + } + return nil +} + +// mergeTypes merges gnoe and goe into a single ast.Expr. +// +// gnoe and goe are expected to have the same underlying structure, but they +// may differ in their type identifiers (possibly qualified, ie pkg.T). +// if they differ, mergeTypes returns nil. +// +// When two type identifiers are found, they are checked against the list of +// linkedTypes to determine if they refer to a linkedType. If they are not, +// mergeTypes returns nil. If they are, the *ast.Ident/*ast.SelectorExpr is +// replaced with a *linkedIdent. +// +// mergeTypes does not modify the given gnoe or goe; the returned ast.Expr is +// (recursively) newly allocated. +func (m *mapping) mergeTypes(gnoe, goe ast.Expr) ast.Expr { + resolveGoNamed := func(lt *linkedType) bool { + switch goe := goe.(type) { + case *ast.SelectorExpr: + // selector - resolve pkg ident to path + lt.goPackage = resolveSelectorImport(m.goImports, goe) + lt.goName = goe.Sel.Name + case *ast.Ident: + // local name -- use import path of go pkg + lt.goPackage = m.GoImportPath + lt.goName = goe.Name + default: + return false + } + return true + } + + switch gnoe := gnoe.(type) { + // We're working with a subset of all expressions: + // https://go.dev/ref/spec#Type + + case *ast.SelectorExpr: + lt := linkedType{ + gnoPackage: resolveSelectorImport(m.gnoImports, gnoe), + gnoName: gnoe.Sel.Name, + } + if !resolveGoNamed(<) || !linkedTypeExists(lt) { + return nil + } + return &linkedIdent{lt: lt} + case *ast.Ident: + // easy case - built-in identifiers + goi, ok := goe.(*ast.Ident) + if ok && isBuiltin(gnoe.Name) && gnoe.Name == goi.Name { + return &ast.Ident{Name: gnoe.Name} + } + + lt := linkedType{ + gnoPackage: m.GnoImportPath, + gnoName: gnoe.Name, + } + if !resolveGoNamed(<) || !linkedTypeExists(lt) { + return nil + } + return &linkedIdent{lt: lt} + + // easier cases -- check for equality of structure and underlying types + case *ast.StarExpr: + goe, ok := goe.(*ast.StarExpr) + if !ok { + return nil + } + x := m.mergeTypes(gnoe.X, goe.X) + if x == nil { + return nil + } + return &ast.StarExpr{X: x} + case *ast.ArrayType: + goe, ok := goe.(*ast.ArrayType) + if !ok || !basicLitsEqual(gnoe.Len, goe.Len) { + return nil + } + elt := m.mergeTypes(gnoe.Elt, goe.Elt) + if elt == nil { + return nil + } + var l ast.Expr + if gnoe.Len != nil { + l = &ast.BasicLit{Value: gnoe.Len.(*ast.BasicLit).Value} + } + return &ast.ArrayType{Len: l, Elt: elt} + + case *ast.StructType, + *ast.FuncType, + *ast.InterfaceType, + *ast.MapType, + *ast.Ellipsis: + // TODO + panic("not implemented") + default: + panic(fmt.Errorf("invalid expression as func param/return type: %T (%v)", gnoe, gnoe)) + } +} + +// returns full import path from package ident +func resolveImport(imports []*ast.ImportSpec, ident string) string { + for _, i := range imports { + s, err := strconv.Unquote(i.Path.Value) + if err != nil { + panic(fmt.Errorf("could not unquote import path literal: %s", i.Path.Value)) + } + + // TODO: for simplicity, if i.Name is nil we assume the name to be == + // to the last part of the import path. + // ideally, use importer to resolve package directory on user's FS and + // resolve by parsing and reading package clause + var name string + if i.Name != nil { + name = i.Name.Name + } else { + name = path.Base(s) + } + + if name == ident { + return s + } + } + return "" +} + +func resolveSelectorImport(imports []*ast.ImportSpec, sx *ast.SelectorExpr) string { + pkgIdent, ok := sx.X.(*ast.Ident) + if !ok { + panic(fmt.Errorf("encountered unhandled SelectorExpr.X type: %T (%v)", sx.X, sx)) + } + impPath := resolveImport(imports, pkgIdent.Name) + if impPath == "" { + panic(fmt.Errorf( + "unknown identifier %q (for resolving type %q)", + pkgIdent.Name, pkgIdent.Name+"."+sx.Sel.Name, + )) + } + return impPath +} + +// simple equivalence between two BasicLits. +// Note that this returns true only if the expressions are exactly the same; +// ie. 16 != 0x10, only 16 == 16. +func basicLitsEqual(x1, x2 ast.Expr) bool { + if x1 == nil || x2 == nil { + return x1 == nil && x2 == nil + } + l1, ok1 := x1.(*ast.BasicLit) + l2, ok2 := x2.(*ast.BasicLit) + if !ok1 || !ok2 { + return false + } + return l1.Value == l2.Value +} + +// Signatures match when they accept the same elementary types, or a linked +// type mapping (see [linkedTypes]). +// +// Additionally, if the first parameter to the Go function is +// *[gnolang.Machine], it is ignored when matching to the Gno function. +func (m *mapping) signaturesMatch(gnof, gof funcDecl) bool { + if gnof.Type.TypeParams != nil || gof.Type.TypeParams != nil { + panic("type parameters not supported") + } + + // if first param of go function is *gno.Machine, remove it + gofp := gof.Type.Params + if gofp != nil && len(gofp.List) > 0 && m.isGnoMachine(gofp.List[0]) { + // avoid touching original struct + n := *gofp + n.List = n.List[1:] + gofp = &n + + m.MachineParam = true + } + + return m.fieldListsMatch(gnof.Type.Params, gofp) && + m.fieldListsMatch(gnof.Type.Results, gof.Type.Results) +} + +var errNoMatch = errors.New("no match") + +func (m *mapping) fieldListsMatch(gnofl, gofl *ast.FieldList) bool { + if gnofl == nil || gofl == nil { + return gnofl == nil && gofl == nil + } + if gnofl.NumFields() != gofl.NumFields() { + return false + } + err := iterFields(gnofl.List, gofl.List, func(gnoe, goe ast.Expr) error { + // if the go type is gno.TypedValue, we just don't perform reflect-based conversion. + if m.isTypedValue(goe) { + return nil + } + if m.mergeTypes(gnoe, goe) == nil { + return errNoMatch + } + return nil + }) + return err == nil +} + +// TODO: this is created based on the uverse definitions. This should be +// centralized, or at least have a CI/make check to make sure this stays the +// same +var builtinTypes = [...]string{ + "bool", + "string", + "int", + "int8", + "int16", + "rune", + "int32", + "int64", + "uint", + "byte", + "uint8", + "uint16", + "uint32", + "uint64", + "bigint", + "float32", + "float64", + "error", +} + +func isBuiltin(name string) bool { + for _, x := range builtinTypes { + if x == name { + return true + } + } + return false +} + +type linkedType struct { + gnoPackage string + gnoName string + goPackage string + goName string +} + +var linkedTypes = [...]linkedType{ + { + "std", "Address", + "github.com/gnolang/gno/tm2/pkg/crypto", "Bech32Address", + }, + { + "std", "Coin", + "github.com/gnolang/gno/tm2/pkg/std", "Coin", + }, + { + "std", "Coins", + "github.com/gnolang/gno/tm2/pkg/std", "Coins", + }, + { + "std", "Realm", + "github.com/gnolang/gno/gnovm/stdlibs/std", "Realm", + }, + { + "std", "BankerType", + "github.com/gnolang/gno/gnovm/stdlibs/std", "BankerType", + }, + { + "std", "Banker", + "github.com/gnolang/gno/gnovm/stdlibs/std", "Banker", + }, +} + +func linkedTypeExists(lt linkedType) bool { + for _, ltx := range linkedTypes { + if lt == ltx { + return true + } + } + return false +} diff --git a/misc/genstd/mapping_test.go b/misc/genstd/mapping_test.go new file mode 100644 index 00000000000..b0cfa1bd4a7 --- /dev/null +++ b/misc/genstd/mapping_test.go @@ -0,0 +1,294 @@ +package main + +import ( + "fmt" + "go/ast" + "go/parser" + "os" + "path/filepath" + "sync" + "testing" + + "github.com/jaekwon/testify/assert" + "github.com/jaekwon/testify/require" +) + +const testdataDir = "github.com/gnolang/gno/misc/genstd/testdata/" + +var initWD = func() string { + d, err := os.Getwd() + if err != nil { + panic(err) + } + return d +}() + +func chdir(t *testing.T, s string) { + t.Helper() + + os.Chdir(filepath.Join(initWD, s)) + t.Cleanup(func() { + os.Chdir(initWD) + dirsOnce = sync.Once{} + memoGitRoot, memoRelPath = "", "" + }) +} + +func Test_linkFunctions(t *testing.T) { + chdir(t, "testdata/linkFunctions") + + pkgs, err := walkStdlibs(".") + require.NoError(t, err) + + mappings := linkFunctions(pkgs) + require.Len(t, mappings, 8) + + const ( + ret = 1 << iota + param + machine + ) + str := func(i int) string { + s := "Fn" + if i&machine != 0 { + s += "Machine" + } + if i¶m != 0 { + s += "Param" + } + if i&ret != 0 { + s += "Ret" + } + return s + } + + for i, v := range mappings { + exp := str(i) + assert.Equal(t, v.GnoFunc, exp) + assert.Equal(t, v.GoFunc, exp) + assert.Equal(t, v.GnoImportPath, "std") + assert.Equal(t, v.GoImportPath, testdataDir+"linkFunctions/std") + + assert.Equal(t, v.MachineParam, i&machine != 0, "MachineParam should match expected value") + if i¶m != 0 { + // require, otherwise the following would panic + require.Len(t, v.Params, 1) + p := v.Params[0] + assert.Equal(t, p.GnoType(), "int") + assert.Equal(t, p.GoQualifiedName(), "int") + assert.False(t, p.IsTypedValue) + } else { + assert.Len(t, v.Params, 0) + } + if i&ret != 0 { + // require, otherwise the following would panic + require.Len(t, v.Results, 1) + p := v.Results[0] + assert.Equal(t, p.GnoType(), "int") + assert.Equal(t, p.GoQualifiedName(), "int") + assert.False(t, p.IsTypedValue) + } else { + assert.Len(t, v.Results, 0) + } + } +} + +func Test_linkFunctions_unexp(t *testing.T) { + chdir(t, "testdata/linkFunctions_unexp") + + pkgs, err := walkStdlibs(".") + require.NoError(t, err) + + mappings := linkFunctions(pkgs) + require.Len(t, mappings, 2) + + assert.Equal(t, mappings[0].MachineParam, false) + assert.Equal(t, mappings[0].GnoFunc, "t1") + assert.Equal(t, mappings[0].GoFunc, "X_t1") + + assert.Equal(t, mappings[1].MachineParam, true) + assert.Equal(t, mappings[1].GnoFunc, "t2") + assert.Equal(t, mappings[1].GoFunc, "X_t2") +} + +func Test_linkFunctions_TypedValue(t *testing.T) { + chdir(t, "testdata/linkFunctions_TypedValue") + + pkgs, err := walkStdlibs(".") + require.NoError(t, err) + + mappings := linkFunctions(pkgs) + require.Len(t, mappings, 3) + + assert.Equal(t, mappings[0].MachineParam, false) + assert.Equal(t, mappings[0].GnoFunc, "TVParam") + assert.Equal(t, mappings[0].GoFunc, "TVParam") + assert.Len(t, mappings[0].Results, 0) + _ = assert.Len(t, mappings[0].Params, 1) && + assert.Equal(t, mappings[0].Params[0].IsTypedValue, true) && + assert.Equal(t, mappings[0].Params[0].GnoType(), "struct{m1 map[string]interface{}}") + + assert.Equal(t, mappings[1].MachineParam, false) + assert.Equal(t, mappings[1].GnoFunc, "TVResult") + assert.Equal(t, mappings[1].GoFunc, "TVResult") + assert.Len(t, mappings[1].Params, 0) + _ = assert.Len(t, mappings[1].Results, 1) && + assert.Equal(t, mappings[1].Results[0].IsTypedValue, true) && + assert.Equal(t, mappings[1].Results[0].GnoType(), "interface{S() map[int]Banker}") + + assert.Equal(t, mappings[2].MachineParam, true) + assert.Equal(t, mappings[2].GnoFunc, "TVFull") + assert.Equal(t, mappings[2].GoFunc, "TVFull") + assert.Len(t, mappings[2].Params, 1) + assert.Len(t, mappings[2].Results, 1) +} + +func Test_linkFunctions_noMatch(t *testing.T) { + chdir(t, "testdata/linkFunctions_noMatch") + + pkgs, err := walkStdlibs(".") + require.NoError(t, err) + + defer func() { + r := recover() + assert.NotNil(t, r) + assert.Contains(t, fmt.Sprint(r), "no matching go function declaration") + }() + + linkFunctions(pkgs) +} + +func Test_linkFunctions_noMatchSig(t *testing.T) { + chdir(t, "testdata/linkFunctions_noMatchSig") + + pkgs, err := walkStdlibs(".") + require.NoError(t, err) + + defer func() { + r := recover() + assert.NotNil(t, r) + assert.Contains(t, fmt.Sprint(r), "doesn't match signature of go function") + }() + + linkFunctions(pkgs) +} + +// mergeTypes - separate tests. + +var mergeTypesMapping = &mapping{ + GnoImportPath: "std", + GnoFunc: "Fn", + GoImportPath: "github.com/gnolang/gno/gnovm/stdlibs/std", + GoFunc: "Fn", + goImports: []*ast.ImportSpec{ + { + Name: &ast.Ident{Name: "gno"}, + Path: &ast.BasicLit{Value: `"github.com/gnolang/gno/gnovm/pkg/gnolang"`}, + }, + { + Path: &ast.BasicLit{Value: `"github.com/gnolang/gno/tm2/pkg/crypto"`}, + }, + }, + gnoImports: []*ast.ImportSpec{ + { + // cheating a bit -- but we currently only have linked types in `std`. + Path: &ast.BasicLit{Value: `"std"`}, + }, + { + Path: &ast.BasicLit{Value: `"math"`}, + }, + }, +} + +func Test_mergeTypes(t *testing.T) { + tt := []struct { + gnoe, goe string + result ast.Expr + }{ + {"int", "int", &ast.Ident{Name: "int"}}, + {"*[11][]rune", "*[11][]rune", &ast.StarExpr{ + X: &ast.ArrayType{Len: &ast.BasicLit{Value: "11"}, Elt: &ast.ArrayType{ + Elt: &ast.Ident{Name: "rune"}, + }}, + }}, + + {"Address", "crypto.Bech32Address", &linkedIdent{lt: linkedType{ + gnoPackage: "std", + gnoName: "Address", + goPackage: "github.com/gnolang/gno/tm2/pkg/crypto", + goName: "Bech32Address", + }}}, + {"std.Realm", "Realm", &linkedIdent{lt: linkedType{ + gnoPackage: "std", + gnoName: "Realm", + goPackage: "github.com/gnolang/gno/gnovm/stdlibs/std", + goName: "Realm", + }}}, + } + + for _, tv := range tt { + t.Run(tv.gnoe, func(t *testing.T) { + gnoe, err := parser.ParseExpr(tv.gnoe) + require.NoError(t, err) + goe, err := parser.ParseExpr(tv.goe) + require.NoError(t, err) + + result := mergeTypesMapping.mergeTypes(gnoe, goe) + assert.Equal(t, result, tv.result) + }) + } +} + +func Test_mergeTypes_invalid(t *testing.T) { + tt := []struct { + gnoe, goe string + panic string + }{ + {"int", "string", ""}, + + {"*int", "int", ""}, + {"string", "*string", ""}, + {"*string", "*int", ""}, + + {"[]int", "[1]int", ""}, + {"[1]int", "[]int", ""}, + {"[2]int", "[2]string", ""}, + // valid, but unsupported (only BasicLits) + {"[(11)]int", "[(11)]string", ""}, + + {"Address", "string", ""}, + {"math.X", "X", ""}, + + {"map[string]string", "map[string]string", "not implemented"}, + {"func(s string)", "func(s string)", "not implemented"}, + {"interface{}", "interface{}", "not implemented"}, + {"struct{}", "struct{}", "not implemented"}, + + {"1 + 2", "1 + 2", "invalid expression"}, + + // even though semantically equal, for simplicity we don't implement + // "true" basic lit equivalence + {"[8]int", "[0x8]int", ""}, + } + + for _, tv := range tt { + t.Run(tv.gnoe, func(t *testing.T) { + gnoe, err := parser.ParseExpr(tv.gnoe) + require.NoError(t, err) + goe, err := parser.ParseExpr(tv.goe) + require.NoError(t, err) + + defer func() { + r := recover() + if tv.panic == "" { + assert.Nil(t, r) + } else { + assert.Contains(t, fmt.Sprint(r), tv.panic) + } + }() + + result := mergeTypesMapping.mergeTypes(gnoe, goe) + assert.Nil(t, result) + }) + } +} diff --git a/misc/genstd/template.tmpl b/misc/genstd/template.tmpl new file mode 100644 index 00000000000..f2cad0a851b --- /dev/null +++ b/misc/genstd/template.tmpl @@ -0,0 +1,88 @@ +// This file is autogenerated from the genstd tool (@/misc/genstd); do not edit. +// To regenerate it, run `go generate` from this directory. + +package stdlibs + +import ( + "reflect" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" +{{- range .Imports }} + {{ .Name }} {{ printf "%q" .Path }} +{{- end }} +) + +type nativeFunc struct { + gnoPkg string + gnoFunc gno.Name + params []gno.FieldTypeExpr + results []gno.FieldTypeExpr + f func(m *gno.Machine) +} + +var nativeFuncs = [...]nativeFunc{ +{{- range $i, $m := .Mappings }} + { + {{ printf "%q" $m.GnoImportPath }}, + {{ printf "%q" $m.GnoFunc }}, + {{- /* TODO: set nil if empty */}} + []gno.FieldTypeExpr{ + {{- range $i, $p := $m.Params }} + {Name: gno.N("p{{ $i }}"), Type: gno.X({{ printf "%q" $p.GnoType }})}, + {{- end }} + }, + []gno.FieldTypeExpr{ + {{- range $i, $r := $m.Results }} + {Name: gno.N("r{{ $i }}"), Type: gno.X({{ printf "%q" $r.GnoType }})}, + {{- end }} + }, + func(m *gno.Machine) { + {{ if $m.Params -}} + b := m.LastBlock() + var ( + {{- range $pn, $pv := $m.Params -}} + {{- if $pv.IsTypedValue }} + p{{ $pn }} = gno.NewValuePathBlock(1, {{ $pn }}, "")).TV + {{- else }} + p{{ $pn }} {{ $pv.GoQualifiedName }} + rp{{ $pn }} = reflect.ValueOf(&p{{ $pn }}).Elem() + {{- end }} + {{- end }} + ) + + {{ range $pn, $pv := $m.Params -}} + {{- if not $pv.IsTypedValue }} + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, {{ $pn }}, "")).TV, rp{{ $pn }}) + {{- end -}} + {{ end }} + {{- end }} + + {{ range $rn, $rv := $m.Results -}} + {{- if gt $rn 0 -}}, {{ end -}} + r{{ $rn }} + {{- end -}} + {{- if $m.Results }} := {{ end -}} + {{ $.PkgName $m.GoImportPath }}.{{ $m.GoFunc }}( + {{- if $m.MachineParam }} + m, + {{ end -}} + {{- range $pn, $pv := $m.Params -}} + p{{ $pn }}, + {{- end -}} + ) + + {{ range $rn, $rv := $m.Results -}} + {{- if $rv.IsTypedValue }} + m.PushValue(r{{ $rn }}) + {{- else }} + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r{{ $rn }}).Elem(), {{- /* necessary to support interfaces (ie. error) */}} + )) + {{- end }} + {{- end }} + }, + }, +{{- end }} +} diff --git a/misc/genstd/testdata/linkFunctions/std/std.gno b/misc/genstd/testdata/linkFunctions/std/std.gno new file mode 100644 index 00000000000..ab04b4084ba --- /dev/null +++ b/misc/genstd/testdata/linkFunctions/std/std.gno @@ -0,0 +1,23 @@ +package std + +func Fn() + +func FnRet() int + +func FnParam(n int) + +func FnParamRet(n int) int + +func FnMachine() + +func FnMachineRet() int + +func FnMachineParam(n int) + +func FnMachineParamRet(n int) int + +func Ignored() int { + // Ignored even if it has a matching go definition - + // as this one has a body. + return 1 +} diff --git a/misc/genstd/testdata/linkFunctions/std/std.go b/misc/genstd/testdata/linkFunctions/std/std.go new file mode 100644 index 00000000000..1b7a791c6cc --- /dev/null +++ b/misc/genstd/testdata/linkFunctions/std/std.go @@ -0,0 +1,47 @@ +package std + +import ( + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" +) + +func Fn() { + println("call Fn") +} + +func FnRet() int { + println("call FnRet") + return 1 +} + +func FnParam(n int) { + println("call FnParam", n) +} + +func FnParamRet(n int) int { + println("call FnParamRet", n) + return 1 +} + +func FnMachine(m *gno.Machine) { + println("call FnMachine") +} + +func FnMachineRet(m *gno.Machine) int { + println("call FnMachineRet") + return 1 +} + +func FnMachineParam(m *gno.Machine, n int) { + println("call FnMachineParam", n) +} + +func FnMachineParamRet(m *gno.Machine, n int) int { + println("call FnMachineParamRet", n) + return 1 +} + +func Ignored() int { + // Ignored even if it has a matching go definition - + // as gno's has a body. + return 1 +} diff --git a/misc/genstd/testdata/linkFunctions_TypedValue/std/std.gno b/misc/genstd/testdata/linkFunctions_TypedValue/std/std.gno new file mode 100644 index 00000000000..3bba36774e3 --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_TypedValue/std/std.gno @@ -0,0 +1,11 @@ +package std + +type Banker interface { + B() +} + +func TVParam(m struct{ m1 map[string]interface{} }) + +func TVResult() interface{ S() map[int]Banker } + +func TVFull(map[Banker]map[string]interface{}) (n [86]map[string]bool) diff --git a/misc/genstd/testdata/linkFunctions_TypedValue/std/std.go b/misc/genstd/testdata/linkFunctions_TypedValue/std/std.go new file mode 100644 index 00000000000..03d95721438 --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_TypedValue/std/std.go @@ -0,0 +1,16 @@ +package std + +import ( + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" +) + +func TVParam(p gno.TypedValue) { +} + +func TVResult() gno.TypedValue { + return gno.TypedValue{} +} + +func TVFull(m *gno.Machine, v gno.TypedValue) gno.TypedValue { + return gno.TypedValue{} +} diff --git a/misc/genstd/testdata/linkFunctions_noMatch/std/std.gno b/misc/genstd/testdata/linkFunctions_noMatch/std/std.gno new file mode 100644 index 00000000000..2ef4be8abc6 --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_noMatch/std/std.gno @@ -0,0 +1,3 @@ +package std + +func X() int diff --git a/misc/genstd/testdata/linkFunctions_noMatch/std/std.go b/misc/genstd/testdata/linkFunctions_noMatch/std/std.go new file mode 100644 index 00000000000..97399743533 --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_noMatch/std/std.go @@ -0,0 +1,3 @@ +package std + +func Y() {} diff --git a/misc/genstd/testdata/linkFunctions_noMatchSig/std/std.gno b/misc/genstd/testdata/linkFunctions_noMatchSig/std/std.gno new file mode 100644 index 00000000000..75e8e10e2e3 --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_noMatchSig/std/std.gno @@ -0,0 +1,3 @@ +package std + +func X(n int) int diff --git a/misc/genstd/testdata/linkFunctions_noMatchSig/std/std.go b/misc/genstd/testdata/linkFunctions_noMatchSig/std/std.go new file mode 100644 index 00000000000..7a5a0e5893b --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_noMatchSig/std/std.go @@ -0,0 +1,8 @@ +package std + +import ( + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" +) + +func X(m *gno.Machine, n string) { +} diff --git a/misc/genstd/testdata/linkFunctions_unexp/std/std.gno b/misc/genstd/testdata/linkFunctions_unexp/std/std.gno new file mode 100644 index 00000000000..c4811e5e837 --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_unexp/std/std.gno @@ -0,0 +1,4 @@ +package std + +func t1() int +func t2() int diff --git a/misc/genstd/testdata/linkFunctions_unexp/std/std.go b/misc/genstd/testdata/linkFunctions_unexp/std/std.go new file mode 100644 index 00000000000..023b424e87c --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_unexp/std/std.go @@ -0,0 +1,13 @@ +package std + +import ( + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" +) + +func X_t1() int { + return 1 +} + +func X_t2(m *gno.Machine) int { + return m.NumOps +} diff --git a/misc/genstd/util.go b/misc/genstd/util.go new file mode 100644 index 00000000000..061a9604c67 --- /dev/null +++ b/misc/genstd/util.go @@ -0,0 +1,119 @@ +package main + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path" + "path/filepath" + "strings" + "sync" +) + +func runTool(importPath string) error { + shortName := path.Base(importPath) + gr := gitRoot() + + cmd := exec.Command( + "go", "run", "-modfile", filepath.Join(gr, "misc/devdeps/go.mod"), + importPath, "-w", "native.go", + ) + _, err := cmd.Output() + if err != nil { + if err, ok := err.(*exec.ExitError); ok { + return fmt.Errorf("error executing %s: %w; output: %v", shortName, err, string(err.Stderr)) + } + return fmt.Errorf("error executing %s: %w", shortName, err) + } + return nil +} + +var ( + memoGitRoot string + memoRelPath string + + dirsOnce sync.Once +) + +func gitRoot() string { + dirsOnceDo() + return memoGitRoot +} + +func relPath() string { + dirsOnceDo() + return memoRelPath +} + +func dirsOnceDo() { + dirsOnce.Do(func() { + var err error + memoGitRoot, memoRelPath, err = findDirs() + if err != nil { + panic(fmt.Errorf("could not determine git root: %w", err)) + } + }) +} + +func findDirs() (gitRoot string, relPath string, err error) { + wd, err := os.Getwd() + if err != nil { + return + } + p := wd + for { + if s, e := os.Stat(filepath.Join(p, ".git")); e == nil && s.IsDir() { + // make relPath relative to the git root + rp := strings.TrimPrefix(wd, p+string(filepath.Separator)) + // normalize separator to / + rp = strings.ReplaceAll(rp, string(filepath.Separator), "/") + return p, rp, nil + } + + if strings.HasSuffix(p, string(filepath.Separator)) { + return "", "", errors.New("root git not found") + } + + p = filepath.Dir(p) + } +} + +// pkgNameFromPath derives the package name from the given path, +// unambiguously for the most part (so safe for the code generation). +// +// The path is taken and possibly shortened if it starts with a known prefix. +// For instance, github.com/gnolang/gno/stdlibs/std simply becomes "libs_std". +// "Unsafe" characters are removed (ie. invalid for go identifiers). +func pkgNameFromPath(path string) string { + const ( + repoPrefix = "github.com/gnolang/gno/" + vmPrefix = repoPrefix + "gnovm/" + tm2Prefix = repoPrefix + "tm2/pkg/" + libsPrefix = vmPrefix + "stdlibs/" + testlibsPrefix = vmPrefix + "tests/stdlibs/" + ) + + ns := "ext" + switch { + case strings.HasPrefix(path, testlibsPrefix): + ns, path = "testlibs", path[len(testlibsPrefix):] + case strings.HasPrefix(path, libsPrefix): + ns, path = "libs", path[len(libsPrefix):] + case strings.HasPrefix(path, vmPrefix): + ns, path = "vm", path[len(vmPrefix):] + case strings.HasPrefix(path, tm2Prefix): + ns, path = "tm2", path[len(tm2Prefix):] + case strings.HasPrefix(path, repoPrefix): + ns, path = "repo", path[len(repoPrefix):] + case !strings.Contains(path, "."): + ns = "go" + } + + flds := strings.FieldsFunc(path, func(r rune) bool { + return (r < 'a' || r > 'z') && + (r < 'A' || r > 'Z') && + (r < '0' || r > '9') + }) + return ns + "_" + strings.Join(flds, "_") +} diff --git a/misc/genstd/util_test.go b/misc/genstd/util_test.go new file mode 100644 index 00000000000..f6e804d545f --- /dev/null +++ b/misc/genstd/util_test.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + "testing" + + "github.com/jaekwon/testify/assert" +) + +func Test_pkgNameFromPath(t *testing.T) { + tt := []struct { + input, result string + }{ + {"math", "go_math"}, + {"crypto/sha256", "go_crypto_sha256"}, + {"github.com/import/path", "ext_github_com_import_path"}, + // consecutive unsupported characters => _ + {"kebab----------case", "go_kebab_case"}, + + {"github.com/gnolang/gno/misc/test", "repo_misc_test"}, + {"github.com/gnolang/gno/tm2/pkg/crypto", "tm2_crypto"}, + {"github.com/gnolang/gno/gnovm/test", "vm_test"}, + {"github.com/gnolang/gno/gnovm/stdlibs/std", "libs_std"}, + {"github.com/gnolang/gno/gnovm/tests/stdlibs/std", "testlibs_std"}, + } + for i, tv := range tt { + t.Run(fmt.Sprintf("n%d", i+1), func(t *testing.T) { + assert.Equal(t, pkgNameFromPath(tv.input), tv.result) + }) + } +} diff --git a/tm2/pkg/bft/consensus/config/config.go b/tm2/pkg/bft/consensus/config/config.go index 234def843e9..2f9938cca6f 100644 --- a/tm2/pkg/bft/consensus/config/config.go +++ b/tm2/pkg/bft/consensus/config/config.go @@ -50,9 +50,9 @@ func DefaultConsensusConfig() *ConsensusConfig { TimeoutPrevoteDelta: 500 * time.Millisecond, TimeoutPrecommit: 1000 * time.Millisecond, TimeoutPrecommitDelta: 500 * time.Millisecond, - TimeoutCommit: 5000 * time.Millisecond, + TimeoutCommit: 1000 * time.Millisecond, SkipTimeoutCommit: false, - CreateEmptyBlocks: true, + CreateEmptyBlocks: false, CreateEmptyBlocksInterval: 0 * time.Second, PeerGossipSleepDuration: 100 * time.Millisecond, PeerQueryMaj23SleepDuration: 2000 * time.Millisecond, diff --git a/tm2/pkg/bft/rpc/lib/client/http_client.go b/tm2/pkg/bft/rpc/lib/client/http_client.go index d5708f97d40..5a9da9ec052 100644 --- a/tm2/pkg/bft/rpc/lib/client/http_client.go +++ b/tm2/pkg/bft/rpc/lib/client/http_client.go @@ -78,12 +78,6 @@ func parseRemoteAddr(remoteAddr string) (network string, s string, err error) { return "", "", fmt.Errorf("invalid addr: %s", remoteAddr) } - // accept http(s) as an alias for tcp - switch protocol { - case protoHTTP, protoHTTPS: - protocol = protoTCP - } - return protocol, address, nil } @@ -99,6 +93,12 @@ func makeHTTPDialer(remoteAddr string) func(string, string) (net.Conn, error) { return makeErrorDialer(err) } + // net.Dial doesn't understand http/https, so change it to TCP + switch protocol { + case protoHTTP, protoHTTPS: + protocol = protoTCP + } + return func(proto, addr string) (net.Conn, error) { return net.Dial(protocol, address) } diff --git a/tm2/pkg/crypto/keys/client/add.go b/tm2/pkg/crypto/keys/client/add.go index 5f90a9f874e..30b612a9de2 100644 --- a/tm2/pkg/crypto/keys/client/add.go +++ b/tm2/pkg/crypto/keys/client/add.go @@ -29,7 +29,7 @@ type addCfg struct { index uint64 } -func newAddCmd(rootCfg *baseCfg) *commands.Command { +func newAddCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &addCfg{ rootCfg: rootCfg, } @@ -42,7 +42,7 @@ func newAddCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execAdd(cfg, args, commands.NewDefaultIO()) + return execAdd(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/addpkg.go b/tm2/pkg/crypto/keys/client/addpkg.go index 885e1f123d7..3de9a6de546 100644 --- a/tm2/pkg/crypto/keys/client/addpkg.go +++ b/tm2/pkg/crypto/keys/client/addpkg.go @@ -24,7 +24,7 @@ type addPkgCfg struct { deposit string } -func newAddPkgCmd(rootCfg *makeTxCfg) *commands.Command { +func newAddPkgCmd(rootCfg *makeTxCfg, io *commands.IO) *commands.Command { cfg := &addPkgCfg{ rootCfg: rootCfg, } @@ -37,7 +37,7 @@ func newAddPkgCmd(rootCfg *makeTxCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execAddPkg(cfg, args, commands.NewDefaultIO()) + return execAddPkg(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/broadcast.go b/tm2/pkg/crypto/keys/client/broadcast.go index 039a9557c38..f1d448495a6 100644 --- a/tm2/pkg/crypto/keys/client/broadcast.go +++ b/tm2/pkg/crypto/keys/client/broadcast.go @@ -23,7 +23,7 @@ type broadcastCfg struct { tx *std.Tx } -func newBroadcastCmd(rootCfg *baseCfg) *commands.Command { +func newBroadcastCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &broadcastCfg{ rootCfg: rootCfg, } diff --git a/tm2/pkg/crypto/keys/client/call.go b/tm2/pkg/crypto/keys/client/call.go index bcb7be3e550..29fe9739a36 100644 --- a/tm2/pkg/crypto/keys/client/call.go +++ b/tm2/pkg/crypto/keys/client/call.go @@ -22,7 +22,7 @@ type callCfg struct { args commands.StringArr } -func newCallCmd(rootCfg *makeTxCfg) *commands.Command { +func newCallCmd(rootCfg *makeTxCfg, io *commands.IO) *commands.Command { cfg := &callCfg{ rootCfg: rootCfg, } @@ -35,7 +35,7 @@ func newCallCmd(rootCfg *makeTxCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execCall(cfg, args, commands.NewDefaultIO()) + return execCall(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/delete.go b/tm2/pkg/crypto/keys/client/delete.go index 0f216d3467c..e22ac30988c 100644 --- a/tm2/pkg/crypto/keys/client/delete.go +++ b/tm2/pkg/crypto/keys/client/delete.go @@ -16,7 +16,7 @@ type deleteCfg struct { force bool } -func newDeleteCmd(rootCfg *baseCfg) *commands.Command { +func newDeleteCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &deleteCfg{ rootCfg: rootCfg, } @@ -29,7 +29,7 @@ func newDeleteCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execDelete(cfg, args, commands.NewDefaultIO()) + return execDelete(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/export.go b/tm2/pkg/crypto/keys/client/export.go index eda04a5c92f..6eff8aa97b3 100644 --- a/tm2/pkg/crypto/keys/client/export.go +++ b/tm2/pkg/crypto/keys/client/export.go @@ -18,7 +18,7 @@ type exportCfg struct { unsafe bool } -func newExportCmd(rootCfg *baseCfg) *commands.Command { +func newExportCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &exportCfg{ rootCfg: rootCfg, } @@ -31,7 +31,7 @@ func newExportCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execExport(cfg, commands.NewDefaultIO()) + return execExport(cfg, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/generate.go b/tm2/pkg/crypto/keys/client/generate.go index b721e6704ce..d209bd70bd3 100644 --- a/tm2/pkg/crypto/keys/client/generate.go +++ b/tm2/pkg/crypto/keys/client/generate.go @@ -16,7 +16,7 @@ type generateCfg struct { customEntropy bool } -func newGenerateCmd(rootCfg *baseCfg) *commands.Command { +func newGenerateCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &generateCfg{ rootCfg: rootCfg, } @@ -29,7 +29,7 @@ func newGenerateCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execGenerate(cfg, args, commands.NewDefaultIO()) + return execGenerate(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/generate_test.go b/tm2/pkg/crypto/keys/client/generate_test.go index 516912046b6..fae85b664ac 100644 --- a/tm2/pkg/crypto/keys/client/generate_test.go +++ b/tm2/pkg/crypto/keys/client/generate_test.go @@ -46,7 +46,7 @@ func Test_execGenerateUser(t *testing.T) { err = execGenerate(cfg, []string{}, io) require.NoError(t, err) - // Now provide "good" entropy but no answer + // Now provide "io.good" entropy but no answer fakeEntropy = strings.Repeat(":)", 40) + "\n" // entropy + accept count io.SetIn(strings.NewReader(fakeEntropy)) err = execGenerate(cfg, []string{}, io) diff --git a/tm2/pkg/crypto/keys/client/import.go b/tm2/pkg/crypto/keys/client/import.go index 5e0eeecabb5..e1d8af55861 100644 --- a/tm2/pkg/crypto/keys/client/import.go +++ b/tm2/pkg/crypto/keys/client/import.go @@ -18,7 +18,7 @@ type importCfg struct { unsafe bool } -func newImportCmd(rootCfg *baseCfg) *commands.Command { +func newImportCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &importCfg{ rootCfg: rootCfg, } @@ -31,7 +31,7 @@ func newImportCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, _ []string) error { - return execImport(cfg, commands.NewDefaultIO()) + return execImport(cfg, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/list.go b/tm2/pkg/crypto/keys/client/list.go index 50be35cef43..cb86feb2395 100644 --- a/tm2/pkg/crypto/keys/client/list.go +++ b/tm2/pkg/crypto/keys/client/list.go @@ -8,7 +8,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/keys" ) -func newListCmd(rootCfg *baseCfg) *commands.Command { +func newListCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { return commands.NewCommand( commands.Metadata{ Name: "list", @@ -17,7 +17,7 @@ func newListCmd(rootCfg *baseCfg) *commands.Command { }, nil, func(_ context.Context, args []string) error { - return execList(rootCfg, args, commands.NewDefaultIO()) + return execList(rootCfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/maketx.go b/tm2/pkg/crypto/keys/client/maketx.go index cbcc6def0de..36214a5a983 100644 --- a/tm2/pkg/crypto/keys/client/maketx.go +++ b/tm2/pkg/crypto/keys/client/maketx.go @@ -17,7 +17,7 @@ type makeTxCfg struct { chainID string } -func newMakeTxCmd(rootCfg *baseCfg) *commands.Command { +func newMakeTxCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &makeTxCfg{ rootCfg: rootCfg, } @@ -33,9 +33,9 @@ func newMakeTxCmd(rootCfg *baseCfg) *commands.Command { ) cmd.AddSubCommands( - newAddPkgCmd(cfg), - newSendCmd(cfg), - newCallCmd(cfg), + newAddPkgCmd(cfg, io), + newSendCmd(cfg, io), + newCallCmd(cfg, io), ) return cmd diff --git a/tm2/pkg/crypto/keys/client/query.go b/tm2/pkg/crypto/keys/client/query.go index 58923f8787c..50c1f257213 100644 --- a/tm2/pkg/crypto/keys/client/query.go +++ b/tm2/pkg/crypto/keys/client/query.go @@ -3,7 +3,6 @@ package client import ( "context" "flag" - "fmt" "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" @@ -22,7 +21,7 @@ type queryCfg struct { path string } -func newQueryCmd(rootCfg *baseCfg) *commands.Command { +func newQueryCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &queryCfg{ rootCfg: rootCfg, } @@ -35,7 +34,7 @@ func newQueryCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execQuery(cfg, args) + return execQuery(cfg, args, io) }, ) } @@ -63,7 +62,7 @@ func (c *queryCfg) RegisterFlags(fs *flag.FlagSet) { ) } -func execQuery(cfg *queryCfg, args []string) error { +func execQuery(cfg *queryCfg, args []string, io *commands.IO) error { if len(args) != 1 { return flag.ErrHelp } @@ -76,15 +75,16 @@ func execQuery(cfg *queryCfg, args []string) error { } if qres.Response.Error != nil { - fmt.Printf("Log: %s\n", - qres.Response.Log) + io.Printf("Log: %+v\n", + qres.Response) return qres.Response.Error } + resdata := qres.Response.Data // XXX in general, how do we know what to show? // proof := qres.Response.Proof height := qres.Response.Height - fmt.Printf("height: %d\ndata: %s\n", + io.Printf("height: %d\ndata: %s\n", height, string(resdata)) return nil diff --git a/tm2/pkg/crypto/keys/client/root.go b/tm2/pkg/crypto/keys/client/root.go index ad8983f2bb9..550dd408b77 100644 --- a/tm2/pkg/crypto/keys/client/root.go +++ b/tm2/pkg/crypto/keys/client/root.go @@ -18,7 +18,7 @@ type baseCfg struct { BaseOptions } -func NewRootCmd() *commands.Command { +func NewRootCmd(io *commands.IO) *commands.Command { cfg := &baseCfg{} cmd := commands.NewCommand( @@ -35,17 +35,17 @@ func NewRootCmd() *commands.Command { ) cmd.AddSubCommands( - newAddCmd(cfg), - newDeleteCmd(cfg), - newGenerateCmd(cfg), - newExportCmd(cfg), - newImportCmd(cfg), - newListCmd(cfg), - newSignCmd(cfg), - newVerifyCmd(cfg), - newQueryCmd(cfg), - newBroadcastCmd(cfg), - newMakeTxCmd(cfg), + newAddCmd(cfg, io), + newDeleteCmd(cfg, io), + newGenerateCmd(cfg, io), + newExportCmd(cfg, io), + newImportCmd(cfg, io), + newListCmd(cfg, io), + newSignCmd(cfg, io), + newVerifyCmd(cfg, io), + newQueryCmd(cfg, io), + newBroadcastCmd(cfg, io), + newMakeTxCmd(cfg, io), ) return cmd diff --git a/tm2/pkg/crypto/keys/client/send.go b/tm2/pkg/crypto/keys/client/send.go index 8f82778b1e3..6d19ffcb393 100644 --- a/tm2/pkg/crypto/keys/client/send.go +++ b/tm2/pkg/crypto/keys/client/send.go @@ -21,7 +21,7 @@ type sendCfg struct { to string } -func newSendCmd(rootCfg *makeTxCfg) *commands.Command { +func newSendCmd(rootCfg *makeTxCfg, io *commands.IO) *commands.Command { cfg := &sendCfg{ rootCfg: rootCfg, } @@ -34,7 +34,7 @@ func newSendCmd(rootCfg *makeTxCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execSend(cfg, args, commands.NewDefaultIO()) + return execSend(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/sign.go b/tm2/pkg/crypto/keys/client/sign.go index bfc39647141..761e0d7a563 100644 --- a/tm2/pkg/crypto/keys/client/sign.go +++ b/tm2/pkg/crypto/keys/client/sign.go @@ -28,7 +28,7 @@ type signCfg struct { pass string } -func newSignCmd(rootCfg *baseCfg) *commands.Command { +func newSignCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &signCfg{ rootCfg: rootCfg, } @@ -41,7 +41,7 @@ func newSignCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execSign(cfg, args, commands.NewDefaultIO()) + return execSign(cfg, args, io) }, ) } diff --git a/tm2/pkg/crypto/keys/client/verify.go b/tm2/pkg/crypto/keys/client/verify.go index 3dcc5f35dee..bb486c1a8fa 100644 --- a/tm2/pkg/crypto/keys/client/verify.go +++ b/tm2/pkg/crypto/keys/client/verify.go @@ -16,7 +16,7 @@ type verifyCfg struct { docPath string } -func newVerifyCmd(rootCfg *baseCfg) *commands.Command { +func newVerifyCmd(rootCfg *baseCfg, io *commands.IO) *commands.Command { cfg := &verifyCfg{ rootCfg: rootCfg, } @@ -29,7 +29,7 @@ func newVerifyCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execVerify(cfg, args, commands.NewDefaultIO()) + return execVerify(cfg, args, io) }, ) } diff --git a/tm2/pkg/std/memfile.go b/tm2/pkg/std/memfile.go index 99b8061ea3b..c632d3026d0 100644 --- a/tm2/pkg/std/memfile.go +++ b/tm2/pkg/std/memfile.go @@ -13,11 +13,15 @@ type MemFile struct { Body string } +// MemPackage represents the information and files of a package which will be +// stored in memory. It will generally be initialized by package gnolang's +// ReadMemPackage. +// // NOTE: in the future, a MemPackage may represent // updates/additional-files for an existing package. type MemPackage struct { - Name string - Path string + Name string // package name as declared by `package` + Path string // import path Files []*MemFile }