{"id":16854733,"url":"https://github.com/tommyettinger/digital","last_synced_at":"2025-03-22T06:31:05.944Z","repository":{"id":37632379,"uuid":"487714794","full_name":"tommyettinger/digital","owner":"tommyettinger","description":"Utilities for handling math and showing numbers in Java.","archived":false,"fork":false,"pushed_at":"2025-03-17T03:46:20.000Z","size":5864,"stargazers_count":17,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-03-17T04:33:02.699Z","etag":null,"topics":["base","hash","java","math","radix","trigonometry","utility"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tommyettinger.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-02T04:23:52.000Z","updated_at":"2025-03-17T03:46:23.000Z","dependencies_parsed_at":"2023-02-17T14:45:47.958Z","dependency_job_id":"efc2d8f0-c7e4-414a-b53e-92bbc2508994","html_url":"https://github.com/tommyettinger/digital","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":"tommyettinger/libgdx-library-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyettinger%2Fdigital","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyettinger%2Fdigital/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyettinger%2Fdigital/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyettinger%2Fdigital/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tommyettinger","download_url":"https://codeload.github.com/tommyettinger/digital/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244918500,"owners_count":20531682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["base","hash","java","math","radix","trigonometry","utility"],"created_at":"2024-10-13T13:56:53.401Z","updated_at":"2025-03-22T06:31:05.937Z","avatar_url":"https://github.com/tommyettinger.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# digital\n\n![digital](docs/logo.png)\n\nUtilities for handling math and showing numbers in Java.\n\n## What is it?\n\nThere are only a few classes here, and any given application\nonly is likely to use even fewer of them. Still, they're\nuseful as a package, since they often depend on each other.\n\nBitConversion allows converting float and double values to\nint and long versions of their underlying bits (and it does\nthis in a way that works on GWT efficiently). While code that\ndoes this is available in the regular JDK in Float and Double,\nthe implementations for those provided by GWT are extremely\nslow. BitConversion was initially meant just to provide a\nfaster route for those conversions on GWT, but it also has\nsome methods that convert float-int and double-long with\nreversed byte order (using a fast intrinsic on desktop JDKs\nand a special trick on GWT), and others that get only the low\nor high half of a double's bits as an int. It is modeled after\nthe NumberUtils class from libGDX, but offers extra methods.\nIt also has, purely for GWT purposes, an `imul()` method that\nacts just like integer multiplication on most platforms, but\ncompiles down to a call to the JavaScript `Math.imul()`\nfunction on GWT, which automatically handles overflow in the\nsame way a desktop JVM would. Similarly, `countLeadingZeros()`\ncompiles to the JavaScript `Math.clz32()` function on GWT (or\nfor `long` arguments, some small code that calls that), while\nusing `Integer.numberOfLeadingZeros()` (or its `Long`\ncounterpart) on non-GWT platforms. Counting leading zeros is\nan operation that shows up in a surprising assortment of\nplaces and is supposed to be fast, so having an alternative to\n[this monstrosity](https://github.com/gwtproject/gwt/blob/main/user/super/com/google/gwt/emul/java/lang/Integer.java#L118)\nis more than welcome. JDK 19-24 (at least for some versions\nof those JDK releases) has a [known bug](https://bugs.openjdk.org/browse/JDK-8349637)\nin `Integer.numberOfLeadingZeros()` that is avoided currently\nand a fixed version used, while still taking advantage of\nintrinsics on HotSpot and `Math.clz32()` on GWT. There's also\n`countTrailingZeros()` for int and long arguments, which\ncompiles into a single call to\n`Integer.numberOfTrailingZeros()` (or using `Long`) on most\nplatforms, or a JS two-liner on GWT that uses `Math.clz32()`.\nAlso purely for GWT support, `lowestOneBit()` works around a\nbug that was present in GWT 2.8.2 and may still be present,\nwhere its built-in `Long.lowestOneBit()` method could return\nvery wrong results for larger inputs.\n\nBase is much larger, and allows converting any Java primitive\nnumber type to a specific base/radix/number-system. Here,\nBases are flexible enough to be able to be generated randomly\nfor obfuscation purposes (such as to mangle a high score in a\nsave file). There are several Base constants already defined.\nEach one can write numbers as signed (variable-length) or\nunsigned (fixed-length), as well as read back either type from\none method. Parsing is significantly more relaxed than in the\nJDK, and invalid numbers tend to be returned as 0 rather than\nrequiring an Exception to be caught. Base has some options\nwhen writing (or reading) a `float` or `double`. Like other\nnumeric types, you can use `signed()` or `unsigned()` to write\nusing the digits the Base usually uses, though here it writes\nthe bits that compose the float or double for higher accuracy.\nUnlike other numeric types, there are base-10 `decimal()`,\n`scientific()`, `general()`, and `friendly()` methods to write\nfloats/doubles with different rules for when to switch to\nscientific notation, if at all. These can be read back with\n`readFloat()` and `readDouble()`, while the signed/unsigned\noutput needs `readFloatExact()` or `readDoubleExact()`. If you\nuse a scrambled base (a random one, as mentioned before), then\nyou need to use `signed()`/`unsigned()`/`readFloatExact()`/\n`readDoubleExact()` to use the right scrambled digits.\n`decimal()` can take an exact length to limit large output to,\nor zero-pad small output to, as well as a precision for how\nmany digits to show after the decimal point. Base\ncan also combine the String representations of an array of\nprimitives (or part of such an array, since 0.3.1) using\n`join()` or `appendJoined()`, and split apart Strings into \narrays of primitives with methods like `longSplit()`,\n`intSplit()`, and `floatSplit()`. There are also now static\nmethods with `readable` in their names; these output text\nthat can be used in Java source code as numeric or character\nliterals, for code generation. The `Base.BASE10.readLong()`\nmethod can read a long produced by `Base.readable(long)` just\nfine, because readLong() ignores invalid characters after the\nnumber (such as `L` in longs). Reading in the char literals\nthis can produce requires using `Base.readCharReadable()`.\n\nTrigTools tries to be as complete as possible at covering\ntrigonometric functions, offering sin, cos, tan, asin, acos,\natan, and atan2 in radians, degrees, and turns. It also allows\naccess to the lookup tables used by sin and cos. Much of\nTrigTools can be seen as similar to what libGDX's MathUtils\nclass offers, but allowing access to the lookup tables permits\na few novel features (see its docs). It supports float and\ndouble arguments/returns for all functions. It also provides\n\"smooth\" sin and cos approximations that aren't table-based,\nand \"smoother\" sin/cos/tan versions that interpolate between\nentries in the lookup table for very high precision. It should\nbe pointed out that very few libraries support trigonometric\nfunctions that take angles in turns, but turns can be very\nuseful for a variety of cases. For example, if you want to\nstore a hue (which is essentially an angle) into a limited\nformat such as one channel of a color to be sent to the GPU,\nstoring the hue as an angle in turns keeps it in the 0.0\nto 1.0 range, but using radians or degrees would not. There's\nalso a few extra methods, such as `atan2Deg360()`, which acts\nlike the degree version of atan2, but always returns an angle\nbetween 0 (inclusive) and 360 (exclusive), since negative\nangles are less intuitive and sometimes not supported.\n\nMathTools offers a wild grab bag of math functions and\nconstants, from simple lerp, floor, ceil, and clamp methods to\nan optimized cube root function and parameterized splines. It\nis also based on MathUtils from libGDX. It supports float and\ndouble arguments/returns for most functions; some make sense\nfor float only, like the optimized cube root. There's also a\nlot of commonly-defined constants, such as the square root of\n2 and the golden ratio, as floats and doubles. Some methods\nhere are useful in other mathematical code, like gamma and\ngreatestCommonDivisor; others are more esoteric, like\nmodularMultiplicativeInverse. There are some of the standard\nsignal processing functions, such as triangle waves, square\nwaves, and sawtooth waves. There are also a few functions\nhere, \"sway\" and relatives, that look like sine waves when\ngraphed, but are simpler to calculate internally. You can do\nsome bitwise operations with MathTools, such as interleaving\nthe bits of two numbers (also called a Morton code, or a\nposition on the Z-Order Curve). There are ways to bring a\nfloat or double closer to 0.0 by the smallest possible amount.\nThere's the fract() methods, familiar to shader programmers,\nthat get the fractional part of a float or double. There's\nFreya Holmer's recently-discovered method to make a float\napproach another value at an even rate using interpolation.\nVarious methods are there relating to sigmoid functions,\nincluding two configurable methods (basicSigmoid() and\nrootSigmoid()), and an alternative to the just-mentioned\napproach() that uses a sigmoid function to gradually approach\na target value.\nIn general, if you need a math function that isn't\ntrigonometry-related and doesn't do something with text, you\nmay want to look here first.\n\nArrayTools provides common code for dealing with 2D arrays, and\nalso sometimes 1D or 3D arrays. It allows copying, inserting,\nand filling 2D arrays, and creating ranges of 1D arrays. It also\nhas a lot of methods for shuffling 1D arrays, 2D arrays, and\nsections of 1D arrays, for all primitive types and for objects.\nThere are also some \"filler supplies\" in ArrayTools -- methods\nfor filling up char, int, or String arrays with contents that\nare guaranteed to be distinct up to a certain limit. The same\ndata that can be used to fill up arrays with ArrayTools also\ngets used by Hasher for seeding its predefined instances. The\nfiller supplies include the names of Greek letters, the chemical\nelements of the periodic table, and the listing of names of\ndemons from the Ars Goetia (many of which are familiar names\nfrom games that also used that list).\n\nHasher is... large. It provides fast, high-quality hashing\nfunctions for primitive arrays (and arrays of objects, if they\nimplement hashCode()), and has 64-bit and 32-bit variants. The\nspecific hashing algorithm it uses is a somewhat-hardened\nversion of [wyhash](https://github.com/wangyi-fudan/wyhash) that\ndoesn't use 128-bit math.\n\nWhile Hasher can hash all types of 1D\nprimitive array and most types of 2D primitive array using dedicated\nmethods, 3D (and higher) dimensions are hashed using a generalized\nmethod that takes another method, usually one of the previously\nmentioned hashes for primitive arrays. The 3D code is already defined\nfor primitives, using calls like\n`Hasher.byteArray3DHashBulk64.hash64(longSeed, bytes3D)`, where\nlongSeed is a long to seed the hash, and bytes3D is a `byte[][][]`.\nThat byteArray3DHashBulk64() is a lambda of the type\n`SeededHashFunction64\u003cbyte[][][]\u003e` that calls\n`hashBulk64(seed, byteArray2DHashBulk64, data)`; you could change 2D\nto 3D there to make your own hash for 4D byte arrays (treating\nthem as arrays of 3D byte arrays). Hasher\nallows you to specify the seed when you call `hash()` or\n`hash64()` (as static methods), but it also has a fairly-large\narray of `predefined` hash functions with instance methods for\n`hash()` and `hash64()`. Starting in 0.3.0, Hasher can either\nprocess a whole input array or part of one, specified by a start\nindex and a length. Starting in 0.5.1, Hasher also provides\n`hashBulk()` and `hashBulk64()` methods that use a different,\nslightly-higher-quality algorithm, and these methods can be\nfaster than `hash` and `hash64` on large inputs. How large\ndepends on your use case, but the quality improvement may be\nenough reason to switch on its own.\n\nHasher also has a few unary hashes that\ncan be used as quick and dirty random number generators when\napplied to numbers in a sequence. The unary hashes can output\nlongs, bounded ints, floats, and doubles, so they may be useful in\na lot of cases. They are named like `randomize1()`, `randomize2()`,\n`randomize3()`, and so on, with higher numbers being typically\nslightly slower but also higher-quality (and more permissive of\nsets of inputs with atypical patterns). Most usage is probably\nserved best by `randomize3()` and its bounded int, float, and\ndouble variants, since it uses a proven algorithm (MX3) and isn't\nmuch different on runtime performance.\n\nWhile Hasher should usually\nchange only very rarely, if at all, I was concerned about a few\nproperties of the hashing code that might have meant some values\nwould be returned less frequently, though this wasn't detectable to\nthe [SMHasher test suite](https://github.com/rurban/smhasher). In\nversion 0.4.0, Hasher's output for any given seed will be different\nfrom the same seed in previous versions. You can get the older\nHasher instances either from Git history or the test folder's\n[OldHasher v020](src/test/java/com/github/tommyettinger/digital/v020/OldHasher.java)\nor [OldHasher v037](src/test/java/com/github/tommyettinger/digital/v037/OldHasher.java) files,\nif you need to reproduce the results of older seeds. Newer versions\nof SMHasher do find statistical failures in at least some `hash()` and\n`hash64()` methods in `Hasher`, but the recently-added `hashBulk()`\nand `hashBulk64()` methods don't have detectable issues in SMHasher 3.\nThe bulk methods changed in version 0.6.0, so they also pass the older\nSMHasher 2. The changes mainly affect a test for \"bad seeds,\" which\nthe bulk hashes pass without trouble now. This does mean 0.5.x and\n0.6.0 produce different output for bulk hashes.\n\nAlternateRandom is a quick micro-port of a random number generator\nfrom the closely-related [juniper](https://github.com/tommyettinger/juniper)\nlibrary. It is used only in ArrayTools here, as the default when\nno `Random` object is specified. The alternative would be to use\na `java.util.Random` object, but that can't produce as many\npossible shuffles of mid-size arrays, and is slower, both of which\nAlternateRandom solves to some extent. If you don't use juniper,\nthen AlternateRandom is a pretty good replacement for `Random`;\nif you do use juniper, then its `AceRandom` or `PasarRandom`\ngenerators are similar to or the same as AlternateRandom's\nalgorithm, and offer many more features.\n\nShapeTools provides some predefined mathematical constants for\nthe vertices and faces of 3D polyhedra (currently, just the 5\nPlatonic solids). It could be useful for code that needs 3D shapes\nin code for something like continuous noise, but doesn't have\naccess to 3D models.\n\nTextTools provides some features that are similar to ones in Base,\nbut different enough to belong in their own class. It operates on\nCharSequences usually, but sometimes needs Strings, so I'll just\ncall this vague type \"text.\" This class includes many different\nways to search text for something, code to count the occurrences\nof text in a larger piece of text, code to join/split arrays of\ntext and larger texts (this also works for boolean arrays), code\nfor padding text, code for replacing text with literals, and code to\nrepeat pieces of text multiple times (possibly with a delimiter).\nThis class mostly exists to avoid duplicating similar code that\noccurs often throughout my projects, and is related to code here.\n\nInterpolations, along with its nested classes InterpolationFunction\nand Interpolator, provide a way to store and look up functions to\nsmoothly interpolate between floats. This code is very similar to\nthe Interpolation class in libGDX, and all the instances in\nInterpolation have a counterpart in Interpolations (there are some\nmore here, as well). Creating an Interpolator registers its name\nin Interpolations' registry, where it can be looked up on its own\nwith `get(String)` or as a group with `getInterpolatorArray()`.\nThere are also ways to create one of the building blocks of an\nInterpolator, an InterpolationFunction, with methods in\nInterpolations. This makes creating new Interpolators with different\nparameters as easy as assigning a name to the generated function.\n[Here's a sample page that shows how each Interpolator looks graphed.](https://tommyettinger.github.io/digital/interpolators.html)\nUsing that page of graphs is a good idea when you're deciding which\nof the many Interpolator varieties to use.\n\nRoughMath has a variety of \"rough\" and \"rougher\" approximations to a\nvariety of functions. These tend to be faster but less precise.\nThe logistic functions `logisticRough()` and `logisticRougher()` are\nactually not that imprecise for small-ish inputs, and are a handy\ntool that is only available in RoughMath. There's also hyperbolic\ntrigonometric functions, which aren't anywhere else either, plus\n`expRough()` and a variant `pow2Rough()`. MathTools also has\napproximations to `Math.exp()`, including one that has a limited\ndomain (it returns 0 outside that domain, instead of returning\ngradually smaller numbers; this can sometimes be useful).\n\nStringf is a somewhat experimental class that exists mostly to be a\npartial polyfill for the missing `String.format()` method on GWT and\non some versions of TeaVM. It doesn't have anywhere near the full\nfunctionality of `String.format()`, or even the functionality of\n[Formic](https://github.com/tommyettinger/formic)'s `Stringf.format()`,\nbut unlike Formic it should work on TeaVM. It does have enough support\nfor the simpler usages of `%d` and `%f`, as well as common conveniences\nsuch as `%08X` to print a 32-bit int as 8 hex digits, and always 8.\nYou could also use `Base.BASE16.unsigned(num)` for that.\n\nDistributor is a relatively narrow class that exists to redistribute\nint, long, and double inputs to float or double normal-distributed\nvalues, while preserving patterns in the input data. The `probit` family of\nmethods can take int or long input, and act by mapping the lowest possible\ninputs to the lowest normal-distributed values it can produce, and similarly\nfor the highest; inputs near 0 produce results near 0. There are probably all\nsorts of creative uses for mapping int or long inputs to normal-distributed\noutputs; you can find some yourself! Distributor also provides the Ziggurat\nalgorithm, which has higher-quality output in the trail of the distribution,\nbut does not preserve patterns from input to output. The `normal()` method\nusing Ziggurat is about as fast as it gets for generating Gaussian variates.\n\n## How do I get it?\n\nThis library needs Java language level 8, but does not rely on any\nAPIs introduced in Java 8. Targeting level 8 means this will work\neven if your project uses the newest Java versions (20 and later do\nnot support targeting Java 7). Android projects should be able to\nuse digital even without needing core library desugaring, as long\nas they target release 8 or higher. GWT can use digital without\nissue from GWT 2.8.0 up; GWT compatibility is a major focus of this\nlibrary. RoboVM, for iOS, can use digital because no APIs are used\nfrom Java 8.\n\nIn a libGDX project, **you must make sure** the sourceCompatibility is\n8 or higher in your core module and any other modules that use digital.\nThis is currently not the default for gdx-setup projects (which is no\nlonger the official setup tool), but is the default for\n[gdx-liftoff](https://github.com/libgdx/gdx-liftoff) projects (which is\nthe official setup tool). Liftoff also lets you just check a box to\ndepend on digital.\n\nTo depend on digital with Gradle, add this to your dependencies (in\nyour core module's `build.gradle`, for libGDX projects):\n\n```groovy\napi \"com.github.tommyettinger:digital:0.6.2\"\n```\n\nIf you target GWT using libGDX, you will also need this in your\nhtml module's `build.gradle`:\n\n```groovy\napi \"com.github.tommyettinger:digital:0.6.2:sources\"\n```\n\nGWT needs to be told about these changes in your `GdxDefinition.gwt.xml`\nfile. For digital 0.1.7 and later, use:\n\n```xml\n\u003cinherits name=\"com.github.tommyettinger.digital\" /\u003e\n```\n\nIf you are using 0.1.6 or older, **there are probably some GWT\ncompatibility issues**, though you can try using this. You should\n**update to 0.1.7 or later instead of using this**:\n\n```xml\n\u003cinherits name=\"digital\" /\u003e\n```\n\nYou can also use JitPack to get a recent commit; in that case,\nfollow [its instructions here](https://jitpack.io/#tommyettinger/digital/).\nThis also has instructions for Maven and other build tools.\n\n## License\n\n[Apache 2.0](LICENSE). This includes some modified code from\n[libGDX](https://github.com/libgdx/libgdx), \n[SquidLib](https://github.com/yellowstonegames/SquidLib),\n[SquidSquad](https://github.com/yellowstonegames/SquidSquad),\n[Uncommon Maths](https://maths.uncommons.org/),\n[wyhash](https://github.com/wangyi-fudan/wyhash),\n[Apache Commons Lang](https://github.com/apache/commons-lang),\n[fastapprox](https://code.google.com/archive/p/fastapprox/),\n[root-cellar](https://github.com/EvanBalster/root-cellar/),\nand [Ryu](https://github.com/ulfjack/ryu). More code\nis not from a particular repository (for example, some is from\nWikipedia); see each file for specific author credits. The Ryu\ncode is more substantial than the other projects, and even\nthough its license is also Apache 2.0, I included its license\nhere as [LICENSE-RYU.txt](LICENSE-RYU.txt). The Ryu code that\nis relevant to what we use here is also replicated with only\nminor compatibility changes in the `src/test` folder.\nThere's some cases where an individual method was ported from\nanother permissively-licensed repo, such as\n`a_cbrt` from [Stand-alone-junk](https://github.com/Marc-B-Reynolds/Stand-alone-junk)\n(Marc B. Reynolds' in-progress code), or `probit` from\n[P.J. Acklam's site](https://web.archive.org/web/20151030215612/http://home.online.no/~pjacklam/notes/invnorm/)\n(archived), and for most of these I\nhave tried to provide credit and a link to the source in the\ndocumentation for that method. Some code is derived from\npapers, like how Distributor uses\n[this algorithm by Paul Voutier](https://www.researchgate.net/publication/46462650_A_New_Approximation_to_the_Normal_Distribution_Quantile_Function).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftommyettinger%2Fdigital","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftommyettinger%2Fdigital","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftommyettinger%2Fdigital/lists"}