]> Git — Sourcephile - gargantext.git/blob - src/Gargantext/Viz/Phylo/Example.hs
[PHYLO] Code Review + Tools start.
[gargantext.git] / src / Gargantext / Viz / Phylo / Example.hs
1 {-|
2 Module : Gargantext.Viz.Phylo.Example
3 Description : Phylomemy example based on history of Cleopatre.
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
8 Portability : POSIX
9
10 -- | Cesar et Cleôpatre
11 -- Exemple de phylomemie
12 -- French without accents
13
14
15 TODO:
16 - split the functions : RAW -> Document -> Ngrams
17
18 -- reverse history: antechronologique
19 -- metrics support
20
21
22 -}
23
24 {-# LANGUAGE NoImplicitPrelude #-}
25 {-# LANGUAGE FlexibleContexts #-}
26 {-# LANGUAGE OverloadedStrings #-}
27
28 module Gargantext.Viz.Phylo.Example where
29
30 import Control.Lens hiding (both)
31 import Data.List (concat, union, intersect, tails, tail, head, last, null, zip, sort, length, any, (++), nub)
32 import qualified Data.List as List
33 import Data.Text (Text, unwords, toLower, words)
34 import Data.Tuple.Extra
35 import Data.Semigroup (Semigroup)
36
37 import Data.Map (Map, elems, member, adjust, singleton, (!), keys, restrictKeys, mapWithKey)
38 import qualified Data.Map as Map
39
40 import Data.Vector (Vector, fromList, elemIndex)
41 import qualified Data.Vector as Vector
42 import qualified Data.Maybe as Maybe
43
44 import Data.Tuple (fst, snd)
45 import qualified Data.Tuple as Tuple
46
47 import Data.Bool (Bool, not)
48 import qualified Data.Bool as Bool
49
50 import Data.Set (Set)
51 import qualified Data.Set as Set
52 import qualified Data.Matrix as DM'
53
54 import Gargantext.Text.Metrics.FrequentItemSet (fisWithSizePolyMap, Size(..))
55 import Gargantext.Text.Terms.Mono (monoTexts)
56 import Gargantext.Prelude hiding (head)
57 import Gargantext.Viz.Phylo
58 import Gargantext.Viz.Phylo.Tools
59
60 ------------------------------------------------------------------------
61 -- | Types | --
62
63
64 -- | Document : a piece of Text linked to a Date
65 data Document = Document
66 { date :: Date
67 , text :: Text
68 } deriving (Show)
69 -- | Corpus : a list of Documents
70 type Corpus = [Document]
71
72
73 type MapList = [Ngrams]
74 type PeriodeSize = Int
75 -- data Periodes b a = Map (b,b) a
76 type Occurrences = Int
77
78 data Levels = Level_m1 | Level_0 | Level_1 | Level_2 | Level_N
79 deriving (Show, Eq, Enum, Bounded)
80
81
82
83
84 data LinkLvlLabel = Link_m1_0 | Link_0_m1 | Link_1_0 | Link_0_1 | Link_x_y
85 deriving (Show, Eq, Enum, Bounded)
86
87 data LinkLvl = LinkLvl
88 { linkLvlLabel :: LinkLvlLabel
89 , linkLvlFrom :: Int
90 , linkLvlTo :: Int
91 } deriving (Show)
92
93
94 data PhyloError = LevelDoesNotExist
95 | LevelUnassigned
96 deriving (Show)
97
98
99 --------------------------------------------------------------------
100 phyloExampleFinal :: Phylo
101 phyloExampleFinal = undefined
102
103 --------------------------------------------------------------------
104 appariement :: Map (Date, Date) (Map (Set Ngrams) Int)
105 appariement = undefined
106
107 ------------------------------------------------------------------------
108 -- | STEP 10 | -- Incrementaly cluster the PhyloGroups n times, link them through the Periods and build level n of the Phylo
109
110
111 ------------------------------------------------------------------------
112 -- | STEP 8 | -- Cluster the Fis
113
114 ------------------------------------------------------------------------
115 -- | STEP 9 | -- Link the PhyloGroups of level 1 through the Periods
116
117 shouldPair :: PhyloGroup -> PhyloGroup -> Bool
118 shouldPair g g' = (not . null) $ intersect (getNgrams g) (getNgrams g')
119
120
121 getKeyPair :: (Int,Int) -> Map (Int,Int) a -> (Int,Int)
122 getKeyPair (x,y) m = case findPair (x,y) m of
123 Nothing -> panic "[ERR][Viz.Phylo.Example.getKeyPair] Nothing"
124 Just i -> i
125 where
126 --------------------------------------
127 findPair :: (Int,Int) -> Map (Int,Int) a -> Maybe (Int,Int)
128 findPair (x,y) m
129 | member (x,y) m = Just (x,y)
130 | member (y,x) m = Just (y,x)
131 | otherwise = Nothing
132 --------------------------------------
133
134 -- |
135 listToCombi :: forall a b. (a -> b) -> [a] -> [(b,b)]
136 listToCombi f l = [ (f x, f y) | (x:rest) <- tails l
137 , y <- rest
138 ]
139
140 fisToCooc :: Map (Date, Date) Fis -> Map (Int, Int) Double
141 fisToCooc m = map (/docs)
142 $ foldl (\mem x -> adjust (+1) (getKeyPair x mem) mem) cooc
143 $ concat
144 $ map (\x -> listToCombi findIdx $ (Set.toList . fst) x) fis
145 where
146 --------------------------------------
147 fis :: [(Clique,Support)]
148 fis = concat $ map (\x -> Map.toList x) (elems m)
149 --------------------------------------
150 fisNgrams :: [Ngrams]
151 fisNgrams = foldl (\mem x -> union mem $ (Set.toList . fst) x) [] fis
152 --------------------------------------
153 docs :: Double
154 docs = fromIntegral $ foldl (\mem x -> mem + (snd x)) 0 fis
155 --------------------------------------
156 cooc :: Map (Int, Int) (Double)
157 cooc = Map.fromList $ map (\x -> (x,0)) (listToCombi findIdx fisNgrams)
158 --------------------------------------
159
160
161
162 phyloWithAppariement1 :: Phylo
163 phyloWithAppariement1 = phyloLinked_0_1
164
165 ------------------------------------------------------------------------
166 -- | STEP 8 | -- Find the Fis out of Documents and Ngrams and build level 1 of the Phylo
167
168 phyloLinked_0_1 :: Phylo
169 phyloLinked_0_1 = phyloToLinks lvl_0_1 phyloLinked_1_0
170
171 lvl_0_1 :: LinkLvl
172 lvl_0_1 = (LinkLvl Link_0_1 0 1)
173
174 phyloLinked_1_0 :: Phylo
175 phyloLinked_1_0 = phyloToLinks lvl_1_0 phyloWithGroups1
176
177 lvl_1_0 :: LinkLvl
178 lvl_1_0 = (LinkLvl Link_1_0 1 0)
179
180 phyloWithGroups1 :: Phylo
181 phyloWithGroups1 = updatePhyloByLevel Level_1 phyloLinked_m1_0
182
183 cliqueToGroup :: PhyloPeriodId -> Int -> Int -> Ngrams -> (Clique,Support) -> PhyloGroup
184 cliqueToGroup period lvl idx label fis = PhyloGroup ((period, lvl), idx)
185 label
186 (sort $ map findIdx
187 $ Set.toList
188 $ fst fis
189 )
190 (singleton "support" (fromIntegral $ snd fis))
191 [] [] [] []
192
193 fisToPhyloLevel :: Map (Date, Date) Fis -> Phylo -> Phylo
194 fisToPhyloLevel m p = over (phylo_periods . traverse)
195 (\period ->
196 let periodId = _phylo_periodId period
197 fisList = zip [1..] (Map.toList (m ! periodId))
198 in over (phylo_periodLevels)
199 (\levels ->
200 let groups = map (\fis -> cliqueToGroup periodId 1 (fst fis) "" (snd fis)) fisList
201 in (PhyloLevel (periodId, 1) groups) : levels
202 ) period
203 ) p
204
205 -- | To preserve nonempty periods from filtering, please use : filterFisBySupport False ...
206 phyloFisFiltered :: Map (Date, Date) Fis
207 phyloFisFiltered = filterFisBySupport True 1 (filterFisByNested phyloFis)
208
209 filterFisBySupport :: Bool -> Int -> Map (Date, Date) Fis -> Map (Date, Date) Fis
210 filterFisBySupport empty min m = case empty of
211 True -> Map.map (\fis -> filterMinorFis min fis) m
212 False -> Map.map (\fis -> filterMinorFisNonEmpty min fis) m
213
214 filterMinorFis :: Int -> Fis -> Fis
215 filterMinorFis min fis = Map.filter (\s -> s > min) fis
216
217 filterMinorFisNonEmpty :: Int -> Fis -> Fis
218 filterMinorFisNonEmpty min fis = if (Map.null fis') && (not $ Map.null fis)
219 then filterMinorFisNonEmpty (min - 1) fis
220 else fis'
221 where
222 fis' = filterMinorFis min fis
223
224 doesContains :: [Ngrams] -> [Ngrams] -> Bool
225 doesContains l l'
226 | null l' = True
227 | length l' > length l = False
228 | elem (head l') l = doesContains l (tail l')
229 | otherwise = False
230
231 doesAnyContains :: Set Ngrams -> [Set Ngrams] -> [Set Ngrams] -> Bool
232 doesAnyContains h l l' = any (\c -> doesContains (Set.toList c) (Set.toList h)) (l' ++ l)
233
234 filterNestedCliques :: Set Ngrams -> [Set Ngrams] -> [Set Ngrams] -> [Set Ngrams]
235 filterNestedCliques h l l'
236 | null l = if doesAnyContains h l l'
237 then l'
238 else h : l'
239 | doesAnyContains h l l' = filterNestedCliques (head l) (tail l) l'
240 | otherwise = filterNestedCliques (head l) (tail l) (h : l')
241
242
243 filterFisByNested :: Map (Date, Date) Fis -> Map (Date, Date) Fis
244 filterFisByNested = map (\fis -> restrictKeys fis
245 $ Set.fromList
246 $ filterNestedCliques (head (keys fis)) (keys fis) []
247 )
248
249 phyloFis :: Map (Date, Date) Fis
250 phyloFis = termsToFis phyloTerms
251
252 termsToFis :: Map (Date, Date) [Document]
253 -> Map (Date, Date) Fis
254 termsToFis = corpusToFis (words . text)
255
256 -- | TODO: parameters has to be checked
257 -- | TODO FIS on monotexts
258 corpusToFis :: (Document -> [Ngrams])
259 -> Map (Date, Date) [Document]
260 -> Map (Date, Date) (Map (Set Ngrams) Int)
261 corpusToFis f = map (\d -> fisWithSizePolyMap (Segment 1 20) 1 (map f d))
262
263
264 ------------------------------------------------------------------------
265 -- | STEP 7 | -- Link level -1 to level 0
266
267
268 phyloLinked_m1_0 :: Phylo
269 phyloLinked_m1_0 = phyloToLinks lvl_m1_0 phyloLinked_0_m1
270
271 lvl_m1_0 :: LinkLvl
272 lvl_m1_0 = (LinkLvl Link_m1_0 (-1) 0)
273
274
275 ------------------------------------------------------------------------
276 -- | STEP 6 | -- Link level 0 to level -1
277
278
279 addPointer :: Semigroup field
280 => ASetter current target identity (field -> field)
281 -> field -> current -> target
282 addPointer field targetPointer current =
283 set field (<> targetPointer) current
284
285 containsIdx :: [Int] -> [Int] -> Bool
286 containsIdx l l'
287 | null l' = False
288 | last l < head l' = False
289 | head l' `elem` l = True
290 | otherwise = containsIdx l (tail l')
291
292 shouldLink :: LinkLvl -> [Int] -> [Int] -> Bool
293 shouldLink lvl current target = case linkLvlLabel lvl of
294 Link_0_m1 -> containsIdx target current
295 Link_m1_0 -> containsIdx target current
296 Link_0_1 -> containsIdx target current
297 Link_1_0 -> containsIdx target current
298 Link_x_y -> undefined
299 _ -> panic ("error link level to be defined")
300
301 linkGroupToGroups :: LinkLvl -> PhyloGroup -> [PhyloGroup] -> PhyloGroup
302 linkGroupToGroups lvl current targets
303 | linkLvlFrom lvl < linkLvlTo lvl = setLevelParents current
304 | linkLvlFrom lvl > linkLvlTo lvl = setLevelChilds current
305 | otherwise = current
306 where
307 setLevelChilds :: PhyloGroup -> PhyloGroup
308 setLevelChilds = over (phylo_groupLevelChilds) addPointers
309
310 setLevelParents :: PhyloGroup -> PhyloGroup
311 setLevelParents = over (phylo_groupLevelParents) addPointers
312
313 addPointers :: [Pointer] -> [Pointer]
314 addPointers lp = lp ++ Maybe.mapMaybe (\target -> if shouldLink lvl (_phylo_groupNgrams current)
315 (_phylo_groupNgrams target )
316 then Just ((getGroupId target),1)
317 else Nothing
318 ) targets
319
320 addPointers' :: [Pointer] -> [Pointer]
321 addPointers' lp = lp ++ map (\target -> ((getGroupId target),1)) targets
322
323 linkGroupsByLevel :: LinkLvl -> Phylo -> [PhyloGroup] -> [PhyloGroup]
324 linkGroupsByLevel lvl p groups = map (\group ->
325 if getGroupLvl group == linkLvlFrom lvl
326 then linkGroupToGroups lvl group (getGroupsByLevelAndPeriod (linkLvlTo lvl) (getGroupPeriod group) p)
327 else group ) groups
328
329 phyloToLinks :: LinkLvl -> Phylo -> Phylo
330 phyloToLinks lvl p = over ( phylo_periods
331 . traverse
332 . phylo_periodLevels
333 . traverse
334 . phylo_levelGroups
335 )
336 (linkGroupsByLevel lvl p) p
337
338 phyloLinked_0_m1 :: Phylo
339 phyloLinked_0_m1 = phyloToLinks lvl_0_m1 phyloWithGroups0
340
341 lvl_0_m1 :: LinkLvl
342 lvl_0_m1 = (LinkLvl Link_0_m1 0 (-1))
343
344
345 ------------------------------------------------------------------------
346 -- | STEP 5 | -- Build level 0 (for the moment it's just a durty copy of level -1)
347
348
349 setGroupIdLvl :: Int -> PhyloGroup -> PhyloGroup
350 setGroupIdLvl lvl (PhyloGroup ((period, lvl'), idx) gLabel gNgrams gQ gPP gPC gLP gLC)
351 = PhyloGroup ((period, lvl), idx) gLabel gNgrams gQ gPP gPC gLP gLC
352
353 setPhyloLevel :: Int -> PhyloLevel -> PhyloLevel
354 setPhyloLevel lvl (PhyloLevel (periodId, lvl') lvlGroups)
355 = PhyloLevel (periodId, lvl) lvlGroups'
356 where
357 lvlGroups' = map (\g -> setGroupIdLvl lvl g) lvlGroups
358
359 copyPhyloLevel :: Int -> [PhyloLevel] -> [PhyloLevel]
360 copyPhyloLevel lvl l = (setPhyloLevel lvl (head l)) : l
361
362 alterLvl :: Int -> [PhyloPeriod] -> [PhyloPeriod]
363 alterLvl lvl l = map (\p -> PhyloPeriod (_phylo_periodId p) (copyPhyloLevel lvl $ _phylo_periodLevels p)) l
364
365 phyloWithGroups0 :: Phylo
366 phyloWithGroups0 = updatePhyloByLevel Level_0 phyloWithGroupsm1
367
368
369 ------------------------------------------------------------------------
370 -- | STEP 4 | -- Build level -1
371
372
373 findIdx :: Ngrams -> Int
374 findIdx n = case (elemIndex n (_phylo_ngrams phylo)) of
375 Nothing -> panic "PhyloError"
376 Just i -> i
377
378 ngramsToGroup :: [Ngrams] -> Text -> Int -> Int -> Int -> Int -> PhyloGroup
379 ngramsToGroup terms label idx lvl from to = PhyloGroup (((from, to), lvl), idx)
380 label
381 (sort (map (\x -> findIdx x) terms))
382 (Map.empty)
383 [] [] [] []
384
385 docsToLevel :: (Date, Date) -> Corpus -> PhyloLevel
386 docsToLevel k v = PhyloLevel (k,(-1)) (map (\x ->
387 ngramsToGroup [snd x] (snd x) (fst x) (-1) (fst k) (snd k)
388 ) $ zip [1..] $ (nub . concat) $ map (words . text) v)
389
390 corpusToPhyloPeriod :: Map (Date,Date) Corpus -> [PhyloPeriod]
391 corpusToPhyloPeriod corpus = map (\x -> PhyloPeriod (fst x) [(snd x)]) $ zip (keys mapLvl) (elems mapLvl)
392 where
393 mapLvl :: Map (Date,Date) PhyloLevel
394 mapLvl = mapWithKey docsToLevel corpus
395
396 updatePhyloByLevel :: Levels -> Phylo -> Phylo
397 updatePhyloByLevel lvl (Phylo pDuration pNgrams pPeriods)
398 = case lvl of
399
400 Level_m1 -> Phylo pDuration pNgrams pPeriods'
401 where pPeriods' = (corpusToPhyloPeriod phyloTerms) ++ pPeriods
402
403 Level_0 -> Phylo pDuration pNgrams pPeriods'
404 where pPeriods' = alterLvl 0 pPeriods
405
406 Level_1 -> fisToPhyloLevel phyloFisFiltered (Phylo pDuration pNgrams pPeriods)
407
408 _ -> panic ("error level to be defined")
409
410 phyloWithGroupsm1 :: Phylo
411 phyloWithGroupsm1 = updatePhyloByLevel Level_m1 phylo
412
413
414 ------------------------------------------------------------------------
415 -- | STEP 3 | -- Split the Corpus into Periods and reduce each Document as a list of Ngrams
416
417
418 phyloTerms :: Map (Date, Date) [Document]
419 phyloTerms = toPeriodes date 5 3 $ cleanCorpus cleanedActants phyloCorpus
420
421 toPeriodes :: (Ord date, Enum date) => (doc -> date)
422 -> Grain -> Step -> [doc] -> Map (date, date) [doc]
423 toPeriodes _ _ _ [] = panic "Empty corpus can not have any periods"
424 toPeriodes f g s es = Map.fromList $ zip hs $ map (inPeriode f es) hs
425 where
426 hs = steps g s $ both f (head es, last es)
427 --------------------------------------------------------------------
428 -- | Define overlapping periods of time by following regular steps
429 inPeriode :: Ord b => (t -> b) -> [t] -> (b, b) -> [t]
430 inPeriode f' h (start,end) =
431 fst $ List.partition (\d -> f' d >= start && f' d <= end) h
432 --------------------------------------------------------------------
433 -- | Find steps of linear and homogenous time of integer
434 steps :: (Eq date, Enum date) => Grain -> Step -> (date, date) -> [(date, date)]
435 steps s' o' (start,end) = map (\l -> (head l, last l))
436 $ chunkAlong s' o' [start .. end]
437
438 cleanCorpus :: MapList -> Corpus -> Corpus
439 cleanCorpus ml = map (\(Document d t) -> Document d (unwords $ filter (\x -> elem x ml) $ monoTexts t))
440
441
442 ------------------------------------------------------------------------
443 -- | STEP 2 | -- Find some Ngrams (ie: phyloGroup of level -1) out of the Corpus & init the phylo
444
445
446 phylo = Phylo (both date $ (last &&& head) phyloCorpus) phyloNgrams []
447
448 phyloNgrams :: PhyloNgrams
449 phyloNgrams = Vector.fromList cleanedActants
450
451 cleanedActants :: [Ngrams]
452 cleanedActants = map toLower actants
453
454 actants :: [Ngrams]
455 actants = [ "Cleopatre" , "Ptolemee", "Ptolemee-XIII", "Ptolemee-XIV"
456 , "Marc-Antoine", "Cesar" , "Antoine" , "Octave" , "Rome"
457 , "Alexandrie" , "Auguste" , "Pompee" , "Cassius" , "Brutus"]
458
459
460 ------------------------------------------------------------------------
461 -- | STEP 1 | -- Get a corpus of Documents
462
463
464 phyloCorpus :: Corpus
465 phyloCorpus = map (\(d,t) -> Document d t) exampleDocuments
466
467 exampleDocuments :: [(Date, Text)]
468 exampleDocuments = List.sortOn fst [ (-51,"Cleopatre règne sur l’egypte entre 51 et 30 av. J.-C. avec ses frères-epoux Ptolemee-XIII et Ptolemee-XIV, puis aux côtes du general romain Marc-Antoine. Elle est celèbre pour avoir ete la compagne de Jules Cesar puis d'Antoine, avec lesquels elle a eu plusieurs enfants. Partie prenante dans la guerre civile opposant Antoine à Octave, elle est vaincue à la bataille d'Actium en 31 av. J.-C. Sa defaite va permettre aux Romains de mener à bien la conquête de l’egypte, evenement qui marquera la fin de l'epoque hellenistique."), (-40,"Il existe relativement peu d'informations sur son sejour à Rome, au lendemain de l'assassinat de Cesar, ou sur la periode passee à Alexandrie durant l'absence d'Antoine, entre -40 et -37."), (-48,"L'historiographie antique lui est globalement defavorable car inspiree par son vainqueur, l'empereur Auguste, et par son entourage, dont l'interêt est de la noircir, afin d'en faire l'adversaire malfaisant de Rome et le mauvais genie d'Antoine. On observe par ailleurs que Cesar ne fait aucune mention de sa liaison avec elle dans les Commentaires sur la Guerre civile"), (-69,"Cleopatre est nee au cours de l'hiver -69/-686 probablement à Alexandrie."), (-48,"Pompee a en effet ete le protecteur de Ptolemee XII, le père de Cleopatre et de Ptolemee-XIII dont il se considère comme le tuteur."), (-48,"Ptolemee-XIII et Cleopatre auraient d'ailleurs aide Pompee par l'envoi d'une flotte de soixante navires."), (-48,"Mais le jeune roi Ptolemee-XIII et ses conseillers jugent sa cause perdue et pensent s'attirer les bonnes graces du vainqueur en le faisant assassiner à peine a-t-il pose le pied sur le sol egyptien, près de Peluse, le 30 juillet 48 av. J.-C., sous les yeux de son entourage."), (-48,"Cesar fait enterrer la tête de Pompee dans le bosquet de Nemesis en bordure du mur est de l'enceinte d'Alexandrie. Pour autant la mort de Pompee est une aubaine pour Cesar qui tente par ailleurs de profiter des querelles dynastiques pour annexer l’egypte."), (-48,"Il est difficile de se prononcer clairement sur les raisons qui ont pousse Cesar à s'attarder à Alexandrie. Il y a des raisons politiques, mais aussi des raisons plus sentimentales (Cleopatre ?). Il tente d'abord d'obtenir le remboursement de dettes que Ptolemee XII"), (-46,"Les deux souverains sont convoques par Cesar au palais royal d'Alexandrie. Ptolemee-XIII s'y rend après diverses tergiversations ainsi que Cleopatre."), (-47,"A Rome, Cleopatre epouse alors un autre de ses frères cadets, à Alexandrie, Ptolemee-XIV, sur l'injonction de Jules Cesar"), (-46,"Cesar a-t-il comme objectif de montrer ce qu'il en coûte de se revolter contre Rome en faisant figurer dans son triomphe la sœur de Cleopatre et de Ptolemee-XIV, Arsinoe, qui s'est fait reconnaître reine par les troupes de Ptolemee-XIII ?"), (-44,"Au debut de l'annee -44, Cesar est assassine par Brutus. Profitant de la situation confuse qui s'ensuit, Cleopatre quitte alors Rome à la mi-avril, faisant escale en Grèce. Elle parvient à Alexandrie en juillet -44."), (-44,"La guerre que se livrent les assassins de Cesar, Cassius et Brutus et ses heritiers, Octave et Marc-Antoine, oblige Cleopatre à des contorsions diplomatiques."), (-41,"Nous ignorons depuis quand Cleopatre, agee de 29 ans en -41, et Marc-Antoine, qui a une quarantaine d'annees, se connaissent. Marc-Antoine est l'un des officiers qui ont participe au retablissement de Ptolemee XII. Il est plus vraisemblable qu'ils se soient frequentes lors du sejour à Rome de Cleopatre."), (-42,"Brutus tient la Grèce tandis que Cassius s'installe en Syrie. Le gouverneur de Cleopatre à Chypre, Serapion, vient en aide à Cassius."), (-42,"Cassius aurait envisage de s'emparer d'Alexandrie quand le 'debarquement' en Grèce d'Antoine et d'Octave l'oblige à renoncer à ses projets")]
469