From d6d5f7abccc6d61f025817078b048b5222b5c635 Mon Sep 17 00:00:00 2001
From: Colin Leach <colin.leach@comcast.net>
Date: Sun, 26 Nov 2023 15:27:39 -0700
Subject: [PATCH] fractions concept

---
 concepts/fractions/.meta/config.json |   5 ++
 concepts/fractions/about.md          | 101 +++++++++++++++++++++++++++
 concepts/fractions/introduction.md   | 101 +++++++++++++++++++++++++++
 concepts/fractions/links.json        |   6 ++
 config.json                          |   5 ++
 5 files changed, 218 insertions(+)
 create mode 100644 concepts/fractions/.meta/config.json
 create mode 100644 concepts/fractions/about.md
 create mode 100644 concepts/fractions/introduction.md
 create mode 100644 concepts/fractions/links.json

diff --git a/concepts/fractions/.meta/config.json b/concepts/fractions/.meta/config.json
new file mode 100644
index 0000000000..94a4196b77
--- /dev/null
+++ b/concepts/fractions/.meta/config.json
@@ -0,0 +1,5 @@
+{
+  "blurb": "The fractions module enables working with rational numbers, which preserve exact values and avoid the rounding errors common with floats.",
+  "authors": ["bethanyg", "cmccandless", "colinleach"],
+  "contributors": []
+}
diff --git a/concepts/fractions/about.md b/concepts/fractions/about.md
new file mode 100644
index 0000000000..22b0e45d6f
--- /dev/null
+++ b/concepts/fractions/about.md
@@ -0,0 +1,101 @@
+# About
+
+The [`Fractions`][fractions] module allows us to handle `rational numbers`: fractions with an integer numerator divided by an integer denominator.
+For example, we can store `2/3` as an exact fraction instead of the approximate `float` value `0.6666...`
+
+## Creating
+
+The constructor is quite flexible.
+Most obviously, it can take take two integers.
+Common factors are removed, to convert the fraction to its "lowest form": the smallest integers that accurately represent the fraction.
+
+```python
+>>> from fractions import Fraction
+
+>>> f1 = Fraction(2, 3) # 2/3
+>>> f1
+Fraction(2, 3)
+
+>>> f2 = Fraction(6, 9)
+>>> f2
+Fraction(2, 3)  # automatically simplified
+
+>>> f1 == f2
+True
+```
+
+It can also parse a string representation of the fraction:
+
+```python
+>>> f3 = Fraction('2/3')
+>>> f3
+Fraction(2, 3)
+```
+
+It can work with `float` parameters, but this may run into problems with the approximate nature of representing the decimal value interally as binary. 
+For a more reliable result, there is the `limit_denominator()` method.
+This can take an integer parameter if you have specific requirements, but even the default can work well.
+
+```python
+>>> Fraction(1.2)
+Fraction(5404319552844595, 4503599627370496)
+
+>>> Fraction(1.2).limit_denominator()
+Fraction(6, 5)
+```
+
+## Arithmetic
+
+The usual arithmetic operators `+ - * / **` work with fractions.
+Integers and other `Fraction`s can be included and give a `Fraction` result.
+Including a `float` results in `float` output.
+
+```python
+>>> Fraction(2, 3) + Fraction(1, 4) # addition
+Fraction(11, 12)
+
+>>> Fraction(2, 3) * Fraction(6, 5) # multiply fractions
+Fraction(4, 5)
+
+>>> Fraction(2, 3) * 6 / 5 # fraction with integers
+Fraction(4, 5)
+
+>>> Fraction(2, 3) * 1.2  # fraction with float -> float
+0.7999999999999999
+
+>>> Fraction(2, 3) ** 2  # exponentiation with integer
+Fraction(4, 9)
+```
+
+## Conversions
+
+Fractions are great for preserving precision during intermediate calculations, but may not be what you want for the final output.
+
+It is possible to get the numerator and denominator individually or as a tuple:
+
+```python
+>>> Fraction(2, 3).numerator
+2
+>>> Fraction(2, 3).denominator
+3
+>>> Fraction(2, 3).as_integer_ratio()
+(2, 3)
+```
+
+Various standard Python functions also give the expected result:
+
+```python
+>>> round(Fraction(11, 3))
+4
+
+>>> from math import floor, ceil
+>>> floor(Fraction(11, 3))
+3
+>>> ceil(Fraction(11, 3))
+4
+
+>>> float(Fraction(11, 3))
+3.6666666666666665
+```
+
+[fractions]: https://docs.python.org/3/library/fractions.html
diff --git a/concepts/fractions/introduction.md b/concepts/fractions/introduction.md
new file mode 100644
index 0000000000..13ad048486
--- /dev/null
+++ b/concepts/fractions/introduction.md
@@ -0,0 +1,101 @@
+# Introduction
+
+The [`Fractions`][fractions] module allows us to handle `rational numbers`: fractions with an integer numerator divided by an integer denominator.
+For example, we can store `2/3` as an exact fraction instead of the approximate `float` value `0.6666...`
+
+## Creating
+
+The constructor is quite flexible.
+Most obviously, it can take take two integers.
+Common factors are removed, to convert the fraction to its "lowest form": the smallest integers that accurately represent the fraction.
+
+```python
+>>> from fractions import Fraction
+
+>>> f1 = Fraction(2, 3) # 2/3
+>>> f1
+Fraction(2, 3)
+
+>>> f2 = Fraction(6, 9)
+>>> f2
+Fraction(2, 3)  # automatically simplified
+
+>>> f1 == f2
+True
+```
+
+It can also parse a string representation of the fraction:
+
+```python
+>>> f3 = Fraction('2/3')
+>>> f3
+Fraction(2, 3)
+```
+
+It can work with `float` parameters, but this may run into problems with the approximate nature of representing the decimal value interally as binary. 
+For a more reliable result, there is the `limit_denominator()` method.
+This can take an integer parameter if you have specific requirements, but even the default can work well.
+
+```python
+>>> Fraction(1.2)
+Fraction(5404319552844595, 4503599627370496)
+
+>>> Fraction(1.2).limit_denominator()
+Fraction(6, 5)
+```
+
+## Arithmetic
+
+The usual arithmetic operators `+ - * / **` work with fractions.
+Integers and other `Fraction`s can be included and give a `Fraction` result.
+Including a `float` results in `float` output.
+
+```python
+>>> Fraction(2, 3) + Fraction(1, 4) # addition
+Fraction(11, 12)
+
+>>> Fraction(2, 3) * Fraction(6, 5) # multiply fractions
+Fraction(4, 5)
+
+>>> Fraction(2, 3) * 6 / 5 # fraction with integers
+Fraction(4, 5)
+
+>>> Fraction(2, 3) * 1.2  # fraction with float -> float
+0.7999999999999999
+
+>>> Fraction(2, 3) ** 2  # exponentiation with integer
+Fraction(4, 9)
+```
+
+## Conversions
+
+Fractions are great for preserving precision during intermediate calculations, but may not be what you want for the final output.
+
+It is possible to get the numerator and denominator individually or as a tuple:
+
+```python
+>>> Fraction(2, 3).numerator
+2
+>>> Fraction(2, 3).denominator
+3
+>>> Fraction(2, 3).as_integer_ratio()
+(2, 3)
+```
+
+Various standard Python functions also give the expected result:
+
+```python
+>>> round(Fraction(11, 3))
+4
+
+>>> from math import floor, ceil
+>>> floor(Fraction(11, 3))
+3
+>>> ceil(Fraction(11, 3))
+4
+
+>>> float(Fraction(11, 3))
+3.6666666666666665
+```
+
+[fractions]: https://docs.python.org/3/library/fractions.html
diff --git a/concepts/fractions/links.json b/concepts/fractions/links.json
new file mode 100644
index 0000000000..d48ae21b00
--- /dev/null
+++ b/concepts/fractions/links.json
@@ -0,0 +1,6 @@
+[
+  {
+    "url": "https://docs.python.org/3/library/fractions.html/",
+    "description": "Documentation for the Fractions module."
+  }
+]
diff --git a/config.json b/config.json
index 98891388c2..734fa7a98f 100644
--- a/config.json
+++ b/config.json
@@ -2567,6 +2567,11 @@
       "uuid": "565f7618-4552-4eb0-b829-d6bacd03deaf",
       "slug": "with-statement",
       "name": "With Statement"
+    },
+    {
+      "uuid": "000e7768-38b9-4904-9ae2-9a4e448f366c",
+      "slug": "fractions",
+      "name": "Fractions"
     }
   ],
   "key_features": [