Simulator_20260316-5_V8

Simulator_20260316-5_V8 preview image

1 collaborator

Tags

(This model has yet to be categorized with any tags)
Visible to everyone | Changeable by everyone
Model was written in NetLogo 3D 7.0.3 • Viewed 52 times • Downloaded 7 times • Run 0 times
Download the 'Simulator_20260316-5_V8' modelDownload this modelEmbed this model

Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)


WHAT IS IT?

This simulator models the co‑evolution of opinions, internal representations (memes), prevalence, influence, and network structure in a connected population of agents.
Each agent carries:

  • an opinion in ([-1, +1])
  • a prevalence value (0–99), representing the depth of its internal representations
  • an influence level (0–1)
  • meme quantities and meme weights
  • a meta‑type (structural, dynamic, or none)
  • a set of social links that evolve over time
  • a 3D position:
    • X = opinion
    • Y = prevalence
    • Z = influence

The model integrates:

  • meme‑based opinion formation
  • targeted meme injection
  • endogenous and structural meta‑influencers
  • homophily‑based network rewiring
  • group effects
  • reward‑based reinforcement
  • exogenous events
  • CSV import/export of agent states and statistics
  • 3D visualization of social links
  • full restoration of neighbor lists and 3D coordinates from Values CSV
  • programmable meta‑link behavior via meta-link-prog

3D REPRESENTATION

Agents are mapped to:

  • X‑axis: opinion (−1 = left, +1 = right)
  • Y‑axis: prevalence (0–99)
  • Z‑axis: influence (0–1)

Colors:

  • Red: left‑leaning
  • Blue: right‑leaning
  • White: center
  • Yellow: meta‑influencers (structural or dynamic)

Links (visible when show-links? is ON):

  • Green: same‑sign connections
  • Gray: opposite‑sign connections
  • Meta‑links: displayed according to meta-link-prog rules

The 3D view updates continuously as opinions, prevalence, influence, and network structure evolve.


HOW TO USE

  1. Choose population size with pop.
  2. Click Setup to initialize agents, networks, memes, and meta‑structure.
  3. Click Go to run or pause the simulation.
  4. Use output, csv-export, and csv-mode to record results.
  5. Use in_file_values to reload a saved state with full agent information.
  6. Use choice_iter to select which iteration to restore.

GENERAL CONTROLS

  • Setup: initialize agents, networks, memes, meta‑structure
  • Go: start/stop simulation
  • in_file: load simple-format agent states
  • infilevalues: load full agent states (Values CSV) including neighbors and 3D coordinates
  • refresh: reset plots
  • cumulative: if OFF, resets counters each tick
  • output: None | Statistics | Values | File

POPULATION & ITERATIONS

  • pop: number of agents
  • nb_try: number of trials
  • max_iter: iterations per trial
  • threshold: majority threshold
  • choice_iter: iteration to restore when importing Values CSV

META‑INFLUENCERS (STRUCTURAL & DYNAMIC)

Two types of meta‑influencers exist:

Structural meta‑influencers

  • Assigned at setup
  • Controlled by meta-influencers-selection (All / Left / Right)
  • Percentage set by meta-influencers
  • Eligibility via prev-low / prev-high
  • Influence = 1
  • Persistent across imports
  • Create meta‑links
  • Protected by metablock (prevents sign switching)
  • Restored when importing Values CSV

Dynamic meta‑influencers

  • Emerge when influence rises to 1
  • Yellow but not counted as structural
  • Do not create structural meta‑links
  • Fully exported/imported via meta-type

Meta‑link programming (meta-link-prog)

This chooser determines how meta‑links are created:

  • meta → non‑meta
  • meta → meta
  • mixed or restricted patterns
  • structural vs dynamic behavior

This allows fine‑grained control over elite connectivity and structural influence.


SOCIAL NETWORK DYNAMICS

Links evolve through homophily and probabilistic rewiring.

Parameters:

  • link-removal-threshold
  • link-formation-threshold
  • prob
  • linksdown / linksup
  • bridge-prob
  • show-links?
  • linktick

Structural metas may create meta‑links based on:

  • meta-min / meta-max / meta-links
  • meta-ok
  • meta-link-prog

3D Link Visualization

When show-links? is ON:

  • All social links are drawn in the 3D view
  • Colors reflect sign similarity
  • Meta‑links follow the rules of meta-link-prog
  • Links update dynamically as the network rewires

OPINION & PREVALENCE DYNAMICS

Opinion adoption depends on:

  • prevalence-weight
  • polarization-factor
  • adoption-floor
  • noise
  • group effects
  • reward system
  • meme dynamics

Prevalence adapts through:

  • mod-prev
  • rate-mod

GROUP EFFECT

  • group-impact-mode: all or k-nearest
  • group-k
  • group-impact-weight
  • group-impact-alpha

Interpretation of alpha:

  • <1: small clusters matter more
  • =1: linear
  • >1: only large aligned groups matter

REWARD MECHANISM

Successful influencers receive a temporary tx‑bonus.

Parameters:

  • reward-step
  • reward-cap
  • reward-decay
  • reward-scope
  • reward-prev-delta

MEME‑BASED REPRESENTATION

When use-memes? is ON:

Prevalence

Derived from total meme quantity:

  • meme-plus
  • meme-minus

Opinion

Derived from meme weights:

[ \text{opinion} = \frac{meme_plus_w - meme_minus_w}{meme_plus_w + meme_minus_w} ]

Transmission

Receivers gain:

  • meme quantity
  • meme weight proportional to quantity

Parameters:

  • meme-weight-mean
  • meme-weight-sd
  • meme-weight-min / meme-weight-max
  • meme-anti-leak
  • meme-decay

Meme-Based Representation (Weighted Memes + Targeted Injection)

When use-memes? is ON, opinions and prevalence are computed from internal “meme” stocks.
This version distinguishes meme quantity (how many representations an agent holds) from meme weight (how strongly each representation shapes the opinion).


1) Two levels: Quantity vs Weight

Meme quantity → Prevalence

  • meme-plus, meme-minus store how many pro/anti memes the agent holds.
  • Prevalence (0–99) is derived from the total quantity:
    meme-plus + meme-minus rescaled to 0–99.

Interpretation: prevalence approximates how “rich” an agent’s internal representation system is (how many arguments/frames are available).

Meme weight → Opinion

  • meme-plus-w, meme-minus-w store the cumulative weighted strength of pro/anti memes.
  • Opinion is computed from the weighted balance:

[ opinion = \frac{meme\text{-}plus\text{-}w - meme\text{-}minus\text{-}w}{meme\text{-}plus\text{-}w + meme\text{-}minus\text{-}w} ]

  • If pro-weights dominate → opinion moves toward +1
  • If anti-weights dominate → opinion moves toward −1
  • If balanced → opinion stays near 0

A tiny denominator safeguard is used to avoid division by zero.


2) Weighted transmission during interactions

When an agent is influenced, the receiver gets: - a quantity increment (meme-gain) on the side of the emitter (plus or minus), - and a weight increment proportional to that quantity.

Weight distribution parameters

  • meme-weight-mean (typical 0.2–3.0): average strength of newly acquired memes.

    • Low values → many memes are needed to polarize opinions.
    • High values → opinions polarize faster, even with small meme quantities.
  • meme-weight-sd (typical 0.0–1.0): heterogeneity (memes differ in strength).

    • 0.0 → all memes are equally strong.
    • Higher values → mixed populations with “weak” and “strong” memes.
  • meme-weight-min / meme-weight-max: hard bounds preventing unrealistic weights.


3) Meme anti-leak and decay (optional)

  • meme-anti-leak (0–1): when one side grows, a fraction of the opposite stock is reduced.
    High values create “winner-takes-more” dynamics (polarization reinforcement).

  • meme-decay (0–0.05 typical): forgetting rate applied each tick to quantities and weights.


Meme Injection (Targeted diffusion of new representations)

Beyond “events” that shift opinions directly, the simulator can inject memes into a selected subgroup to simulate the introduction and diffusion of a new narrative, argument, or frame.

1) Targeting bounds (preferred controls)

Agents are eligible for injection if they satisfy:

  • inject-low_meme ≤ opinion ≤ inject-high_meme
  • inject-low-prev ≤ prevalence ≤ inject-high-prev

This allows injection into: - moderates only (e.g., −0.2 to +0.2), - one camp only (e.g., +0.2 to +1), - low-prevalence agents only (e.g., 0 to 30).

2) Injection strength and reach

  • inject-prob-max (0–1): maximum share of eligible agents that actually receive the injected memes.
  • inject-sign: "plus" or "minus" (which direction the injected memes support).
  • inject-amount (typical 1–10): how many memes are injected (quantity → raises prevalence).
  • inject-weight (typical 0.2–5.0): how strong injected memes are (weight → shifts opinion more sharply).

Rule of thumb:
- Increase inject-amount to raise prevalence (more representations).
- Increase inject-weight to raise polarization intensity (stronger conviction shift).

3) Scheduling injection

  • auto_inject?: if ON, injection occurs when ticks = inject-tick.
  • inject-tick: the tick at which injection happens.
  • repeat-inject?: if ON, injection repeats every inject-pace ticks.
  • inject-pace: the interval between repeated injections.

Example scenarios

  • One-shot targeted campaign:
    auto_inject? = ON, repeat-inject? = OFF, inject-tick = 50, inject-prob-max = 0.2,
    inject-low_meme = -0.2, inject-high_meme = 0.2, inject-sign = "plus",
    inject-amount = 3, inject-weight = 2.0.

  • Slow diffusion of a weak but persistent narrative:
    repeat-inject? = ON, inject-pace = 25, inject-prob-max = 0.05,
    inject-amount = 1, inject-weight = 0.5.

  • High-impact shock on a small subgroup:
    inject-prob-max = 0.02, inject-amount = 2, inject-weight = 5.0,
    targeting a narrow opinion band.


MEME INJECTION

Eligibility:

  • inject-low_meme ≤ opinion ≤ inject-high_meme
  • inject-low-prev ≤ prevalence ≤ inject-high-prev

Parameters:

  • inject-prob-max
  • inject-sign
  • inject-amount
  • inject-weight
  • auto_inject?, inject-tick
  • repeat-inject?, inject-pace

EXOGENOUS EVENTS

Events shift:

  • opinion (event_size)
  • prevalence (prev_change)

Targeting:

  • meme_set + to_left
  • or bounds: low_meme / high_meme, low-prev / high-prev

Scheduling:

  • auto_event
  • event-init
  • repeat-event
  • event-pace
  • event-prob-max
  • event-meme-change?

EXOGENOUS EVENTS (bounded & probabilistic)

Targeting

  • meme_set + to_left: if ON, structural targeting by camp (Left side / Right side).
  • Otherwise, use bounds: low_meme / high_meme (opinion window) and low-prev / high-prev (prevalence window).

Effects on targeted agents

  • event_size: opinion shift (toward the intended camp).
  • prev_change: prevalence change (clamped to [0,99]).
  • event-prob-max (0–1): maximum share of targeted agents that actually receive the shock (each agent draws U(0,1)).

Triggering

  • event button: one-shot shock (manual).
  • auto_event + tick-event: scheduled automatic shock at iteration tick-event.

Repeated events (per your code)

  • event-init: initial offset of the first event (on setup and at each new trial, tick-event ← event-init).
  • repeat-event (switch): if ON, re-schedules the next event after each occurrence.
  • event-pace (≥ 1 tick recommended): spacing between repeated events.
  • Scheduling logic:

    • If auto_event = ON and iter = tick-event → run event.
    • If repeat-event = ONtick-event ← tick-event + event-pace.
    • Else (OFF) → no automatic re-scheduling (you may adjust tick-event manually).
    • If auto_event = OFFeach tick, tick-event ← iter + event-pace (the next time you switch ON, the event fires ≈ event-pace ticks later).

Quick examples

  • Single calibration shock: auto_event=ON, repeat-event=OFF, event-init=2, event-prob-max=1.0.
  • Periodic pulses: auto_event=ON, repeat-event=ON, event-init=50, event-pace=50, event-prob-max=0.30.
  • Diffuse perturbations: repeat-event=ON, event-pace=100, event-prob-max=0.05.

Parameter Ranges — Key Sliders (Overview)

The table below summarizes the recommended value ranges for the nine main sliders governing opinion transmission, meme dynamics, and external shocks.
Ranges are indicative and meant to support exploratory, comparative, and pedagogical simulations rather than strict calibration.

| Slider name | Typical range | What it controls | Low values → expected effects | High values → expected effects | |------------|---------------|------------------|-------------------------------|--------------------------------| | prevalence-weight | 0.0 – 2.0 | Weight of prevalence differences in adoption probability | Opinion change weakly tied to representational depth; more random diffusion | Deeply rooted agents dominate transmission; strong inertia | | adoption-floor | 0.0 – 0.10 | Minimum probability of adoption regardless of distance | Near-impossible cross-camp adoption | Persistent low-level noise and occasional inversions | | polarization-factor | 0.0 – 1.0 | Penalty applied to large opinion gaps | Distance barely matters; smooth convergence | Strong ideological barriers; entrenched camps | | group-impact-weight | 0.0 – 1.0 | Strength of group alignment effect | Individual interactions dominate | Local majorities strongly condition adoption | | group-impact-alpha | 0.2 – 3.0 | Non-linearity of group effect | Small minorities exert strong influence | Only large aligned groups matter | | meme-max | 50 – 200 | Maximum stock of memes per agent (prevalence scale) | Shallow belief systems; fast saturation | Deep ideological accumulation; slow dynamics | | meme-gain | 0.5 – 2.0 | Meme increment per successful transmission | Slow learning; weak reinforcement | Rapid ideological buildup | | meme-anti-leak | 0.0 – 0.5 | Cross-erosion of opposite meme stock | Memes accumulate independently | Strong competition; winner-takes-more dynamics | | meme-decay | 0.0 – 0.05 | Forgetting rate of memes per tick | Stable long-term memory | Rapid erosion; volatile belief systems | | event-prob-max | 0.0 – 1.0 | Proportion of agents affected by an event | Micro-perturbations, local shocks | System-wide shocks |


Usage notes

  • Low–mid ranges are recommended for exploratory runs and sensitivity analysis.
  • Extreme values are useful to study boundary cases (lock-in, collapse, polarization).
  • Parameters interact strongly: e.g., high meme-gain combined with high meme-anti-leak accelerates polarization.

This table is intended as a cheat sheet; empirical calibration should rely on systematic parameter sweeps.

Meme Injection — Slider Ranges, Effects, and Interactions

This table documents the nine sliders governing meme injection.
Together, they define when, how often, to whom, and with what strength new memes are introduced into the population, allowing controlled simulations of campaigns, rumors, or polarizing shocks.


Core Injection Parameters

| Slider | Typical range | Role in the model | Low values → effects | High values → effects | |------|---------------|-------------------|----------------------|----------------------| | inject-tick | 1 – max_iter | First tick at which meme injection can occur | Early priming of agents; long-run structural impact | Late shock; short-term perturbation with limited propagation | | inject-pace | 1 – 200 | Interval (ticks) between injections | Continuous or quasi-continuous pressure | Rare, punctuated shocks | | inject-prob-max | 0.0 – 1.0 | Maximum probability that an eligible agent receives a meme at an injection tick | Injection remains marginal or localized | Broad exposure; near-systemic dissemination | | inject-amount | 0 – meme-max | Quantity of meme stock added per injection | Subtle informational nudges | Strong narrative saturation or propaganda burst | | inject-weight | 0.0 – 2.0 (or higher) | Relative impact of injected memes on opinion vs. prevalence | Injected memes mainly increase prevalence (attention/salience) | Injected memes strongly bias opinion formation |


Targeting and Selectivity Parameters

| Slider | Typical range | Role in the model | Low values → effects | High values → effects | |------|---------------|-------------------|----------------------|----------------------| | inject-low_meme | −1.0 – 1.0 | Lower bound of opinion eligible for injection | Broad targeting across ideological spectrum | Injection limited to one side or a narrow ideological niche | | inject-high_meme | −1.0 – 1.0 | Upper bound of opinion eligible for injection | Narrow ideological window | Broad or opposing-side reach (depending on bounds) | | inject-low-prev | 0 – 99 | Minimum prevalence required to receive injected memes | Inclusion of low-salience or weakly engaged agents | Targeting already attentive or mobilized agents | | inject-high-prev | 0 – 99 | Maximum prevalence eligible for injection | Focus on low-to-mid engagement agents | Restriction to highly engaged elites |


Interaction Effects (Key Dynamics)

  • inject-tick × inject-pace

    • Early + short pace → persistent campaign dynamics.
    • Late + long pace → isolated shock events.
  • inject-prob-max × inject-amount

    • Low prob + high amount → elite seeding (few agents, strong impact).
    • High prob + low amount → mass diffusion (many agents, weak signal).
  • inject-weight × inject-amount

    • High weight + high amount → rapid opinion polarization.
    • Low weight + high amount → agenda-setting without strong persuasion.
  • inject-lowmeme / inject-highmeme × inject-low-prev / inject-high-prev

    • Narrow opinion + high prevalence → echo-chamber reinforcement.
    • Broad opinion + low prevalence → grassroots diffusion potential.

Conceptual Interpretation

  • Low values generally model background noise, rumors, or weak informational exposure.
  • High values approximate organized campaigns, disinformation bursts, or polarizing media events.
  • Intermediate combinations allow exploration of threshold effects, diffusion delays, and nonlinear amplification.

These sliders are designed to be orthogonal but non-independent: meaningful experiments emerge from their joint configuration, not isolated tuning.

Preset — Campaign / Rumor / Polarizing shock (recommended default profile)

This preset is a ready-to-run parameter profile designed to reproduce a common empirical pattern in opinion dynamics:

1) a stable baseline (moderate homophily, limited cross-camp contact),
2) a rumor/campaign phase (repeated external shocks reaching only part of the population),
3) a polarizing outcome (network segmentation, fewer inversions, stronger within-camp reinforcement).

It is intended as a starting point for university-level experimentation (reproducible and interpretable), not as a calibrated model for a specific case.


A. Core idea

  • Use repeat-event + event-pace to create a campaign/rumeur that reappears periodically.
  • Use event-prob-max < 1 so the shock reaches only a subset of eligible agents, generating diffusion rather than an immediate global collapse.
  • Keep bridge-prob low but non-zero so rare cross-camp bridges exist (possible inversions), while still allowing polarization to emerge.
  • Activate group impact and a mild reward system to reproduce realistic “social proof” and reinforcement mechanisms.

B. Default values (Preset)

(Values assume your current sliders/switches; ranges remain adjustable by the operator.)

1) Events: “campaign / rumor” mechanics

  • auto_event: ON
  • repeat-event: ON
  • event-init: 50 (first shock after an initial stabilization period)
  • event-pace: 25 (shock repeats every 25 iterations)
  • event-prob-max: 0.10 (≈10% of eligible agents receive the shock each cycle)

Bounds & targeting - meme_set: OFF (use bounds rather than structural Left/Right set)
- lowmeme / highmeme: -0.30 / +0.30 (targets the “convertible middle”)
- low-prev / high-prev: 10 / 60 (targets moderate-prevalence agents—reachable but not rigid)

Shock magnitude - event_size: 0.25 (clear movement without saturating ±1 too fast)
- prev_change: +8 (moderate strengthening of prevalence in targeted agents; set 0 if you want “pure opinion shock”)

Interpretation - This reproduces a campaign/rumeur that repeatedly perturbs the middle rather than only extremes, and spreads indirectly through the network.


2) Network: controlled homophily + rare bridges

  • network: ON
  • link-formation-threshold: 0.20
  • link-removal-threshold: 0.40
  • prob: 0.30
  • linksup / linksdown: 10 / 10 (balanced churn)
  • bridge-prob: 0.05 (rare but non-zero cross-camp bridges)
  • show-links?: optional (OFF for performance; ON for demonstrations)

Expected effect - The network remains mostly homophilous, but with occasional “bridges” allowing limited cross-camp exposure.


3) Opinion adoption: prevalence-driven, polarization-aware

  • prevalence-weight: 1.40
  • polarization-factor: 0.60
  • adoption-floor: 0.03
  • noise: 0.01 (small background drift)

Expected effect - Adoption is mostly driven by prevalence differences, while strong polarization reduces cross-camp adoption without making it impossible.


4) Group impact: social proof (moderate strength)

  • group-impact-mode: k-nearest
  • group-k: 8
  • group-impact-weight: 0.60
  • group-impact-alpha: 1.20

Expected effect - Local neighbourhood alignment matters; majorities have slightly more weight than minorities (alpha > 1).


5) Reward: modest reinforcement (avoid runaway)

  • reward-step: 0.03
  • reward-cap: 0.30
  • reward-decay: 0.005
  • reward-scope: both
  • reward-prev-delta: 0 (keep prevalence effects attributable to memes/events; set 1–3 if you want “success breeds conviction”)

Expected effect - Successful influencers become moderately more effective over time, but decay prevents permanent dominance.


6) Memes: ON (recommended for this preset)

  • use-memes?: ON
  • meme-max: 120
  • meme-gain: 1.0
  • meme-anti-leak: 0.20
  • meme-decay: 0.01

Expected effect - Agents gradually accumulate representations; anti-leak creates a mild competitive relation between pro/anti stocks, supporting polarization over repeated shocks.


7) Meta-influencers: optional, controlled

  • meta-ok: ON (optional; ON reproduces real-world asymmetric reach)
  • meta-link-prob: 0 (meta - non-meta) to 1 (meta - meta)
  • meta-influencers-selection: All
  • meta-influencers: 5%
  • prev-low / prev-high: 20 / 80
  • meta-min / meta-max: 8 / 20
  • metablock: ON (prevents sign switching for metas)
  • vary-influence: OFF (recommended OFF to avoid meta inflation; turn ON only if studying endogenous influencer emergence)

Expected effect - A small set of highly connected agents accelerates diffusion while the metablock prevents metas from oscillating across camps.


C. What you should observe (typical outcomes)

  • Early phase (before event-init): moderate clustering, limited movement.
  • During repeated events: a growing asymmetry in meme stocks and prevalence among the targeted “middle”.
  • Over time: stronger within-camp reinforcement, more gray links disappearing, fewer inversions.
  • If you increase event-prob-max toward 0.30–0.50: faster and more global shifts.
  • If you increase bridge-prob toward 0.10–0.20: more cross-camp exposure and more inversions (polarization weakens).

D. Minimal variant (if you want “shock-only”, no campaign)

  • Set repeat-event = OFF and keep a single event-init (or press event once).
  • Keep all other values unchanged to compare one-shot shock vs campaign repetition.

Meme Dynamics — Integrated Monitoring Indicators

This simulator includes eight dedicated monitors designed to track how memes (internal representations) shape opinion formation, prevalence, and polarization over time.
Each monitor is updated at every tick and provides a complementary analytical perspective on the meme–opinion coupling.


1. Mean Meme Stock

What it measures
The average total number of memes held by agents:

Mean Meme Stock = mean(meme-plus + meme-minus)

Why it matters
This indicator captures the global cognitive density of the population.

  • Low values indicate weakly structured opinions (few internal representations).
  • High values reflect ideologically “loaded” agents with many arguments.

Interpretation
- Rising values → accumulation of representations (learning, persuasion, repeated events).
- Falling values → forgetting or erosion (via meme-decay).

Implementation (monitor expression)


2. Meme Polarity Index

What it measures
The net ideological balance of all memes in the system.

It compares the total stock of positive memes to negative memes.

Interpretation
- Values close to +1 → dominance of pro (+) memes.
- Values close to -1 → dominance of contra (−) memes.
- Values near 0 → balanced or plural meme ecology.

Implementation (monitor expression)


3. Opinion–Meme Gap

What it measures
The average absolute difference between: - the agent’s expressed opinion, and
- the opinion implied by its internal meme balance.

Why it matters
This indicator captures latent cognitive inconsistency: agents may express an opinion that is not fully supported by their internal representations.

Interpretation
- Low gap → opinions are well grounded in memes.
- High gap → cognitive tension, instability, or transitional states.

Implementation (monitor expression)


Ideologization Indices (NEW – Dual Measure)

The simulator now exports two distinct ideologization indicators in the CSV Statistics output.
They capture complementary dimensions of ideological structuring and should not be interpreted as redundant.


4a. Ideologization Index (meme-based)

CSV column:
ideologization_index

What it measures
The average representational imbalance within agents, based on their internal meme stocks.

This indicator captures how strongly agents are cognitively oriented toward one side through the asymmetry of supporting arguments, independently of their expressed opinion.

Formal definition
For each agent i: - Meme imbalance = | meme-plus − meme-minus |

The index is the population mean:

Interpretation
- Low values → weakly structured or ambivalent meme ecology
- High values → strong internal representational bias (ideological load)

This measure reflects ideological depth at the cognitive level, even if opinions remain moderate.

Use in the article
- Methods: introduced as a representational ideologization metric
- Results: used to compare cognitive polarization across experimental conditions (Exp1–Exp5)
- Helps explain cases where opinions remain stable despite intense internal meme accumulation


4b. Ideologization Index (opinion-based)

CSV column:
ideologization_opinion_based

What it measures
The degree to which expressed opinion extremity is supported by prevalence (i.e., conviction strength).

This index captures ideological entrenchment at the behavioral level, combining stance and commitment.

Formal definition
For each agent i:

The index is the population mean:

Interpretation
- Low values → weakly anchored opinions or pragmatic attitudes
- High values → extreme opinions supported by high conviction

This measure reflects expressed ideological rigidity, not internal argument diversity.

Use in the article
- Methods: introduced as an expressed ideologization indicator
- Results: contrasted with the meme-based index to distinguish: - cognitive saturation without opinion radicalization
- versus explicit ideological alignment


How to read both indices together

The two measures should be interpreted jointly:

  • High meme-based / low opinion-based
    → cognitively loaded but behaviorally moderate agents (latent ideology)

  • Low meme-based / high opinion-based
    → strong stance with limited internal justification (fragile extremism)

  • High on both
    → fully ideologized population (entrenched opinions with deep representational support)

This distinction is central to the article’s argument on multi-layered ideologization dynamics in agent-based models.


5. Right Meme Polarization

What it measures
The internal ideological polarization of meme stocks among agents holding a positive opinion (opinion >= 0).

It compares reinforcing memes (meme-plus) to counter-memes (meme-minus) within the right-leaning subgroup only, independently of the left side.

Interpretation
- Values close to +1 → strong dominance of reinforcing (pro-right) memes.
- Values near 0 → balanced or internally plural meme structure.
- Values below 0 → erosion or counter-meme dominance inside the right camp.

This indicator reveals whether right-side opinions are: - deeply ideologized, - stable but weakly supported, - or vulnerable to counter-narratives.

Implementation (plot and monitor expression)


6. Left Meme Polarization

What it measures
The internal ideological polarization of meme stocks among agents holding a negative opinion (opinion < 0).

It measures the balance between reinforcing and counter-memes inside the left-leaning subgroup, independently from right-side dynamics.

Interpretation
- Values close to -1 → strong dominance of reinforcing (pro-left) memes.
- Values near 0 → plural or contested internal meme structure.
- Values above 0 → counter-meme pressure within the left camp.

This indicator is essential to detect: - asymmetric polarization, - minority radicalization, - or differential resistance to meme diffusion.

Implementation (plot and monitor expression)


7. Mean Meme-derived Opinion

What it measures
The average opinion value reconstructed from meme stocks, independently of the agents’ current explicit opinions.

This measure computes, for each agent, the opinion implied by the balance of its internal memes
(meme-plus vs meme-minus), then averages this value across the whole population.

Interpretation
- Values close to +1 → the meme ecology strongly supports the positive (right/pro) polarity.
- Values close to -1 → dominance of negative (left/contra) meme representations.
- Values near 0 → balanced or internally conflicting meme structures.

Comparing this indicator with the mean explicit opinion reveals latent ideological tensions: - If meme-derived opinion is stronger than explicit opinion → opinions may soon shift.
- If explicit opinion is stronger than meme-derived opinion → opinions are weakly grounded and fragile.

Why it matters
This indicator captures the deep cognitive orientation of the population, beyond surface-level expressed opinions.
It helps distinguish: - superficial opinion alignment, - structurally grounded ideological states, - and phases where internal representations lag behind observable behavior.


Implementation (monitor expression)


8. Meme Saturation (%)

What it measures
The overall saturation level of meme stocks in the population, expressed as a percentage of the maximum possible meme capacity.

It compares the total number of memes currently held by all agents to the theoretical maximum (meme-max × population).

Interpretation
- Values close to 0% → agents hold very few internal representations; opinions are shallow and weakly grounded.
- Intermediate values (30–60%) → active circulation of arguments with moderate cognitive load.
- Values close to 100% → saturated cognitive environment; agents are highly entrenched and resistant to change.

This indicator captures the cognitive density of the system and helps distinguish: - early diffusion phases, - mature ideological ecosystems, - and saturation-driven stabilization or lock-in effects.

Implementation (monitor expression)

(These indicators are computed only when use-memes? is ON.)


CSV EXPORT

Statistics mode

Exports per tick:

  • mean/median opinion
  • median prevalence
  • median influence
  • left/right shares
  • link creation/removal
  • inversion rate
  • interaction counters
  • fractal indicators
  • ideologization indices
  • meme polarity
  • meme saturation

Values mode

Exports per agent:

  • try
  • tick
  • who
  • prevalence
  • opinion
  • influence
  • meme-plus
  • meme-minus
  • meme-plus-w
  • meme-minus-w
  • meta-type
  • neighbors
  • pos-x, pos-y, pos-z

CSV IMPORT (VALUES MODE)

Pipeline:

  1. Read CSV
  2. Store rows
  3. Select tick = choice_iter
  4. load-and-store-initial-state
  5. restore-initial-state

Restores:

  • opinions
  • prevalence
  • influence
  • meme stocks
  • meme weights
  • meta-type
  • neighbor lists
  • 3D coordinates
  • colors
  • meta-links

Ensures perfect reproducibility of any saved state.


IMPORTING AGENT STATES

The simulator supports two distinct formats for importing agent states.


in_file (Simple format)

The in_file button loads a file containing a single snapshot of agents, typically corresponding to iteration 0.

Each line in the file must contain exactly:

  • tick
  • prevalence
  • opinion
  • influence

This format is intended for:

  • handcrafted initial populations
  • home made created datasets
  • controlled experimental starting points

When loaded:

  • agents are created once
  • meme stocks are initialized from opinion and prevalence
  • the social network is built by the system

infilevalues (CSV Values re-import)

The infilevalues button loads agents from a CSV file previously exported in Values mode or produce by a Generator

Usage:

  • select the iteration to load using choice_iter
  • click infilevalues

Behavior:

  • agents are reconstructed exactly as they were at the selected iteration
  • opinion, prevalence, influence, meme stocks, and meme weights are restored
  • agents with influence = 1 are automatically recolored as meta-agents

This enables:

  • replaying simulations from an intermediate state
  • branching alternative scenarios
  • counterfactual experiments
  • reproducible analysis from archived runs
  • running new kind of regime

MONITORING INDICATORS

  • Mean Meme Stock
  • Meme Polarity Index
  • Opinion–Meme Gap
  • Ideologization Index (meme-based)
  • Ideologization Index (opinion-based)
  • Right Meme Polarization
  • Left Meme Polarization
  • Mean Meme-derived Opinion
  • Meme Saturation (%)

PRESET: CAMPAIGN / RUMOR / POLARIZING SHOCK

A ready-to-run configuration illustrating:

  • baseline stabilization
  • repeated targeted shocks
  • meme accumulation
  • polarization
  • network segmentation

CONCEPTUAL INTERPRETATION

The model integrates:

  • cognitive depth
  • representational structure
  • behavioral stance
  • social reinforcement
  • structural elites
  • emergent influencers
  • network homophily
  • exogenous shocks
  • targeted diffusion

Allows exploration of:

  • polarization
  • ideological saturation
  • diffusion delays
  • nonlinear amplification
  • elite influence
  • cognitive vs behavioral ideologization
  • structural segmentation
  • metastable attractors

USING THE SIMULATOR WITH AN EXTERNAL NETWORK GENERATOR

This simulator can be combined with an external Network Generator to explore how different network topologies shape opinion dynamics, meme propagation, and structural polarization. The Generator produces agent-level data and network structures in CSV format, which can be imported directly into the simulator through the Values CSV pipeline.

This section provides examples of workflows using the Generator and the simulator together.


1. Generating a Network Externally

An external Generator can create networks such as:

  • homophilic networks
  • heterophilic networks
  • multi-cluster networks
  • random networks
  • small-world or scale-free networks
  • custom networks with controlled degree distributions

The Generator typically outputs a CSV file containing, for each agent:

  • opinion
  • prevalence
  • influence
  • meme quantities
  • meme weights
  • meta-type
  • neighbor list
  • 3D coordinates (opinion, prevalence, influence)

This CSV can be loaded directly using infilevalues.


2. Importing a Generated Network into the Simulator

To load a network produced by the Generator:

  1. Place the CSV file in the NetLogo working directory.
  2. Select the file using infilevalues.
  3. Set choice_iter to the desired iteration (usually 1 for Generator outputs).
  4. Click Setup to initialize the import pipeline.
  5. Click Go to continue the simulation from the imported state.

The simulator will restore:

  • all agent variables
  • all meme stocks and weights
  • meta-type assignments
  • neighbor lists
  • 3D positions
  • meta-links (if present)

This ensures perfect reproducibility of the generated network inside the simulator.


3. Using Generator Statistics to Guide Experiments

The Generator can also produce statistical CSV files describing:

  • degree distributions
  • opinion distributions
  • influence distributions
  • prevalence distributions
  • cluster sizes
  • homophily or heterophily indices
  • centrality measures
  • structural signatures

These statistics can be used to:

  • select networks with specific structural properties
  • compare multiple network types
  • identify networks with strong or weak polarization potential
  • study the effect of hubs, bridges, or isolated clusters
  • design controlled experiments with matched or contrasted topologies

4. Example Workflow: Homophilic vs Heterophilic Networks

  1. Use the Generator to create two networks:

    • a homophilic network with strong clustering
    • a heterophilic network with distant clusters and few hubs
  2. Export both as Values CSV.

  3. Import each network separately using infilevalues.

  4. Run identical simulation parameters on both networks.

  5. Compare outcomes such as:

    • convergence speed
    • polarization level
    • meme saturation
    • influence concentration
    • structural segmentation
    • meta-influencer impact

This workflow highlights how network topology shapes collective dynamics.


5. Example Workflow: Reconstructing a Simulation State

The Values CSV export allows the simulator to reconstruct any past state.

  1. Run a simulation and export Values CSV at a chosen tick.
  2. Modify the CSV externally (for example, by altering the network using the Generator).
  3. Re-import the modified CSV using infilevalues.
  4. Continue the simulation from the modified state.

This enables hybrid workflows combining:

  • endogenous evolution inside the simulator
  • exogenous structural transformations from the Generator

6. Example Workflow: Batch Analysis of Network Families

The Generator can produce a family of networks by varying parameters such as:

  • homophily strength
  • degree distribution
  • cluster separation
  • bridge probability
  • meta-link density

For each generated network:

  1. Import the Values CSV.
  2. Run nb_try trials.
  3. Export Statistics CSV.
  4. Compare outcomes across the family of networks.

This approach supports systematic exploration of how structural features influence:

  • opinion convergence
  • meme propagation
  • influence hierarchies
  • metastable attractors
  • resilience to shocks

7. Example Workflow: Using Generator Outputs for Visualization

The 3D coordinates provided by the Generator allow the simulator to display:

  • initial ideological landscapes
  • cluster separation
  • influence gradients
  • structural segmentation
  • meta-influencer placement

By importing these coordinates, the simulator can visualize complex network structures before any dynamics occur.


8. Summary

Using the simulator together with an external Network Generator enables:

  • controlled initialization of complex network structures
  • reproducible experiments across multiple topologies
  • hybrid workflows combining structural and cognitive dynamics
  • systematic comparison of network families
  • reconstruction and modification of simulation states
  • integration of external statistical analysis

This combined approach supports advanced research on opinion dynamics, meme diffusion, polarization, and structural influence.


📊 Fractal and Constellation Measures - Complete Guide

📐 FRACTAL MEASURES (System Complexity)

topological_dimTopological Dimension

What it is: Measures the structural complexity of the social network. Calculated by box-counting on the connection graph.

Interpretation: - > 2.0: Highly hierarchical network, nested clusters (rich structure) - 1.5 – 2.0: Typical "small-world" network - < 1.5: Linear or random network, poorly structured

What it reveals: The underlying social architecture – how agents are potentially connected.


dynamic_dimDynamic Dimension

What it is: Measures the complexity of influence cascades over time. Analyzed via the distribution of cascade sizes.

Interpretation: - 1.5 – 2.0: Cascades of all sizes, dynamically rich system (critical regime) - 1.0 – 1.5: Dominance of medium-sized cascades - < 1.0: Uniform cascades (either all small or all large)

What it reveals: How influence actually propagates – system memory and scaling laws.


criticality_gapCriticality Gap

What it is: |dynamic_dim - topological_dim| / topological_dim

Interpretation: - < 0.2: ⭐ Self-organized criticality – Dynamics perfectly "match" the structure. Optimal propagation, small perturbations can cascade across all scales. - 0.2 – 0.5: Intermediate regime, exploration - > 0.5: Decoupling – either structure is underutilized or dynamics "overflow" (possible artifact)

What it reveals: The harmony between what is structurally possible and what actually happens.


StatusJoint Interpretation

Text message summarizing system state, for example: - 🌟🌟 PERFECT CRITICALITY – Everything aligned - ⚡ CRITICALITY – Structure and dynamics aligned - 🌀 EXPLORATION – Intermediate regime - ⚠️ DECOUPLING – Desynchronization


🌌 CONSTELLATIONS (Population Segmentation)

Constellation Definitions

The population is divided into 5 constellations based on opinion and prevalence:

  • C1 (Left radicals): Very negative opinion (< -0.6) AND high prevalence (≥ 50)
  • C2 (Left moderates): Moderately negative opinion (-0.6 to -0.2)
  • C3 (Floating center): Opinion near zero (-0.2 to 0.2)
  • C4 (Right moderates): Moderately positive opinion (0.2 to 0.6)
  • C5 (Right radicals): Very positive opinion (> 0.6) AND high prevalence (≥ 50)

c1_size to c5_sizeConstellation Sizes

Number of agents in each group.

What it reveals: Population distribution according to ideological positioning and intensity (prevalence).


c1_opinion_avg to c5_opinion_avgAverage Opinion per Constellation

Mean opinion of agents in each group.

Interpretation: - More extreme values indicate more radicalized groups - Evolution over time shows polarization or moderation


c1_prev_avg to c5_prev_avgAverage Prevalence per Constellation

Mean prevalence (engagement, intensity) in each group.

Interpretation: - Radicals (C1, C5) should have high prevalence - Center (C3) should have the lowest prevalence - Increasing prevalence in the center may signal "activation" of moderates


c1_polarization to c5_polarizationInternal Polarization

Standard deviation of opinions within each constellation.

Interpretation: - Low: Homogeneous group, internal consensus - High: Heterogeneous group, internal tensions - A highly polarized group may be about to split


🔄 TURNOVER (Membership Fluidity)

turnover_globalGlobal Turnover

Percentage of the population that changes constellation between two ticks.

Interpretation: - High (> 10%): Very fluid system, unstable opinions, high mobility - Medium (2-10%): Dynamic system with moderate renewal - Low (< 2%): Frozen system, stable segregation


turnover_c1 to turnover_c5Turnover per Constellation

Percentage of agents who left each constellation.

Interpretation: - Indicates the loyalty of each group - C3 (center) should have the highest turnover (transit zone) - C1/C5 (radicals) should have the lowest turnover (retention zones)


📈 GLOBAL METRICS

opinion_entropyOpinion Entropy

Measures the diversity/dispersion of opinions in the population.

Interpretation: - Maximum (~4.32 for 20 bins): Opinions uniformly distributed - Minimum (0): Everyone has the same opinion - Increasing: Diversification, polarization? - Decreasing: Emerging consensus


center_volatilityCenter Volatility

Measures the instability of the center's (C3) average opinion with exponential smoothing.

Interpretation: - High: Center "floats" a lot, moderate instability - Low: Stable center, system anchor point - May predict tipping points: high center volatility often precedes regime changes


📊 SYNTHESIS – WHAT THESE MEASURES REVEAL TOGETHER

| Measure Group | Reveals | Example Interpretation | |---------------|---------|------------------------| | Fractals | System global state | Criticality = system "at the edge of chaos", optimal for propagation | | Sizes | Ideological distribution | C3 > C1+C5? Centrist system. C1+C5 > C3? Polarization | | Average opinions | Group positioning | C1 moving toward 0? Deradicalization trend | | Prevalences | Engagement intensity | C1 and C5 with low prevalence? Weak radicals | | Polarizations | Internal cohesion | Highly polarized C3? Center splitting | | Turnovers | Fluidity | High C3 turnover? Center is a "crossroads" | | Entropy | Global diversity | Decreasing entropy? Emerging consensus | | Volatility | Center stability | High volatility before crisis? |


🔍 What the Fractal Variable Reveals in Conjunction with Other Variables

The fractal variable (classically calculated as ln(total)/ln(change) or according to the meme-adapted formula) is the original complexity measure of your model. Its conjunction with the new metrics reveals deep dimensions of the system.


📊 JOINT INTERPRETATION MATRIX

1. Fractal + Fractal Dimensions → Nature of Complexity

| Combination | Interpretation | Icon | |-------------|----------------|------| | fractal ≈ topological_dim ≈ dynamic_dim | Unified complexity – The process, structure, and dynamics share the same fractal signature. Perfect harmony. | 🎯 | | fractal > topological_dim | Process more complex than structure – Interactions create complexity that exceeds what the network can explain. | 📈 | | fractal < topological_dim | Underutilized structure – The network could support more complexity than what actually occurs. | 📉 |


2. Fractal + Criticality_gap → Position on the Criticality-Complexity Axis

| fractal | criticality_gap | Interpretation | |-----------|-------------------|----------------| | High (> 1.8) | Low (< 0.2) | Complex criticality – Very rich AND perfectly organized system. Ideal for emergence. | | High (> 1.8) | High (> 0.5) | Disorganized complexity – Rich but decoupled, potentially chaotic. | | Low (< 1.2) | Low (< 0.2) | Ordered simplicity – Simple but well-organized system. Stable, predictable. | | Low (< 1.2) | High (> 0.5) | Dysfunctional simplicity – Simple AND disorganized. Inefficient. |


3. Fractal + Global Turnover → Stability vs. Renewal

  • High fractal + High turnover: Complexity maintained by constant renewal. "Living", creative but unstable system.
  • High fractal + Low turnover: Complexity maintained by deep interactions between stable actors. "Mature", structured system.
  • Low fractal + High turnover: Simple but very fluid system. Possible transition or exploration phase.
  • Low fractal + Low turnover: Frozen, sclerotic system. Risk of extinction.

4. Fractal + Opinion Entropy → Diversity vs. Complexity

| Case | Interpretation | |------|----------------| | fractal ↑ & entropy ↑ | Interaction complexity generates opinion diversity. This is an exploratory system, rich in possibilities. | | fractal ↑ & entropy ↓ | Increasing complexity but converging opinions. Possible emerging consensus in a rich system. | | fractal ↓ & entropy ↑ | Simple system but diverse opinions. May indicate a disintegration phase or individualism. | | fractal ↓ & entropy ↓ | Simplification and convergence. Crystallization or "social freezing". |


5. Fractal + Center Volatility → Pivot Stability

  • High fractal + High volatility: Complex system with an unstable center. Tipping risk – a small perturbation could flip the entire system.
  • High fractal + Low volatility: Complexity with a stable center. Resilience – the system can absorb shocks without losing balance.
  • Low fractal + High volatility: Simple system but unstable center. Immature – may evolve rapidly toward more complexity or collapse.
  • Low fractal + Low volatility: Simple and stable system. Static equilibrium – predictable but without innovation potential.

6. Fractal + Constellation Sizes → Ideological Architecture

| Observation | Interpretation | |-------------|----------------| | fractal ↑ & c3_size ↑ | Complexity emerges from a large and active center. Moderates drive the dynamics. | | fractal ↑ & c1_size + c5_size ↑ | Complexity driven by radicals. Polarization as a source of interactional richness. | | fractal ↓ & c3_size ↑ | Simplicity associated with a dominant center. Soft consensus, low tension. | | fractal ↓ & c1_size + c5_size ↑ | Simplicity but polarization. Frozen conflict – separated radicals without complex interactions. |


7. Fractal + Internal Polarization → Group Cohesion

  • High fractal + Low internal polarization: Homogeneous groups interacting complexly. Coherent mosaic.
  • High fractal + High internal polarization: Heterogeneous groups creating complexity through internal tensions. Explosive cocktail.
  • Low fractal + Low internal polarization: Homogeneous isolated groups. Stable segregation.
  • Low fractal + High internal polarization: Divided groups but few interactions. Latent tensions.

🎯 SYSTEM TYPOLOGY (COMPLETE DIAGNOSIS)

By cross-referencing fractal with other variables, you can diagnose your system's regime:

| Type | Fractal | Criticality | Turnover | Entropy | Volatility | Interpretation | |------|---------|-------------|----------|---------|------------|----------------| | 🌟 EMERGENT CRITICAL | High | Low gap | Moderate | High | Moderate | System at the edge of chaos, rich and diverse, ideal for social innovation | | 🏛️ MATURE STRUCTURED | High | Low gap | Low | Moderate | Low | Crystallized complexity, well-organized but inflexible society | | 🌪️ TURBULENT | High | High gap | High | High | High | Rich but disorganized, risk of collapse or transition | | 🧊 GLACIAL | Low | Low gap | Low | Low | Low | Frozen system, total consensus, absence of dynamics | | 🔥 PRE-ERUPTIVE | Moderate | Moderate gap | High | Moderate | High | Highly volatile center, warning signs of tipping | | 💥 EXPLOSIVE | Variable | Variable | Very high | Variable | Very high | Crisis underway, everything changing rapidly |


📈 CONCRETE ANALYSIS EXAMPLE

Imagine a simulation with:

fractal = 1.85 topologicaldim = 1.72 dynamicdim = 1.78 criticalitygap = 0.035 turnoverglobal = 12.5 opinionentropy = 3.95 centervolatility = 0.42 c3size = 280, c1size = 110, c5size = 110 c3volatility = 0.58

Diagnosis: - fractal high (1.85) and very low gap → Perfect criticality - High turnover (12.5%) → Great fluidity - High entropy (3.95/4.32) → Maximum diversity - Large center (280/500) but highly volatile (0.58) → Unstable center driving dynamics

Conclusion: System at the peak of criticality, extremely rich and diverse, with a very active "floating" center generating complexity. This is the ideal regime for observing emergent phenomena, rapid transitions, and great adaptability. The slightest perturbation can cascade throughout the entire system.


🔑 IN SUMMARY

The fractal variable is your complexity compass. By cross-referencing it with:

  • Fractal dimensions → you see whether this complexity is structural, dynamic, or both
  • Criticality gap → you see whether this complexity is organized or chaotic
  • Turnover → you see whether it's maintained by renewal or stability
  • Entropy → you see whether it generates diversity or consensus
  • Volatility → you see whether it rests on a stable or unstable pivot
  • Constellations → you see who carries this complexity (center, radicals, etc.)

It is the ultimate integrator that gives meaning to all other measures. Without it, you have indicators; with it, you have systemic understanding.


CREDITS

  • Original concept: Public Opinion Research Group (GROP.CA)
  • NetLogo implementation & enhancements: Pierre-Alain Cotnoir (2015–2026)
  • AI-assisted design: GPT-5, Copilot & DeepSeek (2024–2026)
  • Contact: pacotnoir@gmail.com

Comments and Questions

Please start the discussion about this model! (You'll first need to log in.)

Click to Run Model

extensions [ sound nw ]

globals [
  ;; core
  min-prevalence
  max-prevalence
  memes-per-change
  meta-influencers-right
  meta-influencers-left
  inject-tick tick-event
  iter change total inversion try major fractale
  ordonnee abscisse profondeur network-animation?
  list_data file-in in_data repet_data 
  links-dead links-create meta-agents meta-links meta-create Interactions %Major
  initial-turtles-data  ;; Liste pour stocker l'état initial de toutes les tortues
  initial-tick-loaded   ;; Le tick qui a été chargé
  initial-turtles-data-copy  ;; Une copie séparée pour préserver l'original
  simple-initial-data  ;; Pour stocker les données initiales du format simple
  simple-import-mode   ;; Pour savoir si on est en mode simple
  file-imported?  ;; Indique si un fichier a été importé
  import-format
  ;; CSV
  csv-file-stats
  csv-file-values
  csv-buffer
  csv-open?
  setup?
  ;; import Values CSV
  list_values_data
  values_file_in
  values_sep
  
  ;; Mode d'importation
  import-mode
  initial-tick-state

  ;; FRACTAL
  topological-dimension
  dynamic-dimension
  criticality-gap
  criticality-history
  box-sizes
  network-snapshots
  cascade-sizes
  last-cascade-id

  ;; CONSTELLATIONS
  constellation-1-size constellation-2-size constellation-3-size 
  constellation-4-size constellation-5-size
  constellation-1-opinion-avg constellation-2-opinion-avg 
  constellation-3-opinion-avg constellation-4-opinion-avg constellation-5-opinion-avg
  constellation-1-prev-avg constellation-2-prev-avg 
  constellation-3-prev-avg constellation-4-prev-avg constellation-5-prev-avg
  constellation-1-polarization constellation-2-polarization 
  constellation-3-polarization constellation-4-polarization constellation-5-polarization
  turnover-global turnover-c1 turnover-c2 turnover-c3 turnover-c4 turnover-c5
  opinion-entropy
  center-volatility
  prev-constellation-list
  last-center-opinion
  c-opinion-extreme
  c-opinion-moderate
  c-prevalence-high
  c-prevalence-low

  ;; LYAPUNOV
  lyapunov-exponent
  perturbation-history
  reference-trajectory
  perturbed-trajectory
  divergence-history
]

turtles-own [
  meta-type   ;; "structural" ou "dynamic"
  opinion
  prevalence
  agent-type
  influence
  opinion-previous
  influence-previous
  x3d y3d z3d
  meme-plus
  meme-minus
  meme-plus-w
  meme-minus-w
  old-opinion
  proposed-opinion
  tx-bonus
  cascade-id
  constellation-prev
  my-neighbors
 pos-x
 pos-y
 pos-z
]

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; LOCALE / FORMAT HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to-report col-sep
  ifelse locale-format = "FR" [ report ";" ] [ report "," ]
end 

to-report replace-all [s old new]
  let out s
  while [ position old out != false ] [
    let i position old out
    set out (word (substring out 0 i) new (substring out (i + length old) (length out)))
  ]
  report out
end 

to-report fmt [x]
  if x = nobody [ report "" ]
  if not is-number? x [ report (word x) ]
  let s (word "" x)
  if locale-format = "FR" [
    set s replace-all s "." ","
  ]
  report s
end 

to-report join-cols [lst]
  let sep col-sep
  let out ""
  foreach lst [ val ->
    let s (word "" val)
    set out ifelse-value (out = "")
      [ s ]
      [ (word out sep s) ]
  ]
  report out
end 

to-report split-by [s sep]
  let parts []
  let rest s
  while [ position sep rest != false ] [
    let i position sep rest
    set parts lput (substring rest 0 i) parts
    set rest substring rest (i + length sep) (length rest)
  ]
  set parts lput rest parts
  report parts
end 

to-report to-number-locale [s sep]
  if s = "" [ report 0 ]
  let x s
  if sep = ";" [
    set x replace-all x "," "."
  ]
  set x replace-all x " " ""
  report read-from-string x
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CONFIG IMPORT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to import-config-from-stats-csv
  let f user-file
  if f = false [ stop ]
  file-close-all
  let cfg-sep ";"
  carefully [
    file-open f
    while [not file-at-end?] [
      let line file-read-line
      if line != "" [
        if (length line >= 7) [
          if (substring line 0 7 = "CONFIG,") [ set cfg-sep "," ]
          if (substring line 0 7 = "CONFIG;") [ set cfg-sep ";" ]
        ]
        if (length line >= 12) [
          if (substring line 0 12 = "CONFIG_BEGIN,") [ set cfg-sep "," ]
          if (substring line 0 12 = "CONFIG_BEGIN;") [ set cfg-sep ";" ]
        ]
        if (length line >= 10) and (substring line 0 10 = "CONFIG_END") [
          file-close
          user-message "Configuration loaded from Statistics CSV. Press SETUP to rebuild."
          stop
        ]
        let prefix (word "CONFIG" cfg-sep)
        if (length line >= length prefix) and (substring line 0 (length prefix) = prefix) [
          let cols split-config-line line cfg-sep
          if length cols >= 3 [
            let p item 1 cols
            let v item 2 cols
            apply-config-param p v
          ]
        ]
      ]
    ]
    file-close
    user-message "No CONFIG_END marker found. Config rows applied."
  ] [
    file-close-all
    user-message "Config import error: could not read or parse the file."
  ]
end 

to-report split-config-line [s sep]
  let parts []
  let rest s
  while [ position sep rest != false ] [
    let i position sep rest
    set parts lput (substring rest 0 i) parts
    set rest substring rest (i + length sep) (length rest)
  ]
  set parts lput rest parts
  report parts
end 

to-report trim-spaces [s]
  let out s
  while [ (length out > 0) and (substring out 0 1 = " ") ] [
    set out substring out 1 (length out)
  ]
  while [ (length out > 0) and (substring out (length out - 1) (length out) = " ") ] [
    set out substring out 0 (length out - 1)
  ]
  report out
end 

to-report maybe-number-fr [s]
  let x trim-spaces s
  if x = "" [ report nobody ]
  set x replace-all x " " ""
  set x replace-all x "," "."
  let r nobody
  carefully [
    set r read-from-string x
  ] [
    set r nobody
  ]
  if is-number? r [ report r ]
  report nobody
end 

to apply-config-param [param value]
  let p trim-spaces param
  let v trim-spaces value
  if p = "meta-type-definition" [ stop ]
  if v = "true"  [ run (word "set " p " true")  stop ]
  if v = "false" [ run (word "set " p " false") stop ]
  let n maybe-number-fr v
  if n != nobody [
    run (word "set " p " " n)
    stop
  ]
  let vv replace-all v "\"" "'"
  run (word "set " p " \"" vv "\"")
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SETUP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to setup
set csv-open? false
set csv-buffer []

  clear-all
  set file-imported? false
  set repet_data false
  set iter 0
  set min-prevalence 0
  set max-prevalence 99
  set-default-shape turtles "person"
  set try 1
  set inject-tick inject-base
  set major 0
  set setup? true

  if not is-string? locale-format [ set locale-format "FR" ]
  if (locale-format != "FR" and locale-format != "EN") [ set locale-format "FR" ]

  if not is-number? event-init [ set event-init 50 ]
  set tick-event event-init

  set links-dead 0
  set links-create 0
  set meta-create 0
  set meta-agents 0
  set change 0
  set total 0
  set inversion 0
  set fractale 0

  if (vary-influence = true) or (meta-ok = true) [ set meta-links meta-min ]

  if not is-boolean? csv-export [ set csv-export false ]
  if (not is-string? csv-basename) or (csv-basename = "") [ set csv-basename "run" ]
  if not is-string? csv-mode [ set csv-mode "Statistics" ]
  if (csv-mode != "Statistics" and csv-mode != "Values") [
    set csv-mode "Statistics"
  ]
  if not is-number? csv-values-step  [ set csv-values-step 10 ]
  if csv-values-step < 1 [ set csv-values-step 1 ]
  if not is-number? csv-values-start [ set csv-values-start 0 ]
  if csv-values-start < 0 [ set csv-values-start 0 ]

  set csv-file-stats ""
  set csv-file-values ""
  set csv-buffer []
  set csv-open? false

  set list_values_data []
  set values_file_in false
  set values_sep ";"
  set import-mode "simple"
  set initial-tick-state 1

  if not is-boolean? inject-metas-only [ set inject-metas-only false ]

  if (not is-string? group-impact-mode) [ set group-impact-mode "all" ]
  if (not is-number? group-k) [ set group-k 10 ]
  if (not is-number? group-impact-weight) [ set group-impact-weight 0.5 ]
  if (not is-number? group-impact-alpha) [ set group-impact-alpha 1.0 ]

  if not is-boolean? show-links [ set show-links false ]
  if not is-boolean? metablock  [ set metablock false ]

  if (not is-number? prevalence-weight) [ set prevalence-weight 1.5 ]
  if (not is-number? adoption-floor)    [ set adoption-floor 0.02 ]
  if (not is-number? bridge-prob)       [ set bridge-prob 0.10 ]

  if not is-number? reward-step       [ set reward-step 0.05 ]
  if not is-number? reward-cap        [ set reward-cap  0.50 ]
  if not is-string? reward-scope      [ set reward-scope "both" ]
  if not is-number? reward-prev-delta [ set reward-prev-delta 0 ]
  if not is-number? reward-decay      [ set reward-decay 0 ]

  if not is-boolean? use-memes?    [ set use-memes? false ]
  if not is-number? meme-max       [ set meme-max 100 ]
  if not is-number? meme-gain      [ set meme-gain 1.0 ]
  if not is-number? meme-anti-leak [ set meme-anti-leak 0.0 ]
  if not is-number? meme-decay     [ set meme-decay 0.0 ]

  if not is-number? meme-weight-mean [ set meme-weight-mean 1.0 ]
  if not is-number? meme-weight-sd   [ set meme-weight-sd 0.0 ]
  if not is-number? meme-weight-min  [ set meme-weight-min 0.05 ]
  if not is-number? meme-weight-max  [ set meme-weight-max 5.0 ]

  if not is-boolean? auto_inject?   [ set auto_inject? false ]
  if not is-boolean? repeat-inject? [ set repeat-inject? false ]
  if not is-number? inject-tick     [ set inject-tick 50 ]
  if not is-number? inject-pace     [ set inject-pace 50 ]

  if not is-string? inject-sign     [ set inject-sign "plus" ]
  if not is-number? inject-amount   [ set inject-amount 1 ]
  if not is-number? inject-weight   [ set inject-weight 1.0 ]
  if not is-number? inject-prob-max [ set inject-prob-max 1.0 ]

  if not is-number? inject-low_meme  [ set inject-low_meme -1.0 ]
  if not is-number? inject-high_meme [ set inject-high_meme  1.0 ]
  if not is-number? inject-low-prev  [ set inject-low-prev  0.0 ]
  if not is-number? inject-high-prev [ set inject-high-prev 99.0 ]

  set-background-black

  set topological-dimension 0
  set dynamic-dimension 0
  set criticality-gap 0
  set criticality-history []
  set box-sizes [0.5 0.25 0.125 0.0625 0.03125 0.015625]
  set network-snapshots []
  set cascade-sizes []
  set last-cascade-id 0

  set c-opinion-extreme 0.6
  set c-opinion-moderate 0.2
  set c-prevalence-high 50
  set c-prevalence-low 30

  set constellation-1-size 0
  set constellation-2-size 0
  set constellation-3-size 0
  set constellation-4-size 0
  set constellation-5-size 0

  set constellation-1-opinion-avg 0
  set constellation-2-opinion-avg 0
  set constellation-3-opinion-avg 0
  set constellation-4-opinion-avg 0
  set constellation-5-opinion-avg 0

  set constellation-1-prev-avg 0
  set constellation-2-prev-avg 0
  set constellation-3-prev-avg 0
  set constellation-4-prev-avg 0
  set constellation-5-prev-avg 0

  set constellation-1-polarization 0
  set constellation-2-polarization 0
  set constellation-3-polarization 0
  set constellation-4-polarization 0
  set constellation-5-polarization 0

  set turnover-global 0
  set turnover-c1 0
  set turnover-c2 0
  set turnover-c3 0
  set turnover-c4 0
  set turnover-c5 0

  set opinion-entropy 0
  set center-volatility 0
  set last-center-opinion 0
  set prev-constellation-list []

  set lyapunov-exponent 0
  set perturbation-history []
  set divergence-history []
  set reference-trajectory []
  set perturbed-trajectory []

  create
  output-headers

  ask turtles [
    set constellation-prev get-constellation self
    set prev-constellation-list lput (list who 0) prev-constellation-list
  ]

  capture-network-snapshot
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CREATE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to create
    if not file-imported? [create-turtles pop / 2 [
    set agent-type "Right side"
    set opinion random-float 1
    set color blue
    set prevalence random-float (opinion * 100)
    set influence random-float 1
    set opinion-previous opinion
    set influence-previous influence
    set tx-bonus 0
    set cascade-id 0
    set constellation-prev 0
    set meta-type "none" 
    init-memes-from-state
    update-3d self
  ]

  create-turtles pop / 2 [
    set agent-type "Left side"
    set opinion (random-float 1 - 1)
    set color red
    set prevalence random-float (abs opinion * 100)
    set influence random-float 1
    set opinion-previous opinion
    set influence-previous influence
    set tx-bonus 0
    set cascade-id 0
    set constellation-prev 0
    set meta-type "none"   
    init-memes-from-state
    update-3d self
  ]]

  reset-ticks
  influencers

  set total 0
  set change 0
  set Interactions 0
  set %Major 0

  update-networks
  recolor-links
  apply-link-visibility
end 

to output-headers
  if output = "Statistics" [
    output-print join-cols (list
      "Try" "Iter"
      "Global Opinion"
      "Right side Opinion" "Left side Opinion"
      "Right side Prevalence" "Left side Prevalence"
      "Right side Influence" "Left side Influence"
      "Left %" "Right %"
      "Links-Remove" "Links-Create"
      "Inversion %" "change" "total" "fractal"
    )
  ]
  if output = "Values" [
    output-print join-cols (list
      "Try" "Ticks" "Agents"
      "Prevalence" "Opinion" "Influence"
      "meme plus" "meme minus"
      "meme plus w" "meme minus w"
    )
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; META-INFLUENCERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to influencers
  if meta-mode = "Pourcent" [
    if meta-influencers-selection = "All" [
      let k round (count turtles * meta-influencers / 100)
      let candidates turtles with [(prevalence >= prev-low and prevalence <= prev-high)]
      if k > 0 and any? candidates [
        let n-to-take min (list k count candidates)
        ask up-to-n-of n-to-take candidates [
          set influence 1
          set color yellow
          set meta-agents meta-agents + 1
          set meta-type "structural"
        ]
      ]
    ]

    if meta-influencers-selection = "Right side" [
      let k round (count turtles * meta-influencers / 100)
      let candidates turtles with [opinion > 0 and (prevalence >= prev-low and prevalence <= prev-high)]
      if k > 0 and any? candidates [
        let n-to-take min (list k count candidates)
        ask up-to-n-of n-to-take candidates [
          set influence 1
          set color yellow
          set meta-agents meta-agents + 1
          set meta-type "structural"
        ]
      ]
    ]

    if meta-influencers-selection = "Left side" [
      let k round (count turtles * meta-influencers / 100)
      let candidates turtles with [opinion < 0 and (prevalence >= prev-low and prevalence <= prev-high)]
      if k > 0 and any? candidates [
        let n-to-take min (list k count candidates)
        ask up-to-n-of n-to-take candidates [
          set influence 1
          set color yellow
          set meta-agents meta-agents + 1
          set meta-type "structural"
        ]
      ]
    ]
  ]

  if meta-mode = "Nombre" [
    if meta-influencers-selection = "All" [
      let k meta-influencers
      let candidates turtles with [(prevalence >= prev-low and prevalence <= prev-high)]
      if k > 0 and any? candidates [
        let n-to-take min (list k count candidates)
        ask up-to-n-of n-to-take candidates [
          set influence 1
          set color yellow
          set meta-agents meta-agents + 1
          set meta-type "structural"
        ]
      ]
    ]

    if meta-influencers-selection = "Right side" [
      let candidates turtles with [opinion > 0 and (prevalence >= prev-low and prevalence <= prev-high)]
      let k meta-influencers
      if k > 0 and any? candidates [
        let n-to-take min (list k count candidates)
        ask up-to-n-of n-to-take candidates [
          set influence 1
          set color yellow
          set meta-agents meta-agents + 1
          set meta-type "structural"
        ]
      ]
    ]

    if meta-influencers-selection = "Left side" [
      let candidates turtles with [opinion < 0 and (prevalence >= prev-low and prevalence <= prev-high)]
      let k meta-influencers
      if k > 0 and any? candidates [
        let n-to-take min (list k count candidates)
        ask up-to-n-of n-to-take candidates [
          set influence 1
          set color yellow
          set meta-agents meta-agents + 1
          set meta-type "structural"
        ]
      ]
    ]
  ]
end 

to-report meta?
  report (color = yellow) or (influence = 1)
end 

to maybe-set-opinion [ new-op ]
  let old-op opinion
  let bounded-op max list -1 min list 1 new-op
  if metablock and meta? and (sign old-op != sign bounded-op) [
    let mag max list (abs old-op) (abs bounded-op)
    set opinion (sign old-op) * mag
    stop
  ]
  set opinion bounded-op
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CSV
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to-report csv-filename [mode-tag]
  report (word csv-basename "-" try "-" mode-tag "-" locale-format ".csv")
end 

to csv-begin
  set csv-buffer []
  set csv-open? true

  ;; --- Mode STATISTICS ---
  if csv-mode = "Statistics" [
    set csv-file-stats csv-filename "stats"
    if file-exists? csv-file-stats [
      carefully [ file-delete csv-file-stats ] [
        set csv-file-stats (word csv-file-stats "_locked_" (random 1000000))
      ]
    ]
    file-open csv-file-stats
 
  set csv-buffer lput (join-cols (list
    "try" "iter" "tick"
    "left_pct" "right_pct"
    "avg_opinion"
    "med_op_right" "med_op_left"
    "med_prev_right" "med_prev_left"
    "med_infl_right" "med_infl_left"
    "links_remove" "links_create" 
    "bridge_links"
    "inversion_pct" "change" "total" "fractal" "major"
    "interactions_per_iter" "majority_pct" "interactions_per_inversion"
    "meta_links" "meta_agents"
    "mean_prevalence" "median_prevalence" "median_opinion"
    "mean_abs_opinion"
    "mean_meme_stock" "mean_meme_derived_opinion"
    "mean_polarity_index" "meme_saturation_pct"
    "right_meme_polarization" "left_meme_polarization"
    "opinion_meme_gap"
    "ideologization_index"
    "ideologization_opinion_based"
    "topological_dim" "dynamic_dim" "criticality_gap" "Status"
    "c1_size" "c2_size" "c3_size" "c4_size" "c5_size"
    "c1_opinion_avg" "c2_opinion_avg" "c3_opinion_avg" "c4_opinion_avg" "c5_opinion_avg"
    "c1_prev_avg" "c2_prev_avg" "c3_prev_avg" "c4_prev_avg" "c5_prev_avg"
    "c1_polarization" "c2_polarization" "c3_polarization" "c4_polarization" "c5_polarization"
    "turnover_global" "turnover_c1" "turnover_c2" "turnover_c3" "turnover_c4" "turnover_c5"
    "opinion_entropy" "center_volatility" "regime_code"
    "lyapunov_exponent")) csv-buffer
 ]
  ;; --- Mode VALUES ---
  if csv-mode = "Values" [
    set csv-file-values csv-filename "values"
    if file-exists? csv-file-values [
      carefully [ file-delete csv-file-values ] [
        set csv-file-values (word csv-file-values "_locked_" (random 1000000))
      ]
    ]

    ;; En-têtes du fichier Values
    set csv-buffer lput (join-cols (list
      "try" "tick" "agent"
      "prevalence" "opinion" "influence"
      "meme_plus" "meme_minus"
      "meme_plus_w" "meme_minus_w"
      "meta-type"
      "neighbors"
    )) csv-buffer
  ]
end 

to ensure-csv-ready
  if not csv-export [ stop ]
  if not csv-open? [ csv-begin ]
end 

to csv-row-statistics
  let sys-status system-status
  let avg-opinion mean [opinion] of turtles
  let opR safe-median (turtles with [opinion >= 0]) "opinion"
  let opL safe-median (turtles with [opinion < 0])  "opinion"
  let prevR (safe-median (turtles with [opinion >= 0]) "prevalence") / 100
  let prevL (safe-median (turtles with [opinion < 0])  "prevalence") / 100
  let inflR safe-median (turtles with [opinion >= 0]) "influence"
  let inflL safe-median (turtles with [opinion < 0])  "influence"
  let leftpct  (count turtles with [opinion < 0])  / (pop / 100)
  let rightpct (count turtles with [opinion >= 0]) / (pop / 100)
  let bridge_links count links with [ (sign [opinion] of end1) != (sign [opinion] of end2) ]
  let interactions-per-iter ifelse-value (iter > 0) [ total / iter ] [ 0 ]
  let majority-pct %Major
  let interactions-per-inversion ifelse-value (change > 0) [ total / change ] [ 0 ]
  let mean-prevalence ifelse-value (any? turtles) [ mean [prevalence] of turtles ] [ 0 ]
  let median-prevalence safe-median turtles "prevalence"
  let median-opinion safe-median turtles "opinion"
  let mean-abs-opinion ifelse-value (any? turtles) [ mean [abs opinion] of turtles ] [ 0 ]
  let mean-meme-stock ifelse-value (any? turtles) [ mean [meme-plus + meme-minus] of turtles ] [ 0 ]
  let mean-meme-derived-opinion ifelse-value (any? turtles) [
    mean [
      ifelse-value ((meme-plus-w + meme-minus-w) > 0)
        [ (meme-plus-w - meme-minus-w) / (meme-plus-w + meme-minus-w) ]
        [ 0 ]
    ] of turtles
  ] [ 0 ]
  let mean-pol-index mean-polarity-index
  let meme-sat meme-saturation-pct
  let right-den sum [meme-plus + meme-minus] of turtles with [opinion >= 0]
  let right-num (sum [meme-plus] of turtles with [opinion >= 0]) - (sum [meme-minus] of turtles with [opinion >= 0])
  let right-meme-pol ifelse-value (right-den > 0) [ right-num / right-den ] [ 0 ]
  let left-den sum [meme-plus + meme-minus] of turtles with [opinion < 0]
  let left-num (sum [meme-plus] of turtles with [opinion < 0]) - (sum [meme-minus] of turtles with [opinion < 0])
  let left-meme-pol ifelse-value (left-den > 0) [ left-num / left-den ] [ 0 ]
  let opinion-meme-gap ifelse-value (any? turtles) [
    mean [
      abs (opinion - (ifelse-value ((meme-plus-w + meme-minus-w) > 0)
        [ (meme-plus-w - meme-minus-w) / (meme-plus-w + meme-minus-w) ]
        [ 0 ]))
    ] of turtles
  ] [ 0 ]
  let ideologization-index ifelse-value (any? turtles) [ ideologization-meme-based ] [ 0 ]
  let ideologization-opinion-based-val ifelse-value (any? turtles) [ ideologization-opinion-based ] [ 0 ]
  let d_top ifelse-value (topological-dimension > 0) [ topological-dimension ] [ 0 ]
  let d_dyn ifelse-value (dynamic-dimension > 0) [ dynamic-dimension ] [ 0 ]
  let c_gap ifelse-value (criticality-gap > 0) [ criticality-gap ] [ 0 ]
  let regime-code system-regime-code

  set csv-buffer lput (join-cols (list
    fmt try
    fmt iter
    fmt ticks
    fmt leftpct
    fmt rightpct
    fmt avg-opinion
    fmt opR
    fmt opL
    fmt prevR
    fmt prevL
    fmt inflR
    fmt inflL
    fmt links-dead
    fmt links-create
    fmt bridge_links
    fmt inversion
    fmt change
    fmt total
    fmt fractale
    fmt major
    fmt interactions-per-iter
    fmt majority-pct
    fmt interactions-per-inversion
    fmt meta-links
    fmt meta-agents
    fmt mean-prevalence
    fmt median-prevalence
    fmt median-opinion
    fmt mean-abs-opinion
    fmt mean-meme-stock
    fmt mean-meme-derived-opinion
    fmt mean-pol-index
    fmt meme-sat
    fmt right-meme-pol
    fmt left-meme-pol
    fmt opinion-meme-gap
    fmt ideologization-index
    fmt ideologization-opinion-based-val
    fmt d_top
    fmt d_dyn
    fmt c_gap
    fmt sys-status
    fmt constellation-1-size
    fmt constellation-2-size
    fmt constellation-3-size
    fmt constellation-4-size
    fmt constellation-5-size
    fmt constellation-1-opinion-avg
    fmt constellation-2-opinion-avg
    fmt constellation-3-opinion-avg
    fmt constellation-4-opinion-avg
    fmt constellation-5-opinion-avg
    fmt constellation-1-prev-avg
    fmt constellation-2-prev-avg
    fmt constellation-3-prev-avg
    fmt constellation-4-prev-avg
    fmt constellation-5-prev-avg
    fmt constellation-1-polarization
    fmt constellation-2-polarization
    fmt constellation-3-polarization
    fmt constellation-4-polarization
    fmt constellation-5-polarization
    fmt turnover-global
    fmt turnover-c1
    fmt turnover-c2
    fmt turnover-c3
    fmt turnover-c4
    fmt turnover-c5
    fmt opinion-entropy
    fmt center-volatility
    fmt regime-code
    fmt lyapunov-exponent
  )) csv-buffer
end 

to csv-add-config-snapshot
  if not csv-export [ stop ]
  if csv-mode != "Statistics" [ stop ]
  if not csv-open? [ stop ]
  set csv-buffer lput "" csv-buffer
  set csv-buffer lput (join-cols (list "CONFIG_BEGIN" (word "try=" try) (word "last_tick=" ticks))) csv-buffer
  let rows (list
    (list "pop" pop)
    (list "nb_try" nb_try)
    (list "max_iter" max_iter)
    (list "threshold" threshold)
    (list "cumulative" cumulative)
    (list "refresh" refresh)
    (list "output" output)
    (list "locale-format" locale-format)
    (list "csv-basename" csv-basename)
    (list "csv-mode" csv-mode)
    (list "csv-values-start" csv-values-start)
    (list "csv-values-step" csv-values-step)
    (list "network" network)
    (list "link-removal-threshold" link-removal-threshold)
    (list "link-formation-threshold" link-formation-threshold)
    (list "prob" prob)
    (list "linksdown" linksdown)
    (list "linksup" linksup)
    (list "bridge-prob" bridge-prob)
    (list "show-links" show-links)
    (list "linktick" linktick)
    (list "polarization-factor" polarization-factor)
    (list "prevalence-weight" prevalence-weight)
    (list "adoption-floor" adoption-floor)
    (list "noise" noise)
    (list "mode_prev" mode_prev)
    (list "rate-mod" rate-mod)
    (list "rate-infl" rate-infl)
    (list "group-impact-mode" group-impact-mode)
    (list "group-k" group-k)
    (list "group-impact-weight" group-impact-weight)
    (list "group-impact-alpha" group-impact-alpha)
    (list "reward-step" reward-step)
    (list "reward-cap" reward-cap)
    (list "reward-scope" reward-scope)
    (list "reward-prev-delta" reward-prev-delta)
    (list "reward-decay" reward-decay)
    (list "meta-ok" meta-ok)
    (list "meta-mode" meta-mode)
    (list "meta-influencers-selection" meta-influencers-selection)
    (list "meta-influencers" meta-influencers)
    (list "prev-low" prev-low)
    (list "prev-high" prev-high)
    (list "meta-min" meta-min)
    (list "meta-max" meta-max)
    (list "meta-links" meta-links)
    (list "meta-type-definition" "structural/dynamic/none")
    (list "metablock" metablock)
    (list "vary-influence" vary-influence)
    (list "auto_event" auto_event)
    (list "repeat_event" repeat_event)
    (list "event-init" event-init)
    (list "event-pace" event-pace)
    (list "event-prob-max" event-prob-max)
    (list "event_size" event_size)
    (list "prev_change" prev_change)
    (list "meme_set" meme_set)
    (list "to_left" to_left)
    (list "low_meme" low_meme)
    (list "high_meme" high_meme)
    (list "low-prev" low-prev)
    (list "high-prev" high-prev)
    (list "use-memes?" use-memes?)
    (list "meme-max" meme-max)
    (list "meme-gain" meme-gain)
    (list "meme-anti-leak" meme-anti-leak)
    (list "meme-decay" meme-decay)
    (list "meme-weight-mean" meme-weight-mean)
    (list "meme-weight-sd" meme-weight-sd)
    (list "meme-weight-min" meme-weight-min)
    (list "meme-weight-max" meme-weight-max)
    (list "auto_inject?" auto_inject?)
    (list "repeat-inject?" repeat-inject?)
    (list "inject-metas-only" inject-metas-only)
    (list "inject-tick" inject-tick)
    (list "inject-pace" inject-pace)
    (list "inject-sign" inject-sign)
    (list "inject-amount" inject-amount)
    (list "inject-weight" inject-weight)
    (list "inject-prob-max" inject-prob-max)
    (list "inject-low_meme" inject-low_meme)
    (list "inject-high_meme" inject-high_meme)
    (list "inject-low-prev" inject-low-prev)
    (list "inject-high-prev" inject-high-prev)
    (list "c-opinion-extreme" c-opinion-extreme)
    (list "c-opinion-moderate" c-opinion-moderate)
    (list "c-prevalence-high" c-prevalence-high)
    (list "c-prevalence-low" c-prevalence-low)
  )
  foreach rows [ r ->
    let k item 0 r
    let v item 1 r
    set csv-buffer lput (join-cols (list "CONFIG" (word k) (fmt v))) csv-buffer
  ]
  set csv-buffer lput (join-cols (list "CONFIG_END" (word "try=" try) "")) csv-buffer
end 

to csv-row-values
  if ticks < csv-values-start [ stop ]
  if (ticks mod csv-values-step) != 0 [ stop ]

  foreach sort turtles [ t ->

    ;; Construire la chaîne de voisins "id1|id2|id3"
let neigh-ids [who] of [link-neighbors] of t
let neigh-str ""
foreach neigh-ids [ nid ->
  ifelse neigh-str = "" [
    set neigh-str (word nid)
  ] [
    set neigh-str (word neigh-str "|" nid)
  ]
]


    set csv-buffer lput (join-cols (list
      fmt try
      fmt ticks
      fmt [who] of t
      fmt [prevalence] of t
      fmt [opinion] of t
      fmt [influence] of t
      fmt [meme-plus] of t
      fmt [meme-minus] of t
      fmt [meme-plus-w] of t
      fmt [meme-minus-w] of t
      fmt [meta-type] of t
      neigh-str
    )) csv-buffer
  ]
end 

to csv-flush
  if empty? csv-buffer [ stop ]
  file-close-all
  let target ""
  if csv-mode = "Statistics" [ set target csv-file-stats ]
  if csv-mode = "Values"     [ set target csv-file-values ]
  file-open target
  foreach csv-buffer [ line -> file-print line ]
  file-close
  set csv-buffer []
end 

to csv-end
  if csv-open? [ csv-flush ]
  file-close-all
  set csv-buffer []
  set csv-open? false
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GO - VERSION CORRIGÉE AVEC ENCHAÎNEMENT AUTOMATIQUE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GO - Version avec enchaînement automatique des essais
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to go
  ;; Vérifier s'il y a des tortues
  if not any? turtles or count turtles != pop or setup? = 0 [
    user-message "Please click SETUP first to create the population!"
    stop
  ]
  
  
  ifelse (iter < max_iter) [
    ;; ========================================================================
    ;; DÉROULEMENT D'UN ESSAI
    ;; ========================================================================
    
    ;; Initialisation CSV
    if csv-export [ ensure-csv-ready ]
    if ticks mod 500 = 0 [ diagnose-system ]

    ;; Mise à jour des statistiques
    if iter > 0 [ set Interactions (total / iter) ]
    if iter > 0 [ set %Major (major / iter * 100) ]
    set iter iter + 1
    set meta-create 0

    ;; Événements
    if auto_event [
      if (iter = tick-event) [
        event
        if repeat_event [
          set tick-event (tick-event + event-pace)
        ]
      ]
    ]

    ;; Injection de mèmes
    if auto_inject? [ 
      if (iter = inject-tick) [
        inject-memes
        if repeat-inject? [ 
          set inject-tick (inject-tick + inject-pace)
        ]
      ]
    ]

    ;; Mise à jour des métriques
    if meta-ok = true [ meta ]
    update-opinions
    if network = true [ update-networks ]
    recolor-links
    apply-link-visibility
    capture-network-snapshot

    ;; Calculs périodiques
    if ticks mod 100 = 0 and ticks > 0 [
      compute-fractal-dimensions
      if ticks mod 500 = 0 [
        output-print (word "SYSTEM STATUS at tick " ticks ": " system-status)
      ]
      compute-lyapunov-exponent
    ]

    ;; Suivi des perturbations
    if ticks = 1 [ initialize-perturbation ]
    if ticks > 1 [ update-perturbation ]

    ;; Mise à jour des constellations
    update-constellations

    ;; Sortie statistiques
    if output = "Statistics" [
      let avg-opinion mean [opinion] of turtles
      let positive-opinion safe-median (turtles with [opinion >= 0]) "opinion"
      let negative-opinion safe-median (turtles with [opinion < 0])  "opinion"
      let positive-prevalence (safe-median (turtles with [opinion >= 0]) "prevalence") / 100
      let negative-prevalence (safe-median (turtles with [opinion < 0])  "prevalence") / 100
      let positive-influence safe-median (turtles with [opinion >= 0]) "influence"
      let negative-influence safe-median (turtles with [opinion < 0])  "influence"
      let Left%  (count turtles with [opinion < 0])  / (pop / 100)
      let Right% (count turtles with [opinion >= 0]) / (pop / 100)
      let ti iter

      output-print join-cols (list
        fmt try
        fmt ti
        fmt avg-opinion
        fmt positive-opinion
        fmt negative-opinion
        fmt positive-prevalence
        fmt negative-prevalence
        fmt positive-influence
        fmt negative-influence
        fmt Left%
        fmt Right%
        fmt links-dead
        fmt links-create
        fmt inversion
        fmt change
        fmt total
        fmt fractale
      )
    ]

    ;; Avancer le temps
    tick

    ;; Mise à jour des métriques fractales
    ifelse use-memes? [
      if (change > 1 and iter > 1) [ set fractale (ln total / ln change) ]
    ] [
      if (change > 1 and total > 1) [ set fractale (ln total) / (ln change) ]
    ]

    if (cumulative = false) [
      set change 0
      set total 0
    ]

    colorer

    if (refresh = true) [
      if ticks > 200 [ reset-ticks clear-plot ]
    ]

    if threshold <= (count turtles with [opinion > 0]) / (pop / 100) [
      set major major + 1
    ]

    ;; Écriture CSV
    if csv-export [
      if csv-mode = "Statistics" [ csv-row-statistics ]
      if csv-mode = "Values"     [ csv-row-values ]
    ]

    ;; ========================================================================
    ;; FIN DE L'ESSAI EN COURS
    ;; ========================================================================
  ] [
    ifelse (try < nb_try) [
      ;; ======================================================================
      ;; PRÉPARATION DU PROCHAIN ESSAI
      ;; ======================================================================
      
      ;; TUER LES TURTLES IMMÉDIATEMENT
      clear-turtles
      clear-links
      
      ;; Sauvegarde CSV si nécessaire
      if csv-export [
        if csv-mode = "Statistics" [ csv-add-config-snapshot ]
        csv-end
      ]

      ;; Réinitialisation des variables globales
      set try try + 1
      set major 0
      clear-turtles
      clear-plot
      set change 0
      set total 0
      set fractale 0
      set meta-links meta-min
      set iter 0
      set links-create 0
      set links-dead 0
      set meta-create 0
      set meta-agents 0
      set min-prevalence 0
      set max-prevalence 99
      set tick-event event-init
      set inject-tick inject-base

   ;; Rechargement des données selon le mode d'importation
      if (repet_data = true) [
        if (import-mode = "values") [
          restore-initial-state
          reset-ticks
          ;; NE PAS appeler go ici - le bouton Forever s'en charge
        ]
        if (import-mode = "simple") [
          data-simple
          reset-ticks
          ;; NE PAS appeler go ici - le bouton Forever s'en charge
        ]
      ]

      ;; Création d'une nouvelle population aléatoire (seulement si pas de fichier importé)
      if not file-imported? [
        create
        reset-ticks
      ]

      ;; Création d'une nouvelle population aléatoire
      ;;create
      ;;reset-ticks
      ;;go    ;; <<< LANCEMENT AUTOMATIQUE DU PROCHAIN ESSAI

      ;; ======================================================================
      ;; FIN DE TOUS LES ESSAIS
      ;; ======================================================================
    ] [
      if csv-export [
        if csv-mode = "Statistics" [ csv-add-config-snapshot ]
        csv-end
      ]
      sound:play-note "Tubular Bells" 60 64 1
      stop
    ]
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DATA IMPORT - VERSIONS CORRIGÉES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to in_file
  if setup? = 0 [user-message "Please click SETUP first before loading file!"
    stop]
  in_file_simple
end 

;; CORRECTION de in_file_simple

to in_file_simple
  carefully [
    set file-in user-file
    if (file-in != false) [
      set list_data []
      file-open file-in
      while [not file-at-end?] [
        set list_data sentence list_data (list (list file-read file-read file-read file-read))
      ]
      file-close
      
      ;; Stocker une copie des données initiales
      set simple-initial-data []
      foreach list_data [ row ->
        set simple-initial-data lput (copy-list row) simple-initial-data
      ]
      
      user-message (word "File uploaded (simple format)! " length list_data " lines loaded.")
      set import-mode "simple"
      set in_data true
      set repet_data true
    ]
  ] [
    user-message "File read error"
  ]
  
  set choice_iter 0
  data-simple
  set file-imported? true
end 

;; NOUVELLE PROCÉDURE pour charger et stocker l'état initial
;; CORRECTION de load-and-store-initial-state
;; CORRECTION de load-and-store-initial-state pour s'assurer que les données sont correctes

;; MODIFICATION de load-and-store-initial-state pour vérifier les données chargées
;; CORRECTION de load-and-store-initial-state
;; CORRECTION de load-and-store-initial-state avec copie profonde

to load-and-store-initial-state

  print "STEP 0: Entering load-and-store-initial-state"

  let tick-to-load initial-tick-loaded
  print (word "STEP 1: Tick-to-load = " tick-to-load)

  let initial-rows []
  print "STEP 2: Initial-rows initialized"

  ;; Sélection des lignes correspondant au tick demandé
  foreach list_values_data [ row ->
    if is-list? row [
      let tick-courant to-number-locale (item 1 row) values_sep
      if tick-courant = tick-to-load [
        set initial-rows lput row initial-rows
      ]
    ]
  ]
  print (word "STEP 3: Length initial-rows = " length initial-rows)

  if empty? initial-rows [
    user-message (word "No data found for tick " tick-to-load " in the file!")
    stop
  ]

  ;; Tri par ID d’agent
  print "STEP 4: Start of sorting"
  set initial-rows sort-by
    [ [a b] ->
        (to-number-locale (item 2 a) values_sep) <
        (to-number-locale (item 2 b) values_sep)
    ]
    initial-rows
  print "STEP 5: Sorting completed"

  set initial-turtles-data []
  set initial-turtles-data-copy []
  print "STEP 6: Lists initialized"

  print "STEP A: Start of line processing"

  foreach initial-rows [ row ->

    print "STEP 7: Start of line"

    ;; --- Extraction des valeurs numériques ---
    let prev-val     to-number-locale (item 3 row) values_sep
    let opin-val     to-number-locale (item 4 row) values_sep
    let infl-val     to-number-locale (item 5 row) values_sep
    let mplus-val    to-number-locale (item 6 row) values_sep
    let mminus-val   to-number-locale (item 7 row) values_sep
    let mplusw-val   to-number-locale (item 8 row) values_sep
    let mminusw-val  to-number-locale (item 9 row) values_sep
    let meta-val     item 10 row

    print "STEP 8: Numerical values read"

    ;; --- Parsing des voisins ---
    let neigh-str ""
    if length row > 11 [
      set neigh-str item 11 row
    ]

    let neigh-list []
    if neigh-str != "" [
      let raw split-by neigh-str "|"
      set raw filter [ x -> x != "" ] raw

      foreach raw [ x ->
        let v -1
        carefully [
          set v read-from-string x
        ] [
          set v -1
        ]
        if v >= 0 [
          set neigh-list lput v neigh-list
        ]
      ]
    ]

    print (word "STEP 9: Neighbors read = " neigh-list)

    ;; --- Positions ---
    let posx 0
    let posy 0
    let posz 0

    ;; Format générateur → colonnes 12, 13, 14
    if import-format = "generator" and length row > 14 [
      set posx to-number-locale (item 12 row) values_sep
      set posy to-number-locale (item 13 row) values_sep
      set posz to-number-locale (item 14 row) values_sep
    ]

    ;; --- Construction de agent-data ---
    let agent-data (list
      prev-val
      opin-val
      infl-val
      mplus-val
      mminus-val
      mplusw-val
      mminusw-val
      meta-val
      neigh-list
      posx
      posy
      posz
    )

    set initial-turtles-data lput agent-data initial-turtles-data
    set initial-turtles-data-copy lput (copy-list agent-data) initial-turtles-data-copy

    print "STEP 10: End of line"

  ]

  print "STEP B: End of processing"
end 
;; Network links visualization.

to animate-network-layout
  repeat network-steps [
    layout-spring turtles links 0.3 5 1
    display
  ]
end 


;; Nouvelle fonction pour copier une liste en profondeur
;; Alternative avec la syntaxe ?
;; Correction de la fonction copy-list

to-report copy-list [lst]
  let new-list []
  foreach lst [ un-element ->  ;; Renommé de "item" à "un-element"
    ifelse is-list? un-element [
      set new-list lput (copy-list un-element) new-list
    ] [
      set new-list lput un-element new-list
    ]
  ]
  report new-list
end 


;; NOUVELLE PROCÉDURE pour restaurer l'état initial
;; CORRECTION de restore-initial-state

to restore-initial-state
if setup? = 0 [
    user-message "Please click SETUP first before loading file!"
    stop
  ]
  if not file-imported? [
    user-message "Veuillez d'abord importer un fichier CSV."
    stop
  ]

  clear-turtles
  reset-ticks

  ;; Création des agents
  foreach initial-turtles-data [ agent-data ->

    create-turtles 1 [

      ;; --- Restauration des valeurs numériques brutes ---
      set prevalence     item 0 agent-data
      set opinion        item 1 agent-data
      set influence      item 2 agent-data
      set meme-plus      item 3 agent-data
      set meme-minus     item 4 agent-data
      set meme-plus-w    item 5 agent-data
      set meme-minus-w   item 6 agent-data

      ;; --- Interprétation stricte de meta-type ---
      let mt item 7 agent-data
      ifelse member? mt ["dynamic" "structural"] [
        set meta-type mt
      ] [
        set meta-type "none"
      ]

      ;; --- Voisins et position ---
      set my-neighbors   item 8  agent-data
      set pos-x          item 9  agent-data
      set pos-y          item 10 agent-data
      set pos-z          item 11 agent-data
      setxy pos-x pos-y

      ;; --- Valeurs internes du modèle ---
      set opinion-previous   opinion
      set influence-previous influence
      set tx-bonus           0
      set cascade-id         0
      set constellation-prev 0

      ;; --- Couleur initiale cohérente avec le reste du modèle ---
      ifelse meta-type != "none" or influence = 1 [
        set color yellow
        set meta-agents meta-agents + 1
      ] [
        ifelse opinion < 0 [
          set color red
          set agent-type "Left side"
        ] [
          ifelse opinion > 0 [
            set color blue
            set agent-type "Right side"
          ] [
            set color white
            set agent-type "Center"
          ]
        ]
      ]
    ]
  ]

  ;; Reconstruction des liens
  ask turtles [
    let neigh my-neighbors
    foreach neigh [ n ->
      if n != who [
        if turtle n != nobody [
          create-link-with turtle n
        ]
      ]
    ]
  ]

  user-message "État initial restauré."
end 



;; CORRECTION de in_file_values

to in_file_values
  if setup? = 0 [
    user-message "Please click SETUP first before loading file!"
    stop
  ]

  carefully [
    set values_file_in user-file
    if values_file_in = false [ stop ]

    set list_values_data []
    file-open values_file_in

    if file-at-end? [
      user-message "Empty file"
      file-close
      stop
    ]

    ;; Lire l'en-tête
    let header file-read-line

    ;; Détecter le séparateur
    ifelse (position ";" header != false)
      [ set values_sep ";" ]
      [ set values_sep "," ]

    ;; Lire toutes les lignes
    while [not file-at-end?] [
      let line file-read-line
      if line != "" [
        let cols parse-csv-line line values_sep
        set list_values_data lput cols list_values_data
      ]
    ]
    file-close

    if empty? list_values_data [
      user-message "No data found in file"
      stop
    ]
    
    ;; Détection automatique du format
    let first-row first list_values_data
    let nb-cols length first-row

    if nb-cols = 12 [
      set import-format "simulator"
    ]
    if nb-cols = 15 [
      set import-format "generator"
    ]
    if nb-cols != 12 and nb-cols != 15 [
      user-message (word "Unsupported CSV format: " nb-cols " columns.")
      stop
    ]

    set import-mode "values"
    set repet_data true
    set initial-tick-loaded choice_iter

    user-message (word "File uploaded (values CSV)! "
                       length list_values_data
                       " lines loaded. Using tick "
                       initial-tick-loaded
                       " as initial state.")

    load-and-store-initial-state

  ] [
    user-message (word "Values file read error: " error-message)
    stop
  ]

  set file-imported? true
  user-message "Import done. Now click 'Restaurer état'."
end 

to-report parse-csv-line [line sep]
  let raw (word line)   ;; force conversion propre
  let cols []
  let tmp raw

  while [ tmp != "" ] [
    let pos position sep tmp

    if pos = false [
      set cols lput (trim-string tmp) cols
      set tmp ""
    ]
    if pos != false [
      let token substring tmp 0 pos
      set cols lput (trim-string token) cols
      set tmp substring tmp (pos + 1) length tmp
    ]
  ]

  report cols
end 

to-report trim-string [s]
  report remove "\r" remove "\n" remove " " s
end 

to data
  ifelse import-mode = "values" [
    data-values
  ] [
    data-simple
  ]
end 

;; CORRECTION de data-simple

to data-simple
  clear-turtles
  clear-links
  
  ;; Utiliser les données stockées dans simple-initial-data
  if not is-list? simple-initial-data or empty? simple-initial-data [
    user-message "No simple data loaded! Please import a file first."
    stop
  ]
  
  let tick-to-load choice_iter
  
  ;; Filtrer les données pour le tick demandé
  let filtered-data filter [ row -> first row = tick-to-load ] simple-initial-data
  
  if empty? filtered-data [
    let available remove-duplicates map [row -> first row] simple-initial-data
    user-message (word "No data for tick " tick-to-load ". Available ticks: " available)
    stop
  ]
  
  let n min list pop length filtered-data
  let selected-data sublist filtered-data 0 n
  
  set meta-agents 0
  
  create-turtles n [
    let agent-data item who selected-data
    
    set prevalence item 1 agent-data
    set opinion    item 2 agent-data
    set influence  item 3 agent-data
    
    if influence = 1 [ set meta-agents meta-agents + influence ]
    
    set opinion-previous opinion
    set influence-previous influence
    set tx-bonus 0
    set cascade-id 0
    set constellation-prev 0
    
    ;; Initialiser les mèmes
    init-memes-from-state
    
    ;; Définir la couleur
    ifelse opinion < 0 [
      set color red
      set agent-type "Left side"
    ] [
      ifelse opinion > 0 [
        set color blue
        set agent-type "Right side"
      ] [
        set color white
        set agent-type "Center"
      ]
    ]
    
    if influence = 1 [ set color yellow ]
    
    update-3d self
  ]
  
  ;; Diagnostic
  output-print "=== RESTORE SIMPLE INITIAL STATE ==="
  output-print (word "Total agents: " count turtles)
  output-print (word "Current tick: " ticks)
  
  let left-agents turtles with [opinion < 0]
  let right-agents turtles with [opinion > 0]
  
  output-print (word "Left side (opinion < 0): " count left-agents " agents")
  output-print (word "Right side (opinion > 0): " count right-agents " agents")
  output-print (word "Influencers: " meta-agents " agents")
  
  ;; Afficher des exemples
  output-print "Examples of left agents (opinion < 0):"
  ifelse any? left-agents [
    let sample-size min (list 3 count left-agents)
    let samples sort [who] of n-of sample-size left-agents
    foreach samples [ id ->
      ask turtle id [
        output-print (word "  Agent " who ": opinion=" precision opinion 6 " color=" color)
      ]
    ]
  ] [
    output-print "  NO LEFT AGENTS FOUND!"
  ]
  
  output-print "Examples of right agents (opinion > 0):"
  ifelse any? right-agents [
    let sample-size min (list 3 count right-agents)
    let samples sort [who] of n-of sample-size right-agents
    foreach samples [ id ->
      ask turtle id [
        output-print (word "  Agent " who ": opinion=" precision opinion 6 " color=" color)
      ]
    ]
  ] [
    output-print "  NO RIGHT AGENTS FOUND!"
  ]
  
  output-print "================================"
  
  update-networks
  apply-link-visibility
  recolor-links
  
  set repet_data true
  set import-mode "simple"
  set tick-event event-init
  set inject-tick inject-base
  set iter 0
  reset-ticks
end 

to data-values
  ;; Cette procédure ne fait que restaurer l'état initial stocké
  restore-initial-state
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; UPDATE OPINIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to update-opinions
  ask turtles [
    set opinion-previous opinion
    let target one-of link-neighbors

    if target != nobody [
      let raw-dprev ([prevalence] of target) - prevalence
      if raw-dprev < 1 [ set raw-dprev 0 ]
      let dprev raw-dprev / max-prevalence

      if dprev > 0 [
        let dmem abs(abs(opinion) - abs([opinion] of target))

        let base-prob dprev * prevalence-weight
        let pol-penalty max list adoption-floor (1 - polarization-factor * dmem)
        let p-adopt base-prob * pol-penalty * [influence] of target * (1 + [tx-bonus] of target)

        let sgn-emitter sign ([opinion] of target)
        let gprob group-alignment-effective self sgn-emitter
        let w group-impact-weight
        let alpha group-impact-alpha
        set p-adopt p-adopt * ((1 - w) + (w * (gprob ^ alpha)))

        if p-adopt < 0 [ set p-adopt 0 ]
        if p-adopt > 1 [ set p-adopt 1 ]

        if random-float 1 < p-adopt [
          set old-opinion opinion
          set proposed-opinion [opinion] of target

          ifelse use-memes? [
            transmit-memes target
            recompute-from-memes
          ] [
            maybe-set-opinion proposed-opinion
          ]

          if opinion = old-opinion [ stop ]
          set total total + 1

          if opinion != old-opinion [
            if [cascade-id] of target != 0 [
              set cascade-id [cascade-id] of target
            ]
            if cascade-id = 0 [
              set last-cascade-id last-cascade-id + 1
              set cascade-id last-cascade-id
            ]

            set cascade-sizes lput (list ticks who [opinion] of target cascade-id) cascade-sizes

            if length cascade-sizes > 1000 [
              let start length cascade-sizes - 1000
              set cascade-sizes sublist cascade-sizes start length cascade-sizes
            ]
          ]

          let emitter-sign sign ([opinion] of target)
          let eligible? (reward-scope = "both") or
                        (reward-scope = "left-only"  and emitter-sign < 0) or
                        (reward-scope = "right-only" and emitter-sign >= 0)
          if eligible? [
            ask target [
              set tx-bonus min (list reward-cap (tx-bonus + reward-step))
            ]
          ]

          if reward-prev-delta > 0 [
            set prevalence min (list max-prevalence (prevalence + reward-prev-delta))
          ]

          set influence-previous influence
if vary-influence = true [
  if abs(old-opinion) > abs(opinion) [
    set influence min (list 1 (influence + rate-infl))
    if (influence-previous < 1 and influence = 1) [
      ;; L’agent devient méta dynamique
      set meta-type "dynamic"

      if meta-ok = true [
        if meta-links < meta-max [ set meta-links meta-links + 1 ]
        set meta-agents meta-agents + 1
      ]
      set color yellow
    ]
            ]
            if abs(old-opinion) < abs(opinion) [
              set influence max (list 0 (influence - rate-infl))
              if (influence < influence-previous and influence-previous = 1) [

  ;; Si c’est un méta dynamique → il perd son statut
  if meta-type = "dynamic" [
    set meta-type "none"
    if meta-ok = true [
      set meta-agents meta-agents - 1
      ifelse opinion >= 0 [ set color blue ] [ set color red ]
    ]
  ]

  ;; Si c’est un méta structurel → il reste méta
  if meta-type = "structural" [
    ;; on restaure son influence à 1
    set influence 1
    set color yellow
  ]
]
            ]
          ]

          if (sign old-opinion) != (sign opinion) [
            set change change + 1
          ]

          if change > 0 [
            set memes-per-change (((sum [meme-plus + meme-minus] of turtles) / change) / pop)
          ]
        ]
      ]
    ]

    if mode_prev = true [
      if prevalence > abs opinion * 100 [
        set prevalence prevalence - abs(opinion - opinion-previous) * influence * rate-mod
      ]
      if prevalence < abs opinion * 100 [
        set prevalence prevalence + abs(opinion - opinion-previous) * influence * rate-mod
      ]
      if prevalence < min-prevalence [ set prevalence min-prevalence ]
      if prevalence > max-prevalence [ set prevalence max-prevalence ]
    ]

    if random-float 1 < noise [
      let delta (random-float 0.4 - 0.2)
      maybe-set-opinion (opinion + delta)
    ]

    if use-memes? [ decay-memes ]
    update-3d self

    if (output = "Values") [ compute-statistics ]
  ]

  if reward-decay > 0 [
    ask turtles [ set tx-bonus max (list 0 (tx-bonus - reward-decay)) ]
  ]

  ifelse (total > 0)
    [ set inversion (100 * change / total) ]
    [ set inversion 0 ]

  if ticks mod 1000 = 0 [
    ask turtles [ set cascade-id 0 ]
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CONSTELLATIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to update-constellations
  compute-turnover
  compute-constellation-membership
  compute-constellation-stats
  compute-entropy
  compute-volatility
end 

to compute-constellation-membership
  set constellation-1-size 0
  set constellation-2-size 0
  set constellation-3-size 0
  set constellation-4-size 0
  set constellation-5-size 0

  let total-op-c1 0
  let total-op-c2 0
  let total-op-c3 0
  let total-op-c4 0
  let total-op-c5 0

  let total-prev-c1 0
  let total-prev-c2 0
  let total-prev-c3 0
  let total-prev-c4 0
  let total-prev-c5 0

  ask turtles [
    let const get-constellation self

    if const = 1 [
      set constellation-1-size constellation-1-size + 1
      set total-op-c1 total-op-c1 + opinion
      set total-prev-c1 total-prev-c1 + prevalence
    ]
    if const = 2 [
      set constellation-2-size constellation-2-size + 1
      set total-op-c2 total-op-c2 + opinion
      set total-prev-c2 total-prev-c2 + prevalence
    ]
    if const = 3 [
      set constellation-3-size constellation-3-size + 1
      set total-op-c3 total-op-c3 + opinion
      set total-prev-c3 total-prev-c3 + prevalence
    ]
    if const = 4 [
      set constellation-4-size constellation-4-size + 1
      set total-op-c4 total-op-c4 + opinion
      set total-prev-c4 total-prev-c4 + prevalence
    ]
    if const = 5 [
      set constellation-5-size constellation-5-size + 1
      set total-op-c5 total-op-c5 + opinion
      set total-prev-c5 total-prev-c5 + prevalence
    ]

    set constellation-prev const
  ]

  set constellation-1-opinion-avg ifelse-value (constellation-1-size > 0) [ total-op-c1 / constellation-1-size ] [ 0 ]
  set constellation-2-opinion-avg ifelse-value (constellation-2-size > 0) [ total-op-c2 / constellation-2-size ] [ 0 ]
  set constellation-3-opinion-avg ifelse-value (constellation-3-size > 0) [ total-op-c3 / constellation-3-size ] [ 0 ]
  set constellation-4-opinion-avg ifelse-value (constellation-4-size > 0) [ total-op-c4 / constellation-4-size ] [ 0 ]
  set constellation-5-opinion-avg ifelse-value (constellation-5-size > 0) [ total-op-c5 / constellation-5-size ] [ 0 ]

  set constellation-1-prev-avg ifelse-value (constellation-1-size > 0) [ total-prev-c1 / constellation-1-size ] [ 0 ]
  set constellation-2-prev-avg ifelse-value (constellation-2-size > 0) [ total-prev-c2 / constellation-2-size ] [ 0 ]
  set constellation-3-prev-avg ifelse-value (constellation-3-size > 0) [ total-prev-c3 / constellation-3-size ] [ 0 ]
  set constellation-4-prev-avg ifelse-value (constellation-4-size > 0) [ total-prev-c4 / constellation-4-size ] [ 0 ]
  set constellation-5-prev-avg ifelse-value (constellation-5-size > 0) [ total-prev-c5 / constellation-5-size ] [ 0 ]
end 

to compute-constellation-stats
  let var-op-c1 0
  let var-op-c2 0
  let var-op-c3 0
  let var-op-c4 0
  let var-op-c5 0

  ask turtles [
    let const get-constellation self
    if const = 1 [
      set var-op-c1 var-op-c1 + ((opinion - constellation-1-opinion-avg) ^ 2)
    ]
    if const = 2 [
      set var-op-c2 var-op-c2 + ((opinion - constellation-2-opinion-avg) ^ 2)
    ]
    if const = 3 [
      set var-op-c3 var-op-c3 + ((opinion - constellation-3-opinion-avg) ^ 2)
    ]
    if const = 4 [
      set var-op-c4 var-op-c4 + ((opinion - constellation-4-opinion-avg) ^ 2)
    ]
    if const = 5 [
      set var-op-c5 var-op-c5 + ((opinion - constellation-5-opinion-avg) ^ 2)
    ]
  ]

  set constellation-1-polarization ifelse-value (constellation-1-size > 1) [ sqrt (var-op-c1 / constellation-1-size) ] [ 0 ]
  set constellation-2-polarization ifelse-value (constellation-2-size > 1) [ sqrt (var-op-c2 / constellation-2-size) ] [ 0 ]
  set constellation-3-polarization ifelse-value (constellation-3-size > 1) [ sqrt (var-op-c3 / constellation-3-size) ] [ 0 ]
  set constellation-4-polarization ifelse-value (constellation-4-size > 1) [ sqrt (var-op-c4 / constellation-4-size) ] [ 0 ]
  set constellation-5-polarization ifelse-value (constellation-5-size > 1) [ sqrt (var-op-c5 / constellation-5-size) ] [ 0 ]
end 

to-report get-constellation [agent]
  let op [opinion] of agent
  let prev [prevalence] of agent

  if op < (- c-opinion-extreme) and prev >= c-prevalence-high [
    report 1
  ]
  if op < (- c-opinion-moderate) and op >= (- c-opinion-extreme) [
    report 2
  ]
  if op >= (- c-opinion-moderate) and op <= c-opinion-moderate [
    report 3
  ]
  if op > c-opinion-moderate and op <= c-opinion-extreme [
    report 4
  ]
  if op > c-opinion-extreme and prev >= c-prevalence-high [
    report 5
  ]

  if op < 0 [ report 2 ]
  if op > 0 [ report 4 ]
  report 3
end 

to compute-turnover
  let changes 0
  let changes-c1 0
  let changes-c2 0
  let changes-c3 0
  let changes-c4 0
  let changes-c5 0

  let prev-size-c1 0
  let prev-size-c2 0
  let prev-size-c3 0
  let prev-size-c4 0
  let prev-size-c5 0

  ask turtles [
    let current-const get-constellation self
    let prev-const constellation-prev

    if prev-const > 0 [
      if prev-const != current-const [
        set changes changes + 1
        if prev-const = 1 [ set changes-c1 changes-c1 + 1 ]
        if prev-const = 2 [ set changes-c2 changes-c2 + 1 ]
        if prev-const = 3 [ set changes-c3 changes-c3 + 1 ]
        if prev-const = 4 [ set changes-c4 changes-c4 + 1 ]
        if prev-const = 5 [ set changes-c5 changes-c5 + 1 ]
      ]

      if prev-const = 1 [ set prev-size-c1 prev-size-c1 + 1 ]
      if prev-const = 2 [ set prev-size-c2 prev-size-c2 + 1 ]
      if prev-const = 3 [ set prev-size-c3 prev-size-c3 + 1 ]
      if prev-const = 4 [ set prev-size-c4 prev-size-c4 + 1 ]
      if prev-const = 5 [ set prev-size-c5 prev-size-c5 + 1 ]
    ]
  ]

  set turnover-global ifelse-value (pop > 0) [ 100 * changes / pop ] [ 0 ]
  set turnover-c1 ifelse-value (prev-size-c1 > 0) [ 100 * changes-c1 / prev-size-c1 ] [ 0 ]
  set turnover-c2 ifelse-value (prev-size-c2 > 0) [ 100 * changes-c2 / prev-size-c2 ] [ 0 ]
  set turnover-c3 ifelse-value (prev-size-c3 > 0) [ 100 * changes-c3 / prev-size-c3 ] [ 0 ]
  set turnover-c4 ifelse-value (prev-size-c4 > 0) [ 100 * changes-c4 / prev-size-c4 ] [ 0 ]
  set turnover-c5 ifelse-value (prev-size-c5 > 0) [ 100 * changes-c5 / prev-size-c5 ] [ 0 ]
end 

to compute-entropy
  let bins (list -1.0 -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1 
                0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0)
  let counts []
  let i 0

  while [i < 20] [
    let lower item i bins
    let upper item (i + 1) bins
    let in-bin count turtles with [opinion >= lower and opinion < upper]
    set counts lput in-bin counts
    set i i + 1
  ]

  let last-bin item 20 bins
  let in-last count turtles with [opinion >= last-bin]
  set counts replace-item 19 counts (in-last)

  let total-pop pop
  let entropy 0

  foreach counts [ c ->
    if c > 0 [
      let p c / total-pop
      set entropy entropy - (p * log p 2)
    ]
  ]

  set opinion-entropy entropy
end 

to compute-volatility
  let center-opinion ifelse-value (constellation-3-size > 0) 
    [ constellation-3-opinion-avg ] 
    [ 0 ]

  if ticks > 1 [
    let delta abs (center-opinion - last-center-opinion)
    set center-volatility 0.3 * delta + 0.7 * center-volatility
  ]

  set last-center-opinion center-opinion
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FRACTAL DIMENSIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to capture-network-snapshot
  if ticks mod 50 = 0 [
    let snapshot get-current-network
    set network-snapshots lput (list ticks snapshot) network-snapshots
    if length network-snapshots > 20 [
      set network-snapshots but-first network-snapshots
    ]
  ]
end 

to-report get-current-network
  let edges []
  ask links [
    set edges lput (list [who] of end1 [who] of end2) edges
  ]
  report edges
end 

to compute-fractal-dimensions
  set topological-dimension compute-topological-dimension
  set dynamic-dimension compute-dynamic-dimension

  if topological-dimension > 0 [
    set criticality-gap abs(dynamic-dimension - topological-dimension) / topological-dimension
  ]

  if ticks mod 500 = 0 [
    output-print (word "Ticks: " ticks 
      " | D_top: " topological-dimension
      " | D_dyn: " dynamic-dimension
      " | Gap: " criticality-gap)
  ]
end 

to-report compute-topological-dimension
  let edges get-current-network
  if length edges < 10 [ report 0 ]

  let results []

  foreach box-sizes [ r ->
    let box-radius max list 1 (round (r * pop / 10))
    let unvisited sort [who] of turtles
    let n-boxes 0

    while [not empty? unvisited] [
      let seed first unvisited
      let in-box [who] of bfs-reachable (turtle seed) box-radius
      set unvisited filter [ [x] -> not member? x in-box ] unvisited
      set n-boxes n-boxes + 1
    ]

    set results lput (list (ln (1 / r)) (ln n-boxes)) results
  ]

  report regression-slope results
end 

to-report bfs-reachable [start-node max-dist]
  if start-node = nobody [ report no-turtles ]

  let visited (turtle-set start-node)
  let frontier (turtle-set start-node)
  let dist 0

  while [dist < max-dist and any? frontier] [
    let new-frontier (turtle-set)
    ask frontier [
      let neighbors-set link-neighbors
      set new-frontier (turtle-set new-frontier neighbors-set)
    ]
    set visited (turtle-set visited new-frontier)
    set frontier new-frontier
    set dist dist + 1
  ]

  report visited
end 

to-report compute-dynamic-dimension
  if length cascade-sizes < 100 [ report 0 ]

  let sizes []
  let current-cascade 0
  let current-size 0

  let sorted-cascades sort-by [ [a b] -> item 3 a < item 3 b ] cascade-sizes

  foreach sorted-cascades [ evt ->
    let cid item 3 evt
    ifelse cid != current-cascade [
      if current-size > 0 [ set sizes lput current-size sizes ]
      set current-cascade cid
      set current-size 1
    ] [
      set current-size current-size + 1
    ]
  ]
  if current-size > 0 [ set sizes lput current-size sizes ]

  set sizes filter [x -> x > 1] sizes
  if length sizes < 20 [ report 0 ]

  let unique-sizes remove-duplicates sort sizes
  let ccdf []

  foreach unique-sizes [ s ->
    let count-greater length filter [x -> x >= s] sizes
    if count-greater > 0 [
      set ccdf lput (list (ln s) (ln count-greater)) ccdf
    ]
  ]

  if length ccdf < 5 [ report 0 ]

  let slope regression-slope ccdf
  if slope < -10 or slope > 10 [ report 0 ]
  report abs slope
end 

to-report regression-slope [points]
  let n length points
  if n < 2 [ report 0 ]

  let sum-x 0
  let sum-y 0
  let sum-xy 0
  let sum-x2 0

  foreach points [ p ->
    let x item 0 p
    let y item 1 p
    set sum-x sum-x + x
    set sum-y sum-y + y
    set sum-xy sum-xy + x * y
    set sum-x2 sum-x2 + x * x
  ]

  let slope (n * sum-xy - sum-x * sum-y) / (n * sum-x2 - sum-x * sum-x)
  report slope
end 

to-report self-organization-evidence
  if length criticality-history < 10 [ report false ]

  let first-gap item 2 (item 0 criticality-history)
  let last-gap item 2 (item (length criticality-history - 1) criticality-history)

  report last-gap < first-gap * 0.7
end 

to-report system-status
  let f fractale
  let d_top topological-dimension
  let d_dyn dynamic-dimension
  let gap criticality-gap

  if d_top = 0 or d_dyn = 0 [
    report "Initializing..."
  ]

  if gap < 0.2 [
    if abs(f - d_dyn) < 0.3 [
      if abs(f - d_top) < 0.3 [
        report "🌟🌟 PERFECT CRITICALITY: structure, dynamics AND process aligned"
      ]
      report "⭐ DYNAMIC CRITICALITY: good process↔dynamics alignment"
    ]
    if abs(f - d_top) < 0.3 [
      report "⭐ STRUCTURAL CRITICALITY: good process↔network alignment"
    ]
    report "⚡ CRITICALITY: structure and dynamics aligned, autonomous process"
  ]

  if gap < 0.5 [
    if f > 1.5 [
      report "🌀 EXPLORATION: complex process in moderately adapted structure"
    ]
    if f < 1 [
      report "🌀 SIMPLICITY: linear process, structure adapting"
    ]
    report "🌀 INTERMEDIATE REGIME: system exploring possibility space"
  ]

  if d_dyn > d_top * 1.5 [
    report "⚠️ DECOUPLING: dynamics richer than structure (check measurements)"
  ]
  if d_dyn < d_top * 0.5 [
    report "⚠️ UNDERUTILIZATION: rich structure but little influence"
  ]
  report "⚠️ OUTSIDE CRITICALITY: structure and dynamics desynchronized"
end 

to-report system-status-code
  let gap criticality-gap
  if gap < 0.2 [ report 3 ]
  if gap < 0.5 [ report 2 ]
  report 1
end 

to-report system-regime
  let f fractale
  let d_top topological-dimension
  let d_dyn dynamic-dimension
  let gap criticality-gap
  let turn turnover-global
  let entr opinion-entropy
  let volt center-volatility

  if d_top = 0 or d_dyn = 0 or ticks < 100 [
    report "⚪ INITIALIZATION: System stabilizing, waiting for sufficient data"
  ]

  let f-high-threshold 1.6
  let f-low-threshold 1.3
  let gap-low-threshold 0.2
  let gap-high-threshold 0.5
  let turn-low-threshold 3.0
  let turn-mod-high-threshold 8.0
  let turn-high-threshold 15.0
  let entr-low-threshold 2.5
  let entr-high-threshold 3.5
  let volt-low-threshold 0.2
  let volt-mod-high-threshold 0.4
  let volt-high-threshold 0.6

  let f-qual ifelse-value (f >= f-high-threshold) 
    [ "High" ] 
    [ ifelse-value (f <= f-low-threshold) 
      [ "Low" ] 
      [ "Moderate" ] ]

  let gap-qual ifelse-value (gap <= gap-low-threshold) 
    [ "Low" ] 
    [ ifelse-value (gap >= gap-high-threshold) 
      [ "High" ] 
      [ "Moderate" ] ]

  let turn-qual ifelse-value (turn >= turn-high-threshold) 
    [ "Very High" ] 
    [ ifelse-value (turn >= turn-mod-high-threshold) 
      [ "High" ] 
      [ ifelse-value (turn >= turn-low-threshold) 
        [ "Moderate" ] 
        [ "Low" ] ] ]

  let entr-qual ifelse-value (entr >= entr-high-threshold) 
    [ "High" ] 
    [ ifelse-value (entr <= entr-low-threshold) 
      [ "Low" ] 
      [ "Moderate" ] ]

  let volt-qual ifelse-value (volt >= volt-high-threshold) 
    [ "Very High" ] 
    [ ifelse-value (volt >= volt-mod-high-threshold) 
      [ "High" ] 
      [ ifelse-value (volt >= volt-low-threshold) 
        [ "Moderate" ] 
        [ "Low" ] ] ]

  if (f-qual = "High") and (gap-qual = "Low") and 
     (turn-qual = "Moderate") and (entr-qual = "High") and 
     (volt-qual = "Moderate") [
    report (word "🌟 EMERGENT CRITICAL: " 
      "System at the edge of chaos, rich and diverse, ideal for social innovation "
      "[Fractal: " f-qual ", Criticality: " gap-qual 
      ", Turnover: " turn-qual ", Entropy: " entr-qual ", Volatility: " volt-qual "]")
  ]

  if (f-qual = "High") and (gap-qual = "Low") and 
     (turn-qual = "Low") and (entr-qual = "Moderate") and 
     (volt-qual = "Low") [
    report (word "🏛️ MATURE STRUCTURED: "
      "Crystallized complexity, well-organized but inflexible society "
      "[Fractal: " f-qual ", Criticality: " gap-qual 
      ", Turnover: " turn-qual ", Entropy: " entr-qual ", Volatility: " volt-qual "]")
  ]

  if (f-qual = "High") and (gap-qual = "High") and 
     (turn-qual = "High" or turn-qual = "Very High") and 
     (entr-qual = "High") and (volt-qual = "High" or volt-qual = "Very High") [
    report (word "🌪️ TURBULENT: "
      "Rich but disorganized, risk of collapse or transition "
      "[Fractal: " f-qual ", Criticality: " gap-qual 
      ", Turnover: " turn-qual ", Entropy: " entr-qual ", Volatility: " volt-qual "]")
  ]

  if (f-qual = "Low") and (gap-qual = "Low") and 
     (turn-qual = "Low") and (entr-qual = "Low") and 
     (volt-qual = "Low") [
    report (word "🧊 GLACIAL: "
      "Frozen system, total consensus, absence of dynamics "
      "[Fractal: " f-qual ", Criticality: " gap-qual 
      ", Turnover: " turn-qual ", Entropy: " entr-qual ", Volatility: " volt-qual "]")
  ]

  if (f-qual = "Moderate") and (gap-qual = "Moderate") and 
     (turn-qual = "High") and (entr-qual = "Moderate") and 
     (volt-qual = "High" or volt-qual = "Very High") [
    report (word "🔥 PRE-ERUPTIVE: "
      "Highly volatile center, warning signs of tipping "
      "[Fractal: " f-qual ", Criticality: " gap-qual 
      ", Turnover: " turn-qual ", Entropy: " entr-qual ", Volatility: " volt-qual "]")
  ]

  if (turn-qual = "Very High") and (volt-qual = "Very High") [
    report (word "💥 EXPLOSIVE: "
      "Crisis underway, everything changing rapidly "
      "[Fractal: " f-qual ", Criticality: " gap-qual 
      ", Turnover: " turn-qual ", Entropy: " entr-qual ", Volatility: " volt-qual "]")
  ]

  report (word "🔄 MIXED REGIME: System in transition or intermediate state "
    "[Fractal: " f-qual ", Criticality: " gap-qual 
    ", Turnover: " turn-qual ", Entropy: " entr-qual ", Volatility: " volt-qual "]")
end 

to-report system-regime-code
  let f fractale
  let d_top topological-dimension
  let d_dyn dynamic-dimension
  let gap criticality-gap
  let turn turnover-global
  let entr opinion-entropy
  let volt center-volatility

  if d_top = 0 or d_dyn = 0 or ticks < 100 [ report 0 ]

  let f-high 1.6
  let f-low 1.3
  let gap-low 0.2
  let gap-high 0.5
  let turn-mod 3.0
  let turn-high 8.0
  let turn-vhigh 15.0
  let entr-low 2.5
  let entr-high 3.5
  let volt-mod 0.2
  let volt-high 0.4
  let volt-vhigh 0.6

  if (f >= f-high) and (gap <= gap-low) and 
     (turn >= turn-mod and turn < turn-high) and 
     (entr >= entr-high) and (volt >= volt-mod and volt < volt-high) [
    report 1
  ]

  if (f >= f-high) and (gap <= gap-low) and 
     (turn < turn-mod) and (entr >= entr-low and entr < entr-high) and 
     (volt < volt-mod) [
    report 2
  ]

  if (f >= f-high) and (gap >= gap-high) and 
     (turn >= turn-high) and (entr >= entr-high) and 
     (volt >= volt-high) [
    report 3
  ]

  if (f <= f-low) and (gap <= gap-low) and 
     (turn < turn-mod) and (entr <= entr-low) and 
     (volt < volt-mod) [
    report 4
  ]

  if (f > f-low and f < f-high) and (gap > gap-low and gap < gap-high) and 
     (turn >= turn-high) and (entr > entr-low and entr < entr-high) and 
     (volt >= volt-high) [
    report 5
  ]

  if (turn >= turn-vhigh) and (volt >= volt-vhigh) [
    report 6
  ]

  report 0
end 

to calibrate-regime-thresholds
  output-print "=================================================="
  output-print "Current regime thresholds:"
  output-print (word "Fractal High: " 1.6 " | Low: " 1.3)
  output-print (word "Criticality Gap Low: " 0.2 " | High: " 0.5)
  output-print (word "Turnover Low: " 3.0 "% | Mod-High: " 8.0 "% | High: " 15.0 "%")
  output-print (word "Entropy Low: " 2.5 " | High: " 3.5)
  output-print (word "Volatility Low: " 0.2 " | Mod-High: " 0.4 " | High: " 0.6)
  output-print "=================================================="
  output-print "To modify thresholds, edit the values in system-regime procedure"
end 

to diagnose-system
  output-print "=================================================="
  output-print (word "SYSTEM DIAGNOSIS at tick " ticks)
  output-print "=================================================="
  output-print system-regime

  output-print ""
  output-print "--- Detailed Values ---"
  output-print (word "Fractal: " fractale)
  output-print (word "Topological Dim: " topological-dimension)
  output-print (word "Dynamic Dim: " dynamic-dimension)
  output-print (word "Criticality Gap: " criticality-gap)
  output-print (word "Global Turnover: " turnover-global "%")
  output-print (word "Opinion Entropy: " opinion-entropy)
  output-print (word "Center Volatility: " center-volatility)
  output-print (word "Lyapunov Exponent: " lyapunov-exponent)

  output-print ""
  output-print "--- Constellation Sizes ---"
  output-print (word "C1 (Left Radicals): " constellation-1-size)
  output-print (word "C2 (Left Moderates): " constellation-2-size)
  output-print (word "C3 (Center): " constellation-3-size)
  output-print (word "C4 (Right Moderates): " constellation-4-size)
  output-print (word "C5 (Right Radicals): " constellation-5-size)

  output-print "=================================================="
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; LYAPUNOV PROCEDURES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to compute-lyapunov-exponent
  let n length divergence-history
  if n < 10 [ stop ]

  let points []
  foreach divergence-history [ d ->
    set points lput (list (ln (item 0 d)) (ln (item 1 d))) points
  ]

  set lyapunov-exponent regression-slope points
end 

to initialize-perturbation
  set reference-trajectory []
  set perturbed-trajectory []

  let ref-state []
  ask turtles [
    set ref-state lput (list who opinion prevalence influence) ref-state
  ]
  set reference-trajectory lput (list ticks ref-state) reference-trajectory

  ask turtles [
    set opinion opinion + random-float 2e-6 - 1e-6
    set opinion max list -1 min list 1 opinion
  ]

  let pert-state []
  ask turtles [
    set pert-state lput (list who opinion prevalence influence) pert-state
  ]
  set perturbed-trajectory lput (list ticks pert-state) perturbed-trajectory
end 

to update-perturbation
  if length reference-trajectory = 0 [ stop ]

  let last-ref last reference-trajectory
  let last-pert last perturbed-trajectory

  let divergence 0
  let n 0

  foreach (item 1 last-ref) [ ref-state ->
    let who-ref item 0 ref-state
    let match filter [p -> item 0 p = who-ref] (item 1 last-pert)
    if not empty? match [
      let pert-state first match
      let d-opinion (item 1 ref-state) - (item 1 pert-state)
      let d-prevalence (item 2 ref-state) - (item 2 pert-state)
      let d-influence (item 3 ref-state) - (item 3 pert-state)
      set divergence divergence + (d-opinion ^ 2 + d-prevalence ^ 2 + d-influence ^ 2)
      set n n + 1
    ]
  ]

  if n > 0 [
    set divergence sqrt (divergence / n)
    if divergence = 0 [ set divergence 0.0000001 ]
    set divergence-history lput (list ticks divergence) divergence-history

    if ticks mod 10 = 0 [
      rescale-perturbation
    ]
  ]

  let new-ref []
  let new-pert []
  ask turtles [
    set new-ref lput (list who opinion prevalence influence) new-ref
  ]

  ask turtles [
    set new-pert lput (list who opinion prevalence influence) new-pert
  ]

  set reference-trajectory lput (list ticks new-ref) reference-trajectory
  set perturbed-trajectory lput (list ticks new-pert) perturbed-trajectory

  if length reference-trajectory > 100 [
    set reference-trajectory but-first reference-trajectory
    set perturbed-trajectory but-first perturbed-trajectory
  ]
end 

to rescale-perturbation
  if length reference-trajectory = 0 [ stop ]

  let last-ref last reference-trajectory
  ask turtles [
    let my-ref filter [p -> item 0 p = who] (item 1 last-ref)
    if not empty? my-ref [
      let ref-state first my-ref
      let d-opinion opinion - (item 1 ref-state)
      let d-prevalence prevalence - (item 2 ref-state)
      let d-influence influence - (item 3 ref-state)

      let scale 0.01
      set opinion (item 1 ref-state) + d-opinion * scale
      set prevalence (item 2 ref-state) + d-prevalence * scale
      set influence (item 3 ref-state) + d-influence * scale

      set opinion max list -1 min list 1 opinion
      set prevalence max list 0 min list 99 prevalence
      set influence max list 0 min list 1 influence
    ]
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; VALUES OUTPUT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to compute-statistics
  if output = "Values" [
    output-print join-cols (list
      fmt try
      fmt ticks
      fmt who
      fmt prevalence
      fmt opinion
      fmt influence
      fmt meme-plus
      fmt meme-minus
      fmt meme-plus-w
      fmt meme-minus-w
    )
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; COLORING
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to colorer
  ask turtles [
    ifelse meta? [ set color yellow set influence 1] [
      ifelse opinion >= 0 [ set color blue ] [ set color red ]
    ]
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; NETWORK
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to update-networks
  let doomed links with [
    abs([opinion] of end1 - [opinion] of end2) > (link-removal-threshold / 100)
  ]
  let doomedProb doomed with [ random-float 1 < prob ]
  let n-remove min (list linksdown count doomedProb)
  if n-remove > 0 [
    ask n-of n-remove doomedProb [ die ]
    set links-dead links-dead + n-remove
  ]

  let j linksup
  while [j > 0] [
    let t one-of turtles
    if t = nobody [ stop ]
    ask t [
      let myop opinion
      let candidates other turtles with [ not link-neighbor? myself ]
      let pool-homo candidates with [ abs(opinion - myop) < (link-formation-threshold / 100) ]
      let pool-bridge candidates with [ (sign opinion) != (sign myop) ]

      let friend nobody
      if any? pool-bridge and (random-float 1 < bridge-prob) [
        set friend max-one-of pool-bridge [ abs(opinion - myop) ]
      ]
      if (friend = nobody) and any? pool-homo [
        set friend min-one-of pool-homo [ abs(opinion - myop) ]
      ]

      if friend != nobody and (random-float 1 < prob) [
        create-link-with friend
        set links-create links-create + 1
        let same-sign? (sign opinion) = (sign [opinion] of friend)
        ask link-with friend [
          set color (ifelse-value same-sign? [ green ] [ gray ])
          set thickness linktick
          if show-links [ show-link ]
        ]
      ]
    ]
    set j j - 1
  ]
  ask turtles with [
  influence >= 1 and meta-type = "none"
] [
  set meta-type "dynamic"
  set color yellow
]
end 

to meta
  if not network [ stop ]

  ask turtles with [meta-type = "structural"] [

    ;; déterminer le mode actuel selon la probabilité
    let mode meta-mode-auto

    ;; choisir si on cible un méta ou un non-méta
    let choose-meta?
      ifelse-value (mode = "non-meta")
        [ false ]   ;; déterministe : méta → non-méta
        [
          ifelse-value (mode = "meta")
            [ true ] ;; déterministe : méta → méta
            [ random-float 1 < meta-link-prob ] ;; probabiliste : mixte
        ]

    ;; construire le pool selon le choix
    let pool other turtles with [
      (ifelse-value choose-meta?
        [ color = yellow ]      ;; méta → méta
        [ color != yellow ]     ;; méta → non-méta
      ) and
      not link-neighbor? myself and
      (count link-neighbors) < meta-links
    ]

    ;; créer le lien si possible
    if any? pool [
      let friend one-of pool
      create-link-with friend

      let same-sign? (sign opinion) = (sign [opinion] of friend)
      ask link-with friend [
        set color (ifelse-value same-sign? [ green ] [ gray ])
        set thickness linktick
        if show-links [ show-link ]
      ]
    ]
  ]
end 

to-report meta-mode-auto
  if meta-link-prob = 0 [
    report "non-meta"
  ]
  if meta-link-prob = 1 [
    report "meta"
  ]
  report "mixed"
end 

to apply-link-visibility
  ifelse show-links [ ask links [ show-link ] ] [ ask links [ hide-link ] ]
end 

to recolor-links
  ask links [
    let s1 sign [opinion] of end1
    let s2 sign [opinion] of end2
    ifelse s1 = s2 [ set color green ] [ set color gray ]
    set thickness linktick
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; EVENT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to event
  ask turtles [
    let event-prob random-float 1
    if event-prob <= event-prob-max [
      ifelse meme_set = true [
        if (to_left = false) [
          if agent-type = "Right side" [
            if opinion < 0 [ maybe-set-opinion (opinion + event_size) ]
          ]
        ]
        if (to_left = true) [
          if agent-type = "Left side" [
            if opinion > 0 [ maybe-set-opinion (opinion - event_size) ]
          ]
        ]
      ] [
        if (to_left = false) [
          if (opinion < high_meme and opinion > low_meme and prevalence < high-prev and prevalence > low-prev) [
            maybe-set-opinion (opinion + event_size)
            if (prev_change != 0) [ set prevalence min (list max-prevalence (prevalence + prev_change)) ]
          ]
        ]
        if (to_left = true) [
          if (opinion > low_meme and opinion < high_meme and prevalence > low-prev and prevalence < high-prev) [
            maybe-set-opinion (opinion - event_size)
            if (prev_change != 0) [ set prevalence min (list max-prevalence (prevalence + prev_change)) ]
          ]
        ]
      ]
      if use-memes? and event-meme-change? [ init-memes-from-state ]
    ]
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; MISCELLANEOUS UTILITIES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to set-background-black
  ask patches [ set pcolor black ]
end 

to update-3d [agt]
  ask agt [
    set x3d opinion * 16
    set y3d prevalence / 6
    set z3d influence * 16
    setxyz x3d y3d z3d
  ]
end 

to-report safe-median [agentset varname]
  if not any? agentset [ report 0 ]
  report median [ runresult varname ] of agentset
end 

to-report sign [x]
  ifelse x > 0 [ report 1 ] [ ifelse x < 0 [ report -1 ] [ report 0 ] ]
end 

to-report meme-saturation-pct
  if not use-memes? [ report 0 ]
  if not any? turtles [ report 0 ]
  let total-memes sum [meme-plus + meme-minus] of turtles
  let capacity meme-max * count turtles
  if capacity <= 0 [ report 0 ]
  report 100 * total-memes / capacity
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GROUP IMPACT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to-report group-alignment-all [agt sign-ref]
  let nbrs [link-neighbors] of agt
  if not any? nbrs [ report 0.5 ]
  let same count nbrs with [ (sign opinion) = sign-ref ]
  report same / count nbrs
end 

to-report group-alignment-k [agt sign-ref k]
  let nbrs [link-neighbors] of agt
  let deg count nbrs
  if deg = 0 [ report 0.5 ]
  let kk max list 1 min list deg floor k
  let agop [opinion] of agt
  let pool min-n-of kk nbrs [ abs(opinion - agop) ]
  if not any? pool [ report 0.5 ]
  let same count pool with [ (sign opinion) = sign-ref ]
  report same / count pool
end 

to-report group-alignment-effective [agt sign-ref]
  ifelse (group-impact-mode = "k-nearest")
    [ report group-alignment-k agt sign-ref group-k ]
    [ report group-alignment-all agt sign-ref ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; MEMES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to-report initial-prevalence-to-memes [prev]
  report (prev / 99) * meme-max
end 

to init-memes-from-state
  let totq initial-prevalence-to-memes prevalence

  ifelse opinion >= 0 [
    set meme-plus  totq * (0.5 + 0.5 * abs opinion)
    set meme-minus totq - meme-plus
  ] [
    set meme-minus totq * (0.5 + 0.5 * abs opinion)
    set meme-plus  totq - meme-minus
  ]

  set meme-plus-w  meme-plus  * meme-weight-mean
  set meme-minus-w meme-minus * meme-weight-mean

  if meme-plus < 0 [ set meme-plus 0 ]
  if meme-minus < 0 [ set meme-minus 0 ]
  if meme-plus-w < 0 [ set meme-plus-w 0 ]
  if meme-minus-w < 0 [ set meme-minus-w 0 ]
end 

to-report draw-meme-weight
  let w meme-weight-mean
  if meme-weight-sd > 0 [
    set w (meme-weight-mean + (random-float (2 * meme-weight-sd) - meme-weight-sd))
  ]
  if w < meme-weight-min [ set w meme-weight-min ]
  if w > meme-weight-max [ set w meme-weight-max ]
  report w
end 

to recompute-from-memes
  let totw meme-plus-w + meme-minus-w
  if totw < 1e-6 [ set totw 1e-6 ]
  set proposed-opinion ((meme-plus-w - meme-minus-w) / totw)
  maybe-set-opinion proposed-opinion

  let totq meme-plus + meme-minus
  let scaled (totq / meme-max) * 99
  if scaled < 0 [ set scaled 0 ]
  if scaled > 99 [ set scaled 99 ]
  set prevalence scaled
end 

to decay-memes
  if meme-decay <= 0 [ stop ]
  let f (1 - meme-decay)
  set meme-plus    max list 0 (meme-plus    * f)
  set meme-minus   max list 0 (meme-minus   * f)
  set meme-plus-w  max list 0 (meme-plus-w  * f)
  set meme-minus-w max list 0 (meme-minus-w * f)
end 

to transmit-memes [emitter]
  let sgn sign ([opinion] of emitter)
  let w draw-meme-weight
  let leak (meme-anti-leak * meme-gain)

  ifelse sgn >= 0 [
    set meme-plus   meme-plus + meme-gain
    set meme-plus-w meme-plus-w + (w * meme-gain)

    set meme-minus   max list 0 (meme-minus - leak)
    set meme-minus-w max list 0 (meme-minus-w - (w * leak))
  ] [
    set meme-minus   meme-minus + meme-gain
    set meme-minus-w meme-minus-w + (w * meme-gain)

    set meme-plus   max list 0 (meme-plus - leak)
    set meme-plus-w max list 0 (meme-plus-w - (w * leak))
  ]

  let totq meme-plus + meme-minus
  if totq > meme-max [
    let factor meme-max / totq
    set meme-plus    meme-plus    * factor
    set meme-minus   meme-minus   * factor
    set meme-plus-w  meme-plus-w  * factor
    set meme-minus-w meme-minus-w * factor
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INJECTION
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to inject-memes
  let base-pool turtles with [
    opinion >= inject-low_meme and opinion <= inject-high_meme and
    prevalence >= inject-low-prev and prevalence <= inject-high-prev
  ]

  let pool base-pool
  if inject-metas-only [
    set pool base-pool with [ color = yellow ]
  ]

  ask pool [
    if random-float 1 <= inject-prob-max [
      let w inject-weight
      if w < meme-weight-min [ set w meme-weight-min ]
      if w > meme-weight-max [ set w meme-weight-max ]

      if inject-amount < 0 [ stop ]

      if inject-sign = "plus" [
        set meme-plus   meme-plus + inject-amount
        set meme-plus-w meme-plus-w + (w * inject-amount)
      ]
      if inject-sign = "minus" [
        set meme-minus   meme-minus + inject-amount
        set meme-minus-w meme-minus-w + (w * inject-amount)
      ]

      let totq meme-plus + meme-minus
      if totq > meme-max [
        let factor meme-max / totq
        set meme-plus    meme-plus    * factor
        set meme-minus   meme-minus   * factor
        set meme-plus-w  meme-plus-w  * factor
        set meme-minus-w meme-minus-w * factor
      ]

      if use-memes? [ recompute-from-memes ]
    ]
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Additional reporters
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to-report mean-polarity-index
  let total-plus  sum [meme-plus] of turtles
  let total-minus sum [meme-minus] of turtles
  ifelse (total-plus + total-minus > 0)
    [ report (total-plus - total-minus) / (total-plus + total-minus) ]
    [ report 0 ]
end 

to-report ideologization-meme-based
  if not any? turtles [ report 0 ]
  report mean [ (abs (meme-plus - meme-minus) / 100) ] of turtles
end 

to-report ideologization-opinion-based
  if not any? turtles [ report 0 ]
  report mean [ (abs opinion) * (prevalence / 99) ] of turtles
end 

There is only one version of this model, created 17 days ago by Pierre-Alain Cotnoir.

Attached files

File Type Description Last updated
Simulator_20260316-5_V8.png preview Preview for 'Simulator_20260316-5_V8' 17 days ago, by Pierre-Alain Cotnoir Download

This model does not have any ancestors.

Children:

Graph of models related to 'Simulator_20260316-5_V8'