Fraction.js

import { unSiPositifMoinsUnSinon, texFractionSigne, arrondi, fractionSimplifiee, obtenirListeFacteursPremiers, calcul, texFraction, quotientier, extraireRacineCarree } from './outils.js'
import { point, vecteur, segment, carre, cercle, arc, translation, rotation, texteParPosition } from './2d.js'
import { fraction } from './fractions.js'

const definePropRo = (obj, prop, get) => {
  Object.defineProperty(obj, prop, {
    enumerable: true,
    get,
    set: () => { throw Error(`${prop} est en lecture seule`) }
  })
}

/**
 * @class
 * @param {number} num numérateur
 * @param {number} den dénominateur
 * @author Jean-Claude Lhote et Sébastien Lozano
*/
class Fraction {
  constructor (num, den) {
    /**
     * Numérateur (0 par défaut)
     * @type {number}
     */
    this.num = num || 0
    if (typeof this.num !== 'number' || Number.isNaN(this.num)) throw Error(`Numérateur invalide ${this.num}`)
    /**
     * Dénominateur (1 par défaut)
     * @type {number}
     */
    this.den = den || 1
    if (typeof this.den !== 'number' || Number.isNaN(this.den)) throw Error(`Dénominateur invalide ${this.den}`)
    if (this.den === 0) throw Error('Dénominateur nul impossible')
    // pour ne pas faire ces calculs à chaque instanciation de Fraction, on passe par defineProperty
    // (qui permet de ne faire le calcul qu'à la première lecture de la propriété)
    /**
     * Numérateur réduit
     * @property numIrred
     * @type {number}
     */
    let numIrred
    definePropRo(this, 'numIrred', () => {
      if (!numIrred) numIrred = fractionSimplifiee(this.num, this.den)[0]
      return numIrred
    })
    /**
     * Dénominateur réduit
     * @property denIrred
     * @type {number}
     */
    let denIrred
    definePropRo(this, 'denIrred', () => {
      if (!denIrred) denIrred = fractionSimplifiee(this.num, this.den)[1]
      return denIrred
    })
    /**
     * Valeur de la fraction × 100
     * @property pourcentage
     * @type {number}
     */
    let pourcentage
    definePropRo(this, 'pourcentage', () => {
      if (!pourcentage) pourcentage = calcul(this.numIrred * 100 / this.denIrred)
      return pourcentage
    })
    /**
     * le signe de la fraction : -1 pour négatif , 0 ou 1 pour positif
     * @type {number}
     */
    this.signe = (this.num === 0) ? 0 : unSiPositifMoinsUnSinon(this.num * this.den)

    /**
     * le signe de la fraction : '-' ou '+'
     * @type {string}
     */
    this.signeString = (this.signe === -1) ? '-' : (this.signe === 0) ? '' : '+'

    /**
     * num/den
     * @property texFraction
     * @type {string}
     */
    let texFraction
    definePropRo(this, 'texFraction', () => {
      if (!texFraction) texFraction = `\\dfrac{${this.num}}{${this.den}}`
      return texFraction
    })
    /**
     * num/den mais avec simplification des signes
     * @property texFractionSigneDevant
     * @type {string}
     */
    let texFractionSigneDevant
    definePropRo(this, 'texFractionSigneDevant', () => {
      if (!texFractionSigneDevant) texFractionSigneDevant = den === -1 ? String(-num) : den === 1 ? String(num) : num * den > 0 ? `\\dfrac{${Math.abs(num)}}{${Math.abs(den)}}` : num * den < 0 ? `-\\dfrac{${Math.abs(num)}}{${Math.abs(den)}}` : '0'
      return texFractionSigneDevant
    })

    /**
     * +n/d si positif, -n/d si négatif
     * @property texFractionSignee
     * @type {string}
     */
    let texFractionSignee
    definePropRo(this, 'texFractionSignee', () => {
      if (!texFractionSignee) texFractionSignee = (this.signe === -1) ? this.texFraction : '+' + this.texFraction
      return texFractionSignee
    })
    /**
     * n/d si positif, (-n/d) sinon
     * @property texFractionSigneeParentheses
     * @type {string}
     */
    let texFractionSigneeParentheses
    definePropRo(this, 'texFractionSigneeParentheses', () => {
      if (!texFractionSigneeParentheses) texFractionSigneeParentheses = (this.signe >= 0) ? this.texFraction : `(${this.texFractionSignee})`
      return texFractionSigneeParentheses
    })
    /**
     * le code LaTeX de la fraction simplifiée
     * @property texFractionSimplifiee
     * @type {string}
     */
    let texFractionSimplifiee
    definePropRo(this, 'texFractionSimplifiee', () => {
      if (!texFractionSimplifiee) texFractionSimplifiee = texFractionSigne(this.numIrred, this.denIrred)
      return texFractionSimplifiee
    })
    /**
     * le code LaTeX de l'écriture algébrique de la fraction
     * @property ecritureAlgebrique
     * @type {string}
     */
    let ecritureAlgebrique
    definePropRo(this, 'ecritureAlgebrique', () => {
      if (!ecritureAlgebrique) ecritureAlgebrique = this.signe === 1 ? '+' + this.texFractionSigneDevant : this.texFractionSigneDevant
      return ecritureAlgebrique
    })
    /**
     * le code LaTeX de l'écriture avec parenthèse si négatif
     * @property ecritureParentheseSiNegatif
     * @type {string}
     */
    let ecritureParentheseSiNegatif
    definePropRo(this, 'ecritureParentheseSiNegatif', () => {
      if (!ecritureParentheseSiNegatif) ecritureParentheseSiNegatif = this.signe === 1 ? this.texFractionSigneDevant : '\\left(' + this.texFractionSigneDevant + '\\right)'
      return ecritureParentheseSiNegatif
    })

    /**
     * Valeurs avec 6 décimales
     * @type {number}
     */
    this.valeurDecimale = arrondi(this.num / this.den, 6)

    return this.texFraction
  }

  /**
   * Retourne la fraction réduite
   * @return {Fraction}
   */
  simplifie () {
    return new Fraction(this.numIrred, this.denIrred)
  }

  /**
   * Retourne la chaine latex contenant la racine carrée de la fraction
   * @param {boolean} detaillee Si detaillee est true, une étape de calcul se place avant le résultat.
   * @return {Fraction}
   */
  texRacineCarree (detaillee = false) {
    let factoDen = extraireRacineCarree(Math.abs(this.den))
    let factoNum
    if (factoDen[1] !== 1) {
      factoNum = extraireRacineCarree(Math.abs(this.num * factoDen[1]))
      factoDen = extraireRacineCarree(Math.abs(this.den * factoDen[1]))
    } else {
      factoNum = extraireRacineCarree(Math.abs(this.num))
    }
    const k = fraction(factoNum[0], factoDen[0]).simplifie()
    const r = fraction(factoNum[1], factoDen[1]).simplifie()
    let etape = ''
    if (this.signe === -1) {
      return false
    } else if (this.signe === 0) {
      return '0'
    } else {
      if (detaillee) {
        if (this.den !== 1) {
          etape = `\\sqrt{\\dfrac{${this.num}}{${this.den}}}=`
        } else {
          if (factoNum[0] !== 1) {
            etape = `\\sqrt{${this.num}}=`
          } else {
            etape = ''
          }
        }
        if (k.valeurDecimale !== 1) {
          if (k.den === 1) {
            etape += `\\sqrt{${factoNum[0]}^2\\times${factoNum[1]}}=`
          } else {
            if (factoNum[0] !== 1) {
              etape += `\\sqrt{\\dfrac{${factoNum[0]}^2\\times${factoNum[1]}}{${factoDen[0]}^2\\times${factoDen[1]}}}=`
            } else {
              if (factoDen[1] !== 1) {
                etape += `\\sqrt{\\dfrac{${factoNum[1]}}{${factoDen[0]}^2\\times${factoDen[1]}}}=`
              } else {
                etape += `\\sqrt{\\dfrac{${factoNum[1]}}{${factoDen[0]}^2}}=`
              }
            }
          }
        }
      }

      if (calcul(factoNum[1] / factoDen[1]) === 1) {
        return etape + k.texFraction
      } else {
        if (k.num === 1 && k.den !== 1) {
          if (r.den === 1) {
            return (k.valeurDecimale === 1 ? etape : etape + `\\dfrac{\\sqrt{${r.num}}}{${k.den}}`)
          } else {
            return (k.valeurDecimale === 1 ? etape : etape + k.texFraction) + `\\sqrt{${r.texFraction}}`
          }
        } else {
          return (k.valeurDecimale === 1 ? etape : etape + k.texFraction) + `\\sqrt{${r.texFraction}}`
        }
      }
    }
  }

  /**
   * Retourne la racine carrée de la fraction si c'est une fraction et false sinon
   * @param {boolean} detaillee Si detaillee est true, une étape de calcul se place avant le résultat.
   * @return {Fraction}
   */
  racineCarree () {
    const factoNum = extraireRacineCarree(Math.abs(this.num))
    const factoDen = extraireRacineCarree(Math.abs(this.den))
    const k = fraction(factoNum[0], factoDen[0]).simplifie()
    const r = fraction(factoNum[1], factoDen[1]).simplifie()
    if (r.valeurDecimale !== 1 || this.signe === -1) {
      return false
    } else {
      return k
    }
  }

  /**
   * @param {number} k Le nombre par lequel, le numérateur et le dénominateur sont multipliés.
   * @return {Fraction} La fraction "complexifiée" d'un rapport k
   */
  fractionEgale (k) {
    return new Fraction(calcul(this.num * k), calcul(this.den * k))
  }

  /**
   * Retourne l'opposé de la fraction
   * @return {Fraction}
   */
  oppose () {
    return new Fraction(-this.num, this.den)
  }

  /**
   * Retourne l'opposé de la fraction réduite
   * @return {Fraction}
   */
  opposeIrred () {
    return new Fraction(-this.numIrred, this.denIrred)
  }

  /**
   * Retourne la Valeur absolue de la fraction
   * @return {Fraction}
   */
  valeurAbsolue () {
    return new Fraction(Math.abs(this.num), Math.abs(this.den))
  }

  /**
   * Retourne l'inverse de la fraction
   * @return {Fraction}
   */
  inverse () {
    return new Fraction(this.den, this.num)
  }

  /**
   * Retourne l'inverse de la fraction simplifiée
   * @return {Fraction}
   */
  inverseIrrred () {
    return new Fraction(this.denIrred, this.numIrred)
  }

  /**
   * Retourne la somme de la fraction courante et f2
   * @param {object} f2 La fraction qui s'ajoute
   * @return {Fraction} La somme des fractions
   */
  sommeFraction (f2) {
    return new Fraction(this.num * f2.den + f2.num * this.den, this.den * f2.den)
  }

  /**
   * @param  {Fraction[]} fractions Liste des fractions à ajouter à la fraction
   * @return {Fraction} La somme de toutes les fractions
   */
  sommeFractions (...fractions) {
    let s = new Fraction(this.num, this.den)
    for (const f of fractions) {
      s = s.sommeFraction(f)
    }
    return s
  }

  /**
   * @param {Fraction} f2  La fraction par laquelle est multipliée la fraction
   * @return {Fraction} Le produit des deux fractions
   */
  produitFraction (f2) {
    return new Fraction(this.num * f2.num, this.den * f2.den)
  }

  /**
   * @param {Fraction} f2 la fraction qui multiplie.
   * @return {string} Le calcul du produit de deux fractions avec étape intermédiaire
   */
  texProduitFraction (f2) {
    return `${this.texFraction}\\times ${f2.texFraction}=${texFraction(this.num + '\\times' + f2.num, this.den + '\\times' + f2.den)}=${texFraction(this.num * f2.num, this.den * f2.den)}`
  }

  /**
   * @param {number} n l'exposant de la fraction
   * @return {Fraction} La puissance n de la fraction
   */
  puissanceFraction (n) {
    return new Fraction(this.num ** n, this.den ** n)
  }

  /**
   * @param  {Fraction[]} fractions Les fractions qui multiplient la fraction
   * @return {Fraction} Le produit des fractions
   */
  produitFractions (...fractions) {
    let p = new Fraction(this.num, this.den)
    for (const f of fractions) {
      p = p.produitFraction(f)
    }
    return p
  }

  /**
   * @param {Fraction} f2 est la fracion qui est soustraite de la fraction
   * @return {Fraction} La différence des deux fractions
   */
  differenceFraction (f2) {
    return this.sommeFraction(f2.oppose())
  }

  /**
   *
   * @param {Fraction} f2
   * @return {Fraction}
   */
  diviseFraction (f2) {
    return this.produitFraction(f2.inverse())
  }

  /**
   *
   * @param {Fraction} f2
   * @return {string}
   */
  texQuotientFraction (f2) {
    return `${this.texFraction}\\div ${f2.texFraction}=${this.texFraction}\\times ${f2.inverse().texFraction}=${texFraction(this.num + '\\times' + f2.den, this.den + '\\times' + f2.num)}=${texFraction(this.num * f2.den, this.den * f2.num)}`
  }

  /**
   * Retourne une fraction avec comme dénominateur une puissance de 10 ou 'NaN' si la fraction n'a pas de valeur décimale
   * @return {Fraction}
   */
  fractionDecimale () {
    const den = this.denIrred
    const liste = obtenirListeFacteursPremiers(den)
    let n2 = 0; let n5 = 0
    for (const n of liste) {
      if (n === 2) { n2++ } else if (n === 5) { n5++ } else { return 'NaN' }
    }
    if (n5 === n2) { return new Fraction(this.numIrred, this.fractionDecimale.denIrred) } else if (n5 > n2) { return new Fraction(this.numIrred * 2 ** (n5 - n2), this.denIrred * 2 ** (n5 - n2)) } else { return new Fraction(this.numIrred * 5 ** (n2 - n5), this.denIrred * 5 ** (n2 - n5)) }
  }

  /**
   *
   * @param {number} n entier par lequel multiplier la fraction
   * @return {Fraction} fraction multipliée par n
   */
  multiplieEntier (n) {
    return new Fraction(n * this.num, this.den)
  }

  /**
   *
   * @param {number} n entier par lequel multiplier la fraction
   * @return {Fraction} fraction multipliée par n simplifiée
   */
  multiplieEntierIrred (n) {
    return new Fraction(n * this.num, this.den).simplifie()
  }

  /**
   * @param {number} n entier qui divise la fraction
   * @return {Fraction} fraction divisée par n
   */
  entierDivise (n) {
    return new Fraction(this.num, n * this.den)
  }

  /**
   * Retourne la fraction divisée par n et réduite si possible
   * @param {integer} n entier qui divise la fraction
   * @return {Fraction}
   */
  entierDiviseIrred (n) {
    return new Fraction(this.num, n * this.den).simplifie()
  }

  /**
   * @param {number} n entier divisé par la fraction
   * @return {Fraction} n divisé par fraction
   */
  diviseEntier (n) {
    return new Fraction(n * this.den, this.num)
  }

  /**
   *
   * @param {number} n
   * @return {Fraction}
   */
  diviseEntierIrred (n) {
    return new Fraction(n * this.den, this.num).simplifie()
  }

  /**
   * @param {number} n entier à ajouter à la fraction
   * @return {Fraction} la fraction augmentée de n
   */
  ajouteEntier (n) {
    return new Fraction(this.num + this.den * n, this.den)
  }

  /**
   * @param {number} n l'entier duqel on soustrait la fraction
   * @return {Fraction} n moins la fraction
   */
  entierMoinsFraction (n) {
    return new Fraction(n * this.den - this.num, this.den)
  }

  /**
   * fonctions de comparaison avec une autre fraction.
   * @param {Fraction} f2
   * @return {boolean}
   */
  superieurstrict (f2) {
    return (this.num / this.den) > (f2.num / f2.den)
  }

  /**
   * Retourne true si la fraction courante est supérieure ou égale à f2
   * @param {Fraction} f2
   * @return {boolean}
   */
  superieurlarge (f2) {
    return (this.num / this.den) >= (f2.num / f2.den)
  }

  /**
   * Retourne true si la fraction courante est strictement inférieure à f2
   * @param {Fraction} f2
   * @return {boolean}
   */
  inferieurstrict (f2) {
    return (this.num / this.den) < (f2.num / f2.den)
  }

  /**
   * Retourne true si la fraction courante est inférieure ou égale à f2
   * @param {Fraction} f2
   * @return {boolean}
   */
  inferieurlarge (f2) {
    return (this.num / this.den) < (f2.num / f2.den)
  }

  /**
   * Retourne true si la fraction est égale et "plus simple"
   * @param {Fraction} f2
   * @return {boolean}
   */
  estUneSimplification (f2) {
    return this.egal(f2) && (Math.abs(this.num) < Math.abs(f2.num))
  }

  /**
   * Retourne true si la fraction est irreductible
   * @return {boolean}
   */
  estIrreductible () {
    return (this.num === this.numIrred && this.den === this.denIrred)
  }

  /**
   * Retourne true si la fraction est un entier
   * @return {boolean}
   */
  estEntiere () {
    return (this.denIrred === 1)
  }

  /**
   * Retourne true si la racine carrée de la fraction est rationnelle
   * @return {boolean}
   */
  estParfaite () {
    return (this.racineCarree() !== false)
  }

  /**
   * Retourne true si les deux fractions sont égales (différence < 1e-10)
   * @param {Fraction} f2
   * @return {boolean}
   */
  egal (f2) {
    return Math.abs((this.num / this.den) - (f2.num / f2.den)) < 1e-10
  }

  /**
   *
   * @author Jean-Claude Lhote
   * @param x
   * @param y
   * @param rayon
   * @param {number} depart N° de la première part coloriée (0 correspond à la droite du centre)
   * @param {string} type 'gateau' ou 'segment' ou 'barre'
   * @param couleur
   * @param unite0
   * @param unite1
   * @param scale
   * @param label
   * @return {Object[]}
   */
  representationIrred (x, y, rayon, depart = 0, type = 'gateau', couleur = 'gray', unite0 = 0, unite1 = 1, scale = 1, label = '') {
    let num, k, dep, s, a, O, C
    const objets = []
    const n = quotientier(this.numIrred, this.denIrred)
    num = this.numIrred
    const unegraduation = function (x, y, couleur = 'black', epaisseur = 1) {
      const A = point(x, y + 0.2)
      const B = point(x, y - 0.2)
      const g = segment(A, B)
      g.color = couleur
      g.epaisseur = epaisseur
      return g
    }
    if (type === 'gateau') {
      for (k = 0; k < n; k++) {
        O = point(x + k * 2 * (rayon + 0.5), y)
        C = cercle(O, rayon)
        objets.push(C)
        for (let i = 0; i < this.denIrred; i++) {
          s = segment(O, rotation(point(x + rayon + k * 2 * (rayon + 0.5), y), O, 90 - i * 360 / this.denIrred))
          objets.push(s)
        }
        dep = rotation(point(x + rayon + k * 2 * (rayon + 0.5), y), O, 90 - depart * 360 / this.denIrred)
        for (let j = 0; j < Math.min(this.denIrred, num); j++) {
          a = arc(dep, O, -360 / this.denIrred, true, couleur)
          a.opacite = 0.3
          dep = rotation(dep, O, -360 / this.denIrred)
          objets.push(a)
        }
        num -= this.denIrred
      }
      if (this.num % this.den !== 0) {
        O = point(x + k * 2 * (rayon + 0.5), y)
        C = cercle(O, rayon)
        objets.push(C)
        for (let i = 0; i < this.denIrred; i++) {
          s = segment(O, rotation(point(x + rayon + k * 2 * (rayon + 0.5), y), O, 90 - i * 360 / this.denIrred))
          objets.push(s)
        }
        dep = rotation(point(x + rayon + k * 2 * (rayon + 0.5), y), O, 90 - depart * 360 / this.denIrred)
        for (let j = 0; j < Math.min(this.denIrred, num); j++) {
          a = arc(dep, O, -360 / this.denIrred, true, couleur)
          a.opacite = 0.3
          dep = rotation(dep, O, -360 / this.denIrred)
          objets.push(a)
        }
      }
    } else if (type === 'segment') {
      for (k = 0; k < n; k++) {
        O = point(x + k * rayon, y)
        C = translation(O, vecteur(rayon, 0))
        s = segment(O, C)
        s.styleExtremites = '-|'
        objets.push(s)
        for (let i = 0; i < this.denIrred; i++) {
          s = segment(translation(O, vecteur(i * rayon / this.denIrred, 0)), translation(O, vecteur((i + 1) * rayon / this.denIrred, 0)))
          s.styleExtremites = '|-'
          objets.push(s)
        }
        a = segment(O, point(O.x + Math.min(num, this.denIrred) * rayon / this.denIrred, O.y))
        a.color = couleur
        a.opacite = 0.4
        a.epaisseur = 6
        objets.push(a)
        num -= this.denIrred
      }
      O = point(x + k * rayon, y)
      C = translation(O, vecteur(rayon, 0))
      s = segment(O, C)
      s.styleExtremites = '-|'
      objets.push(s)
      for (let i = 0; i < this.denIrred; i++) {
        s = segment(translation(O, vecteur(i * rayon / this.denIrred, 0)), translation(O, vecteur((i + 1) * rayon / this.denIrred, 0)))
        s.styleExtremites = '|-'
        objets.push(s)
      }
      a = segment(O, point(O.x + Math.min(this.numIrred, this.denIrred) * rayon / this.denIrred, O.y))
      a.color = couleur
      a.opacite = 0.4
      a.epaisseur = 6
      objets.push(a)
      objets.push(unegraduation(x, y))
      if (typeof (unite0) === 'number' && typeof (unite1) === 'number') {
        for (k = 0; k <= n + 1; k++) {
          objets.push(texteParPosition(unite0 + k * (unite1 - unite0), x + rayon * k, y - 0.6, 'milieu', 'black', scale))
        }
      } else {
        if (unite0 !== '') { objets.push(texteParPosition(unite0, x, y - 0.6, 'milieu', 'black', scale)) }
        if (unite1 !== '') { objets.push(texteParPosition(unite1, x + rayon, y - 0.6, 'milieu', 'black', scale)) }
        if (label !== '') { objets.push(texteParPosition(label, x + rayon * this.numIrred / this.denIrred, y - 0.6, 'milieu', 'black', scale)) }
      }
    } else {
      let diviseur
      if (this.denIrred % 6 === 0) { diviseur = 6 } else if (this.denIrred % 5 === 0) { diviseur = 5 } else if (this.denIrred % 4 === 0) { diviseur = 4 } else if (this.denIrred % 3 === 0) { diviseur = 3 } else if (this.denIrred % 2 === 0) { diviseur = 2 } else { diviseur = 1 }

      for (k = 0; k < n; k++) {
        for (let j = 0; j < diviseur; j++) {
          for (let h = 0; h < calcul(this.denIrred / diviseur); h++) {
            O = point(x + k * (rayon + 1) + j * rayon / diviseur, y + h * rayon / diviseur)
            C = translation(O, vecteur(rayon / diviseur, 0))
            dep = carre(O, C)
            dep.color = 'black'
            dep.couleurDeRemplissage = couleur
            dep.opaciteDeRemplissage = 0.4
            objets.push(dep)
          }
        }
        num -= this.den
      }
      if (num > 0) {
        for (let j = 0; j < diviseur; j++) {
          for (let h = 0; h < calcul(this.denIrred / diviseur); h++) {
            O = point(x + k * (rayon + 1) + j * rayon / diviseur, y + h * rayon / diviseur)
            C = translation(O, vecteur(rayon / diviseur, 0))
            dep = carre(O, C)
            dep.color = 'black'
            objets.push(dep)
          }
        }
        for (let i = 0; i < num; i++) {
          O = point(x + k * (rayon + 1) + (i % diviseur) * rayon / diviseur, y + quotientier(i, diviseur) * rayon / diviseur)
          C = translation(O, vecteur(rayon / diviseur, 0))
          dep = carre(O, C)
          dep.color = 'black'
          dep.couleurDeRemplissage = couleur
          dep.opaciteDeRemplissage = 0.4
          objets.push(dep)
        }
      }
    }
    return objets
  }

  /**
   * Représente une fraction sous forme de disque (gateau), de segment ou de rectangle
   * @param x
   * @param y
   * @param rayon
   * @param depart sert pour la représentation disque à fixer l'azimut du premier secteur : 0 correspond à 12h.
   * @param {string} type gateau|segment|barre
   * @param couleur
   * @param unite0 sert pour la représentation 'segment'. On peut ainsi choisir les délimiteurs de l'unité, ce sont habituellement 0 et 1, à ce moment la, chaque entier est affiché sous sa graduation. Si ce sont des variable de type string, il n'y a que ces deux étiquettes qui sont écrites.
   * @param unite1 idem
   * @param scale
   * @param label
   * @return {Object[]}
   */
  representation (x, y, rayon, depart = 0, type = 'gateau', couleur = 'gray', unite0 = 0, unite1 = 1, scale = 1, label = '') {
    const objets = []
    let num, k, dep, s, a, O, C
    const n = quotientier(this.num, this.den)
    num = this.num
    const unegraduation = function (x, y, couleur = 'black', epaisseur = 1) {
      const A = point(x, y + 0.2)
      const B = point(x, y - 0.2)
      const g = segment(A, B)
      g.color = couleur
      g.epaisseur = epaisseur
      return g
    }
    if (type === 'gateau') {
      for (k = 0; k < n; k++) {
        const O = point(x + k * 2 * (rayon + 0.5), y)
        const C = cercle(O, rayon)
        objets.push(C)
        let s, a
        for (let i = 0; i < this.den; i++) {
          s = segment(O, rotation(point(x + rayon + k * 2 * (rayon + 0.5), y), O, 90 - i * 360 / this.den))
          objets.push(s)
        }
        dep = rotation(point(x + rayon + k * 2 * (rayon + 0.5), y), O, 90 - depart * 360 / this.den)
        for (let j = 0; j < Math.min(this.den, num); j++) {
          a = arc(dep, O, -360 / this.den, true, couleur)
          a.opacite = 0.3
          dep = rotation(dep, O, -360 / this.den)
          objets.push(a)
        }
        num -= this.den
      }
      if (this.num % this.den !== 0) {
        const O = point(x + k * 2 * (rayon + 0.5), y)
        const C = cercle(O, rayon)
        objets.push(C)
        for (let i = 0; i < this.den; i++) {
          s = segment(O, rotation(point(x + rayon + k * 2 * (rayon + 0.5), y), O, 90 - i * 360 / this.den))
          objets.push(s)
        }

        dep = rotation(point(x + rayon + k * 2 * (rayon + 0.5), y), O, 90 - depart * 360 / this.den)
        if (this.num % this.den !== 0) {
          for (let j = 0; j < Math.min(this.den, num); j++) {
            a = arc(dep, O, -360 / this.den, true, couleur)
            a.opacite = 0.3
            dep = rotation(dep, O, -360 / this.den)
            objets.push(a)
          }
        }
      }
    } else if (type === 'segment') {
      for (k = 0; k < n; k++) {
        O = point(x + k * rayon, y)
        C = translation(O, vecteur(rayon, 0))
        s = segment(O, C)
        s.styleExtremites = '-|'
        objets.push(s)
        for (let i = 0; i < this.den; i++) {
          s = segment(translation(O, vecteur(i * rayon / this.den, 0)), translation(O, vecteur((i + 1) * rayon / this.den, 0)))
          s.styleExtremites = '|-'
          objets.push(s)
        }
        a = segment(O, point(O.x + Math.min(num, this.den) * rayon / this.den, O.y))
        a.color = couleur
        a.opacite = 0.4
        a.epaisseur = 6
        objets.push(a)
        num -= this.den
      }
      O = point(x + k * rayon, y)
      C = translation(O, vecteur(rayon, 0))
      s = segment(O, C)
      s.styleExtremites = '-|'
      objets.push(s)
      for (let i = 0; i < this.den; i++) {
        s = segment(translation(O, vecteur(i * rayon / this.den, 0)), translation(O, vecteur((i + 1) * rayon / this.den, 0)))
        s.styleExtremites = '|-'
        objets.push(s)
      }
      a = segment(O, point(O.x + Math.min(num, this.den) * rayon / this.den, O.y))
      a.color = couleur
      a.opacite = 0.4
      a.epaisseur = 6
      objets.push(a)
      objets.push(unegraduation(x, y))
      if (typeof (unite0) === 'number' && typeof (unite1) === 'number') {
        for (k = 0; k <= n + 1; k++) {
          objets.push(texteParPosition(unite0 + k * (unite1 - unite0), x + rayon * k, y - 0.6, 'milieu', 'black', scale))
        }
      } else {
        if (unite0 !== '') { objets.push(texteParPosition(unite0, x, y - 0.6, 'milieu', 'black', scale)) }
        if (unite1 !== '') { objets.push(texteParPosition(unite1, x + rayon, y - 0.6, 'milieu', 'black', scale)) }
        if (label !== '') { objets.push(texteParPosition(label, x + rayon * this.num / this.den, y - 0.6, 'milieu', 'black', scale)) }
      }
    } else { // Type barre
      let diviseur
      if (this.den % 6 === 0) { diviseur = 6 } else if (this.den % 5 === 0) { diviseur = 5 } else if (this.den % 4 === 0) { diviseur = 4 } else if (this.den % 3 === 0) { diviseur = 3 } else if (this.den % 2 === 0) { diviseur = 2 } else { diviseur = 1 }

      for (k = 0; k < n; k++) {
        for (let j = 0; j < diviseur; j++) {
          for (let h = 0; h < calcul(this.den / diviseur); h++) {
            O = point(x + k * (rayon + 1) + j * rayon / diviseur, y + h * rayon / diviseur)
            C = translation(O, vecteur(rayon / diviseur, 0))
            dep = carre(O, C)
            dep.color = 'black'
            dep.couleurDeRemplissage = couleur
            dep.opaciteDeRemplissage = 0.4
            objets.push(dep)
          }
        }
        num -= this.den
      }
      if (num > 0) {
        for (let j = 0; j < diviseur; j++) {
          for (let h = 0; h < calcul(this.den / diviseur); h++) {
            O = point(x + k * (rayon + 1) + j * rayon / diviseur, y + h * rayon / diviseur)
            C = translation(O, vecteur(rayon / diviseur, 0))
            dep = carre(O, C)
            dep.color = 'black'
            objets.push(dep)
          }
        }
        for (let i = 0; i < num; i++) {
          O = point(x + k * (rayon + 1) + (i % diviseur) * rayon / diviseur, y + quotientier(i, diviseur) * rayon / diviseur)
          C = translation(O, vecteur(rayon / diviseur, 0))
          dep = carre(O, C)
          dep.color = 'black'
          dep.couleurDeRemplissage = couleur
          dep.opaciteDeRemplissage = 0.4
          objets.push(dep)
        }
      }
    }
    return objets
  }
}

export default Fraction