Déplacement des Variations

pull/603/head
Mael 2019-07-22 12:36:50 +02:00
parent 6b2d4b8dd3
commit ab0b64da12
6 changed files with 122 additions and 177 deletions

View File

@ -1,9 +1,7 @@
import { desugarScale } from 'Engine/mecanisms/barème'
import { decompose, devariateExplanation } from 'Engine/mecanisms/utils'
import { decompose } from 'Engine/mecanisms/utils'
import {
add,
any,
aperture,
curry,
equals,
evolve,
@ -12,9 +10,7 @@ import {
head,
is,
isEmpty,
isNil,
keys,
last,
map,
max,
mergeWith,
@ -23,11 +19,7 @@ import {
pipe,
pluck,
prop,
propEq,
reduce,
reduced,
reject,
sort,
subtract,
toPairs
} from 'ramda'
@ -48,100 +40,15 @@ import {
rewriteNode
} from './evaluation'
import Allègement from './mecanismViews/Allègement'
import Barème from './mecanismViews/Barème'
import BarèmeContinu from './mecanismViews/BarèmeContinu'
import { Node, SimpleRuleLink } from './mecanismViews/common'
import InversionNumérique from './mecanismViews/InversionNumérique'
import Product from './mecanismViews/Product'
import Somme from './mecanismViews/Somme'
import Variations from './mecanismViews/Variations'
import { disambiguateRuleReference, findRuleByDottedName } from './rules'
import { anyNull, val } from './traverse-common-functions'
import uniroot from './uniroot'
import { inferUnit } from 'Engine/units'
/* @devariate = true => This function will produce variations of a same mecanism (e.g. product) that share some common properties */
export let mecanismVariations = (recurse, k, v, devariate) => {
let explanation = devariate
? devariateExplanation(recurse, k, v)
: v.map(({ si, alors, sinon }) =>
sinon !== undefined
? { consequence: recurse(sinon), condition: undefined }
: { consequence: recurse(alors), condition: recurse(si) }
)
let evaluate = (cache, situationGate, parsedRules, node) => {
let evaluateVariationProp = prop =>
prop === undefined
? undefined
: evaluateNode(cache, situationGate, parsedRules, prop),
// mark the satisfied variation if any in the explanation
[, resolvedExplanation] = reduce(
([resolved, result], variation) => {
if (resolved) return [true, [...result, variation]]
// evaluate the condition
let evaluatedCondition = evaluateVariationProp(variation.condition)
if (evaluatedCondition == undefined) {
// We've reached the eventual defaut case
let evaluatedVariation = {
consequence: evaluateVariationProp(variation.consequence),
satisfied: true
}
return [true, [...result, evaluatedVariation]]
}
if (evaluatedCondition.nodeValue === null)
// one case has missing variables => we can't go further
return [true, [...result, { condition: evaluatedCondition }]]
if (evaluatedCondition.nodeValue === true) {
let evaluatedVariation = {
condition: evaluatedCondition,
consequence: evaluateVariationProp(variation.consequence),
satisfied: true
}
return [true, [...result, evaluatedVariation]]
}
return [false, [...result, variation]]
},
[false, []]
)(node.explanation),
satisfiedVariation = resolvedExplanation.find(v => v.satisfied),
nodeValue = satisfiedVariation
? satisfiedVariation.consequence.nodeValue
: null
let leftMissing = mergeAllMissing(
reject(isNil, pluck('condition', resolvedExplanation))
),
candidateVariations = filter(
node => !node.condition || node.condition.nodeValue !== false,
resolvedExplanation
),
rightMissing = mergeAllMissing(
reject(isNil, pluck('consequence', candidateVariations))
),
missingVariables = satisfiedVariation
? collectNodeMissing(satisfiedVariation.consequence)
: mergeMissing(bonus(leftMissing), rightMissing)
return rewriteNode(node, nodeValue, resolvedExplanation, missingVariables)
}
// TODO - find an appropriate representation
return {
explanation,
evaluate,
jsx: Variations,
category: 'mecanism',
name: 'variations',
type: 'numeric',
unit: inferUnit('+', explanation.map(r => r.consequence.unit))
}
}
import variations from 'Engine/mecanisms/variations'
export let mecanismOneOf = (recurse, k, v) => {
if (!is(Array, v)) throw new Error('should be array')
@ -576,7 +483,7 @@ export let mecanismProduct = (recurse, k, v) => {
return decompose(recurse, k, v)
}
if (v.variations) {
return mecanismVariations(recurse, k, v, true)
return variations(recurse, k, v, true)
}
let objectShape = {
@ -621,62 +528,6 @@ export let mecanismProduct = (recurse, k, v) => {
}
}
export let mecanismContinuousScale = (recurse, k, v) => {
let objectShape = {
assiette: false,
multiplicateur: defaultNode(1)
}
let returnRate = v['retourne seulement le taux'] === 'oui'
let effect = ({ assiette, multiplicateur, points }) => {
if (anyNull([assiette, multiplicateur])) return null
//We'll build a linear function given the two constraints that must be respected
let result = pipe(
toPairs,
// we don't rely on the sorting of objects
sort(([k1], [k2]) => k1 - k2),
points => [...points, [Infinity, last(points)[1]]],
aperture(2),
reduce((_, [[lowerLimit, lowerRate], [upperLimit, upperRate]]) => {
let x1 = val(multiplicateur) * lowerLimit,
x2 = val(multiplicateur) * upperLimit,
y1 = val(assiette) * val(recurse(lowerRate)),
y2 = val(assiette) * val(recurse(upperRate))
if (val(assiette) > x1 && val(assiette) <= x2) {
// Outside of these 2 limits, it's a linear function a * x + b
let a = (y2 - y1) / (x2 - x1),
b = y1 - x1 * a,
nodeValue = a * val(assiette) + b,
taux = nodeValue / val(assiette)
return reduced({
nodeValue: returnRate ? taux : nodeValue,
additionalExplanation: {
seuil: val(assiette) / val(multiplicateur),
taux
}
})
}
}, 0)
)(points)
return result
}
let explanation = {
...parseObject(recurse, objectShape, v),
points: v.points,
returnRate
},
evaluate = evaluateObject(objectShape, effect)
return {
evaluate,
jsx: BarèmeContinu,
explanation,
category: 'mecanism',
name: 'barème continu',
type: 'numeric'
}
}
export let mecanismMax = (recurse, k, v) => {
let explanation = v.map(recurse)

View File

@ -1,6 +1,6 @@
import { defaultNode, evaluateObject } from 'Engine/evaluation'
import Barème from 'Engine/mecanismViews/Barème'
import { mecanismVariations } from 'Engine/mecanisms'
import variations from 'Engine/mecanisms/variations'
import { decompose } from 'Engine/mecanisms/utils'
import { val } from 'Engine/traverse-common-functions'
import { inferUnit, parseUnit } from 'Engine/units'
@ -21,7 +21,7 @@ export default (recurse, k, v) => {
return decompose(recurse, k, v)
}
if (v.variations) {
return mecanismVariations(recurse, k, v, true)
return variations(recurse, k, v, true)
}
let tranches = desugarScale(recurse)(v['tranches']),
objectShape = {
@ -52,8 +52,6 @@ export default (recurse, k, v) => {
},
evaluate = evaluateObject(objectShape, effect)
console.log('explanation', explanation)
return {
evaluate,
jsx: Barème('linéaire'),

View File

@ -1,5 +1,5 @@
import { defaultNode, E, rewriteNode } from 'Engine/evaluation'
import { mecanismVariations } from 'Engine/mecanisms'
import variations from 'Engine/mecanisms/variations'
import { decompose } from 'Engine/mecanisms/utils'
import Barème from 'Engine/mecanismViews/Barème'
import { val } from 'Engine/traverse-common-functions'
@ -39,7 +39,7 @@ export default (recurse, k, v) => {
return decompose(recurse, k, v)
}
if (v.variations) {
return mecanismVariations(recurse, k, v, true)
return variations(recurse, k, v, true)
}
let { assiette, multiplicateur } = v,

View File

@ -34,17 +34,3 @@ export let decompose = (recurse, k, v) => {
}
}
export let devariateExplanation = (recurse, mecanismKey, v) => {
let fixedProps = dissoc('variations')(v),
explanation = v.variations.map(({ si, alors, sinon }) => ({
consequence: recurse({
[mecanismKey]: {
...fixedProps,
...(sinon || alors)
}
}),
condition: sinon ? undefined : recurse(si)
}))
return explanation
}

View File

@ -0,0 +1,109 @@
import { inferUnit } from 'Engine/units'
import {
bonus,
collectNodeMissing,
evaluateNode,
mergeAllMissing,
mergeMissing,
rewriteNode
} from 'Engine/evaluation'
import { reject, pluck, isNil, filter, dissoc, reduce } from 'ramda'
import Variations from 'Engine/mecanismViews/Variations'
/* @devariate = true => This function will produce variations of a same mecanism (e.g. product) that share some common properties */
export default (recurse, k, v, devariate) => {
let explanation = devariate
? devariateExplanation(recurse, k, v)
: v.map(({ si, alors, sinon }) =>
sinon !== undefined
? { consequence: recurse(sinon), condition: undefined }
: { consequence: recurse(alors), condition: recurse(si) }
)
let evaluate = (cache, situationGate, parsedRules, node) => {
let evaluateVariationProp = prop =>
prop === undefined
? undefined
: evaluateNode(cache, situationGate, parsedRules, prop),
// mark the satisfied variation if any in the explanation
[, resolvedExplanation] = reduce(
([resolved, result], variation) => {
if (resolved) return [true, [...result, variation]]
// evaluate the condition
let evaluatedCondition = evaluateVariationProp(variation.condition)
if (evaluatedCondition == undefined) {
// We've reached the eventual defaut case
let evaluatedVariation = {
consequence: evaluateVariationProp(variation.consequence),
satisfied: true
}
return [true, [...result, evaluatedVariation]]
}
if (evaluatedCondition.nodeValue === null)
// one case has missing variables => we can't go further
return [true, [...result, { condition: evaluatedCondition }]]
if (evaluatedCondition.nodeValue === true) {
let evaluatedVariation = {
condition: evaluatedCondition,
consequence: evaluateVariationProp(variation.consequence),
satisfied: true
}
return [true, [...result, evaluatedVariation]]
}
return [false, [...result, variation]]
},
[false, []]
)(node.explanation),
satisfiedVariation = resolvedExplanation.find(v => v.satisfied),
nodeValue = satisfiedVariation
? satisfiedVariation.consequence.nodeValue
: null
let leftMissing = mergeAllMissing(
reject(isNil, pluck('condition', resolvedExplanation))
),
candidateVariations = filter(
node => !node.condition || node.condition.nodeValue !== false,
resolvedExplanation
),
rightMissing = mergeAllMissing(
reject(isNil, pluck('consequence', candidateVariations))
),
missingVariables = satisfiedVariation
? collectNodeMissing(satisfiedVariation.consequence)
: mergeMissing(bonus(leftMissing), rightMissing)
return rewriteNode(node, nodeValue, resolvedExplanation, missingVariables)
}
// TODO - find an appropriate representation
return {
explanation,
evaluate,
jsx: Variations,
category: 'mecanism',
name: 'variations',
type: 'numeric',
unit: inferUnit('+', explanation.map(r => r.consequence.unit))
}
}
export let devariateExplanation = (recurse, mecanismKey, v) => {
let fixedProps = dissoc('variations')(v),
explanation = v.variations.map(({ si, alors, sinon }) => ({
consequence: recurse({
[mecanismKey]: {
...fixedProps,
...(sinon || alors)
}
}),
condition: sinon ? undefined : recurse(si)
}))
return explanation
}

View File

@ -2,10 +2,12 @@
// In a specific file
// TODO import them automatically
// TODO convert the legacy functions to new files
import barème from 'Engine/mecanisms/barème.js'
import barèmeContinu from 'Engine/mecanisms/barème-continu.js'
import barèmeLinéaire from 'Engine/mecanisms/barème-linéaire.js'
import barème from 'Engine/mecanisms/barème'
import barèmeContinu from 'Engine/mecanisms/barème-continu'
import barèmeLinéaire from 'Engine/mecanisms/barème-linéaire'
import variations from 'Engine/mecanisms/variations'
import { Parser } from 'nearley'
import {
add,
curry,
@ -42,7 +44,6 @@ import {
mecanismReduction,
mecanismSum,
mecanismSynchronisation,
mecanismVariations,
mecanismOnePossibility
} from './mecanisms'
import { Node } from './mecanismViews/common'
@ -145,7 +146,7 @@ export let parseObject = (rules, rule, parsedRules) => rawNode => {
'une possibilité': mecanismOnePossibility(rule.dottedName),
'inversion numérique': mecanismInversion(rule.dottedName),
allègement: mecanismReduction,
variations: mecanismVariations,
variations,
synchronisation: mecanismSynchronisation,
...operationDispatch,
filter: () =>