Skip to main content

What is a Context?

A context in Giac is an evaluation environment that encapsulates all the state needed to evaluate mathematical expressions. It provides isolation for variables, settings, and history while enabling controlled access to parent contexts. From global.h:
class context {
public:
  sym_tab * tabptr;              // Symbol table (variable bindings)
  context * globalcontextptr;    // Current global context
  context * previous;            // Parent context
  global * globalptr;            // Global settings
  const context * parent;        // Read-only parent
  vecteur * quoted_global_vars;  // Global variables to quote
  vecteur * rootofs;             // Algebraic extensions
  vecteur * history_in_ptr;      // Input history
  vecteur * history_out_ptr;     // Output history
  vecteur * history_plot_ptr;    // Graphics history
};

Why Use Contexts?

Contexts solve several important problems:
Each context has its own symbol table, preventing naming conflicts:
context ctx1, ctx2;

// Set x=5 in ctx1
gen x1("x", &ctx1);
sto(gen(5), x1, &ctx1);

// Set x=10 in ctx2  
gen x2("x", &ctx2);
sto(gen(10), x2, &ctx2);

// x has different values in each context
eval(x1, 1, &ctx1);  // Returns 5
eval(x2, 1, &ctx2);  // Returns 10
Different contexts can have different settings:
context ctx1, ctx2;

// Degrees in ctx1
angle_mode(1, &ctx1);  
gen sin_45_deg = eval(gen("sin(45)", &ctx1), 1, &ctx1);

// Radians in ctx2
angle_mode(0, &ctx2);
gen sin_45_rad = eval(gen("sin(45)", &ctx2), 1, &ctx2);
Each thread can have its own context for safe parallel computation:
void worker_thread(const gen & expr) {
  context thread_ctx;  // Local context for this thread
  gen result = eval(expr, 1, &thread_ctx);
  // No interference with other threads
}
Contexts store evaluation history and can be saved/restored:
context session;
// ... user interaction ...

// Save session
gen state = giac_current_status(true, &session);

// Later: restore session  
unarchive_session(state, 1, undef, &session);

Context Hierarchy

Global Context

The global context (context0) is the top-level context:
extern const context * context0;  // Global default context
When contextptr is NULL or 0, Giac uses context0 as the default.

Local Contexts

Local contexts can be created with a parent:
context parent_ctx;
context child_ctx;

// Link child to parent
child_ctx.previous = &parent_ctx;
child_ctx.globalcontextptr = &parent_ctx;
Lookup behavior:
  1. Search local symbol table first
  2. If not found, search parent context
  3. Continue up the chain to global context

Symbol Tables

Structure

typedef std::map<const char *, gen, ltstr> sym_tab;

struct ltstr {
  bool operator()(const char* s1, const char* s2) const {
    return strcmp(s1, s2) < 0;
  }
};

Variable Binding

// Store a value
gen x("x", contextptr);
sto(gen(42), x, contextptr);  // x := 42

// Retrieve a value
gen value = eval(x, 1, contextptr);  // Returns 42

// Check if variable is bound
gen * ptr = contextptr->tabptr->at("x");
if (ptr) {
  // Variable exists
}

Global Settings

The global structure in a context stores configuration:
class global {
public:
  int _xcas_mode_;              // 0=Xcas, 1=Maple, 2=Mupad, 3=TI89
  int _calc_mode_;              // Calculator mode
  int _decimal_digits_;         // Display precision
  int _angle_mode_;             // 0=radians, 1=degrees
  bool _complex_mode_;          // Allow complex results
  bool _approx_mode_;           // Approximate vs exact
  double _epsilon_;             // Numerical tolerance
  // ... many more settings
};

Accessing Settings

// Get angle mode
int mode = angle_mode(contextptr);

// Set to degrees  
angle_mode(1, contextptr);

// Check if in complex mode
if (complex_mode(contextptr)) {
  // Complex arithmetic enabled
}

// Set numerical tolerance
epsilon(1e-10, contextptr);

The GIAC_CONTEXT Macro

Giac uses a macro convention for passing contexts:
#define GIAC_CONTEXT const context * contextptr
#define GIAC_CONTEXT0 const context * contextptr=0
Usage patterns:
// Function that requires a context
gen my_function(const gen & arg, GIAC_CONTEXT) {
  // contextptr is available here
  return eval(arg, 1, contextptr);
}

// Function with optional context (defaults to context0)
gen another_function(const gen & arg, GIAC_CONTEXT0) {
  if (!contextptr) contextptr = context0;
  return eval(arg, 1, contextptr);
}

Evaluation with Contexts

The eval() Function

gen eval(const gen & e, int level, const context * contextptr);
Parameters:
  • e: Expression to evaluate
  • level: Evaluation depth (typically 1 for full evaluation)
  • contextptr: Context for variable lookup and settings

Evaluation Levels

// Level 0: No evaluation (return as-is)
gen unevaluated = eval(expr, 0, contextptr);

// Level 1: Standard evaluation
gen result = eval(expr, 1, contextptr);

// Higher levels: More aggressive simplification
gen simplified = eval(expr, DEFAULT_EVAL_LEVEL, contextptr);

Example: Function with Local Context

From minigiac.cc:
int main(int argc, char *argv[]) {
  context ct;               // Create local context
  string line;
  gen g;
  
  while (getline(cin, line)) {
    g = gen(line, &ct);     // Parse with context
    
    try {
      cout << eval(g, 1, &ct) << endl;  // Evaluate in context
    } catch (runtime_error & err) {
      cout << "ERROR: " << err.what() << endl;
    }
  }
}
This creates an isolated environment where:
  • Variables are scoped to ct
  • Settings don’t affect other contexts
  • History is maintained separately

Context Lifetime Management

Creation and Destruction

// Stack allocation (automatic cleanup)
void function() {
  context local_ctx;  // Created
  // Use local_ctx
} // Automatically destroyed

// Heap allocation (manual cleanup)
context * ctx = new context();
// Use ctx
delete ctx;  // Must explicitly delete

Cleanup

// Clear all variables and history
clear_context(contextptr);

// Full cleanup including threads
cleanup_context(contextptr);

Common Context Operations

Working with Variables

gen x("x", contextptr);
gen value(42);
sto(value, x, contextptr);  // x := 42

Configuration Examples

// Set to degrees
angle_mode(1, contextptr);
gen s = eval(gen("sin(90)", contextptr), 1, contextptr);  // 1

// Set to radians  
angle_mode(0, contextptr);
gen s2 = eval(gen("sin(90)", contextptr), 1, contextptr);  // 0.894

Advanced Context Patterns

Temporary Context Override

gen evaluate_in_degrees(const gen & expr, GIAC_CONTEXT) {
  // Save current mode
  int old_mode = angle_mode(contextptr);
  
  // Temporarily switch to degrees
  angle_mode(1, contextptr);
  
  // Evaluate
  gen result = eval(expr, 1, contextptr);
  
  // Restore mode
  angle_mode(old_mode, contextptr);
  
  return result;
}

Child Context for Local Scope

gen function_with_locals(const gen & arg, GIAC_CONTEXT) {
  context local_ctx;
  local_ctx.previous = const_cast<context*>(contextptr);
  local_ctx.globalcontextptr = const_cast<context*>(contextptr);
  
  // Create local variable
  gen x("x", &local_ctx);
  sto(arg, x, &local_ctx);
  
  // Evaluate with local x
  gen body = gen("x^2 + 1", &local_ctx);
  gen result = eval(body, 1, &local_ctx);
  
  // local_ctx destroyed here, x goes out of scope
  return result;
}

Thread-Safe Contexts

Per-Thread Contexts

#include <thread>

void parallel_compute(const gen & expr, int thread_id) {
  context thread_ctx;  // Each thread gets its own context
  
  // Configure independently
  angle_mode(thread_id % 2, &thread_ctx);  // Alternate degrees/radians
  
  gen result = eval(expr, 1, &thread_ctx);
  
  // Thread-safe: no interference
}

int main() {
  gen expr("sin(45)", context0);
  
  std::thread t1(parallel_compute, expr, 0);
  std::thread t2(parallel_compute, expr, 1);
  
  t1.join();
  t2.join();
}

Mutex Protection

#ifdef HAVE_LIBPTHREAD
pthread_mutex_t * mutex = mutexptr(contextptr);

pthread_mutex_lock(mutex);
// Critical section: modify shared context
pthread_mutex_unlock(mutex);
#endif

Best Practices

Pass Contexts Consistently

Always use GIAC_CONTEXT or GIAC_CONTEXT0 in function signatures for consistency

Avoid NULL Contexts

Check for NULL contextptr or use GIAC_CONTEXT0 to default to context0

Use Local Contexts for Isolation

Create new contexts when you need variable isolation or different settings

Clean Up Properly

Call cleanup functions when done with a context to free resources

Thread Safety

Give each thread its own context to avoid synchronization issues

Save/Restore State

Use giac_current_status() and unarchive_session() for session management

Common Pitfalls

Dangling Context Pointers: Don’t keep references to stack-allocated contexts after they go out of scope.
gen* bad_function() {
  context local_ctx;
  gen x("x", &local_ctx);
  return &x;  // BAD: local_ctx will be destroyed!
}
Shared Mutable State: Be careful when multiple contexts share the same global pointer - changes affect all.
Forgetting Context: Always pass the context through the entire call chain:
gen good_function(const gen & arg, GIAC_CONTEXT) {
  return eval(arg, 1, contextptr);  // GOOD: passes context
}

gen bad_function(const gen & arg, GIAC_CONTEXT) {
  return eval(arg, 1, context0);  // BAD: ignores parameter
}

Next Steps

Architecture

Learn about Giac’s overall structure

Data Types

Understand the gen class and type system

Build docs developers (and LLMs) love