# Quick Reference

# Automerge R Package Quick Reference

One-page reference for the `automerge` package. See full documentation with `?function_name`.

## Installation

    # From R-universe
    install.packages("automerge", repos = "https://posit-dev.r-universe.dev")

    # From GitHub
    pak::pak("posit-dev/automerge-r")

## Document Lifecycle

    library(automerge)

    # Create
    doc <- am_create() # New document
    doc <- am_create("fd3ca50687f13477f1f9ea2b216b958a") # With custom actor ID

    # Load/Save
    bytes <- am_save(doc) # Save to bytes
    doc <- am_load(bytes) # Load from bytes

    # Actor ID
    actor <- am_get_actor(doc) # Get actor ID (raw bytes)
    actor_hex <- am_get_actor_hex(doc) # Get actor ID as hex string
    am_set_actor(doc, actor_hex) # Set actor ID (raw, hex, or NULL)

    # Fork/Merge
    doc2 <- am_fork(doc) # Create independent copy
    am_merge(doc, doc2) # Merge doc2 into doc1

    # Transactions
    am_commit(doc, "message") # Commit changes
    am_rollback(doc) # Cancel pending changes

## Basic Access (Maps)

    # S3 operators
    doc[["key"]] <- "value" # Set value
    value <- doc[["key"]] # Get value
    doc$key <- value # Alternative syntax
    value <- doc$key # Alternative syntax

    # Functional API
    am_delete(doc, AM_ROOT, "key") # Delete key
    am_put(doc, AM_ROOT, "key", value) # Set value
    value <- am_get(doc, AM_ROOT, "key") # Get value

    # Introspection
    keys <- names(doc) # Get all keys
    keys <- am_keys(doc, AM_ROOT) # Functional version
    values <- am_values(doc, AM_ROOT) # Get all values
    n <- length(doc) # Number of keys
    n <- am_length(doc, AM_ROOT) # Functional version

## Nested Objects

    # Automatic recursive conversion (recommended)
    am_put(
      doc,
      AM_ROOT,
      "user",
      list(
        name = "Alice",
        age = 30L,
        address = list(city = "NYC", zip = 10001L)
      )
    )

    # Path-based access (simple for deep structures)
    am_put_path(doc, c("user", "address", "city"), "Boston")
    city <- am_get_path(doc, c("user", "address", "city"))
    am_delete_path(doc, c("user", "address"))

    # Manual access
    user_obj <- am_get(doc, AM_ROOT, "user")
    am_put(doc, user_obj, "name", "Bob")
    name <- am_get(doc, user_obj, "name")

## Lists

    # Create list
    am_put(doc, AM_ROOT, "items", AM_OBJ_TYPE_LIST)
    items <- am_get(doc, AM_ROOT, "items")

    # Operations (1-based indexing)
    am_insert(doc, items, 1, "first") # Insert at index 1
    am_put(doc, items, 1, "FIRST") # Replace at index 1
    am_put(doc, items, "end", "append") # Append to end
    value <- am_get(doc, items, 1) # Get index 1
    am_delete(doc, items, 1) # Delete index 1

    # Introspection
    n <- am_length(doc, items) # List length

## Text Objects

    # Text objects use 0-based inter-character positions
    # For the text "Hello":
    #  H e l l o
    # 0 1 2 3 4 5  <- positions (0-based, between characters)

    # Create text object
    am_put(doc, AM_ROOT, "content", am_text("Hello"))
    text_obj <- am_get(doc, AM_ROOT, "content")

    # Operations
    am_text_splice(text_obj, 5, 0, " World") # Insert at position 5
    content <- am_text_content(text_obj) # Get full text

    # Update text (ideal for collaborative editing)
    old_text <- am_text_content(text_obj)
    am_text_update(text_obj, old_text, "Hello Universe") # Computes and applies diff

    # Cursors (stable positions)
    cursor <- am_cursor(text_obj, 5) # Create cursor at position 5
    pos <- am_cursor_position(cursor) # Get current position

    # Marks (formatting)
    am_mark(text_obj, 0, 5, "bold", TRUE, expand = "none")
    marks <- am_marks(text_obj) # Get all marks
    marks_at <- am_marks_at(text_obj, 2) # Marks at position 2

## Value Types

    # NULL
    am_put(doc, AM_ROOT, "null", NULL)

    # Boolean
    am_put(doc, AM_ROOT, "bool", TRUE)

    # Integer
    am_put(doc, AM_ROOT, "int", 42L)

    # Double
    am_put(doc, AM_ROOT, "float", 3.14)

    # String
    am_put(doc, AM_ROOT, "str", "text")

    # Raw bytes
    am_put(doc, AM_ROOT, "bytes", raw(10))

    # Timestamp
    am_put(doc, AM_ROOT, "time", Sys.time())

    # Counter
    am_put(doc, AM_ROOT, "score", am_counter(0))
    am_counter_increment(doc, AM_ROOT, "score", 10)
    value <- am_get(doc, AM_ROOT, "score")

    # Unsigned 64-bit integer (for cross-platform interop)
    am_put(doc, AM_ROOT, "id", am_uint64(12345))

    # Explicit type constructors
    am_put(doc, AM_ROOT, "items", am_list()) # Empty list
    am_put(doc, AM_ROOT, "config", am_map()) # Empty map
    am_put(doc, AM_ROOT, "notes", am_text()) # Text object

## Synchronization (High-Level)

    # Bidirectional sync (auto-converge)
    # Documents are modified in place
    rounds <- am_sync(doc1, doc2)
    cat("Converged in", rounds, "rounds\n")

    # One-way merge
    am_merge(doc1, doc2) # Merge doc2 into doc1

## Synchronization (Low-Level)

    # Create sync state
    sync_state <- am_sync_state_new()

    # Encode/decode messages
    msg <- am_sync_encode(doc, sync_state)
    am_sync_decode(doc, sync_state, msg)

    # Manual sync loop
    repeat {
      msg1 <- am_sync_encode(doc1, sync1)
      if (is.null(msg1)) {
        break
      }
      am_sync_decode(doc2, sync2, msg1)

      msg2 <- am_sync_encode(doc2, sync2)
      if (is.null(msg2)) {
        break
      }
      am_sync_decode(doc1, sync1, msg2)
    }

## History & Changes

    # Get heads
    heads <- am_get_heads(doc)

    # Get changes
    changes <- am_get_changes(doc, NULL) # All changes
    changes <- am_get_changes(doc, heads) # Since specific heads

    # Apply changes
    am_apply_changes(doc, changes)

    # Full history
    history <- am_get_history(doc)

## Conversion

    # R → Automerge
    doc <- as_automerge(list(name = "Alice", age = 30L))

    # Automerge → R
    r_list <- from_automerge(doc)

    r_list <- as.list(doc) # S3 method for documents
    text_str <- as.character(text_obj) # S3 method for text objects

## Pipe-Friendly Style

    doc <- am_create() |>
      am_put(AM_ROOT, "name", "Alice") |>
      am_put(AM_ROOT, "age", 30L) |>
      am_commit("Initial data")

## File Operations

    # Save to file
    writeBin(am_save(doc), "document.automerge")

    # Load from file
    doc <- am_load(readBin("document.automerge", "raw", 1e6))

## Constants

    AM_ROOT # Root object (NULL)
    AM_OBJ_TYPE_LIST # "list"
    AM_OBJ_TYPE_MAP # "map"
    AM_OBJ_TYPE_TEXT # "text"

    AM_MARK_EXPAND_NONE # "none" (mark doesn't expand at boundaries)
    AM_MARK_EXPAND_BEFORE # "before" (expands when text inserted before start)
    AM_MARK_EXPAND_AFTER # "after" (expands when text inserted after end)
    AM_MARK_EXPAND_BOTH # "both" (expands at both boundaries)

## Getting Help

    # Function help
    ?am_create
    ?am_put
    ?am_sync

    # Package help
    ?automerge
    help(package = "automerge")

    # Vignettes
    vignette("automerge", "automerge")
    vignette("crdt-concepts", "automerge")
    vignette("sync-protocol", "automerge")
    vignette(package = "automerge") # List all

## System Requirements

- R >= 4.2
- For building from source:
  - Rust >= 1.89.0 (https://rustup.rs/)
  - CMake >= 3.25 (included in Rtools43+ on Windows)
- Or: Install automerge-c (with UTF-32 character indexing) system-wide to skip build

## Resources

- Package site: https://posit-dev.github.io/automerge-r/
- Automerge docs: https://automerge.org/
- Binary format: https://automerge.org/automerge-binary-format-spec/
- CRDT research: https://crdt.tech/

Note: This is a quick reference. See full vignettes for detailed examples and explanations.