diff --git a/Cargo.lock b/Cargo.lock index 3c31d49bd..1b0e454f4 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,21 +4,21 @@ version = 4 [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -31,9 +31,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "approx" @@ -56,7 +56,7 @@ dependencies = [ "paste", "rand 0.9.2", "rand_xoshiro", - "thiserror 2.0.17", + "thiserror 2.0.18", "web-time", ] @@ -72,7 +72,7 @@ dependencies = [ "num-integer", "num-traits", "rand 0.9.2", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -87,15 +87,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "block-buffer" @@ -108,28 +108,28 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -147,9 +147,9 @@ dependencies = [ "ahash", "cached_proc_macro", "cached_proc_macro_types", - "hashbrown", + "hashbrown 0.15.5", "once_cell", - "thiserror 2.0.17", + "thiserror 2.0.18", "web-time", ] @@ -162,7 +162,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -173,18 +173,19 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "cc" -version = "1.2.23" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cpufeatures" @@ -253,15 +254,15 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -269,21 +270,21 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" dependencies = [ "csv-core", "itoa", "ryu", - "serde", + "serde_core", ] [[package]] name = "csv-core" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" dependencies = [ "memchr", ] @@ -309,7 +310,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -320,7 +321,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -331,9 +332,9 @@ checksum = "930c7171c8df9fb1782bdf9b918ed9ed2d33d1d22300abb754f9085bc48bf8e8" [[package]] name = "deranged" -version = "0.4.1" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", ] @@ -351,7 +352,7 @@ dependencies = [ "num-traits", "petgraph", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -366,13 +367,20 @@ dependencies = [ [[package]] name = "dyn-stack" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490bd48eb68fffcfed519b4edbfd82c69cbe741d175b84f0e0cbe8c57cbe0bdd" +checksum = "1c4713e43e2886ba72b8271aa66c93d722116acf7a75555cce11dcde84388fe8" dependencies = [ "bytemuck", + "dyn-stack-macros", ] +[[package]] +name = "dyn-stack-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d926b4d407d372f141f93bb444696142c29d32962ccbd3531117cf3aa0bfa9" + [[package]] name = "either" version = "1.15.0" @@ -388,7 +396,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -417,7 +425,7 @@ checksum = "3bf679796c0322556351f287a51b49e48f7c4986e727b5dd78c972d30e2e16cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -428,7 +436,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -439,25 +447,31 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "extendr-api" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67505d96c7faa49d20e749dba7ba2447db52c40a788fd88cc2b6bef02c02277a" +checksum = "ea54977c6e37236839ffcbc20b5dcea58aa32ae43fbef54a81e1011dc6b19061" dependencies = [ + "extendr-ffi", "extendr-macros", - "libR-sys", "once_cell", "paste", ] +[[package]] +name = "extendr-ffi" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c76777174a82bdb3e66872f580687d3d0143eed1df9b9cd72b321b9596a23ca7" + [[package]] name = "extendr-macros" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b58838056f294411d0b2c35ac1a2b24c507d6828b75f2c1e74f00ee9b99267" +checksum = "661cc4ae29de9c4dafe16cfcbda1dbb9f31bd2568f96ebad232cc1f9bcc8b04d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -507,7 +521,7 @@ checksum = "2cc4b8cd876795d3b19ddfd59b03faa303c0b8adb9af6e188e81fc647c485bb9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -528,6 +542,12 @@ dependencies = [ "reborrow", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + [[package]] name = "fixedbitset" version = "0.5.7" @@ -673,16 +693,17 @@ checksum = "5881e4c3c2433fe4905bb19cfd2b5d49d4248274862b68c27c33d9ba4e13f9ec" [[package]] name = "generator" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" dependencies = [ "cc", "cfg-if", "libc", "log", "rustversion", - "windows", + "windows-link", + "windows-result", ] [[package]] @@ -697,25 +718,25 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", ] [[package]] @@ -810,33 +831,40 @@ checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" [[package]] name = "glam" -version = "0.30.8" +version = "0.30.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12d847aeb25f41be4c0ec9587d624e9cd631bc007a8fd7ce3f5851e064c6460" +checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" [[package]] name = "half" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "bytemuck", "cfg-if", "crunchy", "num-traits", + "zerocopy", ] [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "heck" version = "0.5.0" @@ -845,9 +873,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "ident_case" @@ -857,12 +885,12 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.8.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.1", ] [[package]] @@ -878,15 +906,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -898,39 +926,33 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "libR-sys" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ac9752bc1e83f5a354a62b9e81bd8db4468b1008e29f262441e7f0e91e6bb3" - [[package]] name = "libc" -version = "0.2.171" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets", + "windows-link", ] [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "loom" @@ -956,9 +978,9 @@ dependencies = [ [[package]] name = "matrixmultiply" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" dependencies = [ "autocfg", "rawpointer", @@ -966,9 +988,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "nalgebra" @@ -1009,7 +1031,7 @@ dependencies = [ "glam 0.27.0", "glam 0.28.0", "glam 0.29.3", - "glam 0.30.8", + "glam 0.30.10", "matrixmultiply", "nalgebra-macros", "num-complex", @@ -1027,7 +1049,7 @@ checksum = "973e7178a678cfd059ccec50887658d482ce16b0aa9da3888ddeab5cd5eb4889" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -1145,7 +1167,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1171,9 +1193,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-integer" @@ -1207,9 +1229,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ "hermit-abi", "libc", @@ -1229,20 +1251,19 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pest" -version = "2.8.0" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" dependencies = [ "memchr", - "thiserror 2.0.17", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.0" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" dependencies = [ "pest", "pest_generator", @@ -1250,24 +1271,23 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.0" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "pest_meta" -version = "2.8.0" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ - "once_cell", "pest", "sha2", ] @@ -1279,7 +1299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", - "hashbrown", + "hashbrown 0.15.5", "indexmap", "serde", ] @@ -1304,15 +1324,15 @@ dependencies = [ "serde", "serde_json", "statrs", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pm_rs" @@ -1351,15 +1371,15 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" dependencies = [ "portable-atomic", ] @@ -1376,14 +1396,14 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.24", + "zerocopy", ] [[package]] name = "private-gemm-x86" -version = "0.1.18" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8138b380908e85071bdd6b2841a38b0858ef09848b754a15219d0b9ca90928" +checksum = "0af8c3e5087969c323f667ccb4b789fa0954f5aa650550e38e81cf9108be21b5" dependencies = [ "crossbeam", "defer", @@ -1397,9 +1417,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1433,9 +1453,9 @@ dependencies = [ [[package]] name = "qd" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73940173cf92cd24f3650f5f388946524026712a6ca170762340acf5fb3fde0f" +checksum = "ff8bb755b6008c3b41bf8a0866c8dd4e1245a2f011ceaa22a13ee55c538493e2" dependencies = [ "bytemuck", "libm", @@ -1445,18 +1465,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -1476,7 +1496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -1496,7 +1516,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -1505,16 +1525,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", ] [[package]] @@ -1543,14 +1563,14 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] name = "raw-cpuid" -version = "11.5.0" +version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ "bitflags", ] @@ -1589,9 +1609,9 @@ checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1600,21 +1620,21 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "safe_arch" @@ -1648,41 +1668,52 @@ checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -1706,9 +1737,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "simba" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" +checksum = "c99284beb21666094ba2b75bbceda012e610f5479dfcc2d6e2426f53197ffd95" dependencies = [ "approx", "num-complex", @@ -1719,9 +1750,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "sobol_burley" @@ -1731,9 +1762,9 @@ checksum = "09f37cae1d97c4078377153ede7a26f7813b689ad5c6b76ff45dc52e53afe1d1" [[package]] name = "spindle" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f794dedb367e82477aa6bbf83ea9bbce9bc074b3caacaa82fc4ba398ec9b701d" +checksum = "673aaca3d8aa5387a6eba861fbf984af5348d9df5d940c25c6366b19556fdf64" dependencies = [ "atomic-wait", "crossbeam", @@ -1773,9 +1804,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -1807,11 +1838,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -1822,56 +1853,55 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] name = "time" -version = "0.3.41" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -1879,9 +1909,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1890,20 +1920,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -1922,9 +1952,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -1941,9 +1971,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucd-trie" @@ -1953,9 +1983,9 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "valuable" @@ -1981,49 +2011,37 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2031,22 +2049,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.106", - "wasm-bindgen-backend", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] @@ -2063,9 +2081,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.32" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" dependencies = [ "bytemuck", "safe_arch", @@ -2073,111 +2091,24 @@ dependencies = [ [[package]] name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "windows" -version = "0.61.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" -dependencies = [ - "windows-collections", - "windows-core", - "windows-future", - "windows-link", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-core", -] - -[[package]] -name = "windows-core" -version = "0.61.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46ec44dc15085cea82cf9c78f85a9114c463a369786585ad2882d1ff0b0acf40" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core", - "windows-link", - "windows-threading", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", + "windows-sys 0.61.2", ] [[package]] name = "windows-link" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" - -[[package]] -name = "windows-numerics" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core", - "windows-link", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b895b5356fc36103d0f64dd1e94dfa7ac5633f1c9dd6e80fe9ec4adef69e09d" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a7ab927b2637c19b3dbe0965e75d8f2d30bdd697a1516191cad2ec4df8fb28a" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ "windows-link", ] @@ -2188,45 +2119,20 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] @@ -2237,78 +2143,36 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -2316,56 +2180,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" dependencies = [ - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" -dependencies = [ - "zerocopy-derive 0.8.24", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] -name = "zerocopy-derive" -version = "0.8.24" +name = "zmij" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/R/PMutilities.R b/R/PMutilities.R index 9fbf7c7fe..82514d7e7 100755 --- a/R/PMutilities.R +++ b/R/PMutilities.R @@ -102,7 +102,7 @@ logAxis <- function(side, grid = F, ...) { } axis(side, ticksat1, labels = labels, tcl = -0.5, lwd = 0, lwd.ticks = 1, ...) axis(side, ticksat2, labels = NA, tcl = -0.25, lwd = 0, lwd.ticks = 1, ...) - + if (grid & (side == 1 | side == 3)) abline(v = ticksat2, col = "lightgray", lty = 1) if (grid & (side == 2 | side == 4)) abline(h = ticksat2, col = "lightgray", lty = 1) } @@ -114,7 +114,7 @@ rmnorm <- function(n, mean, sigma) { sigma1 <- sigma ev <- eigen(sigma, symmetric = TRUE) retval <- ev$vectors %*% diag(sqrt(ev$values), length(ev$values)) %*% - t(ev$vectors) + t(ev$vectors) retval <- matrix(rnorm(n * ncol(sigma)), nrow = n) %*% retval retval <- sweep(retval, 2, mean, "+") colnames(retval) <- names(mean) @@ -122,8 +122,9 @@ rmnorm <- function(n, mean, sigma) { } # density function for the multivariate normal distribution, code from mvtnorm package -dmv_norm <- function(x, mean = rep(0, p), sigma = diag(p), log = FALSE, -checkSymmetry = TRUE) { +dmv_norm <- function( + x, mean = rep(0, p), sigma = diag(p), log = FALSE, + checkSymmetry = TRUE) { if (is.vector(x)) { x <- matrix(x, ncol = length(x)) } @@ -157,443 +158,443 @@ checkSymmetry = TRUE) { rss <- colSums(tmp^2) logretval <- -sum(log(diag(dec))) - 0.5 * p * log(2 * pi) - 0.5 * rss - } - names(logretval) <- rownames(x) - if (log) { - logretval - } else { - exp(logretval) - } } - - openHTML <- function(x) pander::openFileInOS(x) - - # parse NP_RF file only for final cycle information; used for bootstrapping - # indpts,ab,corden,nvar,nactve,iaddl,icyctot,par - - - - random_name <- function() { - n <- 1 - a <- do.call(paste0, replicate(5, sample(LETTERS, n, TRUE), FALSE)) - paste0(a, sprintf("%04d", sample(9999, n, TRUE)), sample(LETTERS, n, TRUE)) - } - - - - - # check for numeric id and convert to number if necessary - checkID <- function(id) { - id <- gsub("^[[:blank:]]+", "", id) - id <- gsub("[[:blank:]]+$", "", id) - idNonNum <- suppressWarnings(any(is.na(as.numeric(id)))) - if (!idNonNum) id <- as.numeric(id) - return(id) - } - - # extract pattern from strings - strparse <- function(pattern, x) { - match <- regexpr(pattern, x, ignore.case = T) - start <- match[1] - stop <- match[1] + attr(match, "match.length") - 1 - return(substr(x, start, stop)) - } - - - # parse blocks in new model template - parseBlocks <- function(model) { - modelFile <- scan(model, what = "character", sep = "\n", blank.lines.skip = T, quiet = T) - # remove comment lines - commLn <- grep("^C ", modelFile) - if (length(commLn) > 0) modelFile <- modelFile[-commLn] - if (length(grep("TSTMULT", modelFile)) > 0) { - return(list(status = 0, model = model)) - } - # stop, we already have a fortran model file - blockStart <- grep("#", modelFile) - blockStop <- c(blockStart[-1] - 1, length(modelFile)) - headers <- tolower(modelFile[blockStart]) - primVar <- blockStart[grep("#pri", headers)] - covar <- blockStart[grep("#cov", headers)] - secVar <- blockStart[grep("#sec", headers)] - bolus <- blockStart[grep("#bol", headers)] - ini <- blockStart[grep("#ini", headers)] - f <- blockStart[grep("#f", headers)] - lag <- blockStart[grep("#lag", headers)] - diffeq <- blockStart[grep("#dif", headers)] - eqn <- blockStart[grep("#eqn", headers)] - output <- blockStart[grep("#out", headers)] - error <- blockStart[grep("#err", headers)] - extra <- blockStart[grep("#ext", headers)] - - if (length(diffeq) > 0) { - eqn <- diffeq - } # change diffeq block to eqn for more general - - headerPresent <- which(c( - length(primVar) > 0, length(covar) > 0, length(secVar) > 0, length(bolus) > 0, length(ini) > 0, - length(f) > 0, length(lag) > 0, length(eqn) > 0, length(output) > 0, length(error) > 0, length(extra) > 0 - )) - missing_mandatory <- which(!c(1, 8:10) %in% headerPresent) - if(length(missing_mandatory)){ - # missing mandatory headers - missing_headers <- c("#PRI", "#EQN", "#OUT", "#ERR")[missing_mandatory] - cli::cli_abort(c("x" = "Model file is missing mandatory header{?s}: {missing_headers} ")) - } - - headerOrder <- c(primVar, covar, secVar, bolus, ini, f, lag, eqn, output, error, extra) - blockStart <- blockStart[rank(headerOrder)] - blockStop <- blockStop[rank(headerOrder)] - - # remove headers that have no information - ok <- mapply(function(x, y) x != y, blockStart, blockStop) - blockStart <- blockStart[ok] - blockStop <- blockStop[ok] - headerPresent <- headerPresent[ok] - - # get blocks - blocks <- list(primVar = NA, covar = NA, secVar = NA, bolus = NA, ini = NA, f = NA, lag = NA, eqn = NA, output = NA, error = NA, extra = NA) - for (i in 1:length(headerPresent)) { - temp <- modelFile[(blockStart[i] + 1):blockStop[i]] - allblank <- grep("^[[:blank:]]+$", temp) - if (length(allblank) > 0) temp <- temp[-allblank] - blocks[[headerPresent[i]]] <- tolower(temp) - } - emptyHeaders <- which(is.na(blocks)) - if (length(emptyHeaders) > 0) blocks[emptyHeaders] <- "" - return(blocks) - } # end parseBlocks - - # check all blocks statements for more than maxwidth characters and insert line break if necessary - chunks <- function(x, maxwidth = 60) { - for (i in 1:length(x)) { - for (j in 1:length(x[[i]])) { - temp <- x[[i]][j] - if (nchar(temp) > maxwidth) { - numchunks <- floor(nchar(temp) / maxwidth) - if (nchar(temp) %% maxwidth > 0) numchunks <- numchunks + 1 - splitchunks <- vector("character", numchunks) - chunkindex <- c(seq(0, numchunks * maxwidth, maxwidth), nchar(temp)) - for (k in 1:numchunks) { - splitchunks[k] <- substr(temp, chunkindex[k] + 1, chunkindex[k + 1]) - } - x[[i]][j] <- paste(splitchunks, collapse = "\n & ") + names(logretval) <- rownames(x) + if (log) { + logretval + } else { + exp(logretval) + } +} + +openHTML <- function(x) pander::openFileInOS(x) + +# parse NP_RF file only for final cycle information; used for bootstrapping +# indpts,ab,corden,nvar,nactve,iaddl,icyctot,par + + + +random_name <- function() { + n <- 1 + a <- do.call(paste0, replicate(5, sample(LETTERS, n, TRUE), FALSE)) + paste0(a, sprintf("%04d", sample(9999, n, TRUE)), sample(LETTERS, n, TRUE)) +} + + + + +# check for numeric id and convert to number if necessary +checkID <- function(id) { + id <- gsub("^[[:blank:]]+", "", id) + id <- gsub("[[:blank:]]+$", "", id) + idNonNum <- suppressWarnings(any(is.na(as.numeric(id)))) + if (!idNonNum) id <- as.numeric(id) + return(id) +} + +# extract pattern from strings +strparse <- function(pattern, x) { + match <- regexpr(pattern, x, ignore.case = T) + start <- match[1] + stop <- match[1] + attr(match, "match.length") - 1 + return(substr(x, start, stop)) +} + + +# parse blocks in new model template +parseBlocks <- function(model) { + modelFile <- scan(model, what = "character", sep = "\n", blank.lines.skip = T, quiet = T) + # remove comment lines + commLn <- grep("^C ", modelFile) + if (length(commLn) > 0) modelFile <- modelFile[-commLn] + if (length(grep("TSTMULT", modelFile)) > 0) { + return(list(status = 0, model = model)) + } + # stop, we already have a fortran model file + blockStart <- grep("#", modelFile) + blockStop <- c(blockStart[-1] - 1, length(modelFile)) + headers <- tolower(modelFile[blockStart]) + primVar <- blockStart[grep("#pri", headers)] + covar <- blockStart[grep("#cov", headers)] + secVar <- blockStart[grep("#sec", headers)] + bolus <- blockStart[grep("#bol", headers)] + ini <- blockStart[grep("#ini", headers)] + f <- blockStart[grep("#f", headers)] + lag <- blockStart[grep("#lag", headers)] + diffeq <- blockStart[grep("#dif", headers)] + eqn <- blockStart[grep("#eqn", headers)] + output <- blockStart[grep("#out", headers)] + error <- blockStart[grep("#err", headers)] + extra <- blockStart[grep("#ext", headers)] + + if (length(diffeq) > 0) { + eqn <- diffeq + } # change diffeq block to eqn for more general + + headerPresent <- which(c( + length(primVar) > 0, length(covar) > 0, length(secVar) > 0, length(bolus) > 0, length(ini) > 0, + length(f) > 0, length(lag) > 0, length(eqn) > 0, length(output) > 0, length(error) > 0, length(extra) > 0 + )) + missing_mandatory <- which(!c(1, 8:10) %in% headerPresent) + if (length(missing_mandatory)) { + # missing mandatory headers + missing_headers <- c("#PRI", "#EQN", "#OUT", "#ERR")[missing_mandatory] + cli::cli_abort(c("x" = "Model file is missing mandatory header{?s}: {missing_headers} ")) + } + + headerOrder <- c(primVar, covar, secVar, bolus, ini, f, lag, eqn, output, error, extra) + blockStart <- blockStart[rank(headerOrder)] + blockStop <- blockStop[rank(headerOrder)] + + # remove headers that have no information + ok <- mapply(function(x, y) x != y, blockStart, blockStop) + blockStart <- blockStart[ok] + blockStop <- blockStop[ok] + headerPresent <- headerPresent[ok] + + # get blocks + blocks <- list(primVar = NA, covar = NA, secVar = NA, bolus = NA, ini = NA, f = NA, lag = NA, eqn = NA, output = NA, error = NA, extra = NA) + for (i in 1:length(headerPresent)) { + temp <- modelFile[(blockStart[i] + 1):blockStop[i]] + allblank <- grep("^[[:blank:]]+$", temp) + if (length(allblank) > 0) temp <- temp[-allblank] + blocks[[headerPresent[i]]] <- tolower(temp) + } + emptyHeaders <- which(is.na(blocks)) + if (length(emptyHeaders) > 0) blocks[emptyHeaders] <- "" + return(blocks) +} # end parseBlocks + +# check all blocks statements for more than maxwidth characters and insert line break if necessary +chunks <- function(x, maxwidth = 60) { + for (i in 1:length(x)) { + for (j in 1:length(x[[i]])) { + temp <- x[[i]][j] + if (nchar(temp) > maxwidth) { + numchunks <- floor(nchar(temp) / maxwidth) + if (nchar(temp) %% maxwidth > 0) numchunks <- numchunks + 1 + splitchunks <- vector("character", numchunks) + chunkindex <- c(seq(0, numchunks * maxwidth, maxwidth), nchar(temp)) + for (k in 1:numchunks) { + splitchunks[k] <- substr(temp, chunkindex[k] + 1, chunkindex[k + 1]) } + x[[i]][j] <- paste(splitchunks, collapse = "\n & ") } } - return(x) - } # end chunks - - # change dX[digit] to XP(digit) and X[digit] to X(digit) - fortranize <- function(block) { - block <- purrr::map_chr(block, ~ gsub("dX\\[(\\d+)\\]", "XP\\(\\1\\)", .x, ignore.case = T, perl = T)) - block <- purrr::map_chr(block, ~ gsub("BOLUS\\[\\d+\\]", "", .x, ignore.case = T, perl = T)) - block <- purrr::map_chr(block, ~ gsub("\\[(\\d+)\\]", "\\(\\1\\)", .x, ignore.case = T, perl = T)) - return(block) - } - - - # convert new model template to model fortran file - makeModel <- function(model = "model.txt", data = "data.csv", engine, backend = getPMoptions("backend"), write = T, quiet = F) { - blocks <- parseBlocks(model) - - # check for reserved variable names - reserved <- c( - "ndim", "t", "x", "xp", "rpar", "ipar", "p", "r", "b", "npl", "numeqt", "ndrug", "nadd", "rateiv", "cv", - "n", "nd", "ni", "nup", "nuic", "np", "nbcomp", "psym", "fa", "lag", "tin", "tout" - ) - conflict <- c(match(tolower(blocks$primVar), reserved, nomatch = -99), match(tolower(blocks$secVar), reserved, nomatch = -99), match(tolower(blocks$covar), reserved, nomatch = -99)) - nconflict <- sum(conflict != -99) - if (nconflict > 0) { - msg <- paste("\n", paste(paste("'", reserved[conflict[conflict != -99]], "'", sep = ""), collapse = ", "), " ", c("is a", "are")[1 + as.numeric(nconflict > 1)], " reserved ", c("name", "names")[1 + as.numeric(nconflict > 1)], ", regardless of case.\nPlease choose non-reserved parameter/covariate names.\n", sep = "") - return(list(status = -1, msg = msg)) - } - - # check all blocks statements for more than maxwidth characters and insert line break if necessary - maxwidth <- 60 - blocks <- chunks(x = blocks, maxwidth = maxwidth) - - # ensure in fortran format: dX -> XP and [] -> () - blocks <- purrr::map(blocks, fortranize) - - # primary variable definitions - npvar <- length(blocks$primVar) - psym <- vector("character", npvar) - pvardef <- psym - if (length(grep(";", blocks$primVar)) > 0) { - # using ';' as separator - sep <- ";" - } else { - if (length(grep(",", blocks$primVar)) > 0) { - # using ',' as separator - sep <- "," - } else { - return(list(status = -1, msg = "\nPrimary variables should be defined as 'var,lower_val,upper_val' or 'var,fixed_val'.\n")) - } - } - - # find out if any are fixed to be positive only for IT2B - fixedpos <- grep("\\+", blocks$primVar) - if (length(fixedpos) > 0) blocks$primVar <- gsub("\\+", "", blocks$primVar) - - # find out if any are to be fixed (constant) - fixcon <- grep("!", blocks$primVar) - nofix <- length(fixcon) - if (nofix > 0) blocks$primVar <- gsub("!", "", blocks$primVar) - - - # get limits [a,b] on primary variables - splitprimVar <- strsplit(blocks$primVar, sep) - a <- as.numeric(unlist(lapply(splitprimVar, function(x) x[2]))) - b <- as.numeric(unlist(lapply(splitprimVar, function(x) x[3]))) - - # set parameter type: 1 for random, 0 for constant, -1 for random but pos (IT2B only) and 2 for fixed random - ptype <- c(1, 2)[1 + as.numeric(is.na(b))] - # if any fixed constant variables are present, set ptype to 0 - if (nofix > 0) ptype[fixcon] <- 0 - - # npvar is total number of parameters - # nvar is number of random (estimated) parameters - # nranfix is number of fixed (but unknown) parameters - # nofix is number of constant parameters - nranfix <- sum(as.numeric(is.na(b))) - nofix - nvar <- npvar - nofix - nranfix - - if ((engine$alg == "IT" | engine$alg == "ERR") & length(fixedpos) > 0) ptype[fixedpos] <- -1 - - if (nofix > 0) { - valfix <- a[which(ptype == 0)] + } + return(x) +} # end chunks + +# change dX[digit] to XP(digit) and X[digit] to X(digit) +fortranize <- function(block) { + block <- purrr::map_chr(block, ~ gsub("dX\\[(\\d+)\\]", "XP\\(\\1\\)", .x, ignore.case = T, perl = T)) + block <- purrr::map_chr(block, ~ gsub("BOLUS\\[\\d+\\]", "", .x, ignore.case = T, perl = T)) + block <- purrr::map_chr(block, ~ gsub("\\[(\\d+)\\]", "\\(\\1\\)", .x, ignore.case = T, perl = T)) + return(block) +} + + +# convert new model template to model fortran file +makeModel <- function(model = "model.txt", data = "data.csv", engine, backend = getPMoptions("backend"), write = T, quiet = F) { + blocks <- parseBlocks(model) + + # check for reserved variable names + reserved <- c( + "ndim", "t", "x", "xp", "rpar", "ipar", "p", "r", "b", "npl", "numeqt", "ndrug", "nadd", "rateiv", "cv", + "n", "nd", "ni", "nup", "nuic", "np", "nbcomp", "psym", "fa", "lag", "tin", "tout" + ) + conflict <- c(match(tolower(blocks$primVar), reserved, nomatch = -99), match(tolower(blocks$secVar), reserved, nomatch = -99), match(tolower(blocks$covar), reserved, nomatch = -99)) + nconflict <- sum(conflict != -99) + if (nconflict > 0) { + msg <- paste("\n", paste(paste("'", reserved[conflict[conflict != -99]], "'", sep = ""), collapse = ", "), " ", c("is a", "are")[1 + as.numeric(nconflict > 1)], " reserved ", c("name", "names")[1 + as.numeric(nconflict > 1)], ", regardless of case.\nPlease choose non-reserved parameter/covariate names.\n", sep = "") + return(list(status = -1, msg = msg)) + } + + # check all blocks statements for more than maxwidth characters and insert line break if necessary + maxwidth <- 60 + blocks <- chunks(x = blocks, maxwidth = maxwidth) + + # ensure in fortran format: dX -> XP and [] -> () + blocks <- purrr::map(blocks, fortranize) + + # primary variable definitions + npvar <- length(blocks$primVar) + psym <- vector("character", npvar) + pvardef <- psym + if (length(grep(";", blocks$primVar)) > 0) { + # using ';' as separator + sep <- ";" + } else { + if (length(grep(",", blocks$primVar)) > 0) { + # using ',' as separator + sep <- "," } else { - valfix <- NA + return(list(status = -1, msg = "\nPrimary variables should be defined as 'var,lower_val,upper_val' or 'var,fixed_val'.\n")) } - - if (nranfix > 0) { - valranfix <- a[which(ptype == 2)] + } + + # find out if any are fixed to be positive only for IT2B + fixedpos <- grep("\\+", blocks$primVar) + if (length(fixedpos) > 0) blocks$primVar <- gsub("\\+", "", blocks$primVar) + + # find out if any are to be fixed (constant) + fixcon <- grep("!", blocks$primVar) + nofix <- length(fixcon) + if (nofix > 0) blocks$primVar <- gsub("!", "", blocks$primVar) + + + # get limits [a,b] on primary variables + splitprimVar <- strsplit(blocks$primVar, sep) + a <- as.numeric(unlist(lapply(splitprimVar, function(x) x[2]))) + b <- as.numeric(unlist(lapply(splitprimVar, function(x) x[3]))) + + # set parameter type: 1 for random, 0 for constant, -1 for random but pos (IT2B only) and 2 for fixed random + ptype <- c(1, 2)[1 + as.numeric(is.na(b))] + # if any fixed constant variables are present, set ptype to 0 + if (nofix > 0) ptype[fixcon] <- 0 + + # npvar is total number of parameters + # nvar is number of random (estimated) parameters + # nranfix is number of fixed (but unknown) parameters + # nofix is number of constant parameters + nranfix <- sum(as.numeric(is.na(b))) - nofix + nvar <- npvar - nofix - nranfix + + if ((engine$alg == "IT" | engine$alg == "ERR") & length(fixedpos) > 0) ptype[fixedpos] <- -1 + + if (nofix > 0) { + valfix <- a[which(ptype == 0)] + } else { + valfix <- NA + } + + if (nranfix > 0) { + valranfix <- a[which(ptype == 2)] + } else { + valranfix <- NA + } + + ab.df <- data.frame(a = a[which(ptype == 1)], b = b[which(ptype == 1)]) + + + # replace a,b with SIM limits argument if it is present + if (engine$alg == "SIM" & !all(is.na(engine$limits))) { + if (nrow(engine$limits) == nvar) { + # make sure same row number + replA <- engine$limits[, 1] + replB <- engine$limits[, 2] + ab.df$a[!is.na(replA)] <- replA[!is.na(replA)] + ab.df$b[!is.na(replB)] <- replB[!is.na(replB)] } else { - valranfix <- NA + return(list(status = -1, msg = "Your limit block does not have the same number of parameters as the model file.\n")) } - - ab.df <- data.frame(a = a[which(ptype == 1)], b = b[which(ptype == 1)]) - - - # replace a,b with SIM limits argument if it is present - if (engine$alg == "SIM" & !all(is.na(engine$limits))) { - if (nrow(engine$limits) == nvar) { - # make sure same row number - replA <- engine$limits[, 1] - replB <- engine$limits[, 2] - ab.df$a[!is.na(replA)] <- replA[!is.na(replA)] - ab.df$b[!is.na(replB)] <- replB[!is.na(replB)] - } else { - return(list(status = -1, msg = "Your limit block does not have the same number of parameters as the model file.\n")) - } - } - - if (nofix > 0 & any(is.na(valfix))) { - return(list(status = -1, msg = "One or more variables did not have any boundaries.\n")) + } + + if (nofix > 0 & any(is.na(valfix))) { + return(list(status = -1, msg = "One or more variables did not have any boundaries.\n")) + } + if (nranfix > 0 & any(is.na(valranfix))) { + return(list(status = -1, msg = "One or more variables did not have any boundaries.\n")) + } + + # set grid point index for NPAG if not supplied + if (engine$indpts == -99) { + indpts <- switch(nvar, + 1, + 1, + 3, + 4, + 6 + ) + if (is.null(indpts)) indpts <- 100 + nvar - 5 + if (indpts > 108) indpts <- 108 + } else { + indpts <- engine$indpts + } + + + # transform ab + if (nrow(ab.df) > 0) { + ab <- paste(t(as.matrix(ab.df))) + ab <- c(paste(ab[1:(length(ab) - 1)], "t", sep = ""), ab[length(ab)]) + ab[seq(1, 2 * nvar, 2)] <- sub("t", " ", ab[seq(1, 2 * nvar, 2)]) + ab[seq(2, 2 * nvar, 2)] <- sub("t", "\n", ab[seq(2, 2 * nvar, 2)]) + ab <- paste(ab, collapse = "") + } + + blocks$primVar <- unlist(lapply(splitprimVar, function(x) x[1])) + + for (i in 1:npvar) { + psym[i] <- paste("PSYM(", i, ")='", blocks$primVar[i], "'", sep = "") + pvardef[i] <- paste(blocks$primVar[i], "=P(", i, ")", sep = "") + } + + + # covariate definitions + if (blocks$covar[1] != "") { + ncov <- length(blocks$covar) + interpol <- grep("!", blocks$covar) + blocks$covar <- gsub("!", "", blocks$covar) + covardef <- vector("character", ncov) + for (i in 1:ncov) { + covardef[i] <- paste(blocks$covar[i], "=CV(", i, ")", sep = "") } - if (nranfix > 0 & any(is.na(valranfix))) { - return(list(status = -1, msg = "One or more variables did not have any boundaries.\n")) + if (!identical(1:ncov, which(tolower(engine$covnames) %in% tolower(blocks$covar)))) { + return(list(status = -1, msg = "The covariate set in your model file was not in the same order as in your data file.\n")) } - - # set grid point index for NPAG if not supplied - if (engine$indpts == -99) { - indpts <- switch(nvar, - 1, - 1, - 3, - 4, - 6 - ) - if (is.null(indpts)) indpts <- 100 + nvar - 5 - if (indpts > 108) indpts <- 108 + } else { + covardef <- "" + interpol <- grep("!", blocks$covar) + } + if (engine$ncov > 0) { + ctype <- rep(2, engine$ncov) + } else { + ctype <- -99 + } + # set covariate type based on number of covariates in data file, default is 2, interpolated + if (length(interpol) > 0) ctype[interpol] <- 1 # change those in model file with "!" to constant + + # secondary variable definitions + svardef <- blocks$secVar + + # get secondary variables and remove continuation lines beginning with "&" + secVarNames <- gsub("[[:blank:]]", "", unlist(lapply(strsplit(svardef, "="), function(x) x[1]))) + secVarNames[is.na(secVarNames)] <- "" + oldContLines <- grep("^\\+", secVarNames) + if (length(oldContLines > 0)) { + return(list(status = -1, msg = "\nThe model file format has changed. Please replace '+' with '&' in all continuation lines.\n")) + } + contLines <- grep("^&", secVarNames) + + if (length(contLines) > 0) { + secVarNames <- secVarNames[-contLines] + svardef <- gsub("^&", "", svardef) + } + + # take out any extra declarations in eqn to add to declarations in subroutine + diffdec <- grep("COMMON|EXTERNAL|DIMENSION", blocks$eqn, ignore.case = T) + if (length(diffdec) > 0) { + diffstate <- blocks$eqn[diffdec] + blocks$eqn <- blocks$eqn[-diffdec] + } else { + diffstate <- "" + } + + # detect N + if (blocks$eqn[1] == "" | grepl("^\\{algebraic:", blocks$eqn[1])) { + if ("KE" %in% toupper(secVarNames) | "KE" %in% toupper(blocks$primVar)) { + N <- -1 } else { - indpts <- engine$indpts - } - - - # transform ab - if (nrow(ab.df) > 0) { - ab <- paste(t(as.matrix(ab.df))) - ab <- c(paste(ab[1:(length(ab) - 1)], "t", sep = ""), ab[length(ab)]) - ab[seq(1, 2 * nvar, 2)] <- sub("t", " ", ab[seq(1, 2 * nvar, 2)]) - ab[seq(2, 2 * nvar, 2)] <- sub("t", "\n", ab[seq(2, 2 * nvar, 2)]) - ab <- paste(ab, collapse = "") + N <- 0 } - - blocks$primVar <- unlist(lapply(splitprimVar, function(x) x[1])) - - for (i in 1:npvar) { - psym[i] <- paste("PSYM(", i, ")='", blocks$primVar[i], "'", sep = "") - pvardef[i] <- paste(blocks$primVar[i], "=P(", i, ")", sep = "") + } else { + # get number of equations and verify with data file + # find statements with XP(digit) or dX[digit] + compLines <- grep("XP\\([[:digit:]]+\\)|dX\\[[[:digit:]]+\\]", blocks$eqn, ignore.case = T) + if (length(compLines) == 0) { + N <- 0 + } else { + compStatements <- sapply(blocks$eqn[compLines], function(x) strparse("XP\\([[:digit:]]+\\)|dX\\[[[:digit:]]+\\]", x)) + compNumbers <- sapply(compStatements, function(x) strparse("[[:digit:]]+", x)) + # get max number + N <- max(as.numeric(compNumbers)) } - - - # covariate definitions - if (blocks$covar[1] != "") { - ncov <- length(blocks$covar) - interpol <- grep("!", blocks$covar) - blocks$covar <- gsub("!", "", blocks$covar) - covardef <- vector("character", ncov) - for (i in 1:ncov) { - covardef[i] <- paste(blocks$covar[i], "=CV(", i, ")", sep = "") + } + + # figure out model if N = -1 and if so, assign values to required KA,KE,V,KCP,KPC + # in future, use {algebraic: xx} which is in model files now to select correct algebraic model + # for now, comment the eqn lines in fortran if present + if (length(grep("^\\{algebraic:", blocks$eqn[1])) > 0) { + blocks$eqn[1] <- "This model uses algebraic solutions. Differential equations provided here for reference only." + blocks$eqn <- purrr::map_chr(blocks$eqn, \(x) paste0("! ", x)) + } + + + reqVars <- c("KA", "KE", "KCP", "KPC", "V") + matchVars <- match(reqVars, toupper(c(blocks$primVar, secVarNames))) + if (N == -1) { + if (any(is.na(matchVars))) { + missVars <- reqVars[is.na(matchVars)] + if ("KE" %in% toupper(missVars)) { + return(list(status = -1, msg = "\nYou have specified an algebraic model, which requires a variable named 'KE'\n")) } - if (!identical(1:ncov, which(tolower(engine$covnames) %in% tolower(blocks$covar)))) { - return(list(status = -1, msg = "The covariate set in your model file was not in the same order as in your data file.\n")) + if ("V" %in% toupper(missVars)) { + return(list(status = -1, msg = "\nYou have specified an algebraic model, which requires a variable named 'V'\n")) } + missVarValues <- paste(missVars, "=0", sep = "") + if (length(missVarValues) > 0) svardef <- c(svardef, missVarValues) + svardef <- svardef[svardef != ""] + # add new secondary variables that won't be estimated + secVarNames <- c(secVarNames, missVars) + secVarNames <- secVarNames[secVarNames != ""] } else { - covardef <- "" - interpol <- grep("!", blocks$covar) - } - if (engine$ncov > 0) { - ctype <- rep(2, engine$ncov) - } else { - ctype <- -99 - } - # set covariate type based on number of covariates in data file, default is 2, interpolated - if (length(interpol) > 0) ctype[interpol] <- 1 # change those in model file with "!" to constant - - # secondary variable definitions - svardef <- blocks$secVar - - # get secondary variables and remove continuation lines beginning with "&" - secVarNames <- gsub("[[:blank:]]", "", unlist(lapply(strsplit(svardef, "="), function(x) x[1]))) - secVarNames[is.na(secVarNames)] <- "" - oldContLines <- grep("^\\+", secVarNames) - if (length(oldContLines > 0)) { - return(list(status = -1, msg = "\nThe model file format has changed. Please replace '+' with '&' in all continuation lines.\n")) + missVars <- NA } - contLines <- grep("^&", secVarNames) - - if (length(contLines) > 0) { - secVarNames <- secVarNames[-contLines] - svardef <- gsub("^&", "", svardef) - } - - # take out any extra declarations in eqn to add to declarations in subroutine - diffdec <- grep("COMMON|EXTERNAL|DIMENSION", blocks$eqn, ignore.case = T) - if (length(diffdec) > 0) { - diffstate <- blocks$eqn[diffdec] - blocks$eqn <- blocks$eqn[-diffdec] - } else { - diffstate <- "" - } - - # detect N - if (blocks$eqn[1] == "" | grepl("^\\{algebraic:", blocks$eqn[1])) { - if ("KE" %in% toupper(secVarNames) | "KE" %in% toupper(blocks$primVar)) { - N <- -1 - } else { - N <- 0 - } + } else { + if (any(is.na(matchVars))) { + missVars <- reqVars[is.na(matchVars)] } else { - # get number of equations and verify with data file - # find statements with XP(digit) or dX[digit] - compLines <- grep("XP\\([[:digit:]]+\\)|dX\\[[[:digit:]]+\\]", blocks$eqn, ignore.case = T) - if (length(compLines) == 0) { - N <- 0 - } else { - compStatements <- sapply(blocks$eqn[compLines], function(x) strparse("XP\\([[:digit:]]+\\)|dX\\[[[:digit:]]+\\]", x)) - compNumbers <- sapply(compStatements, function(x) strparse("[[:digit:]]+", x)) - # get max number - N <- max(as.numeric(compNumbers)) - } + missVars <- NA } - - # figure out model if N = -1 and if so, assign values to required KA,KE,V,KCP,KPC - # in future, use {algebraic: xx} which is in model files now to select correct algebraic model - # for now, comment the eqn lines in fortran if present - if (length(grep("^\\{algebraic:", blocks$eqn[1])) > 0) { - blocks$eqn[1] <- "This model uses algebraic solutions. Differential equations provided here for reference only." - blocks$eqn <- purrr::map_chr(blocks$eqn, \(x) paste0("! ", x)) - } - - - reqVars <- c("KA", "KE", "KCP", "KPC", "V") - matchVars <- match(reqVars, toupper(c(blocks$primVar, secVarNames))) - if (N == -1) { - if (any(is.na(matchVars))) { - missVars <- reqVars[is.na(matchVars)] - if ("KE" %in% toupper(missVars)) { - return(list(status = -1, msg = "\nYou have specified an algebraic model, which requires a variable named 'KE'\n")) - } - if ("V" %in% toupper(missVars)) { - return(list(status = -1, msg = "\nYou have specified an algebraic model, which requires a variable named 'V'\n")) - } - missVarValues <- paste(missVars, "=0", sep = "") - if (length(missVarValues) > 0) svardef <- c(svardef, missVarValues) - svardef <- svardef[svardef != ""] - # add new secondary variables that won't be estimated - secVarNames <- c(secVarNames, missVars) - secVarNames <- secVarNames[secVarNames != ""] - } else { - missVars <- NA - } - } else { - if (any(is.na(matchVars))) { - missVars <- reqVars[is.na(matchVars)] - } else { - missVars <- NA - } + } + + # extract bolus inputs and create bolus block, then remove bolus[x] from equations + bolus <- purrr::map(blocks$eqn, ~ stringr::str_extract_all(.x, regex("B[\\[\\(]\\d+|BOL[\\[\\(]\\d+|BOLUS[\\[\\(]\\d+", ignore_case = TRUE), simplify = FALSE)) + blocks$bolus <- purrr::imap(bolus, \(x, idx){ + if (length(x[[1]]) > 0) { + paste0("NBCOMP(", stringr::str_extract(x[[1]], "\\d+$"), ") = ", idx) } - - # extract bolus inputs and create bolus block, then remove bolus[x] from equations - bolus <- purrr::map(blocks$eqn, ~ stringr::str_extract_all(.x, regex("B[\\[\\(]\\d+|BOL[\\[\\(]\\d+|BOLUS[\\[\\(]\\d+", ignore_case = TRUE), simplify = FALSE)) - blocks$bolus <- purrr::imap(bolus, \(x, idx){ - if (length(x[[1]]) > 0) { - paste0("NBCOMP(", stringr::str_extract(x[[1]], "\\d+$"), ") = ", idx) - } - }) %>% unlist() - blocks$eqn <- purrr::map(blocks$eqn, \(x) stringr::str_replace_all(x, regex("(\\+*|-*|\\**)\\s*B[\\[\\(]\\d+[\\]\\)]|(\\+*|-*|\\**)\\s*BOL[\\[\\(]\\d+[\\]\\)]|(\\+*|-*|\\**)\\s*BOLUS[\\[\\(]\\d+[\\]\\)]", ignore_case = TRUE), "")) %>% + }) %>% unlist() + blocks$eqn <- purrr::map(blocks$eqn, \(x) stringr::str_replace_all(x, regex("(\\+*|-*|\\**)\\s*B[\\[\\(]\\d+[\\]\\)]|(\\+*|-*|\\**)\\s*BOL[\\[\\(]\\d+[\\]\\)]|(\\+*|-*|\\**)\\s*BOLUS[\\[\\(]\\d+[\\]\\)]", ignore_case = TRUE), "")) %>% unlist() - - # replace R[x] or R(x) with RATEIV(x) - blocks$eqn <- purrr::map(blocks$eqn, \(x) stringr::str_replace_all(x, regex("R[\\[\\(](\\d+)[\\]\\)]", ignore_case = TRUE), "RATEIV\\(\\1\\)")) %>% + + # replace R[x] or R(x) with RATEIV(x) + blocks$eqn <- purrr::map(blocks$eqn, \(x) stringr::str_replace_all(x, regex("R[\\[\\(](\\d+)[\\]\\)]", ignore_case = TRUE), "RATEIV\\(\\1\\)")) %>% unlist() - - # get number of equations and verify with data file - # find statements with Y(digit) or Y[digit] - outputLines <- grep("Y\\([[:digit:]]+\\)|Y\\[[[:digit:]]+\\]", blocks$output, ignore.case = T) - if (length(outputLines) == 0) { - return(list(status = -1, msg = "\nYou must have at least one output equation of the form 'Y[1] = ...'\n")) - } - # extract numbers - outputStatements <- sapply(blocks$output[outputLines], function(x) strparse("Y\\([[:digit:]]+\\)|Y\\[[[:digit:]]+\\]", x)) - outputNumbers <- sapply(outputStatements, function(x) strparse("[[:digit:]]", x)) - # get max number - modelnumeqt <- max(as.numeric(outputNumbers)) - if (modelnumeqt != engine$numeqt) { - return(list(status = -1, msg = "\nThe number of output equations in the model file\ndoes not match the maximum value of outeq in your datafile.\n")) - } - - # remove leading ampersands from getfa, getix, gettlag if present - oldContLines <- grep("^\\+", c(blocks$f, blocks$ini, blocks$lag)) - if (length(oldContLines > 0)) { - return(list(status = -1, msg = "\nThe model file format has changed. Please replace '+' with '&' in all continuation lines.\n")) - } - if (length(grep("^&", blocks$f) > 0)) blocks$f <- gsub("^&", "", blocks$f) - if (length(grep("^&", blocks$ini) > 0)) blocks$ini <- gsub("^&", "", blocks$ini) - if (length(grep("^&", blocks$lag) > 0)) blocks$lag <- gsub("^&", "", blocks$lag) - - # variable declarations for fortran and make sure not >maxwidth characters - if (secVarNames[1] != "") { - vardec <- paste("REAL*8 ", paste(blocks$primVar, collapse = ","), ",", paste(secVarNames, collapse = ","), sep = "") - } else { - vardec <- paste("REAL*8 ", paste(blocks$primVar, collapse = ","), sep = "") - } - if (blocks$covar[1] != "") { - vardec <- paste(vardec, ",", paste(blocks$covar, collapse = ","), sep = "") - } - if (nchar(vardec) > maxwidth) { - vardec <- paste(unlist(strsplit(vardec, ",")), collapse = ",\n & ") - } - - # error - blocks$error <- tolower(gsub("[[:space:]]", "", blocks$error)) - # check to make sure coefficient lines are the same number as outputs - nErrCoeff <- length(blocks$error) - 1 - if (nErrCoeff != modelnumeqt) { - return(list(status = -1, msg = paste("\nThere ", c("is", "are")[1 + as.numeric(nErrCoeff > 1)], " ", + + # get number of equations and verify with data file + # find statements with Y(digit) or Y[digit] + outputLines <- grep("Y\\([[:digit:]]+\\)|Y\\[[[:digit:]]+\\]", blocks$output, ignore.case = T) + if (length(outputLines) == 0) { + return(list(status = -1, msg = "\nYou must have at least one output equation of the form 'Y[1] = ...'\n")) + } + # extract numbers + outputStatements <- sapply(blocks$output[outputLines], function(x) strparse("Y\\([[:digit:]]+\\)|Y\\[[[:digit:]]+\\]", x)) + outputNumbers <- sapply(outputStatements, function(x) strparse("[[:digit:]]", x)) + # get max number + modelnumeqt <- max(as.numeric(outputNumbers)) + if (modelnumeqt != engine$numeqt) { + return(list(status = -1, msg = "\nThe number of output equations in the model file\ndoes not match the maximum value of outeq in your datafile.\n")) + } + + # remove leading ampersands from getfa, getix, gettlag if present + oldContLines <- grep("^\\+", c(blocks$f, blocks$ini, blocks$lag)) + if (length(oldContLines > 0)) { + return(list(status = -1, msg = "\nThe model file format has changed. Please replace '+' with '&' in all continuation lines.\n")) + } + if (length(grep("^&", blocks$f) > 0)) blocks$f <- gsub("^&", "", blocks$f) + if (length(grep("^&", blocks$ini) > 0)) blocks$ini <- gsub("^&", "", blocks$ini) + if (length(grep("^&", blocks$lag) > 0)) blocks$lag <- gsub("^&", "", blocks$lag) + + # variable declarations for fortran and make sure not >maxwidth characters + if (secVarNames[1] != "") { + vardec <- paste("REAL*8 ", paste(blocks$primVar, collapse = ","), ",", paste(secVarNames, collapse = ","), sep = "") + } else { + vardec <- paste("REAL*8 ", paste(blocks$primVar, collapse = ","), sep = "") + } + if (blocks$covar[1] != "") { + vardec <- paste(vardec, ",", paste(blocks$covar, collapse = ","), sep = "") + } + if (nchar(vardec) > maxwidth) { + vardec <- paste(unlist(strsplit(vardec, ",")), collapse = ",\n & ") + } + + # error + blocks$error <- tolower(gsub("[[:space:]]", "", blocks$error)) + # check to make sure coefficient lines are the same number as outputs + nErrCoeff <- length(blocks$error) - 1 + if (nErrCoeff != modelnumeqt) { + return(list(status = -1, msg = paste("\nThere ", c("is", "are")[1 + as.numeric(nErrCoeff > 1)], " ", nErrCoeff, c(" line", " lines")[1 + as.numeric(nErrCoeff > 1)], " of error coefficients in the model file, but ", modelnumeqt, " output ", c("equation", "equations")[1 + as.numeric(modelnumeqt > 1)], @@ -619,7 +620,7 @@ checkSymmetry = TRUE) { fixed <- grep("!", blocks$error[gamlam[1]]) ierr <- unlist(strsplit(blocks$error[gamlam[1]], "=")) ierrtype <- gsub("[[:space:]]", "", tolower(substr(ierr[1], 1, 1))) - + # NPAG error parameters # IERRMOD # 1 SD WITH GAMMA(IEQ) FIXED @@ -632,7 +633,7 @@ checkSymmetry = TRUE) { # 2 IF ONE SET OF ABOVE Cs USED FOR ALL PATIENTS; # 1 IF Cs ALREADY IN PATIENT FILES WILL BE USED; IF A # PATIENT HAS NO C'S, THEN POPULATION C'S WILL BE USED. - + if (engine$alg == "NP") { if (length(fixed) > 0) { # gamma is fixed (error for lambda) @@ -667,7 +668,7 @@ checkSymmetry = TRUE) { asserr <- paste(gsub("!", "", asserr), collapse = "\n") # clean up asserr iass <- paste(iass, collapse = " ") # clean up iass } - + # IT2B error parameters # IERRMOD # 1 IF GAMMA(IEQ) IS TO REMAIN 1.0 THROUGHOUT THE ANALYSIS; @@ -680,7 +681,7 @@ checkSymmetry = TRUE) { # 2 IF ONE SET OF ABOVE Cs USED FOR ALL PATIENTS; # 1 IF Cs ALREADY IN PATIENT FILES WILL BE USED; IF A # PATIENT HAS NO C'S, THEN POPULATION C'S WILL BE USED. - + # IQVAL # 0 IF OUTPUT EQ. HAS ITS Cs ENTERED BY USER (NOT # ESTIMATED BY assbigxx.exe) AND IERRTYPE(IEQ) = 1 @@ -688,7 +689,7 @@ checkSymmetry = TRUE) { # ESTIMATED BY assbigxx.exe) AND IERRTYPE(IEQ) = 0 # 4 IF OUTPUT EQ. HAD ITS Cs ESTIMATED PREVIOUSLY # BY assbigxx.exe. - + if (engine$alg == "IT") { if (ierrtype == "l") { return(list(status = -1, msg = "\nLambda is not currently implemented in IT2B\nPlease correct the error block in your model file.\n")) @@ -730,11 +731,11 @@ checkSymmetry = TRUE) { return(list(status = -1, msg = "Please specify a gamma or lambda error model in your\nmodel file error block.")) } } - - - - - + + + + + # write report if (!quiet) { cat(paste("\nModel solver mode: ", switch(letters[N + 2], @@ -777,27 +778,27 @@ checkSymmetry = TRUE) { cat(paste("\nCovariates used in model file: ", c(paste(blocks$covar, collapse = ", "), "None")[1 + as.numeric(blocks$covar[1] == "")])) cat(paste("\nSecondary Variables: ", paste(secVarNames, collapse = ", "), sep = "")) cat(paste("\nModel conditions: ", c("bioavailability term defined, ", "no bioavailability term defined, ")[1 + as.numeric(blocks$f[1] == "")], - c("initial conditions are not zero, ", "initial conditions are zero, ")[1 + as.numeric(blocks$ini[1] == "")], - c("lag term defined", "no lag term defined")[1 + as.numeric(blocks$lag[1] == "")], - sep = "" - )) - if (engine$alg != "SIM") cat(paste("\nNumber of cycles to run:", engine$cycles)) - cat("\n\n") -} -# end if quiet -if (getPMoptions("backend") == "rust") { - model_file <- "main.rs" -} else { - model_file <- modelFor -} + c("initial conditions are not zero, ", "initial conditions are zero, ")[1 + as.numeric(blocks$ini[1] == "")], + c("lag term defined", "no lag term defined")[1 + as.numeric(blocks$lag[1] == "")], + sep = "" + )) + if (engine$alg != "SIM") cat(paste("\nNumber of cycles to run:", engine$cycles)) + cat("\n\n") + } + # end if quiet + if (getPMoptions("backend") == "rust") { + model_file <- "main.rs" + } else { + model_file <- modelFor + } -ret_list <- list( - status = 1, N = N, ptype = ptype, model = model_file, - ctype = ctype, nvar = nvar, nofix = nofix, nranfix = nranfix, - valfix = valfix, ab = ab.df, indpts = indpts, - asserr = asserr, blocks = blocks -) -return(ret_list) + ret_list <- list( + status = 1, N = N, ptype = ptype, model = model_file, + ctype = ctype, nvar = nvar, nofix = nofix, nranfix = nranfix, + valfix = valfix, ab = ab.df, indpts = indpts, + asserr = asserr, blocks = blocks + ) + return(ret_list) } # end makeModel function @@ -816,7 +817,7 @@ endNicely <- function(message, model = -99, data = -99) { "it2b*.*", "itas*.*", "it_prep*", "it_run*", "itlog.txt", "ITcontrol", "itscript*", "instr.inx", "assdriv.f", "err_prep*", "err_run*", "ERRcontrol", "errscript*", "errlog.txt" )) - + if (length(cleanUp) > 0) file.remove(cleanUp) stop(message, call. = F) } @@ -836,8 +837,8 @@ var.wt <- function(x, w, na.rm = FALSE) { # weighted t test weighted.t.test <- function(x, w, mu, conf.level = 0.95, alternative = "two.sided", na.rm = TRUE) { if (!missing(conf.level) & - (length(conf.level) != 1 || !is.finite(conf.level) || - conf.level < 0 || conf.level > 1)) { + (length(conf.level) != 1 || !is.finite(conf.level) || + conf.level < 0 || conf.level > 1)) { stop("'conf.level' must be a single number between 0 and 1") } # see if x came from PM_op object @@ -846,29 +847,29 @@ weighted.t.test <- function(x, w, mu, conf.level = 0.95, alternative = "two.side x <- x$d mu <- 0 } - + if (na.rm) { w <- w[i <- !is.na(x)] x <- x[i] } - + # to achieve consistent behavior in loops, return NA-structure in case of complete missings if (sum(is.na(x)) == length(x)) { return(list(estimate = NA, se = NA, conf.int = NA, statistic = NA, df = NA, p.value = NA)) } - + # if only one value is present: this is the best estimate, no significance test provided if (sum(!is.na(x)) == 1) { warning("Warning weighted.t.test: only one value provided; this value is returned without test of significance!", call. = FALSE) return(list(estimate = x[which(!is.na(x))], se = NA, conf.int = NA, statistic = NA, df = NA, p.value = NA)) } - + x.w <- weighted.mean(x, w, na.rm = na.rm) var.w <- var.wt(x, w, na.rm = na.rm) df <- length(x) - 1 t.value <- sqrt(length(x)) * ((x.w - mu) / sqrt(var.w)) se <- sqrt(var.w) / sqrt(length(x)) - + if (alternative == "less") { pval <- pt(t.value, df) cint <- c(-Inf, x.w + se * qt(conf.level, df)) @@ -880,7 +881,7 @@ weighted.t.test <- function(x, w, mu, conf.level = 0.95, alternative = "two.side alpha <- 1 - conf.level cint <- x.w + se * qt(1 - alpha / 2, df) * c(-1, 1) } - + names(t.value) <- "t" return(list(estimate = x.w, se = se, conf.int = cint, statistic = t.value, df = df, p.value = pval)) } @@ -907,13 +908,13 @@ FileExists <- function(filename) { while (!file.exists(filename)) { # oops, filename doesn't exist cat(paste0(filename, " does not exist in ", getwd(), ".\n")) filename <- tryCatch(readline("Enter another filename or 'ESC' to quit: \n"), - interrupt = function(e) { - stop("No filename. Function aborted.\n", call. = F) - } - ) + interrupt = function(e) { + stop("No filename. Function aborted.\n", call. = F) + } + ) + } } -} -return(filename) + return(filename) } @@ -921,11 +922,11 @@ return(filename) getOS <- function() { OS <- switch(Sys.info()[1], - Darwin = 1, - Windows = 2, - Linux = 3 -) -return(OS) + Darwin = 1, + Windows = 2, + Linux = 3 + ) + return(OS) } # This might be a solution: https://community.rstudio.com/t/how-to-get-rstudio-ide-to-use-the-correct-terminal-path-in-mac-os-x/131528/3 @@ -997,7 +998,7 @@ getCov <- function(mdata) { covstart <- NA covend <- NA } - + return(list(ncov = ncov, covnames = covnames, covstart = covstart, covend = covend)) } @@ -1046,11 +1047,11 @@ checkRequiredPackages <- function(pkg, repos = "CRAN", quietly = TRUE) { } # nope, still didn't install } } - + msg <- pkg %>% - map_chr(managePkgs) %>% - keep(~ . != "ok") - + map_chr(managePkgs) %>% + keep(~ . != "ok") + if (length(msg) > 0) { if (!quietly) { cat( @@ -1106,8 +1107,8 @@ obsStatus <- function(data) { # import recycled text into documentation template <- function(name) { insert <- readLines(paste0("man-roxygen/", name, ".R")) %>% - stringr::str_replace("#' ", "") %>% - stringr::str_replace("
", " \n") + stringr::str_replace("#' ", "") %>% + stringr::str_replace("
", " \n") insert <- c(insert, " \n") insert <- paste(insert, collapse = " ") return(insert) @@ -1120,122 +1121,124 @@ template <- function(name) { # modified from Hmisc functions -wtd.table <- function(x, weights = NULL, - type = c("list", "table"), - normwt = TRUE, - na.rm = TRUE) { - type <- match.arg(type) - if (!length(weights)) { - weights <- rep(1, length(x)) - } - isdate <- lubridate::is.Date(x) - ax <- attributes(x) - ax$names <- NULL - if (is.character(x)) { - x <- as.factor(x) - } - lev <- levels(x) - x <- unclass(x) - if (na.rm) { - s <- !is.na(x + weights) - x <- x[s, drop = FALSE] - weights <- weights[s] - } - n <- length(x) - if (normwt) { - weights <- weights * length(x) / sum(weights) - } - i <- order(x) - x <- x[i] - weights <- weights[i] - if (anyDuplicated(x)) { - weights <- tapply(weights, x, sum) - if (length(lev)) { - levused <- lev[sort(unique(x))] - if ((length(weights) > length(levused)) && any(is.na(weights))) { - weights <- weights[!is.na(weights)] - } - if (length(weights) != length(levused)) { - stop("program logic error") - } - names(weights) <- levused +wtd.table <- function( + x, weights = NULL, + type = c("list", "table"), + normwt = TRUE, + na.rm = TRUE) { + type <- match.arg(type) + if (!length(weights)) { + weights <- rep(1, length(x)) + } + isdate <- lubridate::is.Date(x) + ax <- attributes(x) + ax$names <- NULL + if (is.character(x)) { + x <- as.factor(x) + } + lev <- levels(x) + x <- unclass(x) + if (na.rm) { + s <- !is.na(x + weights) + x <- x[s, drop = FALSE] + weights <- weights[s] + } + n <- length(x) + if (normwt) { + weights <- weights * length(x) / sum(weights) + } + i <- order(x) + x <- x[i] + weights <- weights[i] + if (anyDuplicated(x)) { + weights <- tapply(weights, x, sum) + if (length(lev)) { + levused <- lev[sort(unique(x))] + if ((length(weights) > length(levused)) && any(is.na(weights))) { + weights <- weights[!is.na(weights)] } - if (!length(names(weights))) { + if (length(weights) != length(levused)) { stop("program logic error") } - if (type == "table") { - return(weights) - } - x <- all_is_numeric(names(weights), "vector") - if (isdate) { - attributes(x) <- c(attributes(x), ax) - } - names(weights) <- NULL - return(list(x = x, sum.of.weights = weights)) + names(weights) <- levused + } + if (!length(names(weights))) { + stop("program logic error") } - xx <- x + if (type == "table") { + return(weights) + } + x <- all_is_numeric(names(weights), "vector") if (isdate) { - attributes(xx) <- c(attributes(xx), ax) + attributes(x) <- c(attributes(x), ax) } - if (type == "list") { - list(x = if (length(lev)) lev[x] else xx, sum.of.weights = weights) + names(weights) <- NULL + return(list(x = x, sum.of.weights = weights)) + } + xx <- x + if (isdate) { + attributes(xx) <- c(attributes(xx), ax) + } + if (type == "list") { + list(x = if (length(lev)) lev[x] else xx, sum.of.weights = weights) + } else { + names(weights) <- if (length(lev)) { + lev[x] } else { - names(weights) <- if (length(lev)) { - lev[x] - } else { - xx - } - weights + xx } + weights } - - wtd.mean <- function(x, weights = NULL, normwt = "ignored", na.rm = TRUE) { - if (!length(weights)) { - return(mean(x, na.rm = na.rm)) - } - if (na.rm) { - s <- !is.na(x + weights) - x <- x[s] - weights <- weights[s] - } - sum(weights * x) / sum(weights) - } - - - wtd.quantile <- function(x, weights = NULL, probs = c(0, 0.25, 0.5, 0.75, 1), - normwt = TRUE, - na.rm = TRUE) { - if (!length(weights)) { - return(quantile(x, probs = probs, na.rm = na.rm)) - } - - if (any(probs < 0 | probs > 1)) { - cli::cli_abort("Probabilities must be between 0 and 1 inclusive") - } - nams <- paste(format(round(probs * 100, if (length(probs) > +} + +wtd.mean <- function(x, weights = NULL, normwt = "ignored", na.rm = TRUE) { + if (!length(weights)) { + return(mean(x, na.rm = na.rm)) + } + if (na.rm) { + s <- !is.na(x + weights) + x <- x[s] + weights <- weights[s] + } + sum(weights * x) / sum(weights) +} + + +wtd.quantile <- function( + x, weights = NULL, probs = c(0, 0.25, 0.5, 0.75, 1), + normwt = TRUE, + na.rm = TRUE) { + if (!length(weights)) { + return(quantile(x, probs = probs, na.rm = na.rm)) + } + + if (any(probs < 0 | probs > 1)) { + cli::cli_abort("Probabilities must be between 0 and 1 inclusive") + } + nams <- paste(format(round(probs * 100, if (length(probs) > 1) { - 2 - log10(diff(range(probs))) - } else { - 2 - })), "%", sep = "") - i <- is.na(weights) | weights == 0 - if (any(i)) { - x <- x[!i] - weights <- weights[!i] - } - - w <- wtd.table(x, weights, - na.rm = na.rm, normwt = normwt, - type = "list" - ) - x <- w$x - wts <- w$sum.of.weights - n <- sum(wts) - order <- 1 + (n - 1) * probs - low <- pmax(floor(order), 1) - high <- pmin(low + 1, n) - order <- order %% 1 - allq <- approx(cumsum(wts), x, + 2 - log10(diff(range(probs))) + } else { + 2 + })), "%", sep = "") + i <- is.na(weights) | weights == 0 + if (any(i)) { + x <- x[!i] + weights <- weights[!i] + } + + w <- wtd.table(x, weights, + na.rm = na.rm, normwt = normwt, + type = "list" + ) + x <- w$x + wts <- w$sum.of.weights + n <- sum(wts) + order <- 1 + (n - 1) * probs + low <- pmax(floor(order), 1) + high <- pmin(low + 1, n) + order <- order %% 1 + allq <- approx(cumsum(wts), x, xout = c(low, high), method = "constant", f = 1, rule = 2 )$y @@ -1245,311 +1248,316 @@ wtd.table <- function(x, weights = NULL, return(quantiles) } -wtd.var <- function(x, weights = NULL, - normwt = TRUE, - na.rm = TRUE, - method = c("unbiased", "ML")) { - if (any(weights == 1)) { - return(0) - } - - method <- match.arg(method) - if (!length(weights)) { - if (na.rm) { - x <- x[!is.na(x)] - } - return(var(x)) - } +wtd.var <- function( + x, weights = NULL, + normwt = TRUE, + na.rm = TRUE, + method = c("unbiased", "ML")) { + if (any(weights == 1)) { + return(0) + } + + method <- match.arg(method) + if (!length(weights)) { if (na.rm) { - s <- !is.na(x + weights) - x <- x[s] - weights <- weights[s] - } - if (normwt) { - weights <- weights * length(x) / sum(weights) - } - if (normwt || method == "ML") { - return(as.numeric(stats::cov.wt(cbind(x), weights, method = method)$cov)) + x <- x[!is.na(x)] } - sw <- sum(weights) - if (sw <= 1) { - cli::cli_warn("only one effective observation; variance estimate undefined") - } - xbar <- sum(weights * x) / sw - sum(weights * ((x - xbar)^2)) / (sw - 1) - } - - - - # Check if all values numeric --------------------------------------------- - - #' @title Check if all values are numeric - #' @description - #' `r lifecycle::badge("stable")` - #' Checks if all values in a vector are numeric. - #' @details - #' The function checks if all values in a vector are numeric. - #' It can be used to check if a vector contains only numeric values. - #' It can also be used to check if a vector contains any non-numeric values. - #' @param x A vector to check. - #' @param what A character string indicating what to return. - #' Can be "test", "vector", or "nonnum". - #' The default is "test". - #' @param extras A character vector of extra values to exclude from the check. - #' The default is c(".", "NA"). - #' @return A logical value indicating if all values are numeric. - #' If `what` is "vector", a numeric vector is returned. - #' If `what` is "nonnum", a character vector of non-numeric values is returned. - #' If `what` is "test", a logical value is returned. - #' @export - #' @examples - #' \dontrun{ - #' all_is_numeric(c("1", "2", "3")) - #' all_is_numeric(c("1", "2", "a")) - #' all_is_numeric(c("1", "2", "3"), what = "vector") - #' all_is_numeric(c("1", "2", "a"), what = "nonnum") - #' } - all_is_numeric <- function(x, what = c("test", "vector", "nonnum"), extras = c( - ".", - "NA" - )) { - what <- match.arg(what) - x <- sub("[[:space:]]+$", "", x) - x <- sub("^[[:space:]]+", "", x) - xs <- x[!x %in% c("", extras)] - if (!length(xs) || all(is.na(x))) { - return(switch(what, - test = FALSE, - vector = x, - nonnum = x[0] - )) - } - isnon <- suppressWarnings(!is.na(xs) & is.na(as.numeric(xs))) - isnum <- !any(isnon) - switch(what, - test = isnum, - vector = if (isnum) suppressWarnings(as.numeric(x)) else x, - nonnum = xs[isnon] - ) + return(var(x)) } - - - - - # Save Flextable --------------------------------------------------------- - - #' @title Save a flextable object to a file - #' @description - #' `r lifecycle::badge("stable")` - #' Saves flextable objects to a file based on the `file` attribute - #' in the object, set when the flextable generator function is called. - #' Allowable file types are 'docx', 'pptx', 'html', 'png', and 'svg'. - #' @param x A [flextable::flextable] object. - #' @return A message indicating the file was saved. - #' @export - #' @keywords internal - - save_flextable <- function(x) { - # check if x is a flextable - if (!inherits(x, "flextable")) { - cli::cli_abort("{.arg x} must be a flextable object.") - } - - file <- attr(x, "file") - if (!is.null(file)) { - # get file extension - ext <- stringr::str_match(file, "\\.(.*)$")[2] - - # save flextable based on file extension - if (ext %in% c("docx", "doc")) { - flextable::save_as_docx(x, path = file) - } else if (ext %in% c("pptx", "ppt")) { - flextable::save_as_pptx(x, path = file) - } else if (ext %in% c("html", "htm")) { - flextable::save_as_html(x, path = file) - } else if (ext %in% c("png", "svg")) { - flextable::save_as_image(x, path = file) - } else { - cli::cli_abort("File type not recognized. Choose from 'docx', 'pptx', 'html', 'png', or 'svg'.") - } - - cli::cli_inform(paste("The file", file, "was saved to", getwd(), ".")) + if (na.rm) { + s <- !is.na(x + weights) + x <- x[s] + weights <- weights[s] + } + if (normwt) { + weights <- weights * length(x) / sum(weights) + } + if (normwt || method == "ML") { + return(as.numeric(stats::cov.wt(cbind(x), weights, method = method)$cov)) + } + sw <- sum(weights) + if (sw <= 1) { + cli::cli_warn("only one effective observation; variance estimate undefined") + } + xbar <- sum(weights * x) / sw + sum(weights * ((x - xbar)^2)) / (sw - 1) +} + + + +# Check if all values numeric --------------------------------------------- + +#' @title Check if all values are numeric +#' @description +#' `r lifecycle::badge("stable")` +#' Checks if all values in a vector are numeric. +#' @details +#' The function checks if all values in a vector are numeric. +#' It can be used to check if a vector contains only numeric values. +#' It can also be used to check if a vector contains any non-numeric values. +#' @param x A vector to check. +#' @param what A character string indicating what to return. +#' Can be "test", "vector", or "nonnum". +#' The default is "test". +#' @param extras A character vector of extra values to exclude from the check. +#' The default is c(".", "NA"). +#' @return A logical value indicating if all values are numeric. +#' If `what` is "vector", a numeric vector is returned. +#' If `what` is "nonnum", a character vector of non-numeric values is returned. +#' If `what` is "test", a logical value is returned. +#' @export +#' @examples +#' \dontrun{ +#' all_is_numeric(c("1", "2", "3")) +#' all_is_numeric(c("1", "2", "a")) +#' all_is_numeric(c("1", "2", "3"), what = "vector") +#' all_is_numeric(c("1", "2", "a"), what = "nonnum") +#' } +all_is_numeric <- function(x, what = c("test", "vector", "nonnum"), extras = c( + ".", + "NA" + )) { + what <- match.arg(what) + x <- sub("[[:space:]]+$", "", x) + x <- sub("^[[:space:]]+", "", x) + xs <- x[!x %in% c("", extras)] + if (!length(xs) || all(is.na(x))) { + return(switch(what, + test = FALSE, + vector = x, + nonnum = x[0] + )) + } + isnon <- suppressWarnings(!is.na(xs) & is.na(as.numeric(xs))) + isnum <- !any(isnon) + switch(what, + test = isnum, + vector = if (isnum) suppressWarnings(as.numeric(x)) else x, + nonnum = xs[isnon] + ) +} + + + + +# Save Flextable --------------------------------------------------------- + +#' @title Save a flextable object to a file +#' @description +#' `r lifecycle::badge("stable")` +#' Saves flextable objects to a file based on the `file` attribute +#' in the object, set when the flextable generator function is called. +#' Allowable file types are 'docx', 'pptx', 'html', 'png', and 'svg'. +#' @param x A [flextable::flextable] object. +#' @return A message indicating the file was saved. +#' @export +#' @keywords internal + +save_flextable <- function(x) { + # check if x is a flextable + if (!inherits(x, "flextable")) { + cli::cli_abort("{.arg x} must be a flextable object.") + } + + file <- attr(x, "file") + if (!is.null(file)) { + # get file extension + ext <- stringr::str_match(file, "\\.(.*)$")[2] + + # save flextable based on file extension + if (ext %in% c("docx", "doc")) { + flextable::save_as_docx(x, path = file) + } else if (ext %in% c("pptx", "ppt")) { + flextable::save_as_pptx(x, path = file) + } else if (ext %in% c("html", "htm")) { + flextable::save_as_html(x, path = file) + } else if (ext %in% c("png", "svg")) { + flextable::save_as_image(x, path = file) + } else { + cli::cli_abort("File type not recognized. Choose from 'docx', 'pptx', 'html', 'png', or 'svg'.") } - - return(invisible(x)) - } - - - - # Ask with warning -------------------------------------------------------- - - #' @title Ask with warning - #' @description Get user input in warning situation - #' @details Combines the [cli::cli_text] function with [readline]. - #' @param text The warning text. - #' @param prompt The prompt preceding user input. Default is ">>". - #' @param ... Additional parameters which could be passed to [cli::cli_text]. - #' @return The value of the user response - #' @export - #' @keywords internal - #' - cli_ask <- function(text, prompt = ">> ", ...) { - cli::cli_text(text, ...) - ans <- readline(prompt = prompt) - return(ans) - } - - - # Function to Character --------------------------------------------------- - - #' @title Convert a function to a character string - #' @keywords internal - func_to_char <- function(fun){ - deparse(fun, width.cutoff = 500L) %>% + + cli::cli_inform(paste("The file", file, "was saved to", getwd(), ".")) + } + + return(invisible(x)) +} + + + +# Ask with warning -------------------------------------------------------- + +#' @title Ask with warning +#' @description Get user input in warning situation +#' @details Combines the [cli::cli_text] function with [readline]. +#' @param text The warning text. +#' @param prompt The prompt preceding user input. Default is ">>". +#' @param ... Additional parameters which could be passed to [cli::cli_text]. +#' @return The value of the user response +#' @export +#' @keywords internal +#' +cli_ask <- function(text, prompt = ">> ", ...) { + cli::cli_text(text, ...) + ans <- readline(prompt = prompt) + return(ans) +} + + +# Function to Character --------------------------------------------------- + +#' @title Convert a function to a character string +#' @keywords internal +func_to_char <- function(fun) { + deparse(fun, width.cutoff = 500L) %>% stringr::str_trim("left") %>% purrr::discard(\(x) stringr::str_detect(x, "function|\\{|\\}")) - } - - - # Round to x digits --------------------------------------------------- - - #' @title Round to x digits - #' @description - #' `r lifecycle::badge("stable")` - #' Rounds a numeric value to a specified number of digits for display in flextables and plots. - #' @details Uses [base::format] and [base::round] to round a numeric value to a specified number of digits. - #' @param x A numeric value to be rounded. - #' @param digits The number of digits to round to. Default is set using [setPMoptions]. - #' @return A character string representing the rounded value with the specified number of digits. - #' @export - #' @keywords internal - - round2 <- function(x, digits = getPMoptions("digits")) { - if (is.null(digits) || !is.numeric(digits)) digits <- 2 - format(round(x, digits), nsmall = digits) - } - - - # Print data frame in CLI format ------------------------------------------ - #' @title Print data frame in CLI format - #' @description - #' `r lifecycle::badge("stable")` - #' Prints a data frame in a format suitable for the command line interface (CLI). - #' @details - #' Uses [dplyr::mutate] to convert all columns to character, rounds numeric values using [round2], - #' and formats the output using [knitr::kable] for a simple table format. - #' The function replaces spaces with non-breaking spaces for better alignment in the CLI. - #' @param df A data frame to be printed. - #' @return A formatted text output of the data frame. - #' @export - #' @keywords internal - cli_df <- function(df) { - - highlight <- attr(df, "highlight") # get columns to highlight minimums from attributes - - # Convert all columns to character for uniform formatting - df_chr <- df %>% mutate(across(where(is.double), ~round2(.x))) %>% - mutate(across(everything(), ~as.character(.x, stringsAsFactors = FALSE))) - - - if (highlight){ # highlight minimums in requested columns - # first replace minima with special formatting - # mins <- df %>% summarize(across(c(-run, -nvar, -converged, -pval, -best), ~round2(min(.x, na.rm = TRUE)))) # get minima for each column - mins <- df %>% summarize(across(c(-run, -nvar, -converged, -pval, -best), ~ which(.x == min(.x, na.rm = TRUE)))) %>% unlist() # get minima for each column - - best <- df %>% summarize(across(best, ~ which(.x == max(.x, na.rm = TRUE)))) %>% unlist() # get best for best column - - # create table to get the spacing - df_tab <- knitr::kable(df_chr, format = "simple") - - # rebuild the data frame - df2 <- map_vec(df_tab, \(x) str_split(x, "(?<=\\s)(?=\\S)")) - df2 <- as.data.frame(do.call(rbind, df2)) - - # replace minima with highlighted versions - # first 2 rows are headers and spacers, so need to add 2 to the mins row index - for (p in 1:length(mins)){ - df2[mins[p]+2, p+3] <- stringr::str_replace_all(df2[mins[p]+2, p+3], "(\\d+(?:\\.\\d+)?)(\\s+)", "{.red \\1}\\2") - } +} + - # for(p in 1:length(mins)){ - # df2[, p+3] <- stringr::str_replace_all(df2[, p+3], as.character(mins[p]), paste0("{.strong ", as.character(mins[p]), "}")) - # } - # df2$V18 <- stringr::str_replace(df2$V18, as.character(best), paste0("{.red ", as.character(best), "}")) - df2$V17[best+2] <- stringr::str_replace(df2$V17[best+2], "(\\d+(?:\\.\\d+)?)(\\s+)", "{.red \\1}\\2") - - # print header - header <- df2[1,] %>% stringr::str_replace_all(" ", "\u00A0" ) %>% paste(collapse = "") - cli::cli_text("{.strong {header}}") - cli::cli_div(theme = list(span.red = list(color = "red", "font-weight" = "bold"))) - - # replace ≥2 spaces with non-breaking spaces - for (i in 2:nrow(df2)) { - # m <- gregexpr("\\s{2,}", df_tab[i], perl = TRUE) - # regmatches(df_tab[i], m) <- lapply(regmatches(df_tab[i], m), function(ss) { - # vapply(ss, function(one) { - # paste0(rep("\u00A0", nchar(one)), collapse = "") - # }, character(1)) - # }) - # print each row - cli::cli_text(paste(df2[i,], collapse = "") %>% stringr::str_replace_all(" ", "\u00A0" ) %>% stringr::str_replace_all("strong\u00A0+", "strong ") %>% stringr::str_replace_all("red\u00A0+", "red ")) - } - cli::cli_end() - } else { # no highlighting - - # create table - df_tab <- knitr::kable(df_chr, format = "simple") - - # print header - header <- df_tab[1] %>% stringr::str_replace_all(" ", "\u00A0" ) - cli::cli_text("{.strong {header}}") +# Round to x digits --------------------------------------------------- +#' @title Round to x digits +#' @description +#' `r lifecycle::badge("stable")` +#' Rounds a numeric value to a specified number of digits for display in flextables and plots. +#' @details Uses [base::format] and [base::round] to round a numeric value to a specified number of digits. +#' @param x A numeric value to be rounded. +#' @param digits The number of digits to round to. Default is set using [setPMoptions]. +#' @return A character string representing the rounded value with the specified number of digits. +#' @export +#' @keywords internal + +round2 <- function(x, digits = getPMoptions("digits")) { + if (is.null(digits) || !is.numeric(digits)) digits <- 2 + format(round(x, digits), nsmall = digits) +} + + +# Print data frame in CLI format ------------------------------------------ +#' @title Print data frame in CLI format +#' @description +#' `r lifecycle::badge("stable")` +#' Prints a data frame in a format suitable for the command line interface (CLI). +#' @details +#' Uses [dplyr::mutate] to convert all columns to character, rounds numeric values using [round2], +#' and formats the output using [knitr::kable] for a simple table format. +#' The function replaces spaces with non-breaking spaces for better alignment in the CLI. +#' @param df A data frame to be printed. +#' @return A formatted text output of the data frame. +#' @export +#' @keywords internal +cli_df <- function(df) { + highlight <- attr(df, "highlight") # get columns to highlight minimums from attributes + + # Convert all columns to character for uniform formatting + df_chr <- df %>% + mutate(across(where(is.double), ~ round2(.x))) %>% + mutate(across(everything(), ~ as.character(.x, stringsAsFactors = FALSE))) + + + if (highlight) { # highlight minimums in requested columns + # first replace minima with special formatting + # mins <- df %>% summarize(across(c(-run, -nvar, -converged, -pval, -best), ~round2(min(.x, na.rm = TRUE)))) # get minima for each column + mins <- df %>% + summarize(across(c(-run, -nvar, -converged, -pval, -best), ~ which(.x == min(.x, na.rm = TRUE)))) %>% + unlist() # get minima for each column + + best <- df %>% + summarize(across(best, ~ which(.x == max(.x, na.rm = TRUE)))) %>% + unlist() # get best for best column + + # create table to get the spacing + df_tab <- knitr::kable(df_chr, format = "simple") + + # rebuild the data frame + df2 <- map_vec(df_tab, \(x) str_split(x, "(?<=\\s)(?=\\S)")) + df2 <- as.data.frame(do.call(rbind, df2)) + + # replace minima with highlighted versions + # first 2 rows are headers and spacers, so need to add 2 to the mins row index + for (p in 1:length(mins)) { + df2[mins[p] + 2, p + 3] <- stringr::str_replace_all(df2[mins[p] + 2, p + 3], "(\\d+(?:\\.\\d+)?)(\\s+)", "{.red \\1}\\2") + } + + # for(p in 1:length(mins)){ + # df2[, p+3] <- stringr::str_replace_all(df2[, p+3], as.character(mins[p]), paste0("{.strong ", as.character(mins[p]), "}")) + # } + # df2$V18 <- stringr::str_replace(df2$V18, as.character(best), paste0("{.red ", as.character(best), "}")) + df2$V17[best + 2] <- stringr::str_replace(df2$V17[best + 2], "(\\d+(?:\\.\\d+)?)(\\s+)", "{.red \\1}\\2") + + # print header + header <- df2[1, ] %>% + stringr::str_replace_all(" ", "\u00A0") %>% + paste(collapse = "") + cli::cli_text("{.strong {header}}") + cli::cli_div(theme = list(span.red = list(color = "red", "font-weight" = "bold"))) + + # replace ≥2 spaces with non-breaking spaces + for (i in 2:nrow(df2)) { + # m <- gregexpr("\\s{2,}", df_tab[i], perl = TRUE) + # regmatches(df_tab[i], m) <- lapply(regmatches(df_tab[i], m), function(ss) { + # vapply(ss, function(one) { + # paste0(rep("\u00A0", nchar(one)), collapse = "") + # }, character(1)) + # }) # print each row - for (i in 2:length(df_tab)) { - cli::cli_text(df_tab[i] %>% stringr::str_replace_all(" ", "\u00A0" )) - } + cli::cli_text(paste(df2[i, ], collapse = "") %>% stringr::str_replace_all(" ", "\u00A0") %>% stringr::str_replace_all("strong\u00A0+", "strong ") %>% stringr::str_replace_all("red\u00A0+", "red ")) } - - } - - #' @title Convert correlation matrix to covariance matrix - #' @description - #' `r lifecycle::badge("stable")` - #' Converts a correlation matrix to a covariance matrix using standard deviations. - #' @details - #' Uses matrix multiplication to convert a correlation matrix to a covariance matrix. - #' @param cor A correlation matrix. - #' @param sd A vector of standard deviations corresponding to the variables in the correlation matrix. - #' @return A covariance matrix. - #' @export - #' @author Michael Neely - #' - cor2cov <- function(cor, sd){ - cov_matrix <- diag(sd) %*% cor %*% diag(sd) - return(cov_matrix) - } - - - #' @title Check if a matrix is positive definite - #' @description - #' `r lifecycle::badge("stable")` - #' Checks if a matrix is positive definite and attempts to fix it if necessary. - #' @param mat A covariance matrix to check. - #' @return A positive definite covariance matrix, 1 if aborting, or -1 if unable to fix - #' @export - #' @author Michael Neely - #' @keywords internal - pos_def <- function(mat, id, source){ - # check to make sure mat (within 15 sig digits, which is in file) is pos-def and fix if necessary - posdef <- rlang::try_fetch(eigen(signif(mat, 15)), + cli::cli_end() + } else { # no highlighting + + # create table + df_tab <- knitr::kable(df_chr, format = "simple") + + # print header + header <- df_tab[1] %>% stringr::str_replace_all(" ", "\u00A0") + cli::cli_text("{.strong {header}}") + + # print each row + for (i in 2:length(df_tab)) { + cli::cli_text(df_tab[i] %>% stringr::str_replace_all(" ", "\u00A0")) + } + } +} + +#' @title Convert correlation matrix to covariance matrix +#' @description +#' `r lifecycle::badge("stable")` +#' Converts a correlation matrix to a covariance matrix using standard deviations. +#' @details +#' Uses matrix multiplication to convert a correlation matrix to a covariance matrix. +#' @param cor A correlation matrix. +#' @param sd A vector of standard deviations corresponding to the variables in the correlation matrix. +#' @return A covariance matrix. +#' @export +#' @author Michael Neely +#' +cor2cov <- function(cor, sd) { + cov_matrix <- diag(sd) %*% cor %*% diag(sd) + return(cov_matrix) +} + + +#' @title Check if a matrix is positive definite +#' @description +#' `r lifecycle::badge("stable")` +#' Checks if a matrix is positive definite and attempts to fix it if necessary. +#' @param mat A covariance matrix to check. +#' @return A positive definite covariance matrix, 1 if aborting, or -1 if unable to fix +#' @export +#' @author Michael Neely +#' @keywords internal +pos_def <- function(mat, id, source) { + # check to make sure mat (within 15 sig digits, which is in file) is pos-def and fix if necessary + posdef <- rlang::try_fetch(eigen(signif(mat, 15)), error = function(e) { return(list(values = 0)) } ) ans <- NULL if (any(posdef$values < 0)) { - - mat_names <- dimnames(mat)[[1]] #store for later + mat_names <- dimnames(mat)[[1]] # store for later if (is.null(ans)) { cli::cli_alert_warning("Warning: your covariance matrix is not positive definite. This is typically due to small population size.\nChoose one of the following:\n1) end simulation\n2) fix covariances\n3) set covariances to 0\n ") ans <- readline("\n") @@ -1561,24 +1569,22 @@ wtd.var <- function(x, weights = NULL, if (ans == 2) { # eigen decomposition to fix the matrix for (j in 1:10) { # try up to 10 times - eps <- 1e-8 # threshold for small eigenvalues + eps <- 1e-8 # threshold for small eigenvalues eig <- eigen(mat) - eig$values[eig$values < eps] <- eps # threshold small eigenvalues + eig$values[eig$values < eps] <- eps # threshold small eigenvalues mat <- eig$vectors %*% diag(eig$values) %*% t(eig$vectors) - - + + posdef <- eigen(signif(mat, 15)) - + if (all(posdef$values >= 0)) { # success, break out of loop break } - if(j == 10) browser() + if (j == 10) browser() } posdef <- eigen(signif(mat, 15)) # last check if (any(posdef$values < 0)) { - return(-1) - } mat <- data.frame(mat) names(mat) <- mat_names @@ -1592,7 +1598,7 @@ wtd.var <- function(x, weights = NULL, mat <- mat2 } } - + return(mat) } @@ -1600,7 +1606,7 @@ wtd.var <- function(x, weights = NULL, #' @title Modify a list with another list, allowing NULL values #' @description #' `r lifecycle::badge("stable")` -#' Version of [utils::modifyList()] that works with lists which have unnamed elements. +#' Version of [utils::modifyList()] that works with lists which have unnamed elements. #' @param x A list to be modified. #' @param val A list of values to modify `x`. #' @param keep.null A logical value indicating whether to keep NULL values in `val`. @@ -1608,34 +1614,36 @@ wtd.var <- function(x, weights = NULL, #' @return A modified list, as in [utils::modifyList()]. #' @export #' @keywords internal -modifyList2 <- function (x, val, keep.null = FALSE) -{ +modifyList2 <- function(x, val, keep.null = FALSE) { stopifnot(is.list(x), is.list(val)) names(x) <- tolower(names(x)) names(val) <- tolower(names(val)) xnames <- names(x) vnames <- names(val) # handle unnamed lists - if(is.null(xnames)) xnames <- 1:length(x) - if(is.null(vnames)) vnames <- 1:length(val) + if (is.null(xnames)) xnames <- 1:length(x) + if (is.null(vnames)) vnames <- 1:length(val) # handle unnamed elements xnames <- ifelse(xnames == "", as.character(seq_along(xnames)), xnames) vnames <- ifelse(vnames == "", as.character(seq_along(vnames)), vnames) - + vnames <- vnames[nzchar(vnames)] if (keep.null) { for (v in vnames) { - x[v] <- if (v %in% xnames && is.list(x[[v]]) && is.list(val[[v]])) - list(modifyList(x[[v]], val[[v]], keep.null = keep.null)) - else val[v] + x[v] <- if (v %in% xnames && is.list(x[[v]]) && is.list(val[[v]])) { + list(modifyList(x[[v]], val[[v]], keep.null = keep.null)) + } else { + val[v] + } } - } - else { + } else { for (v in vnames) { - x[[v]] <- if (v %in% xnames && is.list(x[[v]]) && - is.list(val[[v]])) - modifyList2(x[[v]], val[[v]], keep.null = keep.null) - else val[[v]] + x[[v]] <- if (v %in% xnames && is.list(x[[v]]) && + is.list(val[[v]])) { + modifyList2(x[[v]], val[[v]], keep.null = keep.null) + } else { + val[[v]] + } } } x @@ -1649,6 +1657,6 @@ modifyList2 <- function (x, val, keep.null = FALSE) #' @return NULL #' @export #' @keywords internal -clear_build <- function(){ +clear_build <- function() { fs::dir_delete(system.file("template", package = "Pmetrics")) } diff --git a/R/mod_lib.R b/R/mod_lib.R index f625aaed6..d36bca6b0 100755 --- a/R/mod_lib.R +++ b/R/mod_lib.R @@ -21,8 +21,9 @@ alt_mod_lib_names <- function(){ # returns model from detected template, 0 if none, and -1 if more than one get_found_model <- function(fun){ eqns <- as.list(body(fun)[-1]) - found_pri <- map(eqns, \(x) deparse(x) %in% mod_lib_names()) %>% unlist() - found_alt <- map(eqns, \(x) deparse(x) %in% alt_mod_lib_names()$alt) %>% unlist() + expr_to_char <- \(x) paste(deparse(x, width.cutoff = 500L), collapse = "\n") + found_pri <- map_lgl(eqns, \(x) expr_to_char(x) %in% mod_lib_names()) + found_alt <- map_lgl(eqns, \(x) expr_to_char(x) %in% alt_mod_lib_names()$alt) all_found <- sum(found_pri, found_alt) if(all_found > 1){ @@ -38,10 +39,10 @@ get_found_model <- function(fun){ } if (any(found_pri)) { # found a primary model name - found_model_name <- eqns[[which(found_pri)]] %>% deparse() + found_model_name <- expr_to_char(eqns[[which(found_pri)]]) } else { # found an alternative model name found_model_name <- alt_mod_lib_names() %>% - filter(alt == eqns[[which(found_alt)]] %>% deparse()) %>% + filter(alt == expr_to_char(eqns[[which(found_alt)]])) %>% pull(primary) } diff --git a/R/model_transpiler.R b/R/model_transpiler.R index c4f78d941..e3aa80e14 100755 --- a/R/model_transpiler.R +++ b/R/model_transpiler.R @@ -224,7 +224,7 @@ expr_to_rust <- function(expr, params = NULL, covs = NULL, tem <- found$name eqns <- as.list(body(fun)[-1]) if (length(eqns) > 0) { - eqns_char <- map_chr(eqns, deparse) + eqns_char <- map_chr(eqns, \(x) paste(deparse(x, width.cutoff = 500L), collapse = "\n")) # check for ODE, which should not be present if (any(stringr::str_detect(eqns_char, regex("dx\\[\\d+\\]", ignore_case = FALSE)))) { cli::cli_abort(c( diff --git a/tests/testthat/test-Examples.R b/tests/testthat/test-Examples.R index e34c4916c..b055e0705 100755 --- a/tests/testthat/test-Examples.R +++ b/tests/testthat/test-Examples.R @@ -190,6 +190,44 @@ test_that("Basic model fitting", { expect_output(exFit$run(intern = T), "The run did not converge before the last cycle.") }) +test_that("Analytical models allow multi-line secondary conditionals", { + mod <- PM_model$new( + pri = list( + Ka = ab(0.5, 6), + Ke = ab(0.1, 1.5), + v0 = ab(25, 120) + ), + cov = list( + gender = interp("none"), + wt = interp() + ), + sec = function() { + v = v0 * wt + if (gender < 1) { + v = v0 * 0.8 + } + }, + eqn = function() { + two_comp_bolus + }, + lag = function() { + lag[1] = 0 + }, + out = function() { + y[1] = x[2] / v + }, + err = list( + proportional(5, c(0.1, 0.15, 0, 0)) + ), + compile = FALSE + ) + + expect_s3_class(mod, "PM_model") + sec_code <- paste(deparse(mod$arg_list$sec), collapse = "\n") + expect_true(grepl("if (gender < 1)", sec_code, fixed = TRUE)) + expect_true(grepl("v = v0 * wt", sec_code, fixed = TRUE)) +}) + # test_that("Load model",{ # exRes <- PM_load(1) # expect_equal(exRes$data$data,PM_data$new(data = "ex.csv", quiet=T)$data, ignore_attr = T)