// entrainement-imm1003.js — générateurs de problèmes à données aléatoires (IMM1003) // Chaque générateur : { id, title, ch, url, gen(R) → { enonce, answer, unit, tol, steps } } // R : utilitaires aléatoires fournis par app.js (R.int(a,b), R.pick(arr), R.step(a,b,step), R.money(v), R.pct(v), R.num(v,d)) window.UQO_TRAINING = [ { id: "rpv", title: "RPV et mise de fonds", ch: "01", url: "/seance/01/#form-1-1", gen(R) { const prix = R.step(320000, 780000, 5000); const evalv = prix - R.step(0, 45000, 5000); const retenue = Math.min(prix, evalv); const pret = retenue * 0.8; const mise = prix - pret; return { enonce: `Une offre d’achat est acceptée à ${R.money(prix)}. L’évaluation exigée par le prêteur conclut à ${R.money(evalv)}. L’acheteur souhaite un prêt conventionnel (RPV maximal de 80 %). Quelle mise de fonds devra-t-il verser ?`, answer: mise, unit: "$", tol: 0.005, steps: `

1. Valeur retenue = la moindre du prix et de l’évaluation = ${R.money(retenue)}.

2. Prêt maximal = ${R.money(retenue)} × 80 % = ${R.money(pret)}.

3. Mise de fonds = prix − prêt = ${R.money(prix)} − ${R.money(pret)} = ${R.money(mise)} (soit ${R.pct(mise / prix * 100, 1)} du prix).

`, }; }, }, { id: "mutation", title: "Droits de mutation (barème 2026)", ch: "01", url: "/seance/01/#s-1-6-3", gen(R) { const prix = R.step(180000, 900000, 1000); const t1 = Math.min(prix, 62900) * 0.005; const t2 = Math.max(0, Math.min(prix, 315000) - 62900) * 0.01; const t3 = Math.max(0, prix - 315000) * 0.015; const tot = t1 + t2 + t3; return { enonce: `Calculez les droits de mutation exigibles pour une propriété acquise ${R.money(prix)} (barème de base 2026 : 0,5 % jusqu’à 62 900 $ ; 1,0 % de 62 900 $ à 315 000 $ ; 1,5 % au-delà ; aucun taux municipal majoré).`, answer: tot, unit: "$", tol: 0.005, steps: `

Tranche 1 : 62 900 × 0,5 % = ${R.money2(t1)}.

Tranche 2 : ${prix > 315000 ? "(315 000 − 62 900)" : "(" + R.money(prix) + " − 62 900)"} × 1,0 % = ${R.money2(t2)}.

${prix > 315000 ? `

Tranche 3 : (${R.money(prix)} − 315 000) × 1,5 % = ${R.money2(t3)}.

` : "

Pas de tranche 3 (prix ≤ 315 000 $).

"}

Total : ${R.money2(tot)}.

`, }; }, }, { id: "temporel", title: "Ajustement temporel d’un comparable", ch: "08", url: "/seance/08/#s-8-4", gen(R) { const prix = R.step(300000, 700000, 1000); const g = R.pick([3, 4, 5, 6, 8]); const mois = R.int(2, 14); const adj = prix * g / 100 / 12 * mois; return { enonce: `Un comparable s’est vendu ${R.money(prix)} il y a ${mois} mois. Le marché progresse de ${g} % par an (ajustement linéaire). Quel est le prix ajusté pour le temps ?`, answer: prix + adj, unit: "$", tol: 0.003, steps: `

Taux mensuel = ${g} % ÷ 12 = ${R.num(g / 12, 4)} %.

Ajustement = ${R.money(prix)} × ${R.num(g / 12, 4)} % × ${mois} = ${R.money(adj)} (positif : le marché a monté depuis la vente).

Prix ajusté = ${R.money(prix)} + ${R.money(adj)} = ${R.money(prix + adj)}.

`, }; }, }, { id: "netbrut", title: "Ajustements net et brut", ch: "09", url: "/seance/09/#s-9-4", gen(R) { const prix = R.step(350000, 650000, 5000); const a = [R.step(-30000, 30000, 1000), R.step(-25000, 25000, 1000), R.step(-20000, 20000, 1000), R.step(-15000, 15000, 1000)].map((x) => (x === 0 ? 5000 : x)); const net = a.reduce((s, x) => s + x, 0); const brut = a.reduce((s, x) => s + Math.abs(x), 0); return { enonce: `Un comparable vendu ${R.money(prix)} reçoit quatre ajustements : ${a.map((x) => `${x > 0 ? "+" : "−"}${R.money(Math.abs(x))}`).join(", ")}. Quel est l’ajustement brut, en pourcentage du prix de vente ? (Répondez en %, ex. 12,5)`, answer: brut / prix * 100, unit: "%", tol: 0.01, steps: `

Net (somme algébrique) = ${R.money(net)} → ${R.pct(net / prix * 100)} ${Math.abs(net / prix) <= 0.15 ? "(≤ 15 %, acceptable)" : "(> 15 % : fiabilité réduite)"}.

Brut (somme des valeurs absolues) = ${R.money(brut)} → ${R.pct(brut / prix * 100)} ${brut / prix <= 0.25 ? "(≤ 25 %, acceptable)" : "(> 25 % : comparable peu fiable)"}.

`, }; }, }, { id: "capitalisation", title: "Capitalisation directe", ch: "11", url: "/seance/11/#s-11-1", gen(R) { const rne = R.step(38000, 260000, 500); const tga = R.pick([4.75, 5, 5.25, 5.5, 5.75, 6, 6.25, 6.5, 7]); return { enonce: `Le revenu net d’exploitation stabilisé d’un immeuble locatif est de ${R.money(rne)} par année. Le taux global d’actualisation extrait du marché est de ${R.num(tga, 2)} %. Quelle est l’indication de valeur par capitalisation directe ?`, answer: rne / (tga / 100), unit: "$", tol: 0.003, steps: `

V = RNE ÷ TGA = ${R.money(rne)} ÷ ${R.num(tga, 2)} % = ${R.money(rne / (tga / 100))}.

Vérification : ${R.money(rne / (tga / 100))} × ${R.num(tga, 2)} % ≈ ${R.money(rne)}.

`, }; }, }, { id: "rne", title: "Du RBP au RNE", ch: "11", url: "/seance/11/#s-11-2", gen(R) { const n = R.pick([4, 6, 8, 12]); const loyer = R.step(850, 1650, 25); const inocc = R.pick([3, 4, 5]); const autres = R.step(0, 6000, 500); const ratio = R.pick([36, 38, 40, 42, 44]); const rbp = n * loyer * 12; const rbe = rbp * (1 - inocc / 100) + autres; const dep = rbe * ratio / 100; const rne = rbe - dep; return { enonce: `Un immeuble de ${n} logements loués ${R.money(loyer)} par mois (loyer du marché). Inoccupation et mauvaises créances : ${inocc} % du RBP ; revenus accessoires : ${R.money(autres)} par an ; dépenses d’exploitation : ${ratio} % du RBE. Quel est le RNE ?`, answer: rne, unit: "$", tol: 0.003, steps: `

RBP = ${n} × ${R.money(loyer)} × 12 = ${R.money(rbp)}.

RBE = ${R.money(rbp)} × (1 − ${inocc} %) + ${R.money(autres)} = ${R.money(rbe)}.

Dépenses = ${ratio} % × ${R.money(rbe)} = ${R.money(dep)}.

RNE = ${R.money(rbe)} − ${R.money(dep)} = ${R.money(rne)}.

`, }; }, }, { id: "tga-extraction", title: "Extraction du TGA", ch: "12", url: "/seance/12/#s-12-2", gen(R) { const prix = R.step(600000, 3200000, 10000); const tga = R.pick([4.8, 5.1, 5.4, 5.6, 5.9, 6.2, 6.5]); const rne = Math.round(prix * tga / 100 / 10) * 10; return { enonce: `Un immeuble comparable s’est vendu ${R.money(prix)} ; son RNE reconstruit selon les conventions du sujet est de ${R.money(rne)}. Quel TGA en extrayez-vous ? (en %, deux décimales)`, answer: rne / prix * 100, unit: "%", tol: 0.005, steps: `

TGA = RNE ÷ prix = ${R.money(rne)} ÷ ${R.money(prix)} = ${R.pct(rne / prix * 100)}.

MRN implicite = 1 ÷ TGA ≈ ${R.num(prix / rne, 2)}.

`, }; }, }, { id: "bande", title: "TGA par bande d’investissement", ch: "12", url: "/seance/12/#s-12-2", gen(R) { const m = R.pick([65, 70, 75, 80]); const i = R.pick([4.5, 5, 5.5, 6]); const re = R.pick([7, 8, 9, 10]); const im = i / 100 / 12; const rm = 12 * im / (1 - Math.pow(1 + im, -300)) * 100; const tga = m / 100 * rm + (1 - m / 100) * re; return { enonce: `Financement type du marché : ratio prêt/valeur de ${m} %, taux hypothécaire de ${R.num(i, 2)} % amorti sur 25 ans (constante hypothécaire annuelle Rm = ${R.pct(rm)}), rendement exigé sur l’équité de ${re} %. Quel est le TGA par bande d’investissement ? (en %)`, answer: tga, unit: "%", tol: 0.01, steps: `

TGA = M × Rm + (1 − M) × Re = ${m} % × ${R.pct(rm)} + ${100 - m} % × ${re} %

= ${R.pct(m / 100 * rm)} + ${R.pct((1 - m / 100) * re)} = ${R.pct(tga)}.

`, }; }, }, { id: "mrb", title: "Multiplicateur de revenu brut", ch: "12", url: "/seance/12/#s-12-5", gen(R) { const rbe = R.step(60000, 420000, 1000); const mrb = R.pick([8.5, 9, 9.5, 10, 10.5, 11]); return { enonce: `Les ventes récentes d’immeubles semblables affichent un MRB de ${R.num(mrb, 1)}. Le sujet a un RBE de ${R.money(rbe)}. Quelle indication de valeur le multiplicateur donne-t-il ?`, answer: rbe * mrb, unit: "$", tol: 0.003, steps: `

V = RBE × MRB = ${R.money(rbe)} × ${R.num(mrb, 1)} = ${R.money(rbe * mrb)}.

Rappel : le MRB est un filtre rapide, aveugle aux dépenses ; il ne remplace pas la capitalisation du RNE.

`, }; }, }, { id: "reversion", title: "Valeur de réversion (DCF)", ch: "12", url: "/seance/12/#s-12-3", gen(R) { const rne = R.step(90000, 400000, 1000); const tgaout = R.pick([5.5, 5.75, 6, 6.25, 6.5]); const frais = R.pick([2, 2.5, 3, 4]); const brute = rne / (tgaout / 100); return { enonce: `À la fin d’une détention de 5 ans, le RNE de l’année 6 est projeté à ${R.money(rne)}. TGA de sortie : ${R.num(tgaout, 2)} % ; frais de vente : ${R.num(frais, 1)} % de la valeur brute. Quelle est la valeur de réversion nette ?`, answer: brute * (1 - frais / 100), unit: "$", tol: 0.003, steps: `

Valeur brute = RNE6 ÷ TGAsortie = ${R.money(rne)} ÷ ${R.num(tgaout, 2)} % = ${R.money(brute)}.

Frais de vente = ${R.num(frais, 1)} % × ${R.money(brute)} = ${R.money(brute * frais / 100)}.

Réversion nette = ${R.money(brute * (1 - frais / 100))}.

`, }; }, }, { id: "va", title: "Valeur présente d’un flux", ch: "12", url: "/seance/12/#s-12-3", gen(R) { const fv = R.step(100000, 2500000, 5000); const r = R.pick([6, 6.5, 7, 7.5, 8, 9]); const n = R.int(3, 10); const pv = fv / Math.pow(1 + r / 100, n); return { enonce: `Quelle est la valeur présente de ${R.money(fv)} reçus dans ${n} ans, actualisés à ${R.num(r, 1)} % ?`, answer: pv, unit: "$", tol: 0.003, steps: `

PV = FV ÷ (1 + r)n = ${R.money(fv)} ÷ (1,${String(r * 10).padStart(3, "0")})${n} = ${R.money(fv)} ÷ ${R.num(Math.pow(1 + r / 100, n), 4)} = ${R.money(pv)}.

`, }; }, }, { id: "cout-agevie", title: "Méthode du coût — âge/vie modifiée", ch: "10", url: "/seance/10/#s-10-3", gen(R) { const cneuf = R.step(380000, 900000, 5000); const cur = R.step(5000, 30000, 500); const age = R.int(8, 35); const vie = R.pick([50, 55, 60, 65]); const terrain = R.step(90000, 260000, 5000); const inc = (cneuf - cur) * age / vie; const val = cneuf - cur - inc + terrain; return { enonce: `Coût de remplacement à neuf : ${R.money(cneuf)} ; entretien différé (curable) : ${R.money(cur)} ; âge effectif : ${age} ans ; vie utile économique : ${vie} ans ; valeur du terrain : ${R.money(terrain)}. Aucune dépréciation fonctionnelle ni économique. Quelle est la valeur par la méthode du coût (âge/vie modifiée) ?`, answer: val, unit: "$", tol: 0.003, steps: `

Base résiduelle = ${R.money(cneuf)} − ${R.money(cur)} = ${R.money(cneuf - cur)}.

Physique incurable = ${R.money(cneuf - cur)} × ${age}/${vie} = ${R.money(inc)}.

Valeur dépréciée des améliorations = ${R.money(cneuf)} − ${R.money(cur)} − ${R.money(inc)} = ${R.money(cneuf - cur - inc)}.

V = ${R.money(cneuf - cur - inc)} + ${R.money(terrain)} = ${R.money(val)}.

`, }; }, }, { id: "reconciliation", title: "Réconciliation pondérée", ch: "14", url: "/seance/14/#s-14-6", gen(R) { const base = R.step(400000, 2000000, 10000); const vc = base, vk = base + R.step(-60000, 60000, 5000), vr = base + R.step(-50000, 50000, 5000); const w = R.pick([[50, 20, 30], [40, 15, 45], [60, 10, 30], [35, 25, 40], [45, 20, 35]]); const val = (vc * w[0] + vk * w[1] + vr * w[2]) / 100; return { enonce: `Indications : comparaison ${R.money(vc)} (poids ${w[0]} %), coût ${R.money(vk)} (poids ${w[1]} %), revenu ${R.money(vr)} (poids ${w[2]} %). Quelle est la valeur pondérée (avant arrondi) ?`, answer: val, unit: "$", tol: 0.002, steps: `

V = ${R.money(vc)} × ${w[0]} % + ${R.money(vk)} × ${w[1]} % + ${R.money(vr)} × ${w[2]} %

= ${R.money(vc * w[0] / 100)} + ${R.money(vk * w[1] / 100)} + ${R.money(vr * w[2] / 100)} = ${R.money(val)} (à arrondir au 5 000 $ : ${R.money(Math.round(val / 5000) * 5000)}).

`, }; }, }, ];