From 6cca4358325ffd4bd1e91a7890c846b57c95d8e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20J=2E=20Basas?= Date: Wed, 1 Jan 2025 17:59:11 +0100 Subject: [PATCH] Initial release of fractus Fractus is a library for performing mathematical operations on fractions. --- packages/preview/fractus/0.1.0/LICENSE | 23 ++++ packages/preview/fractus/0.1.0/README.md | 39 +++++++ packages/preview/fractus/0.1.0/lib.typ | 124 ++++++++++++++++++++++ packages/preview/fractus/0.1.0/typst.toml | 9 ++ 4 files changed, 195 insertions(+) create mode 100644 packages/preview/fractus/0.1.0/LICENSE create mode 100644 packages/preview/fractus/0.1.0/README.md create mode 100644 packages/preview/fractus/0.1.0/lib.typ create mode 100644 packages/preview/fractus/0.1.0/typst.toml diff --git a/packages/preview/fractus/0.1.0/LICENSE b/packages/preview/fractus/0.1.0/LICENSE new file mode 100644 index 000000000..31aa79387 --- /dev/null +++ b/packages/preview/fractus/0.1.0/LICENSE @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/packages/preview/fractus/0.1.0/README.md b/packages/preview/fractus/0.1.0/README.md new file mode 100644 index 000000000..ebf511797 --- /dev/null +++ b/packages/preview/fractus/0.1.0/README.md @@ -0,0 +1,39 @@ +# Fractus + +Fractus is a library for performing mathematical operations on fractions with [Typst](https://typst.app). + +In the etymology tree of the word *fraction*, *fractus* is the past participle of the Latin verb *frangere* (to break). + +## Examples + +```typ +#import "@preview/fractus:0.1.0" as frac + +//Manipulating and displaying a fraction +#frac.simplify("15/-21") + +#frac.opposite("15/-21") + +#frac.inverse("15/-21") + +$#frac.display("3/-18") + 3/7$ + +#frac.display("3/18", style: "display") + +#frac.display("5/1", style: "display") + +#frac.float("3/18") + +//Calculate and compare fractions +#frac.sum(2, "3/18", "5/12", "5/6") + +#frac.difference("3/18", decimal("0.3")) + +#frac.product("-3/18", "5/12", "5/-6") + +#frac.division("3/18", "5/12") + +#frac.eq("-4/-8", frac.opposite("-4/8"), "5/10") + +#frac.eq("-4/-8", "-1/2", "5/10") +``` diff --git a/packages/preview/fractus/0.1.0/lib.typ b/packages/preview/fractus/0.1.0/lib.typ new file mode 100644 index 000000000..f889655ab --- /dev/null +++ b/packages/preview/fractus/0.1.0/lib.typ @@ -0,0 +1,124 @@ +#let _frac(it) = { + if type(it) == int { + (numerator: it, denominator: 1) + } else if type(it) == decimal { + let decimal_part = str(it).match(regex("[+-]?\d+(?:[.](\d*))?")).at("captures").at(0) + let exp = if decimal_part == none {0} else {decimal_part.len()} + let denominator = calc.pow(10,exp) + (numerator: int(it*denominator), denominator: denominator) + } else if type(it) == dictionary and ("numerator" in it) and ("denominator" in it) { + it + } else if type(it) == str { + let it = it.match(regex("^([+-]?\d+)\/([+-]?\d+)$")) + if it == none { + panic("not a fraction") + } else { + let it = it.at("captures").map(int) + (numerator: it.at(0), denominator: it.at(1)) + } + } else { + panic("not a fraction") + } +} + +#let _str(n) = { + /** + "-" (U+002D) and the minus sign "−" (U+2212) are not the same. + The str() function generates the minus sign. + **/ + str(n.numerator).replace("−","-")+"/"+str(n.denominator).replace("−","-") +} + +#let _simplify(n) = { + if n.denominator.signum() == -1 { + n.numerator = n.numerator * -1 + n.denominator = n.denominator * -1 + } + + let divider = calc.gcd(n.numerator, n.denominator) + ( + numerator: calc.div-euclid(n.numerator, divider), + denominator: calc.div-euclid(n.denominator, divider) + ) +} + +#let simplify(n) = { + _str(_simplify(_frac(n))) +} + +#let _opposite(n) = { + (numerator: n.numerator * -1, denominator: n.denominator) +} + +#let opposite(n) = { + _str(_opposite(_simplify(_frac(n)))) +} + +#let _inverse(n) = { + (numerator: n.denominator, denominator: n.numerator) +} + +#let inverse(n) = { + _str(_simplify(_inverse(_frac(n)))) +} + +#let display(n, style: "inline") = { + let n = _frac(n) + if n.denominator == 1 { + n.numerator + } else { + eval("$" + style + "((" + str(n.numerator) + ")/(" + str(n.denominator) + "))$") + } +} + +#let float(n) = { + let n = _simplify(_frac(n)) + n.numerator/n.denominator +} + +#let _sum(..args) = { + let denominator = args.pos().map(n => n.denominator).dedup().reduce(calc.lcm) + _simplify(( + numerator: args.pos().map( + n => calc.div-euclid(denominator, n.denominator)*n.numerator + ).sum(), + denominator: denominator + )) +} + +#let sum(..args) = { + let args = args.pos().map(_frac).map(_simplify) + _str(_sum(..args)) +} + +#let difference(n, m) = { + (n, m) = (n, m).map(_frac).map(_simplify) + sum(n, _opposite(m)) +} + +#let _product(..args) = { + _simplify(( + numerator: args.pos().map(n => n.numerator).product(), + denominator: args.pos().map(n => n.denominator).product() + )) +} + +#let product(..args) = { + let args = args.pos().map(_frac).map(_simplify) + _str(_product(..args)) +} + +#let division(n, m) = { + (n, m) = (n, m).map(_frac).map(_simplify) + product(n, _inverse(m)) +} + +#let _eq(..args) = { + let denominator = args.pos().map(n => n.denominator).dedup().reduce(calc.lcm) + args.pos().map(n => calc.div-euclid(denominator, n.denominator)*n.numerator).dedup().len() == 1 +} + +#let eq(..args) = { + let args = args.pos().map(_frac).map(_simplify) + _eq(..args) +} diff --git a/packages/preview/fractus/0.1.0/typst.toml b/packages/preview/fractus/0.1.0/typst.toml new file mode 100644 index 000000000..a3df35aa0 --- /dev/null +++ b/packages/preview/fractus/0.1.0/typst.toml @@ -0,0 +1,9 @@ +[package] +name = "fractus" +version = "0.1.0" +entrypoint = "lib.typ" +authors = ["Étienne J. Basas "] +repository = "https://github.com/ejbasas/fractus" +license = "MIT" +description = "Operations on fractions." +categories = ["utility","scripting"]