Relationship Generator 1.2.2
Believable families around a person: parents, siblings, partners and children, with ages and names for C++23
Loading...
Searching...
No Matches
relgen.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 Diego Dasso Migotto
3
4#ifndef DASMIG_RELGEN_HPP
5#define DASMIG_RELGEN_HPP
6
7#include <dasmig/birthgen.hpp>
8#include <dasmig/namegen.hpp>
9#include "core.hpp"
10#include <algorithm>
11#include <array>
12#include <cctype>
13#include <chrono>
14#include <cmath>
15#include <cstddef>
16#include <cstdint>
17#include <filesystem>
18#include <format>
19#include <initializer_list>
20#include <map>
21#include <optional>
22#include <ostream>
23#include <span>
24#include <stdexcept>
25#include <string>
26#include <string_view>
27#include <system_error>
28#include <unordered_map>
29#include <utility>
30#include <vector>
31
32#ifdef DASMIG_RELATIONSHIP_EMBED
33#include "relgen_data.hpp"
34#endif
35
36/// @file relgen.hpp
37/// @brief Relationship Generator library — believable families around a person: parents, siblings, partners and children, with ages and names for C++23.
38/// @author Diego Dasso Migotto (diegomigotto at hotmail dot com)
39/// @see See doc/usage.md for the narrative tutorial.
40
41DASMIG_CORE_REQUIRE(1, 1);
42
43namespace dasmig
44{
45
46/// @brief A name the caller already has, as plain words.
47///
48/// `family` holds the family name; for a culture with two surnames (Spain,
49/// Portugal) two words are read as the two surnames.
51{
52 std::string given; ///< Given name; not empty.
53 std::string patronymic; ///< Patronymic, for the cultures that have one; empty for none.
54 std::string family; ///< Family name; empty for none.
55};
56
57/// @brief Someone the caller already knows: used to build the family of a
58/// member of a family that exists, so that both agree.
59///
60/// A person who is alive has no `death`; one who died before the reference
61/// date has it.
63{
64 sex bio_sex{sex::male}; ///< Biological sex.
65 std::chrono::year_month_day birth{}; ///< Date of birth.
66 std::optional<std::chrono::year_month_day> death{}; ///< Date of death, for the dead. // NOLINT(readability-redundant-member-init)
67 std::optional<person_name> name{}; ///< The name, or drawn to match the relatives'. // NOLINT(readability-redundant-member-init)
68 std::vector<known_person> parents{}; ///< This person's own parents, when known: the anchor's grandparents (at most one of each sex). // NOLINT(readability-redundant-member-init)
69};
70
71/// @brief A brother or sister the caller already knows.
73{
74 known_person person{}; ///< Who they are.
75 bool full{true}; ///< Both parents are shared; otherwise only `through`.
76 sex through{sex::male}; ///< For a half-sibling: the shared parent's sex.
77};
78
79/// @brief A child the caller already knows.
81{
82 known_person person{}; ///< Who they are.
83 bool genetic{true}; ///< By birth rather than by adoption.
84};
85
86/// @brief A union of the anchor that the caller already knows, with the
87/// children it had.
89{
90 std::optional<known_person> partner{}; ///< The partner; none when only the children are known. // NOLINT(readability-redundant-member-init)
91 std::chrono::year_month_day start{}; ///< When the union began.
92 std::optional<std::chrono::year_month_day> end{}; ///< When it ended; none if it is ongoing. // NOLINT(readability-redundant-member-init)
93 std::vector<known_child> children{}; ///< The children of this union. // NOLINT(readability-redundant-member-init)
94 std::vector<known_person> stepchildren{}; ///< The partner's children by an earlier union. // NOLINT(readability-redundant-member-init)
95};
96
97/// @brief The person a family is built around, as plain values.
98///
99/// Whatever is not given is drawn. A fixed `birth` wins over `age`.
100///
101/// The relatives (`father`, `mother`, `siblings`, `unions`) are for building
102/// the family of a member of a family that exists (see relg::expand()): a
103/// relative that is given is used as it is, and when a list is given no one
104/// else is drawn for it, even if the list is empty.
106{
107 /// @brief ISO 3166-1 alpha-2 code, or drawn by population.
108 std::optional<std::string> country_code{}; // NOLINT(readability-redundant-member-init)
109 /// @brief Biological sex, or drawn.
110 std::optional<sex> bio_sex{}; // NOLINT(readability-redundant-member-init)
111 /// @brief Date of birth, or drawn.
112 std::optional<std::chrono::year_month_day> birth{}; // NOLINT(readability-redundant-member-init)
113 /// @brief Age in completed years at the reference date, or drawn.
114 std::optional<int> age{}; // NOLINT(readability-redundant-member-init)
115 /// @brief The anchor's name; the parents, siblings and children are named
116 /// to match it. Drawn when omitted.
117 std::optional<person_name> name{}; // NOLINT(readability-redundant-member-init)
118 /// @brief The anchor's father, if known. Needs `up >= 1` to appear.
119 std::optional<known_person> father{}; // NOLINT(readability-redundant-member-init)
120 /// @brief The anchor's mother, if known. Needs `up >= 1` to appear.
121 std::optional<known_person> mother{}; // NOLINT(readability-redundant-member-init)
122 /// @brief The anchor's siblings and half-siblings, if known; none are
123 /// drawn when this is set. Needs `up >= 1` and `siblings`.
124 std::optional<std::vector<known_sibling>> siblings{}; // NOLINT(readability-redundant-member-init)
125 /// @brief The anchor's unions, if known (oldest first); none are drawn
126 /// when this is set, and the children are the ones listed.
127 std::optional<std::vector<known_union>> unions{}; // NOLINT(readability-redundant-member-init)
128};
129
130/// @brief What a family call asks for.
131///
132/// Every generation call takes one; `family_request{}` asks for the
133/// defaults. Choices are never stored on the generator, so one call cannot
134/// change the next.
136{
137 /// @brief The person the family is around; omitted fields are drawn.
139 /// @brief Generations above the anchor: 0 for none, 1 for parents, 2 for
140 /// grandparents too.
141 int up{1};
142 /// @brief Generations below the anchor: 0 for none, 1 for children, 2 for
143 /// grandchildren too.
144 int down{1};
145 /// @brief Whether to include the anchor's siblings (and half-siblings).
146 /// Siblings come from the parents, so `up = 0` gives none.
147 bool siblings{true};
148 /// @brief Whether to include the anchor's partners, current and former.
149 /// Children come from the unions either way.
150 bool partners{true};
151 /// @brief Whether to give everyone a name (needs name-generator's data).
152 bool names{true};
153 /// @brief The culture the names follow; the anchor's country's by default.
154 /// Needs `names`; a culture with no names loaded leaves the family
155 /// without names.
156 std::optional<culture> name_culture{}; // NOLINT(readability-redundant-member-init)
157 /// @brief Whether to include the partners of the anchor's parents who are
158 /// not the other parent (stepfathers and stepmothers), who joined
159 /// a parent while the anchor was a child. Off by default, and then
160 /// nothing else about the family changes. Needs `up >= 1`. With
161 /// `siblings`, it also brings the stepparents' children by an
162 /// earlier union (stepsiblings), and the half-siblings born during
163 /// the union have the stepparent as their other parent.
164 bool stepparents{false};
165 /// @brief Whether to draw who the anchor lives with at the reference date
166 /// (`family::home`). Off by default, and then nothing else about
167 /// the family changes. It is drawn from the relatives the family
168 /// holds, so a family without parents (`up = 0`) leaves a child
169 /// with no one to live with.
170 bool home{false};
171 /// @brief Population-weighted (true) or uniform country, when drawn.
172 bool weighted{true};
173 /// @brief The date ages and who is alive are computed at (1 July 2023,
174 /// the date of the UN estimates, by default).
175 std::chrono::year_month_day reference_date{birth_request::default_reference_date};
176};
177
178/// @brief How a member is related to the anchor.
179enum class relation : std::uint8_t
180{
181 anchor, ///< The person the family is built around.
182 father, ///< The anchor's father.
183 mother, ///< The anchor's mother.
184 sibling, ///< A full brother or sister.
185 half_sibling, ///< A half-brother or half-sister, through one parent.
186 paternal_grandfather, ///< The father's father.
187 paternal_grandmother, ///< The father's mother.
188 maternal_grandfather, ///< The mother's father.
189 maternal_grandmother, ///< The mother's mother.
190 partner, ///< A partner of the anchor, current or former.
191 child, ///< A child of the anchor, by birth or adoption.
192 grandchild, ///< A child of one of the anchor's children.
193 stepchild, ///< A child of a partner by an earlier union, not the anchor's.
194 stepfather, ///< A man who is, or was, a parent's partner while the anchor was a child.
195 stepmother, ///< A woman who is, or was, a parent's partner while the anchor was a child.
196 stepsibling ///< A stepparent's child by an earlier union, not a parent's.
197};
198
199/// @brief The name of a relation as it appears in JSON (`paternal_grandfather`).
200/// @param r The relation.
201/// @return The name.
202[[nodiscard]] constexpr std::string_view relation_name(relation r) noexcept
203{
204 switch (r)
205 {
206 case relation::anchor: return "anchor";
207 case relation::father: return "father";
208 case relation::mother: return "mother";
209 case relation::sibling: return "sibling";
210 case relation::half_sibling: return "half_sibling";
211 case relation::paternal_grandfather: return "paternal_grandfather";
212 case relation::paternal_grandmother: return "paternal_grandmother";
213 case relation::maternal_grandfather: return "maternal_grandfather";
214 case relation::maternal_grandmother: return "maternal_grandmother";
215 case relation::partner: return "partner";
216 case relation::child: return "child";
217 case relation::grandchild: return "grandchild";
218 case relation::stepchild: return "stepchild";
219 case relation::stepfather: return "stepfather";
220 case relation::stepmother: return "stepmother";
221 case relation::stepsibling: return "stepsibling";
222 }
223 return "unknown";
224}
225
226/// @brief A person in a family: who they are to the anchor, their name and
227/// when they live.
228struct person
229{
230 std::size_t id{0}; ///< Index in family::members.
231 relation role{relation::anchor}; ///< How the person is related to the anchor.
232 sex bio_sex{sex::male}; ///< Biological sex.
233 std::string given; ///< Given name; empty without names.
234 std::string patronymic; ///< Patronymic, for the cultures that have one.
235 std::string family; ///< Family name (both of a double surname).
236 std::string name; ///< The full name, in the culture's order.
237 std::chrono::year_month_day birth{}; ///< Date of birth.
238 bool twin{false}; ///< Born on the same day as a full sibling (or the anchor's).
239 bool alive{true}; ///< Alive at the reference date.
240 std::uint8_t age{0}; ///< Age in completed years at the reference date, or at death.
241 std::optional<std::chrono::year_month_day> death{}; ///< Date of death, for the dead. // NOLINT(readability-redundant-member-init)
242};
243
244/// @brief How two members are linked.
245enum class edge_kind : std::uint8_t
246{
247 parent_of, ///< `from` is a parent of `to`.
248 sibling_of, ///< `from` and `to` share a parent; `from` is the older.
249 partner_of, ///< `from` (the anchor, or a parent of the anchor for a stepparent) and `to` are, or were, partners.
250 step_parent_of ///< `from` (a stepfather or stepmother) is a stepparent of `to` (the anchor).
251};
252
253/// @brief How a union ended.
254enum class union_end : std::uint8_t
255{
256 ongoing, ///< It has not ended at the reference date.
257 divorce, ///< The partners separated.
258 death ///< The partner died.
259};
260
261/// @brief The name of an ending as it appears in JSON.
262/// @param e The ending.
263/// @return `ongoing`, `divorce` or `death`.
264[[nodiscard]] constexpr std::string_view union_end_name(union_end e) noexcept
265{
266 switch (e)
267 {
268 case union_end::ongoing: return "ongoing";
269 case union_end::divorce: return "divorce";
270 case union_end::death: return "death";
271 }
272 return "unknown";
273}
274
275/// @brief A link between two members, by id.
276struct edge
277{
278 edge_kind kind{edge_kind::parent_of}; ///< What the link is.
279 std::size_t from{0}; ///< First member.
280 std::size_t to{0}; ///< Second member.
281 bool full{true}; ///< For siblings: both parents are shared.
282 bool genetic{true}; ///< For parents: the child is the parent's by birth, not by adoption.
283 std::optional<int> start{}; ///< For partners: the year the union began.
284 std::optional<int> end{}; ///< For partners: the year it ended, if it has.
285 union_end ended{union_end::ongoing}; ///< For partners: how it ended.
286};
287
288/// @brief The kind of home the anchor lives in at the reference date.
289enum class household_type : std::uint8_t
290{
291 one_person, ///< The anchor lives alone.
292 couple, ///< The anchor and a partner.
293 nuclear, ///< Two adults and children under 18.
294 single_parent, ///< One adult and children under 18.
295 with_parents, ///< An adult living with a parent (and the younger siblings still at home).
296 multi_generation, ///< Three generations, or an adult household that includes a parent.
297 other ///< Anyone else: the relatives the family holds do not live with the anchor.
298};
299
300/// @brief The name of a household type as it appears in JSON.
301/// @param t The type.
302/// @return `one_person`, `couple`, `nuclear`, `single_parent`, `with_parents`,
303/// `multi_generation` or `other`.
304[[nodiscard]] constexpr std::string_view household_type_name(household_type t) noexcept
305{
306 switch (t)
307 {
308 case household_type::one_person: return "one_person";
309 case household_type::couple: return "couple";
310 case household_type::nuclear: return "nuclear";
311 case household_type::single_parent: return "single_parent";
312 case household_type::with_parents: return "with_parents";
313 case household_type::multi_generation: return "multi_generation";
314 case household_type::other: return "other";
315 }
316 return "unknown";
317}
318
319/// @brief Who the anchor lives with.
321{
322 household_type type{household_type::other}; ///< The kind of home.
323 std::vector<std::size_t> members{0}; ///< Ids of the people living there, the anchor (0) first; all alive.
324};
325
326/// @brief Where a table's values came from for the family's country.
327enum class data_source : std::uint8_t
328{
329 country, ///< The country's own row.
330 subregion, ///< The median of its UN subregion.
331 world ///< The world median.
332};
333
334/// @brief The name of a data source as it appears in JSON.
335/// @param s The source.
336/// @return `country`, `subregion` or `world`.
337[[nodiscard]] constexpr std::string_view data_source_name(data_source s) noexcept
338{
339 switch (s)
340 {
341 case data_source::country: return "country";
342 case data_source::subregion: return "subregion";
343 case data_source::world: return "world";
344 }
345 return "unknown";
346}
347
348/// @brief Return type for family generation.
349///
350/// Holds the members (the anchor is `members[0]`) and the links between
351/// them, and supports streaming a short summary via operator<< and JSON
352/// output via to_json().
354{
355 public:
356 std::string country_code; ///< The anchor's country; the family lives there.
357 std::chrono::year_month_day reference_date{}; ///< The date ages and who is alive refer to.
358 std::vector<person> members; ///< Everyone; `members[i].id == i`.
359 std::vector<edge> edges; ///< Parent, sibling and partner links.
360 data_source fertility_source{data_source::country}; ///< Where the fertility figures came from.
361 data_source union_source{data_source::country}; ///< Where the marriage figures came from.
362 std::optional<household> home{}; ///< Who the anchor lives with; set only when `family_request::home` was. // NOLINT(readability-redundant-member-init)
363
364 /// @brief The person the family is built around.
365 /// @return `members[0]`.
366 [[nodiscard]] const person& anchor() const { return members.front(); }
367
368 /// @brief The members with a given role.
369 /// @param r The role.
370 /// @return Copies of them, in id order.
371 [[nodiscard]] std::vector<person> with_role(relation r) const
372 {
373 std::vector<person> out;
374 for (const auto& m : members)
375 {
376 if (m.role == r) out.push_back(m);
377 }
378 return out;
379 }
380
381 /// @brief The anchor's parents.
382 /// @return Father then mother, as present.
383 [[nodiscard]] std::vector<person> parents() const
384 {
385 auto out = with_role(relation::father);
386 for (auto& m : with_role(relation::mother)) out.push_back(std::move(m));
387 return out;
388 }
389
390 /// @brief The anchor's full siblings, oldest first.
391 /// @return The siblings.
392 [[nodiscard]] std::vector<person> siblings() const { return with_role(relation::sibling); }
393
394 /// @brief The anchor's half-siblings, oldest first.
395 ///
396 /// The children of a parent and a stepparent take ids after the
397 /// stepparents (see `family_request::stepparents`), so ids are not in
398 /// birth order when there are some.
399 /// @return The half-siblings.
400 [[nodiscard]] std::vector<person> half_siblings() const
401 {
402 auto out = with_role(relation::half_sibling);
403 std::ranges::stable_sort(out, [](const person& x, const person& y)
404 { return std::chrono::sys_days{x.birth} < std::chrono::sys_days{y.birth}; });
405 return out;
406 }
407
408 /// @brief The anchor's grandparents.
409 /// @return Paternal then maternal, father's side first.
410 [[nodiscard]] std::vector<person> grandparents() const
411 {
412 std::vector<person> out;
413 for (const auto r : {relation::paternal_grandfather, relation::paternal_grandmother,
414 relation::maternal_grandfather, relation::maternal_grandmother})
415 {
416 for (auto& m : with_role(r)) out.push_back(std::move(m));
417 }
418 return out;
419 }
420
421 /// @brief The anchor's partners, in the order the unions began.
422 /// @return The partners, current and former.
423 [[nodiscard]] std::vector<person> partners() const { return with_role(relation::partner); }
424
425 /// @brief The anchor's children, oldest first.
426 /// @return The children, by birth and by adoption.
427 [[nodiscard]] std::vector<person> children() const { return with_role(relation::child); }
428
429 /// @brief The anchor's grandchildren, oldest first.
430 /// @return The grandchildren.
431 [[nodiscard]] std::vector<person> grandchildren() const { return with_role(relation::grandchild); }
432
433 /// @brief The children of the anchor's partners by earlier unions.
434 /// @return The stepchildren, oldest first.
435 [[nodiscard]] std::vector<person> stepchildren() const { return with_role(relation::stepchild); }
436
437 /// @brief The anchor's stepparents: the partners of a parent who are not
438 /// the other parent.
439 /// @return Stepfathers then stepmothers, as present (see `family_request::stepparents`).
440 [[nodiscard]] std::vector<person> stepparents() const
441 {
442 auto out = with_role(relation::stepfather);
443 for (auto& m : with_role(relation::stepmother)) out.push_back(std::move(m));
444 return out;
445 }
446
447 /// @brief The stepparents' children by an earlier union.
448 /// @return The stepsiblings, oldest first (see `family_request::stepparents`).
449 [[nodiscard]] std::vector<person> stepsiblings() const { return with_role(relation::stepsibling); }
450
451 /// @brief The union with a partner.
452 /// @param partner_id The partner's id.
453 /// @return The link, or nothing if the member is not a partner.
454 [[nodiscard]] std::optional<edge> union_with(std::size_t partner_id) const
455 {
456 for (const auto& e : edges)
457 {
458 if (e.kind == edge_kind::partner_of && e.to == partner_id) return e;
459 }
460 return std::nullopt;
461 }
462
463 /// @brief Retrieve the random seed used to generate this family.
464 /// @return The call seed; pass it to get_family(request, seed) with
465 /// the same request and data to replay this family.
466 [[nodiscard]] std::uint64_t seed() const { return _signature.seed; }
467
468 /// @brief The seed and the data this family was generated from.
469 /// @return The replay signature; `data` is relg::data_hash() at the
470 /// time of generation.
471 [[nodiscard]] const core::seed_signature& signature() const
472 {
473 return _signature;
474 }
475
476 /// @brief Human-readable summary string.
477 /// @return For example `BR family of 5 (anchor born 1983-05-20)`.
478 [[nodiscard]] std::string to_string() const
479 {
480 return std::format("{} family of {} (anchor born {})", country_code, members.size(),
481 iso_date(members.front().birth));
482 }
483
484 /// @brief Write this family as a JSON object.
485 /// @param w Writer positioned where a value is allowed.
486 void to_json(core::json_writer& w) const
487 {
488 w.begin_object()
489 .field("country", country_code)
490 .field("reference_date", iso_date(reference_date))
491 .key("members")
492 .begin_array();
493 for (const auto& m : members)
494 {
495 w.begin_object().field("id", m.id).field("role", relation_name(m.role)).field("sex", core::sex_name(m.bio_sex));
496 if (!m.name.empty())
497 {
498 w.key("name").begin_object().field("given", m.given);
499 if (!m.patronymic.empty()) w.field("patronymic", m.patronymic);
500 w.field("family", m.family).field("full", m.name).end_object();
501 }
502 w.field("birth", iso_date(m.birth));
503 if (m.twin) w.field("twin", true);
504 w.field("alive", m.alive);
505 if (m.alive)
506 {
507 w.field("age", static_cast<int>(m.age));
508 }
509 else
510 {
511 w.field("death_age", static_cast<int>(m.age)).field("death", iso_date(*m.death));
512 }
513 w.end_object();
514 }
515 w.end_array().key("edges").begin_array();
516 for (const auto& e : edges)
517 {
518 w.begin_object();
519 switch (e.kind)
520 {
521 case edge_kind::parent_of:
522 w.field("kind", "parent_of").field("from", e.from).field("to", e.to);
523 if (!e.genetic) w.field("genetic", false);
524 break;
525 case edge_kind::sibling_of:
526 w.field("kind", "sibling_of").field("from", e.from).field("to", e.to).field("full", e.full);
527 break;
528 case edge_kind::partner_of:
529 w.field("kind", "partner_of").field("from", e.from).field("to", e.to).field("start", *e.start);
530 if (e.end) w.field("end", *e.end);
531 w.field("ended", union_end_name(e.ended));
532 break;
533 case edge_kind::step_parent_of:
534 w.field("kind", "step_parent_of").field("from", e.from).field("to", e.to);
535 break;
536 }
537 w.end_object();
538 }
539 w.end_array();
540 if (home)
541 {
542 w.key("household").begin_object().field("type", household_type_name(home->type)).key("members").begin_array();
543 for (const auto id : home->members) w.value(id);
544 w.end_array().end_object();
545 }
546 w.key("sources")
547 .begin_object()
548 .field("fertility", data_source_name(fertility_source))
549 .field("unions", data_source_name(union_source))
550 .end_object()
551 .key("signature");
552 _signature.to_json(w);
553 w.end_object();
554 }
555
556 /// @brief This family as a JSON document.
557 /// @return Compact JSON, for example
558 /// `{"country":"BR","reference_date":"2023-07-01","members":[...],"edges":[...],...}`.
559 [[nodiscard]] std::string to_json() const
560 {
561 core::json_writer w;
562 to_json(w);
563 return w.str();
564 }
565
566 /// @brief Implicit conversion to std::string.
567 /// @return Summary string.
568 operator std::string() const // NOLINT(hicpp-explicit-conversions)
569 {
570 return to_string();
571 }
572
573 /// @brief Stream the summary to an output stream.
574 /// @param os Output stream.
575 /// @param v The family to stream.
576 /// @return The stream.
577 friend std::ostream& operator<<(std::ostream& os, const family& v)
578 {
579 os << v.to_string();
580 return os;
581 }
582
583 /// @brief A date as `YYYY-MM-DD`.
584 /// @param d The date.
585 /// @return ISO 8601 text.
586 [[nodiscard]] static std::string iso_date(const std::chrono::year_month_day& d)
587 {
588 return std::format("{:04}-{:02}-{:02}", static_cast<int>(d.year()),
589 static_cast<unsigned>(d.month()), static_cast<unsigned>(d.day()));
590 }
591
592 private:
593 core::seed_signature _signature;
594 friend class relg;
595};
596
597/// @brief The tables of the family data, for extending it.
598enum class family_table : std::uint8_t
599{
600 regions, ///< `regions.tsv`: the UN subregion of each country.
601 fertility, ///< `fertility.tsv`: total fertility rate (now and by period) and fertility by the mother's age.
602 unions, ///< `unions.tsv`: age at first marriage, never married and divorced shares.
603 households, ///< `households.tsv`: household size and types.
604 defaults, ///< `defaults.tsv`: the median of every column per subregion, and the world.
605 parameters ///< `parameters.tsv`: the model's constants.
606};
607
608/// @brief Relationship Generator: builds believable families around a person.
609///
610/// A family is drawn from demography: the mother's age at the anchor's birth
611/// from her country's fertility by age, the father from the typical
612/// difference in marriage ages, the number of siblings from the total
613/// fertility rate of the anchor's birth year (the family size seen from a
614/// child is size-biased, so a family with more children is more likely to be
615/// the anchor's), and who is alive from a Gompertz survival curve that
616/// matches the country's life expectancy. Everything is drawn from named
617/// streams split from the call seed (`"anchor"`, `"parents"`, `"mother"`,
618/// `"father"`, `"siblings"`, `"grandparents"`), so asking for grandparents
619/// or for no siblings does not change the rest. Output is identical on every
620/// compiler and platform (dasmig-core).
621///
622/// The anchor, the country's age pyramid and life expectancy come from
623/// birth-generator (the singleton by default, or an instance passed in).
624///
625/// Can be used as a singleton via instance() or constructed
626/// independently.
627///
628/// @par Thread safety
629/// get_family(request, seed) is const and only calls const members of
630/// birth-generator, so it may run concurrently. get_family(request) draws
631/// from the generator's engine and needs external synchronisation on the
632/// same instance; so do load() and extend().
633class relg
634{
635 public:
636 /// @brief The library instances a family is drawn from.
637 struct sources
638 {
639 bthg* birth{&bthg::instance()}; ///< Anchor, age structure and life expectancy.
640 ng* name{&ng::instance()}; ///< Names.
641 };
642
643 /// @brief Default constructor — creates an empty generator with no
644 /// data, drawing from the birth-generator singleton.
645 relg() = default;
646
647 /// @brief A generator with no data that draws from the given library
648 /// instances (which must outlive it).
649 /// @param from The instances to use.
650 explicit relg(sources from) : _from{from} {}
651
652 relg(const relg&) = delete; ///< Not copyable.
653 relg& operator=(const relg&) = delete; ///< Not copyable.
654 relg(relg&&) noexcept = default; ///< Movable.
655 relg& operator=(relg&&) noexcept = default; ///< Movable.
656 ~relg() = default; ///< Destructor.
657
658 /// @brief Access the global singleton instance.
659 ///
660 /// Loads the `lite` tier (or `full` if `lite` is missing) on first
661 /// access, from compiled-in data or an auto-probed `resources/`.
662 /// @return The singleton.
663 static relg& instance()
664 {
665 static relg inst{auto_probe_tag{}};
666 return inst;
667 }
668
669 // -- Generation -------------------------------------------------------
670
671 /// @brief Generate a random family.
672 /// Use family_request{} for the defaults.
673 /// @param request What to generate.
674 /// @return The family.
675 /// @throws std::runtime_error If this generator or birth-generator has no
676 /// data loaded.
677 /// @throws std::invalid_argument If the request is invalid.
678 [[nodiscard]] family get_family(const family_request& request)
679 {
680 return get_family(request, draw_seed_());
681 }
682
683 /// @brief Generate a deterministic family.
684 /// @param request What to generate.
685 /// @param call_seed Seed; the same request, seed and data give the same
686 /// result. Does not touch the instance engine.
687 /// @return The family.
688 /// @throws std::runtime_error If this generator or birth-generator has no
689 /// data loaded, or names are requested and name-generator has
690 /// none.
691 /// @throws std::invalid_argument If `up` or `down` is not 0 to 2, the
692 /// anchor's age is outside 0 to 122, its birth is after the
693 /// reference date or makes it older than 122, or its country
694 /// code (matched without regard to case) has no data in
695 /// birth-generator.
696 [[nodiscard]] family get_family(const family_request& request, std::uint64_t call_seed) const
697 {
698 require_data_();
699 if (request.up < 0 || request.up > 2)
700 {
701 throw std::invalid_argument("family_request.up must be 0, 1 or 2");
702 }
703 if (request.down < 0 || request.down > 2)
704 {
705 throw std::invalid_argument("family_request.down must be 0, 1 or 2");
706 }
707 if (request.names && !_from.name->has_resources())
708 {
709 throw std::runtime_error("relationship-generator: names were requested and name-generator has no data loaded");
710 }
711 const core::rng call{call_seed};
712 const double ref_day = static_cast<double>(days_(request.reference_date));
713 check_known_(request.anchor);
714
715 const anchor_info anchor = draw_anchor_(request, call.split("anchor").seed(), ref_day);
716 const country_model& cm = lookup_(anchor.country);
717
718 family f;
719 f._signature = {.seed = call_seed, .data = data_hash()};
720 f.country_code = anchor.country;
721 f.reference_date = request.reference_date;
722 f.fertility_source = cm.fertility.source;
723 f.union_source = cm.unions.source;
724
725 // The anchor is alive by definition: a lifespan no one reaches.
726 const auto& spec = request.anchor;
727 const life anchor_life{.birth = anchor.birth_day, .lifespan = never_dies_, .s = anchor.s};
728 std::vector<raw_member> raw;
729 push_(raw, relation::anchor, anchor_life, "anchor");
730 raw[0].fixed = spec.name;
731
732 // The birth of the last child the parents had together: a stepparent
733 // comes after it.
734 double together_until = anchor.birth_day;
735 if (request.up >= 1)
736 {
737 const known_parents known{spec.father ? &*spec.father : nullptr, spec.mother ? &*spec.mother : nullptr};
738 const bool known_siblings = spec.siblings.has_value();
739 // Stepparents need the full siblings even when they are not asked for.
740 auto lin = draw_lineage_(cm, anchor.country, call.split("parents"), anchor.birth_day, ref_day,
741 (request.siblings || request.stepparents) && !known_siblings, known);
742 if (!request.siblings)
743 {
744 for (const auto& s : lin.siblings) together_until = std::max(together_until, s.birth);
745 lin.siblings.clear();
746 }
747 struct half_rec
748 {
749 life l;
750 std::size_t parent;
751 std::string_view label;
752 std::int64_t other;
753 bool father;
754 };
755 std::vector<half_rec> halves;
756 const auto father_slot = raw.size();
757 const auto mother_slot = raw.size() + 1;
758 if (request.siblings && known_siblings)
759 {
760 for (std::size_t i = 0; i < spec.siblings->size(); ++i)
761 {
762 const auto& ks = spec.siblings->at(i);
763 const auto l = life_of_(ks.person, ref_day, static_cast<int>(i));
764 if (ks.full)
765 {
766 lin.siblings.push_back(l);
767 }
768 else
769 {
770 const bool via_father = ks.through == sex::male;
771 halves.push_back({l, via_father ? father_slot : mother_slot, via_father ? "father" : "mother",
772 none_, via_father});
773 }
774 }
775 }
776 // Siblings take ids oldest first; each was drawn from a stream of its
777 // own, so the order does not change what is drawn.
778 std::ranges::stable_sort(lin.siblings, [](const life& x, const life& y) { return x.birth < y.birth; });
779 for (const auto& s : lin.siblings) together_until = std::max(together_until, s.birth);
780 const auto father = push_(raw, relation::father, lin.father, "father");
781 const auto mother = push_(raw, relation::mother, lin.mother, "mother");
782 if (spec.father) raw[father].fixed = spec.father->name;
783 if (spec.mother) raw[mother].fixed = spec.mother->name;
784 raw[father].name_child = 0;
785 raw[mother].name_child = 0;
786 raw[0].parents = {static_cast<std::int64_t>(father), static_cast<std::int64_t>(mother)};
787 for (const auto& s : lin.siblings)
788 {
789 const auto id = push_(raw, relation::sibling, s, "sibling." + std::to_string(s.index));
790 raw[id].parents = raw[0].parents;
791 if (known_siblings) raw[id].fixed = spec.siblings->at(static_cast<std::size_t>(s.index)).person.name;
792 }
793 if (request.siblings)
794 {
795 if (!known_siblings)
796 {
797 const auto half = call.split("half-siblings");
798 // The father's and the mother's other children: the other parent is not a member.
799 const struct
800 {
801 std::size_t parent;
802 std::string_view label;
803 bool father;
804 } sides[] = {{father, "father", true}, {mother, "mother", false}};
805 for (const auto& side : sides)
806 {
807 for (const auto& k : draw_half_siblings_(cm, anchor.country, raw.at(side.parent).life, side.father,
808 side.father ? std::vector<double>{} : lin.mother_births,
809 ref_day, half.split(side.label)))
810 {
811 halves.push_back({k, side.parent, side.label, none_, side.father});
812 }
813 }
814 }
815 // Half-siblings born within a few years of one another through the same
816 // parent are the children of one other parent; later ones, of another.
817 std::ranges::stable_sort(halves, [](const half_rec& x, const half_rec& y) { return x.l.birth < y.l.birth; });
818 std::int64_t next_other = unlisted_ + 1000;
819 std::array<std::int64_t, 2> current{none_, none_};
820 std::array<double, 2> last{0.0, 0.0};
821 for (auto& h : halves)
822 {
823 const std::size_t side = h.father ? 0 : 1;
824 if (current[side] == none_ || h.l.birth - last[side] > half_cluster_years_ * year_days_)
825 {
826 current[side] = next_other++;
827 }
828 last[side] = h.l.birth;
829 h.other = current[side];
830 const auto id = push_(raw, relation::half_sibling, h.l,
831 "half." + std::string{h.label} + "." + std::to_string(h.l.index));
832 raw[id].parents = h.father
833 ? std::array<std::int64_t, 2>{static_cast<std::int64_t>(h.parent), h.other}
834 : std::array<std::int64_t, 2>{h.other, static_cast<std::int64_t>(h.parent)};
835 if (known_siblings) raw[id].fixed = spec.siblings->at(static_cast<std::size_t>(h.l.index)).person.name;
836 }
837 }
838 if (request.up >= 2)
839 {
840 const auto grand = call.split("grandparents");
841 const struct
842 {
843 std::size_t parent;
844 std::string_view label;
845 relation grandfather;
846 relation grandmother;
847 const std::optional<known_person>* given;
848 } sides[] = {{father, "father", relation::paternal_grandfather, relation::paternal_grandmother, &spec.father},
849 {mother, "mother", relation::maternal_grandfather, relation::maternal_grandmother, &spec.mother}};
850 for (const auto& side : sides)
851 {
852 known_parents above{nullptr, nullptr};
853 if (side.given->has_value())
854 {
855 for (const auto& gp : (*side.given)->parents)
856 {
857 (gp.bio_sex == sex::male ? above.father : above.mother) = &gp;
858 }
859 }
860 const auto g = draw_lineage_(cm, anchor.country, grand.split(side.label),
861 raw.at(side.parent).life.birth, ref_day, false, above);
862 const auto gf = push_(raw, side.grandfather, g.father, "gp." + std::string{side.label} + ".m");
863 const auto gm = push_(raw, side.grandmother, g.mother, "gp." + std::string{side.label} + ".f");
864 if (above.father != nullptr) raw[gf].fixed = above.father->name;
865 if (above.mother != nullptr) raw[gm].fixed = above.mother->name;
866 raw[gf].name_child = static_cast<std::int64_t>(side.parent);
867 raw[gm].name_child = static_cast<std::int64_t>(side.parent);
868 raw[side.parent].parents = {static_cast<std::int64_t>(gf), static_cast<std::int64_t>(gm)};
869 }
870 }
871 }
872
873 std::vector<union_rec> unions;
874 if (spec.unions)
875 {
876 for (std::size_t j = 0; j < spec.unions->size(); ++j)
877 {
878 unions.push_back(union_of_(spec.unions->at(j), j, anchor.s, ref_day));
879 }
880 }
881 else if (request.partners || request.down >= 1)
882 {
883 unions = draw_unions_(cm, anchor.country, anchor_life, ref_day, call.split("unions"));
884 }
885 // The member id of each partner, or a token for one who is not listed.
886 std::vector<std::int64_t> partner_slot;
887 std::vector<std::size_t> partner_ids; // by union; none_ for one with no partner member
888 for (std::size_t j = 0; j < unions.size(); ++j)
889 {
890 if (request.partners && unions[j].has_partner)
891 {
892 const auto id = push_(raw, relation::partner, unions[j].partner, "partner." + std::to_string(j));
893 raw[id].fixed = unions[j].partner_name;
894 partner_ids.push_back(id);
895 partner_slot.push_back(static_cast<std::int64_t>(id));
896 }
897 else
898 {
899 partner_ids.push_back(no_member_);
900 partner_slot.push_back(unlisted_ + 10 + static_cast<std::int64_t>(j));
901 }
902 }
903
904 // The partners' children by earlier unions.
905 if (request.down >= 1 && request.partners)
906 {
907 const auto step = call.split("stepchildren");
908 std::vector<std::pair<std::size_t, life>> stepkids; // union, child
909 for (std::size_t j = 0; j < unions.size(); ++j)
910 {
911 if (!unions[j].has_partner) continue;
912 if (spec.unions)
913 {
914 for (std::size_t i = 0; i < spec.unions->at(j).stepchildren.size(); ++i)
915 {
916 stepkids.emplace_back(j, life_of_(spec.unions->at(j).stepchildren[i], ref_day, static_cast<int>(i)));
917 }
918 continue;
919 }
920 for (const auto& k : draw_stepchildren_(cm, anchor.country, unions[j], ref_day, step.split(static_cast<std::uint64_t>(j))))
921 {
922 stepkids.emplace_back(j, k);
923 }
924 }
925 std::ranges::stable_sort(stepkids, [](const auto& x, const auto& y) { return x.second.birth < y.second.birth; });
926 for (const auto& [j, k] : stepkids)
927 {
928 const auto partner = partner_ids.at(j);
929 const auto token = unlisted_ + 500 + static_cast<std::int64_t>(j);
930 const auto id = push_(raw, relation::stepchild, k, "step." + std::to_string(j) + "." + std::to_string(k.index));
931 raw[id].parents = raw.at(partner).life.s == sex::male
932 ? std::array<std::int64_t, 2>{static_cast<std::int64_t>(partner), token}
933 : std::array<std::int64_t, 2>{token, static_cast<std::int64_t>(partner)};
934 if (spec.unions) raw[id].fixed = spec.unions->at(j).stepchildren.at(static_cast<std::size_t>(k.index)).name;
935 }
936 }
937
938 if (request.down >= 1)
939 {
940 auto kids = spec.unions ? known_children_(*spec.unions, ref_day)
941 : draw_union_children_(cm, anchor.country, anchor_life, unions, ref_day,
942 call.split("children"));
943 std::ranges::stable_sort(kids, [](const child_rec& x, const child_rec& y) { return x.life.birth < y.life.birth; });
944 const std::size_t first_child = raw.size();
945 for (const auto& k : kids)
946 {
947 const auto& u = unions.at(static_cast<std::size_t>(k.union_index));
948 const std::int64_t other = partner_slot.at(static_cast<std::size_t>(k.union_index));
949 // The father's slot first; in a same-sex union, the anchor's.
950 const bool anchor_first = u.same_sex || anchor.s == sex::male;
951 const auto id = push_(raw, relation::child, k.life,
952 "child." + std::to_string(k.union_index) + "." + std::to_string(k.life.index));
953 raw[id].parents = anchor_first ? std::array<std::int64_t, 2>{0, other} : std::array<std::int64_t, 2>{other, 0};
954 raw[id].genetic = k.genetic;
955 raw[id].fixed = k.fixed;
956 }
957 if (request.down >= 2)
958 {
959 const auto grand = call.split("grandchildren");
960 std::vector<std::size_t> parents_of_grandchildren;
961 for (std::size_t c = first_child; c < raw.size(); ++c) parents_of_grandchildren.push_back(c);
962 std::vector<std::pair<std::size_t, life>> grandchildren;
963 for (const auto c : parents_of_grandchildren)
964 {
965 const auto kids2 = draw_grandchildren_(cm, anchor.country, raw.at(c).life, ref_day, grand.split(raw.at(c).key));
966 for (const auto& k : kids2) grandchildren.emplace_back(c, k);
967 }
968 std::ranges::stable_sort(grandchildren, [](const auto& x, const auto& y) { return x.second.birth < y.second.birth; });
969 for (const auto& [c, k] : grandchildren)
970 {
971 const auto token = unlisted_ + 100 + static_cast<std::int64_t>(c);
972 const auto id = push_(raw, relation::grandchild, k, "grand." + raw.at(c).key + "." + std::to_string(k.index));
973 raw[id].parents = raw.at(c).life.s == sex::male
974 ? std::array<std::int64_t, 2>{static_cast<std::int64_t>(c), token}
975 : std::array<std::int64_t, 2>{token, static_cast<std::int64_t>(c)};
976 }
977 }
978 }
979
980 // The partners of a parent who are not the other parent. They take the
981 // last ids, so asking for them changes no other member's id.
982 std::vector<step_rec> steps;
983 std::vector<std::size_t> step_ids;
984 std::vector<std::size_t> step_parents;
985 if (request.stepparents && request.up >= 1)
986 {
987 const auto sp = call.split("stepparents");
988 const struct
989 {
990 std::size_t parent;
991 std::string_view label;
992 } sides[] = {{1, "father"}, {2, "mother"}};
993 for (const auto& side : sides)
994 {
995 const auto rec = draw_stepparent_(cm, anchor.country, raw.at(side.parent).life, anchor.birth_day,
996 together_until, ref_day, sp.split(side.label));
997 if (!rec) continue;
998 const auto id = push_(raw, rec->partner.s == sex::male ? relation::stepfather : relation::stepmother,
999 rec->partner, "stepparent." + std::string{side.label});
1000 raw[id].name_child = static_cast<std::int64_t>(side.parent);
1001 steps.push_back(*rec);
1002 step_ids.push_back(id);
1003 step_parents.push_back(side.parent);
1004 }
1005 }
1006
1007 // The children a parent had with a stepparent of the other sex: the
1008 // half-siblings already drawn who were born during the union, and more
1009 // up to the union's own fertility (a later union's). Then the
1010 // stepparents' own children by an earlier union, the stepsiblings.
1011 // Both take the ids after the stepparents, each oldest first.
1012 if (request.siblings && !steps.empty())
1013 {
1014 const auto& p = _model.params;
1015 const auto sib = call.split("stepsiblings");
1016 std::vector<std::pair<std::size_t, life>> joint; // step index, child
1017 std::vector<std::pair<std::size_t, life>> stepsibs; // step index, child
1018 for (std::size_t k = 0; k < steps.size(); ++k)
1019 {
1020 const auto parent = step_parents[k];
1021 const auto sp = step_ids[k];
1022 const auto rk = sib.split(raw.at(sp).key);
1023 const union_rec u{.partner = steps[k].partner, .start = steps[k].start};
1024 for (const auto& c : draw_stepchildren_(cm, anchor.country, u, ref_day, rk)) stepsibs.emplace_back(k, c);
1025 if (steps[k].partner.s == raw.at(parent).life.s) continue;
1026
1027 const int tied = tie_half_siblings_(raw, parent, sp, steps[k], p);
1028 const double mean = (tfr_at_(cm.fertility, 1970.0 + (steps[k].start / year_days_)) + p.union_fertility_offset) *
1029 p.union_fertility * p.later_union_factor;
1030 auto count_rng = rk.split("count");
1031 const int n = std::min(family_size_(count_rng, mean), static_cast<int>(p.max_children));
1032 const bool via_father = raw.at(parent).life.s == sex::male;
1033 const auto mother_id = static_cast<std::int64_t>(via_father ? sp : parent);
1034 std::vector<double> births; // the mother's, so far
1035 for (const auto& m : raw)
1036 {
1037 if (m.parents[1] == mother_id) births.push_back(m.life.birth);
1038 }
1039 for (const auto& [j, c] : stepsibs)
1040 {
1041 if (j == k && via_father) births.push_back(c.birth);
1042 }
1043 const life& mother = via_father ? steps[k].partner : raw.at(parent).life;
1044 const life& father = via_father ? raw.at(parent).life : steps[k].partner;
1045 for (const auto& c : place_union_births_(cm, anchor.country, mother, father, steps[k].start,
1046 steps[k].end, tied, n, births, rk.split("joint")))
1047 {
1048 joint.emplace_back(k, c.life);
1049 }
1050 }
1051 std::ranges::stable_sort(joint, [](const auto& x, const auto& y) { return x.second.birth < y.second.birth; });
1052 std::ranges::stable_sort(stepsibs, [](const auto& x, const auto& y) { return x.second.birth < y.second.birth; });
1053 for (const auto& [k, c] : joint)
1054 {
1055 const auto parent = static_cast<std::int64_t>(step_parents[k]);
1056 const auto sp = static_cast<std::int64_t>(step_ids[k]);
1057 const auto id = push_(raw, relation::half_sibling, c,
1058 "joint." + raw.at(step_ids[k]).key + "." + std::to_string(c.index));
1059 raw[id].parents = raw.at(step_parents[k]).life.s == sex::male ? std::array<std::int64_t, 2>{parent, sp}
1060 : std::array<std::int64_t, 2>{sp, parent};
1061 }
1062 for (const auto& [k, c] : stepsibs)
1063 {
1064 const auto sp = step_ids[k];
1065 const auto token = unlisted_ + 600 + static_cast<std::int64_t>(k);
1066 const auto id = push_(raw, relation::stepsibling, c, "stepsib." + raw.at(sp).key + "." + std::to_string(c.index));
1067 raw[id].parents = raw.at(sp).life.s == sex::male
1068 ? std::array<std::int64_t, 2>{static_cast<std::int64_t>(sp), token}
1069 : std::array<std::int64_t, 2>{token, static_cast<std::int64_t>(sp)};
1070 }
1071 }
1072
1073 // Parent links, then sibling links (members that share a parent, oldest
1074 // first, full when both parents are shared), then the anchor's unions.
1075 for (std::size_t i = 0; i < raw.size(); ++i)
1076 {
1077 for (const auto p : raw[i].parents)
1078 {
1079 if (p >= 0 && p < unlisted_)
1080 {
1081 f.edges.push_back({.kind = edge_kind::parent_of, .from = static_cast<std::size_t>(p), .to = i,
1082 .full = true, .genetic = raw[i].genetic});
1083 }
1084 }
1085 }
1086 std::vector<bool> twin(raw.size(), false);
1087 for (std::size_t i = 0; i < raw.size(); ++i)
1088 {
1089 for (std::size_t k = i + 1; k < raw.size(); ++k)
1090 {
1091 const auto& a = raw[i].parents;
1092 const auto& b = raw[k].parents;
1093 const auto shares = [&](std::int64_t x) { return x != none_ && (x == b[0] || x == b[1]); };
1094 if (!shares(a[0]) && !shares(a[1])) continue;
1095 const bool full = (a[0] == b[0] && a[1] == b[1]) || (a[0] == b[1] && a[1] == b[0]);
1096 if (full && std::floor(raw[i].life.birth) == std::floor(raw[k].life.birth)) twin[i] = twin[k] = true;
1097 const bool older = raw[i].life.birth <= raw[k].life.birth;
1098 f.edges.push_back({.kind = edge_kind::sibling_of, .from = older ? i : k, .to = older ? k : i,
1099 .full = full});
1100 }
1101 }
1102 for (std::size_t j = 0; j < partner_ids.size(); ++j)
1103 {
1104 if (partner_ids[j] == no_member_) continue;
1105 const auto& u = unions[j];
1106 edge e{.kind = edge_kind::partner_of, .from = 0, .to = partner_ids[j]};
1107 e.start = static_cast<int>(from_days_(u.start).year());
1108 if (u.ended != union_end::ongoing) e.end = static_cast<int>(from_days_(u.end).year());
1109 e.ended = u.ended;
1110 f.edges.push_back(e);
1111 }
1112 for (std::size_t k = 0; k < steps.size(); ++k)
1113 {
1114 edge e{.kind = edge_kind::partner_of, .from = step_parents[k], .to = step_ids[k]};
1115 e.start = static_cast<int>(from_days_(steps[k].start).year());
1116 if (steps[k].ended != union_end::ongoing) e.end = static_cast<int>(from_days_(steps[k].end).year());
1117 e.ended = steps[k].ended;
1118 f.edges.push_back(e);
1119 f.edges.push_back({.kind = edge_kind::step_parent_of, .from = step_ids[k], .to = 0});
1120 }
1121
1122 for (std::size_t i = 0; i < raw.size(); ++i)
1123 {
1124 f.members.push_back(to_person_(raw[i], i, ref_day));
1125 f.members.back().twin = twin[i];
1126 }
1127 if (request.names) assign_names_(f.members, raw, call.split("names"), anchor.country, request.name_culture);
1128 if (request.home) f.home = draw_household_(cm, f, call.split("household"));
1129 return f;
1130 }
1131
1132 /// @brief Generate the family of a member of a family, so that the two agree.
1133 ///
1134 /// The member's own birth, sex, name and country are kept, and so are the
1135 /// relatives `base` already has for the member: their parents (and the
1136 /// grandparents, if `base` has them), siblings and half-siblings, and the
1137 /// partners and children that `base` lists. Whatever `base` does not hold
1138 /// is drawn, as get_family() draws it: a partner, say, whom `base` left
1139 /// out is drawn again, and children whose other parent `base` left out
1140 /// are kept without that partner.
1141 ///
1142 /// Only what `base` lists is kept: a member that `base` generated with
1143 /// `partners = false` or `down = 0` has its partners or children drawn
1144 /// again. The request's `anchor` is ignored (`member` takes its place),
1145 /// and so is its `reference_date` (that of `base` is used); `up`, `down`,
1146 /// `siblings`, `partners`, `names`, `name_culture` and `weighted` apply.
1147 ///
1148 /// @param base The family the member belongs to.
1149 /// @param member The member's id (`base.members[member]`).
1150 /// @param request What to generate around the member.
1151 /// @param call_seed Seed; the same base, member, request and seed give the
1152 /// same family.
1153 /// @return The member's family; its anchor is the member.
1154 /// @throws std::invalid_argument If `member` is not an id of `base`.
1155 [[nodiscard]] family expand(const family& base, std::size_t member, family_request request,
1156 std::uint64_t call_seed) const
1157 {
1158 if (member >= base.members.size()) throw std::invalid_argument("expand: the member is not in the family");
1159 request.reference_date = base.reference_date;
1160 request.anchor = anchor_of_(base, member);
1161 return get_family(request, call_seed);
1162 }
1163
1164 /// @brief Generate a random family of a member of a family; see the
1165 /// seeded overload.
1166 /// @param base The family the member belongs to.
1167 /// @param member The member's id.
1168 /// @param request What to generate around the member.
1169 /// @return The member's family.
1170 [[nodiscard]] family expand(const family& base, std::size_t member, const family_request& request)
1171 {
1172 return expand(base, member, request, draw_seed_());
1173 }
1174
1175 // -- Batch generation -------------------------------------------------
1176
1177 /// @brief Generate a batch of family results from a seed drawn like
1178 /// get_family() draws its own.
1179 ///
1180 /// Item `i` is `get_family(request, core::item_seed(batch_seed, options.first + i))`
1181 /// for the drawn batch seed; see the seeded overload.
1182 ///
1183 /// @param request What every item is generated from.
1184 /// @param options Count, first index, threads and uniqueness.
1185 /// @return The results, in index order.
1186 [[nodiscard]] std::vector<family> get_family_batch(const family_request& request,
1187 const core::batch_options& options)
1188 {
1189 return get_family_batch(request, options, draw_seed_());
1190 }
1191
1192 /// @brief Generate a batch of family results, deterministically.
1193 ///
1194 /// Item `i` is exactly `get_family(request, core::item_seed(batch_seed,
1195 /// options.first + i))`, so an item replays on its own, a range can be
1196 /// generated directly, two batches that follow each other equal one, and
1197 /// the output does not depend on `options.threads`.
1198 ///
1199 /// @param request What every item is generated from.
1200 /// @param options Count, first index, threads and uniqueness (see
1201 /// core::unique_rule; the rule must be made for `family`).
1202 /// @param batch_seed Seed of the whole batch.
1203 /// @return The results, in index order.
1204 /// @throws std::invalid_argument If the request or options are invalid.
1205 /// @throws std::runtime_error If no data has been loaded, or a unique
1206 /// batch runs out of attempts.
1207 [[nodiscard]] std::vector<family> get_family_batch(const family_request& request,
1208 const core::batch_options& options,
1209 std::uint64_t batch_seed) const
1210 {
1211 return core::run_batch(options, batch_seed, [&](std::uint64_t item_seed)
1212 { return get_family(request, item_seed); });
1213 }
1214
1215 /// @brief Stream a batch of family results to a sink without holding the
1216 /// whole batch.
1217 ///
1218 /// Generates the same items as get_family_batch() and calls
1219 /// `sink(index, item)` in index order on the calling thread; `index`
1220 /// includes `options.first`.
1221 ///
1222 /// @tparam Sink Callable taking `(std::uint64_t, family&)`; for example
1223 /// core::jsonl_writer or core::csv_writer.
1224 /// @param request What every item is generated from.
1225 /// @param options Count, first index, threads and uniqueness.
1226 /// @param batch_seed Seed of the whole batch.
1227 /// @param sink Receives the items.
1228 template <typename Sink>
1229 void for_each_family(const family_request& request, const core::batch_options& options,
1230 std::uint64_t batch_seed, Sink&& sink) const
1231 {
1232 core::for_each_batch(options, batch_seed,
1233 [&](std::uint64_t item_seed) { return get_family(request, item_seed); },
1234 std::forward<Sink>(sink));
1235 }
1236
1237 /// @brief Stream a batch of family results with a batch seed drawn like
1238 /// get_family() draws its own.
1239 /// @tparam Sink Callable taking `(std::uint64_t, family&)`.
1240 /// @param request What every item is generated from.
1241 /// @param options Count, first index, threads and uniqueness.
1242 /// @param sink Receives the items.
1243 template <typename Sink>
1244 void for_each_family(const family_request& request, const core::batch_options& options,
1245 Sink&& sink)
1246 {
1247 for_each_family(request, options, draw_seed_(), std::forward<Sink>(sink));
1248 }
1249
1250 // -- Data management --------------------------------------------------
1251
1252 /// @brief Check whether this generator and birth-generator have data.
1253 /// @return True when a family can be generated.
1254 [[nodiscard]] bool has_data() const { return has_own_data_() && _from.birth->has_data(); }
1255
1256 /// @brief Check whether the family tables are loaded (birth-generator
1257 /// is not asked).
1258 /// @return True when fertility, parameters and defaults are non-empty.
1259 [[nodiscard]] bool has_family_data() const { return has_own_data_(); }
1260
1261 /// @brief The countries with figures of their own in any table.
1262 /// @return ISO 3166-1 alpha-2 codes, sorted.
1263 [[nodiscard]] std::vector<std::string> countries() const
1264 {
1265 std::vector<std::string> out;
1266 out.reserve(_model.countries.size());
1267 for (const auto& [code, _] : _model.countries) out.push_back(code);
1268 std::ranges::sort(out);
1269 return out;
1270 }
1271
1272 /// @brief Load data from a resource directory, replacing all data and
1273 /// layers loaded before.
1274 ///
1275 /// The directory must contain `regions.tsv`, `fertility.tsv`,
1276 /// `unions.tsv`, `defaults.tsv` and `parameters.tsv`; `households.tsv`
1277 /// is read when present. A path without them is ignored.
1278 ///
1279 /// @param dir Directory such as `resources/lite`.
1280 /// @throws core::tsv_error If a file is malformed; the data is then
1281 /// unchanged.
1282 void load(const std::filesystem::path& dir)
1283 {
1284 std::error_code ec;
1285 if (!std::filesystem::is_directory(dir, ec)) return;
1286 for (const auto t : required_tables_)
1287 {
1288 if (!std::filesystem::is_regular_file(dir / file_name_(t), ec)) return;
1289 }
1290 apply_(read_directory_(dir), core::layer_mode::replace, true);
1291 }
1292
1293 /// @brief Load a dataset tier, replacing all data and layers loaded
1294 /// before.
1295 ///
1296 /// Uses the compiled-in copy when the library is built with
1297 /// `DASMIG_RELATIONSHIP_EMBED`, otherwise searches `resources/`, `../resources`,
1298 /// `relationship-generator/resources` and `../relationship-generator/resources`.
1299 ///
1300 /// @param tier The tier.
1301 /// @return False when the tier was not found.
1302 /// @throws core::tsv_error If a file is malformed.
1303 [[nodiscard]] bool load(dataset tier)
1304 {
1305 const std::string tier_dir{core::tier_name(tier)};
1306 const auto marker = tier_dir + "/" + std::string{file_name_(family_table::fertility)};
1307 const auto root = core::find_resources(library_name_, marker);
1308 documents docs;
1309 for (const auto t : all_tables_)
1310 {
1311 docs.at(index_(t)) = core::load_resource(tier_dir + "/" + std::string{file_name_(t)},
1312 embedded_files_(), root);
1313 }
1314 for (const auto t : required_tables_)
1315 {
1316 if (!docs.at(index_(t))) return false;
1317 }
1318 apply_(std::move(docs), core::layer_mode::replace, true);
1319 return true;
1320 }
1321
1322 /// @brief Add data layers from a directory or a file.
1323 ///
1324 /// A directory may hold any of the table files (see family_table); all are
1325 /// applied together or, if any fails, none. A file must have one of those
1326 /// names.
1327 ///
1328 /// @param path Directory or file.
1329 /// @param mode How the layers combine with the data loaded before.
1330 /// @throws core::tsv_error If a file cannot be read or a row is
1331 /// invalid; the data is then unchanged.
1332 /// @throws std::invalid_argument If @p path is a file with another name.
1333 void extend(const std::filesystem::path& path, core::layer_mode mode = core::layer_mode::override)
1334 {
1335 std::error_code ec;
1336 if (std::filesystem::is_directory(path, ec))
1337 {
1338 apply_(read_directory_(path), mode, false);
1339 return;
1340 }
1341 for (const auto t : all_tables_)
1342 {
1343 if (path.filename() == file_name_(t))
1344 {
1345 (void)extend(t, core::tsv_document::load(path), mode);
1346 return;
1347 }
1348 }
1349 throw std::invalid_argument("extend: expected one of the family tables (regions.tsv, fertility.tsv, "
1350 "unions.tsv, households.tsv, defaults.tsv, parameters.tsv), got " +
1351 path.filename().string());
1352 }
1353
1354 /// @brief Add a data layer to one table.
1355 ///
1356 /// Rows are keyed by the table's key column (`cca2`, `subregion` or
1357 /// `key`). In the default override mode a row replaces the loaded row with
1358 /// the same key and new keys are added; a row with `disabled` set to 1
1359 /// removes its key.
1360 ///
1361 /// @param which The table.
1362 /// @param doc TSV with that table's columns.
1363 /// @param mode How the layer combines with the data loaded before.
1364 /// @return What the layer did (rows added, replaced, removed, warnings).
1365 /// @throws core::tsv_error If a row is invalid; the data is unchanged.
1366 const core::layer_info& extend(family_table which, core::tsv_document doc,
1367 core::layer_mode mode = core::layer_mode::override)
1368 {
1369 documents docs;
1370 docs.at(index_(which)).emplace(std::move(doc));
1371 apply_(std::move(docs), mode, false);
1372 return _tables.at(index_(which)).layers().back();
1373 }
1374
1375 /// @brief Add a data layer to one table from TSV text (for example from a
1376 /// WebAssembly host).
1377 /// @param which The table.
1378 /// @param text TSV with that table's columns.
1379 /// @param source Name used in errors.
1380 /// @param mode How the layer combines with the data loaded before.
1381 /// @return What the layer did.
1382 /// @throws core::tsv_error If a row is invalid; the data is unchanged.
1383 const core::layer_info& extend_from_string(family_table which, std::string_view text, std::string source,
1384 core::layer_mode mode = core::layer_mode::override)
1385 {
1386 return extend(which, core::tsv_document::parse(text, std::move(source)), mode);
1387 }
1388
1389 /// @brief The layers in effect for one table, in order.
1390 /// @param which The table.
1391 /// @return The layer records.
1392 [[nodiscard]] const std::vector<core::layer_info>& layers(family_table which) const
1393 {
1394 return _tables.at(index_(which)).layers();
1395 }
1396
1397 /// @brief Hash of the loaded data: this generator's tables and
1398 /// birth-generator's data. Recorded in each result's signature().
1399 /// @return A hash that changes when either does; 0 when this
1400 /// generator's tables are empty.
1401 [[nodiscard]] std::uint64_t data_hash() const
1402 {
1403 std::uint64_t own = 0;
1404 bool any = false;
1405 for (const auto& t : _tables)
1406 {
1407 any = any || !t.layers().empty();
1408 own = core::hash_combine(own, t.data_hash());
1409 }
1410 if (!any) return 0;
1411 return core::hash_combine(core::hash_combine(own, _from.birth->data_hash()), _from.name->data_hash());
1412 }
1413
1414 private:
1415 using table = family_table;
1416
1417 // -- Tables -------------------------------------------------------------
1418
1419 static constexpr std::string_view library_name_ = "relationship-generator";
1420 static constexpr std::size_t table_count_ = 6;
1421 static constexpr std::array<table, table_count_> all_tables_{table::regions, table::fertility,
1422 table::unions, table::households,
1423 table::defaults, table::parameters};
1424 static constexpr std::array<table, 5> required_tables_{table::regions, table::fertility, table::unions,
1425 table::defaults, table::parameters};
1426 static constexpr std::array<std::string_view, 8> age_columns_{
1427 "pasfr_10_14", "pasfr_15_19", "pasfr_20_24", "pasfr_25_29",
1428 "pasfr_30_34", "pasfr_35_39", "pasfr_40_44", "pasfr_45_49"};
1429 static constexpr std::array<int, 15> tfr_years_{1950, 1955, 1960, 1965, 1970, 1975, 1980, 1985,
1430 1990, 1995, 2000, 2005, 2010, 2015, 2020};
1431 static constexpr double year_days_ = 365.2425;
1432 static constexpr double euler_gamma_ = 0.57721566490153286;
1433
1434 using documents = std::array<std::optional<core::tsv_document>, table_count_>;
1435 using tables = std::array<core::layered_table, table_count_>;
1436 using cells = std::unordered_map<std::string, double>;
1437
1438 static constexpr std::size_t index_(table t) { return static_cast<std::size_t>(t); }
1439
1440 static constexpr std::string_view file_name_(table t)
1441 {
1442 switch (t)
1443 {
1444 case table::regions: return "regions.tsv";
1445 case table::fertility: return "fertility.tsv";
1446 case table::unions: return "unions.tsv";
1447 case table::households: return "households.tsv";
1448 case table::defaults: return "defaults.tsv";
1449 case table::parameters: return "parameters.tsv";
1450 }
1451 return "";
1452 }
1453
1454 // Every numeric column of the demography tables, by table.
1455 static std::vector<std::string> numeric_columns_(table t)
1456 {
1457 std::vector<std::string> cols;
1458 const auto fertility = [&]
1459 {
1460 cols.emplace_back("tfr");
1461 cols.emplace_back("mac");
1462 for (const auto c : age_columns_) cols.emplace_back(c);
1463 for (const int y : tfr_years_) cols.push_back("tfr_" + std::to_string(y));
1464 };
1465 const auto unions = [&]
1466 {
1467 for (const char* c : {"smam_m", "smam_f", "never_m_45_49", "never_f_45_49", "divorced_m_45_49",
1468 "divorced_f_45_49"})
1469 {
1470 cols.emplace_back(c);
1471 }
1472 };
1473 const auto households = [&]
1474 {
1475 for (const char* c : {"size", "one_person", "single_parent", "multi_generation"}) cols.emplace_back(c);
1476 };
1477 switch (t)
1478 {
1479 case table::fertility: fertility(); break;
1480 case table::unions: unions(); break;
1481 case table::households: households(); break;
1482 case table::defaults:
1483 fertility();
1484 unions();
1485 households();
1486 break;
1487 case table::regions:
1488 case table::parameters: break;
1489 }
1490 return cols;
1491 }
1492
1493 static core::table_schema schema_(table t)
1494 {
1495 switch (t)
1496 {
1497 case table::regions: return {.key = {"cca2"}, .required = {"subregion"}, .optional = {}};
1498 case table::fertility:
1499 {
1500 core::table_schema s{.key = {"cca2"}, .required = {"tfr", "mac"}, .optional = {}};
1501 for (const auto c : age_columns_) s.required.emplace_back(c);
1502 for (const int y : tfr_years_) s.optional.push_back("tfr_" + std::to_string(y));
1503 return s;
1504 }
1505 case table::unions:
1506 {
1507 core::table_schema s{.key = {"cca2"}, .required = {}, .optional = {"year"}};
1508 for (auto& c : numeric_columns_(t)) s.optional.push_back(std::move(c));
1509 return s;
1510 }
1511 case table::households:
1512 {
1513 core::table_schema s{.key = {"cca2"}, .required = {}, .optional = {"year", "source"}};
1514 for (auto& c : numeric_columns_(t)) s.optional.push_back(std::move(c));
1515 return s;
1516 }
1517 case table::defaults:
1518 {
1519 core::table_schema s{.key = {"subregion"}, .required = {}, .optional = {"countries"}};
1520 for (auto& c : numeric_columns_(t)) s.optional.push_back(std::move(c));
1521 return s;
1522 }
1523 case table::parameters: return {.key = {"key"}, .required = {"value"}, .optional = {"note"}};
1524 }
1525 return {};
1526 }
1527
1528 static tables make_tables_()
1529 {
1530 return {core::layered_table{schema_(table::regions)}, core::layered_table{schema_(table::fertility)},
1531 core::layered_table{schema_(table::unions)}, core::layered_table{schema_(table::households)},
1532 core::layered_table{schema_(table::defaults)}, core::layered_table{schema_(table::parameters)}};
1533 }
1534
1535 // -- Model --------------------------------------------------------------
1536
1537 /// The constants of the model; the shipped `parameters.tsv` repeats these.
1538 struct parameters
1539 {
1540 double min_mother_age{12.0};
1541 double max_mother_age{49.9};
1542 double min_father_age{15.0};
1543 double max_father_age{80.0};
1544 double father_gap_sd{3.5};
1545 double max_gap_mean{8.0};
1546 double conception_lead{0.75};
1547 double birth_spacing{0.75};
1548 double gompertz_beta{0.09};
1549 double adult_intercept{26.0};
1550 double adult_slope{0.7};
1551 double child_q15_scale{0.012};
1552 double child_q15_decay{11.0};
1553 double dispersion_intercept{0.2};
1554 double dispersion_slope{0.27};
1555 double dispersion_min{0.3};
1556 double max_siblings{14.0};
1557 double male_birth_share{0.512};
1558 double min_union_age{16.0};
1559 double union_age_sd{4.5};
1560 double max_unions{3.0};
1561 double same_sex_share{0.03};
1562 double remarry_share{0.45};
1563 double divorce_duration_mean{9.0};
1564 double remarry_gap_mean{3.0};
1565 double union_fertility{1.0};
1566 double union_fertility_offset{0.2};
1567 double union_early_max{3.0};
1568 double later_union_factor{0.6};
1569 double adoption_factor{0.15};
1570 double half_sibling_factor{0.6};
1571 double half_sibling_extra{0.5};
1572 double married_name_share{0.8};
1573 double max_children{12.0};
1574 double twin_share{0.012};
1575 double stepchild_factor{0.6};
1576 double stepparent_factor{1.0};
1577 double stepparent_start_mean{6.0};
1578 double mother_custody_share{0.8};
1579 double household_minor_age{18.0};
1580 double young_adult_home_age{30.0};
1581 double young_adult_home_share{0.5};
1582 double one_person_scale{3.0};
1583 };
1584
1585 struct param_def
1586 {
1587 std::string_view key;
1588 double parameters::*member;
1589 };
1590
1591 static constexpr std::array<param_def, 43> param_table_{{
1592 {"min_mother_age", &parameters::min_mother_age},
1593 {"max_mother_age", &parameters::max_mother_age},
1594 {"min_father_age", &parameters::min_father_age},
1595 {"max_father_age", &parameters::max_father_age},
1596 {"father_gap_sd", &parameters::father_gap_sd},
1597 {"max_gap_mean", &parameters::max_gap_mean},
1598 {"conception_lead", &parameters::conception_lead},
1599 {"birth_spacing", &parameters::birth_spacing},
1600 {"gompertz_beta", &parameters::gompertz_beta},
1601 {"adult_intercept", &parameters::adult_intercept},
1602 {"adult_slope", &parameters::adult_slope},
1603 {"child_q15_scale", &parameters::child_q15_scale},
1604 {"child_q15_decay", &parameters::child_q15_decay},
1605 {"dispersion_intercept", &parameters::dispersion_intercept},
1606 {"dispersion_slope", &parameters::dispersion_slope},
1607 {"dispersion_min", &parameters::dispersion_min},
1608 {"max_siblings", &parameters::max_siblings},
1609 {"male_birth_share", &parameters::male_birth_share},
1610 {"min_union_age", &parameters::min_union_age},
1611 {"union_age_sd", &parameters::union_age_sd},
1612 {"max_unions", &parameters::max_unions},
1613 {"same_sex_share", &parameters::same_sex_share},
1614 {"remarry_share", &parameters::remarry_share},
1615 {"divorce_duration_mean", &parameters::divorce_duration_mean},
1616 {"remarry_gap_mean", &parameters::remarry_gap_mean},
1617 {"union_fertility", &parameters::union_fertility},
1618 {"union_fertility_offset", &parameters::union_fertility_offset},
1619 {"union_early_max", &parameters::union_early_max},
1620 {"later_union_factor", &parameters::later_union_factor},
1621 {"adoption_factor", &parameters::adoption_factor},
1622 {"half_sibling_factor", &parameters::half_sibling_factor},
1623 {"half_sibling_extra", &parameters::half_sibling_extra},
1624 {"married_name_share", &parameters::married_name_share},
1625 {"max_children", &parameters::max_children},
1626 {"twin_share", &parameters::twin_share},
1627 {"stepchild_factor", &parameters::stepchild_factor},
1628 {"stepparent_factor", &parameters::stepparent_factor},
1629 {"stepparent_start_mean", &parameters::stepparent_start_mean},
1630 {"mother_custody_share", &parameters::mother_custody_share},
1631 {"household_minor_age", &parameters::household_minor_age},
1632 {"young_adult_home_age", &parameters::young_adult_home_age},
1633 {"young_adult_home_share", &parameters::young_adult_home_share},
1634 {"one_person_scale", &parameters::one_person_scale},
1635 }};
1636
1637 struct fertility_row
1638 {
1639 double tfr{0.0};
1640 std::vector<std::pair<int, double>> history; // year, tfr, ascending, ending with the current one
1641 std::optional<core::discrete> by_age; // the eight five-year groups from 10-14
1642 data_source source{data_source::country};
1643 };
1644
1645 struct union_row
1646 {
1647 double smam_m{0.0};
1648 double smam_f{0.0};
1649 double never_m{0.0}; // percent still never married at 45 to 49
1650 double never_f{0.0};
1651 double divorced_m{0.0}; // percent divorced or separated at 45 to 49
1652 double divorced_f{0.0};
1653 data_source source{data_source::country};
1654 };
1655
1656 struct household_row
1657 {
1658 double size{0.0}; // mean persons per household
1659 double one_person{0.0}; // percent of households with one person
1660 double single_parent{0.0}; // percent of households with a lone parent and children
1661 double multi_generation{0.0}; // percent of households with three generations
1662 data_source source{data_source::country};
1663 };
1664
1665 struct country_model
1666 {
1667 fertility_row fertility;
1668 union_row unions;
1669 household_row households;
1670 };
1671
1672 struct model
1673 {
1674 parameters params;
1675 std::unordered_map<std::string, country_model> countries;
1676 country_model world;
1677 bool ready{false};
1678 };
1679
1680 static void check_number_(const core::tsv_row& row, std::string_view column)
1681 {
1682 if (row.has(column))
1683 {
1684 const double v = row.get<double>(column);
1685 if (!std::isfinite(v)) throw row.error(column, "must be a finite number");
1686 }
1687 }
1688
1689 static void validate_(table t, const core::tsv_row& row)
1690 {
1691 switch (t)
1692 {
1693 case table::regions:
1694 if (row.get<std::string>("subregion").empty()) throw row.error("subregion", "must not be empty");
1695 return;
1696 case table::parameters:
1697 {
1698 const auto key = row.get<std::string>("key");
1699 if (std::ranges::none_of(param_table_, [&](const param_def& d) { return d.key == key; }))
1700 {
1701 throw row.error("key", "unknown parameter '" + key + "'");
1702 }
1703 const double v = row.get<double>("value");
1704 if (!std::isfinite(v) || v < 0.0) throw row.error("value", "must be a non-negative number");
1705 return;
1706 }
1707 case table::fertility:
1708 {
1709 const double tfr = row.get<double>("tfr");
1710 if (!(tfr > 0.0)) throw row.error("tfr", "must be positive");
1711 double sum = 0.0;
1712 for (const auto c : age_columns_)
1713 {
1714 const double v = row.get<double>(c);
1715 if (!(v >= 0.0)) throw row.error(c, "must not be negative");
1716 sum += v;
1717 }
1718 if (!(sum > 0.0)) throw row.error("pasfr_10_14", "the age distribution must not be all zero");
1719 break;
1720 }
1721 case table::unions:
1722 case table::households:
1723 case table::defaults: break;
1724 }
1725 const auto schema = schema_(t);
1726 for (const auto& c : numeric_columns_(t))
1727 {
1728 if (std::ranges::find(schema.optional, c) != schema.optional.end() ||
1729 std::ranges::find(schema.required, c) != schema.required.end())
1730 {
1731 check_number_(row, c);
1732 }
1733 }
1734 }
1735
1736 static cells read_cells_(const core::tsv_row& row, table t)
1737 {
1738 cells out;
1739 const auto& schema_cols = schema_(t);
1740 for (const auto& c : numeric_columns_(t))
1741 {
1742 const bool known = std::ranges::find(schema_cols.optional, c) != schema_cols.optional.end() ||
1743 std::ranges::find(schema_cols.required, c) != schema_cols.required.end();
1744 if (known && row.has(c)) out[c] = row.get<double>(c);
1745 }
1746 return out;
1747 }
1748
1749 static std::unordered_map<std::string, cells> read_table_(const core::layered_table& tbl, table t)
1750 {
1751 std::unordered_map<std::string, cells> out;
1752 const std::string key = t == table::defaults ? "subregion" : "cca2";
1753 for (const auto& row : tbl.rows()) out[row.get<std::string>(key)] = read_cells_(row, t);
1754 return out;
1755 }
1756
1757 // The first of the three sources that has the column, and which it was.
1758 struct lookup_cells
1759 {
1760 const cells* country;
1761 const cells* subregion;
1762 const cells* world;
1763
1764 [[nodiscard]] double value(const std::string& column) const
1765 {
1766 for (const cells* c : {country, subregion, world})
1767 {
1768 if (c == nullptr) continue;
1769 const auto it = c->find(column);
1770 if (it != c->end()) return it->second;
1771 }
1772 throw std::runtime_error("relationship-generator: no value for '" + column +
1773 "' in the country, its subregion or the world row of defaults.tsv");
1774 }
1775 };
1776
1777 static fertility_row make_fertility_(const lookup_cells& l, data_source source)
1778 {
1779 fertility_row r;
1780 r.source = source;
1781 r.tfr = l.value("tfr");
1782 std::vector<double> weights;
1783 for (const auto c : age_columns_) weights.push_back(l.value(std::string{c}));
1784 r.by_age.emplace(weights);
1785 for (const int y : tfr_years_)
1786 {
1787 try
1788 {
1789 r.history.emplace_back(y, l.value("tfr_" + std::to_string(y)));
1790 }
1791 catch (const std::runtime_error&)
1792 {
1793 // A period without a figure is skipped; the others interpolate across it.
1794 }
1795 }
1796 r.history.emplace_back(2023, r.tfr);
1797 return r;
1798 }
1799
1800 static union_row make_union_(const lookup_cells& l, data_source source)
1801 {
1802 return {.smam_m = l.value("smam_m"),
1803 .smam_f = l.value("smam_f"),
1804 .never_m = l.value("never_m_45_49"),
1805 .never_f = l.value("never_f_45_49"),
1806 .divorced_m = l.value("divorced_m_45_49"),
1807 .divorced_f = l.value("divorced_f_45_49"),
1808 .source = source};
1809 }
1810
1811 static household_row make_household_(const lookup_cells& l, data_source source)
1812 {
1813 return {.size = l.value("size"),
1814 .one_person = l.value("one_person"),
1815 .single_parent = l.value("single_parent"),
1816 .multi_generation = l.value("multi_generation"),
1817 .source = source};
1818 }
1819
1820 static model build_model_(const tables& tbls)
1821 {
1822 model m;
1823 for (const auto& row : tbls[index_(table::parameters)].rows())
1824 {
1825 const auto key = row.get<std::string>("key");
1826 const double v = row.get<double>("value");
1827 for (const auto& d : param_table_)
1828 {
1829 if (d.key == key) m.params.*d.member = v;
1830 }
1831 }
1832 if (m.params.gompertz_beta <= 0.0 || m.params.dispersion_min <= 0.0 || m.params.child_q15_decay <= 0.0 || m.params.birth_spacing <= 0.0 ||
1833 m.params.min_mother_age >= m.params.max_mother_age || m.params.male_birth_share > 1.0 ||
1834 m.params.twin_share < 0.0 || m.params.twin_share > 0.5 || m.params.stepchild_factor < 0.0 ||
1835 m.params.stepparent_factor < 0.0 || m.params.stepparent_start_mean <= 0.0 ||
1836 m.params.mother_custody_share < 0.0 || m.params.mother_custody_share > 1.0 ||
1837 m.params.household_minor_age < 1.0 || m.params.young_adult_home_share < 0.0 ||
1838 m.params.young_adult_home_share > 1.0 || m.params.one_person_scale < 0.0)
1839 {
1840 throw std::runtime_error("relationship-generator: parameters.tsv has an impossible value");
1841 }
1842
1843 std::unordered_map<std::string, std::string> region;
1844 for (const auto& row : tbls[index_(table::regions)].rows())
1845 {
1846 region[row.get<std::string>("cca2")] = row.get<std::string>("subregion");
1847 }
1848 const auto defaults = read_table_(tbls[index_(table::defaults)], table::defaults);
1849 const auto fert = read_table_(tbls[index_(table::fertility)], table::fertility);
1850 const auto uni = read_table_(tbls[index_(table::unions)], table::unions);
1851 const auto hh = read_table_(tbls[index_(table::households)], table::households);
1852 const auto world = defaults.find("World");
1853 if (world == defaults.end()) return m; // not ready: has_own_data_() reports it
1854
1855 const auto resolve = [&](const std::string& code, const std::unordered_map<std::string, cells>& own,
1856 table t) -> std::pair<lookup_cells, data_source>
1857 {
1858 const auto sub = region.find(code);
1859 const auto sub_cells = sub == region.end() ? defaults.end() : defaults.find(sub->second);
1860 const auto mine = own.find(code);
1861 lookup_cells l{.country = mine == own.end() ? nullptr : &mine->second,
1862 .subregion = sub_cells == defaults.end() ? nullptr : &sub_cells->second,
1863 .world = &world->second};
1864 (void)t;
1865 data_source src = data_source::world;
1866 if (l.country != nullptr) src = data_source::country;
1867 else if (l.subregion != nullptr) src = data_source::subregion;
1868 return {l, src};
1869 };
1870
1871 std::vector<std::string> codes;
1872 for (const auto& [code, _] : region) codes.push_back(code);
1873 for (const auto& [code, _] : fert) codes.push_back(code);
1874 for (const auto& [code, _] : uni) codes.push_back(code);
1875 for (const auto& [code, _] : hh) codes.push_back(code);
1876 std::ranges::sort(codes);
1877 codes.erase(std::ranges::unique(codes).begin(), codes.end());
1878
1879 for (const auto& code : codes)
1880 {
1881 const auto [fl, fs] = resolve(code, fert, table::fertility);
1882 const auto [ul, us] = resolve(code, uni, table::unions);
1883 const auto [hl, hs] = resolve(code, hh, table::households);
1884 m.countries.emplace(code, country_model{.fertility = make_fertility_(fl, fs), .unions = make_union_(ul, us),
1885 .households = make_household_(hl, hs)});
1886 }
1887 const lookup_cells only_world{.country = nullptr, .subregion = nullptr, .world = &world->second};
1888 m.world = {.fertility = make_fertility_(only_world, data_source::world),
1889 .unions = make_union_(only_world, data_source::world),
1890 .households = make_household_(only_world, data_source::world)};
1891 m.ready = true;
1892 return m;
1893 }
1894
1895 static documents read_directory_(const std::filesystem::path& dir)
1896 {
1897 documents docs;
1898 for (const auto t : all_tables_)
1899 {
1900 const auto file = dir / file_name_(t);
1901 std::error_code ec;
1902 if (std::filesystem::is_regular_file(file, ec))
1903 {
1904 docs.at(index_(t)) = core::tsv_document::load(file);
1905 }
1906 }
1907 return docs;
1908 }
1909
1910 void apply_(documents docs, core::layer_mode mode, bool reset)
1911 {
1912 tables next = _tables;
1913 for (const auto t : all_tables_)
1914 {
1915 auto& doc = docs.at(index_(t));
1916 auto& tbl = next.at(index_(t));
1917 if (!doc)
1918 {
1919 if (reset) tbl.clear();
1920 continue;
1921 }
1922 (void)tbl.extend(std::move(*doc), mode, [t](const core::tsv_row& row) { validate_(t, row); });
1923 }
1924 auto built = build_model_(next);
1925 _tables = std::move(next);
1926 _model = std::move(built);
1927 }
1928
1929 // -- State --------------------------------------------------------------
1930
1931 sources _from{};
1932 tables _tables{make_tables_()};
1933 model _model;
1934 core::rng _engine{core::rng::from_entropy()};
1935
1936 struct auto_probe_tag {};
1937
1938 explicit relg(auto_probe_tag /*tag*/)
1939 {
1940 if (!load(dataset::lite)) (void)load(dataset::full);
1941 }
1942
1943 static std::span<const core::embedded_file> embedded_files_()
1944 {
1945#ifdef DASMIG_RELATIONSHIP_EMBED
1946 return embedded::relgen::files;
1947#else
1948 return {};
1949#endif
1950 }
1951
1952 std::uint64_t draw_seed_() { return _engine(); }
1953
1954 [[nodiscard]] bool has_own_data_() const { return _model.ready; }
1955
1956 void require_data_() const
1957 {
1958 if (!has_data()) throw std::runtime_error("relationship-generator: this generator or birth-generator has no data loaded");
1959 }
1960
1961 [[nodiscard]] const country_model& lookup_(const std::string& code) const
1962 {
1963 const auto it = _model.countries.find(code);
1964 return it == _model.countries.end() ? _model.world : it->second;
1965 }
1966
1967 // -- Expansion ----------------------------------------------------------
1968
1969 // A member of a family as a known person; with `above`, the parents too.
1970 [[nodiscard]] static known_person known_of_(const family& f, std::size_t id, bool above)
1971 {
1972 const auto& m = f.members.at(id);
1973 known_person k;
1974 k.bio_sex = m.bio_sex;
1975 k.birth = m.birth;
1976 k.death = m.death;
1977 if (!m.name.empty()) k.name = person_name{m.given, m.patronymic, m.family};
1978 if (above)
1979 {
1980 for (const auto& e : f.edges)
1981 {
1982 if (e.kind == edge_kind::parent_of && e.to == id) k.parents.push_back(known_of_(f, e.from, false));
1983 }
1984 }
1985 return k;
1986 }
1987
1988 [[nodiscard]] static std::vector<std::size_t> parents_in_(const family& f, std::size_t id)
1989 {
1990 std::vector<std::size_t> out;
1991 for (const auto& e : f.edges)
1992 {
1993 if (e.kind == edge_kind::parent_of && e.to == id) out.push_back(e.from);
1994 }
1995 return out;
1996 }
1997
1998 [[nodiscard]] static std::chrono::year_month_day mid_year_(int year)
1999 {
2000 return {std::chrono::year{year}, std::chrono::July, std::chrono::day{1}};
2001 }
2002
2003 [[nodiscard]] static std::chrono::year_month_day year_before_(const std::chrono::year_month_day& d)
2004 {
2005 return std::chrono::year_month_day{std::chrono::sys_days{d} - std::chrono::days{365}};
2006 }
2007
2008 // The anchor spec of a member: the member's own data and the relatives
2009 // the family holds.
2010 [[nodiscard]] static anchor_spec anchor_of_(const family& f, std::size_t id)
2011 {
2012 const auto& m = f.members.at(id);
2013 anchor_spec a;
2014 a.country_code = f.country_code;
2015 a.bio_sex = m.bio_sex;
2016 a.birth = m.birth;
2017 if (!m.name.empty()) a.name = person_name{m.given, m.patronymic, m.family};
2018
2019 const auto parents = parents_in_(f, id);
2020 if (!parents.empty())
2021 {
2022 // A single parent takes the slot of its sex; two take them in the family's order.
2023 if (parents.size() == 1)
2024 {
2025 (f.members.at(parents[0]).bio_sex == sex::male ? a.father : a.mother) = known_of_(f, parents[0], true);
2026 }
2027 else
2028 {
2029 a.father = known_of_(f, parents[0], true);
2030 a.mother = known_of_(f, parents[1], true);
2031 }
2032 std::vector<known_sibling> sibs;
2033 for (const auto& e : f.edges)
2034 {
2035 if (e.kind != edge_kind::sibling_of || (e.from != id && e.to != id)) continue;
2036 const auto other = e.from == id ? e.to : e.from;
2037 known_sibling ks;
2038 ks.person = known_of_(f, other, false);
2039 ks.full = e.full;
2040 if (!e.full)
2041 {
2042 const auto theirs = parents_in_(f, other);
2043 for (const auto p : parents)
2044 {
2045 if (std::ranges::find(theirs, p) != theirs.end()) ks.through = f.members.at(p).bio_sex;
2046 }
2047 }
2048 sibs.push_back(std::move(ks));
2049 }
2050 a.siblings = std::move(sibs);
2051 }
2052
2053 // Unions: the anchor's are listed as edges; another member's are the
2054 // other parents of the children listed for them.
2055 std::vector<known_union> unions;
2056 const auto add_child = [&](known_union& u, std::size_t child)
2057 {
2058 known_child c{known_of_(f, child, false), true};
2059 for (const auto& e : f.edges)
2060 {
2061 if (e.kind == edge_kind::parent_of && e.to == child && e.from == id) c.genetic = e.genetic;
2062 }
2063 u.children.push_back(std::move(c));
2064 };
2065 std::vector<std::pair<std::optional<std::size_t>, std::size_t>> kids; // other parent (if listed), child
2066 for (const auto& e : f.edges)
2067 {
2068 if (e.kind != edge_kind::parent_of || e.from != id) continue;
2069 std::optional<std::size_t> other;
2070 for (const auto p : parents_in_(f, e.to))
2071 {
2072 if (p != id) other = p;
2073 }
2074 kids.emplace_back(other, e.to);
2075 }
2076 if (id == 0)
2077 {
2078 for (const auto& e : f.edges)
2079 {
2080 if (e.kind != edge_kind::partner_of || e.from != 0) continue;
2081 known_union u;
2082 u.partner = known_of_(f, e.to, false);
2083 u.start = mid_year_(*e.start);
2084 if (e.ended != union_end::ongoing)
2085 {
2086 u.end = (e.ended == union_end::death && u.partner->death) ? *u.partner->death : mid_year_(*e.end);
2087 }
2088 for (const auto& [other, child] : kids)
2089 {
2090 if (other && *other == e.to) add_child(u, child);
2091 }
2092 // The partner's other children.
2093 for (const auto& pe : f.edges)
2094 {
2095 if (pe.kind != edge_kind::parent_of || pe.from != e.to) continue;
2096 if (f.members.at(pe.to).role == relation::stepchild) u.stepchildren.push_back(known_of_(f, pe.to, false));
2097 }
2098 unions.push_back(std::move(u));
2099 }
2100 known_union loose;
2101 for (const auto& [other, child] : kids)
2102 {
2103 if (!other) add_child(loose, child);
2104 }
2105 if (!loose.children.empty())
2106 {
2107 loose.start = year_before_(loose.children.front().person.birth);
2108 unions.push_back(std::move(loose));
2109 }
2110 }
2111 else
2112 {
2113 std::vector<std::optional<std::size_t>> seen;
2114 for (const auto& [other, child] : kids)
2115 {
2116 if (std::ranges::find(seen, other) != seen.end()) continue;
2117 seen.push_back(other);
2118 known_union u;
2119 if (other) u.partner = known_of_(f, *other, false);
2120 for (const auto& [o2, c2] : kids)
2121 {
2122 if (o2 == other) add_child(u, c2);
2123 }
2124 u.start = year_before_(u.children.front().person.birth);
2125 if (u.partner && u.partner->death) u.end = u.partner->death;
2126 unions.push_back(std::move(u));
2127 }
2128 }
2129 if (!unions.empty()) a.unions = std::move(unions);
2130 return a;
2131 }
2132
2133 // -- Drawing ------------------------------------------------------------
2134
2135 static constexpr double never_dies_ = 1000.0;
2136 static constexpr double max_age_ = 122.0;
2137 static constexpr double child_age_ = 15.0;
2138 static constexpr std::int64_t none_ = -1;
2139 static constexpr std::size_t no_member_ = static_cast<std::size_t>(-1);
2140 // Half-siblings through one parent born this many years apart or less have the same other parent.
2141 static constexpr double half_cluster_years_ = 4.0;
2142 // A parent slot at or above this is a person who is not a member (the other
2143 // parent of a half-sibling, a partner left out of the list).
2144 static constexpr std::int64_t unlisted_ = 100000;
2145
2146 /// A life: the birth as a day count since 1970-01-01 and the age at death
2147 /// in years. The person is alive at a day if the death falls after it.
2148 struct life
2149 {
2150 double birth{0.0};
2151 double lifespan{0.0};
2152 sex s{sex::male};
2153 int index{0}; // the draw that made this life, which names its streams
2154 };
2155
2156 struct raw_member
2157 {
2158 relation role{relation::anchor};
2159 relg::life life;
2160 std::string key; // names the stream of the person's name
2161 std::array<std::int64_t, 2> parents{none_, none_}; // father (or first) and mother (or second), as ids or tokens
2162 std::int64_t name_child{none_}; // for a parent: the member whose name theirs follows
2163 bool genetic{true};
2164 std::optional<person_name> fixed{}; // NOLINT(readability-redundant-member-init) a name given by the caller
2165 };
2166
2167 struct anchor_info
2168 {
2169 std::string country;
2170 sex s{sex::male};
2171 double birth_day{0.0};
2172 };
2173
2174 struct lineage
2175 {
2176 life father;
2177 life mother;
2178 std::vector<life> siblings;
2179 std::vector<double> mother_births; // the mother's births (days): the anchor's and the siblings'
2180 };
2181
2182 // Parents the caller knows; the other is drawn to match.
2183 struct known_parents
2184 {
2185 const known_person* father;
2186 const known_person* mother;
2187 };
2188
2189 struct union_rec
2190 {
2191 life partner;
2192 double start{0.0};
2193 double end{0.0};
2194 union_end ended{union_end::ongoing};
2195 bool same_sex{false};
2196 bool has_partner{true}; // false for a union known by its children alone
2197 std::optional<person_name> partner_name{}; // NOLINT(readability-redundant-member-init)
2198 };
2199
2200 struct child_rec
2201 {
2202 relg::life life;
2203 int union_index{0};
2204 bool genetic{true};
2205 std::optional<person_name> fixed{}; // NOLINT(readability-redundant-member-init)
2206 };
2207
2208 static std::int64_t days_(const std::chrono::year_month_day& d)
2209 {
2210 return std::chrono::sys_days{d}.time_since_epoch().count();
2211 }
2212
2213 static std::chrono::year_month_day from_days_(double day)
2214 {
2215 return std::chrono::year_month_day{
2216 std::chrono::sys_days{std::chrono::days{static_cast<std::int64_t>(std::floor(day))}}};
2217 }
2218
2219 static double clamp_(double v, double lo, double hi) { return std::min(std::max(v, lo), hi); }
2220
2221 // A day `years` before or after another, the product fenced against FMA.
2222 static double back_(double day, double years) { return day - core::math::fenced(years * year_days_); }
2223 static double forward_(double day, double years) { return day + core::math::fenced(years * year_days_); }
2224
2225 // Completed years from a birth to a date.
2226 static int calendar_age_(const std::chrono::year_month_day& birth, const std::chrono::year_month_day& at)
2227 {
2228 int age = static_cast<int>(at.year()) - static_cast<int>(birth.year());
2229 if (at.month() < birth.month() || (at.month() == birth.month() && at.day() < birth.day())) --age;
2230 return std::max(age, 0);
2231 }
2232
2233 static std::size_t push_(std::vector<raw_member>& raw, relation role, const life& l, std::string key)
2234 {
2235 raw.push_back({.role = role, .life = l, .key = std::move(key)});
2236 return raw.size() - 1;
2237 }
2238
2239 static person to_person_(const raw_member& m, std::size_t id, double ref_day)
2240 {
2241 person p;
2242 p.id = id;
2243 p.role = m.role;
2244 p.bio_sex = m.life.s;
2245 p.birth = from_days_(m.life.birth);
2246 const double death_day = forward_(m.life.birth, m.life.lifespan);
2247 p.alive = death_day > ref_day;
2248 if (p.alive)
2249 {
2250 p.age = static_cast<std::uint8_t>(std::min(calendar_age_(p.birth, from_days_(ref_day)), 255));
2251 }
2252 else
2253 {
2254 p.death = from_days_(death_day);
2255 p.age = static_cast<std::uint8_t>(std::min(calendar_age_(p.birth, *p.death), 255));
2256 }
2257 return p;
2258 }
2259
2260 // Names of the people the caller gave need a given name.
2261 static void check_name_(const std::optional<person_name>& n)
2262 {
2263 if (n && n->given.empty()) throw std::invalid_argument("a known person's name needs a given name");
2264 }
2265
2266 static void check_person_(const known_person& k)
2267 {
2268 check_name_(k.name);
2269 for (const auto& parent : k.parents) check_person_(parent);
2270 }
2271
2272 static void check_known_(const anchor_spec& a)
2273 {
2274 check_name_(a.name);
2275 if (a.father) check_person_(*a.father);
2276 if (a.mother) check_person_(*a.mother);
2277 for (const auto& s : a.siblings.value_or(std::vector<known_sibling>{})) check_person_(s.person);
2278 for (const auto& u : a.unions.value_or(std::vector<known_union>{}))
2279 {
2280 if (u.partner) check_person_(*u.partner);
2281 for (const auto& c : u.children) check_person_(c.person);
2282 }
2283 }
2284
2285 // A life from someone the caller knows: dead before the reference date if
2286 // a death is given, alive otherwise.
2287 [[nodiscard]] static life life_of_(const known_person& k, double ref_day, int index)
2288 {
2289 if (!k.birth.ok()) throw std::invalid_argument("a known person's birth is not a date");
2290 const double birth = static_cast<double>(days_(k.birth));
2291 double lifespan = never_dies_;
2292 if (k.death)
2293 {
2294 if (!k.death->ok()) throw std::invalid_argument("a known person's death is not a date");
2295 const double d = static_cast<double>(days_(*k.death));
2296 if (d < birth) throw std::invalid_argument("a known person died before being born");
2297 // Just past the day, so that the date of death comes back as given.
2298 if (d < ref_day) lifespan = ((d - birth) / year_days_) + 1e-6;
2299 }
2300 return {.birth = birth, .lifespan = lifespan, .s = k.bio_sex, .index = index};
2301 }
2302
2303 // A union the caller knows, as a drawn one.
2304 [[nodiscard]] static union_rec union_of_(const known_union& k, std::size_t j, sex anchor_sex, double ref_day)
2305 {
2306 if (!k.start.ok()) throw std::invalid_argument("a known union's start is not a date");
2307 union_rec u;
2308 if (k.partner)
2309 {
2310 u.partner = life_of_(*k.partner, ref_day, static_cast<int>(j));
2311 u.partner_name = k.partner->name;
2312 u.same_sex = k.partner->bio_sex == anchor_sex;
2313 }
2314 else
2315 {
2316 u.partner = {.birth = 0.0, .lifespan = never_dies_, .s = anchor_sex == sex::male ? sex::female : sex::male,
2317 .index = static_cast<int>(j)};
2318 u.has_partner = false;
2319 }
2320 u.start = static_cast<double>(days_(k.start));
2321 u.end = ref_day;
2322 if (k.end)
2323 {
2324 if (!k.end->ok()) throw std::invalid_argument("a known union's end is not a date");
2325 const double e = static_cast<double>(days_(*k.end));
2326 if (e < u.start) throw std::invalid_argument("a known union ends before it begins");
2327 if (e < ref_day)
2328 {
2329 u.end = e;
2330 const bool partner_died =
2331 k.partner && k.partner->death && static_cast<double>(days_(*k.partner->death)) <= e;
2332 u.ended = partner_died ? union_end::death : union_end::divorce;
2333 }
2334 }
2335 return u;
2336 }
2337
2338 // The children of known unions.
2339 [[nodiscard]] static std::vector<child_rec> known_children_(const std::vector<known_union>& unions, double ref_day)
2340 {
2341 std::vector<child_rec> out;
2342 for (std::size_t j = 0; j < unions.size(); ++j)
2343 {
2344 for (std::size_t i = 0; i < unions[j].children.size(); ++i)
2345 {
2346 const auto& c = unions[j].children[i];
2347 out.push_back({.life = life_of_(c.person, ref_day, static_cast<int>(i)),
2348 .union_index = static_cast<int>(j),
2349 .genetic = c.genetic,
2350 .fixed = c.person.name});
2351 }
2352 }
2353 return out;
2354 }
2355
2356 [[nodiscard]] anchor_info draw_anchor_(const family_request& request, std::uint64_t seed, double ref_day) const
2357 {
2358 const auto& spec = request.anchor;
2359 if (spec.birth && !spec.birth->ok()) throw std::invalid_argument("family_request.anchor.birth is not a date");
2360 if (spec.age && (*spec.age < 0 || *spec.age > static_cast<int>(max_age_)))
2361 {
2362 throw std::invalid_argument("family_request.anchor.age must be 0 to 122");
2363 }
2364 std::optional<std::string> country;
2365 if (spec.country_code)
2366 {
2367 std::string code = *spec.country_code;
2368 for (char& c : code) c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
2369 const auto known = _from.birth->countries();
2370 if (std::find(known.begin(), known.end(), code) == known.end())
2371 {
2372 throw std::invalid_argument("relationship-generator: unknown country code: " + *spec.country_code);
2373 }
2374 country = std::move(code);
2375 }
2376 anchor_info a;
2377 if (spec.birth && country && spec.bio_sex)
2378 {
2379 if (calendar_age_(*spec.birth, from_days_(ref_day)) > static_cast<int>(max_age_))
2380 {
2381 throw std::invalid_argument("the anchor would be older than 122 at the reference date");
2382 }
2383 a.country = *country;
2384 a.s = *spec.bio_sex;
2385 a.birth_day = static_cast<double>(days_(*spec.birth));
2386 }
2387 else
2388 {
2389 birth_request br;
2390 br.country_code = country;
2391 br.bio_sex = spec.bio_sex;
2392 br.weighted = request.weighted;
2393 br.reference_date = request.reference_date;
2394 if (spec.birth)
2395 {
2396 br.year = year_t{static_cast<std::uint16_t>(static_cast<int>(spec.birth->year()))};
2397 }
2398 else if (spec.age)
2399 {
2400 const auto age = static_cast<std::uint8_t>(*spec.age);
2401 br.ages = age_range{.min = age, .max = age};
2402 }
2403 const auto b = _from.birth->get_birth(br, seed);
2404 a.country = b.country_code;
2405 a.s = b.bio_sex;
2406 a.birth_day = static_cast<double>(days_(
2407 spec.birth ? *spec.birth
2408 : std::chrono::year_month_day{std::chrono::year{b.year},
2409 std::chrono::month{b.month}, std::chrono::day{b.day}}));
2410 }
2411 if (a.birth_day > ref_day) throw std::invalid_argument("the anchor is born after the reference date");
2412 return a;
2413 }
2414
2415 // Age at death of a person known to be alive until `alive_until`.
2416 //
2417 // Two parts. Childhood: the chance of dying before 15 is `q15`, from the
2418 // life expectancy (about 1% at 80 and 14% at 53), and a child who dies
2419 // does so early (a squared uniform age). Adulthood: from 15, a Gompertz
2420 // curve, S(x) = exp(-c (e^(beta x) - 1)), whose mean age at death is the
2421 // typical one for that life expectancy (`adult_intercept + adult_slope *
2422 // e0`, a line through the Coale-Demeny west model life tables) and whose
2423 // mode sits euler_gamma / beta above its mean. Fitting the curve to the
2424 // life expectancy at birth instead would put adults' deaths far too early
2425 // where child deaths are common. Inverting S(x) / S(alive_until) = u gives
2426 // the adult draw.
2427 [[nodiscard]] life draw_life_(core::rng r, const std::string& country, sex s, double birth_day,
2428 double alive_until) const
2429 {
2430 const auto& p = _model.params;
2431 const double e0 = _from.birth->life_expectancy(country, s);
2432 const double q15 = std::min(0.9, p.child_q15_scale * core::math::exp((80.0 - e0) / p.child_q15_decay));
2433 const double beta = p.gompertz_beta;
2434 const double adult_mean = p.adult_intercept + core::math::fenced(p.adult_slope * e0);
2435 const double mode = adult_mean + (euler_gamma_ / beta);
2436 const double c = core::math::exp(-(beta * mode));
2437 const double child_roll = core::uniform01(r);
2438 const double within = core::uniform01(r);
2439 const double u = 1.0 - core::uniform01(r); // (0, 1]
2440 if (alive_until < child_age_ && child_roll < q15 * ((child_age_ - alive_until) / child_age_))
2441 {
2442 return {.birth = birth_day,
2443 .lifespan = alive_until + core::math::fenced(((child_age_ - alive_until) * within) * within),
2444 .s = s};
2445 }
2446 const double from = std::max(alive_until, child_age_);
2447 const double grown = core::math::exp(beta * from) - (core::math::log(u) / c);
2448 const double lifespan = core::math::log(grown) / beta;
2449 return {.birth = birth_day, .lifespan = clamp_(lifespan, from, max_age_), .s = s};
2450 }
2451
2452 // A mother's age at a birth, from the five-year group weights and uniform
2453 // within the group, inside the fertile years.
2454 [[nodiscard]] double draw_mother_age_(const fertility_row& f, core::rng& r) const
2455 {
2456 const auto& p = _model.params;
2457 double a = p.min_mother_age;
2458 for (int i = 0; i < 12; ++i)
2459 {
2460 const double group = 10.0 + (5.0 * static_cast<double>((*f.by_age)(r)));
2461 a = group + core::math::fenced(5.0 * core::uniform01(r));
2462 if (a >= p.min_mother_age && a <= p.max_mother_age) return a;
2463 }
2464 return clamp_(a, p.min_mother_age, p.max_mother_age);
2465 }
2466
2467 // The total fertility rate in a (fractional) year, between the periods on file.
2468 [[nodiscard]] static double tfr_at_(const fertility_row& f, double year)
2469 {
2470 const auto& h = f.history;
2471 if (year <= static_cast<double>(h.front().first)) return h.front().second;
2472 for (std::size_t i = 1; i < h.size(); ++i)
2473 {
2474 if (year <= static_cast<double>(h[i].first))
2475 {
2476 const double y0 = static_cast<double>(h[i - 1].first);
2477 const double t = (year - y0) / (static_cast<double>(h[i].first) - y0);
2478 return h[i - 1].second + core::math::fenced(t * (h[i].second - h[i - 1].second));
2479 }
2480 }
2481 return h.back().second;
2482 }
2483
2484 // Marsaglia and Tsang (2000) for a gamma variate; a shape below 1 uses the
2485 // shape + 1 variate times u^(1 / shape).
2486 [[nodiscard]] static double gamma_(core::rng& r, double shape, double scale)
2487 {
2488 if (shape < 1.0)
2489 {
2490 const double u = 1.0 - core::uniform01(r); // (0, 1]
2491 return gamma_(r, shape + 1.0, scale) * core::math::exp(core::math::log(u) / shape);
2492 }
2493 const double d = shape - (1.0 / 3.0);
2494 const double c = 1.0 / std::sqrt(9.0 * d);
2495 for (;;)
2496 {
2497 const double x = core::normal(r, 0.0, 1.0);
2498 const double t = 1.0 + core::math::fenced(c * x);
2499 if (t <= 0.0) continue;
2500 const double v = t * t * t;
2501 const double u = core::uniform01(r);
2502 if (u <= 0.0) continue;
2503 const double bound = core::math::fenced(0.5 * x * x) + d - core::math::fenced(d * v) +
2504 core::math::fenced(d * core::math::log(v));
2505 if (core::math::log(u) < bound) return d * v * scale;
2506 }
2507 }
2508
2509 // Knuth's multiplication method.
2510 [[nodiscard]] static int poisson_(core::rng& r, double lambda)
2511 {
2512 const double limit = core::math::exp(-std::min(lambda, 40.0));
2513 double p = 1.0;
2514 int k = 0;
2515 do
2516 {
2517 ++k;
2518 p *= core::uniform01(r);
2519 } while (p > limit && k < 200);
2520 return k - 1;
2521 }
2522
2523 [[nodiscard]] static double exponential_(core::rng& r, double mean)
2524 {
2525 return -(mean * core::math::log(1.0 - core::uniform01(r)));
2526 }
2527
2528 // How spread out family sizes are, as variance over mean, for a mean size.
2529 [[nodiscard]] double dispersion_(double mean) const
2530 {
2531 const auto& p = _model.params;
2532 return std::max(p.dispersion_min, p.dispersion_intercept + (p.dispersion_slope * mean));
2533 }
2534
2535 // The number of children of a family of mean size `mean`.
2536 // The spread of family sizes, as variance over mean `d`, grows with
2537 // fertility: families are close in size when few children are born
2538 // (d < 1) and some are much larger when many are (d > 1).
2539 // - d < 0.9: binomial, with p = 1 - d and n = mean / p;
2540 // - d near 1: Poisson;
2541 // - d > 1.05: negative binomial, a Poisson with a gamma-distributed
2542 // mean of shape k = mean / (d - 1).
2543 [[nodiscard]] int family_size_(core::rng& r, double mean) const
2544 {
2545 if (mean <= 0.0) return 0;
2546 const double d = dispersion_(mean);
2547 if (d < 0.9)
2548 {
2549 const int n = std::max(1, static_cast<int>(std::lround(mean / (1.0 - d))));
2550 const double prob = std::min(1.0, mean / static_cast<double>(n));
2551 int size = 0;
2552 for (int i = 0; i < n; ++i) size += core::bernoulli(r, prob) ? 1 : 0;
2553 return size;
2554 }
2555 if (d <= 1.05) return poisson_(r, mean);
2556 const double k = mean / (d - 1.0);
2557 return poisson_(r, gamma_(r, k, mean / k));
2558 }
2559
2560 // The number of siblings of a child of a family whose mean size is `mean`.
2561 //
2562 // Seen from a child, a family is size-biased: a family of n children is n
2563 // times as likely to be the one drawn, so the siblings are one fewer than
2564 // a size-biased family size. The size-biased forms of the three
2565 // distributions of family_size_() are
2566 // - binomial: 1 + Binomial(n - 1, p), so the siblings are Binomial(n - 1, p);
2567 // - Poisson: 1 + Poisson(mean), so the siblings are Poisson(mean);
2568 // - negative binomial: 1 + Poisson(gamma(k + 1, mean / k)).
2569 [[nodiscard]] int sibling_count_(core::rng& r, double mean) const
2570 {
2571 const double d = dispersion_(mean);
2572 if (d < 0.9)
2573 {
2574 const int n = std::max(1, static_cast<int>(std::lround(mean / (1.0 - d))));
2575 const double prob = std::min(1.0, mean / static_cast<double>(n));
2576 int siblings = 0;
2577 for (int i = 1; i < n; ++i) siblings += core::bernoulli(r, prob) ? 1 : 0;
2578 return siblings;
2579 }
2580 if (d <= 1.05) return poisson_(r, mean);
2581 const double k = mean / (d - 1.0);
2582 return poisson_(r, gamma_(r, k + 1.0, mean / k));
2583 }
2584
2585 // The parents of a person born on `child_birth`, and optionally the
2586 // person's brothers and sisters. Parents the caller knows are used as
2587 // they are; the other is drawn to sit at the usual gap from them.
2588 [[nodiscard]] lineage draw_lineage_(const country_model& cm, const std::string& country, const core::rng& r,
2589 double child_birth, double ref_day, bool with_siblings,
2590 const known_parents& known) const
2591 {
2592 const auto& p = _model.params;
2593 lineage out;
2594 auto age_rng = r.split("mother-age");
2595 double mother_age = draw_mother_age_(cm.fertility, age_rng);
2596 auto gap_rng = r.split("father-gap");
2597 const double mean_gap = clamp_(cm.unions.smam_m - cm.unions.smam_f, 0.0, p.max_gap_mean);
2598 double father_age = clamp_(mother_age + core::normal(gap_rng, mean_gap, p.father_gap_sd),
2599 p.min_father_age, p.max_father_age);
2600 const double drawn_gap = father_age - mother_age;
2601 std::optional<life> known_mother;
2602 std::optional<life> known_father;
2603 if (known.mother != nullptr)
2604 {
2605 known_mother = life_of_(*known.mother, ref_day, 0);
2606 mother_age = (child_birth - known_mother->birth) / year_days_;
2607 }
2608 if (known.father != nullptr)
2609 {
2610 known_father = life_of_(*known.father, ref_day, 0);
2611 father_age = (child_birth - known_father->birth) / year_days_;
2612 }
2613 if (known_mother && !known_father) father_age = clamp_(mother_age + drawn_gap, p.min_father_age, p.max_father_age);
2614 if (known_father && !known_mother) mother_age = clamp_(father_age - drawn_gap, p.min_mother_age, p.max_mother_age);
2615 if ((known_mother && mother_age <= 0.0) || (known_father && father_age <= 0.0))
2616 {
2617 throw std::invalid_argument("a known parent is not born before the child");
2618 }
2619 out.mother = known_mother ? *known_mother
2620 : draw_life_(r.split("mother-life"), country, sex::female,
2621 back_(child_birth, mother_age), mother_age);
2622 out.father = known_father ? *known_father
2623 : draw_life_(r.split("father-life"), country, sex::male,
2624 back_(child_birth, father_age),
2625 std::max(0.0, father_age - p.conception_lead));
2626 out.mother_births.push_back(child_birth);
2627 if (!with_siblings) return out;
2628
2629 auto count_rng = r.split("sibling-count");
2630 const double mean = tfr_at_(cm.fertility, 1970.0 + (child_birth / year_days_));
2631 const int wanted = std::min(sibling_count_(count_rng, mean), static_cast<int>(p.max_siblings));
2632
2633 // Each sibling is placed by the mother's age at the birth, and left out
2634 // if no age fits: before she could have a child, after she died or the
2635 // father died, after the reference date, or too close to another birth.
2636 std::vector<double> mother_ages{mother_age};
2637 const auto siblings = r.split("siblings");
2638 for (int i = 0; i < wanted; ++i)
2639 {
2640 auto ri = siblings.split(static_cast<std::uint64_t>(i));
2641 for (int attempt = 0; attempt < 12; ++attempt)
2642 {
2643 const double a = draw_mother_age_(cm.fertility, ri);
2644 const double birth = forward_(child_birth, a - mother_age);
2645 const double fa = father_age + (a - mother_age);
2646 if (a >= out.mother.lifespan || birth > ref_day || fa < p.min_father_age || fa > p.max_father_age ||
2647 fa - p.conception_lead >= out.father.lifespan)
2648 {
2649 continue;
2650 }
2651 if (std::ranges::any_of(mother_ages, [&](double e) { return std::abs(a - e) < p.birth_spacing; }))
2652 {
2653 continue;
2654 }
2655 mother_ages.push_back(a);
2656 out.mother_births.push_back(birth);
2657 const sex s = core::bernoulli(ri, p.male_birth_share) ? sex::male : sex::female;
2658 auto l = draw_life_(ri.split("life"), country, s, birth, 0.0);
2659 l.index = i;
2660 out.siblings.push_back(l);
2661 if (const auto t = twin_of_(ri, country, l)) out.siblings.push_back(*t);
2662 break;
2663 }
2664 }
2665 // The anchor's own twin: a person is twice as likely to be one of a
2666 // pair as a delivery is to be a twin delivery.
2667 auto twin_rng = r.split("anchor-twin");
2668 if (p.max_siblings >= 1.0 && core::bernoulli(twin_rng, 2.0 * p.twin_share))
2669 {
2670 const sex s = core::bernoulli(twin_rng, p.male_birth_share) ? sex::male : sex::female;
2671 auto l = draw_life_(twin_rng.split("life"), country, s, child_birth, 0.0);
2672 l.index = 1000;
2673 out.siblings.push_back(l);
2674 }
2675 return out;
2676 }
2677
2678 // Another child born with `l`, with the chance of a twin delivery.
2679 [[nodiscard]] std::optional<life> twin_of_(core::rng& r, const std::string& country, const life& l) const
2680 {
2681 const auto& p = _model.params;
2682 if (!core::bernoulli(r, p.twin_share)) return std::nullopt;
2683 const sex s = core::bernoulli(r, p.male_birth_share) ? sex::male : sex::female;
2684 auto t = draw_life_(r.split("twin"), country, s, l.birth, 0.0);
2685 t.index = 1000 + l.index;
2686 return t;
2687 }
2688
2689 // Children of one person alone, the other parent being left out: placed by
2690 // the parent's age at each birth (a mother's from the fertility by age, a
2691 // father's from a mother's age and the usual gap), and left out if no age
2692 // fits (after the reference date or the parent's death, or, for a mother,
2693 // too close to another of her births, `mother_births`).
2694 [[nodiscard]] std::vector<life> draw_offspring_(const country_model& cm, const std::string& country,
2695 const life& parent, int count,
2696 std::vector<double> mother_births, double ref_day,
2697 const core::rng& r) const
2698 {
2699 const auto& p = _model.params;
2700 std::vector<life> out;
2701 const bool female = parent.s == sex::female;
2702 const double mean_gap = clamp_(cm.unions.smam_m - cm.unions.smam_f, 0.0, p.max_gap_mean);
2703 for (int i = 0; i < count; ++i)
2704 {
2705 auto ri = r.split(static_cast<std::uint64_t>(i));
2706 for (int attempt = 0; attempt < 12; ++attempt)
2707 {
2708 const double am = draw_mother_age_(cm.fertility, ri);
2709 const double pa = female ? am
2710 : clamp_(am + core::normal(ri, mean_gap, p.father_gap_sd), p.min_father_age,
2711 p.max_father_age);
2712 const double birth = forward_(parent.birth, pa);
2713 if (birth > ref_day || (female ? pa >= parent.lifespan : pa - p.conception_lead >= parent.lifespan))
2714 {
2715 continue;
2716 }
2717 if (female && std::ranges::any_of(mother_births, [&](double e)
2718 { return std::abs(birth - e) < p.birth_spacing * year_days_; }))
2719 {
2720 continue;
2721 }
2722 if (female) mother_births.push_back(birth);
2723 const sex s = core::bernoulli(ri, p.male_birth_share) ? sex::male : sex::female;
2724 auto l = draw_life_(ri.split("life"), country, s, birth, 0.0);
2725 l.index = i;
2726 out.push_back(l);
2727 if (const auto t = twin_of_(ri, country, l)) out.push_back(*t);
2728 break;
2729 }
2730 }
2731 return out;
2732 }
2733
2734 // The half-siblings a parent gives the anchor with someone else: some of
2735 // the parents' unions end in a separation, and a share of those parents
2736 // have children with another partner (divorced share among the ever
2737 // married, times `half_sibling_factor`).
2738 [[nodiscard]] std::vector<life> draw_half_siblings_(const country_model& cm, const std::string& country,
2739 const life& parent, bool father,
2740 std::vector<double> mother_births, double ref_day,
2741 const core::rng& r) const
2742 {
2743 const auto& p = _model.params;
2744 const double never = father ? cm.unions.never_m : cm.unions.never_f;
2745 const double divorced = father ? cm.unions.divorced_m : cm.unions.divorced_f;
2746 const double chance = clamp_(p.half_sibling_factor * divorced / std::max(100.0 - never, 10.0), 0.0, 0.9);
2747 auto roll = r.split("roll");
2748 if (!core::bernoulli(roll, chance)) return {};
2749 const int count = std::min(1 + poisson_(roll, p.half_sibling_extra), static_cast<int>(p.max_siblings));
2750 return draw_offspring_(cm, country, parent, count, std::move(mother_births), ref_day, r.split("births"));
2751 }
2752
2753 // The anchor's grandchildren through one child.
2754 [[nodiscard]] std::vector<life> draw_grandchildren_(const country_model& cm, const std::string& country,
2755 const life& child, double ref_day, const core::rng& r) const
2756 {
2757 const auto& p = _model.params;
2758 auto count_rng = r.split("count");
2759 const double mean = tfr_at_(cm.fertility, 1970.0 + (child.birth / year_days_) + 28.0);
2760 const int count = std::min(family_size_(count_rng, mean), static_cast<int>(p.max_children));
2761 return draw_offspring_(cm, country, child, count, {}, ref_day, r.split("births"));
2762 }
2763
2764 // The children a partner had before the union began, with someone else:
2765 // the partner's divorced share among the ever married, times
2766 // `stepchild_factor`, gives the chance, and the births are placed by the
2767 // partner's age and end before the union.
2768 [[nodiscard]] std::vector<life> draw_stepchildren_(const country_model& cm, const std::string& country,
2769 const union_rec& u, double ref_day, const core::rng& r) const
2770 {
2771 const auto& p = _model.params;
2772 const bool male = u.partner.s == sex::male;
2773 const double never = male ? cm.unions.never_m : cm.unions.never_f;
2774 const double divorced = male ? cm.unions.divorced_m : cm.unions.divorced_f;
2775 const double chance = clamp_(p.stepchild_factor * divorced / std::max(100.0 - never, 10.0), 0.0, 0.9);
2776 auto roll = r.split("roll");
2777 if (!core::bernoulli(roll, chance)) return {};
2778 const int count = std::min(1 + poisson_(roll, p.half_sibling_extra), static_cast<int>(p.max_children));
2779 return draw_offspring_(cm, country, u.partner, count, {}, std::min(u.start - 0.75 * year_days_, ref_day),
2780 r.split("births"));
2781 }
2782
2783 struct step_rec
2784 {
2785 life partner;
2786 double start{0.0};
2787 double end{0.0};
2788 union_end ended{union_end::ongoing};
2789 };
2790
2791 // A parent's partner who is not the anchor's other parent. The chance is
2792 // the parent's divorced share among the ever married, times
2793 // `remarry_share` and `stepparent_factor`; the union begins a year or
2794 // more after the last child the parents had together (`together_until`,
2795 // the anchor or a younger full sibling; an exponential spell with mean
2796 // `stepparent_start_mean`) while the parent is alive and the anchor is
2797 // under `household_minor_age`, and the partner is drawn as for the
2798 // anchor's own unions, of the other sex unless the union is same-sex. It
2799 // ends in a separation, or at the first death.
2800 [[nodiscard]] std::optional<step_rec> draw_stepparent_(const country_model& cm, const std::string& country,
2801 const life& parent, double child_birth, double together_until,
2802 double ref_day, const core::rng& r) const
2803 {
2804 const auto& p = _model.params;
2805 const bool male = parent.s == sex::male;
2806 const double never = male ? cm.unions.never_m : cm.unions.never_f;
2807 const double divorced = male ? cm.unions.divorced_m : cm.unions.divorced_f;
2808 const double p_divorce = clamp_(divorced / std::max(100.0 - never, 10.0), 0.0, 0.7);
2809 const double chance = clamp_(p.stepparent_factor * p_divorce * p.remarry_share, 0.0, 0.9);
2810 auto roll = r.split("roll");
2811 if (!core::bernoulli(roll, chance)) return std::nullopt;
2812 const double start = forward_(together_until, 1.0 + exponential_(roll, p.stepparent_start_mean));
2813 const double parent_death = forward_(parent.birth, parent.lifespan);
2814 const double parent_age = (start - parent.birth) / year_days_;
2815 if (start >= std::min(parent_death, ref_day) || parent_age < p.min_union_age ||
2816 start >= forward_(child_birth, p.household_minor_age))
2817 {
2818 return std::nullopt;
2819 }
2820
2821 step_rec out;
2822 out.start = start;
2823 auto sex_rng = r.split("sex");
2824 const bool same_sex = core::bernoulli(sex_rng, p.same_sex_share);
2825 const sex ps = same_sex ? parent.s : (male ? sex::female : sex::male);
2826 const double mean_gap = clamp_(cm.unions.smam_m - cm.unions.smam_f, 0.0, p.max_gap_mean);
2827 auto gap_rng = r.split("gap");
2828 const double signed_gap = same_sex ? 0.0 : (male ? -mean_gap : mean_gap);
2829 const double partner_age = std::max(p.min_union_age, parent_age + core::normal(gap_rng, signed_gap, p.father_gap_sd));
2830 out.partner = draw_life_(r.split("life"), country, ps, back_(start, partner_age), partner_age);
2831 const double partner_death = forward_(out.partner.birth, out.partner.lifespan);
2832 out.end = ref_day;
2833 auto end_rng = r.split("end");
2834 const bool separates = core::bernoulli(end_rng, p_divorce);
2835 const double separation = forward_(start, 0.5 + exponential_(end_rng, p.divorce_duration_mean));
2836 const double first_death = std::min(partner_death, parent_death);
2837 if (separates && separation < std::min(first_death, ref_day))
2838 {
2839 out.end = separation;
2840 out.ended = union_end::divorce;
2841 }
2842 else if (first_death <= ref_day)
2843 {
2844 out.end = first_death;
2845 out.ended = union_end::death;
2846 }
2847 return out;
2848 }
2849
2850 // The half-siblings through `parent` who were born during the union with a
2851 // stepparent of the other sex take the stepparent as their other parent,
2852 // where the stepparent could be one: a stepmother between
2853 // `min_mother_age` and `max_mother_age`, alive at the birth and
2854 // `birth_spacing` from her other births; a stepfather between
2855 // `min_father_age` and `max_father_age`, alive at the conception. Nothing
2856 // is drawn.
2857 // @return How many were tied, twins counting once.
2858 static int tie_half_siblings_(std::vector<raw_member>& raw, std::size_t parent, std::size_t stepparent,
2859 const step_rec& u, const parameters& p)
2860 {
2861 const life sp = raw.at(stepparent).life;
2862 const bool via_father = raw.at(parent).life.s == sex::male;
2863 const std::size_t own = via_father ? 0 : 1;
2864 const auto parent_id = static_cast<std::int64_t>(parent);
2865 const double death = forward_(sp.birth, sp.lifespan);
2866 std::vector<double> births; // the tied ones'
2867 for (auto& m : raw)
2868 {
2869 if (m.role != relation::half_sibling || m.parents.at(own) != parent_id) continue;
2870 const double b = m.life.birth;
2871 if (b < u.start || b > u.end) continue;
2872 const double age = (b - sp.birth) / year_days_;
2873 if (via_father)
2874 {
2875 if (age < p.min_mother_age || age > p.max_mother_age || b > death) continue;
2876 if (std::ranges::any_of(births, [&](double e)
2877 { return e != b && std::abs(b - e) < p.birth_spacing * year_days_; }))
2878 {
2879 continue;
2880 }
2881 }
2882 else if (age < p.min_father_age || age > p.max_father_age || age - p.conception_lead >= sp.lifespan)
2883 {
2884 continue;
2885 }
2886 m.parents.at(1 - own) = static_cast<std::int64_t>(stepparent);
2887 if (std::ranges::find(births, b) == births.end()) births.push_back(b);
2888 }
2889 return static_cast<int>(births.size());
2890 }
2891
2892 // Who the anchor lives with at the reference date, from the members the
2893 // family holds (all of them alive).
2894 //
2895 // A child lives with its living parents, as a lone parent's child with
2896 // the share of lone-parent households (`single_parent`), or with the
2897 // parent who has a partner (a stepparent) and that partner; younger
2898 // brothers and sisters of those parents come along (and the stepparent's
2899 // own children under 18, with the custody share of the stepparent's
2900 // sex, `mother_custody_share`), and with the share of
2901 // three-generation households (`multi_generation`) a grandparent. An adult
2902 // lives with an ongoing partner and the children under 18 of that union
2903 // (and of the partner's) and, after a separation, with the children the
2904 // mother (`mother_custody_share`) or the father keeps. Without either, a
2905 // young adult may live with the parents (`young_adult_home_share`), and
2906 // others live alone in the share of one-person households scaled to
2907 // people (`one_person_scale`).
2908 [[nodiscard]] household draw_household_(const country_model& cm, const family& f, const core::rng& r) const
2909 {
2910 const auto& p = _model.params;
2911 const auto& row = cm.households;
2912 const auto& me = f.members.front();
2913 auto roll = r.split("roll");
2914 household out;
2915 const auto pct = [](double v) { return clamp_(v / 100.0, 0.0, 0.9); };
2916 const auto minor = [&](std::size_t id)
2917 { return f.members[id].alive && static_cast<double>(f.members[id].age) < p.household_minor_age; };
2918 const auto has_edge = [&](edge_kind kind, std::size_t from, std::size_t to)
2919 {
2920 return std::ranges::any_of(f.edges, [&](const edge& e) { return e.kind == kind && e.from == from && e.to == to; });
2921 };
2922 const auto ongoing = [&](std::size_t a, std::size_t b)
2923 {
2924 return std::ranges::any_of(f.edges, [&](const edge& e)
2925 { return e.kind == edge_kind::partner_of && e.from == a && e.to == b &&
2926 e.ended == union_end::ongoing; });
2927 };
2928 const auto add = [&](std::size_t id)
2929 {
2930 if (std::ranges::find(out.members, id) == out.members.end()) out.members.push_back(id);
2931 };
2932 const auto role_ids = [&](std::initializer_list<relation> roles, bool alive_only)
2933 {
2934 std::vector<std::size_t> ids;
2935 for (const auto& m : f.members)
2936 {
2937 if (std::ranges::find(roles, m.role) != roles.end() && (!alive_only || m.alive)) ids.push_back(m.id);
2938 }
2939 return ids;
2940 };
2941 // The younger brothers and sisters of the anchor who share one of `parents`.
2942 const auto add_siblings = [&](const std::vector<std::size_t>& parents)
2943 {
2944 for (const auto id : role_ids({relation::sibling, relation::half_sibling}, true))
2945 {
2946 if (minor(id) && std::ranges::any_of(parents, [&](std::size_t q) { return has_edge(edge_kind::parent_of, q, id); }))
2947 add(id);
2948 }
2949 };
2950 // One living member of `candidates`, for the three-generation draw.
2951 const auto pick = [&](const std::vector<std::size_t>& candidates) -> std::optional<std::size_t>
2952 {
2953 if (candidates.empty()) return std::nullopt;
2954 return candidates[static_cast<std::size_t>(core::bounded(roll, candidates.size()))];
2955 };
2956
2957 if (static_cast<double>(me.age) < p.household_minor_age)
2958 {
2959 const auto live = role_ids({relation::father, relation::mother}, true);
2960 if (live.empty())
2961 {
2962 out.type = household_type::other;
2963 return out;
2964 }
2965 // A parent with an ongoing partner who is not the other parent.
2966 std::vector<std::size_t> stepped;
2967 std::optional<std::size_t> step_of_chosen;
2968 for (const auto q : live)
2969 {
2970 for (const auto s : role_ids({relation::stepfather, relation::stepmother}, true))
2971 {
2972 if (ongoing(q, s)) stepped.push_back(q);
2973 }
2974 }
2975 const auto is_mother = [&](std::size_t q) { return f.members[q].role == relation::mother; };
2976 const auto keep_one = [&](const std::vector<std::size_t>& both)
2977 {
2978 const bool mother_first = core::bernoulli(roll, p.mother_custody_share);
2979 for (const auto q : both)
2980 {
2981 if (is_mother(q) == mother_first) return q;
2982 }
2983 return both.front();
2984 };
2985 std::vector<std::size_t> chosen;
2986 if (!stepped.empty())
2987 {
2988 const auto q = stepped.size() == 2 ? keep_one(stepped) : stepped.front();
2989 chosen.push_back(q);
2990 for (const auto s : role_ids({relation::stepfather, relation::stepmother}, true))
2991 {
2992 if (ongoing(q, s)) step_of_chosen = s;
2993 }
2994 }
2995 else if (live.size() == 2 && core::bernoulli(roll, pct(row.single_parent)))
2996 {
2997 chosen.push_back(keep_one(live));
2998 }
2999 else
3000 {
3001 chosen = live;
3002 }
3003 for (const auto q : chosen) add(q);
3004 if (step_of_chosen) add(*step_of_chosen);
3005 const std::size_t adults = out.members.size() - 1;
3006 add_siblings(chosen);
3007 // The stepparent's own children under 18, if the stepparent kept them.
3008 if (step_of_chosen)
3009 {
3010 auto kept = r.split("stepsiblings");
3011 const double share = f.members[*step_of_chosen].bio_sex == sex::female ? p.mother_custody_share
3012 : 1.0 - p.mother_custody_share;
3013 std::vector<std::size_t> theirs;
3014 for (const auto id : role_ids({relation::stepsibling}, true))
3015 {
3016 if (minor(id) && has_edge(edge_kind::parent_of, *step_of_chosen, id)) theirs.push_back(id);
3017 }
3018 if (!theirs.empty() && core::bernoulli(kept, share))
3019 {
3020 for (const auto id : theirs) add(id);
3021 }
3022 }
3023 out.type = adults >= 2 ? household_type::nuclear : household_type::single_parent;
3024 if (core::bernoulli(roll, pct(row.multi_generation)))
3025 {
3026 std::vector<std::size_t> grand;
3027 for (const auto g : role_ids({relation::paternal_grandfather, relation::paternal_grandmother,
3028 relation::maternal_grandfather, relation::maternal_grandmother},
3029 true))
3030 {
3031 if (std::ranges::any_of(chosen, [&](std::size_t q) { return has_edge(edge_kind::parent_of, g, q); }))
3032 grand.push_back(g);
3033 }
3034 if (const auto g = pick(grand))
3035 {
3036 add(*g);
3037 out.type = household_type::multi_generation;
3038 }
3039 }
3040 return out;
3041 }
3042
3043 // An adult: the ongoing partner, then the children under 18 the anchor lives with.
3044 std::optional<std::size_t> partner;
3045 for (const auto id : role_ids({relation::partner}, true))
3046 {
3047 if (ongoing(0, id)) partner = id;
3048 }
3049 std::map<std::int64_t, bool> keeps; // by the other parent: whether the anchor has the children
3050 bool kids = false;
3051 for (const auto id : role_ids({relation::child, relation::stepchild}, true))
3052 {
3053 if (!minor(id)) continue;
3054 std::optional<std::size_t> other;
3055 for (const auto& e : f.edges)
3056 {
3057 if (e.kind == edge_kind::parent_of && e.to == id && e.from != 0) other = e.from;
3058 }
3059 const bool step = f.members[id].role == relation::stepchild;
3060 bool lives = false;
3061 if (partner && other && *other == *partner)
3062 {
3063 lives = true;
3064 }
3065 else if (!step)
3066 {
3067 const std::int64_t key = other ? static_cast<std::int64_t>(*other) : -1;
3068 auto it = keeps.find(key);
3069 if (it == keeps.end())
3070 {
3071 auto kr = roll.split("custody").split(static_cast<std::uint64_t>(key + 1));
3072 const double share = me.bio_sex == sex::female ? p.mother_custody_share : 1.0 - p.mother_custody_share;
3073 it = keeps.emplace(key, core::bernoulli(kr, share)).first;
3074 }
3075 lives = it->second;
3076 }
3077 if (lives)
3078 {
3079 add(id);
3080 kids = true;
3081 }
3082 }
3083 if (partner) add(*partner);
3084
3085 if (partner || kids)
3086 {
3087 out.type = !kids ? household_type::couple : (partner ? household_type::nuclear : household_type::single_parent);
3088 if (core::bernoulli(roll, pct(row.multi_generation)))
3089 {
3090 if (const auto g = pick(role_ids({relation::father, relation::mother}, true)))
3091 {
3092 add(*g);
3093 out.type = household_type::multi_generation;
3094 }
3095 }
3096 return out;
3097 }
3098
3099 const auto parents = role_ids({relation::father, relation::mother}, true);
3100 if (!parents.empty() && static_cast<double>(me.age) < p.young_adult_home_age &&
3101 core::bernoulli(roll, p.young_adult_home_share))
3102 {
3103 for (const auto q : parents) add(q);
3104 add_siblings(parents);
3105 out.type = household_type::with_parents;
3106 return out;
3107 }
3108 out.type = core::bernoulli(roll, pct(row.one_person * p.one_person_scale)) ? household_type::one_person
3109 : household_type::other;
3110 return out;
3111 }
3112
3113 // The anchor's partners, in order. Whether the anchor ever has one comes
3114 // from the share still never married at 45 to 49; the first union begins
3115 // around the mean age at first marriage; the partner is of the other sex
3116 // unless the union is same-sex (`same_sex_share`), older or younger by the
3117 // marriage-age gap; a union ends in a separation with the divorced share
3118 // among the ever married, after a spell, or at the partner's death; after
3119 // an ending the anchor may begin another (`remarry_share`).
3120 [[nodiscard]] std::vector<union_rec> draw_unions_(const country_model& cm, const std::string& country,
3121 const life& a, double ref_day, const core::rng& r) const
3122 {
3123 const auto& p = _model.params;
3124 std::vector<union_rec> out;
3125 const bool male = a.s == sex::male;
3126 const double never = male ? cm.unions.never_m : cm.unions.never_f;
3127 const double divorced = male ? cm.unions.divorced_m : cm.unions.divorced_f;
3128 auto ever = r.split("ever");
3129 if (core::bernoulli(ever, clamp_(never / 100.0, 0.0, 1.0))) return out;
3130 const double p_divorce = clamp_(divorced / std::max(100.0 - never, 10.0), 0.0, 0.7);
3131 const double mean_gap = clamp_(cm.unions.smam_m - cm.unions.smam_f, 0.0, p.max_gap_mean);
3132 const double smam = male ? cm.unions.smam_m : cm.unions.smam_f;
3133 auto age_rng = r.split("start-age");
3134 // Unions begin earlier than first marriages where people cohabit first:
3135 // by half the years the mean age at marriage is above 21, up to `union_early_max`.
3136 const double earlier = clamp_(0.5 * (smam - 21.0), 0.0, p.union_early_max);
3137 double start = forward_(a.birth, clamp_(core::normal(age_rng, smam - earlier, p.union_age_sd),
3138 p.min_union_age, 60.0));
3139 for (int j = 0; j < static_cast<int>(p.max_unions) && start <= ref_day; ++j)
3140 {
3141 const auto uj = r.split("union").split(static_cast<std::uint64_t>(j));
3142 union_rec u;
3143 u.start = start;
3144 auto sex_rng = uj.split("sex");
3145 u.same_sex = core::bernoulli(sex_rng, p.same_sex_share);
3146 const sex ps = u.same_sex ? a.s : (male ? sex::female : sex::male);
3147 auto gap_rng = uj.split("gap");
3148 const double signed_gap = u.same_sex ? 0.0 : (male ? -mean_gap : mean_gap);
3149 const double my_age = (start - a.birth) / year_days_;
3150 const double partner_age =
3151 std::max(p.min_union_age, my_age + core::normal(gap_rng, signed_gap, p.father_gap_sd));
3152 u.partner = draw_life_(uj.split("life"), country, ps, back_(start, partner_age), partner_age);
3153 const double partner_death = forward_(u.partner.birth, u.partner.lifespan);
3154 u.end = ref_day;
3155 auto end_rng = uj.split("end");
3156 const bool separates = core::bernoulli(end_rng, p_divorce);
3157 const double separation = forward_(start, 0.5 + exponential_(end_rng, p.divorce_duration_mean));
3158 if (separates && separation < std::min(partner_death, ref_day))
3159 {
3160 u.end = separation;
3161 u.ended = union_end::divorce;
3162 }
3163 else if (partner_death <= ref_day)
3164 {
3165 u.end = partner_death;
3166 u.ended = union_end::death;
3167 }
3168 out.push_back(u);
3169 if (u.ended == union_end::ongoing) break;
3170 auto again = uj.split("remarry");
3171 if (!core::bernoulli(again, p.remarry_share)) break;
3172 start = forward_(u.end, 0.5 + exponential_(again, p.remarry_gap_mean));
3173 }
3174 return out;
3175 }
3176
3177 // The anchor's children by their unions. A different-sex union has the
3178 // woman's children born inside it (her age at each birth from the
3179 // fertility by age, left out if no age fits: the father dead nine months
3180 // before, her or his age out of range, too close to another of her births,
3181 // or outside the union) and a mean size that is the fertility rate of the
3182 // year it began, a later union's scaled by `later_union_factor`. A
3183 // same-sex union has adopted children instead, a fraction of that mean
3184 // (`adoption_factor`), born up to two years before it began.
3185 [[nodiscard]] std::vector<child_rec> draw_union_children_(const country_model& cm, const std::string& country,
3186 const life& a, const std::vector<union_rec>& unions,
3187 double ref_day, const core::rng& r) const
3188 {
3189 const auto& p = _model.params;
3190 std::vector<child_rec> out;
3191 std::vector<double> anchor_births; // days; the anchor's, when she is the mother, across unions
3192 for (std::size_t j = 0; j < unions.size(); ++j)
3193 {
3194 const auto& u = unions[j];
3195 const auto uj = r.split("union").split(static_cast<std::uint64_t>(j));
3196 const double window_end = std::min(u.end, ref_day);
3197 double mean = (tfr_at_(cm.fertility, 1970.0 + (u.start / year_days_)) + p.union_fertility_offset) *
3198 p.union_fertility;
3199 if (j > 0) mean *= p.later_union_factor;
3200 auto count_rng = uj.split("count");
3201 if (u.same_sex)
3202 {
3203 const int n = std::min(family_size_(count_rng, mean * p.adoption_factor), static_cast<int>(p.max_children));
3204 const double lo = back_(u.start, 2.0);
3205 for (int i = 0; i < n && window_end > lo; ++i)
3206 {
3207 auto ci = uj.split("adopted").split(static_cast<std::uint64_t>(i));
3208 const double birth = lo + core::math::fenced(core::uniform01(ci) * (window_end - lo));
3209 const sex s = core::bernoulli(ci, p.male_birth_share) ? sex::male : sex::female;
3210 auto l = draw_life_(ci.split("life"), country, s, birth, 0.0);
3211 l.index = i;
3212 out.push_back({.life = l, .union_index = static_cast<int>(j), .genetic = false});
3213 }
3214 continue;
3215 }
3216 const life& mother = a.s == sex::female ? a : u.partner;
3217 const life& father = a.s == sex::male ? a : u.partner;
3218 std::vector<double> local_births;
3219 std::vector<double>& births = a.s == sex::female ? anchor_births : local_births;
3220 const int n = std::min(family_size_(count_rng, mean), static_cast<int>(p.max_children));
3221 for (auto& c : place_union_births_(cm, country, mother, father, u.start, window_end, 0, n, births, uj))
3222 {
3223 c.union_index = static_cast<int>(j);
3224 out.push_back(c);
3225 }
3226 }
3227 return out;
3228 }
3229
3230 // Children `first` to `last - 1` of a man and a woman, each placed by the
3231 // mother's age at the birth between `from` and `to`, while both could be
3232 // parents, and `birth_spacing` from the mother's other `births` (which
3233 // gains theirs); a child no age fits is left out.
3234 [[nodiscard]] std::vector<child_rec> place_union_births_(const country_model& cm, const std::string& country,
3235 const life& mother, const life& father, double from,
3236 double to, int first, int last,
3237 std::vector<double>& births, const core::rng& r) const
3238 {
3239 const auto& p = _model.params;
3240 std::vector<child_rec> out;
3241 for (int i = first; i < last; ++i)
3242 {
3243 auto ci = r.split("child").split(static_cast<std::uint64_t>(i));
3244 for (int attempt = 0; attempt < 24; ++attempt)
3245 {
3246 const double am = draw_mother_age_(cm.fertility, ci);
3247 const double birth = forward_(mother.birth, am);
3248 if (birth < from || birth > to || am >= mother.lifespan) continue;
3249 const double fa = (birth - father.birth) / year_days_;
3250 if (fa < p.min_father_age || fa > p.max_father_age || fa - p.conception_lead >= father.lifespan)
3251 {
3252 continue;
3253 }
3254 if (std::ranges::any_of(births, [&](double e)
3255 { return std::abs(birth - e) < p.birth_spacing * year_days_; }))
3256 {
3257 continue;
3258 }
3259 births.push_back(birth);
3260 const sex s = core::bernoulli(ci, p.male_birth_share) ? sex::male : sex::female;
3261 auto l = draw_life_(ci.split("life"), country, s, birth, 0.0);
3262 l.index = i;
3263 out.push_back({.life = l, .genetic = true});
3264 if (const auto t = twin_of_(ci, country, l)) out.push_back({.life = *t, .genetic = true});
3265 break;
3266 }
3267 }
3268 return out;
3269 }
3270
3271 // Names, from name-generator, by the culture of the anchor's country (or
3272 // the one asked for): the anchor is drawn, or follows the parents when
3273 // they have names and she or he has not; a parent is named after the
3274 // child (get_parent_name); a child after both parents (get_child_name),
3275 // with a made-up other parent where one is not a member; a partner is
3276 // drawn, and a wife may take the anchor's name (get_married_name,
3277 // `married_name_share`). A name the caller gave is used as it is, and the
3278 // others follow it. Members with no culture for the country are left
3279 // without names, but for the ones the caller named.
3280 void assign_names_(std::vector<person>& people, const std::vector<raw_member>& raw, const core::rng& rng,
3281 const std::string& country, const std::optional<culture>& asked) const
3282 {
3283 culture c = culture::any;
3284 if (asked)
3285 {
3286 c = *asked;
3287 }
3288 else
3289 {
3290 std::string lower = country;
3291 std::ranges::transform(lower, lower.begin(),
3292 [](char ch) { return static_cast<char>(ch >= 'A' && ch <= 'Z' ? ch - 'A' + 'a' : ch); });
3293 c = ng::to_culture(core::from_utf8(lower));
3294 }
3295 if (c != culture::any)
3296 {
3297 // A culture whose lists are not loaded in the tier (name-generator's lite
3298 // has fewer than its full tier) leaves the whole family without names.
3299 try
3300 {
3301 assign_names_unchecked_(people, raw, rng, c);
3302 return;
3303 }
3304 catch (const std::invalid_argument&)
3305 {
3306 for (auto& p : people) p.given = p.patronymic = p.family = p.name = {};
3307 }
3308 }
3309 for (std::size_t i = 0; i < raw.size(); ++i)
3310 {
3311 if (!raw[i].fixed) continue;
3312 const auto& n = *raw[i].fixed;
3313 people[i].given = n.given;
3314 people[i].patronymic = n.patronymic;
3315 people[i].family = n.family;
3316 people[i].name = n.given;
3317 for (const auto* part : {&n.patronymic, &n.family})
3318 {
3319 if (!part->empty()) people[i].name += " " + *part;
3320 }
3321 }
3322 }
3323
3324 // The words of a name the caller gave, split into surnames by the culture.
3325 [[nodiscard]] static std::vector<std::wstring> surnames_of_(const std::string& family, const naming_convention& cv)
3326 {
3327 std::vector<std::wstring> out;
3328 if (family.empty() || !cv.family_name) return out;
3329 std::vector<std::string> words;
3330 std::string word;
3331 for (const char ch : family)
3332 {
3333 if (ch == ' ')
3334 {
3335 if (!word.empty()) words.push_back(std::move(word));
3336 word.clear();
3337 }
3338 else
3339 {
3340 word += ch;
3341 }
3342 }
3343 if (!word.empty()) words.push_back(std::move(word));
3344 if (cv.surnames == 2 && words.size() == 2)
3345 {
3346 for (const auto& w : words) out.push_back(core::from_utf8(w));
3347 }
3348 else
3349 {
3350 out.push_back(core::from_utf8(family));
3351 }
3352 return out;
3353 }
3354
3355 void assign_names_unchecked_(std::vector<person>& people, const std::vector<raw_member>& raw, const core::rng& rng,
3356 culture c) const
3357 {
3358 const auto gender_of = [](sex s) { return s == sex::male ? gender::m : gender::f; };
3359 const auto seed_of = [&](const std::string& key) { return rng.split(key).seed(); };
3360 const ng& names = *_from.name;
3361 const auto cv = names.convention(c);
3362 const auto full = [&](sex s, const std::string& key)
3363 { return names.get_full_name({.gender = gender_of(s), .culture = c}, seed_of(key)); };
3364 std::vector<std::optional<name>> nm(raw.size());
3365 std::unordered_map<std::int64_t, name> made_up;
3366 const auto slot = [&](std::int64_t token, sex s) -> const name&
3367 {
3368 if (token >= 0 && token < unlisted_) return *nm.at(static_cast<std::size_t>(token));
3369 auto it = made_up.find(token);
3370 if (it == made_up.end()) it = made_up.emplace(token, full(s, "other." + std::to_string(token))).first;
3371 return it->second;
3372 };
3373 // The first parent's slot is a father's. Where a woman holds it (a
3374 // same-sex union), a man of her family stands in, so that the culture's
3375 // convention (a patronymic, a masculine surname) applies as it does.
3376 const auto first_slot = [&](std::int64_t token) -> const name&
3377 {
3378 if (token >= 0 && token < unlisted_ && raw.at(static_cast<std::size_t>(token)).life.s == sex::female)
3379 {
3380 const auto key = -token - 2;
3381 auto it = made_up.find(key);
3382 if (it == made_up.end())
3383 {
3384 it = made_up
3385 .emplace(key, names.get_parent_name(*nm.at(static_cast<std::size_t>(token)), gender::m,
3386 seed_of("stand-in." + raw.at(static_cast<std::size_t>(token)).key)))
3387 .first;
3388 }
3389 return it->second;
3390 }
3391 return slot(token, sex::male);
3392 };
3393
3394 for (std::size_t i = 0; i < raw.size(); ++i)
3395 {
3396 if (!raw[i].fixed) continue;
3397 const auto& n = *raw[i].fixed;
3398 if (n.given.empty()) throw std::invalid_argument("a name needs a given name");
3399 nm[i] = names.compose_name({.gender = gender_of(raw[i].life.s),
3400 .given = core::from_utf8(n.given),
3401 .patronymic = core::from_utf8(n.patronymic),
3402 .family = surnames_of_(n.family, cv)},
3403 c, seed_of(raw[i].key));
3404 }
3405
3406 if (!nm[0])
3407 {
3408 // Parents with names give the anchor a name that follows theirs.
3409 const auto f = raw[0].parents[0];
3410 const auto m = raw[0].parents[1];
3411 const bool parents = f >= 0 && f < unlisted_ && m >= 0 && m < unlisted_;
3412 if (parents && (nm[static_cast<std::size_t>(f)] || nm[static_cast<std::size_t>(m)]))
3413 {
3414 const auto fi = static_cast<std::size_t>(f);
3415 const auto mi = static_cast<std::size_t>(m);
3416 if (!nm[fi]) nm[fi] = full(raw[fi].life.s, raw[fi].key);
3417 if (!nm[mi]) nm[mi] = full(raw[mi].life.s, raw[mi].key);
3418 nm[0] = names.get_child_name(*nm[fi], *nm[mi], {.gender = gender_of(raw[0].life.s)}, seed_of(raw[0].key));
3419 }
3420 else
3421 {
3422 nm[0] = full(raw[0].life.s, raw[0].key);
3423 }
3424 }
3425 // The adults first, then the children, who may come before a parent
3426 // (a half-sibling and a stepparent). Each name has its own stream, so
3427 // the order changes no name.
3428 const auto is_child = [](relation r)
3429 {
3430 return r == relation::sibling || r == relation::half_sibling || r == relation::child ||
3431 r == relation::grandchild || r == relation::stepchild || r == relation::stepsibling;
3432 };
3433 std::vector<std::size_t> order;
3434 for (std::size_t i = 1; i < raw.size(); ++i)
3435 {
3436 if (!is_child(raw[i].role)) order.push_back(i);
3437 }
3438 for (std::size_t i = 1; i < raw.size(); ++i)
3439 {
3440 if (is_child(raw[i].role)) order.push_back(i);
3441 }
3442 for (const auto i : order)
3443 {
3444 if (nm[i]) continue;
3445 const auto& m = raw[i];
3446 switch (m.role)
3447 {
3448 case relation::anchor: break;
3449 case relation::father:
3450 case relation::mother:
3451 case relation::paternal_grandfather:
3452 case relation::paternal_grandmother:
3453 case relation::maternal_grandfather:
3454 case relation::maternal_grandmother:
3455 nm[i] = names.get_parent_name(*nm.at(static_cast<std::size_t>(m.name_child)), gender_of(m.life.s),
3456 seed_of(m.key));
3457 break;
3458 case relation::stepfather:
3459 case relation::stepmother:
3460 {
3461 nm[i] = full(m.life.s, m.key);
3462 auto married = rng.split(m.key + ".married");
3463 const auto parent = static_cast<std::size_t>(m.name_child);
3464 if (raw[parent].life.s == sex::male && m.life.s == sex::female &&
3465 core::bernoulli(married, _model.params.married_name_share))
3466 {
3467 nm[i] = names.get_married_name(*nm[i], *nm.at(parent));
3468 }
3469 break;
3470 }
3471 case relation::partner:
3472 {
3473 nm[i] = full(m.life.s, m.key);
3474 auto married = rng.split(m.key + ".married");
3475 if (raw[0].life.s == sex::male && m.life.s == sex::female &&
3476 core::bernoulli(married, _model.params.married_name_share))
3477 {
3478 nm[i] = names.get_married_name(*nm[i], *nm[0]);
3479 }
3480 break;
3481 }
3482 case relation::sibling:
3483 case relation::half_sibling:
3484 case relation::child:
3485 case relation::grandchild:
3486 case relation::stepchild:
3487 case relation::stepsibling:
3488 nm[i] = names.get_child_name(first_slot(m.parents[0]), slot(m.parents[1], sex::female),
3489 {.gender = gender_of(m.life.s)}, seed_of(m.key));
3490 break;
3491 }
3492 }
3493 for (std::size_t i = 0; i < raw.size(); ++i)
3494 {
3495 const auto& n = *nm[i];
3496 people[i].given = core::to_utf8(n.given());
3497 people[i].patronymic = core::to_utf8(n.patronymic());
3498 people[i].family = core::to_utf8(n.family());
3499 people[i].name = core::to_utf8(static_cast<std::wstring>(n));
3500 }
3501 }
3502};
3503
3504} // namespace dasmig
3505
3506#endif // DASMIG_RELGEN_HPP
Return type for family generation.
Definition relgen.hpp:354
std::vector< person > with_role(relation r) const
The members with a given role.
Definition relgen.hpp:371
std::uint64_t seed() const
Retrieve the random seed used to generate this family.
Definition relgen.hpp:466
std::vector< person > stepparents() const
The anchor's stepparents: the partners of a parent who are not the other parent.
Definition relgen.hpp:440
std::vector< person > siblings() const
The anchor's full siblings, oldest first.
Definition relgen.hpp:392
std::vector< person > members
Everyone; members[i].id == i.
Definition relgen.hpp:358
std::vector< person > stepchildren() const
The children of the anchor's partners by earlier unions.
Definition relgen.hpp:435
std::chrono::year_month_day reference_date
The date ages and who is alive refer to.
Definition relgen.hpp:357
const person & anchor() const
The person the family is built around.
Definition relgen.hpp:366
const core::seed_signature & signature() const
The seed and the data this family was generated from.
Definition relgen.hpp:471
friend std::ostream & operator<<(std::ostream &os, const family &v)
Stream the summary to an output stream.
Definition relgen.hpp:577
std::vector< edge > edges
Parent, sibling and partner links.
Definition relgen.hpp:359
std::optional< household > home
Who the anchor lives with; set only when family_request::home was. // NOLINT(readability-redundant-me...
Definition relgen.hpp:362
void to_json(core::json_writer &w) const
Write this family as a JSON object.
Definition relgen.hpp:486
std::vector< person > parents() const
The anchor's parents.
Definition relgen.hpp:383
std::string to_json() const
This family as a JSON document.
Definition relgen.hpp:559
std::string country_code
The anchor's country; the family lives there.
Definition relgen.hpp:356
static std::string iso_date(const std::chrono::year_month_day &d)
A date as YYYY-MM-DD.
Definition relgen.hpp:586
std::string to_string() const
Human-readable summary string.
Definition relgen.hpp:478
std::vector< person > children() const
The anchor's children, oldest first.
Definition relgen.hpp:427
data_source union_source
Where the marriage figures came from.
Definition relgen.hpp:361
std::optional< edge > union_with(std::size_t partner_id) const
The union with a partner.
Definition relgen.hpp:454
std::vector< person > stepsiblings() const
The stepparents' children by an earlier union.
Definition relgen.hpp:449
std::vector< person > partners() const
The anchor's partners, in the order the unions began.
Definition relgen.hpp:423
std::vector< person > grandchildren() const
The anchor's grandchildren, oldest first.
Definition relgen.hpp:431
std::vector< person > grandparents() const
The anchor's grandparents.
Definition relgen.hpp:410
data_source fertility_source
Where the fertility figures came from.
Definition relgen.hpp:360
std::vector< person > half_siblings() const
The anchor's half-siblings, oldest first.
Definition relgen.hpp:400
Relationship Generator: builds believable families around a person.
Definition relgen.hpp:634
relg()=default
Default constructor — creates an empty generator with no data, drawing from the birth-generator singl...
static relg & instance()
Access the global singleton instance.
Definition relgen.hpp:663
relg(sources from)
A generator with no data that draws from the given library instances (which must outlive it).
Definition relgen.hpp:650
family expand(const family &base, std::size_t member, family_request request, std::uint64_t call_seed) const
Generate the family of a member of a family, so that the two agree.
Definition relgen.hpp:1155
const std::vector< core::layer_info > & layers(family_table which) const
The layers in effect for one table, in order.
Definition relgen.hpp:1392
std::vector< family > get_family_batch(const family_request &request, const core::batch_options &options, std::uint64_t batch_seed) const
Generate a batch of family results, deterministically.
Definition relgen.hpp:1207
relg & operator=(const relg &)=delete
Not copyable.
relg(const relg &)=delete
Not copyable.
std::uint64_t data_hash() const
Hash of the loaded data: this generator's tables and birth-generator's data. Recorded in each result'...
Definition relgen.hpp:1401
const core::layer_info & extend_from_string(family_table which, std::string_view text, std::string source, core::layer_mode mode=core::layer_mode::override)
Add a data layer to one table from TSV text (for example from a WebAssembly host).
Definition relgen.hpp:1383
std::vector< family > get_family_batch(const family_request &request, const core::batch_options &options)
Generate a batch of family results from a seed drawn like get_family() draws its own.
Definition relgen.hpp:1186
void extend(const std::filesystem::path &path, core::layer_mode mode=core::layer_mode::override)
Add data layers from a directory or a file.
Definition relgen.hpp:1333
void for_each_family(const family_request &request, const core::batch_options &options, Sink &&sink)
Stream a batch of family results with a batch seed drawn like get_family() draws its own.
Definition relgen.hpp:1244
bool has_family_data() const
Check whether the family tables are loaded (birth-generator is not asked).
Definition relgen.hpp:1259
family expand(const family &base, std::size_t member, const family_request &request)
Generate a random family of a member of a family; see the seeded overload.
Definition relgen.hpp:1170
void for_each_family(const family_request &request, const core::batch_options &options, std::uint64_t batch_seed, Sink &&sink) const
Stream a batch of family results to a sink without holding the whole batch.
Definition relgen.hpp:1229
bool load(dataset tier)
Load a dataset tier, replacing all data and layers loaded before.
Definition relgen.hpp:1303
void load(const std::filesystem::path &dir)
Load data from a resource directory, replacing all data and layers loaded before.
Definition relgen.hpp:1282
family get_family(const family_request &request, std::uint64_t call_seed) const
Generate a deterministic family.
Definition relgen.hpp:696
std::vector< std::string > countries() const
The countries with figures of their own in any table.
Definition relgen.hpp:1263
bool has_data() const
Check whether this generator and birth-generator have data.
Definition relgen.hpp:1254
const core::layer_info & extend(family_table which, core::tsv_document doc, core::layer_mode mode=core::layer_mode::override)
Add a data layer to one table.
Definition relgen.hpp:1366
relg(relg &&) noexcept=default
Movable.
family get_family(const family_request &request)
Generate a random family. Use family_request{} for the defaults.
Definition relgen.hpp:678
constexpr std::string_view union_end_name(union_end e) noexcept
The name of an ending as it appears in JSON.
Definition relgen.hpp:264
relation
How a member is related to the anchor.
Definition relgen.hpp:180
@ father
The anchor's father.
@ paternal_grandmother
The father's mother.
@ sibling
A full brother or sister.
@ child
A child of the anchor, by birth or adoption.
@ stepfather
A man who is, or was, a parent's partner while the anchor was a child.
@ grandchild
A child of one of the anchor's children.
@ anchor
The person the family is built around.
@ paternal_grandfather
The father's father.
@ mother
The anchor's mother.
@ partner
A partner of the anchor, current or former.
@ maternal_grandfather
The mother's father.
@ stepsibling
A stepparent's child by an earlier union, not a parent's.
@ stepmother
A woman who is, or was, a parent's partner while the anchor was a child.
@ stepchild
A child of a partner by an earlier union, not the anchor's.
@ maternal_grandmother
The mother's mother.
@ half_sibling
A half-brother or half-sister, through one parent.
constexpr std::string_view data_source_name(data_source s) noexcept
The name of a data source as it appears in JSON.
Definition relgen.hpp:337
family_table
The tables of the family data, for extending it.
Definition relgen.hpp:599
@ regions
regions.tsv: the UN subregion of each country.
@ parameters
parameters.tsv: the model's constants.
@ households
households.tsv: household size and types.
@ unions
unions.tsv: age at first marriage, never married and divorced shares.
@ defaults
defaults.tsv: the median of every column per subregion, and the world.
@ fertility
fertility.tsv: total fertility rate (now and by period) and fertility by the mother's age.
edge_kind
How two members are linked.
Definition relgen.hpp:246
@ partner_of
from (the anchor, or a parent of the anchor for a stepparent) and to are, or were,...
@ sibling_of
from and to share a parent; from is the older.
@ parent_of
from is a parent of to.
@ step_parent_of
from (a stepfather or stepmother) is a stepparent of to (the anchor).
constexpr std::string_view relation_name(relation r) noexcept
The name of a relation as it appears in JSON (paternal_grandfather).
Definition relgen.hpp:202
household_type
The kind of home the anchor lives in at the reference date.
Definition relgen.hpp:290
@ nuclear
Two adults and children under 18.
@ couple
The anchor and a partner.
@ other
Anyone else: the relatives the family holds do not live with the anchor.
@ one_person
The anchor lives alone.
@ with_parents
An adult living with a parent (and the younger siblings still at home).
@ single_parent
One adult and children under 18.
@ multi_generation
Three generations, or an adult household that includes a parent.
union_end
How a union ended.
Definition relgen.hpp:255
@ death
The partner died.
@ ongoing
It has not ended at the reference date.
@ divorce
The partners separated.
data_source
Where a table's values came from for the family's country.
Definition relgen.hpp:328
@ world
The world median.
@ subregion
The median of its UN subregion.
@ country
The country's own row.
constexpr std::string_view household_type_name(household_type t) noexcept
The name of a household type as it appears in JSON.
Definition relgen.hpp:304
The person a family is built around, as plain values.
Definition relgen.hpp:106
std::optional< std::vector< known_sibling > > siblings
The anchor's siblings and half-siblings, if known; none are drawn when this is set....
Definition relgen.hpp:124
std::optional< known_person > father
The anchor's father, if known. Needs up >= 1 to appear.
Definition relgen.hpp:119
std::optional< person_name > name
The anchor's name; the parents, siblings and children are named to match it. Drawn when omitted.
Definition relgen.hpp:117
std::optional< sex > bio_sex
Biological sex, or drawn.
Definition relgen.hpp:110
std::optional< std::string > country_code
ISO 3166-1 alpha-2 code, or drawn by population.
Definition relgen.hpp:108
std::optional< std::chrono::year_month_day > birth
Date of birth, or drawn.
Definition relgen.hpp:112
std::optional< int > age
Age in completed years at the reference date, or drawn.
Definition relgen.hpp:114
std::optional< known_person > mother
The anchor's mother, if known. Needs up >= 1 to appear.
Definition relgen.hpp:121
std::optional< std::vector< known_union > > unions
The anchor's unions, if known (oldest first); none are drawn when this is set, and the children are t...
Definition relgen.hpp:127
A link between two members, by id.
Definition relgen.hpp:277
std::size_t from
First member.
Definition relgen.hpp:279
bool genetic
For parents: the child is the parent's by birth, not by adoption.
Definition relgen.hpp:282
std::optional< int > start
For partners: the year the union began.
Definition relgen.hpp:283
union_end ended
For partners: how it ended.
Definition relgen.hpp:285
edge_kind kind
What the link is.
Definition relgen.hpp:278
std::size_t to
Second member.
Definition relgen.hpp:280
std::optional< int > end
For partners: the year it ended, if it has.
Definition relgen.hpp:284
bool full
For siblings: both parents are shared.
Definition relgen.hpp:281
What a family call asks for.
Definition relgen.hpp:136
bool home
Whether to draw who the anchor lives with at the reference date (family::home). Off by default,...
Definition relgen.hpp:170
bool siblings
Whether to include the anchor's siblings (and half-siblings). Siblings come from the parents,...
Definition relgen.hpp:147
std::chrono::year_month_day reference_date
The date ages and who is alive are computed at (1 July 2023, the date of the UN estimates,...
Definition relgen.hpp:175
bool stepparents
Whether to include the partners of the anchor's parents who are not the other parent (stepfathers and...
Definition relgen.hpp:164
int up
Generations above the anchor: 0 for none, 1 for parents, 2 for grandparents too.
Definition relgen.hpp:141
std::optional< culture > name_culture
The culture the names follow; the anchor's country's by default. Needs names; a culture with no names...
Definition relgen.hpp:156
bool names
Whether to give everyone a name (needs name-generator's data).
Definition relgen.hpp:152
bool weighted
Population-weighted (true) or uniform country, when drawn.
Definition relgen.hpp:172
int down
Generations below the anchor: 0 for none, 1 for children, 2 for grandchildren too.
Definition relgen.hpp:144
bool partners
Whether to include the anchor's partners, current and former. Children come from the unions either wa...
Definition relgen.hpp:150
anchor_spec anchor
The person the family is around; omitted fields are drawn.
Definition relgen.hpp:138
Who the anchor lives with.
Definition relgen.hpp:321
std::vector< std::size_t > members
Ids of the people living there, the anchor (0) first; all alive.
Definition relgen.hpp:323
household_type type
The kind of home.
Definition relgen.hpp:322
A child the caller already knows.
Definition relgen.hpp:81
bool genetic
By birth rather than by adoption.
Definition relgen.hpp:83
Someone the caller already knows: used to build the family of a member of a family that exists,...
Definition relgen.hpp:63
std::optional< std::chrono::year_month_day > death
Date of death, for the dead. // NOLINT(readability-redundant-member-init)
Definition relgen.hpp:66
sex bio_sex
Biological sex.
Definition relgen.hpp:64
std::vector< known_person > parents
This person's own parents, when known: the anchor's grandparents (at most one of each sex)....
Definition relgen.hpp:68
std::optional< person_name > name
The name, or drawn to match the relatives'. // NOLINT(readability-redundant-member-init)
Definition relgen.hpp:67
std::chrono::year_month_day birth
Date of birth.
Definition relgen.hpp:65
A brother or sister the caller already knows.
Definition relgen.hpp:73
sex through
For a half-sibling: the shared parent's sex.
Definition relgen.hpp:76
bool full
Both parents are shared; otherwise only through.
Definition relgen.hpp:75
A union of the anchor that the caller already knows, with the children it had.
Definition relgen.hpp:89
std::optional< std::chrono::year_month_day > end
When it ended; none if it is ongoing. // NOLINT(readability-redundant-member-init)
Definition relgen.hpp:92
std::vector< known_person > stepchildren
The partner's children by an earlier union. // NOLINT(readability-redundant-member-init)
Definition relgen.hpp:94
std::optional< known_person > partner
The partner; none when only the children are known. // NOLINT(readability-redundant-member-init)
Definition relgen.hpp:90
std::vector< known_child > children
The children of this union. // NOLINT(readability-redundant-member-init)
Definition relgen.hpp:93
std::chrono::year_month_day start
When the union began.
Definition relgen.hpp:91
A name the caller already has, as plain words.
Definition relgen.hpp:51
std::string family
Family name; empty for none.
Definition relgen.hpp:54
std::string given
Given name; not empty.
Definition relgen.hpp:52
std::string patronymic
Patronymic, for the cultures that have one; empty for none.
Definition relgen.hpp:53
A person in a family: who they are to the anchor, their name and when they live.
Definition relgen.hpp:229
std::optional< std::chrono::year_month_day > death
Date of death, for the dead. // NOLINT(readability-redundant-member-init)
Definition relgen.hpp:241
bool twin
Born on the same day as a full sibling (or the anchor's).
Definition relgen.hpp:238
std::string name
The full name, in the culture's order.
Definition relgen.hpp:236
sex bio_sex
Biological sex.
Definition relgen.hpp:232
std::uint8_t age
Age in completed years at the reference date, or at death.
Definition relgen.hpp:240
bool alive
Alive at the reference date.
Definition relgen.hpp:239
std::string given
Given name; empty without names.
Definition relgen.hpp:233
std::string patronymic
Patronymic, for the cultures that have one.
Definition relgen.hpp:234
relation role
How the person is related to the anchor.
Definition relgen.hpp:231
std::string family
Family name (both of a double surname).
Definition relgen.hpp:235
std::chrono::year_month_day birth
Date of birth.
Definition relgen.hpp:237
The library instances a family is drawn from.
Definition relgen.hpp:638
bthg * birth
Anchor, age structure and life expectancy.
Definition relgen.hpp:639